@rocicorp/zero 0.12.2025012902 → 0.12.2025013001

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.
Files changed (55) hide show
  1. package/out/advanced.js +1 -1
  2. package/out/{chunk-QAAJZ32T.js → chunk-ZVLXEWWB.js} +71 -11
  3. package/out/chunk-ZVLXEWWB.js.map +7 -0
  4. package/out/{chunk-HQA4Z25K.js → chunk-ZXVUJVVO.js} +125 -171
  5. package/out/{chunk-HQA4Z25K.js.map → chunk-ZXVUJVVO.js.map} +4 -4
  6. package/out/solid.js +2 -2
  7. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  8. package/out/zero-cache/src/services/change-source/pg/change-source.js +86 -117
  9. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  10. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts +0 -2
  11. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +1 -1
  12. package/out/zero-cache/src/services/change-streamer/error-type-enum.js +0 -1
  13. package/out/zero-cache/src/services/change-streamer/error-type-enum.js.map +1 -1
  14. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  15. package/out/zero-cache/src/services/change-streamer/storer.js +6 -6
  16. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  17. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +1 -1
  18. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  19. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  20. package/out/zero-cache/src/services/view-syncer/view-syncer.js +26 -2
  21. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  22. package/out/zero.js +2 -2
  23. package/out/zql/src/ivm/change.d.ts +4 -4
  24. package/out/zql/src/ivm/change.d.ts.map +1 -1
  25. package/out/zql/src/ivm/change.js +1 -4
  26. package/out/zql/src/ivm/change.js.map +1 -1
  27. package/out/zql/src/ivm/data.d.ts +2 -1
  28. package/out/zql/src/ivm/data.d.ts.map +1 -1
  29. package/out/zql/src/ivm/data.js +7 -0
  30. package/out/zql/src/ivm/data.js.map +1 -1
  31. package/out/zql/src/ivm/exists.d.ts +3 -2
  32. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  33. package/out/zql/src/ivm/exists.js +55 -64
  34. package/out/zql/src/ivm/exists.js.map +1 -1
  35. package/out/zql/src/ivm/filter-push.js +1 -1
  36. package/out/zql/src/ivm/filter-push.js.map +1 -1
  37. package/out/zql/src/ivm/filter.d.ts +2 -3
  38. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  39. package/out/zql/src/ivm/filter.js +12 -7
  40. package/out/zql/src/ivm/filter.js.map +1 -1
  41. package/out/zql/src/ivm/join.d.ts +4 -1
  42. package/out/zql/src/ivm/join.d.ts.map +1 -1
  43. package/out/zql/src/ivm/join.js +32 -25
  44. package/out/zql/src/ivm/join.js.map +1 -1
  45. package/out/zql/src/ivm/skip.d.ts.map +1 -1
  46. package/out/zql/src/ivm/skip.js +1 -2
  47. package/out/zql/src/ivm/skip.js.map +1 -1
  48. package/out/zql/src/ivm/take.d.ts.map +1 -1
  49. package/out/zql/src/ivm/take.js +4 -3
  50. package/out/zql/src/ivm/take.js.map +1 -1
  51. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  52. package/out/zql/src/ivm/view-apply-change.js +4 -10
  53. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  54. package/package.json +2 -1
  55. package/out/chunk-QAAJZ32T.js.map +0 -7
package/out/advanced.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./chunk-ULOTOBTC.js";
2
2
  import {
3
3
  applyChange
4
- } from "./chunk-QAAJZ32T.js";
4
+ } from "./chunk-ZVLXEWWB.js";
5
5
  import "./chunk-424PT5DM.js";
6
6
  export {
7
7
  applyChange
@@ -64,6 +64,64 @@ function must(v, msg) {
64
64
  return v;
65
65
  }
66
66
 
67
+ // ../zql/src/ivm/data.ts
68
+ import { compareUTF8 } from "compare-utf8";
69
+ function compareValues(a, b) {
70
+ a = normalizeUndefined(a);
71
+ b = normalizeUndefined(b);
72
+ if (a === b) {
73
+ return 0;
74
+ }
75
+ if (a === null) {
76
+ return -1;
77
+ }
78
+ if (b === null) {
79
+ return 1;
80
+ }
81
+ if (typeof a === "boolean") {
82
+ assertBoolean(b);
83
+ return a ? 1 : -1;
84
+ }
85
+ if (typeof a === "number") {
86
+ assertNumber(b);
87
+ return a - b;
88
+ }
89
+ if (typeof a === "string") {
90
+ assertString(b);
91
+ return compareUTF8(a, b);
92
+ }
93
+ throw new Error(`Unsupported type: ${a}`);
94
+ }
95
+ function normalizeUndefined(v) {
96
+ return v ?? null;
97
+ }
98
+ function makeComparator(order, reverse) {
99
+ return (a, b) => {
100
+ for (const ord of order) {
101
+ const field = ord[0];
102
+ const comp = compareValues(a[field], b[field]);
103
+ if (comp !== 0) {
104
+ const result = ord[1] === "asc" ? comp : -comp;
105
+ return reverse ? -result : result;
106
+ }
107
+ }
108
+ return 0;
109
+ };
110
+ }
111
+ function valuesEqual(a, b) {
112
+ if (a == null || b == null) {
113
+ return false;
114
+ }
115
+ return a === b;
116
+ }
117
+ function drainStreams(node) {
118
+ for (const stream of Object.values(node.relationships)) {
119
+ for (const node2 of stream()) {
120
+ drainStreams(node2);
121
+ }
122
+ }
123
+ }
124
+
67
125
  // ../zql/src/ivm/view-apply-change.ts
68
126
  function applyChange(parentEntry, change, schema, relationship, format) {
69
127
  if (schema.isHidden) {
@@ -74,7 +132,7 @@ function applyChange(parentEntry, change, schema, relationship, format) {
74
132
  change.node.relationships
75
133
  )) {
76
134
  const childSchema = must(schema.relationships[relationship2]);
77
- for (const node of children) {
135
+ for (const node of children()) {
78
136
  applyChange(
79
137
  parentEntry,
80
138
  { type: change.type, node },
@@ -132,7 +190,7 @@ function applyChange(parentEntry, change, schema, relationship, format) {
132
190
  }
133
191
  const newView = childFormat.singular ? void 0 : [];
134
192
  newEntry[relationship2] = newView;
135
- for (const node of children) {
193
+ for (const node of children()) {
136
194
  applyChange(
137
195
  newEntry,
138
196
  { type: "add", node },
@@ -168,7 +226,11 @@ function applyChange(parentEntry, change, schema, relationship, format) {
168
226
  existing = parentEntry[relationship];
169
227
  } else {
170
228
  const view = getChildEntryList(parentEntry, relationship);
171
- const { pos, found } = binarySearch(view, change.row, schema.compareRows);
229
+ const { pos, found } = binarySearch(
230
+ view,
231
+ change.node.row,
232
+ schema.compareRows
233
+ );
172
234
  assert(found, "node does not exist");
173
235
  existing = view[pos];
174
236
  }
@@ -267,13 +329,6 @@ function makeEntryPreserveRelationships(row, entry, relationships) {
267
329
  }
268
330
  return result;
269
331
  }
270
- function drainStreams(node) {
271
- for (const stream of Object.values(node.relationships)) {
272
- for (const node2 of stream) {
273
- drainStreams(node2);
274
- }
275
- }
276
- }
277
332
  function getChildEntryList(parentEntry, relationship) {
278
333
  const view = parentEntry[relationship];
279
334
  assertArray(view);
@@ -291,6 +346,11 @@ export {
291
346
  assertNotNull,
292
347
  unreachable,
293
348
  must,
349
+ compareValues,
350
+ normalizeUndefined,
351
+ makeComparator,
352
+ valuesEqual,
353
+ drainStreams,
294
354
  applyChange
295
355
  };
296
- //# sourceMappingURL=chunk-QAAJZ32T.js.map
356
+ //# sourceMappingURL=chunk-ZVLXEWWB.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../shared/src/asserts.ts", "../../shared/src/must.ts", "../../zql/src/ivm/data.ts", "../../zql/src/ivm/view-apply-change.ts"],
4
+ "sourcesContent": ["export function assert(\n b: unknown,\n msg: string | (() => string) = 'Assertion failed',\n): asserts b {\n if (!b) {\n const msgStr = typeof msg === 'string' ? msg : msg();\n throw new Error(msgStr);\n }\n}\n\nexport function assertString(v: unknown): asserts v is string {\n assertType(v, 'string');\n}\n\nexport function assertNumber(v: unknown): asserts v is number {\n assertType(v, 'number');\n}\n\nexport function assertBoolean(v: unknown): asserts v is boolean {\n assertType(v, 'boolean');\n}\n\nfunction assertType(v: unknown, t: string) {\n if (typeof v !== t) {\n throwInvalidType(v, t);\n }\n}\n\nexport function assertObject(v: unknown): asserts v is Record<string, unknown> {\n if (v === null) {\n throwInvalidType(v, 'object');\n }\n assertType(v, 'object');\n}\n\nexport function assertArray(v: unknown): asserts v is unknown[] {\n if (!Array.isArray(v)) {\n throwInvalidType(v, 'array');\n }\n}\n\nexport function invalidType(v: unknown, t: string): string {\n let s = 'Invalid type: ';\n if (v === null || v === undefined) {\n s += v;\n } else {\n s += `${typeof v} \\`${v}\\``;\n }\n return s + `, expected ${t}`;\n}\n\nexport function throwInvalidType(v: unknown, t: string): never {\n throw new Error(invalidType(v, t));\n}\n\nexport function assertNotNull<T>(v: T | null): asserts v is T {\n if (v === null) {\n throw new Error('Expected non-null value');\n }\n}\n\nexport function assertUndefined<T>(\n v: T | undefined,\n msg = 'Expected undefined value',\n): asserts v is T {\n if (v !== undefined) {\n throw new Error(msg);\n }\n}\n\nexport function assertNotUndefined<T>(\n v: T | undefined,\n msg = 'Expected non undefined value',\n): asserts v is T {\n if (v === undefined) {\n throw new Error(msg);\n }\n}\n\nexport function assertInstanceof<T>(\n v: unknown,\n t: new (...args: unknown[]) => T,\n): asserts v is T {\n if (!(v instanceof t)) {\n throw new Error(`Expected instanceof ${t.name}`);\n }\n}\n\nexport function assertUint8Array(v: unknown): asserts v is Uint8Array {\n assertInstanceof(v, Uint8Array);\n}\n\nexport function unreachable(): never;\nexport function unreachable(v: never): never;\nexport function unreachable(_?: never): never {\n throw new Error('Unreachable');\n}\n\nexport function notImplemented(): never {\n throw new Error('Not implemented');\n}\n", "export function must<T>(v: T | undefined | null, msg?: string): T {\n // eslint-disable-next-line eqeqeq\n if (v == null) {\n throw new Error(msg ?? `Unexpected ${v} value`);\n }\n return v;\n}\n", "import {compareUTF8} from 'compare-utf8';\nimport {\n assertBoolean,\n assertNumber,\n assertString,\n} from '../../../shared/src/asserts.ts';\nimport type {Ordering} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {Stream} from './stream.ts';\n\n/**\n * A row flowing through the pipeline, plus its relationships.\n * Relationships are generated lazily as read.\n */\nexport type Node = {\n row: Row;\n relationships: Record<string, () => Stream<Node>>;\n};\n\n/**\n * Compare two values. The values must be of the same type. This function\n * throws at runtime if the types differ.\n *\n * Note, this function considers `null === null` and\n * `undefined === undefined`. This is different than SQL. In join code,\n * null must be treated separately.\n *\n * See: https://github.com/rocicorp/mono/pull/2116/files#r1704811479\n *\n * @returns < 0 if a < b, 0 if a === b, > 0 if a > b\n */\nexport function compareValues(a: Value, b: Value): number {\n a = normalizeUndefined(a);\n b = normalizeUndefined(b);\n\n if (a === b) {\n return 0;\n }\n if (a === null) {\n return -1;\n }\n if (b === null) {\n return 1;\n }\n if (typeof a === 'boolean') {\n assertBoolean(b);\n return a ? 1 : -1;\n }\n if (typeof a === 'number') {\n assertNumber(b);\n return a - b;\n }\n if (typeof a === 'string') {\n assertString(b);\n // We compare all strings in Zero as UTF-8. This is the default on SQLite\n // and we need to match it. See:\n // https://blog.replicache.dev/blog/replicache-11-adventures-in-text-encoding.\n //\n // TODO: We could change this since SQLite supports UTF-16. Microbenchmark\n // to see if there's a big win.\n //\n // https://www.sqlite.org/c3ref/create_collation.html\n return compareUTF8(a, b);\n }\n throw new Error(`Unsupported type: ${a}`);\n}\n\nexport type NormalizedValue = Exclude<Value, undefined>;\n\n/**\n * We allow undefined to be passed for the convenience of developers, but we\n * treat it equivalently to null. It's better for perf to not create an copy\n * of input values, so we just normalize at use when necessary.\n */\nexport function normalizeUndefined(v: Value): NormalizedValue {\n return v ?? null;\n}\n\nexport type Comparator = (r1: Row, r2: Row) => number;\n\nexport function makeComparator(\n order: Ordering,\n reverse?: boolean | undefined,\n): Comparator {\n return (a, b) => {\n // Skip destructuring here since it is hot code.\n for (const ord of order) {\n const field = ord[0];\n const comp = compareValues(a[field], b[field]);\n if (comp !== 0) {\n const result = ord[1] === 'asc' ? comp : -comp;\n return reverse ? -result : result;\n }\n }\n return 0;\n };\n}\n\n/**\n * Determine if two values are equal. Note that unlike compareValues() above,\n * this function treats `null` as unequal to itself (and same for `undefined`).\n * This is required to make joins work correctly, but may not be the right\n * semantic for your application.\n */\nexport function valuesEqual(a: Value, b: Value): boolean {\n // eslint-disable-next-line eqeqeq\n if (a == null || b == null) {\n return false;\n }\n return a === b;\n}\n\nexport function drainStreams(node: Node) {\n for (const stream of Object.values(node.relationships)) {\n for (const node of stream()) {\n drainStreams(node);\n }\n }\n}\n", "import {\n assert,\n assertArray,\n assertObject,\n assertUndefined,\n unreachable,\n} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport {drainStreams, type Comparator} from './data.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Entry, EntryList, Format} from './view.ts';\n\nexport function applyChange(\n parentEntry: Entry,\n change: Change,\n schema: SourceSchema,\n relationship: string,\n format: Format,\n) {\n if (schema.isHidden) {\n switch (change.type) {\n case 'add':\n case 'remove':\n for (const [relationship, children] of Object.entries(\n change.node.relationships,\n )) {\n const childSchema = must(schema.relationships[relationship]);\n for (const node of children()) {\n applyChange(\n parentEntry,\n {type: change.type, node},\n childSchema,\n relationship,\n format,\n );\n }\n }\n return;\n case 'edit':\n // If hidden at this level it means that the hidden row was changed. If\n // the row was changed in such a way that it would change the\n // relationships then the edit would have been split into remove and\n // add.\n return;\n case 'child': {\n const childSchema = must(\n schema.relationships[change.child.relationshipName],\n );\n applyChange(\n parentEntry,\n change.child.change,\n childSchema,\n relationship,\n format,\n );\n return;\n }\n default:\n unreachable(change);\n }\n }\n\n const {singular, relationships: childFormats} = format;\n switch (change.type) {\n case 'add': {\n // TODO: Only create a new entry if we need to mutate the existing one.\n const newEntry: Entry = {\n ...change.node.row,\n };\n if (singular) {\n assertUndefined(\n parentEntry[relationship],\n 'single output already exists',\n );\n parentEntry[relationship] = newEntry;\n } else {\n const view = getChildEntryList(parentEntry, relationship);\n const {pos, found} = binarySearch(view, newEntry, schema.compareRows);\n assert(!found, 'node already exists');\n // @ts-expect-error view is readonly\n view.splice(pos, 0, newEntry);\n }\n for (const [relationship, children] of Object.entries(\n change.node.relationships,\n )) {\n // TODO: Is there a flag to make TypeScript complain that dictionary access might be undefined?\n const childSchema = must(schema.relationships[relationship]);\n const childFormat = childFormats[relationship];\n if (childFormat === undefined) {\n continue;\n }\n\n const newView = childFormat.singular ? undefined : ([] as EntryList);\n newEntry[relationship] = newView;\n for (const node of children()) {\n applyChange(\n newEntry,\n {type: 'add', node},\n childSchema,\n relationship,\n childFormat,\n );\n }\n }\n break;\n }\n case 'remove': {\n if (singular) {\n assertObject(parentEntry[relationship]);\n parentEntry[relationship] = undefined;\n } else {\n const view = getChildEntryList(parentEntry, relationship);\n const {pos, found} = binarySearch(\n view,\n change.node.row,\n schema.compareRows,\n );\n assert(found, 'node does not exist');\n // @ts-expect-error view is readonly\n view.splice(pos, 1);\n }\n // Needed to ensure cleanup of operator state is fully done.\n drainStreams(change.node);\n break;\n }\n case 'child': {\n let existing: Entry;\n if (singular) {\n assertObject(parentEntry[relationship]);\n existing = parentEntry[relationship];\n } else {\n const view = getChildEntryList(parentEntry, relationship);\n const {pos, found} = binarySearch(\n view,\n change.node.row,\n schema.compareRows,\n );\n assert(found, 'node does not exist');\n existing = view[pos];\n }\n\n const childSchema = must(\n schema.relationships[change.child.relationshipName],\n );\n const childFormat = format.relationships[change.child.relationshipName];\n if (childFormat !== undefined) {\n applyChange(\n existing,\n change.child.change,\n childSchema,\n change.child.relationshipName,\n childFormat,\n );\n }\n break;\n }\n case 'edit': {\n if (singular) {\n assertObject(parentEntry[relationship]);\n parentEntry[relationship] = {\n ...parentEntry[relationship],\n ...change.node.row,\n };\n } else {\n const view = parentEntry[relationship] as EntryList | undefined;\n assertArray(view);\n // If the order changed due to the edit, we need to remove and reinsert.\n if (schema.compareRows(change.oldNode.row, change.node.row) === 0) {\n const {pos, found} = binarySearch(\n view,\n change.oldNode.row,\n schema.compareRows,\n );\n assert(found, 'node does not exists');\n view[pos] = makeEntryPreserveRelationships(\n change.node.row,\n view[pos],\n schema.relationships,\n );\n } else {\n // Remove\n const {pos, found} = binarySearch(\n view,\n change.oldNode.row,\n schema.compareRows,\n );\n assert(found, 'node does not exists');\n const oldEntry = view[pos];\n view.splice(pos, 1);\n\n // Insert\n {\n const {pos, found} = binarySearch(\n view,\n change.node.row,\n schema.compareRows,\n );\n assert(!found, 'node already exists');\n view.splice(\n pos,\n 0,\n makeEntryPreserveRelationships(\n change.node.row,\n oldEntry,\n schema.relationships,\n ),\n );\n }\n }\n }\n break;\n }\n default:\n unreachable(change);\n }\n}\n\n// TODO: Do not return an object. It puts unnecessary pressure on the GC.\nfunction binarySearch(view: EntryList, target: Entry, comparator: Comparator) {\n let low = 0;\n let high = view.length - 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n const comparison = comparator(view[mid] as Row, target as Row);\n if (comparison < 0) {\n low = mid + 1;\n } else if (comparison > 0) {\n high = mid - 1;\n } else {\n return {pos: mid, found: true};\n }\n }\n return {pos: low, found: false};\n}\n\nfunction makeEntryPreserveRelationships(\n row: Row,\n entry: Entry,\n relationships: {[key: string]: SourceSchema},\n): Entry {\n const result: Entry = {...row};\n for (const relationship in relationships) {\n assert(!(relationship in row), 'Relationship already exists');\n result[relationship] = entry[relationship];\n }\n return result;\n}\n\nfunction getChildEntryList(\n parentEntry: Entry,\n relationship: string,\n): EntryList {\n const view = parentEntry[relationship] as unknown;\n assertArray(view);\n return view as EntryList;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["node", "relationship", "pos", "found"]
7
+ }
@@ -10,10 +10,15 @@ import {
10
10
  assertNumber,
11
11
  assertObject,
12
12
  assertString,
13
+ compareValues,
14
+ drainStreams,
15
+ makeComparator,
13
16
  must,
17
+ normalizeUndefined,
14
18
  throwInvalidType,
15
- unreachable
16
- } from "./chunk-QAAJZ32T.js";
19
+ unreachable,
20
+ valuesEqual
21
+ } from "./chunk-ZVLXEWWB.js";
17
22
  import {
18
23
  __export,
19
24
  __reExport
@@ -8939,63 +8944,6 @@ function isTwoHop(r) {
8939
8944
  return r.length === 2;
8940
8945
  }
8941
8946
 
8942
- // ../zql/src/ivm/change.ts
8943
- function rowForChange(change) {
8944
- const { type } = change;
8945
- return type === "child" ? change.row : change.node.row;
8946
- }
8947
-
8948
- // ../zql/src/ivm/data.ts
8949
- import { compareUTF8 as compareUTF84 } from "compare-utf8";
8950
- function compareValues(a, b) {
8951
- a = normalizeUndefined(a);
8952
- b = normalizeUndefined(b);
8953
- if (a === b) {
8954
- return 0;
8955
- }
8956
- if (a === null) {
8957
- return -1;
8958
- }
8959
- if (b === null) {
8960
- return 1;
8961
- }
8962
- if (typeof a === "boolean") {
8963
- assertBoolean(b);
8964
- return a ? 1 : -1;
8965
- }
8966
- if (typeof a === "number") {
8967
- assertNumber(b);
8968
- return a - b;
8969
- }
8970
- if (typeof a === "string") {
8971
- assertString(b);
8972
- return compareUTF84(a, b);
8973
- }
8974
- throw new Error(`Unsupported type: ${a}`);
8975
- }
8976
- function normalizeUndefined(v2) {
8977
- return v2 ?? null;
8978
- }
8979
- function makeComparator(order, reverse) {
8980
- return (a, b) => {
8981
- for (const ord of order) {
8982
- const field = ord[0];
8983
- const comp = compareValues(a[field], b[field]);
8984
- if (comp !== 0) {
8985
- const result = ord[1] === "asc" ? comp : -comp;
8986
- return reverse ? -result : result;
8987
- }
8988
- }
8989
- return 0;
8990
- };
8991
- }
8992
- function valuesEqual(a, b) {
8993
- if (a == null || b == null) {
8994
- return false;
8995
- }
8996
- return a === b;
8997
- }
8998
-
8999
8947
  // ../zql/src/ivm/operator.ts
9000
8948
  var throwOutput = {
9001
8949
  push(_change) {
@@ -9056,17 +9004,19 @@ var Exists = class {
9056
9004
  }
9057
9005
  *fetch(req) {
9058
9006
  for (const node of this.#input.fetch(req)) {
9059
- if (this.#filter(node.row)) {
9007
+ if (this.#filter(node)) {
9060
9008
  yield node;
9061
9009
  }
9062
9010
  }
9063
9011
  }
9064
9012
  *cleanup(req) {
9065
9013
  for (const node of this.#input.cleanup(req)) {
9066
- if (this.#filter(node.row)) {
9014
+ if (this.#filter(node)) {
9067
9015
  yield node;
9016
+ } else {
9017
+ drainStreams(node);
9068
9018
  }
9069
- this.#delSize(node.row);
9019
+ this.#delSize(node);
9070
9020
  }
9071
9021
  }
9072
9022
  push(change) {
@@ -9079,12 +9029,12 @@ var Exists = class {
9079
9029
  return;
9080
9030
  }
9081
9031
  case "remove": {
9082
- const size = this.#getSize(change.node.row);
9032
+ const size = this.#getSize(change.node);
9083
9033
  if (size === void 0) {
9084
9034
  return;
9085
9035
  }
9086
9036
  this.#pushWithFilter(change, size);
9087
- this.#delSize(change.node.row);
9037
+ this.#delSize(change.node);
9088
9038
  return;
9089
9039
  }
9090
9040
  case "child":
@@ -9094,13 +9044,13 @@ var Exists = class {
9094
9044
  }
9095
9045
  switch (change.child.change.type) {
9096
9046
  case "add": {
9097
- let size = this.#getSize(change.row);
9047
+ let size = this.#getSize(change.node);
9098
9048
  if (size !== void 0) {
9099
9049
  size++;
9100
- this.#setCachedSize(change.row, size);
9101
- this.#setSize(change.row, size);
9050
+ this.#setCachedSize(change.node, size);
9051
+ this.#setSize(change.node, size);
9102
9052
  } else {
9103
- size = this.#fetchSize(change.row);
9053
+ size = this.#fetchSize(change.node);
9104
9054
  }
9105
9055
  if (size === 1) {
9106
9056
  const type = this.#not ? "remove" : "add";
@@ -9109,7 +9059,7 @@ var Exists = class {
9109
9059
  }
9110
9060
  this.#output.push({
9111
9061
  type,
9112
- node: this.#fetchNodeForRow(change.row)
9062
+ node: change.node
9113
9063
  });
9114
9064
  } else {
9115
9065
  this.#pushWithFilter(change, size);
@@ -9117,16 +9067,16 @@ var Exists = class {
9117
9067
  return;
9118
9068
  }
9119
9069
  case "remove": {
9120
- let size = this.#getSize(change.row);
9070
+ let size = this.#getSize(change.node);
9121
9071
  if (size !== void 0) {
9122
9072
  if (size === 0) {
9123
9073
  return;
9124
9074
  }
9125
9075
  size--;
9126
- this.#setCachedSize(change.row, size);
9127
- this.#setSize(change.row, size);
9076
+ this.#setCachedSize(change.node, size);
9077
+ this.#setSize(change.node, size);
9128
9078
  } else {
9129
- size = this.#fetchSize(change.row);
9079
+ size = this.#fetchSize(change.node);
9130
9080
  }
9131
9081
  if (size === 0) {
9132
9082
  const type = this.#not ? "add" : "remove";
@@ -9135,7 +9085,7 @@ var Exists = class {
9135
9085
  }
9136
9086
  this.#output.push({
9137
9087
  type,
9138
- node: this.#fetchNodeForRow(change.row)
9088
+ node: change.node
9139
9089
  });
9140
9090
  } else {
9141
9091
  this.#pushWithFilter(change, size);
@@ -9149,106 +9099,90 @@ var Exists = class {
9149
9099
  }
9150
9100
  }
9151
9101
  /**
9152
- * Returns whether or not the change's row's this.#relationshipName
9102
+ * Returns whether or not the node's this.#relationshipName
9153
9103
  * relationship passes the exist/not exists filter condition.
9154
9104
  * If the optional `size` is passed it is used.
9155
9105
  * Otherwise, if there is a stored size for the row it is used.
9156
- * Otherwise the size is computed by fetching a node for the row from
9157
- * this.#input (this computed size is also stored).
9106
+ * Otherwise the size is computed by streaming the node's
9107
+ * relationship with this.#relationshipName (this computed size is also
9108
+ * stored).
9158
9109
  */
9159
- #filter(row, size) {
9160
- const exists = (size ?? this.#getOrFetchSize(row)) > 0;
9110
+ #filter(node, size) {
9111
+ const exists = (size ?? this.#getOrFetchSize(node)) > 0;
9161
9112
  return this.#not ? !exists : exists;
9162
9113
  }
9163
9114
  /**
9164
9115
  * Pushes a change if this.#filter is true for its row.
9165
9116
  */
9166
9117
  #pushWithFilter(change, size) {
9167
- const row = rowForChange(change);
9168
- if (this.#filter(row, size)) {
9118
+ if (this.#filter(change.node, size)) {
9169
9119
  this.#output.push(change);
9170
9120
  }
9171
9121
  }
9172
- #getSize(row) {
9173
- return this.#storage.get(this.#makeSizeStorageKey(row));
9122
+ #getSize(node) {
9123
+ return this.#storage.get(this.#makeSizeStorageKey(node));
9174
9124
  }
9175
- #setSize(row, size) {
9176
- this.#storage.set(this.#makeSizeStorageKey(row), size);
9125
+ #setSize(node, size) {
9126
+ this.#storage.set(this.#makeSizeStorageKey(node), size);
9177
9127
  }
9178
- #setCachedSize(row, size) {
9128
+ #setCachedSize(node, size) {
9179
9129
  if (this.#skipCache) {
9180
9130
  return;
9181
9131
  }
9182
- this.#storage.set(this.#makeCacheStorageKey(row), size);
9132
+ this.#storage.set(this.#makeCacheStorageKey(node), size);
9183
9133
  }
9184
- #getCachedSize(row) {
9134
+ #getCachedSize(node) {
9185
9135
  if (this.#skipCache) {
9186
9136
  return void 0;
9187
9137
  }
9188
- return this.#storage.get(this.#makeCacheStorageKey(row));
9138
+ return this.#storage.get(this.#makeCacheStorageKey(node));
9189
9139
  }
9190
- #delSize(row) {
9191
- this.#storage.del(this.#makeSizeStorageKey(row));
9140
+ #delSize(node) {
9141
+ this.#storage.del(this.#makeSizeStorageKey(node));
9192
9142
  if (!this.#skipCache) {
9193
- const cacheKey = this.#makeCacheStorageKey(row);
9143
+ const cacheKey = this.#makeCacheStorageKey(node);
9194
9144
  if (first(this.#storage.scan({ prefix: `${cacheKey}/` })) === void 0) {
9195
9145
  this.#storage.del(cacheKey);
9196
9146
  }
9197
9147
  }
9198
9148
  }
9199
- #getOrFetchSize(row) {
9200
- const size = this.#getSize(row);
9149
+ #getOrFetchSize(node) {
9150
+ const size = this.#getSize(node);
9201
9151
  if (size !== void 0) {
9202
9152
  return size;
9203
9153
  }
9204
- return this.#fetchSize(row);
9154
+ return this.#fetchSize(node);
9205
9155
  }
9206
- #fetchSize(row) {
9207
- const cachedSize = this.#getCachedSize(row);
9156
+ #fetchSize(node) {
9157
+ const cachedSize = this.#getCachedSize(node);
9208
9158
  if (cachedSize !== void 0) {
9209
- this.#setSize(row, cachedSize);
9159
+ this.#setSize(node, cachedSize);
9210
9160
  return cachedSize;
9211
9161
  }
9212
- const relationship = this.#fetchNodeForRow(row).relationships[this.#relationshipName];
9162
+ const relationship = node.relationships[this.#relationshipName];
9213
9163
  assert(relationship);
9214
9164
  let size = 0;
9215
- for (const _relatedNode of relationship) {
9165
+ for (const _relatedNode of relationship()) {
9216
9166
  size++;
9217
9167
  }
9218
- this.#setCachedSize(row, size);
9219
- this.#setSize(row, size);
9168
+ this.#setCachedSize(node, size);
9169
+ this.#setSize(node, size);
9220
9170
  return size;
9221
9171
  }
9222
- #fetchNodeForRow(row) {
9223
- const fetched = must(
9224
- first(
9225
- this.#input.fetch({
9226
- start: { row, basis: "at" }
9227
- })
9228
- )
9229
- );
9230
- assert(
9231
- this.getSchema().compareRows(row, fetched.row) === 0,
9232
- () => `fetchNodeForRow returned unexpected row, expected ${JSON.stringify(
9233
- row
9234
- )}, received ${JSON.stringify(fetched.row)}`
9235
- );
9236
- return fetched;
9237
- }
9238
- #makeCacheStorageKey(row) {
9172
+ #makeCacheStorageKey(node) {
9239
9173
  return `row/${JSON.stringify(
9240
- this.#getKeyValues(row, this.#parentJoinKey)
9174
+ this.#getKeyValues(node, this.#parentJoinKey)
9241
9175
  )}`;
9242
9176
  }
9243
- #makeSizeStorageKey(row) {
9244
- return `row/${this.#skipCache ? "" : JSON.stringify(this.#getKeyValues(row, this.#parentJoinKey))}/${JSON.stringify(
9245
- this.#getKeyValues(row, this.#input.getSchema().primaryKey)
9177
+ #makeSizeStorageKey(node) {
9178
+ return `row/${this.#skipCache ? "" : JSON.stringify(this.#getKeyValues(node, this.#parentJoinKey))}/${JSON.stringify(
9179
+ this.#getKeyValues(node, this.#input.getSchema().primaryKey)
9246
9180
  )}`;
9247
9181
  }
9248
- #getKeyValues(row, def) {
9182
+ #getKeyValues(node, def) {
9249
9183
  const values = [];
9250
9184
  for (const key of def) {
9251
- values.push(normalizeUndefined(row[key]));
9185
+ values.push(normalizeUndefined(node.row[key]));
9252
9186
  }
9253
9187
  return values;
9254
9188
  }
@@ -9383,7 +9317,7 @@ function filterPush(change, output, predicate) {
9383
9317
  }
9384
9318
  break;
9385
9319
  case "child":
9386
- if (predicate(change.row)) {
9320
+ if (predicate(change.node.row)) {
9387
9321
  output.push(change);
9388
9322
  }
9389
9323
  break;
@@ -9414,16 +9348,19 @@ var Filter = class {
9414
9348
  getSchema() {
9415
9349
  return this.#input.getSchema();
9416
9350
  }
9417
- fetch(req) {
9418
- return this.#filter(this.#input.fetch(req));
9419
- }
9420
- cleanup(req) {
9421
- return this.#filter(this.#input.cleanup(req));
9351
+ *fetch(req) {
9352
+ for (const node of this.#input.fetch(req)) {
9353
+ if (this.#predicate(node.row)) {
9354
+ yield node;
9355
+ }
9356
+ }
9422
9357
  }
9423
- *#filter(stream) {
9424
- for (const node of stream) {
9358
+ *cleanup(req) {
9359
+ for (const node of this.#input.cleanup(req)) {
9425
9360
  if (this.#predicate(node.row)) {
9426
9361
  yield node;
9362
+ } else {
9363
+ drainStreams(node);
9427
9364
  }
9428
9365
  }
9429
9366
  }
@@ -9581,7 +9518,11 @@ var Join = class {
9581
9518
  for (const parentNode of parentNodes) {
9582
9519
  const childChange = {
9583
9520
  type: "child",
9584
- row: parentNode.row,
9521
+ node: this.#processParentNode(
9522
+ parentNode.row,
9523
+ parentNode.relationships,
9524
+ "fetch"
9525
+ ),
9585
9526
  child: {
9586
9527
  relationshipName: this.#relationshipName,
9587
9528
  change: change2
@@ -9596,7 +9537,7 @@ var Join = class {
9596
9537
  pushChildChange(change.node.row, change);
9597
9538
  break;
9598
9539
  case "child":
9599
- pushChildChange(change.row, change);
9540
+ pushChildChange(change.node.row, change);
9600
9541
  break;
9601
9542
  case "edit": {
9602
9543
  const childRow = change.node.row;
@@ -9620,35 +9561,49 @@ var Join = class {
9620
9561
  }
9621
9562
  }
9622
9563
  #processParentNode(parentNodeRow, parentNodeRelations, mode) {
9623
- const storageKey = makeStorageKey(
9624
- this.#parentKey,
9625
- this.#parent.getSchema().primaryKey,
9626
- parentNodeRow
9627
- );
9628
9564
  let method = mode;
9629
- if (mode === "cleanup") {
9630
- const [, second] = take(
9631
- this.#storage.scan({
9632
- prefix: makeStorageKeyPrefix(parentNodeRow, this.#parentKey)
9633
- }),
9634
- 2
9635
- );
9636
- method = second ? "fetch" : "cleanup";
9637
- }
9638
- const childStream = this.#child[method]({
9639
- constraint: Object.fromEntries(
9640
- this.#childKey.map((key, i) => [
9641
- key,
9642
- parentNodeRow[this.#parentKey[i]]
9643
- ])
9644
- )
9645
- });
9646
- if (mode === "fetch") {
9647
- this.#storage.set(storageKey, true);
9648
- } else {
9649
- mode;
9650
- this.#storage.del(storageKey);
9651
- }
9565
+ let storageUpdated = false;
9566
+ const childStream = () => {
9567
+ if (!storageUpdated) {
9568
+ if (mode === "cleanup") {
9569
+ this.#storage.del(
9570
+ makeStorageKey(
9571
+ this.#parentKey,
9572
+ this.#parent.getSchema().primaryKey,
9573
+ parentNodeRow
9574
+ )
9575
+ );
9576
+ const empty = [
9577
+ ...take(
9578
+ this.#storage.scan({
9579
+ prefix: makeStorageKeyPrefix(parentNodeRow, this.#parentKey)
9580
+ }),
9581
+ 1
9582
+ )
9583
+ ].length === 0;
9584
+ method = empty ? "cleanup" : "fetch";
9585
+ }
9586
+ storageUpdated = true;
9587
+ if (mode === "fetch") {
9588
+ this.#storage.set(
9589
+ makeStorageKey(
9590
+ this.#parentKey,
9591
+ this.#parent.getSchema().primaryKey,
9592
+ parentNodeRow
9593
+ ),
9594
+ true
9595
+ );
9596
+ }
9597
+ }
9598
+ return this.#child[method]({
9599
+ constraint: Object.fromEntries(
9600
+ this.#childKey.map((key, i) => [
9601
+ key,
9602
+ parentNodeRow[this.#parentKey[i]]
9603
+ ])
9604
+ )
9605
+ });
9606
+ };
9652
9607
  return {
9653
9608
  row: parentNodeRow,
9654
9609
  relationships: {
@@ -9736,8 +9691,7 @@ var Skip = class {
9736
9691
  return;
9737
9692
  }
9738
9693
  change;
9739
- const changeRow = change.type === "child" ? change.row : change.node.row;
9740
- if (shouldBePresent(changeRow)) {
9694
+ if (shouldBePresent(change.node.row)) {
9741
9695
  this.#output.push(change);
9742
9696
  }
9743
9697
  }
@@ -9917,7 +9871,7 @@ var Take = class {
9917
9871
  this.#pushEditChange(change);
9918
9872
  return;
9919
9873
  }
9920
- const { takeState, takeStateKey, maxBound, constraint } = this.#getStateAndConstraint(rowForChange(change));
9874
+ const { takeState, takeStateKey, maxBound, constraint } = this.#getStateAndConstraint(change.node.row);
9921
9875
  if (!takeState) {
9922
9876
  return;
9923
9877
  }
@@ -10042,7 +9996,7 @@ var Take = class {
10042
9996
  );
10043
9997
  this.#output.push(change);
10044
9998
  } else if (change.type === "child") {
10045
- if (takeState.bound && compareRows(change.row, takeState.bound) <= 0) {
9999
+ if (takeState.bound && compareRows(change.node.row, takeState.bound) <= 0) {
10046
10000
  this.#output.push(change);
10047
10001
  }
10048
10002
  }
@@ -12793,9 +12747,9 @@ function* generateRows(data, scanStart, reverse) {
12793
12747
  }
12794
12748
 
12795
12749
  // ../zql/src/ivm/memory-storage.ts
12796
- import { compareUTF8 as compareUTF85 } from "compare-utf8";
12750
+ import { compareUTF8 as compareUTF84 } from "compare-utf8";
12797
12751
  function comparator(a, b) {
12798
- return compareUTF85(a[0], b[0]);
12752
+ return compareUTF84(a[0], b[0]);
12799
12753
  }
12800
12754
  var MemoryStorage = class {
12801
12755
  #data = new BTreeSet(comparator);
@@ -13418,7 +13372,7 @@ function makeMessage(message, context, logLevel) {
13418
13372
  }
13419
13373
 
13420
13374
  // ../zero-client/src/client/version.ts
13421
- var version2 = "0.12.2025012902";
13375
+ var version2 = "0.12.2025013001";
13422
13376
 
13423
13377
  // ../zero-client/src/client/log-options.ts
13424
13378
  var LevelFilterLogSink = class {
@@ -15485,4 +15439,4 @@ export {
15485
15439
  escapeLike,
15486
15440
  Zero
15487
15441
  };
15488
- //# sourceMappingURL=chunk-HQA4Z25K.js.map
15442
+ //# sourceMappingURL=chunk-ZXVUJVVO.js.map