es-toolkit 1.52.0-dev.2095 → 1.52.0-dev.2096
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/dist/browser.global.js +4 -4
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/index.mjs +2 -1
- package/dist/util/hash/browser.d.mts +26 -0
- package/dist/util/hash/browser.d.ts +26 -0
- package/dist/util/hash/browser.js +31 -0
- package/dist/util/hash/browser.mjs +30 -0
- package/dist/util/hash/node.d.mts +26 -0
- package/dist/util/hash/node.d.ts +26 -0
- package/dist/util/hash/node.js +494 -0
- package/dist/util/hash/node.mjs +493 -0
- package/dist/util/hash/sha256.js +158 -0
- package/dist/util/hash/sha256.mjs +158 -0
- package/dist/util/index.d.mts +2 -1
- package/dist/util/index.d.ts +2 -1
- package/dist/util/index.js +2 -0
- package/dist/util/index.mjs +2 -1
- package/dist/util/serialize/compareValues.js +23 -0
- package/dist/util/serialize/compareValues.mjs +23 -0
- package/dist/util/serialize/serialize.d.mts +44 -0
- package/dist/util/serialize/serialize.d.ts +44 -0
- package/dist/util/serialize/serialize.js +64 -0
- package/dist/util/serialize/serialize.mjs +63 -0
- package/dist/util/serialize/serializeBigInt.js +15 -0
- package/dist/util/serialize/serializeBigInt.mjs +15 -0
- package/dist/util/serialize/serializeFunction.js +26 -0
- package/dist/util/serialize/serializeFunction.mjs +26 -0
- package/dist/util/serialize/serializeNumber.js +21 -0
- package/dist/util/serialize/serializeNumber.mjs +21 -0
- package/dist/util/serialize/serializeObject.js +85 -0
- package/dist/util/serialize/serializeObject.mjs +85 -0
- package/dist/util/serialize/serializePlainObject.js +31 -0
- package/dist/util/serialize/serializePlainObject.mjs +31 -0
- package/dist/util/serialize/serializeString.js +18 -0
- package/dist/util/serialize/serializeString.mjs +18 -0
- package/dist/util/serialize/serializeSymbol.js +23 -0
- package/dist/util/serialize/serializeSymbol.mjs +23 -0
- package/package.json +47 -1
- package/util/hash.d.ts +1 -0
- package/util/hash.js +1 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { hash as hash$1 } from "node:crypto";
|
|
2
|
+
//#region src/util/serialize/serializeBigInt.ts
|
|
3
|
+
/**
|
|
4
|
+
* Serializes a bigint into a string with an `n` suffix, like a bigint literal.
|
|
5
|
+
*
|
|
6
|
+
* @param value - The bigint to serialize.
|
|
7
|
+
* @returns The serialized string.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* serializeBigInt(123n); // "123n"
|
|
11
|
+
*/
|
|
12
|
+
function serializeBigInt(value) {
|
|
13
|
+
return `${value}n`;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/util/serialize/serializeFunction.ts
|
|
17
|
+
/**
|
|
18
|
+
* Serializes a function into a string of the form `name:source`.
|
|
19
|
+
*
|
|
20
|
+
* Native functions have no meaningful source, so they are serialized as
|
|
21
|
+
* `name:[native]`. For other functions, newlines and their surrounding
|
|
22
|
+
* whitespace are collapsed so that formatting differences do not change
|
|
23
|
+
* the output.
|
|
24
|
+
*
|
|
25
|
+
* @param value - The function to serialize.
|
|
26
|
+
* @returns The serialized string.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* function sum(a, b) {
|
|
30
|
+
* return a + b;
|
|
31
|
+
* }
|
|
32
|
+
* serializeFunction(sum); // "sum:function sum(a, b) {return a + b;}"
|
|
33
|
+
* serializeFunction(Math.max); // "max:[native]"
|
|
34
|
+
*/
|
|
35
|
+
function serializeFunction(value) {
|
|
36
|
+
const source = Function.prototype.toString.call(value);
|
|
37
|
+
if (source.endsWith("[native code] }")) return `${value.name}:[native]`;
|
|
38
|
+
return `${value.name}:${source.replace(/\s*\n\s*/g, "")}`;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/util/serialize/serializeNumber.ts
|
|
42
|
+
/**
|
|
43
|
+
* Serializes a number into a string.
|
|
44
|
+
*
|
|
45
|
+
* `-0` is serialized as `0`, and `NaN` and `Infinity` are serialized
|
|
46
|
+
* as their string representations.
|
|
47
|
+
*
|
|
48
|
+
* @param value - The number to serialize.
|
|
49
|
+
* @returns The serialized string.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* serializeNumber(1); // "1"
|
|
53
|
+
* serializeNumber(-0); // "0"
|
|
54
|
+
* serializeNumber(NaN); // "NaN"
|
|
55
|
+
* serializeNumber(Infinity); // "Infinity"
|
|
56
|
+
*/
|
|
57
|
+
function serializeNumber(value) {
|
|
58
|
+
return String(value);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/util/serialize/compareValues.ts
|
|
62
|
+
/**
|
|
63
|
+
* Compares two values for sorting `Set` values and `Map` keys.
|
|
64
|
+
*
|
|
65
|
+
* Numbers are compared numerically and strings by code unit. Any other
|
|
66
|
+
* combination is compared by the code unit order of the serialized values.
|
|
67
|
+
*
|
|
68
|
+
* @param a - The first value to compare.
|
|
69
|
+
* @param b - The second value to compare.
|
|
70
|
+
* @param refs - The circular reference context shared with the surrounding serialization.
|
|
71
|
+
* @returns A negative number if `a` sorts before `b`, a positive number if
|
|
72
|
+
* `a` sorts after `b`, and `0` if they are equal.
|
|
73
|
+
*/
|
|
74
|
+
function compareValues(a, b, refs) {
|
|
75
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
76
|
+
const serializedA = typeof a === "string" && typeof b === "string" ? a : serializeValue(a, refs);
|
|
77
|
+
const serializedB = typeof a === "string" && typeof b === "string" ? b : serializeValue(b, refs);
|
|
78
|
+
if (serializedA === serializedB) return 0;
|
|
79
|
+
return serializedA < serializedB ? -1 : 1;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/util/serialize/serializeString.ts
|
|
83
|
+
/**
|
|
84
|
+
* Serializes a string into a single-quoted string literal.
|
|
85
|
+
*
|
|
86
|
+
* The string is not escaped; the output is intended for hashing and
|
|
87
|
+
* change detection, not for parsing or re-evaluation.
|
|
88
|
+
*
|
|
89
|
+
* @param value - The string to serialize.
|
|
90
|
+
* @returns The serialized string.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* serializeString('abc'); // "'abc'"
|
|
94
|
+
*/
|
|
95
|
+
function serializeString(value) {
|
|
96
|
+
return `'${value}'`;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/util/serialize/serializePlainObject.ts
|
|
100
|
+
/**
|
|
101
|
+
* Serializes the own enumerable string-keyed properties of an object
|
|
102
|
+
* into a `{'key':value}` string.
|
|
103
|
+
*
|
|
104
|
+
* Keys are sorted by code unit so that the output does not depend on
|
|
105
|
+
* property insertion order, and always quoted so that a key containing
|
|
106
|
+
* `:` or `,` cannot be confused with the surrounding structure. Symbol
|
|
107
|
+
* keys and non-enumerable properties are ignored.
|
|
108
|
+
*
|
|
109
|
+
* @param object - The object to serialize.
|
|
110
|
+
* @param refs - The circular reference context shared with the surrounding serialization.
|
|
111
|
+
* @returns The serialized string.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* serializePlainObject({ b: 2, a: 1 }, new Map()); // "{'a':1,'b':2}"
|
|
115
|
+
*/
|
|
116
|
+
function serializePlainObject(object, refs) {
|
|
117
|
+
const keys = Object.keys(object).sort();
|
|
118
|
+
let result = "{";
|
|
119
|
+
for (let i = 0; i < keys.length; i++) {
|
|
120
|
+
const key = keys[i];
|
|
121
|
+
if (i > 0) result += ",";
|
|
122
|
+
result += `${serializeString(key)}:${serializeValue(object[key], refs)}`;
|
|
123
|
+
}
|
|
124
|
+
return result + "}";
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/predicate/isArrayBuffer.ts
|
|
128
|
+
/**
|
|
129
|
+
* Checks if a given value is `ArrayBuffer`.
|
|
130
|
+
*
|
|
131
|
+
* This function can also serve as a type predicate in TypeScript, narrowing the type of the argument to `ArrayBuffer`.
|
|
132
|
+
*
|
|
133
|
+
* @param value The value to check if it is a `ArrayBuffer`.
|
|
134
|
+
* @returns Returns `true` if `value` is a `ArrayBuffer`, else `false`.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const value1 = new ArrayBuffer();
|
|
138
|
+
* const value2 = new Array();
|
|
139
|
+
* const value3 = new Map();
|
|
140
|
+
*
|
|
141
|
+
* console.log(isArrayBuffer(value1)); // true
|
|
142
|
+
* console.log(isArrayBuffer(value2)); // false
|
|
143
|
+
* console.log(isArrayBuffer(value3)); // false
|
|
144
|
+
*/
|
|
145
|
+
function isArrayBuffer(value) {
|
|
146
|
+
return value instanceof ArrayBuffer;
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/predicate/isDate.ts
|
|
150
|
+
/**
|
|
151
|
+
* Checks if `value` is a Date object.
|
|
152
|
+
*
|
|
153
|
+
* @param value The value to check.
|
|
154
|
+
* @returns Returns `true` if `value` is a Date object, `false` otherwise.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* const value1 = new Date();
|
|
158
|
+
* const value2 = '2024-01-01';
|
|
159
|
+
*
|
|
160
|
+
* console.log(isDate(value1)); // true
|
|
161
|
+
* console.log(isDate(value2)); // false
|
|
162
|
+
*/
|
|
163
|
+
function isDate(value) {
|
|
164
|
+
return value instanceof Date;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/predicate/isError.ts
|
|
168
|
+
/**
|
|
169
|
+
* Checks if `value` is an Error object.
|
|
170
|
+
*
|
|
171
|
+
* @param value The value to check.
|
|
172
|
+
* @returns Returns `true` if `value` is an Error object, `false` otherwise.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* console.log(isError(new Error())); // true
|
|
177
|
+
* console.log(isError('Error')); // false
|
|
178
|
+
* console.log(isError({ name: 'Error', message: '' })); // false
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
function isError(value) {
|
|
182
|
+
return value instanceof Error;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/predicate/isMap.ts
|
|
186
|
+
/**
|
|
187
|
+
* Checks if a given value is `Map`.
|
|
188
|
+
*
|
|
189
|
+
* This function can also serve as a type predicate in TypeScript, narrowing the type of the argument to `Map`.
|
|
190
|
+
*
|
|
191
|
+
* @param value The value to check if it is a `Map`.
|
|
192
|
+
* @returns Returns `true` if `value` is a `Map`, else `false`.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* const value1 = new Map();
|
|
196
|
+
* const value2 = new Set();
|
|
197
|
+
* const value3 = new WeakMap();
|
|
198
|
+
*
|
|
199
|
+
* console.log(isMap(value1)); // true
|
|
200
|
+
* console.log(isMap(value2)); // false
|
|
201
|
+
* console.log(isMap(value3)); // false
|
|
202
|
+
*/
|
|
203
|
+
function isMap(value) {
|
|
204
|
+
return value instanceof Map;
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/predicate/isPlainObject.ts
|
|
208
|
+
/**
|
|
209
|
+
* Checks if a given value is a plain object.
|
|
210
|
+
*
|
|
211
|
+
* @param value - The value to check.
|
|
212
|
+
* @returns True if the value is a plain object, otherwise false.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* // ✅👇 True
|
|
217
|
+
*
|
|
218
|
+
* isPlainObject({ }); // ✅
|
|
219
|
+
* isPlainObject({ key: 'value' }); // ✅
|
|
220
|
+
* isPlainObject({ key: new Date() }); // ✅
|
|
221
|
+
* isPlainObject(new Object()); // ✅
|
|
222
|
+
* isPlainObject(Object.create(null)); // ✅
|
|
223
|
+
* isPlainObject({ nested: { key: true} }); // ✅
|
|
224
|
+
* isPlainObject(new Proxy({}, {})); // ✅
|
|
225
|
+
* isPlainObject({ [Symbol('tag')]: 'A' }); // ✅
|
|
226
|
+
*
|
|
227
|
+
* // ✅👇 (cross-realms, node context, workers, ...)
|
|
228
|
+
* const runInNewContext = await import('node:vm').then(
|
|
229
|
+
* (mod) => mod.runInNewContext
|
|
230
|
+
* );
|
|
231
|
+
* isPlainObject(runInNewContext('({})')); // ✅
|
|
232
|
+
*
|
|
233
|
+
* // ❌👇 False
|
|
234
|
+
*
|
|
235
|
+
* class Test { };
|
|
236
|
+
* isPlainObject(new Test()) // ❌
|
|
237
|
+
* isPlainObject(10); // ❌
|
|
238
|
+
* isPlainObject(null); // ❌
|
|
239
|
+
* isPlainObject('hello'); // ❌
|
|
240
|
+
* isPlainObject([]); // ❌
|
|
241
|
+
* isPlainObject(new Date()); // ❌
|
|
242
|
+
* isPlainObject(new Uint8Array([1])); // ❌
|
|
243
|
+
* isPlainObject(Buffer.from('ABC')); // ❌
|
|
244
|
+
* isPlainObject(Promise.resolve({})); // ❌
|
|
245
|
+
* isPlainObject(Object.create({})); // ❌
|
|
246
|
+
* isPlainObject(new (class Cls {})); // ❌
|
|
247
|
+
* isPlainObject(globalThis); // ❌,
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
function isPlainObject(value) {
|
|
251
|
+
if (!value || typeof value !== "object") return false;
|
|
252
|
+
const proto = Object.getPrototypeOf(value);
|
|
253
|
+
if (!(proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null)) return false;
|
|
254
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
255
|
+
}
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/predicate/isRegExp.ts
|
|
258
|
+
/**
|
|
259
|
+
* Checks if `value` is a RegExp.
|
|
260
|
+
*
|
|
261
|
+
* @param value The value to check.
|
|
262
|
+
* @returns Returns `true` if `value` is a RegExp, `false` otherwise.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* const value1 = /abc/;
|
|
266
|
+
* const value2 = '/abc/';
|
|
267
|
+
*
|
|
268
|
+
* console.log(isRegExp(value1)); // true
|
|
269
|
+
* console.log(isRegExp(value2)); // false
|
|
270
|
+
*/
|
|
271
|
+
function isRegExp(value) {
|
|
272
|
+
return value instanceof RegExp;
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
275
|
+
//#region src/predicate/isSet.ts
|
|
276
|
+
/**
|
|
277
|
+
* Checks if a given value is `Set`.
|
|
278
|
+
*
|
|
279
|
+
* This function can also serve as a type predicate in TypeScript, narrowing the type of the argument to `Set`.
|
|
280
|
+
*
|
|
281
|
+
* @param value The value to check if it is a `Set`.
|
|
282
|
+
* @returns Returns `true` if `value` is a `Set`, else `false`.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* const value1 = new Set();
|
|
286
|
+
* const value2 = new Map();
|
|
287
|
+
* const value3 = new WeakSet();
|
|
288
|
+
*
|
|
289
|
+
* console.log(isSet(value1)); // true
|
|
290
|
+
* console.log(isSet(value2)); // false
|
|
291
|
+
* console.log(isSet(value3)); // false
|
|
292
|
+
*/
|
|
293
|
+
function isSet(value) {
|
|
294
|
+
return value instanceof Set;
|
|
295
|
+
}
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/predicate/isTypedArray.ts
|
|
298
|
+
/**
|
|
299
|
+
* Checks if a value is a TypedArray.
|
|
300
|
+
* @param x The value to check.
|
|
301
|
+
* @returns Returns true if `x` is a TypedArray, false otherwise.
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* const arr = new Uint8Array([1, 2, 3]);
|
|
305
|
+
* isTypedArray(arr); // true
|
|
306
|
+
*
|
|
307
|
+
* const regularArray = [1, 2, 3];
|
|
308
|
+
* isTypedArray(regularArray); // false
|
|
309
|
+
*
|
|
310
|
+
* const buffer = new ArrayBuffer(16);
|
|
311
|
+
* isTypedArray(buffer); // false
|
|
312
|
+
*/
|
|
313
|
+
function isTypedArray(x) {
|
|
314
|
+
return ArrayBuffer.isView(x) && !(x instanceof DataView);
|
|
315
|
+
}
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/util/serialize/serializeObject.ts
|
|
318
|
+
/**
|
|
319
|
+
* Serializes an object, handling circular references and repeated references.
|
|
320
|
+
*
|
|
321
|
+
* The first time an object is visited, it is registered as `#ref{n}` where `n`
|
|
322
|
+
* is the visit order; if the object is reached again while it is still being
|
|
323
|
+
* serialized, the back-reference is emitted instead. Once completed, the
|
|
324
|
+
* serialized string is memoized so that repeated references serialize
|
|
325
|
+
* in constant time.
|
|
326
|
+
*
|
|
327
|
+
* @param value - The object to serialize, or `null`.
|
|
328
|
+
* @param refs - The circular reference context shared across one serialization.
|
|
329
|
+
* @returns The serialized string.
|
|
330
|
+
* @throws {TypeError} If the object cannot be serialized.
|
|
331
|
+
*/
|
|
332
|
+
function serializeObject(value, refs) {
|
|
333
|
+
if (value === null) return "null";
|
|
334
|
+
const cached = refs.get(value);
|
|
335
|
+
if (cached !== void 0) return cached;
|
|
336
|
+
refs.set(value, `#ref${refs.size}`);
|
|
337
|
+
const result = serializeObjectImpl(value, refs);
|
|
338
|
+
refs.set(value, result);
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
function serializeObjectImpl(value, refs) {
|
|
342
|
+
if (Array.isArray(value)) return serializeArray(value, refs);
|
|
343
|
+
if (isPlainObject(value)) return serializePlainObject(value, refs);
|
|
344
|
+
if (isDate(value)) return Number.isNaN(value.getTime()) ? "Date(null)" : `Date(${serializeString(value.toISOString())})`;
|
|
345
|
+
if (isRegExp(value)) return `RegExp(${value.toString()})`;
|
|
346
|
+
if (isSet(value)) return `Set${serializeArray(Array.from(value).sort((a, b) => compareValues(a, b, refs)), refs)}`;
|
|
347
|
+
if (isMap(value)) return serializeEntries("Map", value.entries(), refs);
|
|
348
|
+
if (isTypedArray(value)) {
|
|
349
|
+
const name = value[Symbol.toStringTag];
|
|
350
|
+
if (name === "BigInt64Array" || name === "BigUint64Array") return `${name}[${value.join("n,")}${value.length > 0 ? "n" : ""}]`;
|
|
351
|
+
return `${name}[${value.join(",")}]`;
|
|
352
|
+
}
|
|
353
|
+
if (isArrayBuffer(value)) return `ArrayBuffer[${new Uint8Array(value).join(",")}]`;
|
|
354
|
+
if (isError(value)) return `Error(${value.name}: ${serializeString(value.message)})`;
|
|
355
|
+
const tag = Object.prototype.toString.call(value).slice(8, -1);
|
|
356
|
+
if (tag === "Object") return serializeClassInstance(value, refs);
|
|
357
|
+
if (typeof value.entries === "function") return serializeEntries(tag, value.entries(), refs);
|
|
358
|
+
throw new TypeError(`Cannot serialize ${tag}`);
|
|
359
|
+
}
|
|
360
|
+
function serializeArray(array, refs) {
|
|
361
|
+
let result = "[";
|
|
362
|
+
for (let i = 0; i < array.length; i++) {
|
|
363
|
+
if (i > 0) result += ",";
|
|
364
|
+
result += serializeValue(array[i], refs);
|
|
365
|
+
}
|
|
366
|
+
return result + "]";
|
|
367
|
+
}
|
|
368
|
+
function serializeEntries(tag, entries, refs) {
|
|
369
|
+
const sortedEntries = Array.from(entries).sort((a, b) => compareValues(a[0], b[0], refs));
|
|
370
|
+
let result = `${tag}{`;
|
|
371
|
+
for (let i = 0; i < sortedEntries.length; i++) {
|
|
372
|
+
const [key, value] = sortedEntries[i];
|
|
373
|
+
if (i > 0) result += ",";
|
|
374
|
+
result += `${serializeValue(key, refs)}:${serializeValue(value, refs)}`;
|
|
375
|
+
}
|
|
376
|
+
return result + "}";
|
|
377
|
+
}
|
|
378
|
+
function serializeClassInstance(value, refs) {
|
|
379
|
+
const constructor = value.constructor;
|
|
380
|
+
const name = constructor === Object || constructor === void 0 ? "" : constructor.name;
|
|
381
|
+
if ("toJSON" in value && typeof value.toJSON === "function") {
|
|
382
|
+
const json = value.toJSON();
|
|
383
|
+
if (json !== null && typeof json === "object") return name + serializeObject(json, refs);
|
|
384
|
+
return `${name}(${serializeValue(json, refs)})`;
|
|
385
|
+
}
|
|
386
|
+
return name + serializePlainObject(value, refs);
|
|
387
|
+
}
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region src/util/serialize/serializeSymbol.ts
|
|
390
|
+
/**
|
|
391
|
+
* Serializes a symbol into a string using its description.
|
|
392
|
+
*
|
|
393
|
+
* The description is quoted like any other string, so a symbol without a
|
|
394
|
+
* description (`Symbol()`) is distinguishable from one with an empty
|
|
395
|
+
* description (`Symbol('')`). Note that two different symbols with the same
|
|
396
|
+
* description still serialize to the same string, since a symbol's identity
|
|
397
|
+
* cannot be captured in a string.
|
|
398
|
+
*
|
|
399
|
+
* @param value - The symbol to serialize.
|
|
400
|
+
* @returns The serialized string.
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* serializeSymbol(Symbol('test')); // "Symbol('test')"
|
|
404
|
+
* serializeSymbol(Symbol()); // "Symbol()"
|
|
405
|
+
*/
|
|
406
|
+
function serializeSymbol(value) {
|
|
407
|
+
return value.description === void 0 ? "Symbol()" : `Symbol(${serializeString(value.description)})`;
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/util/serialize/serialize.ts
|
|
411
|
+
/**
|
|
412
|
+
* Serializes any value into a stable string.
|
|
413
|
+
*
|
|
414
|
+
* Two values with the same structure always serialize to the same string,
|
|
415
|
+
* regardless of key insertion order, so the output is suitable for hashing,
|
|
416
|
+
* cache keys, and change detection. It is not designed for security purposes;
|
|
417
|
+
* intentional collisions can be crafted from user input.
|
|
418
|
+
*
|
|
419
|
+
* Plain object keys, `Map` keys, and `Set` values are sorted, so the output
|
|
420
|
+
* does not depend on insertion order. String keys are always quoted, so a
|
|
421
|
+
* string key never collides with a key of another type. Circular references
|
|
422
|
+
* are serialized as `#ref{n}` back-references, where `n` is the order in
|
|
423
|
+
* which the object was first visited.
|
|
424
|
+
*
|
|
425
|
+
* Objects that cannot be serialized meaningfully, such as `Promise`, `WeakMap`,
|
|
426
|
+
* or `Blob`, throw a `TypeError`.
|
|
427
|
+
*
|
|
428
|
+
* @param value - The value to serialize.
|
|
429
|
+
* @returns The serialized string.
|
|
430
|
+
* @throws {TypeError} If the value contains an object that cannot be serialized.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* serialize({ b: 2, a: 1 }); // "{'a':1,'b':2}"
|
|
434
|
+
* serialize([1, 2n, 'a', { k: 1 }]); // "[1,2n,'a',{'k':1}]"
|
|
435
|
+
* serialize(new Set([3, 1, 2])); // "Set[1,2,3]"
|
|
436
|
+
* serialize(new Date(0)); // "Date('1970-01-01T00:00:00.000Z')"
|
|
437
|
+
*
|
|
438
|
+
* const obj = {};
|
|
439
|
+
* obj.self = obj;
|
|
440
|
+
* serialize(obj); // "{'self':#ref0}"
|
|
441
|
+
*/
|
|
442
|
+
function serialize(value) {
|
|
443
|
+
return serializeValue(value, /* @__PURE__ */ new Map());
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Serializes a value with a shared circular reference context.
|
|
447
|
+
*
|
|
448
|
+
* @param value - The value to serialize.
|
|
449
|
+
* @param refs - Objects that are being serialized or have been serialized,
|
|
450
|
+
* mapped to their back-reference placeholder or completed serialization.
|
|
451
|
+
* @returns The serialized string.
|
|
452
|
+
*/
|
|
453
|
+
function serializeValue(value, refs) {
|
|
454
|
+
switch (typeof value) {
|
|
455
|
+
case "string": return serializeString(value);
|
|
456
|
+
case "number": return serializeNumber(value);
|
|
457
|
+
case "bigint": return serializeBigInt(value);
|
|
458
|
+
case "symbol": return serializeSymbol(value);
|
|
459
|
+
case "function": return serializeFunction(value);
|
|
460
|
+
case "object": return serializeObject(value, refs);
|
|
461
|
+
case "boolean": return String(value);
|
|
462
|
+
case "undefined": return "undefined";
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/util/hash/node.ts
|
|
467
|
+
/**
|
|
468
|
+
* Hashes any value into a stable 43-character string.
|
|
469
|
+
*
|
|
470
|
+
* The value is serialized with `serialize`, so two values with the same
|
|
471
|
+
* structure always hash to the same string regardless of key insertion
|
|
472
|
+
* order, and then digested with SHA-256 and encoded in Base64URL format.
|
|
473
|
+
*
|
|
474
|
+
* The hash is stable across platforms, but it is not designed for security
|
|
475
|
+
* purposes; intentional collisions can be crafted from user input.
|
|
476
|
+
*
|
|
477
|
+
* This entry uses the native `node:crypto` implementation and requires
|
|
478
|
+
* Node.js 20.12 or later; in browsers, a pure JavaScript implementation
|
|
479
|
+
* with identical output is used instead.
|
|
480
|
+
*
|
|
481
|
+
* @param value - The value to hash.
|
|
482
|
+
* @returns The Base64URL-encoded SHA-256 hash of the serialized value.
|
|
483
|
+
* @throws {TypeError} If the value contains an object that cannot be serialized.
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* hash({ b: 2, a: 1 }) === hash({ a: 1, b: 2 }); // true
|
|
487
|
+
* hash([1, 2, 3]); // "phXuruId5Red4IDejDBSyNqQEThAa6ccOMAyhF99VPQ" (43 characters)
|
|
488
|
+
*/
|
|
489
|
+
function hash(value) {
|
|
490
|
+
return hash$1("sha256", serialize(value), "base64url");
|
|
491
|
+
}
|
|
492
|
+
//#endregion
|
|
493
|
+
export { hash };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
//#region src/util/hash/sha256.ts
|
|
2
|
+
const K = new Int32Array([
|
|
3
|
+
1116352408,
|
|
4
|
+
1899447441,
|
|
5
|
+
3049323471,
|
|
6
|
+
3921009573,
|
|
7
|
+
961987163,
|
|
8
|
+
1508970993,
|
|
9
|
+
2453635748,
|
|
10
|
+
2870763221,
|
|
11
|
+
3624381080,
|
|
12
|
+
310598401,
|
|
13
|
+
607225278,
|
|
14
|
+
1426881987,
|
|
15
|
+
1925078388,
|
|
16
|
+
2162078206,
|
|
17
|
+
2614888103,
|
|
18
|
+
3248222580,
|
|
19
|
+
3835390401,
|
|
20
|
+
4022224774,
|
|
21
|
+
264347078,
|
|
22
|
+
604807628,
|
|
23
|
+
770255983,
|
|
24
|
+
1249150122,
|
|
25
|
+
1555081692,
|
|
26
|
+
1996064986,
|
|
27
|
+
2554220882,
|
|
28
|
+
2821834349,
|
|
29
|
+
2952996808,
|
|
30
|
+
3210313671,
|
|
31
|
+
3336571891,
|
|
32
|
+
3584528711,
|
|
33
|
+
113926993,
|
|
34
|
+
338241895,
|
|
35
|
+
666307205,
|
|
36
|
+
773529912,
|
|
37
|
+
1294757372,
|
|
38
|
+
1396182291,
|
|
39
|
+
1695183700,
|
|
40
|
+
1986661051,
|
|
41
|
+
2177026350,
|
|
42
|
+
2456956037,
|
|
43
|
+
2730485921,
|
|
44
|
+
2820302411,
|
|
45
|
+
3259730800,
|
|
46
|
+
3345764771,
|
|
47
|
+
3516065817,
|
|
48
|
+
3600352804,
|
|
49
|
+
4094571909,
|
|
50
|
+
275423344,
|
|
51
|
+
430227734,
|
|
52
|
+
506948616,
|
|
53
|
+
659060556,
|
|
54
|
+
883997877,
|
|
55
|
+
958139571,
|
|
56
|
+
1322822218,
|
|
57
|
+
1537002063,
|
|
58
|
+
1747873779,
|
|
59
|
+
1955562222,
|
|
60
|
+
2024104815,
|
|
61
|
+
2227730452,
|
|
62
|
+
2361852424,
|
|
63
|
+
2428436474,
|
|
64
|
+
2756734187,
|
|
65
|
+
3204031479,
|
|
66
|
+
3329325298
|
|
67
|
+
]);
|
|
68
|
+
const BASE64_URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
69
|
+
/**
|
|
70
|
+
* Computes the SHA-256 digest of a string and encodes it in Base64URL
|
|
71
|
+
* format without padding.
|
|
72
|
+
*
|
|
73
|
+
* The output is byte-identical to
|
|
74
|
+
* `crypto.hash('sha256', data, 'base64url')` in Node.js.
|
|
75
|
+
*
|
|
76
|
+
* @param data - The string to hash. Encoded as UTF-8 before hashing.
|
|
77
|
+
* @returns The 43-character Base64URL-encoded digest.
|
|
78
|
+
*/
|
|
79
|
+
function sha256(data) {
|
|
80
|
+
const bytes = new TextEncoder().encode(data);
|
|
81
|
+
const byteLength = bytes.length;
|
|
82
|
+
const paddedLength = ((byteLength + 8 >> 6) + 1) * 64;
|
|
83
|
+
const padded = new Uint8Array(paddedLength);
|
|
84
|
+
padded.set(bytes);
|
|
85
|
+
padded[byteLength] = 128;
|
|
86
|
+
const view = new DataView(padded.buffer);
|
|
87
|
+
view.setUint32(paddedLength - 8, Math.floor(byteLength / 536870912));
|
|
88
|
+
view.setUint32(paddedLength - 4, byteLength << 3 >>> 0);
|
|
89
|
+
let h0 = 1779033703;
|
|
90
|
+
let h1 = -1150833019;
|
|
91
|
+
let h2 = 1013904242;
|
|
92
|
+
let h3 = -1521486534;
|
|
93
|
+
let h4 = 1359893119;
|
|
94
|
+
let h5 = -1694144372;
|
|
95
|
+
let h6 = 528734635;
|
|
96
|
+
let h7 = 1541459225;
|
|
97
|
+
const w = new Int32Array(64);
|
|
98
|
+
for (let offset = 0; offset < paddedLength; offset += 64) {
|
|
99
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4) | 0;
|
|
100
|
+
for (let i = 16; i < 64; i++) {
|
|
101
|
+
const x = w[i - 15];
|
|
102
|
+
const y = w[i - 2];
|
|
103
|
+
const sigma0 = (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ x >>> 3 | 0;
|
|
104
|
+
const sigma1 = (y >>> 17 | y << 15) ^ (y >>> 19 | y << 13) ^ y >>> 10 | 0;
|
|
105
|
+
w[i] = w[i - 16] + sigma0 + w[i - 7] + sigma1 | 0;
|
|
106
|
+
}
|
|
107
|
+
let a = h0;
|
|
108
|
+
let b = h1;
|
|
109
|
+
let c = h2;
|
|
110
|
+
let d = h3;
|
|
111
|
+
let e = h4;
|
|
112
|
+
let f = h5;
|
|
113
|
+
let g = h6;
|
|
114
|
+
let h = h7;
|
|
115
|
+
for (let i = 0; i < 64; i++) {
|
|
116
|
+
const bigSigma1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
|
|
117
|
+
const ch = e & f ^ ~e & g;
|
|
118
|
+
const t1 = h + bigSigma1 + ch + K[i] + w[i] | 0;
|
|
119
|
+
const t2 = ((a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10)) + (a & b ^ a & c ^ b & c) | 0;
|
|
120
|
+
h = g;
|
|
121
|
+
g = f;
|
|
122
|
+
f = e;
|
|
123
|
+
e = d + t1 | 0;
|
|
124
|
+
d = c;
|
|
125
|
+
c = b;
|
|
126
|
+
b = a;
|
|
127
|
+
a = t1 + t2 | 0;
|
|
128
|
+
}
|
|
129
|
+
h0 = h0 + a | 0;
|
|
130
|
+
h1 = h1 + b | 0;
|
|
131
|
+
h2 = h2 + c | 0;
|
|
132
|
+
h3 = h3 + d | 0;
|
|
133
|
+
h4 = h4 + e | 0;
|
|
134
|
+
h5 = h5 + f | 0;
|
|
135
|
+
h6 = h6 + g | 0;
|
|
136
|
+
h7 = h7 + h | 0;
|
|
137
|
+
}
|
|
138
|
+
const digest = new Uint8Array(32);
|
|
139
|
+
const digestView = new DataView(digest.buffer);
|
|
140
|
+
digestView.setInt32(0, h0);
|
|
141
|
+
digestView.setInt32(4, h1);
|
|
142
|
+
digestView.setInt32(8, h2);
|
|
143
|
+
digestView.setInt32(12, h3);
|
|
144
|
+
digestView.setInt32(16, h4);
|
|
145
|
+
digestView.setInt32(20, h5);
|
|
146
|
+
digestView.setInt32(24, h6);
|
|
147
|
+
digestView.setInt32(28, h7);
|
|
148
|
+
let result = "";
|
|
149
|
+
for (let i = 0; i < 30; i += 3) {
|
|
150
|
+
const n = digest[i] << 16 | digest[i + 1] << 8 | digest[i + 2];
|
|
151
|
+
result += BASE64_URL_ALPHABET[n >>> 18 & 63] + BASE64_URL_ALPHABET[n >>> 12 & 63] + BASE64_URL_ALPHABET[n >>> 6 & 63] + BASE64_URL_ALPHABET[n & 63];
|
|
152
|
+
}
|
|
153
|
+
const n = digest[30] << 8 | digest[31];
|
|
154
|
+
result += BASE64_URL_ALPHABET[n >>> 10 & 63] + BASE64_URL_ALPHABET[n >>> 4 & 63] + BASE64_URL_ALPHABET[n << 2 & 63];
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
exports.sha256 = sha256;
|