@tanstack/db-ivm 0.1.17 → 0.1.18
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/hashing/hash.cjs +24 -0
- package/dist/cjs/hashing/hash.cjs.map +1 -1
- package/dist/cjs/index.cjs +42 -42
- package/dist/esm/hashing/hash.js +24 -0
- package/dist/esm/hashing/hash.js.map +1 -1
- package/dist/esm/index.js +15 -15
- package/dist/esm/operators/topKArray.js +1 -1
- package/package.json +3 -2
- package/src/hashing/hash.ts +32 -0
|
@@ -13,6 +13,21 @@ const ARRAY_MARKER = murmur.randomHash();
|
|
|
13
13
|
const MAP_MARKER = murmur.randomHash();
|
|
14
14
|
const SET_MARKER = murmur.randomHash();
|
|
15
15
|
const UINT8ARRAY_MARKER = murmur.randomHash();
|
|
16
|
+
const TEMPORAL_MARKER = murmur.randomHash();
|
|
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
|
+
}
|
|
16
31
|
const UINT8ARRAY_CONTENT_HASH_THRESHOLD = 128;
|
|
17
32
|
const hashCache = /* @__PURE__ */ new WeakMap();
|
|
18
33
|
function hash(input) {
|
|
@@ -39,6 +54,8 @@ function hashObject(input) {
|
|
|
39
54
|
}
|
|
40
55
|
} else if (input instanceof File) {
|
|
41
56
|
return cachedReferenceHash(input);
|
|
57
|
+
} else if (isTemporal(input)) {
|
|
58
|
+
valueHash = hashTemporal(input);
|
|
42
59
|
} else {
|
|
43
60
|
let plainObjectInput = input;
|
|
44
61
|
let marker = OBJECT_MARKER;
|
|
@@ -73,6 +90,13 @@ function hashUint8Array(input) {
|
|
|
73
90
|
}
|
|
74
91
|
return hasher.digest();
|
|
75
92
|
}
|
|
93
|
+
function hashTemporal(input) {
|
|
94
|
+
const hasher = new murmur.MurmurHashStream();
|
|
95
|
+
hasher.update(TEMPORAL_MARKER);
|
|
96
|
+
hasher.update(input[Symbol.toStringTag]);
|
|
97
|
+
hasher.update(input.toString());
|
|
98
|
+
return hasher.digest();
|
|
99
|
+
}
|
|
76
100
|
function hashPlainObject(input, marker) {
|
|
77
101
|
const hasher = new murmur.MurmurHashStream();
|
|
78
102
|
hasher.update(marker);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.cjs","sources":["../../../src/hashing/hash.ts"],"sourcesContent":["import { MurmurHashStream, randomHash } from './murmur.js'\nimport type { Hasher } from './murmur.js'\n\n/*\n * Implementation of structural hashing based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst TRUE = randomHash()\nconst FALSE = randomHash()\nconst NULL = randomHash()\nconst UNDEFINED = randomHash()\nconst KEY = randomHash()\nconst FUNCTIONS = randomHash()\nconst DATE_MARKER = randomHash()\nconst OBJECT_MARKER = randomHash()\nconst ARRAY_MARKER = randomHash()\nconst MAP_MARKER = randomHash()\nconst SET_MARKER = randomHash()\nconst UINT8ARRAY_MARKER = randomHash()\n\n// Maximum byte length for Uint8Arrays to hash by content instead of reference\n// Arrays smaller than this will be hashed by content, allowing proper equality comparisons\n// for small arrays like ULIDs (16 bytes) while still avoiding performance costs for large arrays\nconst UINT8ARRAY_CONTENT_HASH_THRESHOLD = 128\n\nconst hashCache = new WeakMap<object, number>()\n\nexport function hash(input: any): number {\n const hasher = new MurmurHashStream()\n updateHasher(hasher, input)\n return hasher.digest()\n}\n\nfunction hashObject(input: object): number {\n const cachedHash = hashCache.get(input)\n if (cachedHash !== undefined) {\n return cachedHash\n }\n\n let valueHash: number | undefined\n if (input instanceof Date) {\n valueHash = hashDate(input)\n } else if (\n // Check if input is a Uint8Array or Buffer\n (typeof Buffer !== `undefined` && input instanceof Buffer) ||\n input instanceof Uint8Array\n ) {\n // For small Uint8Arrays/Buffers (e.g., ULIDs, UUIDs), hash by content\n // to enable proper equality comparisons. For large arrays, hash by reference\n // to avoid performance costs.\n if (input.byteLength <= UINT8ARRAY_CONTENT_HASH_THRESHOLD) {\n valueHash = hashUint8Array(input)\n } else {\n // Deeply hashing large arrays would be too costly\n // so we track them by reference and cache them in a weak map\n return cachedReferenceHash(input)\n }\n } else if (input instanceof File) {\n // Files are always hashed by reference due to their potentially large size\n return cachedReferenceHash(input)\n } else {\n let plainObjectInput = input\n let marker = OBJECT_MARKER\n\n if (input instanceof Array) {\n marker = ARRAY_MARKER\n }\n\n if (input instanceof Map) {\n marker = MAP_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n if (input instanceof Set) {\n marker = SET_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n valueHash = hashPlainObject(plainObjectInput, marker)\n }\n\n hashCache.set(input, valueHash)\n return valueHash\n}\n\nfunction hashDate(input: Date): number {\n const hasher = new MurmurHashStream()\n hasher.update(DATE_MARKER)\n hasher.update(input.getTime())\n return hasher.digest()\n}\n\nfunction hashUint8Array(input: Uint8Array): number {\n const hasher = new MurmurHashStream()\n hasher.update(UINT8ARRAY_MARKER)\n // Hash the byte length first to differentiate arrays of different sizes\n hasher.update(input.byteLength)\n // Hash each byte in the array\n for (let i = 0; i < input.byteLength; i++) {\n hasher.writeByte(input[i]!)\n }\n return hasher.digest()\n}\n\nfunction hashPlainObject(input: object, marker: number): number {\n const hasher = new MurmurHashStream()\n\n // Mark the type of the input\n hasher.update(marker)\n const keys = Object.keys(input)\n keys.sort(keySort)\n for (const key of keys) {\n hasher.update(KEY)\n hasher.update(key)\n updateHasher(hasher, input[key as keyof typeof input])\n }\n\n return hasher.digest()\n}\n\nfunction updateHasher(hasher: Hasher, input: unknown): void {\n if (input === null) {\n hasher.update(NULL)\n return\n }\n switch (typeof input) {\n case `undefined`:\n hasher.update(UNDEFINED)\n return\n case `boolean`:\n hasher.update(input ? TRUE : FALSE)\n return\n case `number`:\n // Normalize NaNs and -0\n hasher.update(isNaN(input) ? NaN : input === 0 ? 0 : input)\n return\n case `bigint`:\n case `string`:\n case `symbol`:\n hasher.update(input)\n return\n case `object`:\n hasher.update(getCachedHash(input))\n return\n case `function`:\n // Functions are assigned a globally unique ID\n // and that ID is cached in the weak map\n hasher.update(cachedReferenceHash(input))\n return\n default:\n console.warn(\n `Ignored input during hashing because it is of type ${typeof input} which is not supported`,\n )\n }\n}\n\nfunction getCachedHash(input: object): number {\n let valueHash = hashCache.get(input)\n if (valueHash === undefined) {\n valueHash = hashObject(input)\n }\n return valueHash\n}\n\nlet nextRefId = 1\nfunction cachedReferenceHash(fn: object): number {\n let valueHash = hashCache.get(fn)\n if (valueHash === undefined) {\n valueHash = nextRefId ^ FUNCTIONS\n nextRefId++\n hashCache.set(fn, valueHash)\n }\n return valueHash\n}\n\n/**\n * Strings sorted lexicographically.\n */\nfunction keySort(a: string, b: string): number {\n return a.localeCompare(b)\n}\n"],"names":["randomHash","MurmurHashStream"],"mappings":";;;AAQA,MAAM,OAAOA,OAAAA,WAAA;AACb,MAAM,QAAQA,OAAAA,WAAA;AACd,MAAM,OAAOA,OAAAA,WAAA;AACb,MAAM,YAAYA,OAAAA,WAAA;AAClB,MAAM,MAAMA,OAAAA,WAAA;AACZ,MAAM,YAAYA,OAAAA,WAAA;AAClB,MAAM,cAAcA,OAAAA,WAAA;AACpB,MAAM,gBAAgBA,OAAAA,WAAA;AACtB,MAAM,eAAeA,OAAAA,WAAA;AACrB,MAAM,aAAaA,OAAAA,WAAA;AACnB,MAAM,aAAaA,OAAAA,WAAA;AACnB,MAAM,oBAAoBA,OAAAA,WAAA;
|
|
1
|
+
{"version":3,"file":"hash.cjs","sources":["../../../src/hashing/hash.ts"],"sourcesContent":["import { MurmurHashStream, randomHash } from './murmur.js'\nimport type { Hasher } from './murmur.js'\n\n/*\n * Implementation of structural hashing based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst TRUE = randomHash()\nconst FALSE = randomHash()\nconst NULL = randomHash()\nconst UNDEFINED = randomHash()\nconst KEY = randomHash()\nconst FUNCTIONS = randomHash()\nconst DATE_MARKER = randomHash()\nconst OBJECT_MARKER = randomHash()\nconst ARRAY_MARKER = randomHash()\nconst MAP_MARKER = randomHash()\nconst SET_MARKER = randomHash()\nconst UINT8ARRAY_MARKER = randomHash()\nconst TEMPORAL_MARKER = randomHash()\n\nconst temporalTypes = new Set([\n `Temporal.Duration`,\n `Temporal.Instant`,\n `Temporal.PlainDate`,\n `Temporal.PlainDateTime`,\n `Temporal.PlainMonthDay`,\n `Temporal.PlainTime`,\n `Temporal.PlainYearMonth`,\n `Temporal.ZonedDateTime`,\n])\n\ninterface TemporalLike {\n [Symbol.toStringTag]: string\n toString: () => string\n}\n\nfunction isTemporal(input: object): input is TemporalLike {\n const tag = (input as Record<symbol, unknown>)[Symbol.toStringTag]\n return typeof tag === `string` && temporalTypes.has(tag)\n}\n\n// Maximum byte length for Uint8Arrays to hash by content instead of reference\n// Arrays smaller than this will be hashed by content, allowing proper equality comparisons\n// for small arrays like ULIDs (16 bytes) while still avoiding performance costs for large arrays\nconst UINT8ARRAY_CONTENT_HASH_THRESHOLD = 128\n\nconst hashCache = new WeakMap<object, number>()\n\nexport function hash(input: any): number {\n const hasher = new MurmurHashStream()\n updateHasher(hasher, input)\n return hasher.digest()\n}\n\nfunction hashObject(input: object): number {\n const cachedHash = hashCache.get(input)\n if (cachedHash !== undefined) {\n return cachedHash\n }\n\n let valueHash: number | undefined\n if (input instanceof Date) {\n valueHash = hashDate(input)\n } else if (\n // Check if input is a Uint8Array or Buffer\n (typeof Buffer !== `undefined` && input instanceof Buffer) ||\n input instanceof Uint8Array\n ) {\n // For small Uint8Arrays/Buffers (e.g., ULIDs, UUIDs), hash by content\n // to enable proper equality comparisons. For large arrays, hash by reference\n // to avoid performance costs.\n if (input.byteLength <= UINT8ARRAY_CONTENT_HASH_THRESHOLD) {\n valueHash = hashUint8Array(input)\n } else {\n // Deeply hashing large arrays would be too costly\n // so we track them by reference and cache them in a weak map\n return cachedReferenceHash(input)\n }\n } else if (input instanceof File) {\n // Files are always hashed by reference due to their potentially large size\n return cachedReferenceHash(input)\n } else if (isTemporal(input)) {\n valueHash = hashTemporal(input)\n } else {\n let plainObjectInput = input\n let marker = OBJECT_MARKER\n\n if (input instanceof Array) {\n marker = ARRAY_MARKER\n }\n\n if (input instanceof Map) {\n marker = MAP_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n if (input instanceof Set) {\n marker = SET_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n valueHash = hashPlainObject(plainObjectInput, marker)\n }\n\n hashCache.set(input, valueHash)\n return valueHash\n}\n\nfunction hashDate(input: Date): number {\n const hasher = new MurmurHashStream()\n hasher.update(DATE_MARKER)\n hasher.update(input.getTime())\n return hasher.digest()\n}\n\nfunction hashUint8Array(input: Uint8Array): number {\n const hasher = new MurmurHashStream()\n hasher.update(UINT8ARRAY_MARKER)\n // Hash the byte length first to differentiate arrays of different sizes\n hasher.update(input.byteLength)\n // Hash each byte in the array\n for (let i = 0; i < input.byteLength; i++) {\n hasher.writeByte(input[i]!)\n }\n return hasher.digest()\n}\n\nfunction hashTemporal(input: TemporalLike): number {\n const hasher = new MurmurHashStream()\n hasher.update(TEMPORAL_MARKER)\n hasher.update(input[Symbol.toStringTag])\n hasher.update(input.toString())\n return hasher.digest()\n}\n\nfunction hashPlainObject(input: object, marker: number): number {\n const hasher = new MurmurHashStream()\n\n // Mark the type of the input\n hasher.update(marker)\n const keys = Object.keys(input)\n keys.sort(keySort)\n for (const key of keys) {\n hasher.update(KEY)\n hasher.update(key)\n updateHasher(hasher, input[key as keyof typeof input])\n }\n\n return hasher.digest()\n}\n\nfunction updateHasher(hasher: Hasher, input: unknown): void {\n if (input === null) {\n hasher.update(NULL)\n return\n }\n switch (typeof input) {\n case `undefined`:\n hasher.update(UNDEFINED)\n return\n case `boolean`:\n hasher.update(input ? TRUE : FALSE)\n return\n case `number`:\n // Normalize NaNs and -0\n hasher.update(isNaN(input) ? NaN : input === 0 ? 0 : input)\n return\n case `bigint`:\n case `string`:\n case `symbol`:\n hasher.update(input)\n return\n case `object`:\n hasher.update(getCachedHash(input))\n return\n case `function`:\n // Functions are assigned a globally unique ID\n // and that ID is cached in the weak map\n hasher.update(cachedReferenceHash(input))\n return\n default:\n console.warn(\n `Ignored input during hashing because it is of type ${typeof input} which is not supported`,\n )\n }\n}\n\nfunction getCachedHash(input: object): number {\n let valueHash = hashCache.get(input)\n if (valueHash === undefined) {\n valueHash = hashObject(input)\n }\n return valueHash\n}\n\nlet nextRefId = 1\nfunction cachedReferenceHash(fn: object): number {\n let valueHash = hashCache.get(fn)\n if (valueHash === undefined) {\n valueHash = nextRefId ^ FUNCTIONS\n nextRefId++\n hashCache.set(fn, valueHash)\n }\n return valueHash\n}\n\n/**\n * Strings sorted lexicographically.\n */\nfunction keySort(a: string, b: string): number {\n return a.localeCompare(b)\n}\n"],"names":["randomHash","MurmurHashStream"],"mappings":"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"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,52 +3,47 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const d2 = require("./d2.cjs");
|
|
4
4
|
const multiset = require("./multiset.cjs");
|
|
5
5
|
const utils = require("./utils.cjs");
|
|
6
|
-
const groupBy = require("./operators/groupBy.cjs");
|
|
7
|
-
const pipe = require("./operators/pipe.cjs");
|
|
8
|
-
const map = require("./operators/map.cjs");
|
|
9
|
-
const tap = require("./operators/tap.cjs");
|
|
10
|
-
const filter = require("./operators/filter.cjs");
|
|
11
|
-
const negate = require("./operators/negate.cjs");
|
|
12
6
|
const concat = require("./operators/concat.cjs");
|
|
13
|
-
const debug = require("./operators/debug.cjs");
|
|
14
|
-
const output = require("./operators/output.cjs");
|
|
15
7
|
const consolidate = require("./operators/consolidate.cjs");
|
|
16
|
-
const join = require("./operators/join.cjs");
|
|
17
|
-
const reduce = require("./operators/reduce.cjs");
|
|
18
8
|
const count = require("./operators/count.cjs");
|
|
9
|
+
const debug = require("./operators/debug.cjs");
|
|
19
10
|
const distinct = require("./operators/distinct.cjs");
|
|
20
|
-
const
|
|
21
|
-
const topK = require("./operators/topK.cjs");
|
|
22
|
-
const topKWithFractionalIndex = require("./operators/topKWithFractionalIndex.cjs");
|
|
11
|
+
const filter = require("./operators/filter.cjs");
|
|
23
12
|
const groupedTopKWithFractionalIndex = require("./operators/groupedTopKWithFractionalIndex.cjs");
|
|
24
|
-
const
|
|
25
|
-
const
|
|
13
|
+
const join = require("./operators/join.cjs");
|
|
14
|
+
const map = require("./operators/map.cjs");
|
|
15
|
+
const negate = require("./operators/negate.cjs");
|
|
16
|
+
const output = require("./operators/output.cjs");
|
|
17
|
+
const reduce = require("./operators/reduce.cjs");
|
|
18
|
+
const tap = require("./operators/tap.cjs");
|
|
19
|
+
const topKWithFractionalIndex = require("./operators/topKWithFractionalIndex.cjs");
|
|
26
20
|
const filterBy = require("./operators/filterBy.cjs");
|
|
21
|
+
const groupBy = require("./operators/groupBy.cjs");
|
|
22
|
+
const groupedOrderBy = require("./operators/groupedOrderBy.cjs");
|
|
23
|
+
const keying = require("./operators/keying.cjs");
|
|
24
|
+
const orderBy = require("./operators/orderBy.cjs");
|
|
25
|
+
const pipe = require("./operators/pipe.cjs");
|
|
26
|
+
const topK = require("./operators/topK.cjs");
|
|
27
27
|
exports.D2 = d2.D2;
|
|
28
28
|
exports.RootStreamBuilder = d2.RootStreamBuilder;
|
|
29
29
|
exports.StreamBuilder = d2.StreamBuilder;
|
|
30
30
|
exports.MultiSet = multiset.MultiSet;
|
|
31
31
|
exports.compareKeys = utils.compareKeys;
|
|
32
32
|
exports.serializeValue = utils.serializeValue;
|
|
33
|
-
exports.groupBy = groupBy.groupBy;
|
|
34
|
-
exports.groupByOperators = groupBy.groupByOperators;
|
|
35
|
-
exports.pipe = pipe.pipe;
|
|
36
|
-
exports.MapOperator = map.MapOperator;
|
|
37
|
-
exports.map = map.map;
|
|
38
|
-
exports.TapOperator = tap.TapOperator;
|
|
39
|
-
exports.tap = tap.tap;
|
|
40
|
-
exports.FilterOperator = filter.FilterOperator;
|
|
41
|
-
exports.filter = filter.filter;
|
|
42
|
-
exports.NegateOperator = negate.NegateOperator;
|
|
43
|
-
exports.negate = negate.negate;
|
|
44
33
|
exports.ConcatOperator = concat.ConcatOperator;
|
|
45
34
|
exports.concat = concat.concat;
|
|
46
|
-
exports.DebugOperator = debug.DebugOperator;
|
|
47
|
-
exports.debug = debug.debug;
|
|
48
|
-
exports.OutputOperator = output.OutputOperator;
|
|
49
|
-
exports.output = output.output;
|
|
50
35
|
exports.ConsolidateOperator = consolidate.ConsolidateOperator;
|
|
51
36
|
exports.consolidate = consolidate.consolidate;
|
|
37
|
+
exports.CountOperator = count.CountOperator;
|
|
38
|
+
exports.count = count.count;
|
|
39
|
+
exports.DebugOperator = debug.DebugOperator;
|
|
40
|
+
exports.debug = debug.debug;
|
|
41
|
+
exports.DistinctOperator = distinct.DistinctOperator;
|
|
42
|
+
exports.distinct = distinct.distinct;
|
|
43
|
+
exports.FilterOperator = filter.FilterOperator;
|
|
44
|
+
exports.filter = filter.filter;
|
|
45
|
+
exports.GroupedTopKWithFractionalIndexOperator = groupedTopKWithFractionalIndex.GroupedTopKWithFractionalIndexOperator;
|
|
46
|
+
exports.groupedTopKWithFractionalIndex = groupedTopKWithFractionalIndex.groupedTopKWithFractionalIndex;
|
|
52
47
|
exports.JoinOperator = join.JoinOperator;
|
|
53
48
|
exports.antiJoin = join.antiJoin;
|
|
54
49
|
exports.fullJoin = join.fullJoin;
|
|
@@ -56,25 +51,30 @@ exports.innerJoin = join.innerJoin;
|
|
|
56
51
|
exports.join = join.join;
|
|
57
52
|
exports.leftJoin = join.leftJoin;
|
|
58
53
|
exports.rightJoin = join.rightJoin;
|
|
54
|
+
exports.MapOperator = map.MapOperator;
|
|
55
|
+
exports.map = map.map;
|
|
56
|
+
exports.NegateOperator = negate.NegateOperator;
|
|
57
|
+
exports.negate = negate.negate;
|
|
58
|
+
exports.OutputOperator = output.OutputOperator;
|
|
59
|
+
exports.output = output.output;
|
|
59
60
|
exports.ReduceOperator = reduce.ReduceOperator;
|
|
60
61
|
exports.reduce = reduce.reduce;
|
|
61
|
-
exports.
|
|
62
|
-
exports.
|
|
63
|
-
exports.
|
|
64
|
-
exports.
|
|
62
|
+
exports.TapOperator = tap.TapOperator;
|
|
63
|
+
exports.tap = tap.tap;
|
|
64
|
+
exports.TopKWithFractionalIndexOperator = topKWithFractionalIndex.TopKWithFractionalIndexOperator;
|
|
65
|
+
exports.topKWithFractionalIndex = topKWithFractionalIndex.topKWithFractionalIndex;
|
|
66
|
+
exports.filterBy = filterBy.filterBy;
|
|
67
|
+
exports.groupBy = groupBy.groupBy;
|
|
68
|
+
exports.groupByOperators = groupBy.groupByOperators;
|
|
69
|
+
exports.groupedOrderByWithFractionalIndex = groupedOrderBy.groupedOrderByWithFractionalIndex;
|
|
65
70
|
exports.keyBy = keying.keyBy;
|
|
66
71
|
exports.rekey = keying.rekey;
|
|
67
72
|
exports.unkey = keying.unkey;
|
|
68
|
-
exports.topK = topK.topK;
|
|
69
|
-
exports.topKWithIndex = topK.topKWithIndex;
|
|
70
|
-
exports.TopKWithFractionalIndexOperator = topKWithFractionalIndex.TopKWithFractionalIndexOperator;
|
|
71
|
-
exports.topKWithFractionalIndex = topKWithFractionalIndex.topKWithFractionalIndex;
|
|
72
|
-
exports.GroupedTopKWithFractionalIndexOperator = groupedTopKWithFractionalIndex.GroupedTopKWithFractionalIndexOperator;
|
|
73
|
-
exports.groupedTopKWithFractionalIndex = groupedTopKWithFractionalIndex.groupedTopKWithFractionalIndex;
|
|
74
73
|
exports.orderBy = orderBy.orderBy;
|
|
75
74
|
exports.orderByWithFractionalIndex = orderBy.orderByWithFractionalIndex;
|
|
76
75
|
exports.orderByWithFractionalIndexBase = orderBy.orderByWithFractionalIndexBase;
|
|
77
76
|
exports.orderByWithIndex = orderBy.orderByWithIndex;
|
|
78
|
-
exports.
|
|
79
|
-
exports.
|
|
77
|
+
exports.pipe = pipe.pipe;
|
|
78
|
+
exports.topK = topK.topK;
|
|
79
|
+
exports.topKWithIndex = topK.topKWithIndex;
|
|
80
80
|
//# sourceMappingURL=index.cjs.map
|
package/dist/esm/hashing/hash.js
CHANGED
|
@@ -11,6 +11,21 @@ const ARRAY_MARKER = randomHash();
|
|
|
11
11
|
const MAP_MARKER = randomHash();
|
|
12
12
|
const SET_MARKER = randomHash();
|
|
13
13
|
const UINT8ARRAY_MARKER = randomHash();
|
|
14
|
+
const TEMPORAL_MARKER = randomHash();
|
|
15
|
+
const temporalTypes = /* @__PURE__ */ new Set([
|
|
16
|
+
`Temporal.Duration`,
|
|
17
|
+
`Temporal.Instant`,
|
|
18
|
+
`Temporal.PlainDate`,
|
|
19
|
+
`Temporal.PlainDateTime`,
|
|
20
|
+
`Temporal.PlainMonthDay`,
|
|
21
|
+
`Temporal.PlainTime`,
|
|
22
|
+
`Temporal.PlainYearMonth`,
|
|
23
|
+
`Temporal.ZonedDateTime`
|
|
24
|
+
]);
|
|
25
|
+
function isTemporal(input) {
|
|
26
|
+
const tag = input[Symbol.toStringTag];
|
|
27
|
+
return typeof tag === `string` && temporalTypes.has(tag);
|
|
28
|
+
}
|
|
14
29
|
const UINT8ARRAY_CONTENT_HASH_THRESHOLD = 128;
|
|
15
30
|
const hashCache = /* @__PURE__ */ new WeakMap();
|
|
16
31
|
function hash(input) {
|
|
@@ -37,6 +52,8 @@ function hashObject(input) {
|
|
|
37
52
|
}
|
|
38
53
|
} else if (input instanceof File) {
|
|
39
54
|
return cachedReferenceHash(input);
|
|
55
|
+
} else if (isTemporal(input)) {
|
|
56
|
+
valueHash = hashTemporal(input);
|
|
40
57
|
} else {
|
|
41
58
|
let plainObjectInput = input;
|
|
42
59
|
let marker = OBJECT_MARKER;
|
|
@@ -71,6 +88,13 @@ function hashUint8Array(input) {
|
|
|
71
88
|
}
|
|
72
89
|
return hasher.digest();
|
|
73
90
|
}
|
|
91
|
+
function hashTemporal(input) {
|
|
92
|
+
const hasher = new MurmurHashStream();
|
|
93
|
+
hasher.update(TEMPORAL_MARKER);
|
|
94
|
+
hasher.update(input[Symbol.toStringTag]);
|
|
95
|
+
hasher.update(input.toString());
|
|
96
|
+
return hasher.digest();
|
|
97
|
+
}
|
|
74
98
|
function hashPlainObject(input, marker) {
|
|
75
99
|
const hasher = new MurmurHashStream();
|
|
76
100
|
hasher.update(marker);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.js","sources":["../../../src/hashing/hash.ts"],"sourcesContent":["import { MurmurHashStream, randomHash } from './murmur.js'\nimport type { Hasher } from './murmur.js'\n\n/*\n * Implementation of structural hashing based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst TRUE = randomHash()\nconst FALSE = randomHash()\nconst NULL = randomHash()\nconst UNDEFINED = randomHash()\nconst KEY = randomHash()\nconst FUNCTIONS = randomHash()\nconst DATE_MARKER = randomHash()\nconst OBJECT_MARKER = randomHash()\nconst ARRAY_MARKER = randomHash()\nconst MAP_MARKER = randomHash()\nconst SET_MARKER = randomHash()\nconst UINT8ARRAY_MARKER = randomHash()\n\n// Maximum byte length for Uint8Arrays to hash by content instead of reference\n// Arrays smaller than this will be hashed by content, allowing proper equality comparisons\n// for small arrays like ULIDs (16 bytes) while still avoiding performance costs for large arrays\nconst UINT8ARRAY_CONTENT_HASH_THRESHOLD = 128\n\nconst hashCache = new WeakMap<object, number>()\n\nexport function hash(input: any): number {\n const hasher = new MurmurHashStream()\n updateHasher(hasher, input)\n return hasher.digest()\n}\n\nfunction hashObject(input: object): number {\n const cachedHash = hashCache.get(input)\n if (cachedHash !== undefined) {\n return cachedHash\n }\n\n let valueHash: number | undefined\n if (input instanceof Date) {\n valueHash = hashDate(input)\n } else if (\n // Check if input is a Uint8Array or Buffer\n (typeof Buffer !== `undefined` && input instanceof Buffer) ||\n input instanceof Uint8Array\n ) {\n // For small Uint8Arrays/Buffers (e.g., ULIDs, UUIDs), hash by content\n // to enable proper equality comparisons. For large arrays, hash by reference\n // to avoid performance costs.\n if (input.byteLength <= UINT8ARRAY_CONTENT_HASH_THRESHOLD) {\n valueHash = hashUint8Array(input)\n } else {\n // Deeply hashing large arrays would be too costly\n // so we track them by reference and cache them in a weak map\n return cachedReferenceHash(input)\n }\n } else if (input instanceof File) {\n // Files are always hashed by reference due to their potentially large size\n return cachedReferenceHash(input)\n } else {\n let plainObjectInput = input\n let marker = OBJECT_MARKER\n\n if (input instanceof Array) {\n marker = ARRAY_MARKER\n }\n\n if (input instanceof Map) {\n marker = MAP_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n if (input instanceof Set) {\n marker = SET_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n valueHash = hashPlainObject(plainObjectInput, marker)\n }\n\n hashCache.set(input, valueHash)\n return valueHash\n}\n\nfunction hashDate(input: Date): number {\n const hasher = new MurmurHashStream()\n hasher.update(DATE_MARKER)\n hasher.update(input.getTime())\n return hasher.digest()\n}\n\nfunction hashUint8Array(input: Uint8Array): number {\n const hasher = new MurmurHashStream()\n hasher.update(UINT8ARRAY_MARKER)\n // Hash the byte length first to differentiate arrays of different sizes\n hasher.update(input.byteLength)\n // Hash each byte in the array\n for (let i = 0; i < input.byteLength; i++) {\n hasher.writeByte(input[i]!)\n }\n return hasher.digest()\n}\n\nfunction hashPlainObject(input: object, marker: number): number {\n const hasher = new MurmurHashStream()\n\n // Mark the type of the input\n hasher.update(marker)\n const keys = Object.keys(input)\n keys.sort(keySort)\n for (const key of keys) {\n hasher.update(KEY)\n hasher.update(key)\n updateHasher(hasher, input[key as keyof typeof input])\n }\n\n return hasher.digest()\n}\n\nfunction updateHasher(hasher: Hasher, input: unknown): void {\n if (input === null) {\n hasher.update(NULL)\n return\n }\n switch (typeof input) {\n case `undefined`:\n hasher.update(UNDEFINED)\n return\n case `boolean`:\n hasher.update(input ? TRUE : FALSE)\n return\n case `number`:\n // Normalize NaNs and -0\n hasher.update(isNaN(input) ? NaN : input === 0 ? 0 : input)\n return\n case `bigint`:\n case `string`:\n case `symbol`:\n hasher.update(input)\n return\n case `object`:\n hasher.update(getCachedHash(input))\n return\n case `function`:\n // Functions are assigned a globally unique ID\n // and that ID is cached in the weak map\n hasher.update(cachedReferenceHash(input))\n return\n default:\n console.warn(\n `Ignored input during hashing because it is of type ${typeof input} which is not supported`,\n )\n }\n}\n\nfunction getCachedHash(input: object): number {\n let valueHash = hashCache.get(input)\n if (valueHash === undefined) {\n valueHash = hashObject(input)\n }\n return valueHash\n}\n\nlet nextRefId = 1\nfunction cachedReferenceHash(fn: object): number {\n let valueHash = hashCache.get(fn)\n if (valueHash === undefined) {\n valueHash = nextRefId ^ FUNCTIONS\n nextRefId++\n hashCache.set(fn, valueHash)\n }\n return valueHash\n}\n\n/**\n * Strings sorted lexicographically.\n */\nfunction keySort(a: string, b: string): number {\n return a.localeCompare(b)\n}\n"],"names":[],"mappings":";AAQA,MAAM,OAAO,WAAA;AACb,MAAM,QAAQ,WAAA;AACd,MAAM,OAAO,WAAA;AACb,MAAM,YAAY,WAAA;AAClB,MAAM,MAAM,WAAA;AACZ,MAAM,YAAY,WAAA;AAClB,MAAM,cAAc,WAAA;AACpB,MAAM,gBAAgB,WAAA;AACtB,MAAM,eAAe,WAAA;AACrB,MAAM,aAAa,WAAA;AACnB,MAAM,aAAa,WAAA;AACnB,MAAM,oBAAoB,WAAA;
|
|
1
|
+
{"version":3,"file":"hash.js","sources":["../../../src/hashing/hash.ts"],"sourcesContent":["import { MurmurHashStream, randomHash } from './murmur.js'\nimport type { Hasher } from './murmur.js'\n\n/*\n * Implementation of structural hashing based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst TRUE = randomHash()\nconst FALSE = randomHash()\nconst NULL = randomHash()\nconst UNDEFINED = randomHash()\nconst KEY = randomHash()\nconst FUNCTIONS = randomHash()\nconst DATE_MARKER = randomHash()\nconst OBJECT_MARKER = randomHash()\nconst ARRAY_MARKER = randomHash()\nconst MAP_MARKER = randomHash()\nconst SET_MARKER = randomHash()\nconst UINT8ARRAY_MARKER = randomHash()\nconst TEMPORAL_MARKER = randomHash()\n\nconst temporalTypes = new Set([\n `Temporal.Duration`,\n `Temporal.Instant`,\n `Temporal.PlainDate`,\n `Temporal.PlainDateTime`,\n `Temporal.PlainMonthDay`,\n `Temporal.PlainTime`,\n `Temporal.PlainYearMonth`,\n `Temporal.ZonedDateTime`,\n])\n\ninterface TemporalLike {\n [Symbol.toStringTag]: string\n toString: () => string\n}\n\nfunction isTemporal(input: object): input is TemporalLike {\n const tag = (input as Record<symbol, unknown>)[Symbol.toStringTag]\n return typeof tag === `string` && temporalTypes.has(tag)\n}\n\n// Maximum byte length for Uint8Arrays to hash by content instead of reference\n// Arrays smaller than this will be hashed by content, allowing proper equality comparisons\n// for small arrays like ULIDs (16 bytes) while still avoiding performance costs for large arrays\nconst UINT8ARRAY_CONTENT_HASH_THRESHOLD = 128\n\nconst hashCache = new WeakMap<object, number>()\n\nexport function hash(input: any): number {\n const hasher = new MurmurHashStream()\n updateHasher(hasher, input)\n return hasher.digest()\n}\n\nfunction hashObject(input: object): number {\n const cachedHash = hashCache.get(input)\n if (cachedHash !== undefined) {\n return cachedHash\n }\n\n let valueHash: number | undefined\n if (input instanceof Date) {\n valueHash = hashDate(input)\n } else if (\n // Check if input is a Uint8Array or Buffer\n (typeof Buffer !== `undefined` && input instanceof Buffer) ||\n input instanceof Uint8Array\n ) {\n // For small Uint8Arrays/Buffers (e.g., ULIDs, UUIDs), hash by content\n // to enable proper equality comparisons. For large arrays, hash by reference\n // to avoid performance costs.\n if (input.byteLength <= UINT8ARRAY_CONTENT_HASH_THRESHOLD) {\n valueHash = hashUint8Array(input)\n } else {\n // Deeply hashing large arrays would be too costly\n // so we track them by reference and cache them in a weak map\n return cachedReferenceHash(input)\n }\n } else if (input instanceof File) {\n // Files are always hashed by reference due to their potentially large size\n return cachedReferenceHash(input)\n } else if (isTemporal(input)) {\n valueHash = hashTemporal(input)\n } else {\n let plainObjectInput = input\n let marker = OBJECT_MARKER\n\n if (input instanceof Array) {\n marker = ARRAY_MARKER\n }\n\n if (input instanceof Map) {\n marker = MAP_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n if (input instanceof Set) {\n marker = SET_MARKER\n plainObjectInput = [...input.entries()]\n }\n\n valueHash = hashPlainObject(plainObjectInput, marker)\n }\n\n hashCache.set(input, valueHash)\n return valueHash\n}\n\nfunction hashDate(input: Date): number {\n const hasher = new MurmurHashStream()\n hasher.update(DATE_MARKER)\n hasher.update(input.getTime())\n return hasher.digest()\n}\n\nfunction hashUint8Array(input: Uint8Array): number {\n const hasher = new MurmurHashStream()\n hasher.update(UINT8ARRAY_MARKER)\n // Hash the byte length first to differentiate arrays of different sizes\n hasher.update(input.byteLength)\n // Hash each byte in the array\n for (let i = 0; i < input.byteLength; i++) {\n hasher.writeByte(input[i]!)\n }\n return hasher.digest()\n}\n\nfunction hashTemporal(input: TemporalLike): number {\n const hasher = new MurmurHashStream()\n hasher.update(TEMPORAL_MARKER)\n hasher.update(input[Symbol.toStringTag])\n hasher.update(input.toString())\n return hasher.digest()\n}\n\nfunction hashPlainObject(input: object, marker: number): number {\n const hasher = new MurmurHashStream()\n\n // Mark the type of the input\n hasher.update(marker)\n const keys = Object.keys(input)\n keys.sort(keySort)\n for (const key of keys) {\n hasher.update(KEY)\n hasher.update(key)\n updateHasher(hasher, input[key as keyof typeof input])\n }\n\n return hasher.digest()\n}\n\nfunction updateHasher(hasher: Hasher, input: unknown): void {\n if (input === null) {\n hasher.update(NULL)\n return\n }\n switch (typeof input) {\n case `undefined`:\n hasher.update(UNDEFINED)\n return\n case `boolean`:\n hasher.update(input ? TRUE : FALSE)\n return\n case `number`:\n // Normalize NaNs and -0\n hasher.update(isNaN(input) ? NaN : input === 0 ? 0 : input)\n return\n case `bigint`:\n case `string`:\n case `symbol`:\n hasher.update(input)\n return\n case `object`:\n hasher.update(getCachedHash(input))\n return\n case `function`:\n // Functions are assigned a globally unique ID\n // and that ID is cached in the weak map\n hasher.update(cachedReferenceHash(input))\n return\n default:\n console.warn(\n `Ignored input during hashing because it is of type ${typeof input} which is not supported`,\n )\n }\n}\n\nfunction getCachedHash(input: object): number {\n let valueHash = hashCache.get(input)\n if (valueHash === undefined) {\n valueHash = hashObject(input)\n }\n return valueHash\n}\n\nlet nextRefId = 1\nfunction cachedReferenceHash(fn: object): number {\n let valueHash = hashCache.get(fn)\n if (valueHash === undefined) {\n valueHash = nextRefId ^ FUNCTIONS\n nextRefId++\n hashCache.set(fn, valueHash)\n }\n return valueHash\n}\n\n/**\n * Strings sorted lexicographically.\n */\nfunction keySort(a: string, b: string): number {\n return a.localeCompare(b)\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { D2, RootStreamBuilder, StreamBuilder } from "./d2.js";
|
|
2
2
|
import { MultiSet } from "./multiset.js";
|
|
3
3
|
import { compareKeys, serializeValue } from "./utils.js";
|
|
4
|
-
import { groupBy, groupByOperators } from "./operators/groupBy.js";
|
|
5
|
-
import { pipe } from "./operators/pipe.js";
|
|
6
|
-
import { MapOperator, map } from "./operators/map.js";
|
|
7
|
-
import { TapOperator, tap } from "./operators/tap.js";
|
|
8
|
-
import { FilterOperator, filter } from "./operators/filter.js";
|
|
9
|
-
import { NegateOperator, negate } from "./operators/negate.js";
|
|
10
4
|
import { ConcatOperator, concat } from "./operators/concat.js";
|
|
11
|
-
import { DebugOperator, debug } from "./operators/debug.js";
|
|
12
|
-
import { OutputOperator, output } from "./operators/output.js";
|
|
13
5
|
import { ConsolidateOperator, consolidate } from "./operators/consolidate.js";
|
|
14
|
-
import { JoinOperator, antiJoin, fullJoin, innerJoin, join, leftJoin, rightJoin } from "./operators/join.js";
|
|
15
|
-
import { ReduceOperator, reduce } from "./operators/reduce.js";
|
|
16
6
|
import { CountOperator, count } from "./operators/count.js";
|
|
7
|
+
import { DebugOperator, debug } from "./operators/debug.js";
|
|
17
8
|
import { DistinctOperator, distinct } from "./operators/distinct.js";
|
|
18
|
-
import {
|
|
19
|
-
import { topK, topKWithIndex } from "./operators/topK.js";
|
|
20
|
-
import { TopKWithFractionalIndexOperator, topKWithFractionalIndex } from "./operators/topKWithFractionalIndex.js";
|
|
9
|
+
import { FilterOperator, filter } from "./operators/filter.js";
|
|
21
10
|
import { GroupedTopKWithFractionalIndexOperator, groupedTopKWithFractionalIndex } from "./operators/groupedTopKWithFractionalIndex.js";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
11
|
+
import { JoinOperator, antiJoin, fullJoin, innerJoin, join, leftJoin, rightJoin } from "./operators/join.js";
|
|
12
|
+
import { MapOperator, map } from "./operators/map.js";
|
|
13
|
+
import { NegateOperator, negate } from "./operators/negate.js";
|
|
14
|
+
import { OutputOperator, output } from "./operators/output.js";
|
|
15
|
+
import { ReduceOperator, reduce } from "./operators/reduce.js";
|
|
16
|
+
import { TapOperator, tap } from "./operators/tap.js";
|
|
17
|
+
import { TopKWithFractionalIndexOperator, topKWithFractionalIndex } from "./operators/topKWithFractionalIndex.js";
|
|
24
18
|
import { filterBy } from "./operators/filterBy.js";
|
|
19
|
+
import { groupBy, groupByOperators } from "./operators/groupBy.js";
|
|
20
|
+
import { groupedOrderByWithFractionalIndex } from "./operators/groupedOrderBy.js";
|
|
21
|
+
import { keyBy, rekey, unkey } from "./operators/keying.js";
|
|
22
|
+
import { orderBy, orderByWithFractionalIndex, orderByWithFractionalIndexBase, orderByWithIndex } from "./operators/orderBy.js";
|
|
23
|
+
import { pipe } from "./operators/pipe.js";
|
|
24
|
+
import { topK, topKWithIndex } from "./operators/topK.js";
|
|
25
25
|
export {
|
|
26
26
|
ConcatOperator,
|
|
27
27
|
ConsolidateOperator,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { generateKeyBetween } from "fractional-indexing";
|
|
2
|
-
import { compareKeys,
|
|
2
|
+
import { compareKeys, binarySearch, diffHalfOpen } from "../utils.js";
|
|
3
3
|
function indexedValue(value, index) {
|
|
4
4
|
return [value, index];
|
|
5
5
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/db-ivm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Incremental View Maintenance for TanStack DB based on Differential Dataflow",
|
|
5
5
|
"author": "Sam Willis",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/debug": "^4.1.12",
|
|
52
|
-
"@vitest/coverage-istanbul": "^3.2.4"
|
|
52
|
+
"@vitest/coverage-istanbul": "^3.2.4",
|
|
53
|
+
"temporal-polyfill": "^0.3.0"
|
|
53
54
|
},
|
|
54
55
|
"scripts": {
|
|
55
56
|
"build": "vite build",
|
package/src/hashing/hash.ts
CHANGED
|
@@ -18,6 +18,28 @@ const ARRAY_MARKER = randomHash()
|
|
|
18
18
|
const MAP_MARKER = randomHash()
|
|
19
19
|
const SET_MARKER = randomHash()
|
|
20
20
|
const UINT8ARRAY_MARKER = randomHash()
|
|
21
|
+
const TEMPORAL_MARKER = randomHash()
|
|
22
|
+
|
|
23
|
+
const temporalTypes = new Set([
|
|
24
|
+
`Temporal.Duration`,
|
|
25
|
+
`Temporal.Instant`,
|
|
26
|
+
`Temporal.PlainDate`,
|
|
27
|
+
`Temporal.PlainDateTime`,
|
|
28
|
+
`Temporal.PlainMonthDay`,
|
|
29
|
+
`Temporal.PlainTime`,
|
|
30
|
+
`Temporal.PlainYearMonth`,
|
|
31
|
+
`Temporal.ZonedDateTime`,
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
interface TemporalLike {
|
|
35
|
+
[Symbol.toStringTag]: string
|
|
36
|
+
toString: () => string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isTemporal(input: object): input is TemporalLike {
|
|
40
|
+
const tag = (input as Record<symbol, unknown>)[Symbol.toStringTag]
|
|
41
|
+
return typeof tag === `string` && temporalTypes.has(tag)
|
|
42
|
+
}
|
|
21
43
|
|
|
22
44
|
// Maximum byte length for Uint8Arrays to hash by content instead of reference
|
|
23
45
|
// Arrays smaller than this will be hashed by content, allowing proper equality comparisons
|
|
@@ -59,6 +81,8 @@ function hashObject(input: object): number {
|
|
|
59
81
|
} else if (input instanceof File) {
|
|
60
82
|
// Files are always hashed by reference due to their potentially large size
|
|
61
83
|
return cachedReferenceHash(input)
|
|
84
|
+
} else if (isTemporal(input)) {
|
|
85
|
+
valueHash = hashTemporal(input)
|
|
62
86
|
} else {
|
|
63
87
|
let plainObjectInput = input
|
|
64
88
|
let marker = OBJECT_MARKER
|
|
@@ -103,6 +127,14 @@ function hashUint8Array(input: Uint8Array): number {
|
|
|
103
127
|
return hasher.digest()
|
|
104
128
|
}
|
|
105
129
|
|
|
130
|
+
function hashTemporal(input: TemporalLike): number {
|
|
131
|
+
const hasher = new MurmurHashStream()
|
|
132
|
+
hasher.update(TEMPORAL_MARKER)
|
|
133
|
+
hasher.update(input[Symbol.toStringTag])
|
|
134
|
+
hasher.update(input.toString())
|
|
135
|
+
return hasher.digest()
|
|
136
|
+
}
|
|
137
|
+
|
|
106
138
|
function hashPlainObject(input: object, marker: number): number {
|
|
107
139
|
const hasher = new MurmurHashStream()
|
|
108
140
|
|