@fuzdev/fuz_util 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/array.d.ts +15 -0
- package/dist/array.d.ts.map +1 -0
- package/dist/array.js +25 -0
- package/dist/async.d.ts +62 -0
- package/dist/async.d.ts.map +1 -0
- package/dist/async.js +147 -0
- package/dist/colors.d.ts +41 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +106 -0
- package/dist/counter.d.ts +7 -0
- package/dist/counter.d.ts.map +1 -0
- package/dist/counter.js +7 -0
- package/dist/deep_equal.d.ts +18 -0
- package/dist/deep_equal.d.ts.map +1 -0
- package/dist/deep_equal.js +152 -0
- package/dist/dom.d.ts +35 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +95 -0
- package/dist/error.d.ts +15 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +18 -0
- package/dist/fetch.d.ts +81 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +162 -0
- package/dist/fs.d.ts +34 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/fs.js +73 -0
- package/dist/function.d.ts +27 -0
- package/dist/function.d.ts.map +1 -0
- package/dist/function.js +21 -0
- package/dist/git.d.ts +132 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +288 -0
- package/dist/id.d.ts +18 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +18 -0
- package/dist/iterator.d.ts +5 -0
- package/dist/iterator.d.ts.map +1 -0
- package/dist/iterator.js +9 -0
- package/dist/json.d.ts +30 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +44 -0
- package/dist/library_json.d.ts +42 -0
- package/dist/library_json.d.ts.map +1 -0
- package/dist/library_json.js +76 -0
- package/dist/log.d.ts +188 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +393 -0
- package/dist/map.d.ts +12 -0
- package/dist/map.d.ts.map +1 -0
- package/dist/map.js +14 -0
- package/dist/maths.d.ts +85 -0
- package/dist/maths.d.ts.map +1 -0
- package/dist/maths.js +87 -0
- package/dist/object.d.ts +46 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/object.js +89 -0
- package/dist/package_json.d.ts +90 -0
- package/dist/package_json.d.ts.map +1 -0
- package/dist/package_json.js +112 -0
- package/dist/path.d.ts +63 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +83 -0
- package/dist/print.d.ts +52 -0
- package/dist/print.d.ts.map +1 -0
- package/dist/print.js +89 -0
- package/dist/process.d.ts +77 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +148 -0
- package/dist/random.d.ts +25 -0
- package/dist/random.d.ts.map +1 -0
- package/dist/random.js +35 -0
- package/dist/random_alea.d.ts +23 -0
- package/dist/random_alea.d.ts.map +1 -0
- package/dist/random_alea.js +95 -0
- package/dist/regexp.d.ts +12 -0
- package/dist/regexp.d.ts.map +1 -0
- package/dist/regexp.js +16 -0
- package/dist/result.d.ts +64 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +48 -0
- package/dist/source_json.d.ts +375 -0
- package/dist/source_json.d.ts.map +1 -0
- package/dist/source_json.js +189 -0
- package/dist/string.d.ts +51 -0
- package/dist/string.d.ts.map +1 -0
- package/dist/string.js +92 -0
- package/dist/throttle.d.ts +26 -0
- package/dist/throttle.d.ts.map +1 -0
- package/dist/throttle.js +53 -0
- package/dist/timings.d.ts +33 -0
- package/dist/timings.d.ts.map +1 -0
- package/dist/timings.js +75 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/url.d.ts +10 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +8 -0
- package/package.json +125 -0
- package/src/lib/array.ts +30 -0
- package/src/lib/async.ts +182 -0
- package/src/lib/colors.ts +132 -0
- package/src/lib/counter.ts +11 -0
- package/src/lib/deep_equal.ts +155 -0
- package/src/lib/dom.ts +108 -0
- package/src/lib/error.ts +22 -0
- package/src/lib/fetch.ts +231 -0
- package/src/lib/fs.ts +128 -0
- package/src/lib/function.ts +32 -0
- package/src/lib/git.ts +390 -0
- package/src/lib/id.ts +30 -0
- package/src/lib/iterator.ts +8 -0
- package/src/lib/json.ts +61 -0
- package/src/lib/library_json.ts +122 -0
- package/src/lib/log.ts +469 -0
- package/src/lib/map.ts +18 -0
- package/src/lib/maths.ts +91 -0
- package/src/lib/object.ts +110 -0
- package/src/lib/package_json.ts +135 -0
- package/src/lib/path.ts +137 -0
- package/src/lib/print.ts +111 -0
- package/src/lib/process.ts +207 -0
- package/src/lib/random.ts +48 -0
- package/src/lib/random_alea.ts +107 -0
- package/src/lib/regexp.ts +17 -0
- package/src/lib/result.ts +67 -0
- package/src/lib/source_json.ts +209 -0
- package/src/lib/string.ts +99 -0
- package/src/lib/throttle.ts +70 -0
- package/src/lib/timings.ts +93 -0
- package/src/lib/types.ts +99 -0
- package/src/lib/url.ts +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Ryan Atkinson <mail@ryanatkn.com> <https://ryanatkn.com/>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @ryanatkn/belt
|
|
2
|
+
|
|
3
|
+
[<img src="static/logo.svg" alt="a green sauropod wearing a brown utility belt" align="right" width="256" height="256">](https://belt.ryanatkn.com/)
|
|
4
|
+
|
|
5
|
+
> utility belt for JS 🦕 ancient not extinct
|
|
6
|
+
|
|
7
|
+
**[belt.ryanatkn.com](https://belt.ryanatkn.com)**
|
|
8
|
+
|
|
9
|
+
design:
|
|
10
|
+
|
|
11
|
+
- kitchen-sink utilities library - sorry, I wish it weren't so, JS made me do it
|
|
12
|
+
- two optional runtime dependencies on [`zod`](https://github.com/colinhacks/zod)
|
|
13
|
+
and [`esm-env`](https://github.com/benmccann/esm-env),
|
|
14
|
+
one optional type dependency on `@types/node`
|
|
15
|
+
- mix of JS module environments - browser-only, Node-only, universal
|
|
16
|
+
- mostly small pure functions
|
|
17
|
+
- all TypeScript, for styles and Svelte and SvelteKit
|
|
18
|
+
see <a href="https://github.com/fuz-dev/fuz">@ryanatkn/fuz</a>
|
|
19
|
+
- complements the modern web platform, drops legacy quickly
|
|
20
|
+
- kinda minimal in many ways but also not, treeshakes well
|
|
21
|
+
|
|
22
|
+
## usage
|
|
23
|
+
|
|
24
|
+
Install from [npm](https://www.npmjs.com/package/@ryanatkn/belt):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm i -D @ryanatkn/belt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Import modules at their full paths:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {type Result, unwrap} from '@ryanatkn/belt/result.js';
|
|
34
|
+
import {random_int} from '@ryanatkn/belt/random.js';
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`.ts` imports also work:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import {deep_equal} from '@ryanatkn/belt/deep_equal.ts';
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Docs at [belt.ryanatkn.com/docs](https://belt.ryanatkn.com/docs).
|
|
44
|
+
|
|
45
|
+
## build
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run build
|
|
49
|
+
# or
|
|
50
|
+
gro build
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## test
|
|
54
|
+
|
|
55
|
+
For more see [`uvu`](https://github.com/lukeed/uvu)
|
|
56
|
+
and [Gro's test docs](https://github.com/feltjs/gro/blob/main/src/docs/test.md).
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
gro test
|
|
60
|
+
gro test filepattern1 filepatternB
|
|
61
|
+
gro test -- uvu --forwarded_args 'to uvu'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## deploy
|
|
65
|
+
|
|
66
|
+
[Deploy](https://github.com/feltjs/gro/blob/main/src/docs/deploy.md)
|
|
67
|
+
(build, commit, and push) to the `deploy` branch, e.g. for GitHub Pages:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm run deploy
|
|
71
|
+
# or
|
|
72
|
+
gro deploy
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## credits 🐢<sub>🐢</sub><sub><sub>🐢</sub></sub>
|
|
76
|
+
|
|
77
|
+
My sister Lisa helped me with the logo -
|
|
78
|
+
[instagram.com/lisaeatkinson](https://www.instagram.com/lisaeatkinson/) -
|
|
79
|
+
she's a designer and currently looking for work
|
|
80
|
+
|
|
81
|
+
## license [🐦](https://wikipedia.org/wiki/Free_and_open-source_software)
|
|
82
|
+
|
|
83
|
+
[MIT](LICENSE)
|
package/dist/array.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ArrayElement } from './types.js';
|
|
2
|
+
export declare const EMPTY_ARRAY: Array<any>;
|
|
3
|
+
/** Converts `value` to an array if it's not already. */
|
|
4
|
+
export declare const to_array: <T>(value: T) => T extends ReadonlyArray<any> ? T : Array<T>;
|
|
5
|
+
/**
|
|
6
|
+
* Removes an element from `array` at `index` in an unordered manner.
|
|
7
|
+
* @mutates array swaps element at index with last element, then removes last element
|
|
8
|
+
*/
|
|
9
|
+
export declare const remove_unordered: (array: Array<any>, index: number) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Returns a function that returns the next item in the `array`
|
|
12
|
+
* in a linear sequence, looping back to index 0 when it reaches the end.
|
|
13
|
+
*/
|
|
14
|
+
export declare const to_next: <T extends ReadonlyArray<any>>(array: T) => (() => ArrayElement<T>);
|
|
15
|
+
//# sourceMappingURL=array.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"array.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/array.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAG7C,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,GAAG,CAA4B,CAAC;AAEhE,wDAAwD;AACxD,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CACxB,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,MAAM,KAAG,IAGnE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAG,CAAC,MAAM,YAAY,CAAC,CAAC,CAAC,CAOtF,CAAC"}
|
package/dist/array.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// TODO try to cange to readonly again, see if upstream errors are tolerably fixed
|
|
2
|
+
export const EMPTY_ARRAY = Object.freeze([]);
|
|
3
|
+
/** Converts `value` to an array if it's not already. */
|
|
4
|
+
export const to_array = (value) => Array.isArray(value) ? value : [value];
|
|
5
|
+
/**
|
|
6
|
+
* Removes an element from `array` at `index` in an unordered manner.
|
|
7
|
+
* @mutates array swaps element at index with last element, then removes last element
|
|
8
|
+
*/
|
|
9
|
+
export const remove_unordered = (array, index) => {
|
|
10
|
+
array[index] = array[array.length - 1];
|
|
11
|
+
array.pop();
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Returns a function that returns the next item in the `array`
|
|
15
|
+
* in a linear sequence, looping back to index 0 when it reaches the end.
|
|
16
|
+
*/
|
|
17
|
+
export const to_next = (array) => {
|
|
18
|
+
let i = -1;
|
|
19
|
+
return () => {
|
|
20
|
+
i++;
|
|
21
|
+
if (i >= array.length)
|
|
22
|
+
i = 0;
|
|
23
|
+
return array[i];
|
|
24
|
+
};
|
|
25
|
+
};
|
package/dist/async.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type AsyncStatus = 'initial' | 'pending' | 'success' | 'failure';
|
|
2
|
+
/**
|
|
3
|
+
* Waits for the given `duration` before resolving.
|
|
4
|
+
*/
|
|
5
|
+
export declare const wait: (duration?: number) => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Checks if `value` is a `Promise`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const is_promise: (value: any) => value is Promise<any>;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a deferred object with a promise and its resolve/reject handlers.
|
|
12
|
+
*/
|
|
13
|
+
export interface Deferred<T> {
|
|
14
|
+
promise: Promise<T>;
|
|
15
|
+
resolve: (value: T) => void;
|
|
16
|
+
reject: (reason: any) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a object with a `promise` and its `resolve`/`reject` handlers.
|
|
20
|
+
*/
|
|
21
|
+
export declare const create_deferred: <T>() => Deferred<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Maps over items with controlled concurrency, preserving input order.
|
|
24
|
+
*
|
|
25
|
+
* @param items array of items to process
|
|
26
|
+
* @param fn async function to apply to each item
|
|
27
|
+
* @param concurrency maximum number of concurrent operations
|
|
28
|
+
* @returns promise resolving to array of results in same order as input
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const results = await map_concurrent(
|
|
33
|
+
* file_paths,
|
|
34
|
+
* async (path) => readFile(path, 'utf8'),
|
|
35
|
+
* 5, // max 5 concurrent reads
|
|
36
|
+
* );
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare const map_concurrent: <T, R>(items: Array<T>, fn: (item: T, index: number) => Promise<R>, concurrency: number) => Promise<Array<R>>;
|
|
40
|
+
/**
|
|
41
|
+
* Like `map_concurrent` but collects all results/errors instead of failing fast.
|
|
42
|
+
* Returns an array of settlement objects matching the `Promise.allSettled` pattern.
|
|
43
|
+
*
|
|
44
|
+
* @param items array of items to process
|
|
45
|
+
* @param fn async function to apply to each item
|
|
46
|
+
* @param concurrency maximum number of concurrent operations
|
|
47
|
+
* @returns promise resolving to array of `PromiseSettledResult` objects in input order
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const results = await map_concurrent_settled(urls, fetch, 5);
|
|
52
|
+
* for (const [i, result] of results.entries()) {
|
|
53
|
+
* if (result.status === 'fulfilled') {
|
|
54
|
+
* console.log(`${urls[i]}: ${result.value.status}`);
|
|
55
|
+
* } else {
|
|
56
|
+
* console.error(`${urls[i]}: ${result.reason}`);
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare const map_concurrent_settled: <T, R>(items: Array<T>, fn: (item: T, index: number) => Promise<R>, concurrency: number) => Promise<Array<PromiseSettledResult<R>>>;
|
|
62
|
+
//# sourceMappingURL=async.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/async.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,iBAAY,KAAG,OAAO,CAAC,IAAI,CACQ,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,GAAG,KAAG,KAAK,IAAI,OAAO,CAAC,GAAG,CAClB,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,OAAK,QAAQ,CAAC,CAAC,CAQ/C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,GAAU,CAAC,EAAE,CAAC,EACxC,OAAO,KAAK,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,aAAa,MAAM,KACjB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAoDlB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,GAAU,CAAC,EAAE,CAAC,EAChD,OAAO,KAAK,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,aAAa,MAAM,KACjB,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CA6CxC,CAAC"}
|
package/dist/async.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waits for the given `duration` before resolving.
|
|
3
|
+
*/
|
|
4
|
+
export const wait = (duration = 0) => new Promise((resolve) => setTimeout(resolve, duration));
|
|
5
|
+
/**
|
|
6
|
+
* Checks if `value` is a `Promise`.
|
|
7
|
+
*/
|
|
8
|
+
export const is_promise = (value) => value && typeof value.then === 'function';
|
|
9
|
+
/**
|
|
10
|
+
* Creates a object with a `promise` and its `resolve`/`reject` handlers.
|
|
11
|
+
*/
|
|
12
|
+
export const create_deferred = () => {
|
|
13
|
+
let resolve;
|
|
14
|
+
let reject;
|
|
15
|
+
const promise = new Promise((res, rej) => {
|
|
16
|
+
resolve = res;
|
|
17
|
+
reject = rej;
|
|
18
|
+
});
|
|
19
|
+
return { promise, resolve, reject };
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Maps over items with controlled concurrency, preserving input order.
|
|
23
|
+
*
|
|
24
|
+
* @param items array of items to process
|
|
25
|
+
* @param fn async function to apply to each item
|
|
26
|
+
* @param concurrency maximum number of concurrent operations
|
|
27
|
+
* @returns promise resolving to array of results in same order as input
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const results = await map_concurrent(
|
|
32
|
+
* file_paths,
|
|
33
|
+
* async (path) => readFile(path, 'utf8'),
|
|
34
|
+
* 5, // max 5 concurrent reads
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export const map_concurrent = async (items, fn, concurrency) => {
|
|
39
|
+
if (concurrency < 1) {
|
|
40
|
+
throw new Error('concurrency must be at least 1');
|
|
41
|
+
}
|
|
42
|
+
const results = new Array(items.length);
|
|
43
|
+
let next_index = 0;
|
|
44
|
+
let active_count = 0;
|
|
45
|
+
let rejected = false;
|
|
46
|
+
let reject_error;
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const run_next = () => {
|
|
49
|
+
// Stop spawning if we've rejected
|
|
50
|
+
if (rejected)
|
|
51
|
+
return;
|
|
52
|
+
// Check if we're done
|
|
53
|
+
if (next_index >= items.length && active_count === 0) {
|
|
54
|
+
resolve(results);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Spawn workers up to concurrency limit
|
|
58
|
+
while (active_count < concurrency && next_index < items.length) {
|
|
59
|
+
const index = next_index++;
|
|
60
|
+
const item = items[index];
|
|
61
|
+
active_count++;
|
|
62
|
+
fn(item, index)
|
|
63
|
+
.then((result) => {
|
|
64
|
+
if (rejected)
|
|
65
|
+
return;
|
|
66
|
+
results[index] = result;
|
|
67
|
+
active_count--;
|
|
68
|
+
run_next();
|
|
69
|
+
})
|
|
70
|
+
.catch((error) => {
|
|
71
|
+
if (rejected)
|
|
72
|
+
return;
|
|
73
|
+
rejected = true;
|
|
74
|
+
reject_error = error;
|
|
75
|
+
reject(reject_error); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
// Handle empty array
|
|
80
|
+
if (items.length === 0) {
|
|
81
|
+
resolve(results);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
run_next();
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Like `map_concurrent` but collects all results/errors instead of failing fast.
|
|
89
|
+
* Returns an array of settlement objects matching the `Promise.allSettled` pattern.
|
|
90
|
+
*
|
|
91
|
+
* @param items array of items to process
|
|
92
|
+
* @param fn async function to apply to each item
|
|
93
|
+
* @param concurrency maximum number of concurrent operations
|
|
94
|
+
* @returns promise resolving to array of `PromiseSettledResult` objects in input order
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* const results = await map_concurrent_settled(urls, fetch, 5);
|
|
99
|
+
* for (const [i, result] of results.entries()) {
|
|
100
|
+
* if (result.status === 'fulfilled') {
|
|
101
|
+
* console.log(`${urls[i]}: ${result.value.status}`);
|
|
102
|
+
* } else {
|
|
103
|
+
* console.error(`${urls[i]}: ${result.reason}`);
|
|
104
|
+
* }
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export const map_concurrent_settled = async (items, fn, concurrency) => {
|
|
109
|
+
if (concurrency < 1) {
|
|
110
|
+
throw new Error('concurrency must be at least 1');
|
|
111
|
+
}
|
|
112
|
+
const results = new Array(items.length);
|
|
113
|
+
let next_index = 0;
|
|
114
|
+
let active_count = 0;
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
const run_next = () => {
|
|
117
|
+
// Check if we're done
|
|
118
|
+
if (next_index >= items.length && active_count === 0) {
|
|
119
|
+
resolve(results);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Spawn workers up to concurrency limit
|
|
123
|
+
while (active_count < concurrency && next_index < items.length) {
|
|
124
|
+
const index = next_index++;
|
|
125
|
+
const item = items[index];
|
|
126
|
+
active_count++;
|
|
127
|
+
fn(item, index)
|
|
128
|
+
.then((value) => {
|
|
129
|
+
results[index] = { status: 'fulfilled', value };
|
|
130
|
+
})
|
|
131
|
+
.catch((reason) => {
|
|
132
|
+
results[index] = { status: 'rejected', reason };
|
|
133
|
+
})
|
|
134
|
+
.finally(() => {
|
|
135
|
+
active_count--;
|
|
136
|
+
run_next();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
// Handle empty array
|
|
141
|
+
if (items.length === 0) {
|
|
142
|
+
resolve(results);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
run_next();
|
|
146
|
+
});
|
|
147
|
+
};
|
package/dist/colors.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Flavored } from './types.js';
|
|
2
|
+
export type Hsl = readonly [Hue, Saturation, Lightness];
|
|
3
|
+
export type Hue = Flavored<number, 'Hue'>;
|
|
4
|
+
export type Saturation = Flavored<number, 'Saturation'>;
|
|
5
|
+
export type Lightness = Flavored<number, 'Lightness'>;
|
|
6
|
+
export type Rgb = readonly [Red, Green, Blue];
|
|
7
|
+
export type Red = Flavored<number, 'Red'>;
|
|
8
|
+
export type Green = Flavored<number, 'Green'>;
|
|
9
|
+
export type Blue = Flavored<number, 'Blue'>;
|
|
10
|
+
/**
|
|
11
|
+
* Converts an RGB color to a hex color.
|
|
12
|
+
*/
|
|
13
|
+
export declare const rgb_to_hex: (r: number, g: number, b: number) => number;
|
|
14
|
+
/**
|
|
15
|
+
* Converts a hex color to an RGB color.
|
|
16
|
+
*/
|
|
17
|
+
export declare const hex_to_rgb: (hex: number) => Rgb;
|
|
18
|
+
export declare const hex_string_to_rgb: (hex: string) => Rgb;
|
|
19
|
+
export declare const rgb_to_hex_string: (r: number, g: number, b: number) => string;
|
|
20
|
+
export declare const to_hex_component: (v: number) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Converts an RGB color value to HSL. Conversion formula
|
|
23
|
+
* adapted from http://wikipedia.org/wiki/HSL_color_space.
|
|
24
|
+
* Values r/g/b are in the range [0,255] and
|
|
25
|
+
* returns h/s/l in the range [0,1].
|
|
26
|
+
*/
|
|
27
|
+
export declare const rgb_to_hsl: (r: number, g: number, b: number) => Hsl;
|
|
28
|
+
/**
|
|
29
|
+
* Converts an HSL color value to RGB. Conversion formula
|
|
30
|
+
* adapted from http://wikipedia.org/wiki/HSL_color_space.
|
|
31
|
+
* Values h/s/l are in the range [0,1] and
|
|
32
|
+
* returns r/g/b in the range [0,255].
|
|
33
|
+
*/
|
|
34
|
+
export declare const hsl_to_rgb: (h: Hue, s: Saturation, l: Lightness) => Rgb;
|
|
35
|
+
export declare const hue_to_rgb_component: (p: number, q: number, t: number) => number;
|
|
36
|
+
export declare const hsl_to_hex: (h: Hue, s: Saturation, l: Lightness) => number;
|
|
37
|
+
export declare const hsl_to_hex_string: (h: Hue, s: Saturation, l: Lightness) => string;
|
|
38
|
+
export declare const hsl_to_string: (h: Hue, s: Saturation, l: Lightness) => string;
|
|
39
|
+
export declare const hex_string_to_hsl: (hex: string) => Hsl;
|
|
40
|
+
export declare const parse_hsl_string: (hsl: string) => Hsl;
|
|
41
|
+
//# sourceMappingURL=colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/colors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAQzC,MAAM,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AACxD,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC1C,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACxD,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAEtD,MAAM,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;AAC9C,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC1C,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC9C,MAAM,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE5C;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAkC,CAAC;AAEhG;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,GAAuD,CAAC;AAEjG,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,GAI/C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MACE,CAAC;AAEvE,eAAO,MAAM,gBAAgB,GAAI,GAAG,MAAM,KAAG,MAG5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,GA2B5D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,GAAG,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,KAAG,GAYhE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAMtE,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,GAAG,GAAG,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,KAAG,MAGhE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,GAAG,GAAG,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,KAAG,MAGvE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,GAAG,GAAG,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,KAAG,MACU,CAAC;AAE/E,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,GAG/C,CAAC;AAIF,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,GAI9C,CAAC"}
|
package/dist/colors.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { round } from './maths.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts an RGB color to a hex color.
|
|
4
|
+
*/
|
|
5
|
+
export const rgb_to_hex = (r, g, b) => (r << 16) + (g << 8) + b;
|
|
6
|
+
/**
|
|
7
|
+
* Converts a hex color to an RGB color.
|
|
8
|
+
*/
|
|
9
|
+
export const hex_to_rgb = (hex) => [(hex >> 16) & 255, (hex >> 8) & 255, hex & 255];
|
|
10
|
+
export const hex_string_to_rgb = (hex) => {
|
|
11
|
+
var h = hex[0] === '#' ? hex.substring(1) : hex;
|
|
12
|
+
if (h.length !== 6 && h.length !== 8)
|
|
13
|
+
throw new Error('invalid hex string');
|
|
14
|
+
return [parseInt(h[0] + h[1], 16), parseInt(h[2] + h[3], 16), parseInt(h[4] + h[5], 16)];
|
|
15
|
+
};
|
|
16
|
+
export const rgb_to_hex_string = (r, g, b) => '#' + to_hex_component(r) + to_hex_component(g) + to_hex_component(b);
|
|
17
|
+
export const to_hex_component = (v) => {
|
|
18
|
+
var h = v.toString(16);
|
|
19
|
+
return h.length === 1 ? '0' + h : h;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Converts an RGB color value to HSL. Conversion formula
|
|
23
|
+
* adapted from http://wikipedia.org/wiki/HSL_color_space.
|
|
24
|
+
* Values r/g/b are in the range [0,255] and
|
|
25
|
+
* returns h/s/l in the range [0,1].
|
|
26
|
+
*/
|
|
27
|
+
export const rgb_to_hsl = (r, g, b) => {
|
|
28
|
+
var r2 = r / 255;
|
|
29
|
+
var g2 = g / 255;
|
|
30
|
+
var b2 = b / 255;
|
|
31
|
+
var max = Math.max(r2, g2, b2);
|
|
32
|
+
var min = Math.min(r2, g2, b2);
|
|
33
|
+
var l = (max + min) / 2;
|
|
34
|
+
var h, s;
|
|
35
|
+
if (max === min) {
|
|
36
|
+
h = s = 0; // achromatic
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
var d = max - min;
|
|
40
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
41
|
+
switch (max) {
|
|
42
|
+
case r2:
|
|
43
|
+
h = (g2 - b2) / d + (g2 < b2 ? 6 : 0);
|
|
44
|
+
break;
|
|
45
|
+
case g2:
|
|
46
|
+
h = (b2 - r2) / d + 2;
|
|
47
|
+
break;
|
|
48
|
+
case b2:
|
|
49
|
+
h = (r2 - g2) / d + 4;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
h /= 6;
|
|
53
|
+
}
|
|
54
|
+
return [h, round(s, 2), round(l, 2)];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Converts an HSL color value to RGB. Conversion formula
|
|
58
|
+
* adapted from http://wikipedia.org/wiki/HSL_color_space.
|
|
59
|
+
* Values h/s/l are in the range [0,1] and
|
|
60
|
+
* returns r/g/b in the range [0,255].
|
|
61
|
+
*/
|
|
62
|
+
export const hsl_to_rgb = (h, s, l) => {
|
|
63
|
+
var r, g, b;
|
|
64
|
+
if (s === 0) {
|
|
65
|
+
r = g = b = l; // achromatic
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
69
|
+
var p = 2 * l - q;
|
|
70
|
+
r = hue_to_rgb_component(p, q, h + 1 / 3);
|
|
71
|
+
g = hue_to_rgb_component(p, q, h);
|
|
72
|
+
b = hue_to_rgb_component(p, q, h - 1 / 3);
|
|
73
|
+
}
|
|
74
|
+
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
|
75
|
+
};
|
|
76
|
+
export const hue_to_rgb_component = (p, q, t) => {
|
|
77
|
+
var t2 = t < 0 ? t + 1 : t > 1 ? t - 1 : t;
|
|
78
|
+
if (t2 < 1 / 6)
|
|
79
|
+
return p + (q - p) * 6 * t2;
|
|
80
|
+
if (t2 < 1 / 2)
|
|
81
|
+
return q;
|
|
82
|
+
if (t2 < 2 / 3)
|
|
83
|
+
return p + (q - p) * (2 / 3 - t2) * 6;
|
|
84
|
+
return p;
|
|
85
|
+
};
|
|
86
|
+
export const hsl_to_hex = (h, s, l) => {
|
|
87
|
+
var rgb = hsl_to_rgb(h, s, l); // TODO could safely use the optimized variant
|
|
88
|
+
return rgb_to_hex(rgb[0], rgb[1], rgb[2]);
|
|
89
|
+
};
|
|
90
|
+
export const hsl_to_hex_string = (h, s, l) => {
|
|
91
|
+
var rgb = hsl_to_rgb(h, s, l); // TODO could safely use the optimized variant
|
|
92
|
+
return rgb_to_hex_string(rgb[0], rgb[1], rgb[2]);
|
|
93
|
+
};
|
|
94
|
+
export const hsl_to_string = (h, s, l) => `hsl(${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%)`;
|
|
95
|
+
export const hex_string_to_hsl = (hex) => {
|
|
96
|
+
var rgb = hex_string_to_rgb(hex); // TODO could safely use the optimized variant
|
|
97
|
+
return rgb_to_hsl(rgb[0], rgb[1], rgb[2]);
|
|
98
|
+
};
|
|
99
|
+
const HSL_STRING_MATCHER = /^(hsl\()?\s*(\d+),?\s*(\d+)%,?\s*(\d+)%/;
|
|
100
|
+
export const parse_hsl_string = (hsl) => {
|
|
101
|
+
var match = HSL_STRING_MATCHER.exec(hsl);
|
|
102
|
+
if (!match)
|
|
103
|
+
throw new Error('invalid HSL string');
|
|
104
|
+
return [Number(match[2]) / 360, Number(match[3]) / 100, Number(match[4]) / 100];
|
|
105
|
+
};
|
|
106
|
+
// TODO either add an hsla variant or support alpha in the hsl variant
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"counter.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/counter.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC;AAEnC,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,aAG5B,CAAC"}
|
package/dist/counter.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep equality comparison that checks both structure and type.
|
|
3
|
+
*
|
|
4
|
+
* Key behaviors:
|
|
5
|
+
*
|
|
6
|
+
* - Compares by constructor to prevent type confusion (security: `{}` ≠ `[]`, `{}` ≠ `new Map()`, `new ClassA()` ≠ `new ClassB()`)
|
|
7
|
+
* - Prevents asymmetry bugs: `deep_equal(a, b)` always equals `deep_equal(b, a)`
|
|
8
|
+
* - Compares only enumerable own properties (ignores prototypes, symbols, non-enumerable)
|
|
9
|
+
* - Special handling for: Date (timestamp), Number/Boolean (boxed primitives), Error (message/name)
|
|
10
|
+
* - Promises always return false (cannot be meaningfully compared)
|
|
11
|
+
* - Maps/Sets compare by reference for object keys/values
|
|
12
|
+
*
|
|
13
|
+
* @param a first value to compare
|
|
14
|
+
* @param b second value to compare
|
|
15
|
+
* @returns true if deeply equal, false otherwise
|
|
16
|
+
*/
|
|
17
|
+
export declare const deep_equal: (a: unknown, b: unknown) => boolean;
|
|
18
|
+
//# sourceMappingURL=deep_equal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deep_equal.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/deep_equal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,OAAO,EAAE,GAAG,OAAO,KAAG,OA0InD,CAAC"}
|