@tanstack/db-ivm 0.1.20 → 0.1.24
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/cjs/d2.cjs +12 -2
- package/dist/cjs/d2.cjs.map +1 -1
- package/dist/cjs/hashing/hash.cjs +92 -104
- package/dist/cjs/hashing/hash.cjs.map +1 -1
- package/dist/cjs/hashing/hash.d.cts +5 -0
- package/dist/cjs/hashing/temporal.cjs +18 -0
- package/dist/cjs/hashing/temporal.cjs.map +1 -0
- package/dist/cjs/hashing/temporal.d.cts +5 -0
- package/dist/cjs/multiset.cjs +47 -92
- package/dist/cjs/multiset.cjs.map +1 -1
- package/dist/cjs/multiset.d.cts +16 -0
- package/dist/cjs/operators/distinct.cjs.map +1 -1
- package/dist/cjs/operators/distinct.d.cts +1 -1
- package/dist/cjs/operators/groupBy.cjs +2 -2
- package/dist/cjs/operators/groupBy.cjs.map +1 -1
- package/dist/cjs/operators/groupedTopKWithFractionalIndex.cjs +4 -5
- package/dist/cjs/operators/groupedTopKWithFractionalIndex.cjs.map +1 -1
- package/dist/cjs/operators/topKArray.cjs +2 -0
- package/dist/cjs/operators/topKArray.cjs.map +1 -1
- package/dist/cjs/operators/topKState.cjs +30 -0
- package/dist/cjs/operators/topKState.cjs.map +1 -1
- package/dist/cjs/operators/topKState.d.cts +9 -0
- package/dist/cjs/operators/topKWithFractionalIndex.cjs +4 -5
- package/dist/cjs/operators/topKWithFractionalIndex.cjs.map +1 -1
- package/dist/cjs/utils.cjs +15 -61
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +3 -32
- package/dist/esm/d2.js +12 -2
- package/dist/esm/d2.js.map +1 -1
- package/dist/esm/hashing/hash.d.ts +5 -0
- package/dist/esm/hashing/hash.js +92 -104
- package/dist/esm/hashing/hash.js.map +1 -1
- package/dist/esm/hashing/temporal.d.ts +5 -0
- package/dist/esm/hashing/temporal.js +18 -0
- package/dist/esm/hashing/temporal.js.map +1 -0
- package/dist/esm/multiset.d.ts +16 -0
- package/dist/esm/multiset.js +48 -93
- package/dist/esm/multiset.js.map +1 -1
- package/dist/esm/operators/distinct.d.ts +1 -1
- package/dist/esm/operators/distinct.js.map +1 -1
- package/dist/esm/operators/groupBy.js +2 -2
- package/dist/esm/operators/groupBy.js.map +1 -1
- package/dist/esm/operators/groupedTopKWithFractionalIndex.js +5 -6
- package/dist/esm/operators/groupedTopKWithFractionalIndex.js.map +1 -1
- package/dist/esm/operators/topKArray.js +2 -0
- package/dist/esm/operators/topKArray.js.map +1 -1
- package/dist/esm/operators/topKState.d.ts +9 -0
- package/dist/esm/operators/topKState.js +31 -1
- package/dist/esm/operators/topKState.js.map +1 -1
- package/dist/esm/operators/topKWithFractionalIndex.js +5 -6
- package/dist/esm/operators/topKWithFractionalIndex.js.map +1 -1
- package/dist/esm/utils.d.ts +3 -32
- package/dist/esm/utils.js +15 -61
- package/dist/esm/utils.js.map +1 -1
- package/package.json +3 -1
- package/src/d2.ts +15 -2
- package/src/hashing/hash.ts +147 -135
- package/src/hashing/temporal.ts +25 -0
- package/src/multiset.ts +73 -132
- package/src/operators/distinct.ts +5 -3
- package/src/operators/groupBy.ts +8 -2
- package/src/operators/groupedTopKWithFractionalIndex.ts +10 -6
- package/src/operators/topKArray.ts +4 -0
- package/src/operators/topKState.ts +42 -0
- package/src/operators/topKWithFractionalIndex.ts +10 -6
- package/src/operators/topKWithFractionalIndexBTree.ts +7 -8
- package/src/utils.ts +20 -76
package/dist/cjs/utils.cjs
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
|
|
4
|
-
constructor(defaultValue, entries) {
|
|
5
|
-
super(entries);
|
|
6
|
-
this.defaultValue = defaultValue;
|
|
7
|
-
}
|
|
8
|
-
get(key) {
|
|
9
|
-
if (!this.has(key)) {
|
|
10
|
-
return this.defaultValue();
|
|
11
|
-
}
|
|
12
|
-
return super.get(key);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Update the value for a key using a function.
|
|
16
|
-
*/
|
|
17
|
-
update(key, updater) {
|
|
18
|
-
const value = this.get(key);
|
|
19
|
-
const newValue = updater(value);
|
|
20
|
-
this.set(key, newValue);
|
|
21
|
-
return newValue;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
3
|
+
const temporal = require("./hashing/temporal.cjs");
|
|
24
4
|
const chunkSize = 3e4;
|
|
25
5
|
function chunkedArrayPush(array, other) {
|
|
26
6
|
if (other.length <= chunkSize) {
|
|
@@ -48,43 +28,16 @@ function binarySearch(array, value, comparator) {
|
|
|
48
28
|
}
|
|
49
29
|
return low;
|
|
50
30
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*/
|
|
61
|
-
getId(value) {
|
|
62
|
-
if (typeof value !== `object` || value === null) {
|
|
63
|
-
const str = String(value);
|
|
64
|
-
let hashValue = 0;
|
|
65
|
-
for (let i = 0; i < str.length; i++) {
|
|
66
|
-
const char = str.charCodeAt(i);
|
|
67
|
-
hashValue = (hashValue << 5) - hashValue + char;
|
|
68
|
-
hashValue = hashValue & hashValue;
|
|
69
|
-
}
|
|
70
|
-
return hashValue;
|
|
71
|
-
}
|
|
72
|
-
if (!this.objectIds.has(value)) {
|
|
73
|
-
this.objectIds.set(value, this.nextId++);
|
|
74
|
-
}
|
|
75
|
-
return this.objectIds.get(value);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Get a string representation of the ID for use in composite keys.
|
|
79
|
-
*/
|
|
80
|
-
getStringId(value) {
|
|
81
|
-
if (value === null) return `null`;
|
|
82
|
-
if (value === void 0) return `undefined`;
|
|
83
|
-
if (typeof value !== `object`) return `str_${String(value)}`;
|
|
84
|
-
return `obj_${this.getId(value)}`;
|
|
85
|
-
}
|
|
31
|
+
const objectIds = /* @__PURE__ */ new WeakMap();
|
|
32
|
+
let nextObjectId = 0;
|
|
33
|
+
function getStringId(value) {
|
|
34
|
+
if (value === null) return `null`;
|
|
35
|
+
if (value === void 0) return `undefined`;
|
|
36
|
+
if (typeof value !== `object`) return `str_${String(value)}`;
|
|
37
|
+
let id = objectIds.get(value);
|
|
38
|
+
if (id === void 0) objectIds.set(value, id = nextObjectId++);
|
|
39
|
+
return `obj_${id}`;
|
|
86
40
|
}
|
|
87
|
-
const globalObjectIdGenerator = new ObjectIdGenerator();
|
|
88
41
|
function diffHalfOpen(a, b) {
|
|
89
42
|
const [a1, a2] = a;
|
|
90
43
|
const [b1, b2] = b;
|
|
@@ -142,7 +95,7 @@ function isJsonSafeStructuralValue(value, ancestors) {
|
|
|
142
95
|
return false;
|
|
143
96
|
}
|
|
144
97
|
return withAcyclicValue(value, ancestors, () => {
|
|
145
|
-
if (value instanceof Date || value instanceof RegExp || value instanceof Uint8Array || value instanceof Map || value instanceof Set) {
|
|
98
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof Uint8Array || temporal.isTemporal(value) || value instanceof Map || value instanceof Set) {
|
|
146
99
|
return false;
|
|
147
100
|
}
|
|
148
101
|
return Array.isArray(value) ? value.every((item) => isJsonSafeStructuralValue(item, ancestors)) : Object.keys(value).every(
|
|
@@ -199,6 +152,9 @@ function toCanonicalValue(value, ancestors) {
|
|
|
199
152
|
if (value instanceof Uint8Array) {
|
|
200
153
|
return [`bytes`, Array.from(value)];
|
|
201
154
|
}
|
|
155
|
+
if (temporal.isTemporal(value)) {
|
|
156
|
+
return [`temporal`, value[Symbol.toStringTag], value.toString()];
|
|
157
|
+
}
|
|
202
158
|
if (Array.isArray(value)) {
|
|
203
159
|
return [`array`, value.map((item) => toCanonicalValue(item, ancestors))];
|
|
204
160
|
}
|
|
@@ -249,12 +205,10 @@ function withAcyclicValue(value, ancestors, encode) {
|
|
|
249
205
|
ancestors.delete(value);
|
|
250
206
|
}
|
|
251
207
|
}
|
|
252
|
-
exports.DefaultMap = DefaultMap;
|
|
253
|
-
exports.ObjectIdGenerator = ObjectIdGenerator;
|
|
254
208
|
exports.binarySearch = binarySearch;
|
|
255
209
|
exports.chunkedArrayPush = chunkedArrayPush;
|
|
256
210
|
exports.compareKeys = compareKeys;
|
|
257
211
|
exports.diffHalfOpen = diffHalfOpen;
|
|
258
|
-
exports.
|
|
212
|
+
exports.getStringId = getStringId;
|
|
259
213
|
exports.serializeValue = serializeValue;
|
|
260
214
|
//# sourceMappingURL=utils.cjs.map
|
package/dist/cjs/utils.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["/**\n * Simple assertion function for runtime checks.\n * Throws an error if the condition is false.\n */\nexport function assert(\n condition: unknown,\n message?: string,\n): asserts condition {\n if (!condition) {\n throw new Error(message || `Assertion failed`)\n }\n}\n\n/**\n * A map that returns a default value for keys that are not present.\n */\nexport class DefaultMap<K, V> extends Map<K, V> {\n constructor(\n private defaultValue: () => V,\n entries?: Iterable<[K, V]>,\n ) {\n super(entries)\n }\n\n get(key: K): V {\n if (!this.has(key)) {\n // this.set(key, this.defaultValue())\n return this.defaultValue()\n }\n return super.get(key)!\n }\n\n /**\n * Update the value for a key using a function.\n */\n update(key: K, updater: (value: V) => V): V {\n const value = this.get(key)\n const newValue = updater(value)\n this.set(key, newValue)\n return newValue\n }\n}\n\n// JS engines have various limits on how many args can be passed to a function\n// with a spread operator, so we need to split the operation into chunks\n// 32767 is the max for Chrome 14, all others are higher\n// TODO: investigate the performance of this and other approaches\nconst chunkSize = 30000\nexport function chunkedArrayPush(array: Array<unknown>, other: Array<unknown>) {\n if (other.length <= chunkSize) {\n array.push(...other)\n } else {\n for (let i = 0; i < other.length; i += chunkSize) {\n const chunk = other.slice(i, i + chunkSize)\n array.push(...chunk)\n }\n }\n}\n\nexport function binarySearch<T>(\n array: Array<T>,\n value: T,\n comparator: (a: T, b: T) => number,\n): number {\n let low = 0\n let high = array.length\n while (low < high) {\n const mid = Math.floor((low + high) / 2)\n const comparison = comparator(array[mid]!, value)\n if (comparison < 0) {\n low = mid + 1\n } else if (comparison > 0) {\n high = mid\n } else {\n return mid\n }\n }\n return low\n}\n\n/**\n * Utility for generating unique IDs for objects and values.\n * Uses WeakMap for object reference tracking and consistent hashing for primitives.\n */\nexport class ObjectIdGenerator {\n private objectIds = new WeakMap<object, number>()\n private nextId = 0\n\n /**\n * Get a unique identifier for any value.\n * - Objects: Uses WeakMap for reference-based identity\n * - Primitives: Uses consistent string-based hashing\n */\n getId(value: any): number {\n // For primitives, use a simple hash of their string representation\n if (typeof value !== `object` || value === null) {\n const str = String(value)\n let hashValue = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hashValue = (hashValue << 5) - hashValue + char\n hashValue = hashValue & hashValue // Convert to 32-bit integer\n }\n return hashValue\n }\n\n // For objects, use WeakMap to assign unique IDs\n if (!this.objectIds.has(value)) {\n this.objectIds.set(value, this.nextId++)\n }\n return this.objectIds.get(value)!\n }\n\n /**\n * Get a string representation of the ID for use in composite keys.\n */\n getStringId(value: any): string {\n if (value === null) return `null`\n if (value === undefined) return `undefined`\n if (typeof value !== `object`) return `str_${String(value)}`\n\n return `obj_${this.getId(value)}`\n }\n}\n\n/**\n * Global instance for cases where a shared object ID space is needed.\n */\nexport const globalObjectIdGenerator = new ObjectIdGenerator()\n\nexport function* concatIterable<T>(\n ...iterables: Array<Iterable<T>>\n): Iterable<T> {\n for (const iterable of iterables) {\n yield* iterable\n }\n}\n\nexport function* mapIterable<T, U>(\n it: Iterable<T>,\n fn: (t: T) => U,\n): Iterable<U> {\n for (const t of it) {\n yield fn(t)\n }\n}\n\nexport type HRange = [number, number] // half-open [start, end[ i.e. end is exclusive\n\n/**\n * Computes the difference between two half-open ranges.\n * @param a - The first half-open range\n * @param b - The second half-open range\n * @returns The difference between the two ranges\n */\nexport function diffHalfOpen(a: HRange, b: HRange) {\n const [a1, a2] = a\n const [b1, b2] = b\n\n // A \\ B can be up to two segments (left and right of the overlap)\n const onlyInA: Array<number> = [\n ...range(a1, Math.min(a2, b1)), // left side of A outside B\n ...range(Math.max(a1, b2), a2), // right side of A outside B\n ]\n\n // B \\ A similarly\n const onlyInB: Array<number> = [\n ...range(b1, Math.min(b2, a1)),\n ...range(Math.max(b1, a2), b2),\n ]\n\n return { onlyInA, onlyInB }\n}\n\nfunction range(start: number, end: number): Array<number> {\n const out: Array<number> = []\n for (let i = start; i < end; i++) out.push(i)\n return out\n}\n\n/**\n * Compares two keys (string | number) in a consistent, deterministic way.\n * Handles mixed types by ordering strings before numbers.\n */\nexport function compareKeys(a: string | number, b: string | number): number {\n // Same type: compare directly\n if (typeof a === typeof b) {\n if (typeof a === `number` && typeof b === `number`) {\n const aIsNaN = Number.isNaN(a)\n const bIsNaN = Number.isNaN(b)\n if (aIsNaN || bIsNaN) {\n if (aIsNaN && bIsNaN) return 0\n return aIsNaN ? 1 : -1\n }\n }\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n // Different types: strings come before numbers\n return typeof a === `string` ? -1 : 1\n}\n\ntype CanonicalValue =\n | readonly [`undefined`]\n | readonly [`null`]\n | readonly [`boolean`, boolean]\n | readonly [`number`, number | `NaN` | `Infinity` | `-Infinity`]\n | readonly [`bigint`, string]\n | readonly [`string`, string]\n | readonly [`date`, number | `Invalid`]\n | readonly [`regexp`, string, string]\n | readonly [`bytes`, Array<number>]\n | readonly [`array`, Array<CanonicalValue>]\n | readonly [`map`, Array<readonly [CanonicalValue, CanonicalValue]>]\n | readonly [`set`, Array<CanonicalValue>]\n | readonly [`object`, Array<readonly [string, CanonicalValue]>]\n\n/**\n * Serializes a supported query value into one canonical key.\n *\n * JSON's native encoding is not suitable for relation keys: it merges BigInt\n * with strings when a replacer is used, merges NaN with null, drops undefined,\n * and depends on object insertion order. Ordinary JSON values keep their\n * established wire form. Values that need richer types use a reserved prefix\n * plus a structural, type-tagged encoding.\n */\nexport function serializeValue(value: unknown): string {\n if (isJsonSafeStructuralValue(value, new Set())) {\n return JSON.stringify(toStableJsonValue(value))\n }\n\n return `~${JSON.stringify(toCanonicalValue(value, new Set()))}`\n}\n\ntype JsonValue =\n | null\n | boolean\n | number\n | string\n | Array<JsonValue>\n | { [key: string]: JsonValue }\n\nfunction isJsonSafeStructuralValue(\n value: unknown,\n ancestors: Set<object>,\n): boolean {\n if (value === null) return true\n\n switch (typeof value) {\n case `boolean`:\n case `string`:\n return true\n case `number`:\n return Number.isFinite(value)\n case `undefined`:\n case `bigint`:\n case `symbol`:\n case `function`:\n return false\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Uint8Array ||\n value instanceof Map ||\n value instanceof Set\n ) {\n return false\n }\n\n return Array.isArray(value)\n ? value.every((item) => isJsonSafeStructuralValue(item, ancestors))\n : Object.keys(value).every((key) =>\n isJsonSafeStructuralValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n )\n })\n}\n\nfunction toStableJsonValue(value: unknown): JsonValue {\n if (\n value === null ||\n typeof value === `boolean` ||\n typeof value === `number` ||\n typeof value === `string`\n ) {\n return value\n }\n\n if (Array.isArray(value)) {\n return value.map(toStableJsonValue)\n }\n\n return Object.fromEntries(\n Object.keys(value as object)\n .sort()\n .map((key) => [\n key,\n toStableJsonValue((value as Record<string, unknown>)[key]),\n ]),\n )\n}\n\nfunction toCanonicalValue(\n value: unknown,\n ancestors: Set<object>,\n): CanonicalValue {\n if (value === undefined) return [`undefined`]\n if (value === null) return [`null`]\n\n switch (typeof value) {\n case `boolean`:\n return [`boolean`, value]\n case `number`:\n if (Number.isNaN(value)) return [`number`, `NaN`]\n if (value === Infinity) return [`number`, `Infinity`]\n if (value === -Infinity) return [`number`, `-Infinity`]\n return [`number`, value === 0 ? 0 : value]\n case `bigint`:\n return [`bigint`, value.toString()]\n case `string`:\n return [`string`, value]\n case `symbol`:\n case `function`:\n throw new TypeError(\n `Cannot serialize ${typeof value} as a structural relation key`,\n )\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (value instanceof Date) {\n const timestamp = value.getTime()\n return Number.isNaN(timestamp)\n ? ([`date`, `Invalid`] as const)\n : ([`date`, timestamp] as const)\n }\n\n if (value instanceof RegExp) {\n return [`regexp`, value.source, value.flags]\n }\n\n if (value instanceof Uint8Array) {\n return [`bytes`, Array.from(value)]\n }\n\n if (Array.isArray(value)) {\n return [`array`, value.map((item) => toCanonicalValue(item, ancestors))]\n }\n\n if (value instanceof Map) {\n const entries = [...value.entries()].map(\n ([key, entryValue]) =>\n [\n toCanonicalValue(key, ancestors),\n toCanonicalValue(entryValue, ancestors),\n ] as const,\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`map`, entries]\n }\n\n if (value instanceof Set) {\n const entries = [...value].map((entry) =>\n toCanonicalValue(entry, ancestors),\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`set`, entries]\n }\n\n const entries = Object.keys(value)\n .sort()\n .map(\n (key) =>\n [\n key,\n toCanonicalValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n ] as const,\n )\n return [`object`, entries]\n })\n}\n\nfunction compareSerializedValues(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction withAcyclicValue<T>(\n value: object,\n ancestors: Set<object>,\n encode: () => T,\n): T {\n if (ancestors.has(value)) {\n throw new TypeError(`Cannot serialize a cyclic structural relation key`)\n }\n\n ancestors.add(value)\n try {\n return encode()\n } finally {\n ancestors.delete(value)\n }\n}\n"],"names":["entries"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["import { isTemporal } from './hashing/temporal.js'\n\n/**\n * Simple assertion function for runtime checks.\n * Throws an error if the condition is false.\n */\nexport function assert(\n condition: unknown,\n message?: string,\n): asserts condition {\n if (!condition) {\n throw new Error(message || `Assertion failed`)\n }\n}\n\n// JS engines have various limits on how many args can be passed to a function\n// with a spread operator, so we need to split the operation into chunks\n// 32767 is the max for Chrome 14, all others are higher\n// TODO: investigate the performance of this and other approaches\nconst chunkSize = 30000\nexport function chunkedArrayPush(array: Array<unknown>, other: Array<unknown>) {\n if (other.length <= chunkSize) {\n array.push(...other)\n } else {\n for (let i = 0; i < other.length; i += chunkSize) {\n const chunk = other.slice(i, i + chunkSize)\n array.push(...chunk)\n }\n }\n}\n\nexport function binarySearch<T>(\n array: Array<T>,\n value: T,\n comparator: (a: T, b: T) => number,\n): number {\n let low = 0\n let high = array.length\n while (low < high) {\n const mid = Math.floor((low + high) / 2)\n const comparison = comparator(array[mid]!, value)\n if (comparison < 0) {\n low = mid + 1\n } else if (comparison > 0) {\n high = mid\n } else {\n return mid\n }\n }\n return low\n}\n\nconst objectIds = new WeakMap<object, number>()\nlet nextObjectId = 0\n\n/**\n * A string identity for composite keys: primitives by value, objects by\n * reference.\n */\nexport function getStringId(value: unknown): string {\n if (value === null) return `null`\n if (value === undefined) return `undefined`\n if (typeof value !== `object`) return `str_${String(value)}`\n let id = objectIds.get(value)\n if (id === undefined) objectIds.set(value, (id = nextObjectId++))\n return `obj_${id}`\n}\n\nexport function* concatIterable<T>(\n ...iterables: Array<Iterable<T>>\n): Iterable<T> {\n for (const iterable of iterables) {\n yield* iterable\n }\n}\n\nexport function* mapIterable<T, U>(\n it: Iterable<T>,\n fn: (t: T) => U,\n): Iterable<U> {\n for (const t of it) {\n yield fn(t)\n }\n}\n\nexport type HRange = [number, number] // half-open [start, end[ i.e. end is exclusive\n\n/**\n * Computes the difference between two half-open ranges.\n * @param a - The first half-open range\n * @param b - The second half-open range\n * @returns The difference between the two ranges\n */\nexport function diffHalfOpen(a: HRange, b: HRange) {\n const [a1, a2] = a\n const [b1, b2] = b\n\n // A \\ B can be up to two segments (left and right of the overlap)\n const onlyInA: Array<number> = [\n ...range(a1, Math.min(a2, b1)), // left side of A outside B\n ...range(Math.max(a1, b2), a2), // right side of A outside B\n ]\n\n // B \\ A similarly\n const onlyInB: Array<number> = [\n ...range(b1, Math.min(b2, a1)),\n ...range(Math.max(b1, a2), b2),\n ]\n\n return { onlyInA, onlyInB }\n}\n\nfunction range(start: number, end: number): Array<number> {\n const out: Array<number> = []\n for (let i = start; i < end; i++) out.push(i)\n return out\n}\n\n/**\n * Compares two keys (string | number) in a consistent, deterministic way.\n * Handles mixed types by ordering strings before numbers.\n */\nexport function compareKeys(a: string | number, b: string | number): number {\n // Same type: compare directly\n if (typeof a === typeof b) {\n if (typeof a === `number` && typeof b === `number`) {\n const aIsNaN = Number.isNaN(a)\n const bIsNaN = Number.isNaN(b)\n if (aIsNaN || bIsNaN) {\n if (aIsNaN && bIsNaN) return 0\n return aIsNaN ? 1 : -1\n }\n }\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n // Different types: strings come before numbers\n return typeof a === `string` ? -1 : 1\n}\n\ntype CanonicalValue =\n | readonly [`undefined`]\n | readonly [`null`]\n | readonly [`boolean`, boolean]\n | readonly [`number`, number | `NaN` | `Infinity` | `-Infinity`]\n | readonly [`bigint`, string]\n | readonly [`string`, string]\n | readonly [`date`, number | `Invalid`]\n | readonly [`temporal`, string, string]\n | readonly [`regexp`, string, string]\n | readonly [`bytes`, Array<number>]\n | readonly [`array`, Array<CanonicalValue>]\n | readonly [`map`, Array<readonly [CanonicalValue, CanonicalValue]>]\n | readonly [`set`, Array<CanonicalValue>]\n | readonly [`object`, Array<readonly [string, CanonicalValue]>]\n\n/**\n * Serializes a supported query value into one canonical key.\n *\n * JSON's native encoding is not suitable for relation keys: it merges BigInt\n * with strings when a replacer is used, merges NaN with null, drops undefined,\n * and depends on object insertion order. Ordinary JSON values keep their\n * established wire form. Values that need richer types use a reserved prefix\n * plus a structural, type-tagged encoding.\n */\nexport function serializeValue(value: unknown): string {\n if (isJsonSafeStructuralValue(value, new Set())) {\n return JSON.stringify(toStableJsonValue(value))\n }\n\n return `~${JSON.stringify(toCanonicalValue(value, new Set()))}`\n}\n\ntype JsonValue =\n | null\n | boolean\n | number\n | string\n | Array<JsonValue>\n | { [key: string]: JsonValue }\n\nfunction isJsonSafeStructuralValue(\n value: unknown,\n ancestors: Set<object>,\n): boolean {\n if (value === null) return true\n\n switch (typeof value) {\n case `boolean`:\n case `string`:\n return true\n case `number`:\n return Number.isFinite(value)\n case `undefined`:\n case `bigint`:\n case `symbol`:\n case `function`:\n return false\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Uint8Array ||\n isTemporal(value) ||\n value instanceof Map ||\n value instanceof Set\n ) {\n return false\n }\n\n return Array.isArray(value)\n ? value.every((item) => isJsonSafeStructuralValue(item, ancestors))\n : Object.keys(value).every((key) =>\n isJsonSafeStructuralValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n )\n })\n}\n\nfunction toStableJsonValue(value: unknown): JsonValue {\n if (\n value === null ||\n typeof value === `boolean` ||\n typeof value === `number` ||\n typeof value === `string`\n ) {\n return value\n }\n\n if (Array.isArray(value)) {\n return value.map(toStableJsonValue)\n }\n\n return Object.fromEntries(\n Object.keys(value as object)\n .sort()\n .map((key) => [\n key,\n toStableJsonValue((value as Record<string, unknown>)[key]),\n ]),\n )\n}\n\nfunction toCanonicalValue(\n value: unknown,\n ancestors: Set<object>,\n): CanonicalValue {\n if (value === undefined) return [`undefined`]\n if (value === null) return [`null`]\n\n switch (typeof value) {\n case `boolean`:\n return [`boolean`, value]\n case `number`:\n if (Number.isNaN(value)) return [`number`, `NaN`]\n if (value === Infinity) return [`number`, `Infinity`]\n if (value === -Infinity) return [`number`, `-Infinity`]\n return [`number`, value === 0 ? 0 : value]\n case `bigint`:\n return [`bigint`, value.toString()]\n case `string`:\n return [`string`, value]\n case `symbol`:\n case `function`:\n throw new TypeError(\n `Cannot serialize ${typeof value} as a structural relation key`,\n )\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (value instanceof Date) {\n const timestamp = value.getTime()\n return Number.isNaN(timestamp)\n ? ([`date`, `Invalid`] as const)\n : ([`date`, timestamp] as const)\n }\n\n if (value instanceof RegExp) {\n return [`regexp`, value.source, value.flags]\n }\n\n if (value instanceof Uint8Array) {\n return [`bytes`, Array.from(value)]\n }\n\n if (isTemporal(value)) {\n return [`temporal`, value[Symbol.toStringTag], value.toString()]\n }\n\n if (Array.isArray(value)) {\n return [`array`, value.map((item) => toCanonicalValue(item, ancestors))]\n }\n\n if (value instanceof Map) {\n const entries = [...value.entries()].map(\n ([key, entryValue]) =>\n [\n toCanonicalValue(key, ancestors),\n toCanonicalValue(entryValue, ancestors),\n ] as const,\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`map`, entries]\n }\n\n if (value instanceof Set) {\n const entries = [...value].map((entry) =>\n toCanonicalValue(entry, ancestors),\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`set`, entries]\n }\n\n const entries = Object.keys(value)\n .sort()\n .map(\n (key) =>\n [\n key,\n toCanonicalValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n ] as const,\n )\n return [`object`, entries]\n })\n}\n\nfunction compareSerializedValues(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction withAcyclicValue<T>(\n value: object,\n ancestors: Set<object>,\n encode: () => T,\n): T {\n if (ancestors.has(value)) {\n throw new TypeError(`Cannot serialize a cyclic structural relation key`)\n }\n\n ancestors.add(value)\n try {\n return encode()\n } finally {\n ancestors.delete(value)\n }\n}\n"],"names":["isTemporal","entries"],"mappings":"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"}
|
package/dist/cjs/utils.d.cts
CHANGED
|
@@ -3,42 +3,13 @@
|
|
|
3
3
|
* Throws an error if the condition is false.
|
|
4
4
|
*/
|
|
5
5
|
export declare function assert(condition: unknown, message?: string): asserts condition;
|
|
6
|
-
/**
|
|
7
|
-
* A map that returns a default value for keys that are not present.
|
|
8
|
-
*/
|
|
9
|
-
export declare class DefaultMap<K, V> extends Map<K, V> {
|
|
10
|
-
private defaultValue;
|
|
11
|
-
constructor(defaultValue: () => V, entries?: Iterable<[K, V]>);
|
|
12
|
-
get(key: K): V;
|
|
13
|
-
/**
|
|
14
|
-
* Update the value for a key using a function.
|
|
15
|
-
*/
|
|
16
|
-
update(key: K, updater: (value: V) => V): V;
|
|
17
|
-
}
|
|
18
6
|
export declare function chunkedArrayPush(array: Array<unknown>, other: Array<unknown>): void;
|
|
19
7
|
export declare function binarySearch<T>(array: Array<T>, value: T, comparator: (a: T, b: T) => number): number;
|
|
20
8
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*/
|
|
24
|
-
export declare class ObjectIdGenerator {
|
|
25
|
-
private objectIds;
|
|
26
|
-
private nextId;
|
|
27
|
-
/**
|
|
28
|
-
* Get a unique identifier for any value.
|
|
29
|
-
* - Objects: Uses WeakMap for reference-based identity
|
|
30
|
-
* - Primitives: Uses consistent string-based hashing
|
|
31
|
-
*/
|
|
32
|
-
getId(value: any): number;
|
|
33
|
-
/**
|
|
34
|
-
* Get a string representation of the ID for use in composite keys.
|
|
35
|
-
*/
|
|
36
|
-
getStringId(value: any): string;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Global instance for cases where a shared object ID space is needed.
|
|
9
|
+
* A string identity for composite keys: primitives by value, objects by
|
|
10
|
+
* reference.
|
|
40
11
|
*/
|
|
41
|
-
export declare
|
|
12
|
+
export declare function getStringId(value: unknown): string;
|
|
42
13
|
export declare function concatIterable<T>(...iterables: Array<Iterable<T>>): Iterable<T>;
|
|
43
14
|
export declare function mapIterable<T, U>(it: Iterable<T>, fn: (t: T) => U): Iterable<U>;
|
|
44
15
|
export type HRange = [number, number];
|
package/dist/esm/d2.js
CHANGED
|
@@ -40,8 +40,18 @@ class D2 {
|
|
|
40
40
|
return this.#operators.some((op) => op.hasPendingWork());
|
|
41
41
|
}
|
|
42
42
|
run() {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
if (!this.#finalized) {
|
|
44
|
+
throw new Error(`Graph not finalized`);
|
|
45
|
+
}
|
|
46
|
+
let ran = true;
|
|
47
|
+
while (ran) {
|
|
48
|
+
ran = false;
|
|
49
|
+
for (const op of this.#operators) {
|
|
50
|
+
if (op.hasPendingWork()) {
|
|
51
|
+
op.run();
|
|
52
|
+
ran = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
}
|
package/dist/esm/d2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"d2.js","sources":["../../src/d2.ts"],"sourcesContent":["import { DifferenceStreamWriter } from './graph.js'\nimport type {\n BinaryOperator,\n DifferenceStreamReader,\n UnaryOperator,\n} from './graph.js'\nimport type { MultiSet, MultiSetArray } from './multiset.js'\nimport type { ID2, IStreamBuilder, PipedOperator } from './types.js'\n\nexport class D2 implements ID2 {\n #operators: Array<UnaryOperator<any> | BinaryOperator<any>> = []\n #nextOperatorId = 0\n #finalized = false\n\n constructor() {}\n\n #checkNotFinalized(): void {\n if (this.#finalized) {\n throw new Error(`Graph already finalized`)\n }\n }\n\n getNextOperatorId(): number {\n this.#checkNotFinalized()\n return this.#nextOperatorId++\n }\n\n newInput<T>(): RootStreamBuilder<T> {\n this.#checkNotFinalized()\n const writer = new DifferenceStreamWriter<T>()\n // Use the root stream builder that exposes the sendData and sendFrontier methods\n const streamBuilder = new RootStreamBuilder<T>(this, writer)\n return streamBuilder\n }\n\n addOperator(operator: UnaryOperator<any> | BinaryOperator<any>): void {\n this.#checkNotFinalized()\n this.#operators.push(operator)\n }\n\n finalize() {\n this.#checkNotFinalized()\n this.#finalized = true\n }\n\n step(): void {\n if (!this.#finalized) {\n throw new Error(`Graph not finalized`)\n }\n for (const op of this.#operators) {\n op.run()\n }\n }\n\n pendingWork(): boolean {\n return this.#operators.some((op) => op.hasPendingWork())\n }\n\n run(): void {\n while (this.pendingWork()) {\n this.step()\n }\n }\n}\n\nexport class StreamBuilder<T> implements IStreamBuilder<T> {\n #graph: ID2\n #writer: DifferenceStreamWriter<T>\n\n constructor(graph: ID2, writer: DifferenceStreamWriter<T>) {\n this.#graph = graph\n this.#writer = writer\n }\n\n connectReader(): DifferenceStreamReader<T> {\n return this.#writer.newReader()\n }\n\n get writer(): DifferenceStreamWriter<T> {\n return this.#writer\n }\n\n get graph(): ID2 {\n return this.#graph\n }\n\n // Don't judge, this is the only way to type this function.\n // rxjs has very similar code to type its pipe function\n // https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/util/pipe.ts\n // We go to 20 operators deep, because surly that's enough for anyone...\n // A user can always split the pipe into multiple pipes to get around this.\n pipe<O>(o1: PipedOperator<T, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, T20>, o20: PipedOperator<T20, O>): IStreamBuilder<O>\n\n pipe(...operators: Array<PipedOperator<any, any>>): IStreamBuilder<any> {\n return operators.reduce((stream, operator) => {\n return operator(stream)\n }, this as IStreamBuilder<any>)\n }\n}\n\nexport class RootStreamBuilder<T> extends StreamBuilder<T> {\n sendData(collection: MultiSet<T> | MultiSetArray<T>): void {\n this.writer.sendData(collection)\n }\n}\n"],"names":[],"mappings":";AASO,MAAM,GAAkB;AAAA,EAC7B,aAA8D,CAAA;AAAA,EAC9D,kBAAkB;AAAA,EAClB,aAAa;AAAA,EAEb,cAAc;AAAA,EAAC;AAAA,EAEf,qBAA2B;AACzB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,oBAA4B;AAC1B,SAAK,mBAAA;AACL,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAoC;AAClC,SAAK,mBAAA;AACL,UAAM,SAAS,IAAI,uBAAA;AAEnB,UAAM,gBAAgB,IAAI,kBAAqB,MAAM,MAAM;AAC3D,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,UAA0D;AACpE,SAAK,mBAAA;AACL,SAAK,WAAW,KAAK,QAAQ;AAAA,EAC/B;AAAA,EAEA,WAAW;AACT,SAAK,mBAAA;AACL,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,OAAa;AACX,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AACA,eAAW,MAAM,KAAK,YAAY;AAChC,SAAG,IAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,WAAW,KAAK,CAAC,OAAO,GAAG,gBAAgB;AAAA,EACzD;AAAA,EAEA,MAAY;AACV,WAAO,KAAK,eAAe;AACzB,WAAK,KAAA;AAAA,IACP;AAAA,EACF;AACF;AAEO,MAAM,cAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EAEA,YAAY,OAAY,QAAmC;AACzD,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,gBAA2C;AACzC,WAAO,KAAK,QAAQ,UAAA;AAAA,EACtB;AAAA,EAEA,IAAI,SAAoC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAa;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EA+CA,QAAQ,WAAgE;AACtE,WAAO,UAAU,OAAO,CAAC,QAAQ,aAAa;AAC5C,aAAO,SAAS,MAAM;AAAA,IACxB,GAAG,IAA2B;AAAA,EAChC;AACF;AAEO,MAAM,0BAA6B,cAAiB;AAAA,EACzD,SAAS,YAAkD;AACzD,SAAK,OAAO,SAAS,UAAU;AAAA,EACjC;AACF;"}
|
|
1
|
+
{"version":3,"file":"d2.js","sources":["../../src/d2.ts"],"sourcesContent":["import { DifferenceStreamWriter } from './graph.js'\nimport type {\n BinaryOperator,\n DifferenceStreamReader,\n UnaryOperator,\n} from './graph.js'\nimport type { MultiSet, MultiSetArray } from './multiset.js'\nimport type { ID2, IStreamBuilder, PipedOperator } from './types.js'\n\nexport class D2 implements ID2 {\n #operators: Array<UnaryOperator<any> | BinaryOperator<any>> = []\n #nextOperatorId = 0\n #finalized = false\n\n constructor() {}\n\n #checkNotFinalized(): void {\n if (this.#finalized) {\n throw new Error(`Graph already finalized`)\n }\n }\n\n getNextOperatorId(): number {\n this.#checkNotFinalized()\n return this.#nextOperatorId++\n }\n\n newInput<T>(): RootStreamBuilder<T> {\n this.#checkNotFinalized()\n const writer = new DifferenceStreamWriter<T>()\n // Use the root stream builder that exposes the sendData and sendFrontier methods\n const streamBuilder = new RootStreamBuilder<T>(this, writer)\n return streamBuilder\n }\n\n addOperator(operator: UnaryOperator<any> | BinaryOperator<any>): void {\n this.#checkNotFinalized()\n this.#operators.push(operator)\n }\n\n finalize() {\n this.#checkNotFinalized()\n this.#finalized = true\n }\n\n step(): void {\n if (!this.#finalized) {\n throw new Error(`Graph not finalized`)\n }\n for (const op of this.#operators) {\n op.run()\n }\n }\n\n pendingWork(): boolean {\n return this.#operators.some((op) => op.hasPendingWork())\n }\n\n run(): void {\n if (!this.#finalized) {\n throw new Error(`Graph not finalized`)\n }\n\n // A later registered operator can send work to an earlier one. Continue\n // until a complete pass runs none.\n let ran = true\n while (ran) {\n ran = false\n for (const op of this.#operators) {\n if (op.hasPendingWork()) {\n op.run()\n ran = true\n }\n }\n }\n }\n}\n\nexport class StreamBuilder<T> implements IStreamBuilder<T> {\n #graph: ID2\n #writer: DifferenceStreamWriter<T>\n\n constructor(graph: ID2, writer: DifferenceStreamWriter<T>) {\n this.#graph = graph\n this.#writer = writer\n }\n\n connectReader(): DifferenceStreamReader<T> {\n return this.#writer.newReader()\n }\n\n get writer(): DifferenceStreamWriter<T> {\n return this.#writer\n }\n\n get graph(): ID2 {\n return this.#graph\n }\n\n // Don't judge, this is the only way to type this function.\n // rxjs has very similar code to type its pipe function\n // https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/util/pipe.ts\n // We go to 20 operators deep, because surly that's enough for anyone...\n // A user can always split the pipe into multiple pipes to get around this.\n pipe<O>(o1: PipedOperator<T, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, T20>, o20: PipedOperator<T20, O>): IStreamBuilder<O>\n\n pipe(...operators: Array<PipedOperator<any, any>>): IStreamBuilder<any> {\n return operators.reduce((stream, operator) => {\n return operator(stream)\n }, this as IStreamBuilder<any>)\n }\n}\n\nexport class RootStreamBuilder<T> extends StreamBuilder<T> {\n sendData(collection: MultiSet<T> | MultiSetArray<T>): void {\n this.writer.sendData(collection)\n }\n}\n"],"names":[],"mappings":";AASO,MAAM,GAAkB;AAAA,EAC7B,aAA8D,CAAA;AAAA,EAC9D,kBAAkB;AAAA,EAClB,aAAa;AAAA,EAEb,cAAc;AAAA,EAAC;AAAA,EAEf,qBAA2B;AACzB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,oBAA4B;AAC1B,SAAK,mBAAA;AACL,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAoC;AAClC,SAAK,mBAAA;AACL,UAAM,SAAS,IAAI,uBAAA;AAEnB,UAAM,gBAAgB,IAAI,kBAAqB,MAAM,MAAM;AAC3D,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,UAA0D;AACpE,SAAK,mBAAA;AACL,SAAK,WAAW,KAAK,QAAQ;AAAA,EAC/B;AAAA,EAEA,WAAW;AACT,SAAK,mBAAA;AACL,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,OAAa;AACX,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AACA,eAAW,MAAM,KAAK,YAAY;AAChC,SAAG,IAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,WAAW,KAAK,CAAC,OAAO,GAAG,gBAAgB;AAAA,EACzD;AAAA,EAEA,MAAY;AACV,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AAIA,QAAI,MAAM;AACV,WAAO,KAAK;AACV,YAAM;AACN,iBAAW,MAAM,KAAK,YAAY;AAChC,YAAI,GAAG,kBAAkB;AACvB,aAAG,IAAA;AACH,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,cAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EAEA,YAAY,OAAY,QAAmC;AACzD,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,gBAA2C;AACzC,WAAO,KAAK,QAAQ,UAAA;AAAA,EACtB;AAAA,EAEA,IAAI,SAAoC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAa;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EA+CA,QAAQ,WAAgE;AACtE,WAAO,UAAU,OAAO,CAAC,QAAQ,aAAa;AAC5C,aAAO,SAAS,MAAM;AAAA,IACxB,GAAG,IAA2B;AAAA,EAChC;AACF;AAEO,MAAM,0BAA6B,cAAiB;AAAA,EACzD,SAAS,YAAkD;AACzD,SAAK,OAAO,SAAS,UAAU;AAAA,EACjC;AACF;"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
/** @internal Register a mutable handle before it enters a structural value. */
|
|
2
2
|
export declare function registerOpaqueHash(value: object): void;
|
|
3
3
|
export declare function hash(input: any): number;
|
|
4
|
+
/** @internal Compare immutable structural values without computing a digest.
|
|
5
|
+
* Pair memoization also permits cyclic values that structural hashing rejects.
|
|
6
|
+
* Reference-valued leaves remain opaque, including registered mutable handles.
|
|
7
|
+
*/
|
|
8
|
+
export declare function equalHashValues(left: unknown, right: unknown): boolean;
|
package/dist/esm/hashing/hash.js
CHANGED
|
@@ -1,35 +1,24 @@
|
|
|
1
1
|
import { MurmurHashStream, randomHash, getSymbolIdentity } from "./murmur.js";
|
|
2
|
+
import { isTemporal } from "./temporal.js";
|
|
2
3
|
const TRUE = randomHash();
|
|
3
4
|
const FALSE = randomHash();
|
|
4
5
|
const NULL = randomHash();
|
|
5
6
|
const UNDEFINED = randomHash();
|
|
6
7
|
const KEY = randomHash();
|
|
7
8
|
const FUNCTIONS = randomHash();
|
|
8
|
-
const DATE_MARKER = randomHash();
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
9
|
+
const DATE_MARKER = { hash: randomHash() };
|
|
10
|
+
const REGEXP_MARKER = { hash: randomHash() };
|
|
11
|
+
const OBJECT_MARKER = { hash: randomHash() };
|
|
12
|
+
const ARRAY_MARKER = { hash: randomHash() };
|
|
13
|
+
const MAP_MARKER = { hash: randomHash() };
|
|
14
|
+
const SET_MARKER = { hash: randomHash() };
|
|
15
|
+
const UINT8ARRAY_MARKER = { hash: randomHash() };
|
|
16
|
+
const TEMPORAL_MARKER = { hash: randomHash() };
|
|
15
17
|
const MAX_STRUCTURAL_HASH_WORK = 1e6;
|
|
16
18
|
const MAX_STRUCTURAL_HASH_DEPTH = 768;
|
|
17
|
-
const temporalTypes = /* @__PURE__ */ new Set([
|
|
18
|
-
`Temporal.Duration`,
|
|
19
|
-
`Temporal.Instant`,
|
|
20
|
-
`Temporal.PlainDate`,
|
|
21
|
-
`Temporal.PlainDateTime`,
|
|
22
|
-
`Temporal.PlainMonthDay`,
|
|
23
|
-
`Temporal.PlainTime`,
|
|
24
|
-
`Temporal.PlainYearMonth`,
|
|
25
|
-
`Temporal.ZonedDateTime`
|
|
26
|
-
]);
|
|
27
|
-
function isTemporal(input) {
|
|
28
|
-
const tag = input[Symbol.toStringTag];
|
|
29
|
-
return typeof tag === `string` && temporalTypes.has(tag);
|
|
30
|
-
}
|
|
31
19
|
const UINT8ARRAY_CONTENT_HASH_THRESHOLD = 128;
|
|
32
20
|
const hashCache = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
const referenceValues = /* @__PURE__ */ new WeakSet();
|
|
33
22
|
function registerOpaqueHash(value) {
|
|
34
23
|
cachedReferenceHash(value);
|
|
35
24
|
}
|
|
@@ -47,73 +36,51 @@ function hashObject(input, context) {
|
|
|
47
36
|
context.activeObjects.add(input);
|
|
48
37
|
let valueHash;
|
|
49
38
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
39
|
+
const [marker, header, body] = objectParts(input);
|
|
40
|
+
const hasher = new MurmurHashStream();
|
|
41
|
+
hasher.update(marker.hash);
|
|
42
|
+
for (const value of header)
|
|
43
|
+
if (body) updateHasher(hasher, value, context);
|
|
44
|
+
else hasher.update(value);
|
|
45
|
+
if (body) {
|
|
46
|
+
const keys = Object.keys(body).sort(keySort);
|
|
47
|
+
keys.push(
|
|
48
|
+
...Object.getOwnPropertySymbols(body).filter(
|
|
49
|
+
(key) => Object.prototype.propertyIsEnumerable.call(body, key)
|
|
50
|
+
).sort(
|
|
51
|
+
(left, right) => getSymbolIdentity(left) - getSymbolIdentity(right)
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
for (const key of keys) {
|
|
55
|
+
hasher.update(KEY);
|
|
56
|
+
hasher.update(key);
|
|
57
|
+
updateHasher(hasher, body[key], context);
|
|
69
58
|
}
|
|
70
|
-
valueHash = hashPlainObject(plainObjectInput, marker, context);
|
|
71
59
|
}
|
|
60
|
+
valueHash = hasher.digest();
|
|
72
61
|
} finally {
|
|
73
62
|
context.activeObjects.delete(input);
|
|
74
63
|
}
|
|
75
64
|
context.pendingHashes.set(input, valueHash);
|
|
76
65
|
return valueHash;
|
|
77
66
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return hasher.digest();
|
|
83
|
-
}
|
|
84
|
-
function hashUint8Array(input) {
|
|
85
|
-
const hasher = new MurmurHashStream();
|
|
86
|
-
hasher.update(UINT8ARRAY_MARKER);
|
|
87
|
-
hasher.update(input.byteLength);
|
|
88
|
-
for (let i = 0; i < input.byteLength; i++) {
|
|
89
|
-
hasher.writeByte(input[i]);
|
|
90
|
-
}
|
|
91
|
-
return hasher.digest();
|
|
92
|
-
}
|
|
93
|
-
function hashTemporal(input) {
|
|
94
|
-
const hasher = new MurmurHashStream();
|
|
95
|
-
hasher.update(TEMPORAL_MARKER);
|
|
96
|
-
hasher.update(input[Symbol.toStringTag]);
|
|
97
|
-
hasher.update(input.toString());
|
|
98
|
-
return hasher.digest();
|
|
99
|
-
}
|
|
100
|
-
function hashPlainObject(input, marker, context) {
|
|
101
|
-
const hasher = new MurmurHashStream();
|
|
102
|
-
hasher.update(marker);
|
|
103
|
-
const keys = Object.keys(input);
|
|
104
|
-
keys.sort(keySort);
|
|
105
|
-
for (const key of keys) {
|
|
106
|
-
hasher.update(KEY);
|
|
107
|
-
hasher.update(key);
|
|
108
|
-
updateHasher(hasher, input[key], context);
|
|
67
|
+
function objectParts(input) {
|
|
68
|
+
if (input instanceof Date) {
|
|
69
|
+
const time = input.getTime();
|
|
70
|
+
return [DATE_MARKER, [Number.isNaN(time) ? `invalid` : time]];
|
|
109
71
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
72
|
+
if (isBinaryValue(input))
|
|
73
|
+
return [
|
|
74
|
+
UINT8ARRAY_MARKER,
|
|
75
|
+
[String.fromCharCode.apply(null, input)]
|
|
76
|
+
];
|
|
77
|
+
if (isTemporal(input))
|
|
78
|
+
return [TEMPORAL_MARKER, [input[Symbol.toStringTag], input.toString()]];
|
|
79
|
+
if (input instanceof RegExp)
|
|
80
|
+
return [REGEXP_MARKER, [input.source, input.flags, input.lastIndex], input];
|
|
81
|
+
if (input instanceof Map) return [MAP_MARKER, [], [...input.entries()]];
|
|
82
|
+
if (input instanceof Set) return [SET_MARKER, [], [...input.values()]];
|
|
83
|
+
return input instanceof Array ? [ARRAY_MARKER, [input.length], input] : [OBJECT_MARKER, [], input];
|
|
117
84
|
}
|
|
118
85
|
function updateHasher(hasher, input, context) {
|
|
119
86
|
if (context && ++context.work > MAX_STRUCTURAL_HASH_WORK) {
|
|
@@ -143,43 +110,62 @@ function updateHasher(hasher, input, context) {
|
|
|
143
110
|
return;
|
|
144
111
|
case `function`:
|
|
145
112
|
hasher.update(cachedReferenceHash(input));
|
|
146
|
-
return;
|
|
147
|
-
default:
|
|
148
|
-
console.warn(
|
|
149
|
-
`Ignored input during hashing because it is of type ${typeof input} which is not supported`
|
|
150
|
-
);
|
|
151
113
|
}
|
|
152
114
|
}
|
|
153
115
|
function getCachedHash(input, context) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
context
|
|
159
|
-
|
|
160
|
-
work: 0,
|
|
161
|
-
pendingHashes: /* @__PURE__ */ new Map()
|
|
162
|
-
};
|
|
163
|
-
const result = hashObject(input, context);
|
|
164
|
-
for (const [object, valueHash2] of context.pendingHashes) {
|
|
165
|
-
hashCache.set(object, valueHash2);
|
|
116
|
+
const cached = hashCache.get(input) ?? context?.pendingHashes.get(input);
|
|
117
|
+
if (cached !== void 0) return cached;
|
|
118
|
+
if (isReferenceHashedObject(input)) return cachedReferenceHash(input);
|
|
119
|
+
if (context) {
|
|
120
|
+
if (context.activeObjects.has(input)) {
|
|
121
|
+
throw new TypeError(`Cannot hash cyclic structural values`);
|
|
166
122
|
}
|
|
167
|
-
return
|
|
123
|
+
return hashObject(input, context);
|
|
168
124
|
}
|
|
169
|
-
|
|
170
|
-
|
|
125
|
+
const root = {
|
|
126
|
+
activeObjects: /* @__PURE__ */ new Set(),
|
|
127
|
+
work: 0,
|
|
128
|
+
pendingHashes: /* @__PURE__ */ new Map()
|
|
129
|
+
};
|
|
130
|
+
const result = hashObject(input, root);
|
|
131
|
+
for (const [object, valueHash] of root.pendingHashes) {
|
|
132
|
+
hashCache.set(object, valueHash);
|
|
171
133
|
}
|
|
172
|
-
|
|
173
|
-
const valueHash = hashCache.get(input) ?? context.pendingHashes.get(input);
|
|
174
|
-
if (valueHash !== void 0) return valueHash;
|
|
175
|
-
return hashObject(input, context);
|
|
134
|
+
return result;
|
|
176
135
|
}
|
|
177
136
|
function isReferenceHashedObject(input) {
|
|
178
|
-
return input instanceof File || isBinaryValue(input) && input.byteLength > UINT8ARRAY_CONTENT_HASH_THRESHOLD;
|
|
137
|
+
return referenceValues.has(input) || typeof File !== `undefined` && input instanceof File || isBinaryValue(input) && input.byteLength > UINT8ARRAY_CONTENT_HASH_THRESHOLD;
|
|
179
138
|
}
|
|
180
139
|
function isBinaryValue(input) {
|
|
181
140
|
return typeof Buffer !== `undefined` && input instanceof Buffer || input instanceof Uint8Array;
|
|
182
141
|
}
|
|
142
|
+
function equalHashValues(left, right) {
|
|
143
|
+
const compared = /* @__PURE__ */ new Map();
|
|
144
|
+
const keys = (value) => Reflect.ownKeys(value).filter(
|
|
145
|
+
(key) => Object.prototype.propertyIsEnumerable.call(value, key)
|
|
146
|
+
);
|
|
147
|
+
function equal(a, b) {
|
|
148
|
+
if (a === b || Number.isNaN(a) && Number.isNaN(b)) return true;
|
|
149
|
+
if (a === null || b === null || typeof a !== `object` || typeof b !== `object` || isReferenceHashedObject(a) || isReferenceHashedObject(b))
|
|
150
|
+
return false;
|
|
151
|
+
if (Array.isArray(a) && Array.isArray(b) && a.length !== b.length)
|
|
152
|
+
return false;
|
|
153
|
+
const peers = compared.get(a);
|
|
154
|
+
if (peers?.has(b)) return true;
|
|
155
|
+
const [aMarker, aHeader, aBody] = objectParts(a);
|
|
156
|
+
const [bMarker, bHeader, bBody] = objectParts(b);
|
|
157
|
+
if (aMarker !== bMarker || !aHeader.every((value, index) => value === bHeader[index]))
|
|
158
|
+
return false;
|
|
159
|
+
if (!aBody || !bBody) return true;
|
|
160
|
+
if (peers) peers.add(b);
|
|
161
|
+
else compared.set(a, /* @__PURE__ */ new Set([b]));
|
|
162
|
+
const aKeys = keys(aBody);
|
|
163
|
+
return aKeys.length === keys(bBody).length && aKeys.every(
|
|
164
|
+
(key) => Object.prototype.propertyIsEnumerable.call(bBody, key) && equal(aBody[key], bBody[key])
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return equal(left, right);
|
|
168
|
+
}
|
|
183
169
|
let nextRefId = 1;
|
|
184
170
|
function cachedReferenceHash(fn) {
|
|
185
171
|
let valueHash = hashCache.get(fn);
|
|
@@ -187,6 +173,7 @@ function cachedReferenceHash(fn) {
|
|
|
187
173
|
valueHash = nextRefId ^ FUNCTIONS;
|
|
188
174
|
nextRefId++;
|
|
189
175
|
hashCache.set(fn, valueHash);
|
|
176
|
+
referenceValues.add(fn);
|
|
190
177
|
}
|
|
191
178
|
return valueHash;
|
|
192
179
|
}
|
|
@@ -194,6 +181,7 @@ function keySort(a, b) {
|
|
|
194
181
|
return a.localeCompare(b);
|
|
195
182
|
}
|
|
196
183
|
export {
|
|
184
|
+
equalHashValues,
|
|
197
185
|
hash,
|
|
198
186
|
registerOpaqueHash
|
|
199
187
|
};
|