@rocicorp/zero 1.0.1-canary.0 → 1.1.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.
Files changed (59) hide show
  1. package/out/_virtual/{_@oxc-project_runtime@0.115.0 → _@oxc-project_runtime@0.122.0}/helpers/usingCtx.js +1 -1
  2. package/out/replicache/src/mutation-recovery.js +0 -3
  3. package/out/zero/package.js +7 -6
  4. package/out/zero/package.js.map +1 -1
  5. package/out/zero-cache/src/services/analyze.js +1 -1
  6. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  7. package/out/zero-cache/src/services/change-source/pg/change-source.js +37 -16
  8. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  9. package/out/zero-cache/src/services/change-source/pg/lsn.js +1 -1
  10. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  11. package/out/zero-cache/src/services/life-cycle.js +6 -2
  12. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  13. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
  14. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  15. package/out/zero-cache/src/workers/replicator.js +1 -0
  16. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  17. package/out/zero-client/src/client/version.js +1 -1
  18. package/out/zql/src/builder/builder.d.ts.map +1 -1
  19. package/out/zql/src/builder/builder.js +15 -5
  20. package/out/zql/src/builder/builder.js.map +1 -1
  21. package/out/zql/src/ivm/cap.d.ts +32 -0
  22. package/out/zql/src/ivm/cap.d.ts.map +1 -0
  23. package/out/zql/src/ivm/cap.js +226 -0
  24. package/out/zql/src/ivm/cap.js.map +1 -0
  25. package/out/zql/src/ivm/join-utils.d.ts +2 -0
  26. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  27. package/out/zql/src/ivm/join-utils.js +35 -1
  28. package/out/zql/src/ivm/join-utils.js.map +1 -1
  29. package/out/zql/src/ivm/join.d.ts.map +1 -1
  30. package/out/zql/src/ivm/join.js +6 -2
  31. package/out/zql/src/ivm/join.js.map +1 -1
  32. package/out/zql/src/ivm/memory-source.d.ts +15 -2
  33. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  34. package/out/zql/src/ivm/memory-source.js +69 -8
  35. package/out/zql/src/ivm/memory-source.js.map +1 -1
  36. package/out/zql/src/ivm/schema.d.ts +1 -1
  37. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  38. package/out/zql/src/ivm/skip.d.ts.map +1 -1
  39. package/out/zql/src/ivm/skip.js +3 -0
  40. package/out/zql/src/ivm/skip.js.map +1 -1
  41. package/out/zql/src/ivm/source.d.ts +1 -1
  42. package/out/zql/src/ivm/source.d.ts.map +1 -1
  43. package/out/zql/src/ivm/take.d.ts +4 -1
  44. package/out/zql/src/ivm/take.d.ts.map +1 -1
  45. package/out/zql/src/ivm/take.js +4 -2
  46. package/out/zql/src/ivm/take.js.map +1 -1
  47. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  48. package/out/zql/src/ivm/union-fan-in.js +1 -0
  49. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  50. package/out/zqlite/src/query-builder.d.ts +1 -1
  51. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  52. package/out/zqlite/src/query-builder.js +7 -2
  53. package/out/zqlite/src/query-builder.js.map +1 -1
  54. package/out/zqlite/src/table-source.d.ts +1 -1
  55. package/out/zqlite/src/table-source.d.ts.map +1 -1
  56. package/out/zqlite/src/table-source.js +15 -10
  57. package/out/zqlite/src/table-source.js.map +1 -1
  58. package/package.json +7 -6
  59. package/out/replicache/src/mutation-recovery.js.map +0 -1
@@ -0,0 +1,226 @@
1
+ import { assert } from "../../../shared/src/asserts.js";
2
+ import { throwOutput } from "./operator.js";
3
+ import { constraintMatchesPartitionKey, makePartitionKeyComparator } from "./take.js";
4
+ //#region ../zql/src/ivm/cap.ts
5
+ /**
6
+ * The Cap operator is a count-based limiter for EXISTS subqueries that
7
+ * does not require ordering. Unlike Take, it tracks membership by primary
8
+ * key set rather than by a sorted bound. This means:
9
+ *
10
+ * - No comparator needed (no ordering requirement)
11
+ * - No `start` or `reverse` fetch support
12
+ * - No `#rowHiddenFromFetch` complexity (we can defer when adding to the pk set)
13
+ *
14
+ * Cap is used in EXISTS child pipelines where only the count of matching
15
+ * rows matters, not their order. This allows SQLite to skip ORDER BY
16
+ * entirely, enabling much faster query plans.
17
+ *
18
+ * Cap can count rows globally or by unique value of some partition key
19
+ * (same as Take).
20
+ */
21
+ var Cap = class {
22
+ #input;
23
+ #storage;
24
+ #limit;
25
+ #partitionKey;
26
+ #partitionKeyComparator;
27
+ #primaryKey;
28
+ #output = throwOutput;
29
+ constructor(input, storage, limit, partitionKey) {
30
+ assert(limit >= 0, "Limit must be non-negative");
31
+ input.setOutput(this);
32
+ this.#input = input;
33
+ this.#storage = storage;
34
+ this.#limit = limit;
35
+ this.#partitionKey = partitionKey;
36
+ this.#partitionKeyComparator = partitionKey && makePartitionKeyComparator(partitionKey);
37
+ this.#primaryKey = input.getSchema().primaryKey;
38
+ }
39
+ setOutput(output) {
40
+ this.#output = output;
41
+ }
42
+ getSchema() {
43
+ return this.#input.getSchema();
44
+ }
45
+ *fetch(req) {
46
+ assert(!req.start, "Cap does not support start");
47
+ assert(!req.reverse, "Cap does not support reverse");
48
+ if (!this.#partitionKey || req.constraint && constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {
49
+ const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);
50
+ const capState = this.#storage.get(capStateKey);
51
+ if (!capState) {
52
+ yield* this.#initialFetch(req);
53
+ return;
54
+ }
55
+ if (capState.size === 0) return;
56
+ for (const pk of capState.pks) {
57
+ const constraint = deserializePKToConstraint(pk, this.#primaryKey);
58
+ for (const inputNode of this.#input.fetch({ constraint })) {
59
+ if (inputNode === "yield") {
60
+ yield inputNode;
61
+ continue;
62
+ }
63
+ yield inputNode;
64
+ }
65
+ }
66
+ return;
67
+ }
68
+ const pkSetCache = /* @__PURE__ */ new Map();
69
+ for (const inputNode of this.#input.fetch(req)) {
70
+ if (inputNode === "yield") {
71
+ yield inputNode;
72
+ continue;
73
+ }
74
+ const capStateKey = getCapStateKey(this.#partitionKey, inputNode.row);
75
+ if (!pkSetCache.has(capStateKey)) {
76
+ const capState = this.#storage.get(capStateKey);
77
+ pkSetCache.set(capStateKey, capState && capState.size > 0 ? new Set(capState.pks) : null);
78
+ }
79
+ const pkSet = pkSetCache.get(capStateKey);
80
+ if (pkSet) {
81
+ const pk = serializePK(inputNode.row, this.#primaryKey);
82
+ if (pkSet.has(pk)) yield inputNode;
83
+ }
84
+ }
85
+ }
86
+ *#initialFetch(req) {
87
+ assert(constraintMatchesPartitionKey(req.constraint, this.#partitionKey), "Constraint should match partition key");
88
+ if (this.#limit === 0) return;
89
+ const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);
90
+ assert(this.#storage.get(capStateKey) === void 0, "Cap state should be undefined");
91
+ let size = 0;
92
+ const pks = [];
93
+ let downstreamEarlyReturn = true;
94
+ let exceptionThrown = false;
95
+ try {
96
+ for (const inputNode of this.#input.fetch(req)) {
97
+ if (inputNode === "yield") {
98
+ yield "yield";
99
+ continue;
100
+ }
101
+ yield inputNode;
102
+ pks.push(serializePK(inputNode.row, this.#primaryKey));
103
+ size++;
104
+ if (size === this.#limit) break;
105
+ }
106
+ downstreamEarlyReturn = false;
107
+ } catch (e) {
108
+ exceptionThrown = true;
109
+ throw e;
110
+ } finally {
111
+ if (!exceptionThrown) {
112
+ this.#storage.set(capStateKey, {
113
+ size,
114
+ pks
115
+ });
116
+ assert(!downstreamEarlyReturn, "Unexpected early return prevented full hydration");
117
+ }
118
+ }
119
+ }
120
+ *push(change) {
121
+ if (change.type === "edit") {
122
+ yield* this.#pushEditChange(change);
123
+ return;
124
+ }
125
+ const capStateKey = getCapStateKey(this.#partitionKey, change.node.row);
126
+ const capState = this.#storage.get(capStateKey);
127
+ if (!capState) return;
128
+ const pk = serializePK(change.node.row, this.#primaryKey);
129
+ if (change.type === "add") {
130
+ if (capState.size < this.#limit) {
131
+ const pks = [...capState.pks, pk];
132
+ this.#storage.set(capStateKey, {
133
+ size: capState.size + 1,
134
+ pks
135
+ });
136
+ yield* this.#output.push(change, this);
137
+ return;
138
+ }
139
+ return;
140
+ } else if (change.type === "remove") {
141
+ const pkIndex = capState.pks.indexOf(pk);
142
+ if (pkIndex === -1) return;
143
+ const pks = [...capState.pks];
144
+ pks.splice(pkIndex, 1);
145
+ const newSize = capState.size - 1;
146
+ const pkSet = new Set(pks);
147
+ const constraint = this.#partitionKey ? Object.fromEntries(this.#partitionKey.map((key) => [key, change.node.row[key]])) : void 0;
148
+ let replacement;
149
+ for (const node of this.#input.fetch({ constraint })) {
150
+ if (node === "yield") {
151
+ yield node;
152
+ continue;
153
+ }
154
+ const nodePK = serializePK(node.row, this.#primaryKey);
155
+ if (!pkSet.has(nodePK)) {
156
+ replacement = node;
157
+ break;
158
+ }
159
+ }
160
+ if (replacement) {
161
+ this.#storage.set(capStateKey, {
162
+ size: newSize,
163
+ pks
164
+ });
165
+ yield* this.#output.push(change, this);
166
+ const replacementPK = serializePK(replacement.row, this.#primaryKey);
167
+ pks.push(replacementPK);
168
+ this.#storage.set(capStateKey, {
169
+ size: newSize + 1,
170
+ pks
171
+ });
172
+ yield* this.#output.push({
173
+ type: "add",
174
+ node: replacement
175
+ }, this);
176
+ } else {
177
+ this.#storage.set(capStateKey, {
178
+ size: newSize,
179
+ pks
180
+ });
181
+ yield* this.#output.push(change, this);
182
+ }
183
+ } else if (change.type === "child") {
184
+ if (new Set(capState.pks).has(pk)) yield* this.#output.push(change, this);
185
+ }
186
+ }
187
+ *#pushEditChange(change) {
188
+ assert(!this.#partitionKeyComparator || this.#partitionKeyComparator(change.oldNode.row, change.node.row) === 0, "Unexpected change of partition key");
189
+ const capStateKey = getCapStateKey(this.#partitionKey, change.oldNode.row);
190
+ const capState = this.#storage.get(capStateKey);
191
+ if (!capState) return;
192
+ const oldPK = serializePK(change.oldNode.row, this.#primaryKey);
193
+ if (new Set(capState.pks).has(oldPK)) {
194
+ const newPK = serializePK(change.node.row, this.#primaryKey);
195
+ if (oldPK !== newPK) {
196
+ const pks = capState.pks.map((p) => p === oldPK ? newPK : p);
197
+ this.#storage.set(capStateKey, {
198
+ size: capState.size,
199
+ pks
200
+ });
201
+ }
202
+ yield* this.#output.push(change, this);
203
+ }
204
+ }
205
+ destroy() {
206
+ this.#input.destroy();
207
+ }
208
+ };
209
+ function getCapStateKey(partitionKey, rowOrConstraint) {
210
+ const partitionValues = [];
211
+ if (partitionKey && rowOrConstraint) for (const key of partitionKey) partitionValues.push(rowOrConstraint[key]);
212
+ return JSON.stringify(["cap", ...partitionValues]);
213
+ }
214
+ function serializePK(row, primaryKey) {
215
+ return JSON.stringify(primaryKey.map((k) => row[k]));
216
+ }
217
+ function deserializePKToConstraint(pk, primaryKey) {
218
+ const values = JSON.parse(pk);
219
+ const constraint = {};
220
+ for (let i = 0; i < primaryKey.length; i++) constraint[primaryKey[i]] = values[i];
221
+ return constraint;
222
+ }
223
+ //#endregion
224
+ export { Cap };
225
+
226
+ //# sourceMappingURL=cap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cap.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#primaryKey","#output","#initialFetch","#pushEditChange"],"sources":["../../../../../zql/src/ivm/cap.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {type Change, type EditChange} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\nimport {\n constraintMatchesPartitionKey,\n makePartitionKeyComparator,\n type PartitionKey,\n} from './take.ts';\n\ntype CapState = {\n size: number;\n pks: string[];\n};\n\ninterface CapStorage {\n get(key: string): CapState | undefined;\n set(key: string, value: CapState): void;\n del(key: string): void;\n}\n\n/**\n * The Cap operator is a count-based limiter for EXISTS subqueries that\n * does not require ordering. Unlike Take, it tracks membership by primary\n * key set rather than by a sorted bound. This means:\n *\n * - No comparator needed (no ordering requirement)\n * - No `start` or `reverse` fetch support\n * - No `#rowHiddenFromFetch` complexity (we can defer when adding to the pk set)\n *\n * Cap is used in EXISTS child pipelines where only the count of matching\n * rows matters, not their order. This allows SQLite to skip ORDER BY\n * entirely, enabling much faster query plans.\n *\n * Cap can count rows globally or by unique value of some partition key\n * (same as Take).\n */\nexport class Cap implements Operator {\n readonly #input: Input;\n readonly #storage: CapStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n readonly #primaryKey: PrimaryKey;\n\n #output: Output = throwOutput;\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as CapStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n this.#primaryKey = input.getSchema().primaryKey;\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(!req.start, 'Cap does not support start');\n assert(!req.reverse, 'Cap does not support reverse');\n\n if (\n !this.#partitionKey ||\n (req.constraint &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey))\n ) {\n const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n yield* this.#initialFetch(req);\n return;\n }\n if (capState.size === 0) {\n return;\n }\n // PK-based point lookups: fetch each tracked row by its PK directly,\n // rather than scanning the partition and filtering.\n for (const pk of capState.pks) {\n const constraint = deserializePKToConstraint(pk, this.#primaryKey);\n for (const inputNode of this.#input.fetch({constraint})) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n yield inputNode;\n }\n }\n return;\n }\n // There is a partition key, but the fetch is not constrained or constrained\n // on a different key. This currently only happens with nested sub-queries.\n const pkSetCache = new Map<string, Set<string> | null>();\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n const capStateKey = getCapStateKey(this.#partitionKey, inputNode.row);\n if (!pkSetCache.has(capStateKey)) {\n const capState = this.#storage.get(capStateKey);\n pkSetCache.set(\n capStateKey,\n capState && capState.size > 0 ? new Set(capState.pks) : null,\n );\n }\n const pkSet = pkSetCache.get(capStateKey);\n if (pkSet) {\n const pk = serializePK(inputNode.row, this.#primaryKey);\n if (pkSet.has(pk)) {\n yield inputNode;\n }\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n if (this.#limit === 0) {\n return;\n }\n\n const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(capStateKey) === undefined,\n 'Cap state should be undefined',\n );\n\n let size = 0;\n const pks: string[] = [];\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n pks.push(serializePK(inputNode.row, this.#primaryKey));\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#storage.set(capStateKey, {size, pks});\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that capState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n *push(change: Change): Stream<'yield'> {\n if (change.type === 'edit') {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const capStateKey = getCapStateKey(this.#partitionKey, change.node.row);\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return;\n }\n\n const pk = serializePK(change.node.row, this.#primaryKey);\n\n if (change.type === 'add') {\n if (capState.size < this.#limit) {\n const pks = [...capState.pks, pk];\n this.#storage.set(capStateKey, {size: capState.size + 1, pks});\n yield* this.#output.push(change, this);\n return;\n }\n // Full — drop\n return;\n } else if (change.type === 'remove') {\n const pkIndex = capState.pks.indexOf(pk);\n if (pkIndex === -1) {\n // Not in our set — drop\n return;\n }\n // Remove from set\n const pks = [...capState.pks];\n pks.splice(pkIndex, 1);\n const newSize = capState.size - 1;\n\n // Try to refill: fetch from input with partition constraint,\n // find first row NOT in PK set\n const pkSet = new Set(pks);\n const constraint = this.#partitionKey\n ? (Object.fromEntries(\n this.#partitionKey.map(key => [key, change.node.row[key]] as const),\n ) as Constraint)\n : undefined;\n\n let replacement: Node | undefined;\n for (const node of this.#input.fetch({constraint})) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const nodePK = serializePK(node.row, this.#primaryKey);\n if (!pkSet.has(nodePK)) {\n replacement = node;\n break;\n }\n }\n\n if (replacement) {\n // Store state WITHOUT replacement during remove forward,\n // matching Take's pattern of hiding in-flight changes from re-fetches.\n this.#storage.set(capStateKey, {size: newSize, pks});\n yield* this.#output.push(change, this);\n // Now add replacement to set and forward the add.\n const replacementPK = serializePK(replacement.row, this.#primaryKey);\n pks.push(replacementPK);\n this.#storage.set(capStateKey, {size: newSize + 1, pks});\n yield* this.#output.push({type: 'add', node: replacement}, this);\n } else {\n this.#storage.set(capStateKey, {size: newSize, pks});\n yield* this.#output.push(change, this);\n }\n } else if (change.type === 'child') {\n const pkSet = new Set(capState.pks);\n if (pkSet.has(pk)) {\n yield* this.#output.push(change, this);\n }\n }\n }\n\n *#pushEditChange(change: EditChange): Stream<'yield'> {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(change.oldNode.row, change.node.row) === 0,\n 'Unexpected change of partition key',\n );\n const capStateKey = getCapStateKey(this.#partitionKey, change.oldNode.row);\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return;\n }\n\n const oldPK = serializePK(change.oldNode.row, this.#primaryKey);\n const pkSet = new Set(capState.pks);\n if (pkSet.has(oldPK)) {\n // Update the PK in our set if it changed\n const newPK = serializePK(change.node.row, this.#primaryKey);\n if (oldPK !== newPK) {\n const pks = capState.pks.map(p => (p === oldPK ? newPK : p));\n this.#storage.set(capStateKey, {size: capState.size, pks});\n }\n yield* this.#output.push(change, this);\n }\n // If not in our set, drop\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getCapStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['cap', ...partitionValues]);\n}\n\nfunction serializePK(row: Row, primaryKey: PrimaryKey): string {\n return JSON.stringify(primaryKey.map(k => row[k]));\n}\n\nfunction deserializePKToConstraint(\n pk: string,\n primaryKey: PrimaryKey,\n): Constraint {\n const values = JSON.parse(pk) as Value[];\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < primaryKey.length; i++) {\n constraint[primaryKey[i]] = values[i];\n }\n return constraint;\n}\n"],"mappings":"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"}
@@ -10,6 +10,8 @@ export type JoinChangeOverlay = {
10
10
  };
11
11
  export declare function generateWithOverlayNoYield(stream: Stream<Node>, overlay: Change, schema: SourceSchema): Stream<Node>;
12
12
  export declare function generateWithOverlay(stream: Stream<Node | 'yield'>, overlay: Change, schema: SourceSchema): Stream<Node | 'yield'>;
13
+ export declare function generateWithOverlayNoYieldUnordered(stream: Stream<Node>, overlay: Change, schema: SourceSchema): Stream<Node>;
14
+ export declare function generateWithOverlayUnordered(stream: Stream<Node | 'yield'>, overlay: Change, schema: SourceSchema): Stream<Node | 'yield'>;
13
15
  export declare function rowEqualsForCompoundKey(a: Row, b: Row, key: CompoundKey): boolean;
14
16
  export declare function isJoinMatch(parent: Row, parentKey: CompoundKey, child: Row, childKey: CompoundKey): boolean;
15
17
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"join-utils.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/join-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAE,KAAK,EAAC,MAAM,oCAAoC,CAAC;AACnE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAA6B,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAEhE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,mCAAmC,CAAC;AAEnE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,IAAI,CAAC,CAEd;AAED,wBAAiB,mBAAmB,CAClC,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,EAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CA6FxB;AAED,wBAAgB,uBAAuB,CACrC,CAAC,EAAE,GAAG,EACN,CAAC,EAAE,GAAG,EACN,GAAG,EAAE,WAAW,GACf,OAAO,CAOT;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,GAAG,EACX,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,WAAW,WAQtB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,GAAG,EACd,SAAS,EAAE,WAAW,EACtB,SAAS,EAAE,WAAW,GACrB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,SAAS,CAUnC"}
1
+ {"version":3,"file":"join-utils.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/join-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAE,KAAK,EAAC,MAAM,oCAAoC,CAAC;AACnE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAA6B,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAEhE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,mCAAmC,CAAC;AAEnE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,IAAI,CAAC,CAEd;AAED,wBAAiB,mBAAmB,CAClC,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,EAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CA6FxB;AAED,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,IAAI,CAAC,CAEd;AAED,wBAAiB,4BAA4B,CAC3C,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,EAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CAmDxB;AAED,wBAAgB,uBAAuB,CACrC,CAAC,EAAE,GAAG,EACN,CAAC,EAAE,GAAG,EACN,GAAG,EAAE,WAAW,GACf,OAAO,CAOT;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,GAAG,EACX,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,WAAW,WAQtB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,GAAG,EACd,SAAS,EAAE,WAAW,EACtB,SAAS,EAAE,WAAW,GACrB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,SAAS,CAUnC"}
@@ -68,6 +68,40 @@ function* generateWithOverlay(stream, overlay, schema) {
68
68
  }
69
69
  assert(applied, "overlayGenerator: overlay was never applied to any fetched node");
70
70
  }
71
+ function* generateWithOverlayUnordered(stream, overlay, schema) {
72
+ if (overlay.type === "remove") yield overlay.node;
73
+ else if (overlay.type === "edit") yield overlay.oldNode;
74
+ let suppressed = false;
75
+ for (const node of stream) {
76
+ if (node === "yield") {
77
+ yield node;
78
+ continue;
79
+ }
80
+ if (!suppressed) {
81
+ if (overlay.type === "add" || overlay.type === "edit") {
82
+ if (rowEqualsForCompoundKey(overlay.node.row, node.row, schema.primaryKey)) {
83
+ suppressed = true;
84
+ continue;
85
+ }
86
+ }
87
+ if (overlay.type === "child") {
88
+ if (rowEqualsForCompoundKey(overlay.node.row, node.row, schema.primaryKey)) {
89
+ suppressed = true;
90
+ yield {
91
+ row: node.row,
92
+ relationships: {
93
+ ...node.relationships,
94
+ [overlay.child.relationshipName]: () => generateWithOverlay(node.relationships[overlay.child.relationshipName](), overlay.child.change, schema.relationships[overlay.child.relationshipName])
95
+ }
96
+ };
97
+ continue;
98
+ }
99
+ }
100
+ }
101
+ yield node;
102
+ }
103
+ assert(suppressed || overlay.type === "remove", "overlayGenerator: overlay was never applied to any fetched node");
104
+ }
71
105
  function rowEqualsForCompoundKey(a, b, key) {
72
106
  for (let i = 0; i < key.length; i++) if (compareValues(a[key[i]], b[key[i]]) !== 0) return false;
73
107
  return true;
@@ -91,6 +125,6 @@ function buildJoinConstraint(sourceRow, sourceKey, targetKey) {
91
125
  return constraint;
92
126
  }
93
127
  //#endregion
94
- export { buildJoinConstraint, generateWithOverlay, generateWithOverlayNoYield, isJoinMatch, rowEqualsForCompoundKey };
128
+ export { buildJoinConstraint, generateWithOverlay, generateWithOverlayNoYield, generateWithOverlayUnordered, isJoinMatch, rowEqualsForCompoundKey };
95
129
 
96
130
  //# sourceMappingURL=join-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"join-utils.js","names":[],"sources":["../../../../../zql/src/ivm/join-utils.ts"],"sourcesContent":["import type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport {compareValues, valuesEqual, type Node} from './data.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\n\nexport type JoinChangeOverlay = {\n change: Change;\n position: Row | undefined;\n};\n\nexport function generateWithOverlayNoYield(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlay(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlay(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n let applied = false;\n let editOldApplied = false;\n let editNewApplied = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n let yieldNode = true;\n if (!applied) {\n switch (overlay.type) {\n case 'add': {\n if (schema.compareRows(overlay.node.row, node.row) === 0) {\n applied = true;\n yieldNode = false;\n }\n break;\n }\n case 'remove': {\n if (schema.compareRows(overlay.node.row, node.row) < 0) {\n applied = true;\n yield overlay.node;\n }\n break;\n }\n case 'edit': {\n if (\n !editOldApplied &&\n schema.compareRows(overlay.oldNode.row, node.row) < 0\n ) {\n editOldApplied = true;\n if (editNewApplied) {\n applied = true;\n }\n yield overlay.oldNode;\n }\n if (\n !editNewApplied &&\n schema.compareRows(overlay.node.row, node.row) === 0\n ) {\n editNewApplied = true;\n if (editOldApplied) {\n applied = true;\n }\n yieldNode = false;\n }\n break;\n }\n case 'child': {\n if (schema.compareRows(overlay.node.row, node.row) === 0) {\n applied = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay.child.relationshipName]: () =>\n generateWithOverlay(\n node.relationships[overlay.child.relationshipName](),\n overlay.child.change,\n schema.relationships[overlay.child.relationshipName],\n ),\n },\n };\n yieldNode = false;\n }\n break;\n }\n }\n }\n if (yieldNode) {\n yield node;\n }\n }\n if (!applied) {\n if (overlay.type === 'remove') {\n applied = true;\n yield overlay.node;\n } else if (overlay.type === 'edit') {\n assert(\n editNewApplied,\n 'edit overlay: new node must be applied before old node',\n );\n editOldApplied = true;\n applied = true;\n yield overlay.oldNode;\n }\n }\n\n assert(\n applied,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function rowEqualsForCompoundKey(\n a: Row,\n b: Row,\n key: CompoundKey,\n): boolean {\n for (let i = 0; i < key.length; i++) {\n if (compareValues(a[key[i]], b[key[i]]) !== 0) {\n return false;\n }\n }\n return true;\n}\n\nexport function isJoinMatch(\n parent: Row,\n parentKey: CompoundKey,\n child: Row,\n childKey: CompoundKey,\n) {\n for (let i = 0; i < parentKey.length; i++) {\n if (!valuesEqual(parent[parentKey[i]], child[childKey[i]])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a constraint object by mapping values from `sourceRow` using `sourceKey`\n * to keys specified by `targetKey`. Returns `undefined` if any source value is `null`,\n * since null foreign keys cannot match any rows.\n */\nexport function buildJoinConstraint(\n sourceRow: Row,\n sourceKey: CompoundKey,\n targetKey: CompoundKey,\n): Record<string, Value> | undefined {\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < targetKey.length; i++) {\n const value = sourceRow[sourceKey[i]];\n if (value === null) {\n return undefined;\n }\n constraint[targetKey[i]] = value;\n }\n return constraint;\n}\n"],"mappings":";;;AAaA,SAAgB,2BACd,QACA,SACA,QACc;AACd,QAAO,oBAAoB,QAAQ,SAAS,OAAO;;AAGrD,UAAiB,oBACf,QACA,SACA,QACwB;CACxB,IAAI,UAAU;CACd,IAAI,iBAAiB;CACrB,IAAI,iBAAiB;AACrB,MAAK,MAAM,QAAQ,QAAQ;AACzB,MAAI,SAAS,SAAS;AACpB,SAAM;AACN;;EAEF,IAAI,YAAY;AAChB,MAAI,CAAC,QACH,SAAQ,QAAQ,MAAhB;GACE,KAAK;AACH,QAAI,OAAO,YAAY,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,GAAG;AACxD,eAAU;AACV,iBAAY;;AAEd;GAEF,KAAK;AACH,QAAI,OAAO,YAAY,QAAQ,KAAK,KAAK,KAAK,IAAI,GAAG,GAAG;AACtD,eAAU;AACV,WAAM,QAAQ;;AAEhB;GAEF,KAAK;AACH,QACE,CAAC,kBACD,OAAO,YAAY,QAAQ,QAAQ,KAAK,KAAK,IAAI,GAAG,GACpD;AACA,sBAAiB;AACjB,SAAI,eACF,WAAU;AAEZ,WAAM,QAAQ;;AAEhB,QACE,CAAC,kBACD,OAAO,YAAY,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,GACnD;AACA,sBAAiB;AACjB,SAAI,eACF,WAAU;AAEZ,iBAAY;;AAEd;GAEF,KAAK;AACH,QAAI,OAAO,YAAY,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,GAAG;AACxD,eAAU;AACV,WAAM;MACJ,KAAK,KAAK;MACV,eAAe;OACb,GAAG,KAAK;QACP,QAAQ,MAAM,yBACb,oBACE,KAAK,cAAc,QAAQ,MAAM,mBAAmB,EACpD,QAAQ,MAAM,QACd,OAAO,cAAc,QAAQ,MAAM,kBACpC;OACJ;MACF;AACD,iBAAY;;AAEd;;AAIN,MAAI,UACF,OAAM;;AAGV,KAAI,CAAC;MACC,QAAQ,SAAS,UAAU;AAC7B,aAAU;AACV,SAAM,QAAQ;aACL,QAAQ,SAAS,QAAQ;AAClC,UACE,gBACA,yDACD;AACD,oBAAiB;AACjB,aAAU;AACV,SAAM,QAAQ;;;AAIlB,QACE,SACA,kEACD;;AAGH,SAAgB,wBACd,GACA,GACA,KACS;AACT,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,IAC9B,KAAI,cAAc,EAAE,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,EAC1C,QAAO;AAGX,QAAO;;AAGT,SAAgB,YACd,QACA,WACA,OACA,UACA;AACA,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACpC,KAAI,CAAC,YAAY,OAAO,UAAU,KAAK,MAAM,SAAS,IAAI,CACxD,QAAO;AAGX,QAAO;;;;;;;AAQT,SAAgB,oBACd,WACA,WACA,WACmC;CACnC,MAAM,aAAoC,EAAE;AAC5C,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;EACzC,MAAM,QAAQ,UAAU,UAAU;AAClC,MAAI,UAAU,KACZ;AAEF,aAAW,UAAU,MAAM;;AAE7B,QAAO"}
1
+ {"version":3,"file":"join-utils.js","names":[],"sources":["../../../../../zql/src/ivm/join-utils.ts"],"sourcesContent":["import type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport {compareValues, valuesEqual, type Node} from './data.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\n\nexport type JoinChangeOverlay = {\n change: Change;\n position: Row | undefined;\n};\n\nexport function generateWithOverlayNoYield(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlay(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlay(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n let applied = false;\n let editOldApplied = false;\n let editNewApplied = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n let yieldNode = true;\n if (!applied) {\n switch (overlay.type) {\n case 'add': {\n if (schema.compareRows(overlay.node.row, node.row) === 0) {\n applied = true;\n yieldNode = false;\n }\n break;\n }\n case 'remove': {\n if (schema.compareRows(overlay.node.row, node.row) < 0) {\n applied = true;\n yield overlay.node;\n }\n break;\n }\n case 'edit': {\n if (\n !editOldApplied &&\n schema.compareRows(overlay.oldNode.row, node.row) < 0\n ) {\n editOldApplied = true;\n if (editNewApplied) {\n applied = true;\n }\n yield overlay.oldNode;\n }\n if (\n !editNewApplied &&\n schema.compareRows(overlay.node.row, node.row) === 0\n ) {\n editNewApplied = true;\n if (editOldApplied) {\n applied = true;\n }\n yieldNode = false;\n }\n break;\n }\n case 'child': {\n if (schema.compareRows(overlay.node.row, node.row) === 0) {\n applied = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay.child.relationshipName]: () =>\n generateWithOverlay(\n node.relationships[overlay.child.relationshipName](),\n overlay.child.change,\n schema.relationships[overlay.child.relationshipName],\n ),\n },\n };\n yieldNode = false;\n }\n break;\n }\n }\n }\n if (yieldNode) {\n yield node;\n }\n }\n if (!applied) {\n if (overlay.type === 'remove') {\n applied = true;\n yield overlay.node;\n } else if (overlay.type === 'edit') {\n assert(\n editNewApplied,\n 'edit overlay: new node must be applied before old node',\n );\n editOldApplied = true;\n applied = true;\n yield overlay.oldNode;\n }\n }\n\n assert(\n applied,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function generateWithOverlayNoYieldUnordered(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlayUnordered(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlayUnordered(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n // Eager inject\n if (overlay.type === 'remove') {\n yield overlay.node;\n } else if (overlay.type === 'edit') {\n yield overlay.oldNode;\n }\n\n // Stream with inline suppress\n let suppressed = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!suppressed) {\n if (overlay.type === 'add' || overlay.type === 'edit') {\n if (\n rowEqualsForCompoundKey(overlay.node.row, node.row, schema.primaryKey)\n ) {\n suppressed = true;\n continue;\n }\n }\n if (overlay.type === 'child') {\n if (\n rowEqualsForCompoundKey(overlay.node.row, node.row, schema.primaryKey)\n ) {\n suppressed = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay.child.relationshipName]: () =>\n generateWithOverlay(\n node.relationships[overlay.child.relationshipName](),\n overlay.child.change,\n schema.relationships[overlay.child.relationshipName],\n ),\n },\n };\n continue;\n }\n }\n }\n yield node;\n }\n assert(\n suppressed || overlay.type === 'remove',\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function rowEqualsForCompoundKey(\n a: Row,\n b: Row,\n key: CompoundKey,\n): boolean {\n for (let i = 0; i < key.length; i++) {\n if (compareValues(a[key[i]], b[key[i]]) !== 0) {\n return false;\n }\n }\n return true;\n}\n\nexport function isJoinMatch(\n parent: Row,\n parentKey: CompoundKey,\n child: Row,\n childKey: CompoundKey,\n) {\n for (let i = 0; i < parentKey.length; i++) {\n if (!valuesEqual(parent[parentKey[i]], child[childKey[i]])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a constraint object by mapping values from `sourceRow` using `sourceKey`\n * to keys specified by `targetKey`. Returns `undefined` if any source value is `null`,\n * since null foreign keys cannot match any rows.\n */\nexport function buildJoinConstraint(\n sourceRow: Row,\n sourceKey: CompoundKey,\n targetKey: CompoundKey,\n): Record<string, Value> | undefined {\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < targetKey.length; i++) {\n const value = sourceRow[sourceKey[i]];\n if (value === null) {\n return undefined;\n }\n constraint[targetKey[i]] = value;\n }\n return constraint;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"join.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/join.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,WAAW,EAAE,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAG3E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAQpC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IAEb,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,WAAW,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,IAAK,YAAW,KAAK;;gBAYpB,EACV,MAAM,EACN,KAAK,EACL,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,MAAM,GACP,EAAE,IAAI;IAkCP,OAAO,IAAI,IAAI;IAKf,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;CA0LlD"}
1
+ {"version":3,"file":"join.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/join.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,WAAW,EAAE,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAG3E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AASpC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IAEb,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,WAAW,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,IAAK,YAAW,KAAK;;gBAYpB,EACV,MAAM,EACN,KAAK,EACL,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,MAAM,GACP,EAAE,IAAI;IAkCP,OAAO,IAAI,IAAI;IAKf,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;CAkMlD"}
@@ -1,6 +1,6 @@
1
1
  import { assert, unreachable } from "../../../shared/src/asserts.js";
2
2
  import { throwOutput } from "./operator.js";
3
- import { buildJoinConstraint, generateWithOverlay, isJoinMatch, rowEqualsForCompoundKey } from "./join-utils.js";
3
+ import { buildJoinConstraint, generateWithOverlay, generateWithOverlayUnordered, isJoinMatch, rowEqualsForCompoundKey } from "./join-utils.js";
4
4
  //#region ../zql/src/ivm/join.ts
5
5
  /**
6
6
  * The Join operator joins the output from two upstream inputs. Zero's join
@@ -147,7 +147,11 @@ var Join = class {
147
147
  const childStream = () => {
148
148
  const constraint = buildJoinConstraint(parentNodeRow, this.#parentKey, this.#childKey);
149
149
  const stream = constraint ? this.#child.fetch({ constraint }) : [];
150
- if (this.#inprogressChildChange && isJoinMatch(parentNodeRow, this.#parentKey, this.#inprogressChildChange.change.node.row, this.#childKey) && this.#inprogressChildChange.position && this.#schema.compareRows(parentNodeRow, this.#inprogressChildChange.position) > 0) return generateWithOverlay(stream, this.#inprogressChildChange.change, this.#child.getSchema());
150
+ if (this.#inprogressChildChange && isJoinMatch(parentNodeRow, this.#parentKey, this.#inprogressChildChange.change.node.row, this.#childKey) && this.#inprogressChildChange.position && this.#schema.compareRows(parentNodeRow, this.#inprogressChildChange.position) > 0) {
151
+ const childSchema = this.#child.getSchema();
152
+ if (childSchema.sort === void 0) return generateWithOverlayUnordered(stream, this.#inprogressChildChange.change, childSchema);
153
+ return generateWithOverlay(stream, this.#inprogressChildChange.change, childSchema);
154
+ }
151
155
  return stream;
152
156
  };
153
157
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#pushParent","#pushChild","#output","#processParentNode","#pushChildChange","#inprogressChildChange"],"sources":["../../../../../zql/src/ivm/join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change, ChildChange} from './change.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlay,\n isJoinMatch,\n rowEqualsForCompoundKey,\n type JoinChangeOverlay,\n} from './join-utils.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in parentKey corresponds to the nth key in childKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * The Join operator joins the output from two upstream inputs. Zero's join\n * is a little different from SQL's join in that we output hierarchical data,\n * not a flat table. This makes it a lot more useful for UI programming and\n * avoids duplicating tons of data like left join would.\n *\n * The Nodes output from Join have a new relationship added to them, which has\n * the name #relationshipName. The value of the relationship is a stream of\n * child nodes which are the corresponding values from the child source.\n */\nexport class Join implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: JoinChangeOverlay | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#parent.destroy();\n this.#child.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n for (const parentNode of this.#parent.fetch(req)) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n yield this.#processParentNode(parentNode.row, parentNode.relationships);\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n switch (change.type) {\n case 'add':\n yield* this.#output.push(\n {\n type: 'add',\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n },\n this,\n );\n break;\n case 'remove':\n yield* this.#output.push(\n {\n type: 'remove',\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n },\n this,\n );\n break;\n case 'child':\n yield* this.#output.push(\n {\n type: 'child',\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n child: change.child,\n },\n this,\n );\n break;\n case 'edit': {\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(\n change.oldNode.row,\n change.node.row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n {\n type: 'edit',\n oldNode: this.#processParentNode(\n change.oldNode.row,\n change.oldNode.relationships,\n ),\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n },\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change.type) {\n case 'add':\n case 'remove':\n yield* this.#pushChildChange(change.node.row, change);\n break;\n case 'child':\n yield* this.#pushChildChange(change.node.row, change);\n break;\n case 'edit': {\n const childRow = change.node.row;\n const oldChildRow = change.oldNode.row;\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(oldChildRow, childRow, this.#childKey),\n 'Child edit must not change relationship.',\n );\n yield* this.#pushChildChange(childRow, change);\n break;\n }\n\n default:\n unreachable(change);\n }\n }\n\n *#pushChildChange(childRow: Row, change: Change): Stream<'yield'> {\n this.#inprogressChildChange = {\n change,\n position: undefined,\n };\n try {\n const constraint = buildJoinConstraint(\n childRow,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodes = constraint ? this.#parent.fetch({constraint}) : [];\n\n for (const parentNode of parentNodes) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n this.#inprogressChildChange.position = parentNode.row;\n const childChange: ChildChange = {\n type: 'child',\n node: this.#processParentNode(\n parentNode.row,\n parentNode.relationships,\n ),\n child: {\n relationshipName: this.#relationshipName,\n change,\n },\n };\n yield* this.#output.push(childChange, this);\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n #processParentNode(\n parentNodeRow: Row,\n parentNodeRelations: Record<string, () => Stream<Node | 'yield'>>,\n ): Node {\n const childStream = () => {\n const constraint = buildJoinConstraint(\n parentNodeRow,\n this.#parentKey,\n this.#childKey,\n );\n const stream = constraint ? this.#child.fetch({constraint}) : [];\n\n if (\n this.#inprogressChildChange &&\n isJoinMatch(\n parentNodeRow,\n this.#parentKey,\n this.#inprogressChildChange.change.node.row,\n this.#childKey,\n ) &&\n this.#inprogressChildChange.position &&\n this.#schema.compareRows(\n parentNodeRow,\n this.#inprogressChildChange.position,\n ) > 0\n ) {\n return generateWithOverlay(\n stream,\n this.#inprogressChildChange.change,\n this.#child.getSchema(),\n );\n }\n return stream;\n };\n\n return {\n row: parentNodeRow,\n relationships: {\n ...parentNodeRelations,\n [this.#relationshipName]: childStream,\n },\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#pushParent","#pushChild","#output","#processParentNode","#pushChildChange","#inprogressChildChange"],"sources":["../../../../../zql/src/ivm/join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change, ChildChange} from './change.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlay,\n generateWithOverlayUnordered,\n isJoinMatch,\n rowEqualsForCompoundKey,\n type JoinChangeOverlay,\n} from './join-utils.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in parentKey corresponds to the nth key in childKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * The Join operator joins the output from two upstream inputs. Zero's join\n * is a little different from SQL's join in that we output hierarchical data,\n * not a flat table. This makes it a lot more useful for UI programming and\n * avoids duplicating tons of data like left join would.\n *\n * The Nodes output from Join have a new relationship added to them, which has\n * the name #relationshipName. The value of the relationship is a stream of\n * child nodes which are the corresponding values from the child source.\n */\nexport class Join implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: JoinChangeOverlay | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#parent.destroy();\n this.#child.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n for (const parentNode of this.#parent.fetch(req)) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n yield this.#processParentNode(parentNode.row, parentNode.relationships);\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n switch (change.type) {\n case 'add':\n yield* this.#output.push(\n {\n type: 'add',\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n },\n this,\n );\n break;\n case 'remove':\n yield* this.#output.push(\n {\n type: 'remove',\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n },\n this,\n );\n break;\n case 'child':\n yield* this.#output.push(\n {\n type: 'child',\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n child: change.child,\n },\n this,\n );\n break;\n case 'edit': {\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(\n change.oldNode.row,\n change.node.row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n {\n type: 'edit',\n oldNode: this.#processParentNode(\n change.oldNode.row,\n change.oldNode.relationships,\n ),\n node: this.#processParentNode(\n change.node.row,\n change.node.relationships,\n ),\n },\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change.type) {\n case 'add':\n case 'remove':\n yield* this.#pushChildChange(change.node.row, change);\n break;\n case 'child':\n yield* this.#pushChildChange(change.node.row, change);\n break;\n case 'edit': {\n const childRow = change.node.row;\n const oldChildRow = change.oldNode.row;\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(oldChildRow, childRow, this.#childKey),\n 'Child edit must not change relationship.',\n );\n yield* this.#pushChildChange(childRow, change);\n break;\n }\n\n default:\n unreachable(change);\n }\n }\n\n *#pushChildChange(childRow: Row, change: Change): Stream<'yield'> {\n this.#inprogressChildChange = {\n change,\n position: undefined,\n };\n try {\n const constraint = buildJoinConstraint(\n childRow,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodes = constraint ? this.#parent.fetch({constraint}) : [];\n\n for (const parentNode of parentNodes) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n this.#inprogressChildChange.position = parentNode.row;\n const childChange: ChildChange = {\n type: 'child',\n node: this.#processParentNode(\n parentNode.row,\n parentNode.relationships,\n ),\n child: {\n relationshipName: this.#relationshipName,\n change,\n },\n };\n yield* this.#output.push(childChange, this);\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n #processParentNode(\n parentNodeRow: Row,\n parentNodeRelations: Record<string, () => Stream<Node | 'yield'>>,\n ): Node {\n const childStream = () => {\n const constraint = buildJoinConstraint(\n parentNodeRow,\n this.#parentKey,\n this.#childKey,\n );\n const stream = constraint ? this.#child.fetch({constraint}) : [];\n\n if (\n this.#inprogressChildChange &&\n isJoinMatch(\n parentNodeRow,\n this.#parentKey,\n this.#inprogressChildChange.change.node.row,\n this.#childKey,\n ) &&\n this.#inprogressChildChange.position &&\n this.#schema.compareRows(\n parentNodeRow,\n this.#inprogressChildChange.position,\n ) > 0\n ) {\n const childSchema = this.#child.getSchema();\n if (childSchema.sort === undefined) {\n return generateWithOverlayUnordered(\n stream,\n this.#inprogressChildChange.change,\n childSchema,\n );\n }\n return generateWithOverlay(\n stream,\n this.#inprogressChildChange.change,\n childSchema,\n );\n }\n return stream;\n };\n\n return {\n row: parentNodeRow,\n relationships: {\n ...parentNodeRelations,\n [this.#relationshipName]: childStream,\n },\n };\n }\n}\n"],"mappings":"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"}
@@ -21,7 +21,7 @@ export type Overlays = {
21
21
  export type Connection = {
22
22
  input: Input;
23
23
  output: Output | undefined;
24
- sort: Ordering;
24
+ sort?: Ordering | undefined;
25
25
  splitEditKeys: Set<string> | undefined;
26
26
  compareRows: Comparator;
27
27
  filters: {
@@ -48,7 +48,7 @@ export declare class MemorySource implements Source {
48
48
  };
49
49
  fork(): MemorySource;
50
50
  get data(): BTreeSet<Row>;
51
- connect(sort: Ordering, filters?: Condition, splitEditKeys?: Set<string>): SourceInput;
51
+ connect(sort: Ordering | undefined, filters?: Condition, splitEditKeys?: Set<string>): SourceInput;
52
52
  getIndexKeys(): string[];
53
53
  push(change: SourceChange): Stream<'yield'>;
54
54
  genPush(change: SourceChange): Generator<"yield" | undefined, void, unknown>;
@@ -79,5 +79,18 @@ export declare function generateWithOverlayInner(rowIterator: Iterable<Row>, ove
79
79
  row: Readonly<Record<string, import("../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>;
80
80
  relationships: {};
81
81
  }, void, unknown>;
82
+ /**
83
+ * Like {@link generateWithOverlay} but for unordered streams.
84
+ * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly
85
+ * at the start, and suppresses add/new-edit rows inline by PK match.
86
+ */
87
+ export declare function generateWithOverlayUnordered(rows: Iterable<Row>, constraint: Constraint | undefined, overlay: Overlay | undefined, lastPushedEpoch: number, primaryKey: PrimaryKey, filterPredicate?: ((row: Row) => boolean) | undefined): Generator<{
88
+ row: Readonly<Record<string, import("../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>;
89
+ relationships: {};
90
+ }, void, unknown>;
91
+ export declare function generateWithOverlayInnerUnordered(rowIterator: Iterable<Row>, overlays: Overlays, primaryKey: PrimaryKey): Generator<{
92
+ row: Readonly<Record<string, import("../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>;
93
+ relationships: {};
94
+ }, void, unknown>;
82
95
  export declare function stringify(change: SourceChange): string;
83
96
  //# sourceMappingURL=memory-source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { assert, unreachable } from "../../../shared/src/asserts.js";
2
2
  import { hasOwn } from "../../../shared/src/has-own.js";
3
3
  import { once } from "../../../shared/src/iterables.js";
4
+ import { must } from "../../../shared/src/must.js";
4
5
  import { skipYields } from "./operator.js";
5
6
  import { compareValues, makeComparator, valuesEqual } from "./data.js";
6
7
  import { filterPush } from "./filter-push.js";
@@ -51,12 +52,12 @@ var MemorySource = class MemorySource {
51
52
  get data() {
52
53
  return this.#getPrimaryIndex().data;
53
54
  }
54
- #getSchema(connection) {
55
+ #getSchema(connection, unordered) {
55
56
  return {
56
57
  tableName: this.#tableName,
57
58
  columns: this.#columns,
58
59
  primaryKey: this.#primaryKey,
59
- sort: connection.sort,
60
+ sort: unordered ? void 0 : connection.sort,
60
61
  system: "client",
61
62
  relationships: {},
62
63
  isHidden: false,
@@ -65,6 +66,8 @@ var MemorySource = class MemorySource {
65
66
  }
66
67
  connect(sort, filters, splitEditKeys) {
67
68
  const transformedFilters = transformFilters(filters);
69
+ const unordered = sort === void 0;
70
+ const internalSort = sort ?? this.#primaryIndexSort;
68
71
  const input = {
69
72
  getSchema: () => schema,
70
73
  fetch: (req) => this.#fetch(req, connection),
@@ -79,17 +82,17 @@ var MemorySource = class MemorySource {
79
82
  const connection = {
80
83
  input,
81
84
  output: void 0,
82
- sort,
85
+ sort: internalSort,
83
86
  splitEditKeys,
84
- compareRows: makeComparator(sort),
87
+ compareRows: makeComparator(internalSort),
85
88
  filters: transformedFilters.filters ? {
86
89
  condition: transformedFilters.filters,
87
90
  predicate: createPredicate(transformedFilters.filters)
88
91
  } : void 0,
89
92
  lastPushedEpoch: 0
90
93
  };
91
- const schema = this.#getSchema(connection);
92
- assertOrderingIncludesPK(sort, this.#primaryKey);
94
+ const schema = this.#getSchema(connection, unordered);
95
+ if (!unordered) assertOrderingIncludesPK(internalSort, this.#primaryKey);
93
96
  this.#connections.push(connection);
94
97
  return input;
95
98
  }
@@ -125,7 +128,8 @@ var MemorySource = class MemorySource {
125
128
  return [...this.#indexes.keys()];
126
129
  }
127
130
  *#fetch(req, conn) {
128
- const { sort: requestedSort, compareRows } = conn;
131
+ const requestedSort = must(conn.sort);
132
+ const { compareRows } = conn;
129
133
  const connectionComparator = (r1, r2) => compareRows(r1, r2) * (req.reverse ? -1 : 1);
130
134
  const pkConstraint = primaryKeyConstraintFromFilters(conn.filters?.condition, this.#primaryKey);
131
135
  const fetchOrPkConstraint = pkConstraint ?? req.constraint;
@@ -367,6 +371,63 @@ function* generateWithOverlayInner(rowIterator, overlays, compare) {
367
371
  relationships: {}
368
372
  };
369
373
  }
374
+ /**
375
+ * Like {@link generateWithOverlay} but for unordered streams.
376
+ * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly
377
+ * at the start, and suppresses add/new-edit rows inline by PK match.
378
+ */
379
+ function* generateWithOverlayUnordered(rows, constraint, overlay, lastPushedEpoch, primaryKey, filterPredicate) {
380
+ let overlayToApply = void 0;
381
+ if (overlay && lastPushedEpoch >= overlay.epoch) overlayToApply = overlay;
382
+ let overlays = {
383
+ add: void 0,
384
+ remove: void 0
385
+ };
386
+ switch (overlayToApply?.change.type) {
387
+ case "add":
388
+ overlays = {
389
+ add: overlayToApply.change.row,
390
+ remove: void 0
391
+ };
392
+ break;
393
+ case "remove":
394
+ overlays = {
395
+ add: void 0,
396
+ remove: overlayToApply.change.row
397
+ };
398
+ break;
399
+ case "edit":
400
+ overlays = {
401
+ add: overlayToApply.change.row,
402
+ remove: overlayToApply.change.oldRow
403
+ };
404
+ break;
405
+ }
406
+ if (constraint) overlays = overlaysForConstraint(overlays, constraint);
407
+ if (filterPredicate) overlays = overlaysForFilterPredicate(overlays, filterPredicate);
408
+ yield* generateWithOverlayInnerUnordered(rows, overlays, primaryKey);
409
+ }
410
+ function* generateWithOverlayInnerUnordered(rowIterator, overlays, primaryKey) {
411
+ if (overlays.add) yield {
412
+ row: overlays.add,
413
+ relationships: {}
414
+ };
415
+ let removeSkipped = false;
416
+ for (const row of rowIterator) {
417
+ if (!removeSkipped && overlays.remove && rowMatchesPK(overlays.remove, row, primaryKey)) {
418
+ removeSkipped = true;
419
+ continue;
420
+ }
421
+ yield {
422
+ row,
423
+ relationships: {}
424
+ };
425
+ }
426
+ }
427
+ function rowMatchesPK(a, b, primaryKey) {
428
+ for (const key of primaryKey) if (!valuesEqual(a[key], b[key])) return false;
429
+ return true;
430
+ }
370
431
  var minValue = Symbol("min-value");
371
432
  var maxValue = Symbol("max-value");
372
433
  function makeBoundComparator(sort) {
@@ -394,6 +455,6 @@ function stringify(change) {
394
455
  return JSON.stringify(change, (_, v) => typeof v === "bigint" ? v.toString() : v);
395
456
  }
396
457
  //#endregion
397
- export { MemorySource, genPushAndWriteWithSplitEdit, generateWithOverlay, generateWithStart };
458
+ export { MemorySource, genPushAndWriteWithSplitEdit, generateWithOverlay, generateWithOverlayUnordered, generateWithStart };
398
459
 
399
460
  //# sourceMappingURL=memory-source.js.map