@rocicorp/zero 1.6.0-canary.0 → 1.6.0-canary.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +13 -4
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +8 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +10 -19
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +9 -9
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +3 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js +7 -0
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +2 -2
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +7 -17
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/memory-source.js +1 -1
- package/out/zql/src/ivm/take.js +2 -2
- package/out/zqlite/src/table-source.js +1 -1
- package/package.json +1 -1
- package/out/zql/src/ivm/cap.d.ts +0 -32
- package/out/zql/src/ivm/cap.d.ts.map +0 -1
- package/out/zql/src/ivm/cap.js +0 -205
- package/out/zql/src/ivm/cap.js.map +0 -1
|
@@ -4,12 +4,12 @@ import { once, toSorted } from "../../../shared/src/iterables.js";
|
|
|
4
4
|
import { must } from "../../../shared/src/must.js";
|
|
5
5
|
import { skipYields } from "./skip-yields.js";
|
|
6
6
|
import { makeAddChange, makeEditChange, makeRemoveChange } from "./change.js";
|
|
7
|
-
import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
|
|
8
7
|
import { compareValues, makeComparator, valuesEqual } from "./data.js";
|
|
9
8
|
import { filterPush } from "./filter-push.js";
|
|
10
9
|
import { constraintMatchesPrimaryKey, constraintMatchesRow, primaryKeyConstraintFromFilters } from "./constraint.js";
|
|
11
10
|
import { BTreeSet } from "../../../shared/src/btree-set.js";
|
|
12
11
|
import { createPredicate, transformFilters } from "../builder/filter.js";
|
|
12
|
+
import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
|
|
13
13
|
import { makeSourceChangeAdd, makeSourceChangeRemove } from "./source.js";
|
|
14
14
|
//#region ../zql/src/ivm/memory-source.ts
|
|
15
15
|
/**
|
package/out/zql/src/ivm/take.js
CHANGED
|
@@ -2,8 +2,8 @@ import { assert, unreachable } from "../../../shared/src/asserts.js";
|
|
|
2
2
|
import { hasOwn } from "../../../shared/src/has-own.js";
|
|
3
3
|
import { throwOutput } from "./operator.js";
|
|
4
4
|
import { makeAddChange, makeRemoveChange } from "./change.js";
|
|
5
|
-
import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
|
|
6
5
|
import { compareValues } from "./data.js";
|
|
6
|
+
import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
|
|
7
7
|
//#region ../zql/src/ivm/take.ts
|
|
8
8
|
var MAX_BOUND_KEY = "maxBound";
|
|
9
9
|
/**
|
|
@@ -405,6 +405,6 @@ function makePartitionKeyComparator(partitionKey) {
|
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
407
|
//#endregion
|
|
408
|
-
export { Take
|
|
408
|
+
export { Take };
|
|
409
409
|
|
|
410
410
|
//# sourceMappingURL=take.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { assert, unreachable } from "../../shared/src/asserts.js";
|
|
2
2
|
import { must } from "../../shared/src/must.js";
|
|
3
|
-
import { assertOrderingIncludesPK } from "../../zql/src/query/complete-ordering.js";
|
|
4
3
|
import { makeComparator } from "../../zql/src/ivm/data.js";
|
|
5
4
|
import { createPredicate, transformFilters } from "../../zql/src/builder/filter.js";
|
|
5
|
+
import { assertOrderingIncludesPK } from "../../zql/src/query/complete-ordering.js";
|
|
6
6
|
import { genPushAndWriteWithSplitEdit, generateWithOverlay, generateWithOverlayUnordered, generateWithStart } from "../../zql/src/ivm/memory-source.js";
|
|
7
7
|
import { timeSampled } from "../../otel/src/maybe-time.js";
|
|
8
8
|
import { compile, format, sql } from "./internal/sql.js";
|
package/package.json
CHANGED
package/out/zql/src/ivm/cap.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { type Change } from './change.ts';
|
|
2
|
-
import type { Node } from './data.ts';
|
|
3
|
-
import { type FetchRequest, type Input, type Operator, type Output, type Storage } from './operator.ts';
|
|
4
|
-
import type { SourceSchema } from './schema.ts';
|
|
5
|
-
import { type Stream } from './stream.ts';
|
|
6
|
-
import { type PartitionKey } from './take.ts';
|
|
7
|
-
/**
|
|
8
|
-
* The Cap operator is a count-based limiter for EXISTS subqueries that
|
|
9
|
-
* does not require ordering. Unlike Take, it tracks membership by primary
|
|
10
|
-
* key set rather than by a sorted bound. This means:
|
|
11
|
-
*
|
|
12
|
-
* - No comparator needed (no ordering requirement)
|
|
13
|
-
* - No `start` or `reverse` fetch support
|
|
14
|
-
* - No `#rowHiddenFromFetch` complexity (we can defer when adding to the pk set)
|
|
15
|
-
*
|
|
16
|
-
* Cap is used in EXISTS child pipelines where only the count of matching
|
|
17
|
-
* rows matters, not their order. This allows SQLite to skip ORDER BY
|
|
18
|
-
* entirely, enabling much faster query plans.
|
|
19
|
-
*
|
|
20
|
-
* Cap can count rows globally or by unique value of some partition key
|
|
21
|
-
* (same as Take).
|
|
22
|
-
*/
|
|
23
|
-
export declare class Cap implements Operator {
|
|
24
|
-
#private;
|
|
25
|
-
constructor(input: Input, storage: Storage, limit: number, partitionKey?: PartitionKey);
|
|
26
|
-
setOutput(output: Output): void;
|
|
27
|
-
getSchema(): SourceSchema;
|
|
28
|
-
fetch(req: FetchRequest): Stream<Node | 'yield'>;
|
|
29
|
-
push(change: Change): Stream<'yield'>;
|
|
30
|
-
destroy(): void;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=cap.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cap.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/cap.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,MAAM,EAAkB,MAAM,aAAa,CAAC;AAExE,OAAO,KAAK,EAAa,IAAI,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAanB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,GAAI,YAAW,QAAQ;;gBAWhC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,YAAY;IAa7B,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;IA0FhD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;IAsHtC,OAAO,IAAI,IAAI;CAGhB"}
|
package/out/zql/src/ivm/cap.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { assert } from "../../../shared/src/asserts.js";
|
|
2
|
-
import { throwOutput } from "./operator.js";
|
|
3
|
-
import { makeAddChange } from "./change.js";
|
|
4
|
-
import { constraintMatchesPartitionKey, makePartitionKeyComparator } from "./take.js";
|
|
5
|
-
//#region ../zql/src/ivm/cap.ts
|
|
6
|
-
/**
|
|
7
|
-
* The Cap operator is a count-based limiter for EXISTS subqueries that
|
|
8
|
-
* does not require ordering. Unlike Take, it tracks membership by primary
|
|
9
|
-
* key set rather than by a sorted bound. This means:
|
|
10
|
-
*
|
|
11
|
-
* - No comparator needed (no ordering requirement)
|
|
12
|
-
* - No `start` or `reverse` fetch support
|
|
13
|
-
* - No `#rowHiddenFromFetch` complexity (we can defer when adding to the pk set)
|
|
14
|
-
*
|
|
15
|
-
* Cap is used in EXISTS child pipelines where only the count of matching
|
|
16
|
-
* rows matters, not their order. This allows SQLite to skip ORDER BY
|
|
17
|
-
* entirely, enabling much faster query plans.
|
|
18
|
-
*
|
|
19
|
-
* Cap can count rows globally or by unique value of some partition key
|
|
20
|
-
* (same as Take).
|
|
21
|
-
*/
|
|
22
|
-
var Cap = class {
|
|
23
|
-
#input;
|
|
24
|
-
#storage;
|
|
25
|
-
#limit;
|
|
26
|
-
#partitionKey;
|
|
27
|
-
#partitionKeyComparator;
|
|
28
|
-
#primaryKey;
|
|
29
|
-
#output = throwOutput;
|
|
30
|
-
constructor(input, storage, limit, partitionKey) {
|
|
31
|
-
assert(limit >= 0, "Limit must be non-negative");
|
|
32
|
-
input.setOutput(this);
|
|
33
|
-
this.#input = input;
|
|
34
|
-
this.#storage = storage;
|
|
35
|
-
this.#limit = limit;
|
|
36
|
-
this.#partitionKey = partitionKey;
|
|
37
|
-
this.#partitionKeyComparator = partitionKey && makePartitionKeyComparator(partitionKey);
|
|
38
|
-
this.#primaryKey = input.getSchema().primaryKey;
|
|
39
|
-
}
|
|
40
|
-
setOutput(output) {
|
|
41
|
-
this.#output = output;
|
|
42
|
-
}
|
|
43
|
-
getSchema() {
|
|
44
|
-
return this.#input.getSchema();
|
|
45
|
-
}
|
|
46
|
-
*fetch(req) {
|
|
47
|
-
assert(!req.start, "Cap does not support start");
|
|
48
|
-
assert(!req.reverse, "Cap does not support reverse");
|
|
49
|
-
assert(!this.#partitionKey || req.constraint !== void 0 && constraintMatchesPartitionKey(req.constraint, this.#partitionKey), "Cap fetch: constraint must match partition key when partitioned");
|
|
50
|
-
const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);
|
|
51
|
-
const capState = this.#storage.get(capStateKey);
|
|
52
|
-
if (!capState) {
|
|
53
|
-
yield* this.#initialFetch(req);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (capState.size === 0) return;
|
|
57
|
-
for (const pk of capState.pks) {
|
|
58
|
-
const constraint = deserializePKToConstraint(pk, this.#primaryKey);
|
|
59
|
-
for (const inputNode of this.#input.fetch({ constraint })) {
|
|
60
|
-
if (inputNode === "yield") {
|
|
61
|
-
yield inputNode;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
yield inputNode;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
*#initialFetch(req) {
|
|
69
|
-
if (this.#limit === 0) return;
|
|
70
|
-
assert(constraintMatchesPartitionKey(req.constraint, this.#partitionKey), "Constraint should match partition key");
|
|
71
|
-
const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);
|
|
72
|
-
assert(this.#storage.get(capStateKey) === void 0, "Cap state should be undefined");
|
|
73
|
-
let size = 0;
|
|
74
|
-
const pks = [];
|
|
75
|
-
let downstreamEarlyReturn = true;
|
|
76
|
-
let exceptionThrown = false;
|
|
77
|
-
try {
|
|
78
|
-
for (const inputNode of this.#input.fetch(req)) {
|
|
79
|
-
if (inputNode === "yield") {
|
|
80
|
-
yield "yield";
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
yield inputNode;
|
|
84
|
-
pks.push(serializePK(inputNode.row, this.#primaryKey));
|
|
85
|
-
size++;
|
|
86
|
-
if (size === this.#limit) break;
|
|
87
|
-
}
|
|
88
|
-
downstreamEarlyReturn = false;
|
|
89
|
-
} catch (e) {
|
|
90
|
-
exceptionThrown = true;
|
|
91
|
-
throw e;
|
|
92
|
-
} finally {
|
|
93
|
-
if (!exceptionThrown) {
|
|
94
|
-
this.#storage.set(capStateKey, {
|
|
95
|
-
size,
|
|
96
|
-
pks
|
|
97
|
-
});
|
|
98
|
-
assert(!downstreamEarlyReturn, "Unexpected early return prevented full hydration");
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
*push(change) {
|
|
103
|
-
if (change[0] === 2) {
|
|
104
|
-
yield* this.#pushEditChange(change);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const capStateKey = getCapStateKey(this.#partitionKey, change[1].row);
|
|
108
|
-
const capState = this.#storage.get(capStateKey);
|
|
109
|
-
if (!capState) return;
|
|
110
|
-
const pk = serializePK(change[1].row, this.#primaryKey);
|
|
111
|
-
if (change[0] === 0) {
|
|
112
|
-
if (capState.size < this.#limit) {
|
|
113
|
-
const pks = [...capState.pks, pk];
|
|
114
|
-
this.#storage.set(capStateKey, {
|
|
115
|
-
size: capState.size + 1,
|
|
116
|
-
pks
|
|
117
|
-
});
|
|
118
|
-
yield* this.#output.push(change, this);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
return;
|
|
122
|
-
} else if (change[0] === 1) {
|
|
123
|
-
const pkIndex = capState.pks.indexOf(pk);
|
|
124
|
-
if (pkIndex === -1) return;
|
|
125
|
-
const pks = [...capState.pks];
|
|
126
|
-
pks.splice(pkIndex, 1);
|
|
127
|
-
const newSize = capState.size - 1;
|
|
128
|
-
const pkSet = new Set(pks);
|
|
129
|
-
const constraint = this.#partitionKey ? Object.fromEntries(this.#partitionKey.map((key) => [key, change[1].row[key]])) : void 0;
|
|
130
|
-
let replacement;
|
|
131
|
-
for (const node of this.#input.fetch({ constraint })) {
|
|
132
|
-
if (node === "yield") {
|
|
133
|
-
yield node;
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const nodePK = serializePK(node.row, this.#primaryKey);
|
|
137
|
-
if (!pkSet.has(nodePK)) {
|
|
138
|
-
replacement = node;
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (replacement) {
|
|
143
|
-
this.#storage.set(capStateKey, {
|
|
144
|
-
size: newSize,
|
|
145
|
-
pks
|
|
146
|
-
});
|
|
147
|
-
yield* this.#output.push(change, this);
|
|
148
|
-
const replacementPK = serializePK(replacement.row, this.#primaryKey);
|
|
149
|
-
pks.push(replacementPK);
|
|
150
|
-
this.#storage.set(capStateKey, {
|
|
151
|
-
size: newSize + 1,
|
|
152
|
-
pks
|
|
153
|
-
});
|
|
154
|
-
yield* this.#output.push(makeAddChange(replacement), this);
|
|
155
|
-
} else {
|
|
156
|
-
this.#storage.set(capStateKey, {
|
|
157
|
-
size: newSize,
|
|
158
|
-
pks
|
|
159
|
-
});
|
|
160
|
-
yield* this.#output.push(change, this);
|
|
161
|
-
}
|
|
162
|
-
} else if (change[0] === 3) {
|
|
163
|
-
if (new Set(capState.pks).has(pk)) yield* this.#output.push(change, this);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
*#pushEditChange(change) {
|
|
167
|
-
assert(!this.#partitionKeyComparator || this.#partitionKeyComparator(change[2].row, change[1].row) === 0, "Unexpected change of partition key");
|
|
168
|
-
const capStateKey = getCapStateKey(this.#partitionKey, change[2].row);
|
|
169
|
-
const capState = this.#storage.get(capStateKey);
|
|
170
|
-
if (!capState) return;
|
|
171
|
-
const oldPK = serializePK(change[2].row, this.#primaryKey);
|
|
172
|
-
if (new Set(capState.pks).has(oldPK)) {
|
|
173
|
-
const newPK = serializePK(change[1].row, this.#primaryKey);
|
|
174
|
-
if (oldPK !== newPK) {
|
|
175
|
-
const pks = capState.pks.map((p) => p === oldPK ? newPK : p);
|
|
176
|
-
this.#storage.set(capStateKey, {
|
|
177
|
-
size: capState.size,
|
|
178
|
-
pks
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
yield* this.#output.push(change, this);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
destroy() {
|
|
185
|
-
this.#input.destroy();
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
function getCapStateKey(partitionKey, rowOrConstraint) {
|
|
189
|
-
const partitionValues = [];
|
|
190
|
-
if (partitionKey && rowOrConstraint) for (const key of partitionKey) partitionValues.push(rowOrConstraint[key]);
|
|
191
|
-
return JSON.stringify(["cap", ...partitionValues]);
|
|
192
|
-
}
|
|
193
|
-
function serializePK(row, primaryKey) {
|
|
194
|
-
return JSON.stringify(primaryKey.map((k) => row[k]));
|
|
195
|
-
}
|
|
196
|
-
function deserializePKToConstraint(pk, primaryKey) {
|
|
197
|
-
const values = JSON.parse(pk);
|
|
198
|
-
const constraint = {};
|
|
199
|
-
for (let i = 0; i < primaryKey.length; i++) constraint[primaryKey[i]] = values[i];
|
|
200
|
-
return constraint;
|
|
201
|
-
}
|
|
202
|
-
//#endregion
|
|
203
|
-
export { Cap };
|
|
204
|
-
|
|
205
|
-
//# sourceMappingURL=cap.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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 {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {makeAddChange, 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 // Cap is only built for non-flipped EXISTS subqueries, whose only\n // downstream consumer is a Join that always fetches with a constraint\n // built from the correlation's childField — which is Cap's partition\n // key. So either partitionKey is undefined, or constraint matches.\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey)),\n 'Cap fetch: constraint must match partition key when partitioned',\n );\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 }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n if (this.#limit === 0) {\n return;\n }\n\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\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[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const capStateKey = getCapStateKey(\n this.#partitionKey,\n change[ChangeIndex.NODE].row,\n );\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return;\n }\n\n const pk = serializePK(change[ChangeIndex.NODE].row, this.#primaryKey);\n\n if (change[ChangeIndex.TYPE] === ChangeType.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[ChangeIndex.TYPE] === ChangeType.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(\n key => [key, change[ChangeIndex.NODE].row[key]] as const,\n ),\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(makeAddChange(replacement), this);\n } else {\n this.#storage.set(capStateKey, {size: newSize, pks});\n yield* this.#output.push(change, this);\n }\n } else if (change[ChangeIndex.TYPE] === ChangeType.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(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n const capStateKey = getCapStateKey(\n this.#partitionKey,\n change[ChangeIndex.OLD_NODE].row,\n );\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return;\n }\n\n const oldPK = serializePK(\n change[ChangeIndex.OLD_NODE].row,\n this.#primaryKey,\n );\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[ChangeIndex.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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"}
|