Docs update
Some checks failed
Build / Build NPM Project (push) Failing after 28s
Build / Tag Version (push) Has been skipped

This commit is contained in:
Zakary Timson 2024-09-22 02:38:13 -04:00
parent a0f0699a85
commit 3896949fc1
15 changed files with 984 additions and 110 deletions

5
.gitmodules vendored Normal file
View File

@ -0,0 +1,5 @@
[submodule "docs"]
path = docs
url = git@git.zakscode.com:ztimson/utils.wiki.git
branch = master
ignore = any

90
README.md Normal file
View File

@ -0,0 +1,90 @@
<!-- Header -->
<div id="top" align="center">
<br />
<!-- Logo -->
<img src="https://git.zakscode.com/repo-avatars/77dbab78e5fb3302dc9e751c4d6315a64df8d3a714d5cd5719b0dc39e6619119" alt="Logo" width="200" height="200">
<!-- Title -->
### @ztimson/utils
<!-- Description -->
Javascript/Typescript Utilities
<!-- Repo badges -->
[![Version](https://img.shields.io/badge/dynamic/json.svg?label=Version&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/utils/tags&query=$[0].name)](https://git.zakscode.com/ztimson/utils/tags)
[![Pull Requests](https://img.shields.io/badge/dynamic/json.svg?label=Pull%20Requests&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/utils&query=open_pr_counter)](https://git.zakscode.com/ztimson/utils/pulls)
[![Issues](https://img.shields.io/badge/dynamic/json.svg?label=Issues&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/utils&query=open_issues_count)](https://git.zakscode.com/ztimson/utils/issues)
<!-- Links -->
---
<div>
<a href="https://git.zakscode.com/ztimson/persist/releases" target="_blank">Release Notes</a>
<a href="https://git.zakscode.com/ztimson/persist/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
<a href="https://git.zakscode.com/ztimson/persist/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
</div>
---
</div>
## Table of Contents
- [@ztimson/utils](#top)
- [About](#about)
- [Built With](#built-with)
- [Setup](#setup)
- [Production](#production)
- [Development](#development)
- [Documentation](https://git.zakscode.com/ztimson/utils/wiki)
- [License](#license)
## About
A collection of utilities to make life a little easier
### Built With
[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://typescriptlang.org/)
## Setup
<details>
<summary>
<h3 id="production" style="display: inline">
Production
</h3>
</summary>
#### Prerequisites
- [Node.js](https://nodejs.org/en/download)
#### Instructions
1. Install persist: `npm i @ztimosn/utils`
</details>
<details>
<summary>
<h3 id="development" style="display: inline">
Development
</h3>
</summary>
#### Prerequisites
- [Node.js](https://nodejs.org/en/download)
#### Instructions
1. Install the dependencies: `npm i`
2. Build library: `npm build`
3. Run unit tests: `npm test`
</details>
## Documentation
[Available Here](https://git.zakscode.com/ztimson/utils/wiki)
## License
Copyright © 2023 Zakary Timson | Available under MIT Licensing
See the [license](_media/LICENSE) for more information.

1
docs Submodule

@ -0,0 +1 @@
Subproject commit dbf5d8cd076d8ddbf4e4b1b1e0c0c301270303dd

598
package-lock.json generated
View File

@ -1,18 +1,20 @@
{
"name": "@ztimson/utils",
"version": "0.15.3",
"version": "0.15.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@ztimson/utils",
"version": "0.15.3",
"version": "0.15.6",
"license": "MIT",
"devDependencies": {
"@types/jest": "^29.5.12",
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"ts-jest": "^29.1.2",
"typedoc": "^0.26.7",
"typedoc-plugin-markdown": "^4.2.7",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vite-plugin-dts": "^3.7.2"
@ -1732,6 +1734,57 @@
"string-argv": "~0.3.1"
}
},
"node_modules/@shikijs/core": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.18.0.tgz",
"integrity": "sha512-VK4BNVCd2leY62Nm2JjyxtRLkyrZT/tv104O81eyaCjHq4Adceq2uJVFJJAIof6lT1mBwZrEo2qT/T+grv3MQQ==",
"dev": true,
"dependencies": {
"@shikijs/engine-javascript": "1.18.0",
"@shikijs/engine-oniguruma": "1.18.0",
"@shikijs/types": "1.18.0",
"@shikijs/vscode-textmate": "^9.2.2",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.3"
}
},
"node_modules/@shikijs/engine-javascript": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.18.0.tgz",
"integrity": "sha512-qoP/aO/ATNwYAUw1YMdaip/YVEstMZEgrwhePm83Ll9OeQPuxDZd48szZR8oSQNQBT8m8UlWxZv8EA3lFuyI5A==",
"dev": true,
"dependencies": {
"@shikijs/types": "1.18.0",
"@shikijs/vscode-textmate": "^9.2.2",
"oniguruma-to-js": "0.4.3"
}
},
"node_modules/@shikijs/engine-oniguruma": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.18.0.tgz",
"integrity": "sha512-B9u0ZKI/cud+TcmF8Chyh+R4V5qQVvyDOqXC2l2a4x73PBSBc6sZ0JRAX3eqyJswqir6ktwApUUGBYePdKnMJg==",
"dev": true,
"dependencies": {
"@shikijs/types": "1.18.0",
"@shikijs/vscode-textmate": "^9.2.2"
}
},
"node_modules/@shikijs/types": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.18.0.tgz",
"integrity": "sha512-O9N36UEaGGrxv1yUrN2nye7gDLG5Uq0/c1LyfmxsvzNPqlHzWo9DI0A4+fhW2y3bGKuQu/fwS7EPdKJJCowcVA==",
"dev": true,
"dependencies": {
"@shikijs/vscode-textmate": "^9.2.2",
"@types/hast": "^3.0.4"
}
},
"node_modules/@shikijs/vscode-textmate": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz",
"integrity": "sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==",
"dev": true
},
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@ -1818,6 +1871,15 @@
"@types/node": "*"
}
},
"node_modules/@types/hast": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
"dev": true,
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@ -1852,6 +1914,15 @@
"pretty-format": "^29.0.0"
}
},
"node_modules/@types/mdast": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
"integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
"dev": true,
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/node": {
"version": "18.19.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.14.tgz",
@ -1867,6 +1938,12 @@
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
"dev": true
},
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"dev": true
},
"node_modules/@types/yargs": {
"version": "17.0.32",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@ -1882,6 +1959,12 @@
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"dev": true
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
"node_modules/@volar/language-core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz",
@ -2281,6 +2364,16 @@
}
]
},
"node_modules/ccount": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@ -2306,6 +2399,26 @@
"node": ">=10"
}
},
"node_modules/character-entities-html4": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
"integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-entities-legacy": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
"integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@ -2384,6 +2497,16 @@
"node": ">=0.1.90"
}
},
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
"integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
@ -2493,6 +2616,15 @@
"node": ">=0.10.0"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/detect-newline": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@ -2502,6 +2634,19 @@
"node": ">=8"
}
},
"node_modules/devlop": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
"dev": true,
"dependencies": {
"dequal": "^2.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
@ -2885,6 +3030,42 @@
"node": ">= 0.4"
}
},
"node_modules/hast-util-to-html": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz",
"integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==",
"dev": true,
"dependencies": {
"@types/hast": "^3.0.0",
"@types/unist": "^3.0.0",
"ccount": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"hast-util-whitespace": "^3.0.0",
"html-void-elements": "^3.0.0",
"mdast-util-to-hast": "^13.0.0",
"property-information": "^6.0.0",
"space-separated-tokens": "^2.0.0",
"stringify-entities": "^4.0.0",
"zwitch": "^2.0.4"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-whitespace": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
"integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
"dev": true,
"dependencies": {
"@types/hast": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -2900,6 +3081,16 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"node_modules/html-void-elements": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
"integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@ -3761,6 +3952,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"dev": true,
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@ -3809,6 +4009,12 @@
"node": ">=10"
}
},
"node_modules/lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"node_modules/make-dir": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
@ -3839,12 +4045,151 @@
"tmpl": "1.0.5"
}
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
},
"bin": {
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/markdown-it/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/mdast-util-to-hast": {
"version": "13.2.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
"integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
"dev": true,
"dependencies": {
"@types/hast": "^3.0.0",
"@types/mdast": "^4.0.0",
"@ungap/structured-clone": "^1.0.0",
"devlop": "^1.0.0",
"micromark-util-sanitize-uri": "^2.0.0",
"trim-lines": "^3.0.0",
"unist-util-position": "^5.0.0",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"dev": true
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
},
"node_modules/micromark-util-character": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
"integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
"dev": true,
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
],
"dependencies": {
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
}
},
"node_modules/micromark-util-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz",
"integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==",
"dev": true,
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
]
},
"node_modules/micromark-util-sanitize-uri": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz",
"integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==",
"dev": true,
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
],
"dependencies": {
"micromark-util-character": "^2.0.0",
"micromark-util-encode": "^2.0.0",
"micromark-util-symbol": "^2.0.0"
}
},
"node_modules/micromark-util-symbol": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
"integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
"dev": true,
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
]
},
"node_modules/micromark-util-types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz",
"integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==",
"dev": true,
"funding": [
{
"type": "GitHub Sponsors",
"url": "https://github.com/sponsors/unifiedjs"
},
{
"type": "OpenCollective",
"url": "https://opencollective.com/unified"
}
]
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
@ -3868,9 +4213,9 @@
}
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@ -3987,6 +4332,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/oniguruma-to-js": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz",
"integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==",
"dev": true,
"dependencies": {
"regex": "^4.3.2"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -4201,6 +4558,16 @@
"node": ">= 6"
}
},
"node_modules/property-information": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
"integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -4210,6 +4577,15 @@
"node": ">=6"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/pure-rand": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz",
@ -4232,6 +4608,12 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
"node_modules/regex": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/regex/-/regex-4.3.2.tgz",
"integrity": "sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw==",
"dev": true
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -4358,6 +4740,20 @@
"node": ">=8"
}
},
"node_modules/shiki": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-1.18.0.tgz",
"integrity": "sha512-8jo7tOXr96h9PBQmOHVrltnETn1honZZY76YA79MHheGQg55jBvbm9dtU+MI5pjC5NJCFuA6rvVTLVeSW5cE4A==",
"dev": true,
"dependencies": {
"@shikijs/core": "1.18.0",
"@shikijs/engine-javascript": "1.18.0",
"@shikijs/engine-oniguruma": "1.18.0",
"@shikijs/types": "1.18.0",
"@shikijs/vscode-textmate": "^9.2.2",
"@types/hast": "^3.0.4"
}
},
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@ -4407,6 +4803,16 @@
"source-map": "^0.6.0"
}
},
"node_modules/space-separated-tokens": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
"integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -4461,6 +4867,20 @@
"node": ">=8"
}
},
"node_modules/stringify-entities": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
"integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
"dev": true,
"dependencies": {
"character-entities-html4": "^2.0.0",
"character-entities-legacy": "^3.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@ -4590,6 +5010,16 @@
"node": ">=8.0"
}
},
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
"integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/ts-jest": {
"version": "29.1.2",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz",
@ -4654,6 +5084,40 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typedoc": {
"version": "0.26.7",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.7.tgz",
"integrity": "sha512-gUeI/Wk99vjXXMi8kanwzyhmeFEGv1LTdTQsiyIsmSYsBebvFxhbcyAx7Zjo4cMbpLGxM4Uz3jVIjksu/I2v6Q==",
"dev": true,
"dependencies": {
"lunr": "^2.3.9",
"markdown-it": "^14.1.0",
"minimatch": "^9.0.5",
"shiki": "^1.16.2",
"yaml": "^2.5.1"
},
"bin": {
"typedoc": "bin/typedoc"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x"
}
},
"node_modules/typedoc-plugin-markdown": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.2.7.tgz",
"integrity": "sha512-bLsQdweSm48P9j6kGqQ3/4GCH5zu2EnURSkkxqirNc+uVFE9YK825ogDw+WbNkRHIV6eZK/1U43gT7YfglyYOg==",
"dev": true,
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"typedoc": "0.26.x"
}
},
"node_modules/typescript": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
@ -4667,12 +5131,86 @@
"node": ">=14.17"
}
},
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"dev": true
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/unist-util-is": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
"integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
"dev": true,
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-position": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
"integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
"dev": true,
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-stringify-position": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
"integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"dev": true,
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-visit": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
"integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
"dev": true,
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-is": "^6.0.0",
"unist-util-visit-parents": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-visit-parents": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
"integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
"dev": true,
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-is": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@ -4753,6 +5291,34 @@
"node": ">= 0.10"
}
},
"node_modules/vfile": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
"integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
"dev": true,
"dependencies": {
"@types/unist": "^3.0.0",
"vfile-message": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/vfile-message": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
"integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
"dev": true,
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-stringify-position": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/vite": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
@ -4942,6 +5508,18 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/yaml": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
"integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
"dev": true,
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
@ -5000,6 +5578,16 @@
"optionalDependencies": {
"commander": "^9.4.1"
}
},
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@ztimson/utils",
"version": "0.15.6",
"version": "0.16.0",
"description": "Utility library",
"author": "Zak Timson",
"license": "MIT",
@ -21,6 +21,7 @@
},
"scripts": {
"build": "npx tsc && npx vite build",
"docs": "typedoc --plugin typedoc-plugin-markdown --hidePageHeader --out ./docs --entryPoints src/**/*.ts",
"test": "npx jest",
"test:coverage": "npx jest --coverage",
"watch": "npx vite build --watch"
@ -30,6 +31,8 @@
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"ts-jest": "^29.1.2",
"typedoc": "^0.26.7",
"typedoc-plugin-markdown": "^4.2.7",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vite-plugin-dts": "^3.7.2"

View File

@ -1,10 +1,32 @@
import {dotNotation, isEqual} from './objects';
/**
* Only add element to array if it isn't already included
*
* @example
* ```js
* const arr = addUnique([1, 2, 3], 3);
* console.log(arr); // Output: [1, 2, 3]
* ```
*
* @param {T[]} array Target array element will be added to
* @param {T} el Unique element to add
* @return {T[]} Array with element if it was unique
* @deprecated Use ASet to create unique arrays
*/
export function addUnique<T>(array: T[], el: T): T[] {
if(array.indexOf(el) === -1) array.push(el);
return array;
}
/**
* Find all unique elements in arrays
*
* @param {any[]} a First array to compare
* @param {any[]} b Second array to compare
* @return {any[]} Unique elements
* @deprecated Use ASet to perform Set operations on arrays
*/
export function arrayDiff(a: any[], b: any[]): any[] {
return makeUnique([
...a.filter(v1 => !b.includes((v2: any) => isEqual(v1, v2))),
@ -33,6 +55,25 @@ export function caseInsensitiveSort(prop: string) {
};
}
/**
* Shorthand to find objects with a property value
*
* @example
* ```js
* const found = [
* {name: 'Batman'},
* {name: 'Superman'},
* ].filter(findByProp('name', 'Batman'));
* ```
*
* @param {string} prop Property to compare (Dot nation supported)
* @param value Value property must have
* @return {(v: any) => boolean} Function used by `filter` or `find`
*/
export function findByProp(prop: string, value: any) {
return (v: any) => isEqual(dotNotation(v, prop), value);
}
/**
* Recursively flatten nested arrays
*
@ -91,10 +132,13 @@ export function sortByProp(prop: string, reverse = false) {
};
}
export function findByProp(prop: string, value: any) {
return (v: any) => isEqual(v[prop], value);
}
/**
* Make sure every element in array is unique
*
* @param {any[]} arr Array that will be filtered in place
* @return {any[]} Original array
* @deprecated Please use ASet to create a guaranteed unique array
*/
export function makeUnique(arr: any[]) {
for(let i = arr.length - 1; i >= 0; i--) {
if(arr.slice(0, i).find(n => isEqual(n, arr[i]))) arr.splice(i, 1);
@ -103,7 +147,8 @@ export function makeUnique(arr: any[]) {
}
/**
* Make sure value is an array, if it isn't wrap it in one.
* Make sure value is an array, if it isn't wrap it in one
*
* @param {T[] | T} value Value that should be an array
* @returns {T[]} Value in an array
*/

View File

@ -1,21 +1,39 @@
import {deepCopy, JSONAttemptParse} from './objects.ts';
import {JSONAttemptParse} from './objects.ts';
import {PromiseProgress} from './promise-progress';
export function download(href: any, name: string) {
/**
* Download a file from a URL
*
* @param href URL that will be downloaded
* @param {string} name Override download name
*/
export function download(href: any, name?: string) {
const a = document.createElement('a');
a.href = href;
a.download = name;
a.download = name || href.split('/').pop();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
/**
* Download blob as a file
*
* @param {Blob} blob File as a blob
* @param {string} name Name blob will be downloaded as
*/
export function downloadBlob(blob: Blob, name: string) {
const url = URL.createObjectURL(blob);
download(url, name);
URL.revokeObjectURL(url);
}
/**
* Open filebrowser & return selected file
*
* @param {{accept?: string, multiple?: boolean}} options accept - selectable mimetypes, multiple - Allow selecting more than 1 file
* @return {Promise<File[]>} Array of selected files
*/
export function fileBrowser(options: {accept?: string, multiple?: boolean} = {}): Promise<File[]> {
return new Promise(res => {
const input = document.createElement('input');
@ -32,6 +50,25 @@ export function fileBrowser(options: {accept?: string, multiple?: boolean} = {})
});
}
/**
* Create timestamp intended for filenames from a date
*
* @param {string} name Name of file, `{{TIMESTAMP}}` will be replaced
* @param {Date | number | string} date Date to use for timestamp
* @return {string} Interpolated filename, or the raw timestamp if name was omitted
*/
export function timestampFilename(name?: string, date: Date | number | string = new Date()) {
if(typeof date == 'number' || typeof date == 'string') date = new Date(date);
const timestamp = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}_${date.getHours().toString().padStart(2, '0')}-${date.getMinutes().toString().padStart(2, '0')}-${date.getSeconds().toString().padStart(2, '0')}`;
return name ? name.replace('{{TIMESTAMP}}', timestamp) : timestamp;
}
/**
* Upload file to URL with progress callback using PromiseProgress
*
* @param {{url: string, files: File[], headers?: {[p: string]: string}, withCredentials?: boolean}} options
* @return {PromiseProgress<T>} Promise of request with `onProgress` callback
*/
export function uploadWithProgress<T>(options: {
url: string;
files: File[];

View File

@ -11,3 +11,4 @@ export * from './objects';
export * from './promise-progress';
export * from './string';
export * from './time';
export * from './types';

View File

@ -11,42 +11,3 @@ export function gravatar(email: string, def='mp') {
if(!email) return '';
return `https://www.gravatar.com/avatar/${md5(email)}?d=${def}`;
}
/** Parts of a URL */
export type ParsedUrl = {
protocol?: string,
subdomain?: string,
domain: string,
host: string,
port?: number,
path?: string,
query?: {[name: string]: string}
fragment?: string
}
/**
*
* @param {string} url
* @returns {RegExpExecArray}
*/
export function urlParser(url: string): ParsedUrl {
const processed = new RegExp(
'(?:(?<protocol>[\\w\\d]+)\\:\\/\\/)?(?:(?<user>.+)\\@)?(?<host>(?<domain>[^:\\/\\?#@\\n]+)(?:\\:(?<port>\\d*))?)(?<path>\\/.*?)?(?:\\?(?<query>.*?))?(?:#(?<fragment>.*?))?$',
'gm').exec(url);
const groups: ParsedUrl = <any>processed?.groups ?? {};
const domains = groups.domain.split('.');
if(groups['port'] != null) groups.port = Number(groups.port);
if(domains.length > 2) {
groups.domain = domains.splice(-2, 2).join('.');
groups.subdomain = domains.join('.');
}
if(groups.query) {
const split = (<any>groups.query).split('&'), query: any = {};
split.forEach((q: any) => {
const [key, val] = q.split('=');
query[key] = val;
});
groups.query = query;
}
return groups;
}

View File

@ -1,5 +1,5 @@
/**
* Removes any null values from an object in-place
* Removes any null values from an object in-place
*
* @example
* ```ts
@ -27,12 +27,12 @@ export function clean<T>(obj: T, undefinedOnly = false): Partial<T> {
* Create a deep copy of an object (vs. a shallow copy of references)
*
* Should be replaced by `structuredClone` once released.
*
* @param {T} value Object to copy
* @returns {T} Type
* @deprecated Please use `structuredClone`
*/
export function deepCopy<T>(value: T): T {
return JSON.parse(JSON.stringify(value));
return structuredClone(value);
}
/**
@ -91,8 +91,28 @@ export function dotNotation<T>(obj: any, prop: string, set?: T): T | undefined {
}, obj);
}
/**
* Recursively flatten a nested object, while maintaining key structure.
* Convert object into URL encoded query string
*
* @example
* ```js
* const query = encodeQuery({page: 1, size: 20});
* console.log(query); // Output: "page=1&size=20"
* ```
*
* @param {any} data - data to convert
* @returns {string} - Encoded form data
*/
export function encodeQuery(data: any): string {
return Object.entries(data).map(([key, value]) =>
encodeURIComponent(key) + '=' + encodeURIComponent(<any>value)
).join('&');
}
/**
* Recursively flatten a nested object, while maintaining key structure
*
* @example
* ```ts
@ -121,6 +141,7 @@ export function flattenObj(obj: any, parent?: any, result: any = {}) {
/**
* Convert object to FormData
*
* @param target - Object to convert
* @return {FormData} - Form object
*/
@ -173,6 +194,12 @@ export function isEqual(a: any, b: any): boolean {
return Object.keys(a).every(key => isEqual(a[key], b[key]));
}
/**
* Experimental: Combine multiple object prototypes into one
*
* @param target Object that will have prototypes added
* @param {any[]} constructors Additionally prototypes that should be merged into target
*/
export function mixin(target: any, constructors: any[]) {
constructors.forEach(c => {
Object.getOwnPropertyNames(c.prototype).forEach((name) => {
@ -186,30 +213,31 @@ export function mixin(target: any, constructors: any[]) {
});
}
/**
* Parse JSON but return the original string if it fails
*
* @param {string} json JSON string to parse
* @return {string | T} Object if successful, original string otherwise
*/
export function JSONAttemptParse<T>(json: string): T | string {
try { return JSON.parse(json); }
catch { return json; }
}
export function JSONSanitized(obj: any, space?: number) {
/**
* Convert an object to a JSON string avoiding any circular references.
*
* @param obj Object to convert to JSON
* @param {number} space Format the JSON with spaces
* @return {string} JSON string
*/
export function JSONSanitize(obj: any, space?: number): string {
let cache: any[] = [];
return JSON.parse(JSON.stringify(obj, (key, value) => {
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.includes(value)) return;
cache.push(value);
}
return value;
}, space));
}
/**
* Convert object into URL encoded string
*
* @param {any} data - data to convert
* @returns {string} - Encoded form data
*/
export function urlEncode(data: any): string {
return Object.entries(data).map(([key, value]) =>
encodeURIComponent(key) + '=' + encodeURIComponent(<any>value)
).join('&');
}, space);
}

View File

@ -1,5 +1,25 @@
export type ProgressCallback = (progress: number) => any;
/**
* A promise that fires the `onProgress` callback on incremental progress
*
* @example
* ```js
* const promise = new Promise((resolve, reject, progress) => {
* const max = 10;
* for(let i = 0; i < max; i++) progress(i / max);
* resolve(1);
* });
*
* console.log(promise.progress);
*
* promise.onProgress(console.log)
* .then(console.log)
* .catch(console.error)
* .finally(...);
*
* ```
*/
export class PromiseProgress<T> extends Promise<T> {
private listeners: ProgressCallback[] = [];

View File

@ -1,28 +1,10 @@
export function countChars(text: string, pattern: RegExp) {
return text.length - text.replaceAll(pattern, '').length;
}
export function createHex(length: number) {
return Array(length).fill(null).map(() => Math.round(Math.random() * 0xF).toString(16)).join('');
}
export function formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
}
/**
* String of all letters
*
*/
const LETTER_LIST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
/**
* String of all numbers
*
*/
const NUMBER_LIST = '0123456789';
@ -36,6 +18,37 @@ const SYMBOL_LIST = '~`!@#$%^&*()_-+={[}]|\\:;"\'<,>.?/';
*/
const CHAR_LIST = LETTER_LIST + NUMBER_LIST + SYMBOL_LIST;
/**
* Generate a random hexadecimal value
*
* @param {number} length Number of hexadecimal place values
* @return {string} Hexadecimal number as a string
*/
export function randomHex(length: number) {
return Array(length).fill(null).map(() => Math.round(Math.random() * 0xF).toString(16)).join('');
}
/**
* Convert number of bytes into a human-readable size
*
* @param {number} bytes Number of bytes
* @param {number} decimals Decimal places to preserve
* @return {string} Formated size
*/
export function formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
}
/**
* Extract numbers from a string & create a formated phone number: +1 (123) 456-7890
*
* @param {string} number String that will be parsed for numbers
* @return {string} Formated phone number
*/
export function formatPhoneNumber(number: string) {
const parts = /(\+?1)?.*?(\d{3}).*?(\d{3}).*?(\d{4})/g.exec(number);
if(!parts) throw new Error(`Number cannot be parsed: ${number}`);
@ -46,7 +59,7 @@ export function formatPhoneNumber(number: string) {
* Insert a string into another string at a given position
*
* @example
* ```
* ```js
* console.log(insertAt('Hello world!', ' glorious', 5);
* // Output: Hello glorious world!
* ```
@ -60,12 +73,26 @@ export function insertAt(target: string, str: string, index: number): String {
return `${target.slice(0, index)}${str}${target.slice(index + 1)}`;
}
export function pad(text: any, length: number, char: string, start = true) {
const t = text.toString();
const l = length - t.length;
if(l <= 0) return t;
const padding = Array(~~(l / char.length)).fill(char).join('');
return start ? padding + t : t + padding;
/**
* Add padding to string
*
* @example
* ```js
* const now = new Date();
* const padded = now.getHours() + ':' + pad(now.getMinutes(), 2, '0');
* console.log(padded); // Output: "2:05"
* ```
*
* @param text Text that will be padded
* @param {number} length Target length
* @param {string} char Character to use as padding, defaults to space
* @param {boolean} start Will pad start of text if true, or the end if false
* @return {string} Padded string
* @deprecated Please use `String.padStart` & `String.padEnd`
*/
export function pad(text: any, length: number, char: string = ' ', start = true) {
if(start) return text.toString().padStart(length, char);
return text.toString().padEnd(length, char);
}
/**
@ -149,8 +176,50 @@ export function matchAll(value: string, regex: RegExp | string): RegExpExecArray
return ret;
}
/** Parts of a URL */
export type ParsedUrl = {
protocol?: string,
subdomain?: string,
domain: string,
host: string,
port?: number,
path?: string,
query?: {[name: string]: string}
fragment?: string
}
/**
* Break a URL string into its parts for easy parsing
*
* @param {string} url URL string that will be parsed
* @returns {RegExpExecArray} Parts of URL
*/
export function parseUrl(url: string): ParsedUrl {
const processed = new RegExp(
'(?:(?<protocol>[\\w\\d]+)\\:\\/\\/)?(?:(?<user>.+)\\@)?(?<host>(?<domain>[^:\\/\\?#@\\n]+)(?:\\:(?<port>\\d*))?)(?<path>\\/.*?)?(?:\\?(?<query>.*?))?(?:#(?<fragment>.*?))?$',
'gm').exec(url);
const groups: ParsedUrl = <any>processed?.groups ?? {};
const domains = groups.domain.split('.');
if(groups['port'] != null) groups.port = Number(groups.port);
if(domains.length > 2) {
groups.domain = domains.splice(-2, 2).join('.');
groups.subdomain = domains.join('.');
}
if(groups.query) {
const split = (<any>groups.query).split('&'), query: any = {};
split.forEach((q: any) => {
const [key, val] = q.split('=');
query[key] = val;
});
groups.query = query;
}
return groups;
}
/**
* Create MD5 hash using native javascript
*
* @param d String to hash
* @returns {string} Hashed string
*/

View File

@ -1,13 +1,17 @@
export function formatDate(date: Date | number | string) {
const d = date instanceof Date ? date : new Date(date);
return new Intl.DateTimeFormat("en-us", {
weekday: "long",
month: "short",
day: "numeric",
hour: "numeric",
minute: "numeric",
hour12: true
}).format(d);
/**
* Return date formated highest to lowest: YYYY-MM-DD H:mm AM
*
* @param {Date | number | string} date Date or timestamp to convert to string
* @return {string} Formated date
*/
export function formatDate(date: Date | number | string): string {
if(typeof date == 'number' || typeof date == 'string') date = new Date(date);
let hours = date.getHours(), postfix = 'AM';
if(hours >= 12) {
if(hours > 12) hours -= 12;
postfix = 'PM';
} else if(hours == 0) hours = 12;
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}, ${hours}:${date.getMinutes().toString().padStart(2, '0')} ${postfix}`;
}
/**
@ -17,6 +21,7 @@ export function formatDate(date: Date | number | string) {
* ```js
* await sleep(1000) // Pause for 1 second
* ```
*
* @param {number} ms - Time to pause for in milliseconds
* @returns {Promise<unknown>} - Resolves promise when it's time to resume
*/
@ -33,6 +38,7 @@ export function sleep(ms: number): Promise<void> {
* setTimeout(() => wait = false, 1000);
* await sleepUntil(() => loading); // Won't continue until loading flag is false
* ```
*
* @param {() => boolean} fn Return true to continue
* @param {number} checkInterval Run function ever x milliseconds
* @return {Promise<void>} Callback when sleep is over

20
src/types.ts Normal file
View File

@ -0,0 +1,20 @@
/**
* Return keys on a type as an array of strings
*
* @example
* ```ts
* type Person = {
* firstName: string;
* lastName: string;
* age: number;
* }
*
* const keys = typeKeys<Person>();
* console.log(keys); // Output: ["firstName", "lastName", "age"]
* ```
*
* @return {Array<keyof T>} Available keys
*/
export function tyoeKeys<T extends object>() {
return Object.keys(<T>{}) as Array<keyof T>;
}

View File

@ -1,4 +1,4 @@
import {sleep, urlParser} from '../src';
import {sleep, parseUrl} from '../src';
describe('Miscellanies Utilities', () => {
describe('sleep', () => {
@ -12,7 +12,7 @@ describe('Miscellanies Utilities', () => {
describe('urlParser', () => {
test('localhost w/ port', () => {
const parsed = urlParser('http://localhost:4200/some/path?q1=test1&q2=test2#frag');
const parsed = parseUrl('http://localhost:4200/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('http');
expect(parsed.host).toStrictEqual('localhost:4200');
expect(parsed.domain).toStrictEqual('localhost');
@ -23,7 +23,7 @@ describe('Miscellanies Utilities', () => {
});
test('advanced URL', () => {
const parsed = urlParser('https://sub.domain.example.com/some/path?q1=test1&q2=test2#frag');
const parsed = parseUrl('https://sub.domain.example.com/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('https');
expect(parsed.host).toStrictEqual('sub.domain.example.com');
expect(parsed.domain).toStrictEqual('example.com');