@rocicorp/zero 1.12.0-canary.4 → 1.12.0-canary.5

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 (85) hide show
  1. package/out/analyze-query/src/analyze-cli.d.ts.map +1 -1
  2. package/out/analyze-query/src/analyze-cli.js +2 -1
  3. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  4. package/out/analyze-query/src/bin-transform.js +1 -1
  5. package/out/analyze-query/src/zql-chain.d.ts +23 -0
  6. package/out/analyze-query/src/zql-chain.d.ts.map +1 -0
  7. package/out/analyze-query/src/zql-chain.js +162 -0
  8. package/out/analyze-query/src/zql-chain.js.map +1 -0
  9. package/out/ast-to-zql/src/bin.js +1 -1
  10. package/out/zero/package.js +21 -22
  11. package/out/zero/package.js.map +1 -1
  12. package/out/zero-cache/src/auth/load-permissions.js +1 -1
  13. package/out/zero-cache/src/auth/write-authorizer.js +1 -1
  14. package/out/zero-cache/src/scripts/decommission.js +1 -1
  15. package/out/zero-cache/src/scripts/deploy-permissions.js +1 -1
  16. package/out/zero-cache/src/server/change-streamer.js +1 -1
  17. package/out/zero-cache/src/server/replicator.js +1 -1
  18. package/out/zero-cache/src/server/syncer.js +3 -3
  19. package/out/zero-cache/src/services/analyze.js +2 -2
  20. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
  21. package/out/zero-cache/src/services/change-source/common/replica-schema.js +1 -1
  22. package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
  23. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
  24. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +2 -2
  25. package/out/zero-cache/src/services/change-source/pg/change-source.js +2 -2
  26. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +4 -4
  27. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +2 -2
  28. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
  29. package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
  30. package/out/zero-cache/src/services/change-source/pg/schema/published.js +1 -1
  31. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
  32. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +1 -1
  33. package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
  34. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +2 -2
  35. package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
  36. package/out/zero-cache/src/services/life-cycle.js +1 -1
  37. package/out/zero-cache/src/services/litestream/commands.js +2 -2
  38. package/out/zero-cache/src/services/plannerz.js +2 -2
  39. package/out/zero-cache/src/services/replicator/change-log-db.js +1 -1
  40. package/out/zero-cache/src/services/replicator/incremental-sync.js +3 -3
  41. package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
  42. package/out/zero-cache/src/services/replicator/write-worker.js +3 -3
  43. package/out/zero-cache/src/services/run-ast.js +1 -1
  44. package/out/zero-cache/src/services/statz.js +2 -2
  45. package/out/zero-cache/src/services/view-syncer/cvr-store.js +1 -1
  46. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
  47. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +5 -5
  48. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +1 -1
  49. package/out/zero-cache/src/services/view-syncer/snapshotter.js +1 -1
  50. package/out/zero-cache/src/services/view-syncer/view-syncer.js +6 -6
  51. package/out/zero-cache/src/types/profiler.js +1 -1
  52. package/out/zero-client/src/client/version.js +1 -1
  53. package/out/zql/src/ivm/cap.d.ts +1 -1
  54. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  55. package/out/zql/src/ivm/cap.js +10 -0
  56. package/out/zql/src/ivm/cap.js.map +1 -1
  57. package/out/zql/src/ivm/data.d.ts +15 -1
  58. package/out/zql/src/ivm/data.d.ts.map +1 -1
  59. package/out/zql/src/ivm/data.js +15 -1
  60. package/out/zql/src/ivm/data.js.map +1 -1
  61. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  62. package/out/zql/src/ivm/exists.js +12 -4
  63. package/out/zql/src/ivm/exists.js.map +1 -1
  64. package/out/zql/src/ivm/filter-operators.d.ts +5 -0
  65. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  66. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  67. package/out/zql/src/ivm/join.d.ts.map +1 -1
  68. package/out/zql/src/ivm/join.js +6 -3
  69. package/out/zql/src/ivm/join.js.map +1 -1
  70. package/out/zql/src/ivm/memory-source.js +1 -1
  71. package/out/zql/src/ivm/operator.d.ts +6 -0
  72. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  73. package/out/zql/src/ivm/operator.js.map +1 -1
  74. package/out/zql/src/ivm/take.d.ts.map +1 -1
  75. package/out/zql/src/ivm/take.js +4 -3
  76. package/out/zql/src/ivm/take.js.map +1 -1
  77. package/out/zqlite/src/table-source.js +1 -1
  78. package/package.json +21 -22
  79. package/out/analyze-query/src/bin-analyze.d.ts +0 -2
  80. package/out/analyze-query/src/bin-analyze.d.ts.map +0 -1
  81. package/out/analyze-query/src/bin-analyze.js +0 -285
  82. package/out/analyze-query/src/bin-analyze.js.map +0 -1
  83. package/out/zero/src/analyze-query.d.ts +0 -3
  84. package/out/zero/src/analyze-query.d.ts.map +0 -1
  85. package/out/zero/src/analyze-query.js +0 -3
@@ -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 { compareValues, valueToKey } from "./data.js";
5
6
  import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
6
- import { compareValues } from "./data.js";
7
7
  //#region ../zql/src/ivm/take.ts
8
8
  /**
9
9
  * The Take operator is for implementing limit queries. It takes the first n
@@ -27,8 +27,8 @@ var Take = class {
27
27
  /**
28
28
  * Partitions that incurred removals in Phase 1 and require deficit refills in
29
29
  * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching
30
- * `#storage` keys: `'["take"]'` when unpartitioned, or `'["take", ...partitionValues]'`
31
- * when partitioned.
30
+ * `#storage` keys: `'["take"]'` when unpartitioned, `'take:' + valueToKey(value)` for one
31
+ * partition column, or `'["take", ...partitionValues]'` for more.
32
32
  */
33
33
  #dirtyPartitions = /* @__PURE__ */ new Map();
34
34
  #output = throwOutput;
@@ -414,6 +414,7 @@ var Take = class {
414
414
  }
415
415
  };
416
416
  function getTakeStateKey(partitionKey, rowOrConstraint) {
417
+ if (partitionKey?.length === 1 && rowOrConstraint) return "take:" + valueToKey(rowOrConstraint[partitionKey[0]]);
417
418
  const partitionValues = [];
418
419
  if (partitionKey && rowOrConstraint) for (const key of partitionKey) partitionValues.push(rowOrConstraint[key]);
419
420
  return JSON.stringify(["take", ...partitionValues]);
@@ -1 +1 @@
1
- {"version":3,"file":"take.js","names":[],"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 {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} 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 InputBase,\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 type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n destroy(): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\ntype DirtyPartitionState = {\n constraint: Constraint | undefined;\n /**\n * The bound to report to upstream TakeGate via getBound() until Phase 2\n * reconciliation completes.\n *\n * When removals reduce partition size below limit in Phase 1, normal getBound()\n * would return undefined (unbounded), which would cause upstream TakeGate to open\n * and allow unbounded parent pushes through. Preserving gateBound keeps TakeGate\n * capped at the pre-removal boundary until Phase 2 refills the deficit.\n */\n gateBound: Row | undefined;\n};\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, TakeBoundProvider {\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 #takeGate: TakeGate | undefined;\n /**\n * Partitions that incurred removals in Phase 1 and require deficit refills in\n * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching\n * `#storage` keys: `'[\"take\"]'` when unpartitioned, or `'[\"take\", ...partitionValues]'`\n * when partitioned.\n */\n readonly #dirtyPartitions = new Map<string, DirtyPartitionState>();\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\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 getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const dirty = this.#dirtyPartitions.get(takeStateKey);\n if (dirty) {\n // While dirty in Phase 1, return gateBound (the bound prior to removals) so\n // upstream operators (e.g. TakeGate) remain capped rather than unbounding during push.\n return dirty.gateBound;\n }\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req, takeStateKey);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\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 count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(\n req: FetchRequest,\n takeStateKey: string,\n ): 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 // The caller (fetch) has already checked that there is no state for\n // takeStateKey.\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(takeStateKey, size, bound);\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 constraint: Constraint | undefined;\n if (takeState) {\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, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\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 {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n if (this.#dirtyPartitions.has(takeStateKey)) {\n // While dirty with an unresolved deficit in Phase 1, only admit rows that\n // sort strictly before the current window boundary. We refuse to expand the\n // bound forward during Phase 1 so that Phase 2 can backfill earlier candidate\n // rows from storage in sort order.\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n }\n const nextSize = takeState.size + 1;\n if (nextSize === this.#limit) {\n this.#dirtyPartitions.delete(takeStateKey);\n }\n this.#setTakeState(\n takeStateKey,\n nextSize,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.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 = makeRemoveChange(boundNode);\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[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\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 const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n if (!this.#dirtyPartitions.has(takeStateKey)) {\n // Snapshot the pre-removal bound as the gate bound on the first removal that\n // dirties this partition. Subsequent getBound() calls throughout Phase 1 will return\n // this bound to keep upstream TakeGate bounded until Phase 2 refills deficits.\n this.#dirtyPartitions.set(takeStateKey, {\n constraint,\n gateBound: takeState.bound,\n });\n }\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.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(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\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(takeStateKey, takeState.size, beforeBoundNode.row);\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 this.#takeGate?.open();\n try {\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 } finally {\n this.#takeGate?.close();\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[ChangeIndex.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(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\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(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), 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 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 this.#takeGate?.open();\n try {\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 } finally {\n this.#takeGate?.close();\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[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\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(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n this.#storage.destroy();\n }\n\n *reconcile(_pusher?: InputBase): Stream<'yield'> {\n if (this.#dirtyPartitions.size > 0) {\n const dirty = [...this.#dirtyPartitions.entries()];\n this.#dirtyPartitions.clear();\n\n for (const [takeStateKey, {constraint}] of dirty) {\n const takeState = this.#storage.get(takeStateKey);\n assert(\n takeState !== undefined,\n 'Take: dirty partition must exist in storage',\n );\n const deficit = this.#limit - takeState.size;\n assert(deficit > 0, 'Take: dirty partition must have a deficit');\n\n const toPush: Node[] = [];\n this.#takeGate?.open();\n try {\n const stream = this.#input.fetch({\n start: takeState.bound\n ? {\n row: takeState.bound,\n basis: 'after',\n }\n : undefined,\n constraint,\n });\n\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n toPush.push(node);\n if (toPush.length === deficit) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n\n let currentSize = takeState.size;\n for (const node of toPush) {\n currentSize++;\n this.#setTakeState(takeStateKey, currentSize, node.row);\n yield* this.#output.push(makeAddChange(node), this);\n }\n }\n }\n\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\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 constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\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":[],"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 {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, valueToKey, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\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 type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n destroy(): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\ntype DirtyPartitionState = {\n constraint: Constraint | undefined;\n /**\n * The bound to report to upstream TakeGate via getBound() until Phase 2\n * reconciliation completes.\n *\n * When removals reduce partition size below limit in Phase 1, normal getBound()\n * would return undefined (unbounded), which would cause upstream TakeGate to open\n * and allow unbounded parent pushes through. Preserving gateBound keeps TakeGate\n * capped at the pre-removal boundary until Phase 2 refills the deficit.\n */\n gateBound: Row | undefined;\n};\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, TakeBoundProvider {\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 #takeGate: TakeGate | undefined;\n /**\n * Partitions that incurred removals in Phase 1 and require deficit refills in\n * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching\n * `#storage` keys: `'[\"take\"]'` when unpartitioned, `'take:' + valueToKey(value)` for one\n * partition column, or `'[\"take\", ...partitionValues]'` for more.\n */\n readonly #dirtyPartitions = new Map<string, DirtyPartitionState>();\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\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 getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const dirty = this.#dirtyPartitions.get(takeStateKey);\n if (dirty) {\n // While dirty in Phase 1, return gateBound (the bound prior to removals) so\n // upstream operators (e.g. TakeGate) remain capped rather than unbounding during push.\n return dirty.gateBound;\n }\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req, takeStateKey);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\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 count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(\n req: FetchRequest,\n takeStateKey: string,\n ): 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 // The caller (fetch) has already checked that there is no state for\n // takeStateKey.\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(takeStateKey, size, bound);\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 constraint: Constraint | undefined;\n if (takeState) {\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, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\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 {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n if (this.#dirtyPartitions.has(takeStateKey)) {\n // While dirty with an unresolved deficit in Phase 1, only admit rows that\n // sort strictly before the current window boundary. We refuse to expand the\n // bound forward during Phase 1 so that Phase 2 can backfill earlier candidate\n // rows from storage in sort order.\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n }\n const nextSize = takeState.size + 1;\n if (nextSize === this.#limit) {\n this.#dirtyPartitions.delete(takeStateKey);\n }\n this.#setTakeState(\n takeStateKey,\n nextSize,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.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 = makeRemoveChange(boundNode);\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[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\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 const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n if (!this.#dirtyPartitions.has(takeStateKey)) {\n // Snapshot the pre-removal bound as the gate bound on the first removal that\n // dirties this partition. Subsequent getBound() calls throughout Phase 1 will return\n // this bound to keep upstream TakeGate bounded until Phase 2 refills deficits.\n this.#dirtyPartitions.set(takeStateKey, {\n constraint,\n gateBound: takeState.bound,\n });\n }\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.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(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\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(takeStateKey, takeState.size, beforeBoundNode.row);\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 this.#takeGate?.open();\n try {\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 } finally {\n this.#takeGate?.close();\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[ChangeIndex.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(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\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(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), 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 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 this.#takeGate?.open();\n try {\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 } finally {\n this.#takeGate?.close();\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[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\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(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n this.#storage.destroy();\n }\n\n *reconcile(_pusher?: InputBase): Stream<'yield'> {\n if (this.#dirtyPartitions.size > 0) {\n const dirty = [...this.#dirtyPartitions.entries()];\n this.#dirtyPartitions.clear();\n\n for (const [takeStateKey, {constraint}] of dirty) {\n const takeState = this.#storage.get(takeStateKey);\n assert(\n takeState !== undefined,\n 'Take: dirty partition must exist in storage',\n );\n const deficit = this.#limit - takeState.size;\n assert(deficit > 0, 'Take: dirty partition must have a deficit');\n\n const toPush: Node[] = [];\n this.#takeGate?.open();\n try {\n const stream = this.#input.fetch({\n start: takeState.bound\n ? {\n row: takeState.bound,\n basis: 'after',\n }\n : undefined,\n constraint,\n });\n\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n toPush.push(node);\n if (toPush.length === deficit) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n\n let currentSize = takeState.size;\n for (const node of toPush) {\n currentSize++;\n this.#setTakeState(takeStateKey, currentSize, node.row);\n yield* this.#output.push(makeAddChange(node), this);\n }\n }\n }\n\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n if (partitionKey?.length === 1 && rowOrConstraint) {\n return 'take:' + valueToKey(rowOrConstraint[partitionKey[0]]);\n }\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 constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\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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"}
@@ -2,8 +2,8 @@ import { assert, unreachable } from "../../shared/src/asserts.js";
2
2
  import { getOrInsertComputed } from "../../shared/src/map.js";
3
3
  import { must } from "../../shared/src/must.js";
4
4
  import { assignProperty } from "../../shared/src/objects.js";
5
- import { assertOrderingIncludesPK } from "../../zql/src/query/complete-ordering.js";
6
5
  import { makeComparator } from "../../zql/src/ivm/data.js";
6
+ import { assertOrderingIncludesPK } from "../../zql/src/query/complete-ordering.js";
7
7
  import { createPredicate, transformFilters } from "../../zql/src/builder/filter.js";
8
8
  import { genPushAndWriteWithSplitEdit, generateWithOverlay, generateWithOverlayUnordered, generateWithStart } from "../../zql/src/ivm/memory-source.js";
9
9
  import { compile, format, sql } from "./internal/sql.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rocicorp/zero",
3
- "version": "1.12.0-canary.4",
3
+ "version": "1.12.0-canary.5",
4
4
  "description": "Zero is a web framework for serverless web development.",
5
5
  "homepage": "https://zero.rocicorp.dev",
6
6
  "bugs": {
@@ -14,7 +14,6 @@
14
14
  "directory": "packages/zero"
15
15
  },
16
16
  "bin": {
17
- "analyze-query": "./out/zero/src/analyze-query.js",
18
17
  "ast-to-zql": "./out/zero/src/ast-to-zql.js",
19
18
  "transform-query": "./out/zero/src/transform-query.js",
20
19
  "zero-build-schema": "./out/zero/src/build-schema.js",
@@ -123,10 +122,10 @@
123
122
  "@badrap/valita": "0.5.5",
124
123
  "@databases/escape-identifier": "^1.0.3",
125
124
  "@databases/sql": "^3.3.0",
126
- "@dotenvx/dotenvx": "^1.39.0",
125
+ "@dotenvx/dotenvx": "^1.66.0",
127
126
  "@drdgvhbh/postgres-error-codes": "^0.0.6",
128
- "@fastify/cors": "^10.0.0",
129
- "@fastify/websocket": "^11.0.0",
127
+ "@fastify/cors": "^10.1.0",
128
+ "@fastify/websocket": "^11.2.0",
130
129
  "@google-cloud/precise-date": "^4.0.0",
131
130
  "@opentelemetry/api": "^1.9.1",
132
131
  "@opentelemetry/api-logs": "^0.223.0",
@@ -142,45 +141,45 @@
142
141
  "@rocicorp/logger": "^6.1.0",
143
142
  "@rocicorp/resolver": "^1.0.2",
144
143
  "@rocicorp/zero-sqlite3": "^1.1.7",
145
- "@standard-schema/spec": "^1.0.0",
144
+ "@standard-schema/spec": "^1.1.0",
146
145
  "@types/basic-auth": "^1.1.8",
147
- "@types/ws": "^8.5.12",
146
+ "@types/ws": "^8.18.1",
148
147
  "basic-auth": "^2.0.1",
149
- "chalk-template": "^1.1.0",
150
- "chokidar": "^4.0.1",
148
+ "chalk-template": "^1.1.2",
149
+ "chokidar": "^4.0.3",
151
150
  "cloudevents": "^10.0.0",
152
- "command-line-args": "^6.0.1",
153
- "command-line-usage": "^7.0.3",
151
+ "command-line-args": "^6.0.2",
152
+ "command-line-usage": "^7.0.4",
154
153
  "compare-utf8": "^0.2.3",
155
- "defu": "^6.1.4",
156
- "eventemitter3": "^5.0.1",
154
+ "defu": "^6.1.7",
155
+ "eventemitter3": "^5.0.4",
157
156
  "fastify": "^5.12.7",
158
157
  "is-in-subnet": "^4.0.1",
159
- "jose": "^5.9.3",
158
+ "jose": "^5.10.0",
160
159
  "json-custom-numbers": "^3.1.1",
161
- "kasi": "^1.1.0",
160
+ "kasi": "^1.1.2",
162
161
  "nanoid": "^5.1.16",
163
- "oxfmt": "^0.65.0",
162
+ "oxc-parser": "^0.129.0",
163
+ "oxfmt": "^0.74.0",
164
164
  "parse-prometheus-text-format": "^1.1.1",
165
165
  "pg-format": "npm:pg-format-fix@^1.0.5",
166
166
  "postgres": "3.4.7",
167
- "semver": "^7.5.4",
167
+ "semver": "^7.8.0",
168
168
  "url-pattern": "^1.0.3",
169
169
  "urlpattern-polyfill": "^10.1.0",
170
170
  "ws": "^8.22.0"
171
171
  },
172
172
  "devDependencies": {
173
- "@op-engineering/op-sqlite": ">=15",
173
+ "@op-engineering/op-sqlite": "^18.2.5",
174
174
  "analyze-query": "workspace:*",
175
175
  "ast-to-zql": "workspace:*",
176
176
  "expo": "^58.0.14",
177
- "expo-sqlite": ">=15",
178
- "oxc-parser": "^0.129.0",
177
+ "expo-sqlite": "^58.0.13",
179
178
  "react-native": "^0.88.0",
180
179
  "replicache": "workspace:*",
181
180
  "shared": "workspace:*",
182
- "syncpack": "^14.3.0",
183
- "typedoc": "^0.28.17",
181
+ "syncpack": "^14.3.1",
182
+ "typedoc": "^0.28.19",
184
183
  "typescript": "~7.0.2",
185
184
  "vite": "^8.3.4",
186
185
  "vitest": "^5.0.0",
@@ -1,2 +0,0 @@
1
- import '../../shared/src/dotenv.ts';
2
- //# sourceMappingURL=bin-analyze.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bin-analyze.d.ts","sourceRoot":"","sources":["../../../../analyze-query/src/bin-analyze.ts"],"names":[],"mappings":"AAAA,OAAO,4BAA4B,CAAC"}
@@ -1,285 +0,0 @@
1
- import { valita_exports } from "../../shared/src/valita.js";
2
- import { getOrInsertComputed } from "../../shared/src/map.js";
3
- import { must } from "../../shared/src/must.js";
4
- import "../../zero-protocol/src/ast.js";
5
- import { clientToServer } from "../../zero-schema/src/name-mapper.js";
6
- import { asQueryInternals } from "../../zql/src/query/query-internals.js";
7
- import { newQuery } from "../../zql/src/query/query-impl.js";
8
- import { clientSchemaFrom } from "../../zero-schema/src/builder/schema-builder.js";
9
- import "../../zql/src/query/query.js";
10
- import { QueryDelegateBase } from "../../zql/src/query/query-delegate-base.js";
11
- import "../../shared/src/dotenv.js";
12
- import { logLevel, logOptions } from "../../otel/src/log-options.js";
13
- import { colorConsole, createLogContext } from "../../shared/src/logging.js";
14
- import { parseOptions } from "../../shared/src/options.js";
15
- import { Debug, runtimeDebugFlags } from "../../zql/src/builder/debug-delegate.js";
16
- import { getShardID, upstreamSchema } from "../../zero-cache/src/types/shards.js";
17
- import { ZERO_ENV_VAR_PREFIX, appOptions, shardOptions, zeroOptions } from "../../zero-cache/src/config/zero-config.js";
18
- import { Database } from "../../zqlite/src/db.js";
19
- import { TableSource } from "../../zqlite/src/table-source.js";
20
- import { astToZQL } from "../../ast-to-zql/src/ast-to-zql.js";
21
- import { formatOutput } from "../../ast-to-zql/src/format.js";
22
- import { computeZqlSpecs, mustGetTableSpec } from "../../zero-cache/src/db/lite-tables.js";
23
- import { deployPermissionsOptions, loadSchemaAndPermissions } from "../../zero-cache/src/scripts/permissions.js";
24
- import { runAst } from "../../zero-cache/src/services/run-ast.js";
25
- import { pgClient } from "../../zero-cache/src/types/pg.js";
26
- import { explainQueries } from "../../zqlite/src/explain-queries.js";
27
- import { styleText } from "node:util";
28
- import fs from "node:fs";
29
- //#region ../analyze-query/src/bin-analyze.ts
30
- var options = {
31
- schema: deployPermissionsOptions.schema,
32
- replicaFile: {
33
- ...zeroOptions.replica.file,
34
- desc: ["File path to the SQLite replica to test queries against."]
35
- },
36
- ast: {
37
- type: valita_exports.string().optional(),
38
- desc: ["AST for the query to be analyzed. Only one of ast/query/hash should be provided."]
39
- },
40
- query: {
41
- type: valita_exports.string().optional(),
42
- desc: ["Query to be analyzed in the form of: table.where(...).related(...).etc. ", "Only one of ast/query/hash should be provided."]
43
- },
44
- hash: {
45
- type: valita_exports.string().optional(),
46
- desc: [
47
- "Hash of the query to be analyzed. This is used to look up the query in the database. ",
48
- "Only one of ast/query/hash should be provided.",
49
- "You should run this script from the directory containing your .env file to reduce the amount of",
50
- "configuration required. The .env file should contain the connection URL to the CVR database."
51
- ]
52
- },
53
- applyPermissions: {
54
- type: valita_exports.boolean().optional(() => false),
55
- desc: ["Whether to apply permissions (from your schema file) to the provided query."]
56
- },
57
- authData: {
58
- type: valita_exports.string().optional(),
59
- desc: ["JSON encoded payload of the auth data.", "This will be used to fill permission variables if the \"applyPermissions\" option is set"]
60
- },
61
- outputVendedRows: {
62
- type: valita_exports.boolean().optional(() => false),
63
- desc: ["Whether to output the rows which were read from the replica in order to execute the analyzed query. ", "If the same row is read more than once it will be logged once for each time it was read."]
64
- },
65
- outputSyncedRows: {
66
- type: valita_exports.boolean().optional(() => false),
67
- desc: ["Whether to output the rows which would be synced to the client for the analyzed query."]
68
- },
69
- cvr: { db: {
70
- type: valita_exports.string().optional(),
71
- desc: ["Connection URL to the CVR database. If using --hash, either this or --upstream-db", "must be specified."]
72
- } },
73
- upstream: {
74
- db: {
75
- desc: [`Connection URL to the "upstream" authoritative postgres database. If using --hash, `, "either this or --cvr-db must be specified."],
76
- type: valita_exports.string().optional()
77
- },
78
- type: zeroOptions.upstream.type
79
- },
80
- app: appOptions,
81
- shard: shardOptions,
82
- enableCorrelatedPredicatePushdown: zeroOptions.enableCorrelatedPredicatePushdown,
83
- log: {
84
- ...logOptions,
85
- level: logLevel.optional(() => "error")
86
- }
87
- };
88
- var cfg = parseOptions(options, {
89
- argv: process.argv.slice(2).map((s) => s.replaceAll("\n", " ")),
90
- envNamePrefix: ZERO_ENV_VAR_PREFIX,
91
- description: [{
92
- header: "analyze-query",
93
- content: `Analyze a ZQL query and show information about how it runs against a SQLite replica.
94
-
95
- analyze-query uses the same environment variables and flags as zero-cache-dev. If run from your development environment, it will pick up your ZERO_REPLICA_FILE, ZERO_SCHEMA_PATH, and other env vars automatically.
96
-
97
- If run in another environment (e.g., production) you will have to specify these flags. In particular, you must have a copy of the appropriate Zero schema file to give to the --schema-path flag.`
98
- }, {
99
- header: "Examples",
100
- content: `# In development
101
- npx analyze-query --query='issue.related("comments").limit(10)'
102
- npx analyze-query --ast='\\{"table": "artist","limit": 10\\}'
103
- npx analyze-query --hash=1234567890
104
-
105
- # In production
106
- # First copy schema.ts to your production environment, then run:
107
- npx analyze-query \\
108
- --schema-path='./schema.ts' \\
109
- --replica-file='/path/to/replica.db' \\
110
- --query='issue.related("comments").limit(10)'
111
-
112
- npx analyze-query \\
113
- --schema-path='./schema.ts' \\
114
- --replica-file='/path/to/replica.db' \\
115
- --ast='\\{"table": "artist","limit": 10\\}'
116
-
117
- # cvr-db is required when using the hash option.
118
- # It is typically the same as your upstream db.
119
- npx analyze-query \\
120
- --schema-path='./schema.ts' \\
121
- --replica-file='/path/to/replica.db' \\
122
- --cvr-db='postgres://user:pass@host:port/db' \\
123
- --hash=1234567890
124
- `
125
- }]
126
- });
127
- var config = {
128
- ...cfg,
129
- cvr: {
130
- ...cfg.cvr,
131
- db: cfg.cvr.db ?? cfg.upstream.db
132
- }
133
- };
134
- runtimeDebugFlags.trackRowCountsVended = true;
135
- runtimeDebugFlags.trackRowsVended = config.outputVendedRows;
136
- var lc = createLogContext({ log: config.log });
137
- colorConsole.warn("DEPRECATED: `analyze-query` is deprecated. Please migrate to a project-specific analyze command built with `runAnalyzeCLI` from `@rocicorp/zero/analyze`.");
138
- if (!fs.existsSync(config.replicaFile)) {
139
- colorConsole.error(`Replica file ${config.replicaFile} does not exist`);
140
- process.exit(1);
141
- }
142
- var db = new Database(lc, config.replicaFile);
143
- var { schema, permissions } = await loadSchemaAndPermissions(config.schema.path);
144
- var clientSchema = clientSchemaFrom(schema).clientSchema;
145
- var sources = /* @__PURE__ */ new Map();
146
- var clientToServerMapper = clientToServer(schema.tables);
147
- var debug = new Debug(config.outputVendedRows);
148
- var tableSpecs = computeZqlSpecs(lc, db, { includeBackfillingColumns: false });
149
- var AnalyzeQueryDelegate = class extends QueryDelegateBase {
150
- debug = debug;
151
- defaultQueryComplete = true;
152
- disableCorrelatedPredicatePushdown = !config.enableCorrelatedPredicatePushdown;
153
- getSource(serverTableName) {
154
- return getOrInsertComputed(sources, serverTableName, (serverTableName) => {
155
- const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);
156
- const { primaryKey } = tableSpec.tableSpec;
157
- return new TableSource(lc, config.log, db, serverTableName, tableSpec.zqlSpec, primaryKey);
158
- });
159
- }
160
- };
161
- var host = new AnalyzeQueryDelegate();
162
- var result;
163
- if (config.ast) result = await runAst(lc, clientSchema, JSON.parse(config.ast), true, {
164
- applyPermissions: config.applyPermissions,
165
- auth: config.authData ? {
166
- type: "jwt",
167
- raw: "",
168
- decoded: JSON.parse(config.authData)
169
- } : void 0,
170
- clientToServerMapper,
171
- permissions,
172
- syncedRows: config.outputSyncedRows,
173
- db,
174
- tableSpecs,
175
- host
176
- }, async () => {});
177
- else if (config.query) result = await runQuery(config.query);
178
- else if (config.hash) result = await runHash(config.hash);
179
- else {
180
- colorConsole.error("No query or AST or hash provided");
181
- process.exit(1);
182
- }
183
- function runQuery(queryString) {
184
- const z = { query: Object.fromEntries(Object.entries(schema.tables).map(([name]) => [name, newQuery(schema, name)])) };
185
- const q = new Function("z", `return z.query.${queryString};`)(z);
186
- const ast = asQueryInternals(q).ast;
187
- return runAst(lc, clientSchema, ast, false, {
188
- applyPermissions: config.applyPermissions,
189
- auth: config.authData ? {
190
- type: "jwt",
191
- raw: "",
192
- decoded: JSON.parse(config.authData)
193
- } : void 0,
194
- clientToServerMapper,
195
- permissions,
196
- syncedRows: config.outputSyncedRows,
197
- db,
198
- tableSpecs,
199
- host
200
- }, async () => {});
201
- }
202
- async function runHash(hash) {
203
- const cvrDB = pgClient(lc, must(config.cvr.db, "CVR DB must be provided when using the hash option"), "analyze-query-hash-cvr");
204
- const rows = await cvrDB`select "clientAST", "internal" from ${cvrDB(upstreamSchema(getShardID(config)) + "/cvr")}."queries" where "queryHash" = ${must(hash)} limit 1;`;
205
- await cvrDB.end();
206
- colorConsole.log("ZQL from Hash:");
207
- const ast = rows[0].clientAST;
208
- colorConsole.log(await formatOutput(ast.table + astToZQL(ast)));
209
- return runAst(lc, clientSchema, ast, true, {
210
- applyPermissions: config.applyPermissions,
211
- auth: config.authData ? {
212
- type: "jwt",
213
- raw: "",
214
- decoded: JSON.parse(config.authData)
215
- } : void 0,
216
- clientToServerMapper,
217
- permissions,
218
- syncedRows: config.outputSyncedRows,
219
- db,
220
- tableSpecs,
221
- host
222
- }, async () => {});
223
- }
224
- if (config.outputSyncedRows) {
225
- colorConsole.log(styleText(["blue", "bold"], "=== Synced Rows: ===\n"));
226
- for (const [table, rows] of Object.entries(result.syncedRows ?? {})) colorConsole.log(styleText("bold", table + ":"), rows);
227
- }
228
- colorConsole.log(styleText(["blue", "bold"], "=== Query Stats: ===\n"));
229
- colorConsole.log(styleText("bold", "total synced rows:"), result.syncedRowCount);
230
- showStats();
231
- if (config.outputVendedRows) {
232
- colorConsole.log(styleText(["blue", "bold"], "=== JS Row Scan Values: ===\n"));
233
- for (const source of sources.values()) colorConsole.log(styleText("bold", `${source.tableSchema.name}:`), debug.getVendedRows()?.[source.tableSchema.name] ?? {});
234
- }
235
- colorConsole.log(styleText(["blue", "bold"], "\n=== Rows Scanned (by SQLite): ===\n"));
236
- var nvisitCounts = debug.getNVisitCounts();
237
- var totalNVisit = 0;
238
- for (const [table, queries] of Object.entries(nvisitCounts)) {
239
- colorConsole.log(styleText("bold", `${table}:`), queries);
240
- for (const count of Object.values(queries)) totalNVisit += count;
241
- }
242
- colorConsole.log(styleText("bold", "total rows scanned:"), colorRowsConsidered(totalNVisit));
243
- colorConsole.log(styleText(["blue", "bold"], "\n\n=== Query Plans: ===\n"));
244
- var fallbackPlans = explainQueries(debug.getVendedRowCounts() ?? {}, db);
245
- var capturedPlans = debug.getSQLitePlans();
246
- var plans = {
247
- ...fallbackPlans,
248
- ...capturedPlans
249
- };
250
- for (const [query, plan] of Object.entries(plans)) {
251
- colorConsole.log(styleText("bold", "query"), query);
252
- colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join("\n"));
253
- colorConsole.log("\n");
254
- }
255
- function showStats() {
256
- let totalRowsConsidered = 0;
257
- for (const source of sources.values()) {
258
- const values = Object.values(debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {});
259
- for (const v of values) totalRowsConsidered += v;
260
- colorConsole.log(styleText("bold", source.tableSchema.name + " vended:"), debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {});
261
- }
262
- colorConsole.log(styleText("bold", "Rows Read (into JS):"), colorRowsConsidered(totalRowsConsidered));
263
- colorConsole.log(styleText("bold", "time:"), colorTime(result.end - result.start), "ms");
264
- }
265
- function colorTime(duration) {
266
- if (duration < 100) return styleText("green", duration.toFixed(2) + "ms");
267
- else if (duration < 1e3) return styleText("yellow", duration.toFixed(2) + "ms");
268
- return styleText("red", duration.toFixed(2) + "ms");
269
- }
270
- function colorRowsConsidered(n) {
271
- if (n < 1e3) return styleText("green", n.toString());
272
- else if (n < 1e4) return styleText("yellow", n.toString());
273
- return styleText("red", n.toString());
274
- }
275
- function colorPlanRow(row, i) {
276
- if (row.includes("SCAN")) {
277
- if (i === 0) return styleText("yellow", row);
278
- return styleText("red", row);
279
- }
280
- return styleText("green", row);
281
- }
282
- //#endregion
283
- export {};
284
-
285
- //# sourceMappingURL=bin-analyze.js.map