@rocicorp/zero 1.2.0-canary.1 → 1.2.0-canary.3

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 (53) hide show
  1. package/out/z2s/src/sql.js +1 -1
  2. package/out/z2s/src/sql.js.map +1 -1
  3. package/out/zero/package.js +1 -1
  4. package/out/zero/package.js.map +1 -1
  5. package/out/zero-cache/src/config/zero-config.d.ts +1 -1
  6. package/out/zero-cache/src/config/zero-config.js +2 -2
  7. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  8. package/out/zero-cache/src/services/change-source/change-source.d.ts +1 -1
  9. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  10. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  11. package/out/zero-cache/src/services/change-source/pg/change-source.js +65 -22
  12. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  13. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +1 -2
  14. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  15. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +4 -0
  16. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  17. package/out/zero-cache/src/services/change-streamer/change-streamer.js +9 -1
  18. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  19. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  20. package/out/zero-cache/src/services/mutagen/pusher.js +7 -4
  21. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  22. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  23. package/out/zero-cache/src/services/replicator/incremental-sync.js +6 -3
  24. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  25. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  26. package/out/zero-cache/src/services/view-syncer/cvr.js +7 -5
  27. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  28. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  29. package/out/zero-cache/src/services/view-syncer/view-syncer.js +14 -4
  30. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  31. package/out/zero-cache/src/types/pg.d.ts +1 -0
  32. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  33. package/out/zero-cache/src/types/pg.js +4 -1
  34. package/out/zero-cache/src/types/pg.js.map +1 -1
  35. package/out/zero-client/src/client/version.js +1 -1
  36. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  37. package/out/zero-client/src/client/zero-poke-handler.js +6 -2
  38. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  39. package/out/zero-types/src/name-mapper.d.ts +1 -0
  40. package/out/zero-types/src/name-mapper.d.ts.map +1 -1
  41. package/out/zero-types/src/name-mapper.js +3 -0
  42. package/out/zero-types/src/name-mapper.js.map +1 -1
  43. package/out/zql/src/builder/builder.d.ts.map +1 -1
  44. package/out/zql/src/builder/builder.js +5 -15
  45. package/out/zql/src/builder/builder.js.map +1 -1
  46. package/out/zql/src/ivm/take.d.ts.map +1 -1
  47. package/out/zql/src/ivm/take.js +2 -2
  48. package/out/zql/src/ivm/take.js.map +1 -1
  49. package/package.json +1 -1
  50. package/out/zql/src/ivm/cap.d.ts +0 -32
  51. package/out/zql/src/ivm/cap.d.ts.map +0 -1
  52. package/out/zql/src/ivm/cap.js +0 -226
  53. package/out/zql/src/ivm/cap.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"take.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#output","#initialFetch","#rowHiddenFromFetch","#setTakeState","#pushEditChange","#getStateAndConstraint","#pushWithRowHiddenFromFetch"],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {type Change, type EditChange, type RemoveChange} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type 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';\n\nconst MAX_BOUND_KEY = 'maxBound';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: typeof MAX_BOUND_KEY): Row | undefined;\n get(key: string): TakeState | undefined;\n set(key: typeof MAX_BOUND_KEY, value: Row): void;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\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 const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\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 if (\n !this.#partitionKey ||\n (req.constraint &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey))\n ) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n yield* this.#initialFetch(req);\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n }\n return;\n }\n // There is a partition key, but the fetch is not constrained or constrained\n // on a different key. Thus we don't have a single take state to bound by.\n // This currently only happens with nested sub-queries\n // e.g. issues include issuelabels include label. We could remove this\n // case if we added a translation layer (powered by some state) in join.\n // Specifically we need joinKeyValue => parent constraint key\n const maxBound = this.#storage.get(MAX_BOUND_KEY);\n if (maxBound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(inputNode.row, maxBound) > 0) {\n return;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, inputNode.row);\n const takeState = this.#storage.get(takeStateKey);\n if (\n takeState?.bound !== undefined &&\n this.getSchema().compareRows(takeState.bound, inputNode.row) >= 0\n ) {\n yield inputNode;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\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 takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(takeStateKey) === undefined,\n 'Take state should be undefined',\n );\n\n let size = 0;\n let bound: Row | undefined;\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 bound = inputNode.row;\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.#setTakeState(\n takeStateKey,\n size,\n bound,\n this.#storage.get(MAX_BOUND_KEY),\n );\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 takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let maxBound: Row | undefined;\n let constraint: Constraint | undefined;\n if (takeState) {\n maxBound = this.#storage.get(MAX_BOUND_KEY);\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, maxBound, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n maxBound: undefined;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n maxBound: Row | undefined;\n constraint: Constraint | undefined;\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 {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.node.row);\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change.type === 'add') {\n if (takeState.size < this.#limit) {\n this.#setTakeState(\n takeStateKey,\n takeState.size + 1,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change.node.row) < 0\n ? change.node.row\n : takeState.bound,\n maxBound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change.node.row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange: RemoveChange = {\n type: 'remove',\n node: boundNode,\n };\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change.node.row, beforeBoundNode.row) > 0\n ? change.node.row\n : beforeBoundNode.row,\n maxBound,\n );\n yield* this.#pushWithRowHiddenFromFetch(change.node.row, removeChange);\n yield* this.#output.push(change, this);\n } else if (change.type === 'remove') {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(change.node.row, takeState.bound);\n if (compToBound > 0) {\n // change is after bound\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n let newBound: {node: Node; push: boolean} | undefined;\n if (beforeBoundNode) {\n const push = compareRows(beforeBoundNode.row, takeState.bound) > 0;\n newBound = {\n node: beforeBoundNode,\n push,\n };\n }\n if (!newBound?.push) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const push = compareRows(node.row, takeState.bound) > 0;\n newBound = {\n node,\n push,\n };\n if (push) {\n break;\n }\n }\n }\n\n if (newBound?.push) {\n yield* this.#output.push(change, this);\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBound.node.row,\n maxBound,\n );\n yield* this.#output.push(\n {\n type: 'add',\n node: newBound.node,\n },\n this,\n );\n return;\n }\n this.#setTakeState(\n takeStateKey,\n takeState.size - 1,\n newBound?.node.row,\n maxBound,\n );\n yield* this.#output.push(change, this);\n } else if (change.type === 'child') {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change.node.row, takeState.bound) <= 0\n ) {\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\n const {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.oldNode.row);\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(change.oldNode.row, takeState.bound);\n const newCmp = compareRows(change.node.row, takeState.bound);\n\n const that = this;\n const replaceBoundAndForwardChange = function* () {\n that.#setTakeState(\n takeStateKey,\n takeState.size,\n change.node.row,\n maxBound,\n );\n yield* that.#output.push(change, that);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode.row,\n maxBound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change.node.row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n yield* this.#pushWithRowHiddenFromFetch(newBoundNode.row, {\n type: 'remove',\n node: change.oldNode,\n });\n yield* this.#output.push(\n {\n type: 'add',\n node: newBoundNode,\n },\n this,\n );\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n yield* this.#pushWithRowHiddenFromFetch(change.node.row, {\n type: 'remove',\n node: oldBoundNode,\n });\n yield* this.#output.push(\n {\n type: 'add',\n node: change.node,\n },\n this,\n );\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change.node.row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n {\n type: 'remove',\n node: change.oldNode,\n },\n this,\n );\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n afterBoundNode.row,\n maxBound,\n );\n yield* this.#output.push(\n {\n type: 'add',\n node: afterBoundNode,\n },\n this,\n );\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(\n takeStateKey: string,\n size: number,\n bound: Row | undefined,\n maxBound: Row | undefined,\n ) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n if (\n bound !== undefined &&\n (maxBound === undefined ||\n this.getSchema().compareRows(bound, maxBound) > 0)\n ) {\n this.#storage.set(MAX_BOUND_KEY, bound);\n }\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\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(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"take.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#output","#initialFetch","#rowHiddenFromFetch","#setTakeState","#pushEditChange","#getStateAndConstraint","#pushWithRowHiddenFromFetch"],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {type Change, type EditChange, type RemoveChange} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type 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';\n\nconst MAX_BOUND_KEY = 'maxBound';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: typeof MAX_BOUND_KEY): Row | undefined;\n get(key: string): TakeState | undefined;\n set(key: typeof MAX_BOUND_KEY, value: Row): void;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\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 const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\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 if (\n !this.#partitionKey ||\n (req.constraint &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey))\n ) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n yield* this.#initialFetch(req);\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n }\n return;\n }\n // There is a partition key, but the fetch is not constrained or constrained\n // on a different key. Thus we don't have a single take state to bound by.\n // This currently only happens with nested sub-queries\n // e.g. issues include issuelabels include label. We could remove this\n // case if we added a translation layer (powered by some state) in join.\n // Specifically we need joinKeyValue => parent constraint key\n const maxBound = this.#storage.get(MAX_BOUND_KEY);\n if (maxBound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(inputNode.row, maxBound) > 0) {\n return;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, inputNode.row);\n const takeState = this.#storage.get(takeStateKey);\n if (\n takeState?.bound !== undefined &&\n this.getSchema().compareRows(takeState.bound, inputNode.row) >= 0\n ) {\n yield inputNode;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n\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 takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(takeStateKey) === undefined,\n 'Take state should be undefined',\n );\n\n let size = 0;\n let bound: Row | undefined;\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 bound = inputNode.row;\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.#setTakeState(\n takeStateKey,\n size,\n bound,\n this.#storage.get(MAX_BOUND_KEY),\n );\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 takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let maxBound: Row | undefined;\n let constraint: Constraint | undefined;\n if (takeState) {\n maxBound = this.#storage.get(MAX_BOUND_KEY);\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, maxBound, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n maxBound: undefined;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n maxBound: Row | undefined;\n constraint: Constraint | undefined;\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 {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.node.row);\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change.type === 'add') {\n if (takeState.size < this.#limit) {\n this.#setTakeState(\n takeStateKey,\n takeState.size + 1,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change.node.row) < 0\n ? change.node.row\n : takeState.bound,\n maxBound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change.node.row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange: RemoveChange = {\n type: 'remove',\n node: boundNode,\n };\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change.node.row, beforeBoundNode.row) > 0\n ? change.node.row\n : beforeBoundNode.row,\n maxBound,\n );\n yield* this.#pushWithRowHiddenFromFetch(change.node.row, removeChange);\n yield* this.#output.push(change, this);\n } else if (change.type === 'remove') {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(change.node.row, takeState.bound);\n if (compToBound > 0) {\n // change is after bound\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n let newBound: {node: Node; push: boolean} | undefined;\n if (beforeBoundNode) {\n const push = compareRows(beforeBoundNode.row, takeState.bound) > 0;\n newBound = {\n node: beforeBoundNode,\n push,\n };\n }\n if (!newBound?.push) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const push = compareRows(node.row, takeState.bound) > 0;\n newBound = {\n node,\n push,\n };\n if (push) {\n break;\n }\n }\n }\n\n if (newBound?.push) {\n yield* this.#output.push(change, this);\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBound.node.row,\n maxBound,\n );\n yield* this.#output.push(\n {\n type: 'add',\n node: newBound.node,\n },\n this,\n );\n return;\n }\n this.#setTakeState(\n takeStateKey,\n takeState.size - 1,\n newBound?.node.row,\n maxBound,\n );\n yield* this.#output.push(change, this);\n } else if (change.type === 'child') {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change.node.row, takeState.bound) <= 0\n ) {\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\n const {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.oldNode.row);\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(change.oldNode.row, takeState.bound);\n const newCmp = compareRows(change.node.row, takeState.bound);\n\n const that = this;\n const replaceBoundAndForwardChange = function* () {\n that.#setTakeState(\n takeStateKey,\n takeState.size,\n change.node.row,\n maxBound,\n );\n yield* that.#output.push(change, that);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode.row,\n maxBound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change.node.row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n yield* this.#pushWithRowHiddenFromFetch(newBoundNode.row, {\n type: 'remove',\n node: change.oldNode,\n });\n yield* this.#output.push(\n {\n type: 'add',\n node: newBoundNode,\n },\n this,\n );\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n yield* this.#pushWithRowHiddenFromFetch(change.node.row, {\n type: 'remove',\n node: oldBoundNode,\n });\n yield* this.#output.push(\n {\n type: 'add',\n node: change.node,\n },\n this,\n );\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change.node.row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n {\n type: 'remove',\n node: change.oldNode,\n },\n this,\n );\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n afterBoundNode.row,\n maxBound,\n );\n yield* this.#output.push(\n {\n type: 'add',\n node: afterBoundNode,\n },\n this,\n );\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(\n takeStateKey: string,\n size: number,\n bound: Row | undefined,\n maxBound: Row | undefined,\n ) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n if (\n bound !== undefined &&\n (maxBound === undefined ||\n this.getSchema().compareRows(bound, maxBound) > 0)\n ) {\n this.#storage.set(MAX_BOUND_KEY, bound);\n }\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\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(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rocicorp/zero",
3
- "version": "1.2.0-canary.1",
3
+ "version": "1.2.0-canary.3",
4
4
  "description": "Zero is a web framework for serverless web development.",
5
5
  "author": "Rocicorp, Inc.",
6
6
  "repository": {
@@ -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":"AAGA,OAAO,EAAC,KAAK,MAAM,EAAkB,MAAM,aAAa,CAAC;AAEzD,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;IA8GhD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;IAwGtC,OAAO,IAAI,IAAI;CAGhB"}
@@ -1,226 +0,0 @@
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
@@ -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 {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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"}