{"id":13725977,"url":"https://github.com/snovakovic/js-flock","last_synced_at":"2025-05-07T21:30:47.426Z","repository":{"id":21011350,"uuid":"91576189","full_name":"snovakovic/js-flock","owner":"snovakovic","description":"Collection of neat modular utilities for bumping up development in NODE and Browser","archived":false,"fork":false,"pushed_at":"2023-01-06T01:38:51.000Z","size":1264,"stargazers_count":170,"open_issues_count":5,"forks_count":6,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-22T23:40:42.400Z","etag":null,"topics":["collar","deepfreeze","enum","promisify","promisifyall","sort","waitfor"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/snovakovic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-17T12:52:53.000Z","updated_at":"2025-02-28T19:21:25.000Z","dependencies_parsed_at":"2023-01-13T21:15:48.769Z","dependency_job_id":null,"html_url":"https://github.com/snovakovic/js-flock","commit_stats":null,"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snovakovic%2Fjs-flock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snovakovic%2Fjs-flock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snovakovic%2Fjs-flock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snovakovic%2Fjs-flock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/snovakovic","download_url":"https://codeload.github.com/snovakovic/js-flock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252957074,"owners_count":21831429,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["collar","deepfreeze","enum","promisify","promisifyall","sort","waitfor"],"created_at":"2024-08-03T01:02:45.221Z","updated_at":"2025-05-07T21:30:47.088Z","avatar_url":"https://github.com/snovakovic.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# js-flock\n\n[![Build Status](https://travis-ci.org/snovakovic/js-flock.svg?branch=master)](https://travis-ci.org/snovakovic/js-flock)\n[![Git Stars](http://githubbadges.com/star.svg?user=snovakovic\u0026repo=js-flock)](http://github.com/snovakovic/js-flock/stargazers)\n[![Code Quality](https://api.codacy.com/project/badge/grade/fe5f8741eaed4c628bca3761c32c3b68)](https://www.codacy.com/app/snovakovic/js-flock/dashboard?bid=4653162)\n[![Code Coverage](https://api.codacy.com/project/badge/Coverage/f0ea30fd63bd4bc88ea3b0965094ced1)](https://www.codacy.com/app/snovakovic/js-flock?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=snovakovic/js-flock\u0026utm_campaign=Badge_Coverage)\n[![Known Vulnerabilities](https://snyk.io/test/github/snovakovic/js-flock/badge.svg)](https://snyk.io/test/github/snovakovic/js-flock)\n[![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://opensource.org/)\n[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php)\n\n[![NPM Package](https://nodei.co/npm/js-flock.png)](https://www.npmjs.com/package/js-flock)\n\n\nCollection of neat modular utilities for bumping up development in NODE and Browser.\n\n## Including library\n\nLibrary is modular so you can include only modules that you need/use (recommended way of using library). **By default unmodified ES6 code is loaded**, optionally you can include transpiled ES5 code (recommended for browser environment).\nTranspiled code is wrapped in [UMD](https://davidbcalhoun.com/2014/what-is-amd-commonjs-and-umd/) and can be loaded in Browser as CommonJs, AMD or as global var.\n\n```javascript\n  // Load unmodified ES6 sort module (recommended for node environment).\n  // In same way we can include any other library module e.g ('js-flock/toEnum', 'js-flock/deepFreeze'...)\n  const sort = require('js-flock/sort');\n\n  // Load transpiled/minified ES5 sort module (recommended for browser).\n  const sort = require('js-flock/es5/sort');\n\n  // Load whole unmodified ES6 library\n  const jsFlock = require('js-flock');\n\n  // Load whole transpiled/minified ES5 library\n  // Note recommended in browser as bundle can be larger than we need\n  const jsFlock = require('js-flock/es5');\n```\n\n## Methods:\n* [last](#last)\n* [empty](#empty)\n* [single](#single)\n* [sort](#sort)\n* [numberIterator](#numberiterator)\n* [castBoolean](#castboolean)\n* [toEnum](#toenum)\n* [singular](#singular)\n* [waitFor](#waitfor)\n* [rerun](#rerun)\n* [delay](#delay)\n* [promiseAll](#promiseall)\n* [promisify](#promisify)\n* [promisify.all](#promisifyall)\n* [collar](#collar)\n* [deepFreeze](#deepfreeze) / [deepSeal](#deepseal) / [deepPreventExtensions](#deeppreventextensions)\n\n### last\n\nGet the last element of an array. If condition is provided get the last element of an array that meets provided condition or undefined.\n\n##### since: v2.2.0\n\n```javascript\nconst last = require('js-flock/last');\n\nlast([1, 4, 2]); // =\u003e 2\n\nconst persons = [{ id: 1, name: 'john'}, { id: 2, name: 'john'}, { id: 3, name: 'doe'}]\n\nlast(persons) // =\u003e  { id: 3, name: 'doe'}\nlast(persons, (p) =\u003e p.name === 'john') // =\u003e { id: 2, name: 'john'}\nlast(persons, (p) =\u003e p.name === 'no-name') // =\u003e undefined\n```\n\n\n### empty\n\nRemove all items from 1 or more provided arrays.\n\n##### since: v3.2.0\n\n```javascript\nconst empty = require('js-flock/empty');\n\nconst arr = [1, 2, 3];\n\n// Shorthand for applying arr.splice(0, arr.length);\nconst emptyArr = empty(arr); // =\u003e arr ==== []\nconsole.log(emptyArr === arr) // =\u003e true\n\n// We can empty multiple arrays. Non array values will be ignored\nempty(arr1, undefined, arr2, 3);\n```\n\n### single\n\nReturns the only element of a sequence that satisfies a specified condition, and throws an exception if more than one such element exists.\n\n##### since: v3.13.0\n\n```javascript\nconst single = require('js-flock/single');\n\n// Without providing any condition\nsingle([1]); // =\u003e 1\nsingle([1, 2]); // =\u003e TypeError('More than one element satisfies the condition')\nsingle([]); // =\u003e TypeError('No element satisfies the condition')\n\n// With condition\nconst users = [\n  { email: 'john@doe.com', fullName: 'John Doe 1' },\n  { email: 'john@doe.com', fullName: 'John Doe 2' }, // NOTE same email as John Doe 1\n  { email: 'mark@johnson.com', fullName: 'Mark Johnson' },\n];\n\nsingle(users, user =\u003e user.email === 'mark@johnson.com'); // { email: 'mark@johnson.com', fullName: 'Mark Johnson' }\nsingle(users, user =\u003e user.email === 'john@doe.com'); // TypeError('More than one element satisfies the condition')\nsingle(users, user =\u003e user.email === 'no@user.com'); // TypeError('No element satisfies the condition')\n```\n\n### NumberIterator\n\nget next number in sequence.\n\n##### since: v3.9.0\n\n````javascript\nconst NumberIterator = require('js-flock/NumberIterator');\n\nconst numberIterator = new NumberIterator();\nnumberIterator.current() // =\u003e 0\nnumberIterator.next() // =\u003e 1\nnumberIterator.next() // =\u003e 2\nnumberIterator.current() // =\u003e 2\n\n// When instantiating iterator we can pass optional startFrom property\nconst numberIterator2 = new NumberIterator({ startFrom: 10 });\nnumberIterator2.current() // =\u003e 10\nnumberIterator2.next() // =\u003e 11\n````\n\nIf iterator reaches `Number.MAX_SAFE_INTEGER` the iterator exhausted error will be thrown on next iteration call.\n\n### castBoolean\n\nCast any value to boolean value. castBoolean will return true for true or \"true\" values\nwhile any other value will be evaluated to false.\n\n##### since: v3.11.0\n\n````javascript\nconst castBoolean = require('js-flock/castBoolean');\n\ncastBoolean(true); // =\u003e true\ncastBoolean('true'); // =\u003e true\n\ncastBoolean(undefined) // =\u003e false\ncastBoolean('foo') // =\u003e false\n````\n### toEnum\n\nConvert object or list of strings to enum representation.\nEnum representation is immutable (frozen)\n\n```javascript\nconst toEnum = require('js-flock/toEnum');\n\nconst vehicleType = toEnum({\n  CAR: 'C',\n  TRUCK: 'T',\n  AIRPLANE: 'A',\n  HELICOPTER: 'H',\n  canFly(type) { // Define custom helper\n    return type === this.AIRPLANE || type === this.HELICOPTER;\n  }\n});\n\nconst vehicle = getVehicle();\n\nif (vehicle.type === vehicleType.TRUCK) {\n  // Special behaviour only for truck vehicles\n}\n\nif (vehicleType.canFly(vehicle.type)) {\n  // Special behaviour for vehicles that can fly\n}\n\n// enum is immutable\nvehicleType.TRUCK = 'boat'; // vehicleType.TRUCK === 'T'\n\n// Each enum have standard helpers\n\nvehicleType.keys(); // ['CAR', 'TRUCK', 'AIRPLANE', 'HELICOPTER'] - helper functions are not included in keys\nvehicleType.values(); // ['C', 'T', 'A', 'H']\n\nvehicleType.exists('C'); // true\nvehicleType.exists('something'); // false\n\nvehicleType.haveKey('CAR'); // true\nvehicleType.haveKey('something'); // false\n\n\n// We can define enum with short notation. Limitation of short notation is that we can't define custom enum helpers.\n\nconst gender = toEnum(['MAN', 'WOMEN', 'OTHER']);\n\ngender.keys(); // ['MAN', 'WOMEN', 'OTHER']\ngender.values(); // [Symbol(MAN), Symbol(Women), Symbol(OTHER)]\n```\n\n\n### singular\n\n Creates singular function that after is called can't be called again until it finishes with execution.\n Singular function injects done function as a first argument of the original function.\n When called done indicates that function has finished with execution and that it can be called again.\n\n For example we will use Vue.js and click handler.\n\n```html\n\u003cspan @click=\"save()\" role=\"button\"\u003eSave User\u003c/span\u003e\n```\n\n```javascript\nconst singular = require('js-flock/singular');\n\nexport default {\n  methods: {\n    save: singular(function(done) {\n      // All subsequent calls to submit will be ignored until done is called\n      UserService.save(this.user)\n        .then(() =\u003e { /* Success handler */ })\n        .catch(() =\u003e { /* Exception handler */ })\n        .then(done);\n    }\n  };\n}\n```\n\n### waitFor\n\nWait for task to complete before executing function. This module is useful when there isn't event\nyou can hook into to signify that a given task is complete. waitFor returns promise that resolves\nafter check function returns truthy value.\n\n##### since: v2.4.0\n\n```javascript\nconst waitFor = require('js-flock/waitFor');\n\nconst options = {\n  interval: Number, // [Default: 50ms] - How frequently will check be preformed.\n  timeout: Number, // [Default: 5000ms] - Timeout if function is not resolved by then.\n};\n\n// Wait for DB connection\nwaitFor(() =\u003e Db.connection, options)\n  .then((connection) =\u003e { /* connection to DB has been established */})\n  .catch(() =\u003e { /* Waiting timed out, handle the error! */ });\n\n// Wait for DOM element to become accessible\nwaitFor(() =\u003e document.getElementById('elId'))\n  .then(($el) =\u003e { /* Element is available now we can do manipulation with $el */})\n  .catch(() =\u003e { /* Waiting timed out, handle the error! */ });\n\n// We can abort execution of waitFor at any moment by calling abort function that is\n// injected to waitFor listener as shown in example.\n// NOTE: Available from v3.6.0\nwaitFor((abort) =\u003e {\n  if(componentIsDestroyed) {\n    // waitFor will immediately stop checking for presence of element\n    // than/catch will never be called after calling abort\n    abort();\n  } else {\n    return document.getElementById('elId');\n  }\n})\n  .then(($el) =\u003e { /* Element is available now we can do manipulation with $el */})\n  .catch(() =\u003e { /* Waiting timed out, handle the error! */ });\n```\n\n### rerun\n\nsetInterval on steroids.\n\n##### since: v3.3.0\n\n```javascript\n\n  // Any user defined function.\n  rerun(Function)\n    // How frequently will rerun function be called\n    .every(timeInMilliseconds)\n    // [Optional] Execution is stopped if falsy value is returned from function. If falsy value is returned first time rerun will never be called.\n    .asLongAs(Function)\n    // Execute rerun function for first time and start execution cycle\n    .start()\n    // [Optional] -\u003e Attach onStop listener\n    .onStop(Function)\n    // [Optional] Stop function execution. Function execution can also be stoped by returning falsy value from asLongAs or by returning `false` value from within rerun function\n    .stop()\n\n  // Example\n  // refreshAuthToken and isUserLoggedIn are custom function implementations\n  const tenMinutesInMs = 10 * 60 * 1000;\n  const refreshTokenRunner = rerun(refreshAuthToken)\n    .every(tenMinutesInMs)\n    .asLongAs(isUserLoggedIn);\n\n  // Function that will be called after user log in\n  // Every call to start will execute function immediately and restart execution cycle\n  eventBus.$on('login', refreshTokenRunner.start);\n```\n\n## delay\n\nDelay a promise a specified amount of time. Think of it as setTimeout for async flow\n\n##### since: v3.8.0\n\n\n```javascript\nconst delay = require('js-flock/delay');\n\nasync exampleFunction() {\n  bar();\n\n  // Wait for 100 milliseconds\n  // If delay time not provided it defaults ot 0ms (next tick)\n  await delay(100);\n\n  // Executed 100 milliseconds later\n  baz();\n}\n```\n\n### promiseAll\n\nAlias for `Promise.all` that works on objects and arrays\n\n##### since: v3.7.0\n\n```javascript\nconst promiseAll = require('js-flock/promiseAll');\n\nconst objectResponse = await promiseAll({\n  users: db.fetchUsers(),\n  schools: db.fetchSchools(),\n});\n\n/*\n  objectResponse =\u003e\n    {\n      users: [{...}, {...}], // value of fetchUsers promise resolve\n      schools: [{...}, {...}], // value of fetchSchools promise resolve\n    }\n*/\n\nconst arrayResponse = await promiseAll([db.fetchUsers(), db.fetchSchools()]);\n\n/*\n  arrayResponse =\u003e [usersResponse, schoolsResponse]\n*/\n\n```\n\n### promisify\n\nPromisify error first callback function. Instead of taking a callback, the returned function\nwill return a promise whose fate is decided by the callback behavior of the given node function.\nPromisify returns native Promise (requires Promise polyfill on older browser)\n\n```javascript\nconst promisify = require('js-flock/promisify');\nconst readFile = require(\"fs\").readFile;\nconst readFileAsync = promisify(readFile);\n\n// Native version of read file\nreadFile('test.txt', 'utf8', (err, data) =\u003e {\n  if (err) {\n    console.log(err);\n    return;\n  }\n  console.log(data);\n});\n\n// Promisify version\nreadFileAsync('test.txt', 'utf8')\n  .then((data) =\u003e console.log(data))\n  .catch((err) =\u003e console.log(err));\n```\n\nIf callback function is called with multiple success values, the fulfillment value will be the\nfirst fulfillment item.\n\nSetting multiArgs options to true means the resulting promise will always fulfill with\nan array of the callback's success value(s). This is needed because promises only support a\nsingle success value while some callback API's have multiple success value.\n\n```javascript\nconst fun = (cb) =\u003e cb(undefined, 'res1', 'res2');\nconst funAsync = promisify(fun, { multiArgs: true });\n\nfunAsync().then(([r1, r2]) =\u003e { /* r1 === res1, r2 === res2 */ });\n```\n\n\n### promisify.all\n\nPromisify the entire object by going through the object's properties and creating\nan async equivalent of each function on the object.\nPromisify.all mutates input object by adding promisified versions to object.\nIt will never overwrite existing properties of object.\n\nBy default promisify.all does not loop over object prototype which can be change by providing\n{ proto: true } option.\n\nThe promisified method name will be the original method name suffixed with suffix (default = 'Async').\n\n```javascript\nconst promisify = require('js-flock/promisify');\nconst fs = promisify.all(require(\"fs\"));\n\n// New function appended by promisify.all\nfs.readFileAsync('test.txt', 'utf8')\n  .then((data) =\u003e console.log(data))\n  .catch((err) =\u003e console.log(err));\n\nconst withOptions = promisify.all(test, {\n  suffix: String, // [default: 'Async'] - Suffix will be appended to original method name\n  multiArgs: Boolean, // [default: false] Promise will resolve with array of values if true\n  proto: Boolean, // [default: false] Promisify object prototype chain if true\n  exclude: [String], // [default: undefined] List of object keys not to promisify\n  include: [String], // [default: undefined] Promisify only provided keys\n});\n```\n\n\n### collar\n\nSet maximum waiting time for promise to resolve. Reject promise if it's not resolved in that time\n\n```javascript\nconst collar = require('js-flock/collar');\n\nconst MAX_WAITING_TIME = 500;\n\n// Reject HTTP request if it's not resolved in 0.5 seconds\ncollar(Http.get('test-url'), MAX_WAITING_TIME)\n.then((response) =\u003e { /* handle response */  })\n.catch((err) =\u003e {\n  // CollarError = { isStrangled: true, message: 'Promise have timed out' }\n  if (typeof err === 'object' \u0026\u0026 err.isStrangled) {\n    console.log(err.message);\n  }\n});\n```\n\n\n### deepFreeze\n\nRecursively apply [Object.freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)\n\n```javascript\nconst deepFreeze = require('js-flock/deepFreeze');\n\nconst person = {\n  fullName: 'test person',\n  dob: new Date(),\n  address: {\n    country: 'testiland',\n    city: 'this one'\n  }\n};\n\nObject.freeze(person);\n\nObject.isFrozen(person); // true\nObject.isFrozen(person.address); // false UH OH\n\ndeepFreeze(person);\n\nObject.isFrozen(person); // true\nObject.isFrozen(person.address); // true WE HE\n\n// We can modify deepFreeze behaviour by providing additional options\n\ndeepFreeze(object, {\n  proto: Boolean, // [default: false] - Freeze object prototype chain\n  exclude: Function, // Fine tune what will be frozen by providing exclude function. Available from version [3.3.2]\n});\n\ndeepFreeze(person, {\n  // address object will not be frozen\n  exclude(key, context) {\n    return key === 'address' \u0026\u0026 context === person;\n  }\n});\n```\n\n\n### deepSeal\n\nRecursively apply [Object.seal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal).\nFor example of usage reference [deepFreeze](#deepfreeze)\n\n\n### deepPreventExtensions\n\nRecursively apply [Object.preventExtensions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions).\nFor example of usage reference [deepFreeze](#deepfreeze)\n\n\n### sort\n\nSource code of sort has been moved to dedicated package https://www.npmjs.com/package/fast-sort and is now just imported here.\nFor more info on sort please check dedicated package from above.\n\n```javascript\n  const sort = require('js-flock/sort');\n\n  sort([1, 4, 2]).asc(); // =\u003e [1, 2, 4]\n  sort([1, 4, 2]).desc(); // =\u003e [4, 2, 1]\n\n  sort(persons).asc(p =\u003e p.firstName);\n\n  // Sort persons by multiple properties\n  sort(persons).desc([\n    p =\u003e p.firstName\n    p =\u003e p.address.city\n  ]);\n\n  // Sort in multiple directions\n  sort(persons).by([\n    { asc: 'name' }\n    { desc: 'age' }\n    { asc: p =\u003e p.address.city }\n  ]);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnovakovic%2Fjs-flock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnovakovic%2Fjs-flock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnovakovic%2Fjs-flock/lists"}