{"id":18751149,"url":"https://github.com/substrate-system/util","last_synced_at":"2025-07-06T22:08:35.220Z","repository":{"id":249161755,"uuid":"830667563","full_name":"substrate-system/util","owner":"substrate-system","description":"Utility functions","archived":false,"fork":false,"pushed_at":"2025-07-02T01:22:22.000Z","size":151,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-02T01:27:10.299Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://substrate-system.github.io/util/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/substrate-system.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-07-18T18:17:14.000Z","updated_at":"2025-07-02T01:22:25.000Z","dependencies_parsed_at":"2024-11-18T05:18:18.719Z","dependency_job_id":"81a3df08-d885-485a-bfcc-4adb738c378e","html_url":"https://github.com/substrate-system/util","commit_stats":null,"previous_names":["substrate-system/util"],"tags_count":30,"template":false,"template_full_name":"nichoth/template-ts-browser","purl":"pkg:github/substrate-system/util","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Futil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Futil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Futil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Futil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/substrate-system","download_url":"https://codeload.github.com/substrate-system/util/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Futil/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263980180,"owners_count":23538922,"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":[],"created_at":"2024-11-07T17:14:40.562Z","updated_at":"2025-07-06T22:08:35.212Z","avatar_url":"https://github.com/substrate-system.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# util\n[![tests](https://img.shields.io/github/actions/workflow/status/substrate-system/util/nodejs.yml?style=flat-square)](https://github.com/substrate-system/util/actions/workflows/nodejs.yml)\n[![types](https://img.shields.io/npm/types/@substrate-system/util?style=flat-square)](README.md)\n[![module](https://img.shields.io/badge/module-ESM%2FCJS-blue?style=flat-square)](README.md)\n[![semantic versioning](https://img.shields.io/badge/semver-2.0.0-blue?logo=semver\u0026style=flat-square)](https://semver.org/)\n[![dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen.svg?style=flat-square)](package.json)\n[![install size](https://flat.badgen.net/packagephobia/install/@substrate-system/util?cache-control=no-cache)](https://packagephobia.com/result?p=@substrate-system/util)\n[![license](https://img.shields.io/badge/license-Polyform_Small_Business-249fbc?style=flat-square)](LICENSE)\n\n\nUtility functions for the front end.\n\n\u003c!-- toc --\u003e\n\n- [install](#install)\n- [import](#import)\n- [API](#api)\n  * [toString](#tostring)\n  * [lock \u0026 unlock scrolling](#lock--unlock-scrolling)\n  * [Constants](#constants)\n  * [HTML classes](#html-classes)\n  * [`self`](#self)\n  * [`humanFilesize`](#humanfilesize)\n  * [Queue](#queue)\n  * [`sleep`](#sleep)\n  * [`isEmailValid(maybeEmail:string)`](#isemailvalidmaybeemailstring)\n  * [`parseForm`](#parseform)\n  * [`attributesToString`](#attributestostring)\n  * [`setAttributes`](#setattributes)\n  * [`attributesAsObject`](#attributesasobject)\n  * [`objectToString`](#objecttostring)\n  * [`CONSTANTS`](#constants)\n\n\u003c!-- tocstop --\u003e\n\n## install\n\n```sh\nnpm i -S @substrate-system/util\n```\n\n## import\n\n```js\nimport util from '@substrate-system/util'\n\n// or individual functions\nimport { attributesToString } from '@substrate-system/util'\n```\n\n## API\n\n### toString\n\nConvert an object into a string suitable for HTML attributes. The object\nshould be like `{ attributeName: value }`. `value` can be and array, string,\nor boolean. If it is a boolean, then this will add the attribute name only, eg\n\n```js\nimport { attributes } from '@substrate-system/util/to-string.js'\n\nattributes({ disabled: true })\n// =\u003e 'disabled'\n```\n\n```js\nimport { attributes } from '@substrate-system/util/to-string.js'\n\nconst string = attributes({ hello: 'abc', ok: '123' })\n\n// =\u003e 'hello=\"abc\" ok=\"123\"'\n```\n\n```js\nimport { attributes } from '@substrate-system/util/to-string.js'\n\ntest('Create attributes with null and undefined', t =\u003e {\n    const str = attributes({\n        hello: 'abc',\n        abc: undefined,\n        def: null,\n        ghi: false,\n        jkl: ''\n    })\n\n    t.equal(str, 'hello=\"abc\"')\n})\n```\n\n### lock \u0026 unlock scrolling\n\nPrevents body scrolling, useful for things like \"dialog\" windows.\nKeeps track of which elements requested a lock so multiple levels of\nlocking are possible without premature unlocking.\n\nOriginally seen in [the shoelace library](https://github.com/shoelace-style/shoelace/blob/fb59fda70ed737c92611051b49bc7e3a5fed5dc5/src/internal/scroll.ts#L58).\n\n#### CSS\n\nDepends on having this CSS:\n\n```css\n@supports (scrollbar-gutter:stable) {\n    .scroll-lock body {\n        overflow: hidden !important;\n    }\n}\n```\n\nImport from here to add it:\n```js\nimport '@substrate-system/util/css/scroll'\n```\n\n#### example\n\n```js\nimport {\n    lockBodyScrolling,\n    unlockBodyScrolling\n} from '@substrate-system/util/scroll'\n\n// stop scroll\nlockBodyScrolling(document.body)\n\n// ...sometime in the future...\nunlockBodyScrolling(document.body)\n```\n\n\n#### `lockBodyScrolling`\nStop scrolling.\n\n```ts\nfunction lockBodyScrolling (lockingEl:HTMLElement):void\n```\n\n#### `unlockBodyScrolling`\nResume scrolling.\n\n```ts\nfunction unlockBodyScrolling (lockingEl:HTMLElement):void\n```\n\n\n### Constants\n\nVarious special characters.\n\n```js\nconst EM_DASH = '\\u2014'\nconst EN_DASH = '\\u2013'\nconst NBSP = '\\u00A0'\nconst PETABYTE = (2 ** 50)\nconst TERABYTE = (2 ** 40)\nconst GIGABYTE = (2 ** 30)\nconst MEGABYTE = (2 ** 20)\nconst KILOBYTE = (2 ** 10)\nconst ELLIPSIS = '\\u2026'\nconst BULLET = '\\u2022'\n```\n\n```js\nimport { ELLIPSIS } from '@substrate-system/util/constants'\n\nelement.textContent = `${ELLIPSIS} something dramatic ${ELLIPSIS}`\n```\n\n### HTML classes\nCreate a new class name string by concattenating the given input.\n\n```js\nimport { classes } from '@substrate-system/util/classes'\nconst className = classes('hello', '123', '100')\n// =\u003e 'hello 123 100'\n```\n\n### `self`\nThe [`self`](https://developer.mozilla.org/en-US/docs/Web/API/Window/self)\nobject for Node.\n\n```js\nimport { self } from '@substrate-system/util/node'\n```\n\n### `humanFilesize`\nTake the number of bytes, return a string abbreviated to common sizes\n(megabyte, kilobyte, etc).\n\n#### Example\n```ts\nimport { humanFilesize } from '@substrate-system/util/filesize'\n\nconst size = humanFilesize(10_000)\nconsole.log(size)\n// =\u003e 9.8 KiB\n```\n\n#### API\n\n```ts\nfunction humanFilesize (\n    bytes:number,\n    si:boolean = false,\n    dp:number = 1\n):string\n```\n\n##### arguments\n\n* `bytes` the byte count\n* `si` -- use [SI](https://en.wikipedia.org/wiki/International_System_of_Units),\n  instead of [EIC](https://en.wikipedia.org/wiki/Binary_prefix) units\n  (default `false`)\n* `dp` is the number of decimal places to show.\n\n-------------------------------------------------------------------\n\n### Queue\n```js\nimport { Queue } from '@substrate-system/util/queue'\n```\n\nCreate a queue of promises. Promises will execute 1 at a time, in\nsequential order.\n\n```ts\nclass Queue\u003cT\u003e {\n    add (createP:()=\u003ePromise\u003cT\u003e):Promise\u003cT|void\u003e\n}\n```\n\n#### `queue.add`\nTake a function that returns a promise. Return a promise that will resolve when\nthe created promise resolves.\n\n```ts\nadd (createP:()=\u003ePromise\u003cT\u003e):Promise\u003cT|void\u003e\n```\n\n\u003e [!NOTE]  \n\u003e This will resolve promises in the order they were added to the queue.\n\n#### example\n\n```ts\nimport { test } from '@substrate-system/tapzero'\nimport { Queue } from '@substrate-system/util'\n\ntest('queue of 3 items', t =\u003e {\n    const q = new Queue\u003cstring\u003e()\n\n    // [p1, p2, p3]\n    const returned = [false, false, false]\n\n    const p1 = q.add(() =\u003e {\n        return new Promise\u003cstring\u003e(resolve =\u003e {\n            setTimeout(() =\u003e resolve('p1'), 300)\n        })\n    })\n\n    const p2 = q.add(() =\u003e {\n        return new Promise\u003cstring\u003e(resolve =\u003e {\n            setTimeout(() =\u003e resolve('p2'), 200)\n        })\n    })\n\n    const p3 = q.add(() =\u003e {\n        return new Promise\u003cstring\u003e(resolve =\u003e {\n            setTimeout(() =\u003e resolve('p3'), 100)\n        })\n    })\n\n    // p1 takes the longest\n    p1.then((value) =\u003e {\n        t.equal(value, 'p1', '\"p1\" string is ok')\n        returned[0] = true\n        t.ok(!returned[2], 'p2 should not have returned yet')\n        t.ok(!returned[1], 'p1 should not have returned yet')\n    })\n\n    p2.then(value =\u003e {\n        t.equal(value, 'p2', 'should get string \"p2\"')\n        returned[1] = true\n        t.ok(returned[0], 'should have p1 b/c it was added first')\n        t.ok(!returned[2], 'should not have 3 yet b/c it was addded last')\n    })\n\n    // p3 is the fastest\n    p3.then(value =\u003e {\n        t.equal(value, 'p3', 'should get string \"p3\"')\n        returned[2] = true\n        t.ok(returned[0], 'should have p1 because it was added first')\n        t.ok(returned[1], 'should have p2 because it was added next')\n    })\n\n    // return 3 so the test knows when to end,\n    // because they resolve in order,\n    // even though the ms are backwards\n    return p3\n})\n```\n\n------------------------------------------------------------------\n\n### `sleep`\nImport sleep from here to reduce duplication.\n\n```ts\nfunction sleep (ms?:number):Promise\u003cvoid\u003e\n```\n\n```js\nimport { sleep } from '@substrate-system/util'\n\nawait sleep(500)  // 1/2 second\n```\n\n### `isEmailValid(maybeEmail:string)`\nValidate an email address.\n\n```ts\nfunction isEmailValid (maybeEmail:string):boolean\n```\n\n#### example\n\n```js\nimport { isEmailValid } from '@substrate-system/util/email'\n\nisEmailValid('aaa@bbb.com')\n// =\u003e true\n```\n\n------------------------------------------------------------------\n\n### `parseForm`\nParse a form and return a plain object. If a form control with the same name\nappears more than once, the property will be converted to an array.\n\n```ts\nfunction parseForm (form:HTMLFormElement):Record\u003cstring, unknown\u003e\n```\n\n### `attributesToString` \nTake an array of attributes, and transform them into a string format. This can\nbe useful for creating\n[web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components).\n\n```ts\nfunction attributesToString (attrs:Attr[]):string {\n```\n\n#### example\n```ts\nimport { attributesToString } from '@substrate-system/util'\n\nconst el = document.getElementById('example')\nconst str = attributesToString(Array.from(el!.attributes))\nconsole.log(str)\n// =\u003e 'type=\"text\" id=\"example\" required'\n```\n\n------------------------------------------------------------------\n\n### `setAttributes`\nSet the given attributes from an object. Will handle boolean attributes\nlike `required`.\n\n```ts\nfunction setAttributes (el:HTMLElement, attrs:Record\u003cstring, string|boolean\u003e)\n```\n\n```ts\nimport { attributesToString, setAttributes } from '@substrate-system/util'\n\nconst input = document.getElementById('test') as HTMLInputElement\n\nsetAttributes(input, {\n    id: 'test',\n    required: true,\n    name: 'fooo',\n    class: 'testing'\n})\n\nconsole.log(attributesToString(Array.from(input.attributes)))\n// =\u003e 'id=\"test\" class=\"testing\" name=\"fooo\" required',\n```\n\n------------------------------------------------------------------\n\n### `attributesAsObject` \nReturn an object of `{ key: value }` from an array of attributes. If an\nattribute is a boolean value, then it will be `{ name: true }`.\n\n```ts\nfunction attributesAsObject (attrs:Attr[]):Record\u003cstring, string|true\u003e\n```\n\n```ts\nimport { attributesAsObject } from '@substrate-system/util'\n\nconst el = document.querySelector('input')\nconst attrs = Array.from(el!.attributes)\nconst obj = attributesAsObject(attrs)\nconsole.log(obj)\n// =\u003e {\n//   \"type\": \"text\",\n//   \"required\": true,\n//   \"name\": \"example\",\n//   \"foo\": \"bar\"\n// }\n```\n\n------------------------------------------------------------------\n\n### `objectToString`\nTake an object, as from `attributesAsObject`, and stringify it for use in HTML.\n\n```ts\nfunction objectToString (obj:Record\u003cstring, string|true\u003e):string\n```\n\n```ts\nimport { objectToString } from '@substrate-system/util'\n\nconst obj =  {\n    type: \"text\",\n    required: true,\n    name: \"example\",\n    foo: \"bar\"\n}\nconst str = objectToString(obj)\nconsole.log(str)\n// =\u003e 'type=\"text\" required name=\"example\" foo=\"bar\"'\n```\n\n### `CONSTANTS`\nExpose unicode characters.\n\n```ts\nimport * as CONSTANTS from '@substrate-system/util/CONSTANTS'\n```\n\n```ts\n// CONSTANTS.ts\nexport const EM_DASH = '\\u2014'\nexport const EN_DASH = '\\u2013'\nexport const NBSP = '\\u00A0'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsubstrate-system%2Futil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsubstrate-system%2Futil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsubstrate-system%2Futil/lists"}