@tanstack/db-ivm 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/types.ts ADDED
@@ -0,0 +1,376 @@
1
+ import type { MultiSet, MultiSetArray } from "./multiset.js"
2
+ import type {
3
+ BinaryOperator,
4
+ DifferenceStreamReader,
5
+ DifferenceStreamWriter,
6
+ UnaryOperator,
7
+ } from "./graph.js"
8
+
9
+ export type KeyValue<K, V> = [K, V]
10
+
11
+ export interface IOperator<_T> {
12
+ run: () => void
13
+ hasPendingWork: () => boolean
14
+ }
15
+
16
+ export interface IDifferenceStreamReader<T> {
17
+ drain: () => Array<MultiSet<T>>
18
+ isEmpty: () => boolean
19
+ }
20
+
21
+ export interface IDifferenceStreamWriter<T> {
22
+ sendData: (collection: MultiSet<T> | MultiSetArray<T>) => void
23
+ newReader: () => IDifferenceStreamReader<T>
24
+ }
25
+
26
+ export interface ID2 {
27
+ getNextOperatorId: () => number
28
+ newInput: <T>() => IStreamBuilder<T>
29
+ addOperator: (operator: UnaryOperator<any> | BinaryOperator<any>) => void
30
+ addStream: (stream: DifferenceStreamReader<any>) => void
31
+ finalize: () => void
32
+ step: () => void
33
+ }
34
+
35
+ export interface IStreamBuilder<T> {
36
+ writer: DifferenceStreamWriter<T>
37
+ connectReader: () => DifferenceStreamReader<T>
38
+ graph: ID2
39
+
40
+ // Don't judge, this is the only way to type this function.
41
+ // rxjs has very similar code to type its pipe function
42
+ // https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/util/pipe.ts
43
+ // We go to 20 operators deep, because surly that's enough for anyone...
44
+ // A user can always split the pipe into multiple pipes to get around this.
45
+ pipe: (<O>(o1: PipedOperator<T, O>) => IStreamBuilder<O>) &
46
+ (<T2, O>(
47
+ o1: PipedOperator<T, T2>,
48
+ o2: PipedOperator<T2, O>
49
+ ) => IStreamBuilder<O>) &
50
+ (<T2, T3, O>(
51
+ o1: PipedOperator<T, T2>,
52
+ o2: PipedOperator<T2, T3>,
53
+ o3: PipedOperator<T3, O>
54
+ ) => IStreamBuilder<O>) &
55
+ (<T2, T3, T4, O>(
56
+ o1: PipedOperator<T, T2>,
57
+ o2: PipedOperator<T2, T3>,
58
+ o3: PipedOperator<T3, T4>,
59
+ o4: PipedOperator<T4, O>
60
+ ) => IStreamBuilder<O>) &
61
+ (<T2, T3, T4, T5, O>(
62
+ o1: PipedOperator<T, T2>,
63
+ o2: PipedOperator<T2, T3>,
64
+ o3: PipedOperator<T3, T4>,
65
+ o4: PipedOperator<T4, T5>,
66
+ o5: PipedOperator<T5, O>
67
+ ) => IStreamBuilder<O>) &
68
+ (<T2, T3, T4, T5, T6, O>(
69
+ o1: PipedOperator<T, T2>,
70
+ o2: PipedOperator<T2, T3>,
71
+ o3: PipedOperator<T3, T4>,
72
+ o4: PipedOperator<T4, T5>,
73
+ o5: PipedOperator<T5, T6>,
74
+ o6: PipedOperator<T6, O>
75
+ ) => IStreamBuilder<O>) &
76
+ (<T2, T3, T4, T5, T6, T7, O>(
77
+ o1: PipedOperator<T, T2>,
78
+ o2: PipedOperator<T2, T3>,
79
+ o3: PipedOperator<T3, T4>,
80
+ o4: PipedOperator<T4, T5>,
81
+ o5: PipedOperator<T5, T6>,
82
+ o6: PipedOperator<T6, T7>,
83
+ o7: PipedOperator<T7, O>
84
+ ) => IStreamBuilder<O>) &
85
+ (<T2, T3, T4, T5, T6, T7, T8, O>(
86
+ o1: PipedOperator<T, T2>,
87
+ o2: PipedOperator<T2, T3>,
88
+ o3: PipedOperator<T3, T4>,
89
+ o4: PipedOperator<T4, T5>,
90
+ o5: PipedOperator<T5, T6>,
91
+ o6: PipedOperator<T6, T7>,
92
+ o7: PipedOperator<T7, T8>,
93
+ o8: PipedOperator<T8, O>
94
+ ) => IStreamBuilder<O>) &
95
+ (<T2, T3, T4, T5, T6, T7, T8, T9, O>(
96
+ o1: PipedOperator<T, T2>,
97
+ o2: PipedOperator<T2, T3>,
98
+ o3: PipedOperator<T3, T4>,
99
+ o4: PipedOperator<T4, T5>,
100
+ o5: PipedOperator<T5, T6>,
101
+ o6: PipedOperator<T6, T7>,
102
+ o7: PipedOperator<T7, T8>,
103
+ o8: PipedOperator<T8, T9>,
104
+ o9: PipedOperator<T9, O>
105
+ ) => IStreamBuilder<O>) &
106
+ (<T2, T3, T4, T5, T6, T7, T8, T9, T10, O>(
107
+ o1: PipedOperator<T, T2>,
108
+ o2: PipedOperator<T2, T3>,
109
+ o3: PipedOperator<T3, T4>,
110
+ o4: PipedOperator<T4, T5>,
111
+ o5: PipedOperator<T5, T6>,
112
+ o6: PipedOperator<T6, T7>,
113
+ o7: PipedOperator<T7, T8>,
114
+ o8: PipedOperator<T8, T9>,
115
+ o9: PipedOperator<T9, T10>,
116
+ o10: PipedOperator<T10, O>
117
+ ) => IStreamBuilder<O>) &
118
+ (<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, O>(
119
+ o1: PipedOperator<T, T2>,
120
+ o2: PipedOperator<T2, T3>,
121
+ o3: PipedOperator<T3, T4>,
122
+ o4: PipedOperator<T4, T5>,
123
+ o5: PipedOperator<T5, T6>,
124
+ o6: PipedOperator<T6, T7>,
125
+ o7: PipedOperator<T7, T8>,
126
+ o8: PipedOperator<T8, T9>,
127
+ o9: PipedOperator<T9, T10>,
128
+ o10: PipedOperator<T10, T11>,
129
+ o11: PipedOperator<T11, O>
130
+ ) => IStreamBuilder<O>) &
131
+ (<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, O>(
132
+ o1: PipedOperator<T, T2>,
133
+ o2: PipedOperator<T2, T3>,
134
+ o3: PipedOperator<T3, T4>,
135
+ o4: PipedOperator<T4, T5>,
136
+ o5: PipedOperator<T5, T6>,
137
+ o6: PipedOperator<T6, T7>,
138
+ o7: PipedOperator<T7, T8>,
139
+ o8: PipedOperator<T8, T9>,
140
+ o9: PipedOperator<T9, T10>,
141
+ o10: PipedOperator<T10, T11>,
142
+ o11: PipedOperator<T11, T12>,
143
+ o12: PipedOperator<T12, O>
144
+ ) => IStreamBuilder<O>) &
145
+ (<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, O>(
146
+ o1: PipedOperator<T, T2>,
147
+ o2: PipedOperator<T2, T3>,
148
+ o3: PipedOperator<T3, T4>,
149
+ o4: PipedOperator<T4, T5>,
150
+ o5: PipedOperator<T5, T6>,
151
+ o6: PipedOperator<T6, T7>,
152
+ o7: PipedOperator<T7, T8>,
153
+ o8: PipedOperator<T8, T9>,
154
+ o9: PipedOperator<T9, T10>,
155
+ o10: PipedOperator<T10, T11>,
156
+ o11: PipedOperator<T11, T12>,
157
+ o12: PipedOperator<T12, T13>,
158
+ o13: PipedOperator<T13, O>
159
+ ) => IStreamBuilder<O>) &
160
+ (<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, O>(
161
+ o1: PipedOperator<T, T2>,
162
+ o2: PipedOperator<T2, T3>,
163
+ o3: PipedOperator<T3, T4>,
164
+ o4: PipedOperator<T4, T5>,
165
+ o5: PipedOperator<T5, T6>,
166
+ o6: PipedOperator<T6, T7>,
167
+ o7: PipedOperator<T7, T8>,
168
+ o8: PipedOperator<T8, T9>,
169
+ o9: PipedOperator<T9, T10>,
170
+ o10: PipedOperator<T10, T11>,
171
+ o11: PipedOperator<T11, T12>,
172
+ o12: PipedOperator<T12, T13>,
173
+ o13: PipedOperator<T13, T14>,
174
+ o14: PipedOperator<T14, O>
175
+ ) => IStreamBuilder<O>) &
176
+ (<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, O>(
177
+ o1: PipedOperator<T, T2>,
178
+ o2: PipedOperator<T2, T3>,
179
+ o3: PipedOperator<T3, T4>,
180
+ o4: PipedOperator<T4, T5>,
181
+ o5: PipedOperator<T5, T6>,
182
+ o6: PipedOperator<T6, T7>,
183
+ o7: PipedOperator<T7, T8>,
184
+ o8: PipedOperator<T8, T9>,
185
+ o9: PipedOperator<T9, T10>,
186
+ o10: PipedOperator<T10, T11>,
187
+ o11: PipedOperator<T11, T12>,
188
+ o12: PipedOperator<T12, T13>,
189
+ o13: PipedOperator<T13, T14>,
190
+ o14: PipedOperator<T14, T15>,
191
+ o15: PipedOperator<T15, O>
192
+ ) => IStreamBuilder<O>) &
193
+ (<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, O>(
194
+ o1: PipedOperator<T, T2>,
195
+ o2: PipedOperator<T2, T3>,
196
+ o3: PipedOperator<T3, T4>,
197
+ o4: PipedOperator<T4, T5>,
198
+ o5: PipedOperator<T5, T6>,
199
+ o6: PipedOperator<T6, T7>,
200
+ o7: PipedOperator<T7, T8>,
201
+ o8: PipedOperator<T8, T9>,
202
+ o9: PipedOperator<T9, T10>,
203
+ o10: PipedOperator<T10, T11>,
204
+ o11: PipedOperator<T11, T12>,
205
+ o12: PipedOperator<T12, T13>,
206
+ o13: PipedOperator<T13, T14>,
207
+ o14: PipedOperator<T14, T15>,
208
+ o15: PipedOperator<T15, T16>,
209
+ o16: PipedOperator<T16, O>
210
+ ) => IStreamBuilder<O>) &
211
+ (<
212
+ T2,
213
+ T3,
214
+ T4,
215
+ T5,
216
+ T6,
217
+ T7,
218
+ T8,
219
+ T9,
220
+ T10,
221
+ T11,
222
+ T12,
223
+ T13,
224
+ T14,
225
+ T15,
226
+ T16,
227
+ T17,
228
+ O,
229
+ >(
230
+ o1: PipedOperator<T, T2>,
231
+ o2: PipedOperator<T2, T3>,
232
+ o3: PipedOperator<T3, T4>,
233
+ o4: PipedOperator<T4, T5>,
234
+ o5: PipedOperator<T5, T6>,
235
+ o6: PipedOperator<T6, T7>,
236
+ o7: PipedOperator<T7, T8>,
237
+ o8: PipedOperator<T8, T9>,
238
+ o9: PipedOperator<T9, T10>,
239
+ o10: PipedOperator<T10, T11>,
240
+ o11: PipedOperator<T11, T12>,
241
+ o12: PipedOperator<T12, T13>,
242
+ o13: PipedOperator<T13, T14>,
243
+ o14: PipedOperator<T14, T15>,
244
+ o15: PipedOperator<T15, T16>,
245
+ o16: PipedOperator<T16, T17>,
246
+ o17: PipedOperator<T17, O>
247
+ ) => IStreamBuilder<O>) &
248
+ (<
249
+ T2,
250
+ T3,
251
+ T4,
252
+ T5,
253
+ T6,
254
+ T7,
255
+ T8,
256
+ T9,
257
+ T10,
258
+ T11,
259
+ T12,
260
+ T13,
261
+ T14,
262
+ T15,
263
+ T16,
264
+ T17,
265
+ T18,
266
+ O,
267
+ >(
268
+ o1: PipedOperator<T, T2>,
269
+ o2: PipedOperator<T2, T3>,
270
+ o3: PipedOperator<T3, T4>,
271
+ o4: PipedOperator<T4, T5>,
272
+ o5: PipedOperator<T5, T6>,
273
+ o6: PipedOperator<T6, T7>,
274
+ o7: PipedOperator<T7, T8>,
275
+ o8: PipedOperator<T8, T9>,
276
+ o9: PipedOperator<T9, T10>,
277
+ o10: PipedOperator<T10, T11>,
278
+ o11: PipedOperator<T11, T12>,
279
+ o12: PipedOperator<T12, T13>,
280
+ o13: PipedOperator<T13, T14>,
281
+ o14: PipedOperator<T14, T15>,
282
+ o15: PipedOperator<T15, T16>,
283
+ o16: PipedOperator<T16, T17>,
284
+ o17: PipedOperator<T17, T18>,
285
+ o18: PipedOperator<T18, O>
286
+ ) => IStreamBuilder<O>) &
287
+ (<
288
+ T2,
289
+ T3,
290
+ T4,
291
+ T5,
292
+ T6,
293
+ T7,
294
+ T8,
295
+ T9,
296
+ T10,
297
+ T11,
298
+ T12,
299
+ T13,
300
+ T14,
301
+ T15,
302
+ T16,
303
+ T17,
304
+ T18,
305
+ T19,
306
+ O,
307
+ >(
308
+ o1: PipedOperator<T, T2>,
309
+ o2: PipedOperator<T2, T3>,
310
+ o3: PipedOperator<T3, T4>,
311
+ o4: PipedOperator<T4, T5>,
312
+ o5: PipedOperator<T5, T6>,
313
+ o6: PipedOperator<T6, T7>,
314
+ o7: PipedOperator<T7, T8>,
315
+ o8: PipedOperator<T8, T9>,
316
+ o9: PipedOperator<T9, T10>,
317
+ o10: PipedOperator<T10, T11>,
318
+ o11: PipedOperator<T11, T12>,
319
+ o12: PipedOperator<T12, T13>,
320
+ o13: PipedOperator<T13, T14>,
321
+ o14: PipedOperator<T14, T15>,
322
+ o15: PipedOperator<T15, T16>,
323
+ o16: PipedOperator<T16, T17>,
324
+ o17: PipedOperator<T17, T18>,
325
+ o18: PipedOperator<T18, T19>,
326
+ o19: PipedOperator<T19, O>
327
+ ) => IStreamBuilder<O>) &
328
+ (<
329
+ T2,
330
+ T3,
331
+ T4,
332
+ T5,
333
+ T6,
334
+ T7,
335
+ T8,
336
+ T9,
337
+ T10,
338
+ T11,
339
+ T12,
340
+ T13,
341
+ T14,
342
+ T15,
343
+ T16,
344
+ T17,
345
+ T18,
346
+ T19,
347
+ T20,
348
+ O,
349
+ >(
350
+ o1: PipedOperator<T, T2>,
351
+ o2: PipedOperator<T2, T3>,
352
+ o3: PipedOperator<T3, T4>,
353
+ o4: PipedOperator<T4, T5>,
354
+ o5: PipedOperator<T5, T6>,
355
+ o6: PipedOperator<T6, T7>,
356
+ o7: PipedOperator<T7, T8>,
357
+ o8: PipedOperator<T8, T9>,
358
+ o9: PipedOperator<T9, T10>,
359
+ o10: PipedOperator<T10, T11>,
360
+ o11: PipedOperator<T11, T12>,
361
+ o12: PipedOperator<T12, T13>,
362
+ o13: PipedOperator<T13, T14>,
363
+ o14: PipedOperator<T14, T15>,
364
+ o15: PipedOperator<T15, T16>,
365
+ o16: PipedOperator<T16, T17>,
366
+ o17: PipedOperator<T17, T18>,
367
+ o18: PipedOperator<T18, T19>,
368
+ o19: PipedOperator<T19, T20>,
369
+ o20: PipedOperator<T20, O>
370
+ ) => IStreamBuilder<O>) &
371
+ (<O>(...operators: Array<PipedOperator<any, any>>) => IStreamBuilder<O>)
372
+ }
373
+
374
+ export type PipedOperator<I, O> = (
375
+ stream: IStreamBuilder<I>
376
+ ) => IStreamBuilder<O>
package/src/utils.ts ADDED
@@ -0,0 +1,163 @@
1
+ import murmurhash from "murmurhash-js"
2
+
3
+ /**
4
+ * A map that returns a default value for keys that are not present.
5
+ */
6
+ export class DefaultMap<K, V> extends Map<K, V> {
7
+ constructor(
8
+ private defaultValue: () => V,
9
+ entries?: Iterable<[K, V]>
10
+ ) {
11
+ super(entries)
12
+ }
13
+
14
+ get(key: K): V {
15
+ if (!this.has(key)) {
16
+ this.set(key, this.defaultValue())
17
+ }
18
+ return super.get(key)!
19
+ }
20
+
21
+ /**
22
+ * Update the value for a key using a function.
23
+ */
24
+ update(key: K, updater: (value: V) => V): V {
25
+ const value = this.get(key)
26
+ const newValue = updater(value)
27
+ this.set(key, newValue)
28
+ return newValue
29
+ }
30
+ }
31
+
32
+ // JS engines have various limits on how many args can be passed to a function
33
+ // with a spread operator, so we need to split the operation into chunks
34
+ // 32767 is the max for Chrome 14, all others are higher
35
+ // TODO: investigate the performance of this and other approaches
36
+ const chunkSize = 30000
37
+ export function chunkedArrayPush(array: Array<unknown>, other: Array<unknown>) {
38
+ if (other.length <= chunkSize) {
39
+ array.push(...other)
40
+ } else {
41
+ for (let i = 0; i < other.length; i += chunkSize) {
42
+ const chunk = other.slice(i, i + chunkSize)
43
+ array.push(...chunk)
44
+ }
45
+ }
46
+ }
47
+
48
+ const hashCache = new WeakMap()
49
+
50
+ /**
51
+ * Replacer function for JSON.stringify that converts unsupported types to strings
52
+ */
53
+ function hashReplacer(_key: string, value: any): any {
54
+ if (typeof value === `bigint`) {
55
+ return String(value)
56
+ } else if (typeof value === `symbol`) {
57
+ return String(value)
58
+ } else if (typeof value === `function`) {
59
+ return String(value)
60
+ } else if (value === undefined) {
61
+ return `undefined`
62
+ } else if (value instanceof Map) {
63
+ return `Map(${JSON.stringify(Array.from(value.entries()), hashReplacer)})`
64
+ } else if (value instanceof Set) {
65
+ return `Set(${JSON.stringify(Array.from(value.values()), hashReplacer)})`
66
+ }
67
+ return value
68
+ }
69
+
70
+ /**
71
+ * A hash method that caches the hash of a value in a week map
72
+ */
73
+ export function hash(data: any): string {
74
+ if (
75
+ data === null ||
76
+ data === undefined ||
77
+ (typeof data !== `object` && typeof data !== `function`)
78
+ ) {
79
+ // Can't be cached in the weak map because it's not an object
80
+ const serialized = JSON.stringify(data, hashReplacer)
81
+ return murmurhash.murmur3(serialized).toString(16)
82
+ }
83
+
84
+ if (hashCache.has(data)) {
85
+ return hashCache.get(data)
86
+ }
87
+
88
+ const serialized = JSON.stringify(data, hashReplacer)
89
+ const hashValue = murmurhash.murmur3(serialized).toString(16)
90
+ hashCache.set(data, hashValue)
91
+ return hashValue
92
+ }
93
+
94
+ export function binarySearch<T>(
95
+ array: Array<T>,
96
+ value: T,
97
+ comparator: (a: T, b: T) => number
98
+ ): number {
99
+ let low = 0
100
+ let high = array.length
101
+ while (low < high) {
102
+ const mid = Math.floor((low + high) / 2)
103
+ const comparison = comparator(array[mid]!, value)
104
+ if (comparison < 0) {
105
+ low = mid + 1
106
+ } else if (comparison > 0) {
107
+ high = mid
108
+ } else {
109
+ return mid
110
+ }
111
+ }
112
+ return low
113
+ }
114
+
115
+ /**
116
+ * Utility for generating unique IDs for objects and values.
117
+ * Uses WeakMap for object reference tracking and consistent hashing for primitives.
118
+ */
119
+ export class ObjectIdGenerator {
120
+ private objectIds = new WeakMap<object, number>()
121
+ private nextId = 0
122
+
123
+ /**
124
+ * Get a unique identifier for any value.
125
+ * - Objects: Uses WeakMap for reference-based identity
126
+ * - Primitives: Uses consistent string-based hashing
127
+ */
128
+ getId(value: any): number {
129
+ // For primitives, use a simple hash of their string representation
130
+ if (typeof value !== `object` || value === null) {
131
+ const str = String(value)
132
+ let hashValue = 0
133
+ for (let i = 0; i < str.length; i++) {
134
+ const char = str.charCodeAt(i)
135
+ hashValue = (hashValue << 5) - hashValue + char
136
+ hashValue = hashValue & hashValue // Convert to 32-bit integer
137
+ }
138
+ return hashValue
139
+ }
140
+
141
+ // For objects, use WeakMap to assign unique IDs
142
+ if (!this.objectIds.has(value)) {
143
+ this.objectIds.set(value, this.nextId++)
144
+ }
145
+ return this.objectIds.get(value)!
146
+ }
147
+
148
+ /**
149
+ * Get a string representation of the ID for use in composite keys.
150
+ */
151
+ getStringId(value: any): string {
152
+ if (value === null) return `null`
153
+ if (value === undefined) return `undefined`
154
+ if (typeof value !== `object`) return `str_${String(value)}`
155
+
156
+ return `obj_${this.getId(value)}`
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Global instance for cases where a shared object ID space is needed.
162
+ */
163
+ export const globalObjectIdGenerator = new ObjectIdGenerator()