{"id":24083391,"url":"https://github.com/mitranim/jol","last_synced_at":"2025-10-08T09:06:33.460Z","repository":{"id":82132052,"uuid":"382882783","full_name":"mitranim/jol","owner":"mitranim","description":"JS Collection Classes. Tiny extensions on JS built-in classes, with nice features such as easy-to-use typed collections, dictionary with structured keys; and more. Tiny, dependency-free, single-file, native module.","archived":false,"fork":false,"pushed_at":"2021-11-18T10:29:53.000Z","size":45,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-12T00:17:27.889Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mitranim.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-04T15:15:53.000Z","updated_at":"2023-08-01T09:09:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"758788fb-7083-4158-9924-784b7b0eb796","html_url":"https://github.com/mitranim/jol","commit_stats":{"total_commits":11,"total_committers":1,"mean_commits":11.0,"dds":0.0,"last_synced_commit":"aeeeca4636cd8f6ff64fc1c6baf93b7bdc8db104"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/mitranim/jol","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fjol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fjol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fjol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fjol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mitranim","download_url":"https://codeload.github.com/mitranim/jol/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Fjol/sbom","scorecard":{"id":650479,"data":{"date":"2025-08-11","repo":{"name":"github.com/mitranim/jol","commit":"aeeeca4636cd8f6ff64fc1c6baf93b7bdc8db104"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/11 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T13:23:33.159Z","repository_id":82132052,"created_at":"2025-08-21T13:23:33.159Z","updated_at":"2025-08-21T13:23:33.159Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278916857,"owners_count":26068186,"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","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2025-01-09T23:56:32.841Z","updated_at":"2025-10-08T09:06:33.421Z","avatar_url":"https://github.com/mitranim.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Overview\n\n\"**J**S C**ol**lection C**l**asses\". Tiny extensions on JS built-in classes such as `Object`, `Array`, `Set`, `Map`, with nice features such as:\n\n* Easy-to-use typed collections such as `Arr\u003cCls\u003e` or `Set\u003cCls\u003e` with automatic idempotent element instantiation.\n* `EqDict`: dictionary with support for structured keys, like `[\"composite\", \"key\"]`. Compares keys by value, not by reference.\n* Consistent constructor signatures: everything is `new Cls(val)`, like ES6 `Map` and `Set`.\n\nTiny, dependency-free, single file, native JS module.\n\nBrowser compatibility: any ES6+ environment. For older browsers, polyfill `Set` and `Map` and allow your bundler to transpile this library.\n\n## TOC\n\n* [Usage](#usage)\n* [API](#api)\n  * [`class Null`](#class-null)\n  * [`class Arr`](#class-arr-extends-array)\n  * [`class ClsArr`](#class-clsarr-extends-arr)\n  * [`class Dict`](#class-dict-extends-map)\n  * [`class ClsDict`](#class-clsdict-extends-dict)\n  * [`class ClsMap`](#class-clsmap-extends-map)\n  * [`class EqDict`](#class-eqdict)\n  * [`class ClsSet`](#class-clsset-extends-set)\n  * [`class Que`](#class-que-extends-set)\n  * [`function assign`](#function-assigntarget-source)\n  * [`function inst`](#function-instval-cls)\n  * [`function opt`](#function-optval-cls)\n  * [`function toKey`](#function-tokeykey)\n  * [`function isPlain`](#isplainval)\n* [License](#license)\n* [Misc](#misc)\n\n## Usage\n\nInstall with NPM, or import by URL:\n\n```sh\nnpm i -E @mitranim/jol\n```\n\n```js\nimport * as j from '@mitranim/jol'\nimport * as j from 'https://cdn.jsdelivr.net/npm/@mitranim/jol@0.1.6/jol.mjs'\n```\n\n## API\n\n### `class Null`\n\nInherits from `null` rather than `Object`. Extend `Null` for a \"squeaky clean\" class.\n\n### `class Arr extends Array`\n\nLike `Array`, but instead of `new Array(...vals)`, the constructor signature is `new Arr(vals)`, mirroring the behavior of `Set` and preventing gotchas.\n\n**Valid** calls:\n\n```js\nnew j.Arr()             // Like [].\nnew j.Arr(null)         // Like [].\nnew j.Arr(0)            // Like [].\nnew j.Arr(8)            // Like Array(8).\nnew j.Arr([10, 20, 30]) // Like [10, 20, 30] or Array(10, 20, 30).\nnew j.Arr('one')        // Like [...'one'] or Array(...'one').\n```\n\n**Invalid** calls (runtime exception):\n\n```js\nnew j.Arr({})\nnew j.Arr(10, 20)\nnew j.Arr(() =\u003e {})\n```\n\n### `class ClsArr extends Arr`\n\nRuntime approximation of `Arr\u003cCls\u003e`. Idempotently auto-instantiates values via `this.cls`.\n\n```js\nclass Model {\n  constructor(val) {this.val = val * 2}\n}\n\nclass Models extends j.ClsArr {\n  get cls() {return Model}\n}\n\nnew Models([10, 20])\n// [ Model { val: 20 }, Model { val: 40 } ]\n```\n\n### `class Dict extends Map`\n\nVariant of `Map` whose behavior is closer to `Object`:\n\n  * Can be constructed from plain objects: `new j.Dict({one: 10})`.\n  * Compatible with JSON. Reversible encoding and decoding.\n  * Allows **only** strings as keys. Other keys cause exceptions.\n\nCompatibility with JSON:\n\n```js\nnew j.Dict({one: 10, two: 20})\n// Dict { \"one\" =\u003e 10, \"two\" =\u003e 20 }\n\nnew j.Dict(JSON.parse(`{\"one\": 10, \"two\": 20}`))\n// Dict { \"one\" =\u003e 10, \"two\" =\u003e 20 }\n\nJSON.stringify(new j.Dict({one: 10, two: 20}))\n// {\"one\":10,\"two\":20}\n```\n\n### `class ClsDict extends Dict`\n\nRuntime approximation of `Dict\u003cCls\u003e`. Idempotently auto-instantiates values via `this.cls`.\n\n```js\nclass Model {\n  constructor(val) {this.val = val * 2}\n}\n\nclass Models extends j.ClsDict {\n  get cls() {return Model}\n}\n\nnew Models({one: 10, two: 20})\n// Models { \"one\" =\u003e Model { val: 20 }, \"two\" =\u003e Model { val: 40 } }\n```\n\n### `class ClsMap extends Map`\n\nRuntime approximation of `Map\u003cany, Cls\u003e`. Idempotently auto-instantiates values via `this.cls`.\n\n```js\nclass Model {\n  constructor(val) {this.val = val * 2}\n}\n\nclass Models extends j.ClsMap {\n  get cls() {return Model}\n}\n\nnew Models([[10, 20], [30, 40]])\n// Models { 10 =\u003e Model { val: 40 }, 30 =\u003e Model { val: 80 } }\n```\n\n### `class EqDict`\n\nLike `Object` or `Map`, but:\n\n* Keys can be structured data, such as `{one: 10}` or `['two', 'three']`.\n* Keys are always compared by structure, not by reference.\n* Internally, keys are encoded as deterministic JSON.\n  * Relies on engine quirks; has not been tested in all browsers.\n  * The conversion function `toKey` is exported separately.\n  * Computational complexity for access-by-key should be similar to `{}`, with the added overhead of JSON encoding (should scale with key size but not overall dict size).\n\nMethods are similar to `Map`:\n\n* `eqMap.has(key)`\n* `eqMap.get(key)`\n* `eqMap.set(key, val)`\n* `eqMap.delete(key)`\n* `eqMap.forEach()`\n* `eqMap.keys()`\n* `eqMap.values()`\n* `eqMap.entries()`\n* Supports `for .. of`.\n\nThese internal methods require plain string keys, and are provided for subclasses:\n\n* `eqMap.hasRaw(key)`\n* `eqMap.getRaw(key)`\n* `eqMap.setRaw(key, val)`\n* `eqMap.deleteRaw(key)`\n\nUnlike the syntax `object[key]`, methods of `EqDict` operate only on _own enumerable properties_. Because keys are always JSON-encoded, there is _no collision_ with `Object` methods and properties.\n\n```js\nconst coll = new j.EqDict()\ncoll.set({one: 10, two: 20}, 'value')\ncoll.get({two: 20, one: 10}) === 'value'\n```\n\nSupports reversible JSON encoding and decoding:\n\n```js\nconst prev = new j.EqDict().set('one', 10)\nconst next = new j.EqDict(JSON.parse(JSON.stringify(prev)))\n// prev ≈ next\n```\n\nHowever, **avoid** the following:\n\n```js\nnew j.EqDict({one: 10})\n```\n\nAll keys must be created by calling `.set()`, otherwise they don't get encoded, and you can't retrieve the values with `.get()`!\n\n### `class ClsSet extends Set`\n\nRuntime approximation of `Set\u003cCls\u003e`. Idempotently auto-instantiates values via `this.cls`.\n\n```js\nclass Model {\n  constructor(val) {this.val = val * 2}\n}\n\nclass Models extends j.ClsSet {\n  get cls() {return Model}\n}\n\nnew Models([10, 20])\n// Models { Model { val: 20 }, Model { val: 40 } }\n```\n\n### `class Que extends Set`\n\nOrdered FIFO queue of functions. Paused by default. Calling `.flush()` immediately dequeues and calls functions one-by-one, and any future calls to `.add()` will result in immediate calls. Call `.pause()` to pause again.\n\n```js\nconst que = new Que()\n\nque.add(function one() {console.log('one')})\nque.add(function two() {console.log('two')})\n\n// Prints 'one'.\n// Prints 'two'.\nque.flush()\n\n// Prints 'three'.\nque.add(function three() {console.log('three')})\n\nque.pause()\n```\n\n### `function assign(target, source)`\n\nSimilar to `Object.assign`, but with differences:\n\n* `target` must be a non-array object (internally termed \"struct\").\n* `source` must be either nil (`undefined` or `null`), a plain dict (`{}` or `Object.create(null)`), or a subclass of `target.constructor`.\n* Does **not** shadow inherited or non-enumerable properties.\n\n**Valid** calls:\n\n```js\nclass Mock {\n  constructor() {this.key = 'val'}\n}\n\nj.assign({},            undefined)\nj.assign({},            {key: 'val'})\nj.assign({},            new Mock())\nj.assign(new Mock(),    {key: 'val'})\nj.assign(new Mock(),    new Mock())\nj.assign(new j.Obj({}), {key: 'val'})\nj.assign({},            new j.Obj({key: 'val'}))\n```\n\n**Invalid** calls (runtime exception):\n\n```js\nclass Mock {}\n\nj.assign(10,            {})\nj.assign('one',         {})\nj.assign({},            [])\nj.assign([],            {})\nj.assign([],            [])\nj.assign(new Mock(),    new j.Obj({}))\nj.assign(new j.Obj({}), new Mock())\n```\n\nNon-shadowing behavior:\n\n```js\nj.assign({}, {constructor: 10, toString: 20, unknown: 30})\n// {unknown: 30}\n```\n\n### `function inst(val, cls)`\n\nIdempotently converts `val` to an instance of `cls`:\n\n* If `val` is an instance of `cls`, returns `val` as-is.\n* Otherwise returns `new cls(val)`.\n\n```js\nclass Mock {}\n\nlet val\nval = j.inst(val, Mock) // new instance\nval = j.inst(val, Mock) // preserves existing instance\nval = j.inst(val, Mock) // preserves existing instance\nval = j.inst(val, Mock) // preserves existing instance\n```\n\n### `function opt(val, cls)`\n\nSame as `inst`, but if `val` is `null` or `undefined`, it's returned as-is, without instantiation.\n\n```js\nclass Mock {constructor(val) {this.val = val}}\nj.opt(undefined, Mock) // undefined\nj.opt(10, Mock)        // Mock{val: 10}\n```\n\n### `function toKey(key)`\n\nUsed internally by `ClsArr`, `ClsSet`, `ClsMap`. Like `JSON.stringify` but:\n\n* When input is `undefined`, returns `''` instead of `undefined`.\n* Sorts object keys, producing deterministic output.\n\n```js\nj.toKey()                   // ''\nj.toKey(null)               // 'null'\nj.toKey(10)                 // '10'\nj.toKey('one')              // '\"one\"'\nj.toKey({one: 10, two: 20}) // '{\"one\":10,\"two\":20}'\nj.toKey({two: 20, one: 10}) // '{\"one\":10,\"two\":20}'\n```\n\n### `isPlain(val)`\n\nReturns `true` if val is either:\n\n* Primitive.\n* Instance of `Array`.\n* Plain dict: `{}` or `Object.create(null)`.\n\nUsed internally by `toKey`, which rejects other inputs (runtime exception).\n\n## Changelog\n\n### 0.1.6\n\nAdditions:\n\n  * Add `Dict`.\n  * Add `ClsDict`.\n\nBreaking:\n\n  * Removed `Obj`.\n  * `assign` now avoids shadowing inherited or non-enumerable properties.\n  * `assign` now allows nil source, doing nothing instead of throwing.\n\nMisc:\n\n  * Overridden methods of `Arr`, `ClsArr`, `ClsMap`, `ClsSet` now return `this` instead of `undefined`.\n  * `Que..pause` and `Que..flush` now return `this`.\n\n### 0.1.5\n\nRenamed `toInst` → `inst`, `toInstOpt` → `opt` for brevity.\n\n### 0.1.4\n\n`toInst` now allows subclass instances, instead of requiring an exact class match. This affects the behavior of all class collections.\n\n### 0.1.3\n\nAvoid an infinite loop when using `NaN` in `toKey` and `EqDict`.\n\n### 0.1.2\n\n`new Null()` now also creates a null-based object, rather than `{}`. The behavior of `Null` subclasses is unchanged.\n\n### 0.1.1\n\nAdded `toInstOpt`. `EqDict` now extends `Object` rather than `Null`.\n\n## License\n\nhttps://unlicense.org\n\n## Misc\n\nI'm receptive to suggestions. If this library _almost_ satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitranim%2Fjol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmitranim%2Fjol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitranim%2Fjol/lists"}