atomic-fns 1.1.7 → 2.0.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/README.md +405 -60
- package/dist/array/index.d.ts +9 -0
- package/dist/array/index.js +9 -0
- package/dist/array/list.d.ts +99 -0
- package/dist/array/list.js +133 -0
- package/dist/array/sets.d.ts +130 -0
- package/dist/array/sets.js +213 -0
- package/dist/async/index.d.ts +6 -0
- package/dist/async/index.js +6 -0
- package/dist/async/retry.d.ts +38 -0
- package/dist/async/retry.js +56 -0
- package/dist/async/sleep.d.ts +42 -0
- package/dist/async/sleep.js +76 -0
- package/dist/collections/bisect.d.ts +103 -0
- package/dist/collections/bisect.js +157 -0
- package/dist/collections/deque.d.ts +67 -97
- package/dist/collections/deque.js +197 -249
- package/dist/collections/entries.d.ts +89 -0
- package/dist/collections/entries.js +131 -0
- package/dist/collections/frozen-set.d.ts +42 -0
- package/dist/collections/frozen-set.js +64 -0
- package/dist/collections/group.d.ts +141 -0
- package/dist/collections/group.js +241 -0
- package/dist/collections/heap.d.ts +64 -0
- package/dist/collections/heap.js +205 -0
- package/dist/collections/index.d.ts +15 -642
- package/dist/collections/index.js +14 -716
- package/dist/collections/internal/arrayKeys.d.ts +19 -0
- package/dist/collections/internal/arrayKeys.js +83 -0
- package/dist/collections/internal/invariants.d.ts +4 -0
- package/dist/collections/internal/invariants.js +12 -0
- package/dist/collections/internal/iterable.d.ts +25 -0
- package/dist/collections/internal/iterable.js +62 -0
- package/dist/collections/iterate.d.ts +162 -0
- package/dist/collections/iterate.js +188 -0
- package/dist/collections/lru-cache.d.ts +58 -0
- package/dist/collections/lru-cache.js +164 -0
- package/dist/collections/order.d.ts +19 -0
- package/dist/collections/order.js +38 -0
- package/dist/collections/shape.d.ts +32 -0
- package/dist/collections/shape.js +86 -0
- package/dist/collections/sorted-map.d.ts +171 -0
- package/dist/collections/sorted-map.js +845 -0
- package/dist/collections/sorted-set.d.ts +103 -0
- package/dist/collections/sorted-set.js +147 -0
- package/dist/collections/splay-tree.d.ts +82 -0
- package/dist/collections/splay-tree.js +354 -0
- package/dist/collections/trie.d.ts +58 -0
- package/dist/collections/trie.js +196 -0
- package/dist/decimal/currency.d.ts +211 -0
- package/dist/decimal/currency.js +321 -0
- package/dist/decimal/decimal.d.ts +299 -0
- package/dist/decimal/decimal.js +660 -0
- package/dist/decimal/index.d.ts +11 -145
- package/dist/decimal/index.js +10 -374
- package/dist/decimal/internal/bits.d.ts +2 -0
- package/dist/decimal/internal/bits.js +7 -0
- package/dist/decimal/internal/echo.d.ts +4 -0
- package/dist/decimal/internal/echo.js +6 -0
- package/dist/decimal/internal/isqrt.d.ts +9 -0
- package/dist/decimal/internal/isqrt.js +34 -0
- package/dist/decimal/internal/locale.d.ts +7 -0
- package/dist/decimal/internal/locale.js +24 -0
- package/dist/decimal/internal/minor-units.d.ts +2 -0
- package/dist/decimal/internal/minor-units.js +33 -0
- package/dist/decimal/internal/pow10.d.ts +24 -0
- package/dist/decimal/internal/pow10.js +96 -0
- package/dist/encoding/base64.d.ts +39 -0
- package/dist/encoding/base64.js +53 -0
- package/dist/encoding/hex.d.ts +23 -0
- package/dist/encoding/hex.js +34 -0
- package/dist/encoding/index.d.ts +6 -0
- package/dist/encoding/index.js +6 -0
- package/dist/function/debounce.d.ts +68 -0
- package/dist/function/debounce.js +203 -0
- package/dist/function/index.d.ts +6 -0
- package/dist/function/index.js +6 -0
- package/dist/function/shared.d.ts +2 -0
- package/dist/function/shared.js +5 -0
- package/dist/function/wrap.d.ts +130 -0
- package/dist/function/wrap.js +113 -0
- package/dist/hash/digest.d.ts +18 -0
- package/dist/hash/digest.js +26 -0
- package/dist/hash/hash.d.ts +22 -0
- package/dist/hash/hash.js +252 -0
- package/dist/hash/hashString.d.ts +13 -0
- package/dist/hash/hashString.js +53 -0
- package/dist/hash/index.d.ts +8 -0
- package/dist/hash/index.js +8 -0
- package/dist/hash/stableStringify.d.ts +19 -0
- package/dist/hash/stableStringify.js +102 -0
- package/dist/id/index.d.ts +7 -0
- package/dist/id/index.js +7 -0
- package/dist/id/nanoid.d.ts +16 -0
- package/dist/id/nanoid.js +65 -0
- package/dist/id/uniqueId.d.ts +14 -0
- package/dist/id/uniqueId.js +20 -0
- package/dist/id/uuid.d.ts +25 -0
- package/dist/id/uuid.js +64 -0
- package/dist/index.d.ts +15 -8
- package/dist/index.js +15 -8
- package/dist/internal/bytes.d.ts +11 -0
- package/dist/internal/bytes.js +33 -0
- package/dist/internal/codec.d.ts +11 -0
- package/dist/internal/codec.js +84 -0
- package/dist/internal/compare.d.ts +10 -0
- package/dist/internal/compare.js +26 -0
- package/dist/internal/crypto.d.ts +5 -0
- package/dist/internal/crypto.js +11 -0
- package/dist/internal/keys.d.ts +12 -0
- package/dist/internal/keys.js +23 -0
- package/dist/internal/kind.d.ts +44 -0
- package/dist/internal/kind.js +139 -0
- package/dist/internal/sparse.d.ts +51 -0
- package/dist/internal/sparse.js +104 -0
- package/dist/internal/text.d.ts +7 -0
- package/dist/internal/text.js +15 -0
- package/dist/internal/timer.d.ts +10 -0
- package/dist/internal/timer.js +17 -0
- package/dist/internal/truncate.d.ts +10 -0
- package/dist/internal/truncate.js +22 -0
- package/dist/internal/walk.d.ts +47 -0
- package/dist/internal/walk.js +100 -0
- package/dist/is/index.d.ts +8 -0
- package/dist/is/index.js +8 -0
- package/dist/is/isArrayLike.d.ts +18 -0
- package/dist/is/isArrayLike.js +25 -0
- package/dist/is/isError.d.ts +13 -0
- package/dist/is/isError.js +18 -0
- package/dist/is/isNil.d.ts +12 -0
- package/dist/is/isNil.js +14 -0
- package/dist/is/isPlainObject.d.ts +15 -0
- package/dist/is/isPlainObject.js +34 -0
- package/dist/number/index.d.ts +7 -0
- package/dist/number/index.js +7 -0
- package/dist/number/round.d.ts +86 -0
- package/dist/number/round.js +130 -0
- package/dist/number/stats.d.ts +52 -0
- package/dist/number/stats.js +115 -0
- package/dist/object/cloneDeep.d.ts +28 -0
- package/dist/object/cloneDeep.js +200 -0
- package/dist/object/index.d.ts +14 -0
- package/dist/object/index.js +13 -0
- package/dist/object/isEqual.d.ts +64 -0
- package/dist/object/isEqual.js +289 -0
- package/dist/object/matches.d.ts +40 -0
- package/dist/object/matches.js +284 -0
- package/dist/object/merge.d.ts +66 -0
- package/dist/object/merge.js +272 -0
- package/dist/object/parsePath.d.ts +22 -0
- package/dist/object/parsePath.js +111 -0
- package/dist/object/path.d.ts +85 -0
- package/dist/object/path.js +208 -0
- package/dist/object/pick.d.ts +91 -0
- package/dist/object/pick.js +180 -0
- package/dist/object/reshape.d.ts +73 -0
- package/dist/object/reshape.js +144 -0
- package/dist/random/index.d.ts +96 -32
- package/dist/random/index.js +215 -50
- package/dist/string/case.d.ts +71 -0
- package/dist/string/case.js +152 -0
- package/dist/string/fold.d.ts +35 -0
- package/dist/string/fold.js +100 -0
- package/dist/string/index.d.ts +6 -187
- package/dist/string/index.js +6 -226
- package/dist/string/truncate.d.ts +25 -0
- package/dist/string/truncate.js +44 -0
- package/dist/string/words.d.ts +27 -0
- package/dist/string/words.js +241 -0
- package/package.json +101 -58
- package/dist/collections/BTree.d.ts +0 -229
- package/dist/collections/BTree.js +0 -1165
- package/dist/collections/Heap.d.ts +0 -69
- package/dist/collections/Heap.js +0 -181
- package/dist/collections/LRUCache.d.ts +0 -80
- package/dist/collections/LRUCache.js +0 -126
- package/dist/collections/SortedMap.d.ts +0 -92
- package/dist/collections/SortedMap.js +0 -131
- package/dist/collections/SortedSet.d.ts +0 -69
- package/dist/collections/SortedSet.js +0 -99
- package/dist/collections/SortedTree.d.ts +0 -103
- package/dist/collections/SortedTree.js +0 -141
- package/dist/collections/SplayTree.d.ts +0 -105
- package/dist/collections/SplayTree.js +0 -338
- package/dist/collections/Trie.d.ts +0 -42
- package/dist/collections/Trie.js +0 -104
- package/dist/collections/abc.d.ts +0 -152
- package/dist/collections/abc.js +0 -39
- package/dist/collections/frozenset.d.ts +0 -19
- package/dist/collections/frozenset.js +0 -30
- package/dist/functools/index.d.ts +0 -130
- package/dist/functools/index.js +0 -235
- package/dist/globals/index.d.ts +0 -285
- package/dist/globals/index.js +0 -463
- package/dist/i18n/currency.d.ts +0 -24
- package/dist/i18n/currency.js +0 -85
- package/dist/i18n/date/index.d.ts +0 -203
- package/dist/i18n/date/index.js +0 -531
- package/dist/i18n/date/utils.d.ts +0 -65
- package/dist/i18n/date/utils.js +0 -290
- package/dist/i18n/duration.d.ts +0 -112
- package/dist/i18n/duration.js +0 -392
- package/dist/i18n/format.d.ts +0 -31
- package/dist/i18n/format.js +0 -506
- package/dist/i18n/index.d.ts +0 -8
- package/dist/i18n/index.js +0 -8
- package/dist/itertools/index.d.ts +0 -255
- package/dist/itertools/index.js +0 -437
- package/dist/math/index.d.ts +0 -96
- package/dist/math/index.js +0 -98
- package/dist/mod.d.ts +0 -1
- package/dist/mod.js +0 -1
- package/dist/operators/index.d.ts +0 -152
- package/dist/operators/index.js +0 -305
- package/dist/types.d.ts +0 -19
- package/dist/types.js +0 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A curried function over the parameters `A`: each call takes any leading slice of the parameters
|
|
3
|
+
* still missing and returns the result once no required parameter is left, else another `Curried`.
|
|
4
|
+
*/
|
|
5
|
+
export type Curried<A extends unknown[], R> = <P extends Partial<A>>(...args: P) => A extends [...Extract<{
|
|
6
|
+
[K in keyof P]: unknown;
|
|
7
|
+
}, unknown[]>, ...infer Rest] ? [] extends Rest ? R : Curried<Rest, R> : R;
|
|
8
|
+
/**
|
|
9
|
+
* A function curried to an explicit arity, which the type system does not track: every call is
|
|
10
|
+
* typed as returning another `LooseFunction`, so narrow the final result yourself.
|
|
11
|
+
*/
|
|
12
|
+
export type LooseFunction = (...args: unknown[]) => LooseFunction;
|
|
13
|
+
/**
|
|
14
|
+
* Creates a function that collects arguments until it has `arity` of them (default `fn.length`),
|
|
15
|
+
* then calls `fn` with them and the `this` of the latest call. Each partial application can be
|
|
16
|
+
* reused. Throws `RangeError` unless `arity` is a non-negative safe integer.
|
|
17
|
+
* @param fn The function to curry.
|
|
18
|
+
* @param arity How many arguments to wait for.
|
|
19
|
+
* @returns The curried function.
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const add = curry((a: number, b: number, c: number) => a + b + c)
|
|
23
|
+
* add(1)(2)(3) // 6
|
|
24
|
+
* add(1, 2, 3) // 6
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function curry<A extends unknown[], R>(fn: (...args: A) => R, arity?: undefined): Curried<A, R>;
|
|
28
|
+
/** Curries to an explicit arity, typed loosely. */
|
|
29
|
+
export declare function curry(fn: (...args: never[]) => unknown, arity: number): LooseFunction;
|
|
30
|
+
/**
|
|
31
|
+
* Composes functions left to right: the first receives every argument, each later one the
|
|
32
|
+
* previous result, and all run with the caller's `this`. With no functions it returns its first
|
|
33
|
+
* argument. Arrays of functions flatten one level, as in lodash.
|
|
34
|
+
* @returns The composed function.
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const shout = flow((s: string) => s.trim(), (s: string) => s.toUpperCase())
|
|
38
|
+
* shout(' hi ') // 'HI'
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function flow(): <T>(value: T) => T;
|
|
42
|
+
/** Flow over these functions, typed from first to last. */
|
|
43
|
+
export declare function flow<A extends unknown[], R>(fn: (...a: A) => R): (...a: A) => R;
|
|
44
|
+
/** Flow over these functions, typed from first to last. */
|
|
45
|
+
export declare function flow<A extends unknown[], B, C>(f1: (...a: A) => B, f2: (b: B) => C): (...a: A) => C;
|
|
46
|
+
/** Flow over these functions, typed from first to last. */
|
|
47
|
+
export declare function flow<A extends unknown[], B, C, D>(f1: (...a: A) => B, f2: (b: B) => C, f3: (c: C) => D): (...a: A) => D;
|
|
48
|
+
/** Flow over these functions, typed from first to last. */
|
|
49
|
+
export declare function flow<A extends unknown[], B, C, D, E>(f1: (...a: A) => B, f2: (b: B) => C, f3: (c: C) => D, f4: (d: D) => E): (...a: A) => E;
|
|
50
|
+
/** Flow over these functions, typed from first to last. */
|
|
51
|
+
export declare function flow<A extends unknown[], B, C, D, E, F>(f1: (...a: A) => B, f2: (b: B) => C, f3: (c: C) => D, f4: (d: D) => E, f5: (e: E) => F): (...a: A) => F;
|
|
52
|
+
/** Flow over these functions, typed from first to last. */
|
|
53
|
+
export declare function flow<A extends unknown[], B, C, D, E, F, G>(f1: (...a: A) => B, f2: (b: B) => C, f3: (c: C) => D, f4: (d: D) => E, f5: (e: E) => F, f6: (f: F) => G): (...a: A) => G;
|
|
54
|
+
/** Flow over any number of functions of one type, or arrays of them. */
|
|
55
|
+
export declare function flow<T>(...fns: (((value: T) => T) | readonly ((value: T) => T)[])[]): (value: T) => T;
|
|
56
|
+
/**
|
|
57
|
+
* Composes functions right to left, as {@link flow} does left to right. Does not reverse an array
|
|
58
|
+
* passed to it.
|
|
59
|
+
* @returns The composed function.
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const shout = compose((s: string) => s.toUpperCase(), (s: string) => s.trim())
|
|
63
|
+
* shout(' hi ') // 'HI'
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function compose(): <T>(value: T) => T;
|
|
67
|
+
/** Compose these functions, typed from last to first. */
|
|
68
|
+
export declare function compose<A extends unknown[], R>(fn: (...a: A) => R): (...a: A) => R;
|
|
69
|
+
/** Compose these functions, typed from last to first. */
|
|
70
|
+
export declare function compose<A extends unknown[], B, C>(f2: (b: B) => C, f1: (...a: A) => B): (...a: A) => C;
|
|
71
|
+
/** Compose these functions, typed from last to first. */
|
|
72
|
+
export declare function compose<A extends unknown[], B, C, D>(f3: (c: C) => D, f2: (b: B) => C, f1: (...a: A) => B): (...a: A) => D;
|
|
73
|
+
/** Compose these functions, typed from last to first. */
|
|
74
|
+
export declare function compose<A extends unknown[], B, C, D, E>(f4: (d: D) => E, f3: (c: C) => D, f2: (b: B) => C, f1: (...a: A) => B): (...a: A) => E;
|
|
75
|
+
/** Compose these functions, typed from last to first. */
|
|
76
|
+
export declare function compose<A extends unknown[], B, C, D, E, F>(f5: (e: E) => F, f4: (d: D) => E, f3: (c: C) => D, f2: (b: B) => C, f1: (...a: A) => B): (...a: A) => F;
|
|
77
|
+
/** Compose these functions, typed from last to first. */
|
|
78
|
+
export declare function compose<A extends unknown[], B, C, D, E, F, G>(f6: (f: F) => G, f5: (e: E) => F, f4: (d: D) => E, f3: (c: C) => D, f2: (b: B) => C, f1: (...a: A) => B): (...a: A) => G;
|
|
79
|
+
/** Compose any number of functions of one type, or arrays of them. */
|
|
80
|
+
export declare function compose<T>(...fns: (((value: T) => T) | readonly ((value: T) => T)[])[]): (value: T) => T;
|
|
81
|
+
/** The store a memoized function reads and writes; a `Map` or anything with its interface. */
|
|
82
|
+
export interface MemoizeCache<K, V> {
|
|
83
|
+
/** Returns the stored value for `key`. */
|
|
84
|
+
get(key: K): V | undefined;
|
|
85
|
+
/** Stores `value` under `key`. */
|
|
86
|
+
set(key: K, value: V): unknown;
|
|
87
|
+
/** Whether `key` is stored. */
|
|
88
|
+
has(key: K): boolean;
|
|
89
|
+
/** Removes `key`. */
|
|
90
|
+
delete(key: K): boolean;
|
|
91
|
+
/** Removes every key. */
|
|
92
|
+
clear(): void;
|
|
93
|
+
/** How many keys are stored. */
|
|
94
|
+
readonly size: number;
|
|
95
|
+
}
|
|
96
|
+
/** A memoized function and its replaceable `cache`. */
|
|
97
|
+
export type Memoized<F extends (...args: never[]) => unknown> = ((this: ThisParameterType<F>, ...args: Parameters<F>) => ReturnType<F>) & {
|
|
98
|
+
/** The store lookups read; assign another `Map`-like store to replace it. */
|
|
99
|
+
cache: MemoizeCache<unknown, ReturnType<F>>;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Creates a function that caches the result of `fn` per key. The key is the first argument, or
|
|
103
|
+
* what `resolver` returns for the arguments. Both run with the caller's `this`. Lookups use `has`,
|
|
104
|
+
* so `undefined` results are cached too. The cache never evicts: clear it with
|
|
105
|
+
* `memoized.cache.clear()` or assign a bounded store to `memoized.cache`.
|
|
106
|
+
* @param fn The function to memoize.
|
|
107
|
+
* @param resolver Computes the cache key from the arguments.
|
|
108
|
+
* @returns The memoized function.
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const square = memoize((n: number) => n * n)
|
|
112
|
+
* square(4) // 16, computed
|
|
113
|
+
* square(4) // 16, from square.cache
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export declare function memoize<F extends (...args: never[]) => unknown>(fn: F, resolver?: (...args: Parameters<F>) => unknown): Memoized<F>;
|
|
117
|
+
/**
|
|
118
|
+
* Creates a function that calls `fn` once and returns that result on every later call. A call
|
|
119
|
+
* made while `fn` is still running, or after it threw, returns `undefined` without calling it.
|
|
120
|
+
* `fn` is released after its call.
|
|
121
|
+
* @param fn The function to restrict.
|
|
122
|
+
* @returns The restricted function.
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const init = once(() => console.log('ready'))
|
|
126
|
+
* init() // logs 'ready'
|
|
127
|
+
* init() // does nothing
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare function once<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R | undefined;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { assertFunction } from './shared.js';
|
|
2
|
+
export function curry(fn, arity) {
|
|
3
|
+
assertFunction(fn, 'fn');
|
|
4
|
+
const required = arity ?? fn.length;
|
|
5
|
+
if (!Number.isSafeInteger(required) || required < 0) {
|
|
6
|
+
throw new RangeError('arity must be a non-negative safe integer');
|
|
7
|
+
}
|
|
8
|
+
const curried = function (...args) {
|
|
9
|
+
if (args.length >= required)
|
|
10
|
+
return Reflect.apply(fn, this, args);
|
|
11
|
+
return function (...more) {
|
|
12
|
+
return Reflect.apply(curried, this, args.concat(more));
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
return curried;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Flattens one level, as lodash `flow` does, and refuses anything but functions. Holes in a nested
|
|
19
|
+
* array count as `undefined`, so they are refused too.
|
|
20
|
+
*/
|
|
21
|
+
function pipelineOf(fns) {
|
|
22
|
+
const pipeline = [];
|
|
23
|
+
for (const entry of fns) {
|
|
24
|
+
if (Array.isArray(entry))
|
|
25
|
+
pipeline.push(...Array.from(entry));
|
|
26
|
+
// `Array.isArray` does not narrow a readonly array out of the union
|
|
27
|
+
else
|
|
28
|
+
pipeline.push(entry);
|
|
29
|
+
}
|
|
30
|
+
for (const fn of pipeline)
|
|
31
|
+
assertFunction(fn, 'each argument');
|
|
32
|
+
return pipeline;
|
|
33
|
+
}
|
|
34
|
+
function pipeThrough(thisArg, fns, args) {
|
|
35
|
+
if (fns.length === 0)
|
|
36
|
+
return args[0];
|
|
37
|
+
let result = Reflect.apply(fns[0], thisArg, args);
|
|
38
|
+
for (let i = 1; i < fns.length; i++)
|
|
39
|
+
result = Reflect.apply(fns[i], thisArg, [result]);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
/** @param fns The functions to apply, left to right. */
|
|
43
|
+
export function flow(...fns) {
|
|
44
|
+
const pipeline = pipelineOf(fns);
|
|
45
|
+
return function (...args) {
|
|
46
|
+
return pipeThrough(this, pipeline, args);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** @param fns The functions to apply, right to left. */
|
|
50
|
+
export function compose(...fns) {
|
|
51
|
+
const reversed = pipelineOf(fns).reverse();
|
|
52
|
+
return function (...args) {
|
|
53
|
+
return pipeThrough(this, reversed, args);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Creates a function that caches the result of `fn` per key. The key is the first argument, or
|
|
58
|
+
* what `resolver` returns for the arguments. Both run with the caller's `this`. Lookups use `has`,
|
|
59
|
+
* so `undefined` results are cached too. The cache never evicts: clear it with
|
|
60
|
+
* `memoized.cache.clear()` or assign a bounded store to `memoized.cache`.
|
|
61
|
+
* @param fn The function to memoize.
|
|
62
|
+
* @param resolver Computes the cache key from the arguments.
|
|
63
|
+
* @returns The memoized function.
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const square = memoize((n: number) => n * n)
|
|
67
|
+
* square(4) // 16, computed
|
|
68
|
+
* square(4) // 16, from square.cache
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function memoize(fn, resolver) {
|
|
72
|
+
assertFunction(fn, 'fn');
|
|
73
|
+
if (resolver != null)
|
|
74
|
+
assertFunction(resolver, 'resolver');
|
|
75
|
+
const cache = new Map();
|
|
76
|
+
const memoized = Object.assign(function (...args) {
|
|
77
|
+
const key = resolver ? Reflect.apply(resolver, this, args) : args[0];
|
|
78
|
+
const store = memoized.cache;
|
|
79
|
+
// `has` said the key is stored, so `get` returns a stored `ReturnType<F>`
|
|
80
|
+
if (store.has(key))
|
|
81
|
+
return store.get(key);
|
|
82
|
+
const value = Reflect.apply(fn, this, args);
|
|
83
|
+
store.set(key, value);
|
|
84
|
+
return value;
|
|
85
|
+
}, { cache });
|
|
86
|
+
return memoized;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Creates a function that calls `fn` once and returns that result on every later call. A call
|
|
90
|
+
* made while `fn` is still running, or after it threw, returns `undefined` without calling it.
|
|
91
|
+
* `fn` is released after its call.
|
|
92
|
+
* @param fn The function to restrict.
|
|
93
|
+
* @returns The restricted function.
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const init = once(() => console.log('ready'))
|
|
97
|
+
* init() // logs 'ready'
|
|
98
|
+
* init() // does nothing
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export function once(fn) {
|
|
102
|
+
assertFunction(fn, 'fn');
|
|
103
|
+
let pending = fn;
|
|
104
|
+
let value;
|
|
105
|
+
return function (...args) {
|
|
106
|
+
if (pending) {
|
|
107
|
+
const target = pending;
|
|
108
|
+
pending = undefined;
|
|
109
|
+
value = Reflect.apply(target, this, args);
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** The digest algorithms every `crypto.subtle` implementation supports. */
|
|
2
|
+
export type DigestAlgorithm = 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
3
|
+
/**
|
|
4
|
+
* Cryptographic digest of `data` as a lowercase hex string, through `crypto.subtle.digest`. A
|
|
5
|
+
* string is hashed as UTF-8, with each lone surrogate as U+FFFD as `TextEncoder` writes it, so
|
|
6
|
+
* `'\uD800'` and `'\uDFFF'` hash alike; bytes on shared memory are copied first, since
|
|
7
|
+
* `crypto.subtle` refuses them. Rejects with `TypeError` where `crypto.subtle` is missing (insecure
|
|
8
|
+
* browser contexts) and for data that is neither a string nor bytes.
|
|
9
|
+
* @param algorithm `'SHA-1'`, `'SHA-256'`, `'SHA-384'` or `'SHA-512'`.
|
|
10
|
+
* @param data A string, an `ArrayBuffer` or `SharedArrayBuffer`, or a typed array or `DataView`.
|
|
11
|
+
* @returns A promise of the hex digest.
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* await digest('SHA-256', 'abc')
|
|
15
|
+
* // 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function digest(algorithm: DigestAlgorithm, data: string | ArrayBuffer | SharedArrayBuffer | ArrayBufferView): Promise<string>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { bytesToHex, isArrayBuffer, toBytes } from '../internal/bytes.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cryptographic digest of `data` as a lowercase hex string, through `crypto.subtle.digest`. A
|
|
4
|
+
* string is hashed as UTF-8, with each lone surrogate as U+FFFD as `TextEncoder` writes it, so
|
|
5
|
+
* `'\uD800'` and `'\uDFFF'` hash alike; bytes on shared memory are copied first, since
|
|
6
|
+
* `crypto.subtle` refuses them. Rejects with `TypeError` where `crypto.subtle` is missing (insecure
|
|
7
|
+
* browser contexts) and for data that is neither a string nor bytes.
|
|
8
|
+
* @param algorithm `'SHA-1'`, `'SHA-256'`, `'SHA-384'` or `'SHA-512'`.
|
|
9
|
+
* @param data A string, an `ArrayBuffer` or `SharedArrayBuffer`, or a typed array or `DataView`.
|
|
10
|
+
* @returns A promise of the hex digest.
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* await digest('SHA-256', 'abc')
|
|
14
|
+
* // 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export async function digest(algorithm, data) {
|
|
18
|
+
const subtle = globalThis.crypto?.subtle;
|
|
19
|
+
if (subtle === undefined)
|
|
20
|
+
throw new TypeError('crypto.subtle is not available');
|
|
21
|
+
const view = toBytes(data);
|
|
22
|
+
const bytes = isArrayBuffer(view.buffer)
|
|
23
|
+
? new Uint8Array(view.buffer, view.byteOffset, view.byteLength)
|
|
24
|
+
: view.slice();
|
|
25
|
+
return bytesToHex(new Uint8Array(await subtle.digest(algorithm, bytes)));
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural hash, consistent with `isEqual`: whenever `isEqual(a, b)` is true, `hash(a)` equals
|
|
3
|
+
* `hash(b)`, so values can be bucketed before they are compared. It reads the kinds `isEqual`
|
|
4
|
+
* compares by content (plain objects, arrays, `Map`, `Set`, Dates, RegExps, buffers, typed arrays,
|
|
5
|
+
* boxed primitives, errors, `arguments`), key order aside; class instances, functions,
|
|
6
|
+
* unregistered symbols and `Map` keys or `Set` members that are objects hash by identity, which
|
|
7
|
+
* holds only for the life of the process. Not cryptographic, and not resistant to keys crafted to
|
|
8
|
+
* collide.
|
|
9
|
+
*
|
|
10
|
+
* It reads at most 8 levels below the root, and structure deeper than that counts only by its
|
|
11
|
+
* kind. So cyclic values that `isEqual` equates hash alike, and `hash`, unlike the other deep walks,
|
|
12
|
+
* never throws on deep input.
|
|
13
|
+
* @param value Any value.
|
|
14
|
+
* @returns An unsigned 32-bit integer.
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* hash({ a: [1, 2], b: 'x' }) === hash({ b: 'x', a: [1, 2] }) // true
|
|
18
|
+
* hash(new Map([[1, NaN]])) === hash(new Map([[1, NaN]])) // true
|
|
19
|
+
* hash(0) === hash(-0) // true
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function hash(value: unknown): number;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { enumerableKeys, ERROR_LOCATION_KEYS } from '../internal/keys.js';
|
|
2
|
+
import { bytesOf, contentKindOf, isFloatView, typedArrayName, unbox } from '../internal/kind.js';
|
|
3
|
+
import { walkArrayIndices } from '../internal/sparse.js';
|
|
4
|
+
import { hashString } from './hashString.js';
|
|
5
|
+
/** Levels below the root a hash reads; deeper structure counts only by its kind. */
|
|
6
|
+
const UNFOLD_DEPTH = 8;
|
|
7
|
+
/** Past this many objects a hash stops remembering them; only time grows, not memory. */
|
|
8
|
+
const MAX_TRACKED_OBJECTS = 2 ** 24;
|
|
9
|
+
/** Seeds that keep the hash of one kind apart from another's. */
|
|
10
|
+
const Tag = {
|
|
11
|
+
Undefined: 4097,
|
|
12
|
+
Null: 4098,
|
|
13
|
+
True: 4099,
|
|
14
|
+
False: 4100,
|
|
15
|
+
NaN: 4101,
|
|
16
|
+
Number: 4102,
|
|
17
|
+
BigInt: 4103,
|
|
18
|
+
String: 4104,
|
|
19
|
+
Symbol: 4105,
|
|
20
|
+
Plain: 4106,
|
|
21
|
+
Array: 4107,
|
|
22
|
+
Map: 4108,
|
|
23
|
+
Set: 4109,
|
|
24
|
+
Date: 4110,
|
|
25
|
+
RegExp: 4111,
|
|
26
|
+
Buffer: 4112,
|
|
27
|
+
View: 4113,
|
|
28
|
+
Boxed: 4114,
|
|
29
|
+
Error: 4115,
|
|
30
|
+
Arguments: 4116,
|
|
31
|
+
NoCause: 4117
|
|
32
|
+
};
|
|
33
|
+
function mix(state, value) {
|
|
34
|
+
const h = Math.imul(state ^ value, 0x5bd1e995);
|
|
35
|
+
return h ^ (h >>> 15);
|
|
36
|
+
}
|
|
37
|
+
function finish(state) {
|
|
38
|
+
let h = state ^ (state >>> 16);
|
|
39
|
+
h = Math.imul(h, 0x85ebca6b);
|
|
40
|
+
h ^= h >>> 13;
|
|
41
|
+
h = Math.imul(h, 0xc2b2ae35);
|
|
42
|
+
return (h ^ (h >>> 16)) >>> 0;
|
|
43
|
+
}
|
|
44
|
+
function canHoldSymbols() {
|
|
45
|
+
try {
|
|
46
|
+
new WeakMap().set(Symbol(), 0);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const ids = new WeakMap();
|
|
54
|
+
const symbolsAreWeakKeys = canHoldSymbols();
|
|
55
|
+
let nextId = 1;
|
|
56
|
+
function identityOf(value) {
|
|
57
|
+
let id = ids.get(value);
|
|
58
|
+
if (id === undefined)
|
|
59
|
+
ids.set(value, (id = nextId++));
|
|
60
|
+
return id;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A registered symbol by its key; any other by identity where the runtime can hold it weakly, and
|
|
64
|
+
* by its description where it cannot, so that hashing never keeps a symbol alive.
|
|
65
|
+
*/
|
|
66
|
+
function symbolHash(value) {
|
|
67
|
+
const key = Symbol.keyFor(value);
|
|
68
|
+
if (key !== undefined)
|
|
69
|
+
return mix(Tag.Symbol, hashString(key));
|
|
70
|
+
if (!symbolsAreWeakKeys)
|
|
71
|
+
return mix(Tag.Symbol, hashString(value.description ?? ''));
|
|
72
|
+
return mix(Tag.Symbol, identityOf(value));
|
|
73
|
+
}
|
|
74
|
+
/** By SameValueZero: `0` and `-0` share a hash, and so do all NaNs. */
|
|
75
|
+
function numberHash(value) {
|
|
76
|
+
if (value !== value)
|
|
77
|
+
return Tag.NaN;
|
|
78
|
+
return mix(Tag.Number, hashString(String(value === 0 ? 0 : value)));
|
|
79
|
+
}
|
|
80
|
+
/** Hash of a value compared by SameValueZero: a primitive by value, an object by identity. */
|
|
81
|
+
function memberHash(value) {
|
|
82
|
+
switch (typeof value) {
|
|
83
|
+
case 'undefined':
|
|
84
|
+
return Tag.Undefined;
|
|
85
|
+
case 'boolean':
|
|
86
|
+
return value ? Tag.True : Tag.False;
|
|
87
|
+
case 'number':
|
|
88
|
+
return numberHash(value);
|
|
89
|
+
case 'bigint':
|
|
90
|
+
return mix(Tag.BigInt, hashString(value.toString(16)));
|
|
91
|
+
case 'string':
|
|
92
|
+
return mix(Tag.String, hashString(value));
|
|
93
|
+
case 'symbol':
|
|
94
|
+
return symbolHash(value);
|
|
95
|
+
default:
|
|
96
|
+
return value === null ? Tag.Null : identityOf(value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function bytesHash(bytes) {
|
|
100
|
+
let state = bytes.length;
|
|
101
|
+
for (const byte of bytes)
|
|
102
|
+
state = mix(state, byte);
|
|
103
|
+
return state;
|
|
104
|
+
}
|
|
105
|
+
/** What a container hashes to once the levels a hash reads run out. */
|
|
106
|
+
const CONTAINER_TAGS = {
|
|
107
|
+
plain: Tag.Plain,
|
|
108
|
+
array: Tag.Array,
|
|
109
|
+
arguments: Tag.Arguments,
|
|
110
|
+
map: Tag.Map,
|
|
111
|
+
error: Tag.Error
|
|
112
|
+
};
|
|
113
|
+
/** One `hash` call: hashes of shared objects, by how many levels were left below them. */
|
|
114
|
+
class Unfolding {
|
|
115
|
+
#memo = new Map();
|
|
116
|
+
of(value, levels) {
|
|
117
|
+
if (typeof value !== 'object' || value === null)
|
|
118
|
+
return memberHash(value);
|
|
119
|
+
let byLevel = this.#memo.get(value);
|
|
120
|
+
const known = byLevel?.[levels];
|
|
121
|
+
if (known !== undefined)
|
|
122
|
+
return known;
|
|
123
|
+
const hashed = this.#object(value, levels);
|
|
124
|
+
if (byLevel === undefined && this.#memo.size < MAX_TRACKED_OBJECTS) {
|
|
125
|
+
this.#memo.set(value, (byLevel = []));
|
|
126
|
+
}
|
|
127
|
+
if (byLevel !== undefined)
|
|
128
|
+
byLevel[levels] = hashed;
|
|
129
|
+
return hashed;
|
|
130
|
+
}
|
|
131
|
+
#object(value, levels) {
|
|
132
|
+
const kind = contentKindOf(value);
|
|
133
|
+
switch (kind) {
|
|
134
|
+
case 'other':
|
|
135
|
+
return identityOf(value);
|
|
136
|
+
case 'set': {
|
|
137
|
+
let sum = 0;
|
|
138
|
+
for (const v of Set.prototype.values.call(value))
|
|
139
|
+
sum = (sum + finish(memberHash(v))) | 0;
|
|
140
|
+
return mix(mix(Tag.Set, value.size), sum);
|
|
141
|
+
}
|
|
142
|
+
case 'date':
|
|
143
|
+
return mix(Tag.Date, numberHash(Date.prototype.getTime.call(value)));
|
|
144
|
+
case 'regexp':
|
|
145
|
+
return mix(mix(Tag.RegExp, hashString(value.source)), hashString(value.flags));
|
|
146
|
+
case 'buffer':
|
|
147
|
+
return mix(Tag.Buffer, bytesHash(bytesOf(value)));
|
|
148
|
+
case 'view':
|
|
149
|
+
return this.#view(value);
|
|
150
|
+
case 'boxed':
|
|
151
|
+
return mix(Tag.Boxed, memberHash(unbox(value)));
|
|
152
|
+
default:
|
|
153
|
+
return levels === 0 ? CONTAINER_TAGS[kind] : this.#container(value, kind, levels - 1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** The kinds that hold other values, read one level further down. */
|
|
157
|
+
#container(value, kind, below) {
|
|
158
|
+
switch (kind) {
|
|
159
|
+
case 'plain':
|
|
160
|
+
return this.#record(value, below);
|
|
161
|
+
case 'array':
|
|
162
|
+
return this.#array(value, below, Tag.Array);
|
|
163
|
+
case 'arguments':
|
|
164
|
+
return this.#array(value, below, Tag.Arguments);
|
|
165
|
+
case 'map': {
|
|
166
|
+
let sum = 0;
|
|
167
|
+
for (const [k, v] of Map.prototype.entries.call(value)) {
|
|
168
|
+
sum = (sum + finish(mix(memberHash(k), this.of(v, below)))) | 0;
|
|
169
|
+
}
|
|
170
|
+
return mix(mix(Tag.Map, value.size), sum);
|
|
171
|
+
}
|
|
172
|
+
case 'error':
|
|
173
|
+
return this.#error(value, below);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** Name, message, an own `cause` and own enumerable properties, as `isEqual` compares errors. */
|
|
177
|
+
#error(value, below) {
|
|
178
|
+
const cause = Object.hasOwn(value, 'cause') ? this.of(value.cause, below) : Tag.NoCause;
|
|
179
|
+
let state = mix(Tag.Error, this.of(value.name, below));
|
|
180
|
+
state = mix(mix(state, this.of(value.message, below)), cause);
|
|
181
|
+
return mix(state, this.#record(value, below, ERROR_LOCATION_KEYS));
|
|
182
|
+
}
|
|
183
|
+
#record(value, below, ignored) {
|
|
184
|
+
const record = value;
|
|
185
|
+
let sum = 0;
|
|
186
|
+
let count = 0;
|
|
187
|
+
for (const key of enumerableKeys(value)) {
|
|
188
|
+
if (ignored?.has(key))
|
|
189
|
+
continue;
|
|
190
|
+
sum = (sum + finish(mix(memberHash(key), this.of(record[key], below)))) | 0;
|
|
191
|
+
count++;
|
|
192
|
+
}
|
|
193
|
+
return mix(mix(Tag.Plain, count), sum);
|
|
194
|
+
}
|
|
195
|
+
/** `undefined` and holes add nothing, since `isEqual` takes a hole to equal `undefined`. */
|
|
196
|
+
#array(value, below, tag) {
|
|
197
|
+
let sum = 0;
|
|
198
|
+
let i = 0;
|
|
199
|
+
for (; i < value.length && i in value; i++) {
|
|
200
|
+
const item = value[i];
|
|
201
|
+
if (item !== undefined)
|
|
202
|
+
sum = (sum + finish(mix(i, this.of(item, below)))) | 0;
|
|
203
|
+
}
|
|
204
|
+
if (i === value.length)
|
|
205
|
+
return mix(mix(tag, value.length), sum);
|
|
206
|
+
walkArrayIndices(value, (ev) => {
|
|
207
|
+
if (ev.kind !== 'index' || ev.index < i)
|
|
208
|
+
return;
|
|
209
|
+
const item = value[ev.index];
|
|
210
|
+
if (item !== undefined)
|
|
211
|
+
sum = (sum + finish(mix(ev.index, this.of(item, below)))) | 0;
|
|
212
|
+
});
|
|
213
|
+
return mix(mix(tag, value.length), sum);
|
|
214
|
+
}
|
|
215
|
+
#view(value) {
|
|
216
|
+
const tag = mix(Tag.View, hashString(typedArrayName(value) ?? 'DataView'));
|
|
217
|
+
if (!isFloatView(value))
|
|
218
|
+
return mix(tag, bytesHash(bytesOf(value)));
|
|
219
|
+
const floats = value;
|
|
220
|
+
const length = floats.length;
|
|
221
|
+
let state = mix(tag, length);
|
|
222
|
+
for (let i = 0; i < length; i++)
|
|
223
|
+
state = mix(state, numberHash(floats[i]));
|
|
224
|
+
return state;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Structural hash, consistent with `isEqual`: whenever `isEqual(a, b)` is true, `hash(a)` equals
|
|
229
|
+
* `hash(b)`, so values can be bucketed before they are compared. It reads the kinds `isEqual`
|
|
230
|
+
* compares by content (plain objects, arrays, `Map`, `Set`, Dates, RegExps, buffers, typed arrays,
|
|
231
|
+
* boxed primitives, errors, `arguments`), key order aside; class instances, functions,
|
|
232
|
+
* unregistered symbols and `Map` keys or `Set` members that are objects hash by identity, which
|
|
233
|
+
* holds only for the life of the process. Not cryptographic, and not resistant to keys crafted to
|
|
234
|
+
* collide.
|
|
235
|
+
*
|
|
236
|
+
* It reads at most 8 levels below the root, and structure deeper than that counts only by its
|
|
237
|
+
* kind. So cyclic values that `isEqual` equates hash alike, and `hash`, unlike the other deep walks,
|
|
238
|
+
* never throws on deep input.
|
|
239
|
+
* @param value Any value.
|
|
240
|
+
* @returns An unsigned 32-bit integer.
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* hash({ a: [1, 2], b: 'x' }) === hash({ b: 'x', a: [1, 2] }) // true
|
|
244
|
+
* hash(new Map([[1, NaN]])) === hash(new Map([[1, NaN]])) // true
|
|
245
|
+
* hash(0) === hash(-0) // true
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
export function hash(value) {
|
|
249
|
+
if (typeof value !== 'object' || value === null)
|
|
250
|
+
return finish(memberHash(value));
|
|
251
|
+
return finish(new Unfolding().of(value, UNFOLD_DEPTH));
|
|
252
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 32-bit FNV-1a hash of the UTF-8 bytes of `str`, so it matches FNV-1a in other languages. Lone
|
|
3
|
+
* surrogates count as U+FFFD, as `TextEncoder` writes them. Fast and well spread, but not
|
|
4
|
+
* cryptographic.
|
|
5
|
+
* @param str The string to hash.
|
|
6
|
+
* @returns An unsigned 32-bit integer.
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* hashString('') // 2166136261
|
|
10
|
+
* hashString('a') // 3826002220
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare function hashString(str: string): number;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const FNV_OFFSET = 0x811c9dc5;
|
|
2
|
+
const FNV_PRIME = 0x01000193;
|
|
3
|
+
/** One byte into an FNV-1a state. */
|
|
4
|
+
function step(state, byte) {
|
|
5
|
+
return Math.imul(state ^ byte, FNV_PRIME);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 32-bit FNV-1a hash of the UTF-8 bytes of `str`, so it matches FNV-1a in other languages. Lone
|
|
9
|
+
* surrogates count as U+FFFD, as `TextEncoder` writes them. Fast and well spread, but not
|
|
10
|
+
* cryptographic.
|
|
11
|
+
* @param str The string to hash.
|
|
12
|
+
* @returns An unsigned 32-bit integer.
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* hashString('') // 2166136261
|
|
16
|
+
* hashString('a') // 3826002220
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function hashString(str) {
|
|
20
|
+
if (typeof str !== 'string')
|
|
21
|
+
throw new TypeError('hashString expects a string');
|
|
22
|
+
let state = FNV_OFFSET;
|
|
23
|
+
// Encodes in place rather than through `TextEncoder`: `hash` calls this for every string and
|
|
24
|
+
// number it meets, and an allocation per call made it about five times slower.
|
|
25
|
+
for (let i = 0; i < str.length; i++) {
|
|
26
|
+
let code = str.charCodeAt(i);
|
|
27
|
+
if (code >= 0xd800 && code <= 0xdbff && i + 1 < str.length) {
|
|
28
|
+
const low = str.charCodeAt(i + 1);
|
|
29
|
+
if (low >= 0xdc00 && low <= 0xdfff) {
|
|
30
|
+
code = 0x10000 + ((code - 0xd800) << 10) + (low - 0xdc00);
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (code >= 0xd800 && code <= 0xdfff)
|
|
35
|
+
code = 0xfffd;
|
|
36
|
+
if (code < 0x80) {
|
|
37
|
+
state = step(state, code);
|
|
38
|
+
}
|
|
39
|
+
else if (code < 0x800) {
|
|
40
|
+
state = step(step(state, 0xc0 | (code >> 6)), 0x80 | (code & 0x3f));
|
|
41
|
+
}
|
|
42
|
+
else if (code < 0x10000) {
|
|
43
|
+
state = step(step(step(state, 0xe0 | (code >> 12)), 0x80 | ((code >> 6) & 0x3f)), 0x80 | (code & 0x3f));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
state = step(state, 0xf0 | (code >> 18));
|
|
47
|
+
state = step(state, 0x80 | ((code >> 12) & 0x3f));
|
|
48
|
+
state = step(state, 0x80 | ((code >> 6) & 0x3f));
|
|
49
|
+
state = step(state, 0x80 | (code & 0x3f));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return state >>> 0;
|
|
53
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String, structural and cryptographic hashes, and a stable JSON form.
|
|
3
|
+
* @module hash
|
|
4
|
+
*/
|
|
5
|
+
export { type DigestAlgorithm, digest } from './digest.js';
|
|
6
|
+
export { hash } from './hash.js';
|
|
7
|
+
export { hashString } from './hashString.js';
|
|
8
|
+
export { stableStringify } from './stableStringify.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String, structural and cryptographic hashes, and a stable JSON form.
|
|
3
|
+
* @module hash
|
|
4
|
+
*/
|
|
5
|
+
export { digest } from './digest.js';
|
|
6
|
+
export { hash } from './hash.js';
|
|
7
|
+
export { hashString } from './hashString.js';
|
|
8
|
+
export { stableStringify } from './stableStringify.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `JSON.stringify` with every object's keys sorted, so equal data always gives the same text (for
|
|
3
|
+
* cache keys, signatures and snapshots). It follows `JSON.stringify` otherwise. A value's own or
|
|
4
|
+
* inherited `toJSON` is called with its key, as JSON's contract has it, so Dates write their ISO
|
|
5
|
+
* string and types such as `Decimal` write their JSON form. Boxed primitives unbox, `undefined`,
|
|
6
|
+
* functions and symbols are dropped from objects and become `null` in arrays, and non-finite
|
|
7
|
+
* numbers become `null`. A cycle or a bigint throws `TypeError`, nesting past 1000 levels throws
|
|
8
|
+
* `RangeError`, and so does a huge sparse array. As in JSON, a `Map`, a `Set` or a class instance
|
|
9
|
+
* without `toJSON` writes only its own enumerable keys, often `'{}'`, so equal text does not imply
|
|
10
|
+
* equal data for those kinds.
|
|
11
|
+
* @param value Any value.
|
|
12
|
+
* @returns The JSON text, or `undefined` for a value JSON cannot write (`undefined`, a function, a
|
|
13
|
+
* symbol).
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* stableStringify({ b: 1, a: [true, { d: 2, c: 3 }] }) // '{"a":[true,{"c":3,"d":2}],"b":1}'
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function stableStringify(value: unknown): string | undefined;
|