@tanstack/db-ivm 0.1.4 → 0.1.6
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/murmur.cjs +12 -4
- package/dist/cjs/hashing/murmur.cjs.map +1 -1
- package/dist/cjs/operators/tap.cjs +2 -4
- package/dist/cjs/operators/tap.cjs.map +1 -1
- package/dist/cjs/operators/tap.d.cts +5 -5
- package/dist/esm/hashing/murmur.js +12 -4
- package/dist/esm/hashing/murmur.js.map +1 -1
- package/dist/esm/operators/tap.d.ts +5 -5
- package/dist/esm/operators/tap.js +2 -4
- package/dist/esm/operators/tap.js.map +1 -1
- package/package.json +2 -2
- package/src/hashing/murmur.ts +15 -4
- package/src/operators/tap.ts +8 -10
|
@@ -8,6 +8,9 @@ const SYMBOL_MARKER = randomHash();
|
|
|
8
8
|
function randomHash() {
|
|
9
9
|
return Math.random() * (2 ** 31 - 1) >>> 0;
|
|
10
10
|
}
|
|
11
|
+
const buf = new ArrayBuffer(8);
|
|
12
|
+
const dv = new DataView(buf);
|
|
13
|
+
const u8 = new Uint8Array(buf);
|
|
11
14
|
class MurmurHashStream {
|
|
12
15
|
constructor() {
|
|
13
16
|
this.hash = RANDOM_SEED;
|
|
@@ -57,10 +60,15 @@ class MurmurHashStream {
|
|
|
57
60
|
}
|
|
58
61
|
return;
|
|
59
62
|
case `number`:
|
|
60
|
-
|
|
61
|
-
this._writeByte(
|
|
62
|
-
this._writeByte(
|
|
63
|
-
this._writeByte(
|
|
63
|
+
dv.setFloat64(0, chunk, true);
|
|
64
|
+
this._writeByte(u8[0]);
|
|
65
|
+
this._writeByte(u8[1]);
|
|
66
|
+
this._writeByte(u8[2]);
|
|
67
|
+
this._writeByte(u8[3]);
|
|
68
|
+
this._writeByte(u8[4]);
|
|
69
|
+
this._writeByte(u8[5]);
|
|
70
|
+
this._writeByte(u8[6]);
|
|
71
|
+
this._writeByte(u8[7]);
|
|
64
72
|
return;
|
|
65
73
|
case `bigint`: {
|
|
66
74
|
let value = chunk;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"murmur.cjs","sources":["../../../src/hashing/murmur.ts"],"sourcesContent":["/*\n * Implementation of murmur hash based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst RANDOM_SEED = randomHash()\nconst STRING_MARKER = randomHash()\nconst BIG_INT_MARKER = randomHash()\nconst NEG_BIG_INT_MARKER = randomHash()\nconst SYMBOL_MARKER = randomHash()\n\nexport type Hash = number\n\nexport function randomHash() {\n return (Math.random() * (2 ** 31 - 1)) >>> 0\n}\n\nexport interface Hasher {\n update: (val: symbol | string | number | bigint) => void\n digest: () => number\n}\n\n/**\n * This implementation of Murmur hash uses a random initial seed and random markers.\n * This means that hashes aren't deterministic across app restarts.\n * This is intentional in the composites polyfill to be resistent to hash-flooding attacks\n * where malicious users would precompute lots of different objects whose hashes collide with each other.\n *\n * Currently, for ts/db-ivm this is fine because we don't persist client state.\n * However, when we will introduce persistence we will either need to store the seeds or remove the randomness\n * to ensure deterministic hashes across app restarts.\n */\nexport class MurmurHashStream implements Hasher {\n private hash: number = RANDOM_SEED\n private length = 0\n private carry = 0\n private carryBytes = 0\n\n private _mix(k1: number): void {\n k1 = Math.imul(k1, 0xcc9e2d51)\n k1 = (k1 << 15) | (k1 >>> 17)\n k1 = Math.imul(k1, 0x1b873593)\n this.hash ^= k1\n this.hash = (this.hash << 13) | (this.hash >>> 19)\n this.hash = Math.imul(this.hash, 5) + 0xe6546b64\n }\n\n private _writeByte(byte: number): void {\n this.carry |= (byte & 0xff) << (8 * this.carryBytes)\n this.carryBytes++\n this.length++\n\n if (this.carryBytes === 4) {\n this._mix(this.carry >>> 0)\n this.carry = 0\n this.carryBytes = 0\n }\n }\n\n update(chunk: symbol | string | number | bigint): void {\n switch (typeof chunk) {\n case `symbol`: {\n this.update(SYMBOL_MARKER)\n const description = chunk.description\n if (!description) {\n return\n }\n\n for (let i = 0; i < description.length; i++) {\n const code = description.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n }\n case `string`:\n this.update(STRING_MARKER)\n for (let i = 0; i < chunk.length; i++) {\n const code = chunk.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n case `number`:\n
|
|
1
|
+
{"version":3,"file":"murmur.cjs","sources":["../../../src/hashing/murmur.ts"],"sourcesContent":["/*\n * Implementation of murmur hash based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst RANDOM_SEED = randomHash()\nconst STRING_MARKER = randomHash()\nconst BIG_INT_MARKER = randomHash()\nconst NEG_BIG_INT_MARKER = randomHash()\nconst SYMBOL_MARKER = randomHash()\n\nexport type Hash = number\n\nexport function randomHash() {\n return (Math.random() * (2 ** 31 - 1)) >>> 0\n}\n\nexport interface Hasher {\n update: (val: symbol | string | number | bigint) => void\n digest: () => number\n}\n\n// Allocate these once, outside the hot path\nconst buf = new ArrayBuffer(8)\n// dv and u8 are 2 different views on the same buffer `buf`\nconst dv = new DataView(buf)\nconst u8 = new Uint8Array(buf)\n\n/**\n * This implementation of Murmur hash uses a random initial seed and random markers.\n * This means that hashes aren't deterministic across app restarts.\n * This is intentional in the composites polyfill to be resistent to hash-flooding attacks\n * where malicious users would precompute lots of different objects whose hashes collide with each other.\n *\n * Currently, for ts/db-ivm this is fine because we don't persist client state.\n * However, when we will introduce persistence we will either need to store the seeds or remove the randomness\n * to ensure deterministic hashes across app restarts.\n */\nexport class MurmurHashStream implements Hasher {\n private hash: number = RANDOM_SEED\n private length = 0\n private carry = 0\n private carryBytes = 0\n\n private _mix(k1: number): void {\n k1 = Math.imul(k1, 0xcc9e2d51)\n k1 = (k1 << 15) | (k1 >>> 17)\n k1 = Math.imul(k1, 0x1b873593)\n this.hash ^= k1\n this.hash = (this.hash << 13) | (this.hash >>> 19)\n this.hash = Math.imul(this.hash, 5) + 0xe6546b64\n }\n\n private _writeByte(byte: number): void {\n this.carry |= (byte & 0xff) << (8 * this.carryBytes)\n this.carryBytes++\n this.length++\n\n if (this.carryBytes === 4) {\n this._mix(this.carry >>> 0)\n this.carry = 0\n this.carryBytes = 0\n }\n }\n\n update(chunk: symbol | string | number | bigint): void {\n switch (typeof chunk) {\n case `symbol`: {\n this.update(SYMBOL_MARKER)\n const description = chunk.description\n if (!description) {\n return\n }\n\n for (let i = 0; i < description.length; i++) {\n const code = description.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n }\n case `string`:\n this.update(STRING_MARKER)\n for (let i = 0; i < chunk.length; i++) {\n const code = chunk.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n case `number`:\n dv.setFloat64(0, chunk, true) // fixed little-endian\n this._writeByte(u8[0]!)\n this._writeByte(u8[1]!)\n this._writeByte(u8[2]!)\n this._writeByte(u8[3]!)\n this._writeByte(u8[4]!)\n this._writeByte(u8[5]!)\n this._writeByte(u8[6]!)\n this._writeByte(u8[7]!)\n return\n case `bigint`: {\n let value = chunk\n if (value < 0n) {\n value = -value\n this.update(NEG_BIG_INT_MARKER)\n } else {\n this.update(BIG_INT_MARKER)\n }\n while (value > 0n) {\n this._writeByte(Number(value & 0xffn))\n value >>= 8n\n }\n if (chunk === 0n) this._writeByte(0)\n return\n }\n default:\n throw new TypeError(`Unsupported input type: ${typeof chunk}`)\n }\n }\n\n digest(): number {\n if (this.carryBytes > 0) {\n let k1 = this.carry >>> 0\n k1 = Math.imul(k1, 0xcc9e2d51)\n k1 = (k1 << 15) | (k1 >>> 17)\n k1 = Math.imul(k1, 0x1b873593)\n this.hash ^= k1\n }\n\n this.hash ^= this.length\n this.hash ^= this.hash >>> 16\n this.hash = Math.imul(this.hash, 0x85ebca6b)\n this.hash ^= this.hash >>> 13\n this.hash = Math.imul(this.hash, 0xc2b2ae35)\n this.hash ^= this.hash >>> 16\n\n return this.hash >>> 0\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -17,10 +17,8 @@ class TapOperator extends graph.LinearUnaryOperator {
|
|
|
17
17
|
__privateSet(this, _f, f);
|
|
18
18
|
}
|
|
19
19
|
inner(collection) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return data;
|
|
23
|
-
});
|
|
20
|
+
__privateGet(this, _f).call(this, collection);
|
|
21
|
+
return collection;
|
|
24
22
|
}
|
|
25
23
|
}
|
|
26
24
|
_f = new WeakMap();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tap.cjs","sources":["../../../src/operators/tap.ts"],"sourcesContent":["import { DifferenceStreamWriter, LinearUnaryOperator } from \"../graph.js\"\nimport { StreamBuilder } from \"../d2.js\"\nimport type { IStreamBuilder, PipedOperator } from \"../types.js\"\nimport type { DifferenceStreamReader } from \"../graph.js\"\nimport type { MultiSet } from \"../multiset.js\"\n\n/**\n * Operator that applies a function to each
|
|
1
|
+
{"version":3,"file":"tap.cjs","sources":["../../../src/operators/tap.ts"],"sourcesContent":["import { DifferenceStreamWriter, LinearUnaryOperator } from \"../graph.js\"\nimport { StreamBuilder } from \"../d2.js\"\nimport type { IStreamBuilder, PipedOperator } from \"../types.js\"\nimport type { DifferenceStreamReader } from \"../graph.js\"\nimport type { MultiSet } from \"../multiset.js\"\n\n/**\n * Operator that applies a function to each multi-set in the input stream\n */\nexport class TapOperator<T> extends LinearUnaryOperator<T, T> {\n #f: (data: MultiSet<T>) => void\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<T>,\n output: DifferenceStreamWriter<T>,\n f: (data: MultiSet<T>) => void\n ) {\n super(id, inputA, output)\n this.#f = f\n }\n\n inner(collection: MultiSet<T>): MultiSet<T> {\n this.#f(collection)\n return collection\n }\n}\n\n/**\n * Invokes a function for each multi-set in the input stream.\n * This operator doesn't modify the stream and is used to perform side effects.\n * @param f - The function to invoke on each multi-set\n * @returns The input stream\n */\nexport function tap<T>(f: (data: MultiSet<T>) => void): PipedOperator<T, T> {\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n const output = new StreamBuilder<T>(\n stream.graph,\n new DifferenceStreamWriter<T>()\n )\n const operator = new TapOperator<T>(\n stream.graph.getNextOperatorId(),\n stream.connectReader(),\n output.writer,\n f\n )\n stream.graph.addOperator(operator)\n stream.graph.addStream(output.connectReader())\n return output\n }\n}\n"],"names":["LinearUnaryOperator","StreamBuilder","DifferenceStreamWriter"],"mappings":";;;;;;;;;;;;AASO,MAAM,oBAAuBA,MAAAA,oBAA0B;AAAA,EAG5D,YACE,IACA,QACA,QACA,GACA;AACA,UAAM,IAAI,QAAQ,MAAM;AAR1B;AASE,uBAAK,IAAK;AAAA,EACZ;AAAA,EAEA,MAAM,YAAsC;AAC1C,uBAAK,IAAL,WAAQ;AACR,WAAO;AAAA,EACT;AACF;AAhBE;AAwBK,SAAS,IAAO,GAAqD;AAC1E,SAAO,CAAC,WAAiD;AACvD,UAAM,SAAS,IAAIC,GAAAA;AAAAA,MACjB,OAAO;AAAA,MACP,IAAIC,MAAAA,uBAAA;AAAA,IAA0B;AAEhC,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,MACP;AAAA,IAAA;AAEF,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO,MAAM,UAAU,OAAO,cAAA,CAAe;AAC7C,WAAO;AAAA,EACT;AACF;;;"}
|
|
@@ -2,17 +2,17 @@ import { DifferenceStreamWriter, LinearUnaryOperator, DifferenceStreamReader } f
|
|
|
2
2
|
import { PipedOperator } from '../types.js';
|
|
3
3
|
import { MultiSet } from '../multiset.js';
|
|
4
4
|
/**
|
|
5
|
-
* Operator that applies a function to each
|
|
5
|
+
* Operator that applies a function to each multi-set in the input stream
|
|
6
6
|
*/
|
|
7
7
|
export declare class TapOperator<T> extends LinearUnaryOperator<T, T> {
|
|
8
8
|
#private;
|
|
9
|
-
constructor(id: number, inputA: DifferenceStreamReader<T>, output: DifferenceStreamWriter<T>, f: (data: T) => void);
|
|
9
|
+
constructor(id: number, inputA: DifferenceStreamReader<T>, output: DifferenceStreamWriter<T>, f: (data: MultiSet<T>) => void);
|
|
10
10
|
inner(collection: MultiSet<T>): MultiSet<T>;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Invokes a function for each
|
|
13
|
+
* Invokes a function for each multi-set in the input stream.
|
|
14
14
|
* This operator doesn't modify the stream and is used to perform side effects.
|
|
15
|
-
* @param f - The function to invoke on each
|
|
15
|
+
* @param f - The function to invoke on each multi-set
|
|
16
16
|
* @returns The input stream
|
|
17
17
|
*/
|
|
18
|
-
export declare function tap<T>(f: (data: T) => void): PipedOperator<T, T>;
|
|
18
|
+
export declare function tap<T>(f: (data: MultiSet<T>) => void): PipedOperator<T, T>;
|
|
@@ -6,6 +6,9 @@ const SYMBOL_MARKER = randomHash();
|
|
|
6
6
|
function randomHash() {
|
|
7
7
|
return Math.random() * (2 ** 31 - 1) >>> 0;
|
|
8
8
|
}
|
|
9
|
+
const buf = new ArrayBuffer(8);
|
|
10
|
+
const dv = new DataView(buf);
|
|
11
|
+
const u8 = new Uint8Array(buf);
|
|
9
12
|
class MurmurHashStream {
|
|
10
13
|
constructor() {
|
|
11
14
|
this.hash = RANDOM_SEED;
|
|
@@ -55,10 +58,15 @@ class MurmurHashStream {
|
|
|
55
58
|
}
|
|
56
59
|
return;
|
|
57
60
|
case `number`:
|
|
58
|
-
|
|
59
|
-
this._writeByte(
|
|
60
|
-
this._writeByte(
|
|
61
|
-
this._writeByte(
|
|
61
|
+
dv.setFloat64(0, chunk, true);
|
|
62
|
+
this._writeByte(u8[0]);
|
|
63
|
+
this._writeByte(u8[1]);
|
|
64
|
+
this._writeByte(u8[2]);
|
|
65
|
+
this._writeByte(u8[3]);
|
|
66
|
+
this._writeByte(u8[4]);
|
|
67
|
+
this._writeByte(u8[5]);
|
|
68
|
+
this._writeByte(u8[6]);
|
|
69
|
+
this._writeByte(u8[7]);
|
|
62
70
|
return;
|
|
63
71
|
case `bigint`: {
|
|
64
72
|
let value = chunk;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"murmur.js","sources":["../../../src/hashing/murmur.ts"],"sourcesContent":["/*\n * Implementation of murmur hash based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst RANDOM_SEED = randomHash()\nconst STRING_MARKER = randomHash()\nconst BIG_INT_MARKER = randomHash()\nconst NEG_BIG_INT_MARKER = randomHash()\nconst SYMBOL_MARKER = randomHash()\n\nexport type Hash = number\n\nexport function randomHash() {\n return (Math.random() * (2 ** 31 - 1)) >>> 0\n}\n\nexport interface Hasher {\n update: (val: symbol | string | number | bigint) => void\n digest: () => number\n}\n\n/**\n * This implementation of Murmur hash uses a random initial seed and random markers.\n * This means that hashes aren't deterministic across app restarts.\n * This is intentional in the composites polyfill to be resistent to hash-flooding attacks\n * where malicious users would precompute lots of different objects whose hashes collide with each other.\n *\n * Currently, for ts/db-ivm this is fine because we don't persist client state.\n * However, when we will introduce persistence we will either need to store the seeds or remove the randomness\n * to ensure deterministic hashes across app restarts.\n */\nexport class MurmurHashStream implements Hasher {\n private hash: number = RANDOM_SEED\n private length = 0\n private carry = 0\n private carryBytes = 0\n\n private _mix(k1: number): void {\n k1 = Math.imul(k1, 0xcc9e2d51)\n k1 = (k1 << 15) | (k1 >>> 17)\n k1 = Math.imul(k1, 0x1b873593)\n this.hash ^= k1\n this.hash = (this.hash << 13) | (this.hash >>> 19)\n this.hash = Math.imul(this.hash, 5) + 0xe6546b64\n }\n\n private _writeByte(byte: number): void {\n this.carry |= (byte & 0xff) << (8 * this.carryBytes)\n this.carryBytes++\n this.length++\n\n if (this.carryBytes === 4) {\n this._mix(this.carry >>> 0)\n this.carry = 0\n this.carryBytes = 0\n }\n }\n\n update(chunk: symbol | string | number | bigint): void {\n switch (typeof chunk) {\n case `symbol`: {\n this.update(SYMBOL_MARKER)\n const description = chunk.description\n if (!description) {\n return\n }\n\n for (let i = 0; i < description.length; i++) {\n const code = description.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n }\n case `string`:\n this.update(STRING_MARKER)\n for (let i = 0; i < chunk.length; i++) {\n const code = chunk.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n case `number`:\n
|
|
1
|
+
{"version":3,"file":"murmur.js","sources":["../../../src/hashing/murmur.ts"],"sourcesContent":["/*\n * Implementation of murmur hash based on the Composites polyfill implementation:\n * https://github.com/tc39/proposal-composites\n */\n\nconst RANDOM_SEED = randomHash()\nconst STRING_MARKER = randomHash()\nconst BIG_INT_MARKER = randomHash()\nconst NEG_BIG_INT_MARKER = randomHash()\nconst SYMBOL_MARKER = randomHash()\n\nexport type Hash = number\n\nexport function randomHash() {\n return (Math.random() * (2 ** 31 - 1)) >>> 0\n}\n\nexport interface Hasher {\n update: (val: symbol | string | number | bigint) => void\n digest: () => number\n}\n\n// Allocate these once, outside the hot path\nconst buf = new ArrayBuffer(8)\n// dv and u8 are 2 different views on the same buffer `buf`\nconst dv = new DataView(buf)\nconst u8 = new Uint8Array(buf)\n\n/**\n * This implementation of Murmur hash uses a random initial seed and random markers.\n * This means that hashes aren't deterministic across app restarts.\n * This is intentional in the composites polyfill to be resistent to hash-flooding attacks\n * where malicious users would precompute lots of different objects whose hashes collide with each other.\n *\n * Currently, for ts/db-ivm this is fine because we don't persist client state.\n * However, when we will introduce persistence we will either need to store the seeds or remove the randomness\n * to ensure deterministic hashes across app restarts.\n */\nexport class MurmurHashStream implements Hasher {\n private hash: number = RANDOM_SEED\n private length = 0\n private carry = 0\n private carryBytes = 0\n\n private _mix(k1: number): void {\n k1 = Math.imul(k1, 0xcc9e2d51)\n k1 = (k1 << 15) | (k1 >>> 17)\n k1 = Math.imul(k1, 0x1b873593)\n this.hash ^= k1\n this.hash = (this.hash << 13) | (this.hash >>> 19)\n this.hash = Math.imul(this.hash, 5) + 0xe6546b64\n }\n\n private _writeByte(byte: number): void {\n this.carry |= (byte & 0xff) << (8 * this.carryBytes)\n this.carryBytes++\n this.length++\n\n if (this.carryBytes === 4) {\n this._mix(this.carry >>> 0)\n this.carry = 0\n this.carryBytes = 0\n }\n }\n\n update(chunk: symbol | string | number | bigint): void {\n switch (typeof chunk) {\n case `symbol`: {\n this.update(SYMBOL_MARKER)\n const description = chunk.description\n if (!description) {\n return\n }\n\n for (let i = 0; i < description.length; i++) {\n const code = description.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n }\n case `string`:\n this.update(STRING_MARKER)\n for (let i = 0; i < chunk.length; i++) {\n const code = chunk.charCodeAt(i)\n this._writeByte(code & 0xff)\n this._writeByte((code >>> 8) & 0xff)\n }\n return\n case `number`:\n dv.setFloat64(0, chunk, true) // fixed little-endian\n this._writeByte(u8[0]!)\n this._writeByte(u8[1]!)\n this._writeByte(u8[2]!)\n this._writeByte(u8[3]!)\n this._writeByte(u8[4]!)\n this._writeByte(u8[5]!)\n this._writeByte(u8[6]!)\n this._writeByte(u8[7]!)\n return\n case `bigint`: {\n let value = chunk\n if (value < 0n) {\n value = -value\n this.update(NEG_BIG_INT_MARKER)\n } else {\n this.update(BIG_INT_MARKER)\n }\n while (value > 0n) {\n this._writeByte(Number(value & 0xffn))\n value >>= 8n\n }\n if (chunk === 0n) this._writeByte(0)\n return\n }\n default:\n throw new TypeError(`Unsupported input type: ${typeof chunk}`)\n }\n }\n\n digest(): number {\n if (this.carryBytes > 0) {\n let k1 = this.carry >>> 0\n k1 = Math.imul(k1, 0xcc9e2d51)\n k1 = (k1 << 15) | (k1 >>> 17)\n k1 = Math.imul(k1, 0x1b873593)\n this.hash ^= k1\n }\n\n this.hash ^= this.length\n this.hash ^= this.hash >>> 16\n this.hash = Math.imul(this.hash, 0x85ebca6b)\n this.hash ^= this.hash >>> 13\n this.hash = Math.imul(this.hash, 0xc2b2ae35)\n this.hash ^= this.hash >>> 16\n\n return this.hash >>> 0\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2,17 +2,17 @@ import { DifferenceStreamWriter, LinearUnaryOperator, DifferenceStreamReader } f
|
|
|
2
2
|
import { PipedOperator } from '../types.js';
|
|
3
3
|
import { MultiSet } from '../multiset.js';
|
|
4
4
|
/**
|
|
5
|
-
* Operator that applies a function to each
|
|
5
|
+
* Operator that applies a function to each multi-set in the input stream
|
|
6
6
|
*/
|
|
7
7
|
export declare class TapOperator<T> extends LinearUnaryOperator<T, T> {
|
|
8
8
|
#private;
|
|
9
|
-
constructor(id: number, inputA: DifferenceStreamReader<T>, output: DifferenceStreamWriter<T>, f: (data: T) => void);
|
|
9
|
+
constructor(id: number, inputA: DifferenceStreamReader<T>, output: DifferenceStreamWriter<T>, f: (data: MultiSet<T>) => void);
|
|
10
10
|
inner(collection: MultiSet<T>): MultiSet<T>;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Invokes a function for each
|
|
13
|
+
* Invokes a function for each multi-set in the input stream.
|
|
14
14
|
* This operator doesn't modify the stream and is used to perform side effects.
|
|
15
|
-
* @param f - The function to invoke on each
|
|
15
|
+
* @param f - The function to invoke on each multi-set
|
|
16
16
|
* @returns The input stream
|
|
17
17
|
*/
|
|
18
|
-
export declare function tap<T>(f: (data: T) => void): PipedOperator<T, T>;
|
|
18
|
+
export declare function tap<T>(f: (data: MultiSet<T>) => void): PipedOperator<T, T>;
|
|
@@ -15,10 +15,8 @@ class TapOperator extends LinearUnaryOperator {
|
|
|
15
15
|
__privateSet(this, _f, f);
|
|
16
16
|
}
|
|
17
17
|
inner(collection) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return data;
|
|
21
|
-
});
|
|
18
|
+
__privateGet(this, _f).call(this, collection);
|
|
19
|
+
return collection;
|
|
22
20
|
}
|
|
23
21
|
}
|
|
24
22
|
_f = new WeakMap();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tap.js","sources":["../../../src/operators/tap.ts"],"sourcesContent":["import { DifferenceStreamWriter, LinearUnaryOperator } from \"../graph.js\"\nimport { StreamBuilder } from \"../d2.js\"\nimport type { IStreamBuilder, PipedOperator } from \"../types.js\"\nimport type { DifferenceStreamReader } from \"../graph.js\"\nimport type { MultiSet } from \"../multiset.js\"\n\n/**\n * Operator that applies a function to each
|
|
1
|
+
{"version":3,"file":"tap.js","sources":["../../../src/operators/tap.ts"],"sourcesContent":["import { DifferenceStreamWriter, LinearUnaryOperator } from \"../graph.js\"\nimport { StreamBuilder } from \"../d2.js\"\nimport type { IStreamBuilder, PipedOperator } from \"../types.js\"\nimport type { DifferenceStreamReader } from \"../graph.js\"\nimport type { MultiSet } from \"../multiset.js\"\n\n/**\n * Operator that applies a function to each multi-set in the input stream\n */\nexport class TapOperator<T> extends LinearUnaryOperator<T, T> {\n #f: (data: MultiSet<T>) => void\n\n constructor(\n id: number,\n inputA: DifferenceStreamReader<T>,\n output: DifferenceStreamWriter<T>,\n f: (data: MultiSet<T>) => void\n ) {\n super(id, inputA, output)\n this.#f = f\n }\n\n inner(collection: MultiSet<T>): MultiSet<T> {\n this.#f(collection)\n return collection\n }\n}\n\n/**\n * Invokes a function for each multi-set in the input stream.\n * This operator doesn't modify the stream and is used to perform side effects.\n * @param f - The function to invoke on each multi-set\n * @returns The input stream\n */\nexport function tap<T>(f: (data: MultiSet<T>) => void): PipedOperator<T, T> {\n return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {\n const output = new StreamBuilder<T>(\n stream.graph,\n new DifferenceStreamWriter<T>()\n )\n const operator = new TapOperator<T>(\n stream.graph.getNextOperatorId(),\n stream.connectReader(),\n output.writer,\n f\n )\n stream.graph.addOperator(operator)\n stream.graph.addStream(output.connectReader())\n return output\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;AASO,MAAM,oBAAuB,oBAA0B;AAAA,EAG5D,YACE,IACA,QACA,QACA,GACA;AACA,UAAM,IAAI,QAAQ,MAAM;AAR1B;AASE,uBAAK,IAAK;AAAA,EACZ;AAAA,EAEA,MAAM,YAAsC;AAC1C,uBAAK,IAAL,WAAQ;AACR,WAAO;AAAA,EACT;AACF;AAhBE;AAwBK,SAAS,IAAO,GAAqD;AAC1E,SAAO,CAAC,WAAiD;AACvD,UAAM,SAAS,IAAI;AAAA,MACjB,OAAO;AAAA,MACP,IAAI,uBAAA;AAAA,IAA0B;AAEhC,UAAM,WAAW,IAAI;AAAA,MACnB,OAAO,MAAM,kBAAA;AAAA,MACb,OAAO,cAAA;AAAA,MACP,OAAO;AAAA,MACP;AAAA,IAAA;AAEF,WAAO,MAAM,YAAY,QAAQ;AACjC,WAAO,MAAM,UAAU,OAAO,cAAA,CAAe;AAC7C,WAAO;AAAA,EACT;AACF;"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/db-ivm",
|
|
3
3
|
"description": "Incremental View Maintenance for TanStack DB based on Differential Dataflow",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"fractional-indexing": "^3.2.0",
|
|
7
7
|
"sorted-btree": "^1.8.1"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@types/debug": "^4.1.12",
|
|
11
|
-
"@vitest/coverage-istanbul": "^3.
|
|
11
|
+
"@vitest/coverage-istanbul": "^3.2.4"
|
|
12
12
|
},
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|
package/src/hashing/murmur.ts
CHANGED
|
@@ -20,6 +20,12 @@ export interface Hasher {
|
|
|
20
20
|
digest: () => number
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// Allocate these once, outside the hot path
|
|
24
|
+
const buf = new ArrayBuffer(8)
|
|
25
|
+
// dv and u8 are 2 different views on the same buffer `buf`
|
|
26
|
+
const dv = new DataView(buf)
|
|
27
|
+
const u8 = new Uint8Array(buf)
|
|
28
|
+
|
|
23
29
|
/**
|
|
24
30
|
* This implementation of Murmur hash uses a random initial seed and random markers.
|
|
25
31
|
* This means that hashes aren't deterministic across app restarts.
|
|
@@ -82,10 +88,15 @@ export class MurmurHashStream implements Hasher {
|
|
|
82
88
|
}
|
|
83
89
|
return
|
|
84
90
|
case `number`:
|
|
85
|
-
|
|
86
|
-
this._writeByte(
|
|
87
|
-
this._writeByte(
|
|
88
|
-
this._writeByte(
|
|
91
|
+
dv.setFloat64(0, chunk, true) // fixed little-endian
|
|
92
|
+
this._writeByte(u8[0]!)
|
|
93
|
+
this._writeByte(u8[1]!)
|
|
94
|
+
this._writeByte(u8[2]!)
|
|
95
|
+
this._writeByte(u8[3]!)
|
|
96
|
+
this._writeByte(u8[4]!)
|
|
97
|
+
this._writeByte(u8[5]!)
|
|
98
|
+
this._writeByte(u8[6]!)
|
|
99
|
+
this._writeByte(u8[7]!)
|
|
89
100
|
return
|
|
90
101
|
case `bigint`: {
|
|
91
102
|
let value = chunk
|
package/src/operators/tap.ts
CHANGED
|
@@ -5,36 +5,34 @@ import type { DifferenceStreamReader } from "../graph.js"
|
|
|
5
5
|
import type { MultiSet } from "../multiset.js"
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Operator that applies a function to each
|
|
8
|
+
* Operator that applies a function to each multi-set in the input stream
|
|
9
9
|
*/
|
|
10
10
|
export class TapOperator<T> extends LinearUnaryOperator<T, T> {
|
|
11
|
-
#f: (data: T) => void
|
|
11
|
+
#f: (data: MultiSet<T>) => void
|
|
12
12
|
|
|
13
13
|
constructor(
|
|
14
14
|
id: number,
|
|
15
15
|
inputA: DifferenceStreamReader<T>,
|
|
16
16
|
output: DifferenceStreamWriter<T>,
|
|
17
|
-
f: (data: T) => void
|
|
17
|
+
f: (data: MultiSet<T>) => void
|
|
18
18
|
) {
|
|
19
19
|
super(id, inputA, output)
|
|
20
20
|
this.#f = f
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
inner(collection: MultiSet<T>): MultiSet<T> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return data
|
|
27
|
-
})
|
|
24
|
+
this.#f(collection)
|
|
25
|
+
return collection
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
/**
|
|
32
|
-
* Invokes a function for each
|
|
30
|
+
* Invokes a function for each multi-set in the input stream.
|
|
33
31
|
* This operator doesn't modify the stream and is used to perform side effects.
|
|
34
|
-
* @param f - The function to invoke on each
|
|
32
|
+
* @param f - The function to invoke on each multi-set
|
|
35
33
|
* @returns The input stream
|
|
36
34
|
*/
|
|
37
|
-
export function tap<T>(f: (data: T) => void): PipedOperator<T, T> {
|
|
35
|
+
export function tap<T>(f: (data: MultiSet<T>) => void): PipedOperator<T, T> {
|
|
38
36
|
return (stream: IStreamBuilder<T>): IStreamBuilder<T> => {
|
|
39
37
|
const output = new StreamBuilder<T>(
|
|
40
38
|
stream.graph,
|