@rocicorp/zero 1.4.0 → 1.5.0-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/auth.d.ts +1 -1
- package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
- package/out/zero-cache/src/auth/auth.js +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
- package/out/zero-cache/src/config/normalize.js +8 -0
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +8 -4
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +28 -6
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +2 -2
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts +21 -7
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +26 -9
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +2 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +5 -2
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/syncer.js +3 -3
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +24 -20
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +258 -45
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +119 -83
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +2 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +1 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +3 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/http-service.d.ts +1 -0
- package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/http-service.js +5 -4
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +1 -2
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +4 -3
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +57 -38
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js +2 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +41 -27
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +147 -104
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +6 -0
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +8 -0
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +3 -3
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +119 -86
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +2 -2
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +7 -7
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +11 -10
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts +15 -7
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.d.ts +1 -1
- package/out/zero-client/src/client/crud-impl.d.ts.map +1 -1
- package/out/zero-client/src/client/crud-impl.js +1 -1
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/crud.d.ts +1 -1
- package/out/zero-client/src/client/crud.d.ts.map +1 -1
- package/out/zero-client/src/client/crud.js +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/keys.d.ts +1 -1
- package/out/zero-client/src/client/keys.d.ts.map +1 -1
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts +2 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js +3 -3
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +2 -2
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/types/client-state.d.ts +1 -1
- package/out/zero-client/src/types/client-state.d.ts.map +1 -1
- package/out/zero-protocol/src/custom-queries.js +1 -1
- package/out/zero-protocol/src/down.js +1 -1
- package/out/zero-protocol/src/error-kind-enum.d.ts +1 -2
- package/out/zero-protocol/src/error-kind-enum.d.ts.map +1 -1
- package/out/zero-protocol/src/error-kind-enum.js.map +1 -1
- package/out/zero-protocol/src/mutate-server.d.ts +165 -0
- package/out/zero-protocol/src/mutate-server.d.ts.map +1 -0
- package/out/zero-protocol/src/mutate-server.js +24 -0
- package/out/zero-protocol/src/mutate-server.js.map +1 -0
- package/out/zero-protocol/src/mutation.d.ts +229 -0
- package/out/zero-protocol/src/mutation.d.ts.map +1 -0
- package/out/zero-protocol/src/mutation.js +112 -0
- package/out/zero-protocol/src/mutation.js.map +1 -0
- package/out/zero-protocol/src/mutations-patch.js +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +3 -234
- package/out/zero-protocol/src/push.d.ts.map +1 -1
- package/out/zero-protocol/src/push.js +3 -114
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/query-server.d.ts +150 -0
- package/out/zero-protocol/src/query-server.d.ts.map +1 -0
- package/out/zero-protocol/src/query-server.js +16 -0
- package/out/zero-protocol/src/query-server.js.map +1 -0
- package/out/zero-protocol/src/up.js +1 -1
- package/out/zero-server/src/mod.d.ts +4 -2
- package/out/zero-server/src/mod.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.d.ts +50 -4
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +73 -36
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.d.ts +3 -3
- package/out/zero-server/src/push-processor.d.ts.map +1 -1
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.d.ts +45 -53
- package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +72 -53
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-types/src/default-types.d.ts +1 -0
- package/out/zero-types/src/default-types.d.ts.map +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +17 -7
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +32 -0
- package/out/zql/src/ivm/cap.d.ts.map +1 -0
- package/out/zql/src/ivm/cap.js +205 -0
- package/out/zql/src/ivm/cap.js.map +1 -0
- package/out/zql/src/ivm/constraint.js +1 -1
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +61 -15
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +3 -4
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/schema.d.ts +8 -0
- package/out/zql/src/ivm/schema.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +2 -2
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/mutate/mutator.d.ts +11 -2
- package/out/zql/src/mutate/mutator.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts +9 -2
- package/out/zql/src/query/query-registry.d.ts.map +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +4 -1
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#pushParent","#pushChild","#output","#inprogressChildChange","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport {constraintsAreCompatible} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n // TODO: When parentKey is the parent's primary key (or more\n // generally when the parent cardinality is expected to be small) a different\n // algorithm should be used: For each child node, fetch all parent nodes\n // eagerly and then sort using quicksort.\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n const parentIterators: Iterator<Node | 'yield'>[] = [];\n let threw = false;\n try {\n for (const childNode of childNodes) {\n // TODO: consider adding the ability to pass a set of\n // ids to fetch, and have them applied to sqlite using IN.\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n parentIterators.push(emptyArray[Symbol.iterator]());\n } else {\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n const iterator = stream[Symbol.iterator]();\n parentIterators.push(iterator);\n }\n }\n const nextParentNodes: (Node | null)[] = [];\n for (let i = 0; i < parentIterators.length; i++) {\n const iter = parentIterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n nextParentNodes[i] = result.done ? null : (result.value as Node);\n }\n\n while (true) {\n let minParentNode = null;\n let minParentNodeChildIndexes: number[] = [];\n for (let i = 0; i < nextParentNodes.length; i++) {\n const parentNode = nextParentNodes[i];\n if (parentNode === null) {\n continue;\n }\n if (minParentNode === null) {\n minParentNode = parentNode;\n minParentNodeChildIndexes.push(i);\n } else {\n const compareResult =\n this.#schema.compareRows(parentNode.row, minParentNode.row) *\n (req.reverse ? -1 : 1);\n if (compareResult === 0) {\n minParentNodeChildIndexes.push(i);\n } else if (compareResult < 0) {\n minParentNode = parentNode;\n minParentNodeChildIndexes = [i];\n }\n }\n }\n if (minParentNode === null) {\n return;\n }\n const relatedChildNodes: Node[] = [];\n for (const minParentNodeChildIndex of minParentNodeChildIndexes) {\n relatedChildNodes.push(childNodes[minParentNodeChildIndex]);\n const iter = parentIterators[minParentNodeChildIndex];\n let result = iter.next();\n // yield yields when advancing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n nextParentNodes[minParentNodeChildIndex] = result.done\n ? null\n : (result.value as Node);\n }\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (\n this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE\n ) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove form relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n } catch (e) {\n threw = true;\n for (const iter of parentIterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of parentIterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#parentKeyIsUnique","#pushParent","#pushChild","#output","#inprogressChildChange","#fetchQuicksort","#fetchMergeSort","#yieldParentWithOverlay","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport {constraintsAreCompatible, keyMatchesPrimaryKey} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n readonly #parentKeyIsUnique: boolean;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#parentKeyIsUnique =\n keyMatchesPrimaryKey(parentKey, parentSchema.primaryKey) ||\n (parentSchema.uniqueIndexes?.some(idx =>\n keyMatchesPrimaryKey(parentKey, idx),\n ) ??\n false);\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n\n if (this.#parentKeyIsUnique) {\n yield* this.#fetchQuicksort(req, childNodes);\n } else {\n yield* this.#fetchMergeSort(req, childNodes);\n }\n }\n\n // When parentKey matches a unique index on the parent (primary or\n // otherwise) each child -> parent fetch returns at most one row, so the\n // merge-sort degenerates to N simultaneous prepared-statement iterators\n // each holding a single-row cursor. Instead, fetch sequentially (letting\n // the statement cache reuse a single prepared statement) and sort the\n // resulting parents into order.\n *#fetchQuicksort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const pairs: {childNode: Node; parentNode: Node}[] = [];\n for (const childNode of childNodes) {\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n // parentKey matches a unique index, so this fetch returns at most\n // one row under the Source contract. Iterate to completion rather\n // than breaking to preserve yield propagation and to avoid silently\n // changing behavior if a source ever returns more.\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n pairs.push({childNode, parentNode: node});\n }\n }\n\n const compareRows = this.#schema.compareRows;\n const dir = req.reverse ? -1 : 1;\n pairs.sort((a, b) => compareRows(a.parentNode.row, b.parentNode.row) * dir);\n\n // Group consecutive pairs with equal parent rows. Array.sort is stable,\n // and childNodes was already in child order, so children within each\n // group retain child order.\n let i = 0;\n while (i < pairs.length) {\n const minParentNode = pairs[i].parentNode;\n const relatedChildNodes: Node[] = [];\n while (\n i < pairs.length &&\n compareRows(pairs[i].parentNode.row, minParentNode.row) === 0\n ) {\n relatedChildNodes.push(pairs[i].childNode);\n i++;\n }\n yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);\n }\n }\n\n *#fetchMergeSort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const parentIterators: Iterator<Node | 'yield'>[] = [];\n let threw = false;\n try {\n for (const childNode of childNodes) {\n // TODO: consider adding the ability to pass a set of\n // ids to fetch, and have them applied to sqlite using IN.\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n parentIterators.push(emptyArray[Symbol.iterator]());\n } else {\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n const iterator = stream[Symbol.iterator]();\n parentIterators.push(iterator);\n }\n }\n const nextParentNodes: (Node | null)[] = [];\n for (let i = 0; i < parentIterators.length; i++) {\n const iter = parentIterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n nextParentNodes[i] = result.done ? null : (result.value as Node);\n }\n\n while (true) {\n let minParentNode = null;\n let minParentNodeChildIndexes: number[] = [];\n for (let i = 0; i < nextParentNodes.length; i++) {\n const parentNode = nextParentNodes[i];\n if (parentNode === null) {\n continue;\n }\n if (minParentNode === null) {\n minParentNode = parentNode;\n minParentNodeChildIndexes.push(i);\n } else {\n const compareResult =\n this.#schema.compareRows(parentNode.row, minParentNode.row) *\n (req.reverse ? -1 : 1);\n if (compareResult === 0) {\n minParentNodeChildIndexes.push(i);\n } else if (compareResult < 0) {\n minParentNode = parentNode;\n minParentNodeChildIndexes = [i];\n }\n }\n }\n if (minParentNode === null) {\n return;\n }\n const relatedChildNodes: Node[] = [];\n for (const minParentNodeChildIndex of minParentNodeChildIndexes) {\n relatedChildNodes.push(childNodes[minParentNodeChildIndex]);\n const iter = parentIterators[minParentNodeChildIndex];\n let result = iter.next();\n // yield yields when advancing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n nextParentNodes[minParentNodeChildIndex] = result.done\n ? null\n : (result.value as Node);\n }\n yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);\n }\n } catch (e) {\n threw = true;\n for (const iter of parentIterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of parentIterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n }\n\n *#yieldParentWithOverlay(\n minParentNode: Node,\n relatedChildNodes: Node[],\n ): Stream<Node> {\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove from relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmDA,IAAa,cAAb,MAA0C;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,UAAkB;CAElB;CACA;CAEA,YAAY,EACV,QACA,OACA,WACA,UACA,kBACA,QACA,UACO;AACP,SAAO,WAAW,OAAO,+CAA+C;AACxE,SACE,UAAU,WAAW,SAAS,QAC9B,wDACD;AACD,QAAA,SAAe;AACf,QAAA,QAAc;AACd,QAAA,YAAkB;AAClB,QAAA,WAAiB;AACjB,QAAA,mBAAyB;EAEzB,MAAM,eAAe,OAAO,WAAW;EACvC,MAAM,cAAc,MAAM,WAAW;AACrC,QAAA,oBACE,qBAAqB,WAAW,aAAa,WAAW,KACvD,aAAa,eAAe,MAAK,QAChC,qBAAqB,WAAW,IAAI,CACrC,IACC;AACJ,QAAA,SAAe;GACb,GAAG;GACH,eAAe;IACb,GAAG,aAAa;KACf,mBAAmB;KAClB,GAAG;KACH,UAAU;KACV;KACD;IACF;GACF;AAED,SAAO,UAAU,EACf,OAAO,WAAmB,MAAA,WAAiB,OAAO,EACnD,CAAC;AACF,QAAM,UAAU,EACd,OAAO,WAAmB,MAAA,UAAgB,OAAO,EAClD,CAAC;;CAGJ,UAAgB;AACd,QAAA,MAAY,SAAS;AACrB,QAAA,OAAa,SAAS;;CAGxB,UAAU,QAAsB;AAC9B,QAAA,SAAe;;CAGjB,YAA0B;AACxB,SAAO,MAAA;;CAGT,CAAC,MAAM,KAA2C;EAGhD,MAAM,kBAAyC,EAAE;EACjD,IAAI,qBAAqB;AACzB,MAAI,IAAI,WACN,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,WAAW,EAAE;GACzD,MAAM,QAAQ,MAAA,UAAgB,QAAQ,IAAI;AAC1C,OAAI,UAAU,IAAI;AAChB,yBAAqB;AACrB,oBAAgB,MAAA,SAAe,UAAU;;;EAK/C,MAAM,aAAqB,EAAE;AAC7B,OAAK,MAAM,QAAQ,MAAA,MAAY,MAC7B,qBAAqB,EAAC,YAAY,iBAAgB,GAAG,EAAE,CACxD,EAAE;AACD,OAAI,SAAS,SAAS;AACpB,UAAM;AACN;;AAEF,cAAW,KAAK,KAAK;;AAWvB,MAAI,MAAA,wBAA8B,OAAsB,GAAmB;GACzE,MAAM,cAAc,MAAA,sBAA4B;GAChD,MAAM,UAAU,MAAA,MAAY,WAAW,CAAC;GACxC,MAAM,YAAY,aAAa,WAAW,SAAQ,MAChD,QAAQ,YAAY,KAAK,WAAW,GAAG,IAAI,CAC5C;AACD,cAAW,OAAO,WAAW,GAAG,YAAY;;AAG9C,MAAI,MAAA,kBACF,QAAO,MAAA,eAAqB,KAAK,WAAW;MAE5C,QAAO,MAAA,eAAqB,KAAK,WAAW;;CAUhD,EAAA,eACE,KACA,YACwB;EACxB,MAAM,QAA+C,EAAE;AACvD,OAAK,MAAM,aAAa,YAAY;GAClC,MAAM,sBAAsB,oBAC1B,UAAU,KACV,MAAA,UACA,MAAA,UACD;AACD,OACE,CAAC,uBACA,IAAI,cACH,CAAC,yBAAyB,qBAAqB,IAAI,WAAW,CAEhE;GAEF,MAAM,SAAS,MAAA,OAAa,MAAM;IAChC,GAAG;IACH,YAAY;KACV,GAAG,IAAI;KACP,GAAG;KACJ;IACF,CAAC;AAKF,QAAK,MAAM,QAAQ,QAAQ;AACzB,QAAI,SAAS,SAAS;AACpB,WAAM;AACN;;AAEF,UAAM,KAAK;KAAC;KAAW,YAAY;KAAK,CAAC;;;EAI7C,MAAM,cAAc,MAAA,OAAa;EACjC,MAAM,MAAM,IAAI,UAAU,KAAK;AAC/B,QAAM,MAAM,GAAG,MAAM,YAAY,EAAE,WAAW,KAAK,EAAE,WAAW,IAAI,GAAG,IAAI;EAK3E,IAAI,IAAI;AACR,SAAO,IAAI,MAAM,QAAQ;GACvB,MAAM,gBAAgB,MAAM,GAAG;GAC/B,MAAM,oBAA4B,EAAE;AACpC,UACE,IAAI,MAAM,UACV,YAAY,MAAM,GAAG,WAAW,KAAK,cAAc,IAAI,KAAK,GAC5D;AACA,sBAAkB,KAAK,MAAM,GAAG,UAAU;AAC1C;;AAEF,UAAO,MAAA,uBAA6B,eAAe,kBAAkB;;;CAIzE,EAAA,eACE,KACA,YACwB;EACxB,MAAM,kBAA8C,EAAE;EACtD,IAAI,QAAQ;AACZ,MAAI;AACF,QAAK,MAAM,aAAa,YAAY;IAGlC,MAAM,sBAAsB,oBAC1B,UAAU,KACV,MAAA,UACA,MAAA,UACD;AACD,QACE,CAAC,uBACA,IAAI,cACH,CAAC,yBAAyB,qBAAqB,IAAI,WAAW,CAEhE,iBAAgB,KAAK,WAAW,OAAO,WAAW,CAAC;SAC9C;KAQL,MAAM,WAPS,MAAA,OAAa,MAAM;MAChC,GAAG;MACH,YAAY;OACV,GAAG,IAAI;OACP,GAAG;OACJ;MACF,CAAC,CACsB,OAAO,WAAW;AAC1C,qBAAgB,KAAK,SAAS;;;GAGlC,MAAM,kBAAmC,EAAE;AAC3C,QAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;IAC/C,MAAM,OAAO,gBAAgB;IAC7B,IAAI,SAAS,KAAK,MAAM;AAExB,WAAO,CAAC,OAAO,QAAQ,OAAO,UAAU,SAAS;AAC/C,WAAM,OAAO;AACb,cAAS,KAAK,MAAM;;AAEtB,oBAAgB,KAAK,OAAO,OAAO,OAAQ,OAAO;;AAGpD,UAAO,MAAM;IACX,IAAI,gBAAgB;IACpB,IAAI,4BAAsC,EAAE;AAC5C,SAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;KAC/C,MAAM,aAAa,gBAAgB;AACnC,SAAI,eAAe,KACjB;AAEF,SAAI,kBAAkB,MAAM;AAC1B,sBAAgB;AAChB,gCAA0B,KAAK,EAAE;YAC5B;MACL,MAAM,gBACJ,MAAA,OAAa,YAAY,WAAW,KAAK,cAAc,IAAI,IAC1D,IAAI,UAAU,KAAK;AACtB,UAAI,kBAAkB,EACpB,2BAA0B,KAAK,EAAE;eACxB,gBAAgB,GAAG;AAC5B,uBAAgB;AAChB,mCAA4B,CAAC,EAAE;;;;AAIrC,QAAI,kBAAkB,KACpB;IAEF,MAAM,oBAA4B,EAAE;AACpC,SAAK,MAAM,2BAA2B,2BAA2B;AAC/D,uBAAkB,KAAK,WAAW,yBAAyB;KAC3D,MAAM,OAAO,gBAAgB;KAC7B,IAAI,SAAS,KAAK,MAAM;AAExB,YAAO,CAAC,OAAO,QAAQ,OAAO,UAAU,SAAS;AAC/C,YAAM,OAAO;AACb,eAAS,KAAK,MAAM;;AAEtB,qBAAgB,2BAA2B,OAAO,OAC9C,OACC,OAAO;;AAEd,WAAO,MAAA,uBAA6B,eAAe,kBAAkB;;WAEhE,GAAG;AACV,WAAQ;AACR,QAAK,MAAM,QAAQ,gBACjB,KAAI;AACF,SAAK,QAAQ,EAAE;YACR,eAAe;AAK1B,SAAM;YACE;AACR,OAAI,CAAC,MACH,MAAK,MAAM,QAAQ,gBACjB,KAAI;AACF,SAAK,UAAU;YACR,eAAe;;;CAShC,EAAA,uBACE,eACA,mBACc;EACd,IAAI,4BAA4B;AAChC,MACE,MAAA,yBACA,MAAA,iCACA,YACE,MAAA,sBAA4B,GAAkB,KAC9C,MAAA,UACA,cAAc,KACd,MAAA,UACD,EACD;GACA,MAAM,qDACJ,MAAA,OACG,WAAW,CACX,YACC,cAAc,KACd,MAAA,8BACD,IAAI;AACT,OAAI,MAAA,sBAA4B,OAAsB;QAChD,mDAGF,6BAA4B,kBAAkB,QAC5C,MAAK,MAAM,MAAA,wBAA8B,GAC1C;cAEM,CAAC,mDACV,6BAA4B,CAC1B,GAAG,2BACD,mBACA,MAAA,uBACA,MAAA,MAAY,WAAW,CACxB,CACF;;AAKL,MAAI,0BAA0B,SAAS,EACrC,OAAM;GACJ,GAAG;GACH,eAAe;IACb,GAAG,cAAc;KAChB,MAAA,yBAA+B;IACjC;GACF;;CAIL,EAAA,UAAY,QAAiC;AAC3C,UAAQ,OAAO,IAAf;GACE,KAAK;GACL,KAAK;AACH,WAAO,MAAA,gBAAsB,OAAO;AACpC;GACF,KAAK;AACH,WACE,wBACE,OAAO,GAAsB,KAC7B,OAAO,GAAkB,KACzB,MAAA,SACD,EACD,2CACD;AACD,WAAO,MAAA,gBAAsB,QAAQ,KAAK;AAC1C;GAEF,KAAK;AACH,WAAO,MAAA,gBAAsB,QAAQ,KAAK;AAC1C;;;CAIN,EAAA,gBAAkB,QAAgB,QAAmC;AACnE,QAAA,wBAA8B;AAC9B,QAAA,gCAAsC,KAAA;AACtC,MAAI;GACF,MAAM,aAAa,oBACjB,OAAO,GAAkB,KACzB,MAAA,UACA,MAAA,UACD;GACD,MAAM,mBAAmB,aACrB,MAAA,OAAa,MAAM,EAAC,YAAW,CAAC,GAChC,EAAE;AACN,QAAK,MAAM,cAAc,kBAAkB;AACzC,QAAI,eAAe,SAAS;AAC1B,WAAM;AACN;;AAEF,UAAA,wBAA8B;AAC9B,UAAA,gCAAsC,WAAW;IACjD,MAAM,wBAAwB;KAC5B,MAAM,aAAa,oBACjB,WAAW,KACX,MAAA,WACA,MAAA,SACD;AACD,YAAO,aAAa,MAAA,MAAY,MAAM,EAAC,YAAW,CAAC,GAAG,EAAE;;AAE1D,QAAI,CAAC,OACH,MAAK,MAAM,aAAa,iBAAiB,EAAE;AACzC,SAAI,cAAc,SAAS;AACzB,YAAM;AACN;;AAEF,SACE,MAAA,MACG,WAAW,CACX,YAAY,UAAU,KAAK,OAAO,GAAkB,IAAI,KAAK,GAChE;AACA,eAAS;AACT;;;AAIN,QAAI,OACF,QAAO,MAAA,OAAa,KAClB,gBACE;KACE,GAAG;KACH,eAAe;MACb,GAAG,WAAW;OACb,MAAA,mBAAyB;MAC3B;KACF,EACD;KACE,kBAAkB,MAAA;KAClB;KACD,CACF,EACD,KACD;SACI;KACL,MAAM,UAAU;MACd,GAAG;MACH,eAAe;OACb,GAAG,WAAW;QACb,MAAA,yBAA+B,CAAC,OAAO,GAAkB;OAC3D;MACF;AACD,YAAO,MAAA,OAAa,KAClB,OAAO,OAAsB,IACzB,cAAc,QAAQ,GACtB,iBAAiB,QAAQ,EAC7B,KACD;;;YAGG;AACR,SAAA,wBAA8B,KAAA;;;CAIlC,EAAA,WAAa,QAAiC;EAC5C,MAAM,mBAAmB,eAAqB;GAC5C,MAAM,aAAa,oBACjB,KAAK,KACL,MAAA,WACA,MAAA,SACD;AACD,UAAO,aAAa,MAAA,MAAY,MAAM,EAAC,YAAW,CAAC,GAAG,EAAE;;EAG1D,MAAM,QAAQ,UAAgB;GAC5B,GAAG;GACH,eAAe;IACb,GAAG,KAAK;KACP,MAAA,mBAAyB,gBAAgB,KAAK;IAChD;GACF;EAGD,IAAI,kBAAkB;AACtB,OAAK,MAAM,QAAQ,gBAAgB,OAAO,GAAkB,EAAE,CAC5D,KAAI,SAAS,SAAS;AACpB,SAAM;AACN;SACK;AACL,qBAAkB;AAClB;;AAGJ,MAAI,CAAC,gBACH;AAGF,UAAQ,OAAO,IAAf;GACE,KAAK;AACH,WAAO,MAAA,OAAa,KAClB,cAAc,KAAK,OAAO,GAAkB,CAAC,EAC7C,KACD;AACD;GACF,KAAK;AACH,WAAO,MAAA,OAAa,KAClB,iBAAiB,KAAK,OAAO,GAAkB,CAAC,EAChD,KACD;AACD;GACF,KAAK;AACH,WAAO,MAAA,OAAa,KAClB,gBACE,KAAK,OAAO,GAAkB,EAC9B,OAAO,GACR,EACD,KACD;AACD;GAEF,KAAK;AACH,WACE,wBACE,OAAO,GAAsB,KAC7B,OAAO,GAAkB,KACzB,MAAA,UACD,EACD,4CACD;AACD,WAAO,MAAA,OAAa,KAClB,eACE,KAAK,OAAO,GAAkB,EAC9B,KAAK,OAAO,GAAsB,CACnC,EACD,KACD;AACD;GAEF,QACE,aAAY,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAC,MAAM,kCAAkC,CAAC;AAI1D,OAAO,KAAK,EACV,SAAS,EACT,QAAQ,EAET,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,GAAG,EAAQ,MAAM,oCAAoC,CAAC;AACnE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAC1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,IAAI,EACV,MAAM,WAAW,CAAC;AAEnB,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,MAAM,EACX,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EAIZ,WAAW,EACZ,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC;IACrB,MAAM,EAAE,GAAG,GAAG,SAAS,CAAC;CACzB,CAAC;AAQF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,UAAU,CAAC;IACxB,OAAO,EACH;QACE,SAAS,EAAE,mBAAmB,CAAC;QAC/B,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;KAClC,GACD,SAAS,CAAC;IACd,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3C,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,qBAAa,YAAa,YAAW,MAAM;;gBAYvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,UAAU,EAAE,UAAU,EACtB,gBAAgB,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC;IAclC,IAAI,WAAW;;;;MAMd;IAED,IAAI;IAUJ,IAAI,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,CAExB;IAeD,OAAO,CACL,IAAI,EAAE,QAAQ,GAAG,SAAS,EAC1B,OAAO,CAAC,EAAE,SAAS,EACnB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,WAAW;
|
|
1
|
+
{"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { assert, unreachable } from "../../../shared/src/asserts.js";
|
|
2
2
|
import { hasOwn } from "../../../shared/src/has-own.js";
|
|
3
|
-
import { once } from "../../../shared/src/iterables.js";
|
|
3
|
+
import { once, toSorted } from "../../../shared/src/iterables.js";
|
|
4
4
|
import { must } from "../../../shared/src/must.js";
|
|
5
5
|
import { skipYields } from "./skip-yields.js";
|
|
6
6
|
import { makeAddChange, makeEditChange, makeRemoveChange } from "./change.js";
|
|
7
|
+
import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
|
|
7
8
|
import { compareValues, makeComparator, valuesEqual } from "./data.js";
|
|
8
9
|
import { filterPush } from "./filter-push.js";
|
|
9
10
|
import { constraintMatchesPrimaryKey, constraintMatchesRow, primaryKeyConstraintFromFilters } from "./constraint.js";
|
|
10
|
-
import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
|
|
11
11
|
import { createPredicate, transformFilters } from "../builder/filter.js";
|
|
12
12
|
import { BTreeSet } from "../../../shared/src/btree-set.js";
|
|
13
13
|
import { makeSourceChangeAdd, makeSourceChangeRemove } from "./source.js";
|
|
@@ -116,8 +116,7 @@ var MemorySource = class MemorySource {
|
|
|
116
116
|
return index;
|
|
117
117
|
}
|
|
118
118
|
const comparator = makeBoundComparator(sort);
|
|
119
|
-
const rows =
|
|
120
|
-
rows.sort(comparator);
|
|
119
|
+
const rows = toSorted(this.#getPrimaryIndex().data, comparator);
|
|
121
120
|
const newIndex = {
|
|
122
121
|
comparator,
|
|
123
122
|
data: BTreeSet.fromSorted(comparator, rows),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-source.js","names":["#tableName","#columns","#primaryKey","#primaryIndexSort","#indexes","#connections","#getPrimaryIndex","#fetch","#disconnect","#getSchema","#getOrCreateIndex","#overlay","#writeChange","#pushEpoch"],"sources":["../../../../../zql/src/ivm/memory-source.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {BTreeSet} from '../../../shared/src/btree-set.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport {once} from '../../../shared/src/iterables.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n Condition,\n Ordering,\n OrderPart,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-types/src/schema-value.ts';\nimport type {DebugDelegate} from '../builder/debug-delegate.ts';\nimport {\n createPredicate,\n transformFilters,\n type NoSubqueryCondition,\n} from '../builder/filter.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {makeAddChange, makeEditChange, makeRemoveChange} from './change.ts';\nimport {\n constraintMatchesPrimaryKey,\n constraintMatchesRow,\n primaryKeyConstraintFromFilters,\n type Constraint,\n} from './constraint.ts';\nimport {\n compareValues,\n makeComparator,\n valuesEqual,\n type Comparator,\n type Node,\n} from './data.ts';\nimport {filterPush} from './filter-push.ts';\nimport {\n skipYields,\n type FetchRequest,\n type Input,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {SourceChangeIndex} from './source-change-index.ts';\nimport type {\n Source,\n SourceChange,\n SourceChangeAdd,\n SourceChangeEdit,\n SourceChangeRemove,\n SourceInput,\n} from './source.ts';\nimport {makeSourceChangeAdd, makeSourceChangeRemove} from './source.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Overlay = {\n epoch: number;\n change: SourceChange;\n};\n\nexport type Overlays = {\n add: Row | undefined;\n remove: Row | undefined;\n};\n\ntype Index = {\n comparator: Comparator;\n data: BTreeSet<Row>;\n usedBy: Set<Connection>;\n};\n\nexport type Connection = {\n input: Input;\n output: Output | undefined;\n sort?: Ordering | undefined;\n splitEditKeys: Set<string> | undefined;\n compareRows: Comparator;\n filters:\n | {\n condition: NoSubqueryCondition;\n predicate: (row: Row) => boolean;\n }\n | undefined;\n readonly debug?: DebugDelegate | undefined;\n lastPushedEpoch: number;\n};\n\n/**\n * A `MemorySource` is a source that provides data to the pipeline from an\n * in-memory data source.\n *\n * This data is kept in sorted order as downstream pipelines will always expect\n * the data they receive from `pull` to be in sorted order.\n */\nexport class MemorySource implements Source {\n readonly #tableName: string;\n readonly #columns: Record<string, SchemaValue>;\n readonly #primaryKey: PrimaryKey;\n readonly #primaryIndexSort: Ordering;\n readonly #indexes: Map<string, Index> = new Map();\n readonly #connections: Connection[] = [];\n\n #overlay: Overlay | undefined;\n #pushEpoch = 0;\n\n constructor(\n tableName: string,\n columns: Record<string, SchemaValue>,\n primaryKey: PrimaryKey,\n primaryIndexData?: BTreeSet<Row>,\n ) {\n this.#tableName = tableName;\n this.#columns = columns;\n this.#primaryKey = primaryKey;\n this.#primaryIndexSort = primaryKey.map(k => [k, 'asc']);\n const comparator = makeBoundComparator(this.#primaryIndexSort);\n this.#indexes.set(JSON.stringify(this.#primaryIndexSort), {\n comparator,\n data: primaryIndexData ?? new BTreeSet<Row>(comparator),\n usedBy: new Set(),\n });\n }\n\n get tableSchema() {\n return {\n name: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n };\n }\n\n fork() {\n const primaryIndex = this.#getPrimaryIndex();\n return new MemorySource(\n this.#tableName,\n this.#columns,\n this.#primaryKey,\n primaryIndex.data.clone(),\n );\n }\n\n get data(): BTreeSet<Row> {\n return this.#getPrimaryIndex().data;\n }\n\n #getSchema(connection: Connection, unordered: boolean): SourceSchema {\n return {\n tableName: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n sort: unordered ? undefined : connection.sort,\n system: 'client',\n relationships: {},\n isHidden: false,\n compareRows: connection.compareRows,\n };\n }\n\n connect(\n sort: Ordering | undefined,\n filters?: Condition,\n splitEditKeys?: Set<string>,\n ): SourceInput {\n const transformedFilters = transformFilters(filters);\n const unordered = sort === undefined;\n const internalSort = sort ?? this.#primaryIndexSort;\n\n const input: SourceInput = {\n getSchema: () => schema,\n fetch: req => this.#fetch(req, connection),\n setOutput: output => {\n connection.output = output;\n },\n destroy: () => {\n this.#disconnect(input);\n },\n fullyAppliedFilters: !transformedFilters.conditionsRemoved,\n };\n\n const connection: Connection = {\n input,\n output: undefined,\n sort: internalSort,\n splitEditKeys,\n compareRows: makeComparator(internalSort),\n filters: transformedFilters.filters\n ? {\n condition: transformedFilters.filters,\n predicate: createPredicate(transformedFilters.filters),\n }\n : undefined,\n lastPushedEpoch: 0,\n };\n const schema = this.#getSchema(connection, unordered);\n if (!unordered) {\n assertOrderingIncludesPK(internalSort, this.#primaryKey);\n }\n this.#connections.push(connection);\n return input;\n }\n\n #disconnect(input: Input): void {\n const idx = this.#connections.findIndex(c => c.input === input);\n assert(idx !== -1, 'Connection not found');\n this.#connections.splice(idx, 1);\n\n // TODO: We used to delete unused indexes here. But in common cases like\n // navigating into issue detail pages it caused a ton of constantly\n // building and destroying indexes.\n //\n // Perhaps some intelligent LRU or something is needed here but for now,\n // the opposite extreme of keeping all indexes for the lifetime of the\n // page seems better.\n }\n\n #getPrimaryIndex(): Index {\n const index = this.#indexes.get(JSON.stringify(this.#primaryIndexSort));\n assert(index, 'Primary index not found');\n return index;\n }\n\n #getOrCreateIndex(sort: Ordering, usedBy: Connection): Index {\n const key = JSON.stringify(sort);\n const index = this.#indexes.get(key);\n // Future optimization could use existing index if it's the same just sorted\n // in reverse of needed.\n if (index) {\n index.usedBy.add(usedBy);\n return index;\n }\n\n const comparator = makeBoundComparator(sort);\n\n // When creating these synchronously becomes a problem, a few options:\n // 1. Allow users to specify needed indexes up front\n // 2. Create indexes in a different thread asynchronously (this would require\n // modifying the BTree to be able to be passed over structured-clone, or using\n // a different library.)\n // 3. We could even theoretically do (2) on multiple threads and then merge the\n // results!\n const rows = [...this.#getPrimaryIndex().data];\n rows.sort(comparator);\n const data = BTreeSet.fromSorted(comparator, rows);\n\n const newIndex = {comparator, data, usedBy: new Set([usedBy])};\n this.#indexes.set(key, newIndex);\n return newIndex;\n }\n\n // For unit testing that we correctly clean up indexes.\n getIndexKeys(): string[] {\n return [...this.#indexes.keys()];\n }\n\n *#fetch(req: FetchRequest, conn: Connection): Stream<Node | 'yield'> {\n const requestedSort = must(conn.sort);\n const {compareRows} = conn;\n // Avoid allocating a new closure when not reversing (the common case).\n const connectionComparator: Comparator = req.reverse\n ? (r1, r2) => compareRows(r2, r1)\n : compareRows;\n\n const pkConstraint = primaryKeyConstraintFromFilters(\n conn.filters?.condition,\n this.#primaryKey,\n );\n // The primary key constraint will be more limiting than the constraint\n // so swap out to that if it exists.\n const fetchOrPkConstraint = pkConstraint ?? req.constraint;\n\n // If there is a constraint, we need an index sorted by it first.\n const indexSort: OrderPart[] = [];\n if (fetchOrPkConstraint) {\n for (const key of Object.keys(fetchOrPkConstraint)) {\n indexSort.push([key, 'asc']);\n }\n }\n\n // For the special case of constraining by PK, we don't need to worry about\n // any requested sort since there can only be one result. Otherwise we also\n // need the index sorted by the requested sort.\n if (\n this.#primaryKey.length > 1 ||\n !fetchOrPkConstraint ||\n !constraintMatchesPrimaryKey(fetchOrPkConstraint, this.#primaryKey)\n ) {\n indexSort.push(...requestedSort);\n }\n\n const index = this.#getOrCreateIndex(indexSort, conn);\n const {data, comparator: compare} = index;\n // Avoid allocating a new closure when not reversing (the common case).\n const indexComparator: Comparator = req.reverse\n ? (r1, r2) => compare(r2, r1)\n : compare;\n\n const startAt = req.start?.row;\n\n // If there is a constraint, we want to start our scan at the first row that\n // matches the constraint. But because the next OrderPart can be `desc`,\n // it's not true that {[constraintKey]: constraintValue} is the first\n // matching row. Because in that case, the other fields will all be\n // `undefined`, and in Zero `undefined` is always less than any other value.\n // So if the second OrderPart is descending then `undefined` values will\n // actually be the *last* row. We need a way to stay \"start at the first row\n // with this constraint value\". RowBound with the corresponding compareBound\n // comparator accomplishes this. The right thing is probably to teach the\n // btree library to support this concept.\n let scanStart: RowBound | undefined;\n\n if (fetchOrPkConstraint) {\n scanStart = {};\n for (const [key, dir] of indexSort) {\n if (hasOwn(fetchOrPkConstraint, key)) {\n scanStart[key] = fetchOrPkConstraint[key];\n } else {\n if (req.reverse) {\n scanStart[key] = dir === 'asc' ? maxValue : minValue;\n } else {\n scanStart[key] = dir === 'asc' ? minValue : maxValue;\n }\n }\n }\n } else {\n scanStart = startAt;\n }\n\n const rowsIterable = generateRows(data, scanStart, req.reverse);\n const withOverlay = generateWithOverlay(\n startAt,\n pkConstraint ? once(rowsIterable) : rowsIterable,\n // use `req.constraint` here and not `fetchOrPkConstraint` since `fetchOrPkConstraint` could be the\n // primary key constraint. The primary key constraint comes from filters and is acting as a filter\n // rather than as the fetch constraint.\n req.constraint,\n this.#overlay,\n conn.lastPushedEpoch,\n // Use indexComparator, generateWithOverlayInner has a subtle dependency\n // on this. Since generateWithConstraint is done after\n // generateWithOverlay, the generator consumed by generateWithOverlayInner\n // does not end when the constraint stops matching and so the final\n // check to yield an add overlay if not yet yielded is not reached.\n // However, using the indexComparator the add overlay will be less than\n // the first row that does not match the constraint, and so any\n // not yet yielded add overlay will be yielded when the first row\n // not matching the constraint is reached.\n indexComparator,\n conn.filters?.predicate,\n );\n\n const withConstraint = generateWithConstraint(\n skipYields(\n generateWithStart(withOverlay, req.start, connectionComparator),\n ),\n // we use `req.constraint` and not `fetchOrPkConstraint` here because we need to\n // AND the constraint with what could have been the primary key constraint\n req.constraint,\n );\n\n yield* conn.filters\n ? generateWithFilter(withConstraint, conn.filters.predicate)\n : withConstraint;\n }\n\n *push(change: SourceChange): Stream<'yield'> {\n for (const result of this.genPush(change)) {\n if (result === 'yield') {\n yield result;\n }\n }\n }\n\n *genPush(change: SourceChange) {\n const primaryIndex = this.#getPrimaryIndex();\n const {data} = primaryIndex;\n const exists = (row: Row) => data.has(row);\n const setOverlay = (o: Overlay | undefined) => (this.#overlay = o);\n const writeChange = (c: SourceChange) => this.#writeChange(c);\n yield* genPushAndWriteWithSplitEdit(\n this.#connections,\n change,\n exists,\n setOverlay,\n writeChange,\n () => ++this.#pushEpoch,\n );\n }\n\n #writeChange(change: SourceChange) {\n for (const {data} of this.#indexes.values()) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const added = data.add(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n added,\n 'MemorySource: add must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.REMOVE: {\n const removed = data.delete(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: remove must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.EDIT: {\n // TODO: We could see if the PK (form the index tree's perspective)\n // changed and if not we could use set.\n // We cannot just do `set` with the new value since the `oldRow` might\n // not map to the same entry as the new `row` in the index btree.\n const removed = data.delete(change[SourceChangeIndex.OLD_ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: edit remove must succeed since row existence was already checked',\n );\n data.add(change[SourceChangeIndex.ROW]);\n break;\n }\n default:\n unreachable(change);\n }\n }\n }\n}\n\nfunction* generateWithConstraint(\n it: Stream<Node>,\n constraint: Constraint | undefined,\n) {\n for (const node of it) {\n if (constraint && !constraintMatchesRow(constraint, node.row)) {\n break;\n }\n yield node;\n }\n}\n\nfunction* generateWithFilter(it: Stream<Node>, filter: (row: Row) => boolean) {\n for (const node of it) {\n if (filter(node.row)) {\n yield node;\n }\n }\n}\n\nexport function* genPushAndWriteWithSplitEdit(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n getNextEpoch: () => number,\n) {\n let shouldSplitEdit = false;\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT) {\n for (const {splitEditKeys} of connections) {\n if (splitEditKeys) {\n for (const key of splitEditKeys) {\n if (\n !valuesEqual(\n change[SourceChangeIndex.ROW][key],\n change[SourceChangeIndex.OLD_ROW][key],\n )\n ) {\n shouldSplitEdit = true;\n break;\n }\n }\n }\n }\n }\n\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT && shouldSplitEdit) {\n yield* genPushAndWrite(\n connections,\n makeSourceChangeRemove(change[SourceChangeIndex.OLD_ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n yield* genPushAndWrite(\n connections,\n makeSourceChangeAdd(change[SourceChangeIndex.ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n } else {\n yield* genPushAndWrite(\n connections,\n change,\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n }\n}\n\nfunction* genPushAndWrite(\n connections: readonly Connection[],\n change: SourceChangeAdd | SourceChangeRemove | SourceChangeEdit,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n pushEpoch: number,\n) {\n for (const x of genPush(connections, change, exists, setOverlay, pushEpoch)) {\n yield x;\n }\n writeChange(change);\n}\n\nfunction* genPush(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => void,\n pushEpoch: number,\n) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n assert(\n !exists(change[SourceChangeIndex.ROW]),\n () => `Row already exists ${stringify(change)}`,\n );\n break;\n case ChangeType.REMOVE:\n assert(\n exists(change[SourceChangeIndex.ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n case ChangeType.EDIT:\n assert(\n exists(change[SourceChangeIndex.OLD_ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n default:\n unreachable(change);\n }\n\n for (const conn of connections) {\n const {output, filters, input} = conn;\n if (output) {\n conn.lastPushedEpoch = pushEpoch;\n setOverlay({epoch: pushEpoch, change});\n const outputChange: Change =\n change[SourceChangeIndex.TYPE] === ChangeType.EDIT\n ? makeEditChange(\n {row: change[SourceChangeIndex.ROW], relationships: {}},\n {row: change[SourceChangeIndex.OLD_ROW], relationships: {}},\n )\n : change[SourceChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n })\n : makeRemoveChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n });\n yield* filterPush(outputChange, output, input, filters?.predicate);\n yield undefined;\n }\n }\n\n setOverlay(undefined);\n}\n\nexport function* generateWithStart(\n nodes: Iterable<Node | 'yield'>,\n start: Start | undefined,\n compare: (r1: Row, r2: Row) => number,\n): Stream<Node | 'yield'> {\n if (!start) {\n yield* nodes;\n return;\n }\n let started = false;\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!started) {\n if (start.basis === 'at') {\n if (compare(node.row, start.row) >= 0) {\n started = true;\n }\n } else if (start.basis === 'after') {\n if (compare(node.row, start.row) > 0) {\n started = true;\n }\n }\n }\n if (started) {\n yield node;\n }\n }\n}\n\n/**\n * Takes an iterator and overlay.\n * Splices the overlay into the iterator at the correct position.\n *\n * @param startAt - if there is a lower bound to the stream. If the lower bound of the stream\n * is above the overlay, the overlay will be skipped.\n * @param rows - the stream into which the overlay should be spliced\n * @param constraint - constraint that was applied to the rowIterator and should\n * also be applied to the overlay.\n * @param overlay - the overlay values to splice in\n * @param compare - the comparator to use to find the position for the overlay\n */\nexport function* generateWithOverlay(\n startAt: Row | undefined,\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n const overlays = computeOverlays(\n startAt,\n constraint,\n overlayToApply,\n compare,\n filterPredicate,\n );\n yield* generateWithOverlayInner(rows, overlays, compare);\n}\n\nfunction computeOverlays(\n startAt: Row | undefined,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n): Overlays {\n let overlays: Overlays = {\n add: undefined,\n remove: undefined,\n };\n switch (overlay?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlay.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: overlay.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n\n if (startAt) {\n overlays = overlaysForStartAt(overlays, startAt, compare);\n }\n\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n\n return overlays;\n}\n\nexport {overlaysForStartAt as overlaysForStartAtForTest};\n\nfunction overlaysForStartAt(\n {add, remove}: Overlays,\n startAt: Row,\n compare: Comparator,\n): Overlays {\n const undefinedIfBeforeStartAt = (row: Row | undefined) =>\n row === undefined || compare(row, startAt) < 0 ? undefined : row;\n return {\n add: undefinedIfBeforeStartAt(add),\n remove: undefinedIfBeforeStartAt(remove),\n };\n}\n\nexport {overlaysForConstraint as overlaysForConstraintForTest};\n\nfunction overlaysForConstraint(\n {add, remove}: Overlays,\n constraint: Constraint,\n): Overlays {\n const undefinedIfDoesntMatchConstraint = (row: Row | undefined) =>\n row === undefined || !constraintMatchesRow(constraint, row)\n ? undefined\n : row;\n\n return {\n add: undefinedIfDoesntMatchConstraint(add),\n remove: undefinedIfDoesntMatchConstraint(remove),\n };\n}\n\nfunction overlaysForFilterPredicate(\n {add, remove}: Overlays,\n filterPredicate: (row: Row) => boolean | undefined,\n): Overlays {\n const undefinedIfDoesntMatchFilter = (row: Row | undefined) =>\n row === undefined || !filterPredicate(row) ? undefined : row;\n\n return {\n add: undefinedIfDoesntMatchFilter(add),\n remove: undefinedIfDoesntMatchFilter(remove),\n };\n}\n\nexport function* generateWithOverlayInner(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n compare: (r1: Row, r2: Row) => number,\n) {\n let addOverlayYielded = false;\n let removeOverlaySkipped = false;\n for (const row of rowIterator) {\n if (!addOverlayYielded && overlays.add) {\n const cmp = compare(overlays.add, row);\n if (cmp < 0) {\n addOverlayYielded = true;\n yield {row: overlays.add, relationships: {}};\n }\n }\n\n if (!removeOverlaySkipped && overlays.remove) {\n const cmp = compare(overlays.remove, row);\n if (cmp === 0) {\n removeOverlaySkipped = true;\n continue;\n }\n }\n yield {row, relationships: {}};\n }\n\n if (!addOverlayYielded && overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n}\n\n/**\n * Like {@link generateWithOverlay} but for unordered streams.\n * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly\n * at the start, and suppresses add/new-edit rows inline by PK match.\n */\nexport function* generateWithOverlayUnordered(\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n primaryKey: PrimaryKey,\n filterPredicate?: (row: Row) => boolean,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n let overlays: Overlays = {add: undefined, remove: undefined};\n switch (overlayToApply?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlayToApply.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: overlayToApply.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n yield* generateWithOverlayInnerUnordered(rows, overlays, primaryKey);\n}\n\nexport function* generateWithOverlayInnerUnordered(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n primaryKey: PrimaryKey,\n) {\n // Eager inject: yield the add overlay at the start (row not yet in storage)\n if (overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n // Stream with inline suppress: skip the remove overlay (row still in storage)\n let removeSkipped = false;\n for (const row of rowIterator) {\n if (\n !removeSkipped &&\n overlays.remove &&\n rowMatchesPK(overlays.remove, row, primaryKey)\n ) {\n removeSkipped = true;\n continue;\n }\n yield {row, relationships: {}};\n }\n}\n\nfunction rowMatchesPK(a: Row, b: Row, primaryKey: PrimaryKey): boolean {\n for (const key of primaryKey) {\n if (!valuesEqual(a[key], b[key])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * A location to begin scanning an index from. Can either be a specific value\n * or the min or max possible value for the type. This is used to start a scan\n * at the beginning of the rows matching a constraint.\n */\ntype Bound = Value | MinValue | MaxValue;\ntype RowBound = Record<string, Bound>;\nconst minValue = Symbol('min-value');\ntype MinValue = typeof minValue;\nconst maxValue = Symbol('max-value');\ntype MaxValue = typeof maxValue;\n\nfunction makeBoundComparator(sort: Ordering): Comparator {\n // Pre-extract the first two keys/directions to avoid per-call array access.\n // All paths share one function literal (single SFI) so the BTree comparator call site\n // stays monomorphic across indexes with different sort orderings, preventing V8 IC deopt.\n // Even a 2-SFI split (e.g. separate len=1 path) creates a polymorphic IC that\n // measurably regresses performance, so we keep a single return body.\n const len = sort.length;\n const k0 = sort[0][0];\n const a0 = sort[0][1] === 'asc';\n const k1 = len > 1 ? sort[1][0] : '';\n const a1 = len > 1 ? sort[1][1] === 'asc' : true;\n\n return (a: RowBound, b: RowBound) => {\n const c0 = a0 ? compareBounds(a[k0], b[k0]) : compareBounds(b[k0], a[k0]);\n if (len === 1 || c0 !== 0) return c0;\n const c1 = a1 ? compareBounds(a[k1], b[k1]) : compareBounds(b[k1], a[k1]);\n if (len === 2 || c1 !== 0) return c1;\n // Hot! Do not use destructuring\n for (let i = 2; i < len; i++) {\n const cmp = compareBounds(a[sort[i][0]], b[sort[i][0]]);\n if (cmp !== 0) return sort[i][1] === 'asc' ? cmp : -cmp;\n }\n return 0;\n };\n}\n\nfunction compareBounds(a: Bound, b: Bound): number {\n if (a === b) {\n return 0;\n }\n // Use typeof to guard the Symbol sentinel checks first. This gives V8 a\n // clear type discriminant so the common non-symbol path compiles as a\n // specialised numeric/string fast-path without a Smi deopt when the\n // minValue/maxValue sentinel symbols appear.\n if (typeof a === 'symbol') {\n return a === minValue ? -1 : 1;\n }\n if (typeof b === 'symbol') {\n return b === minValue ? 1 : -1;\n }\n return compareValues(a, b);\n}\n\nfunction* generateRows(\n data: BTreeSet<Row>,\n scanStart: RowBound | undefined,\n reverse: boolean | undefined,\n) {\n yield* data[reverse ? 'valuesFromReversed' : 'valuesFrom'](\n scanStart as Row | undefined,\n );\n}\n\nexport function stringify(change: SourceChange) {\n return JSON.stringify(change, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v,\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory-source.js","names":["#tableName","#columns","#primaryKey","#primaryIndexSort","#indexes","#connections","#getPrimaryIndex","#fetch","#disconnect","#getSchema","#getOrCreateIndex","#overlay","#writeChange","#pushEpoch"],"sources":["../../../../../zql/src/ivm/memory-source.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {BTreeSet} from '../../../shared/src/btree-set.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport {once, toSorted} from '../../../shared/src/iterables.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n Condition,\n Ordering,\n OrderPart,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-types/src/schema-value.ts';\nimport type {DebugDelegate} from '../builder/debug-delegate.ts';\nimport {\n createPredicate,\n transformFilters,\n type NoSubqueryCondition,\n} from '../builder/filter.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {makeAddChange, makeEditChange, makeRemoveChange} from './change.ts';\nimport {\n constraintMatchesPrimaryKey,\n constraintMatchesRow,\n primaryKeyConstraintFromFilters,\n type Constraint,\n} from './constraint.ts';\nimport {\n compareValues,\n makeComparator,\n valuesEqual,\n type Comparator,\n type Node,\n} from './data.ts';\nimport {filterPush} from './filter-push.ts';\nimport {\n skipYields,\n type FetchRequest,\n type Input,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {SourceChangeIndex} from './source-change-index.ts';\nimport type {\n Source,\n SourceChange,\n SourceChangeAdd,\n SourceChangeEdit,\n SourceChangeRemove,\n SourceInput,\n} from './source.ts';\nimport {makeSourceChangeAdd, makeSourceChangeRemove} from './source.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Overlay = {\n epoch: number;\n change: SourceChange;\n};\n\nexport type Overlays = {\n add: Row | undefined;\n remove: Row | undefined;\n};\n\ntype Index = {\n comparator: Comparator;\n data: BTreeSet<Row>;\n usedBy: Set<Connection>;\n};\n\nexport type Connection = {\n input: Input;\n output: Output | undefined;\n sort?: Ordering | undefined;\n splitEditKeys: Set<string> | undefined;\n compareRows: Comparator;\n filters:\n | {\n condition: NoSubqueryCondition;\n predicate: (row: Row) => boolean;\n }\n | undefined;\n readonly debug?: DebugDelegate | undefined;\n lastPushedEpoch: number;\n};\n\n/**\n * A `MemorySource` is a source that provides data to the pipeline from an\n * in-memory data source.\n *\n * This data is kept in sorted order as downstream pipelines will always expect\n * the data they receive from `pull` to be in sorted order.\n */\nexport class MemorySource implements Source {\n readonly #tableName: string;\n readonly #columns: Record<string, SchemaValue>;\n readonly #primaryKey: PrimaryKey;\n readonly #primaryIndexSort: Ordering;\n readonly #indexes: Map<string, Index> = new Map();\n readonly #connections: Connection[] = [];\n\n #overlay: Overlay | undefined;\n #pushEpoch = 0;\n\n constructor(\n tableName: string,\n columns: Record<string, SchemaValue>,\n primaryKey: PrimaryKey,\n primaryIndexData?: BTreeSet<Row>,\n ) {\n this.#tableName = tableName;\n this.#columns = columns;\n this.#primaryKey = primaryKey;\n this.#primaryIndexSort = primaryKey.map(k => [k, 'asc']);\n const comparator = makeBoundComparator(this.#primaryIndexSort);\n this.#indexes.set(JSON.stringify(this.#primaryIndexSort), {\n comparator,\n data: primaryIndexData ?? new BTreeSet<Row>(comparator),\n usedBy: new Set(),\n });\n }\n\n get tableSchema() {\n return {\n name: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n };\n }\n\n fork() {\n const primaryIndex = this.#getPrimaryIndex();\n return new MemorySource(\n this.#tableName,\n this.#columns,\n this.#primaryKey,\n primaryIndex.data.clone(),\n );\n }\n\n get data(): BTreeSet<Row> {\n return this.#getPrimaryIndex().data;\n }\n\n #getSchema(connection: Connection, unordered: boolean): SourceSchema {\n return {\n tableName: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n sort: unordered ? undefined : connection.sort,\n system: 'client',\n relationships: {},\n isHidden: false,\n compareRows: connection.compareRows,\n };\n }\n\n connect(\n sort: Ordering | undefined,\n filters?: Condition,\n splitEditKeys?: Set<string>,\n ): SourceInput {\n const transformedFilters = transformFilters(filters);\n const unordered = sort === undefined;\n const internalSort = sort ?? this.#primaryIndexSort;\n\n const input: SourceInput = {\n getSchema: () => schema,\n fetch: req => this.#fetch(req, connection),\n setOutput: output => {\n connection.output = output;\n },\n destroy: () => {\n this.#disconnect(input);\n },\n fullyAppliedFilters: !transformedFilters.conditionsRemoved,\n };\n\n const connection: Connection = {\n input,\n output: undefined,\n sort: internalSort,\n splitEditKeys,\n compareRows: makeComparator(internalSort),\n filters: transformedFilters.filters\n ? {\n condition: transformedFilters.filters,\n predicate: createPredicate(transformedFilters.filters),\n }\n : undefined,\n lastPushedEpoch: 0,\n };\n const schema = this.#getSchema(connection, unordered);\n if (!unordered) {\n assertOrderingIncludesPK(internalSort, this.#primaryKey);\n }\n this.#connections.push(connection);\n return input;\n }\n\n #disconnect(input: Input): void {\n const idx = this.#connections.findIndex(c => c.input === input);\n assert(idx !== -1, 'Connection not found');\n this.#connections.splice(idx, 1);\n\n // TODO: We used to delete unused indexes here. But in common cases like\n // navigating into issue detail pages it caused a ton of constantly\n // building and destroying indexes.\n //\n // Perhaps some intelligent LRU or something is needed here but for now,\n // the opposite extreme of keeping all indexes for the lifetime of the\n // page seems better.\n }\n\n #getPrimaryIndex(): Index {\n const index = this.#indexes.get(JSON.stringify(this.#primaryIndexSort));\n assert(index, 'Primary index not found');\n return index;\n }\n\n #getOrCreateIndex(sort: Ordering, usedBy: Connection): Index {\n const key = JSON.stringify(sort);\n const index = this.#indexes.get(key);\n // Future optimization could use existing index if it's the same just sorted\n // in reverse of needed.\n if (index) {\n index.usedBy.add(usedBy);\n return index;\n }\n\n const comparator = makeBoundComparator(sort);\n\n // When creating these synchronously becomes a problem, a few options:\n // 1. Allow users to specify needed indexes up front\n // 2. Create indexes in a different thread asynchronously (this would require\n // modifying the BTree to be able to be passed over structured-clone, or using\n // a different library.)\n // 3. We could even theoretically do (2) on multiple threads and then merge the\n // results!\n const rows = toSorted(this.#getPrimaryIndex().data, comparator);\n const data = BTreeSet.fromSorted(comparator, rows);\n\n const newIndex = {comparator, data, usedBy: new Set([usedBy])};\n this.#indexes.set(key, newIndex);\n return newIndex;\n }\n\n // For unit testing that we correctly clean up indexes.\n getIndexKeys(): string[] {\n return [...this.#indexes.keys()];\n }\n\n *#fetch(req: FetchRequest, conn: Connection): Stream<Node | 'yield'> {\n const requestedSort = must(conn.sort);\n const {compareRows} = conn;\n // Avoid allocating a new closure when not reversing (the common case).\n const connectionComparator: Comparator = req.reverse\n ? (r1, r2) => compareRows(r2, r1)\n : compareRows;\n\n const pkConstraint = primaryKeyConstraintFromFilters(\n conn.filters?.condition,\n this.#primaryKey,\n );\n // The primary key constraint will be more limiting than the constraint\n // so swap out to that if it exists.\n const fetchOrPkConstraint = pkConstraint ?? req.constraint;\n\n // If there is a constraint, we need an index sorted by it first.\n const indexSort: OrderPart[] = [];\n if (fetchOrPkConstraint) {\n for (const key of Object.keys(fetchOrPkConstraint)) {\n indexSort.push([key, 'asc']);\n }\n }\n\n // For the special case of constraining by PK, we don't need to worry about\n // any requested sort since there can only be one result. Otherwise we also\n // need the index sorted by the requested sort.\n if (\n this.#primaryKey.length > 1 ||\n !fetchOrPkConstraint ||\n !constraintMatchesPrimaryKey(fetchOrPkConstraint, this.#primaryKey)\n ) {\n indexSort.push(...requestedSort);\n }\n\n const index = this.#getOrCreateIndex(indexSort, conn);\n const {data, comparator: compare} = index;\n // Avoid allocating a new closure when not reversing (the common case).\n const indexComparator: Comparator = req.reverse\n ? (r1, r2) => compare(r2, r1)\n : compare;\n\n const startAt = req.start?.row;\n\n // If there is a constraint, we want to start our scan at the first row that\n // matches the constraint. But because the next OrderPart can be `desc`,\n // it's not true that {[constraintKey]: constraintValue} is the first\n // matching row. Because in that case, the other fields will all be\n // `undefined`, and in Zero `undefined` is always less than any other value.\n // So if the second OrderPart is descending then `undefined` values will\n // actually be the *last* row. We need a way to stay \"start at the first row\n // with this constraint value\". RowBound with the corresponding compareBound\n // comparator accomplishes this. The right thing is probably to teach the\n // btree library to support this concept.\n let scanStart: RowBound | undefined;\n\n if (fetchOrPkConstraint) {\n scanStart = {};\n for (const [key, dir] of indexSort) {\n if (hasOwn(fetchOrPkConstraint, key)) {\n scanStart[key] = fetchOrPkConstraint[key];\n } else {\n if (req.reverse) {\n scanStart[key] = dir === 'asc' ? maxValue : minValue;\n } else {\n scanStart[key] = dir === 'asc' ? minValue : maxValue;\n }\n }\n }\n } else {\n scanStart = startAt;\n }\n\n const rowsIterable = generateRows(data, scanStart, req.reverse);\n const withOverlay = generateWithOverlay(\n startAt,\n pkConstraint ? once(rowsIterable) : rowsIterable,\n // use `req.constraint` here and not `fetchOrPkConstraint` since `fetchOrPkConstraint` could be the\n // primary key constraint. The primary key constraint comes from filters and is acting as a filter\n // rather than as the fetch constraint.\n req.constraint,\n this.#overlay,\n conn.lastPushedEpoch,\n // Use indexComparator, generateWithOverlayInner has a subtle dependency\n // on this. Since generateWithConstraint is done after\n // generateWithOverlay, the generator consumed by generateWithOverlayInner\n // does not end when the constraint stops matching and so the final\n // check to yield an add overlay if not yet yielded is not reached.\n // However, using the indexComparator the add overlay will be less than\n // the first row that does not match the constraint, and so any\n // not yet yielded add overlay will be yielded when the first row\n // not matching the constraint is reached.\n indexComparator,\n conn.filters?.predicate,\n );\n\n const withConstraint = generateWithConstraint(\n skipYields(\n generateWithStart(withOverlay, req.start, connectionComparator),\n ),\n // we use `req.constraint` and not `fetchOrPkConstraint` here because we need to\n // AND the constraint with what could have been the primary key constraint\n req.constraint,\n );\n\n yield* conn.filters\n ? generateWithFilter(withConstraint, conn.filters.predicate)\n : withConstraint;\n }\n\n *push(change: SourceChange): Stream<'yield'> {\n for (const result of this.genPush(change)) {\n if (result === 'yield') {\n yield result;\n }\n }\n }\n\n *genPush(change: SourceChange) {\n const primaryIndex = this.#getPrimaryIndex();\n const {data} = primaryIndex;\n const exists = (row: Row) => data.has(row);\n const setOverlay = (o: Overlay | undefined) => (this.#overlay = o);\n const writeChange = (c: SourceChange) => this.#writeChange(c);\n yield* genPushAndWriteWithSplitEdit(\n this.#connections,\n change,\n exists,\n setOverlay,\n writeChange,\n () => ++this.#pushEpoch,\n );\n }\n\n #writeChange(change: SourceChange) {\n for (const {data} of this.#indexes.values()) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const added = data.add(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n added,\n 'MemorySource: add must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.REMOVE: {\n const removed = data.delete(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: remove must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.EDIT: {\n // TODO: We could see if the PK (form the index tree's perspective)\n // changed and if not we could use set.\n // We cannot just do `set` with the new value since the `oldRow` might\n // not map to the same entry as the new `row` in the index btree.\n const removed = data.delete(change[SourceChangeIndex.OLD_ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: edit remove must succeed since row existence was already checked',\n );\n data.add(change[SourceChangeIndex.ROW]);\n break;\n }\n default:\n unreachable(change);\n }\n }\n }\n}\n\nfunction* generateWithConstraint(\n it: Stream<Node>,\n constraint: Constraint | undefined,\n) {\n for (const node of it) {\n if (constraint && !constraintMatchesRow(constraint, node.row)) {\n break;\n }\n yield node;\n }\n}\n\nfunction* generateWithFilter(it: Stream<Node>, filter: (row: Row) => boolean) {\n for (const node of it) {\n if (filter(node.row)) {\n yield node;\n }\n }\n}\n\nexport function* genPushAndWriteWithSplitEdit(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n getNextEpoch: () => number,\n) {\n let shouldSplitEdit = false;\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT) {\n for (const {splitEditKeys} of connections) {\n if (splitEditKeys) {\n for (const key of splitEditKeys) {\n if (\n !valuesEqual(\n change[SourceChangeIndex.ROW][key],\n change[SourceChangeIndex.OLD_ROW][key],\n )\n ) {\n shouldSplitEdit = true;\n break;\n }\n }\n }\n }\n }\n\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT && shouldSplitEdit) {\n yield* genPushAndWrite(\n connections,\n makeSourceChangeRemove(change[SourceChangeIndex.OLD_ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n yield* genPushAndWrite(\n connections,\n makeSourceChangeAdd(change[SourceChangeIndex.ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n } else {\n yield* genPushAndWrite(\n connections,\n change,\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n }\n}\n\nfunction* genPushAndWrite(\n connections: readonly Connection[],\n change: SourceChangeAdd | SourceChangeRemove | SourceChangeEdit,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n pushEpoch: number,\n) {\n for (const x of genPush(connections, change, exists, setOverlay, pushEpoch)) {\n yield x;\n }\n writeChange(change);\n}\n\nfunction* genPush(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => void,\n pushEpoch: number,\n) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n assert(\n !exists(change[SourceChangeIndex.ROW]),\n () => `Row already exists ${stringify(change)}`,\n );\n break;\n case ChangeType.REMOVE:\n assert(\n exists(change[SourceChangeIndex.ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n case ChangeType.EDIT:\n assert(\n exists(change[SourceChangeIndex.OLD_ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n default:\n unreachable(change);\n }\n\n for (const conn of connections) {\n const {output, filters, input} = conn;\n if (output) {\n conn.lastPushedEpoch = pushEpoch;\n setOverlay({epoch: pushEpoch, change});\n const outputChange: Change =\n change[SourceChangeIndex.TYPE] === ChangeType.EDIT\n ? makeEditChange(\n {row: change[SourceChangeIndex.ROW], relationships: {}},\n {row: change[SourceChangeIndex.OLD_ROW], relationships: {}},\n )\n : change[SourceChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n })\n : makeRemoveChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n });\n yield* filterPush(outputChange, output, input, filters?.predicate);\n yield undefined;\n }\n }\n\n setOverlay(undefined);\n}\n\nexport function* generateWithStart(\n nodes: Iterable<Node | 'yield'>,\n start: Start | undefined,\n compare: (r1: Row, r2: Row) => number,\n): Stream<Node | 'yield'> {\n if (!start) {\n yield* nodes;\n return;\n }\n let started = false;\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!started) {\n if (start.basis === 'at') {\n if (compare(node.row, start.row) >= 0) {\n started = true;\n }\n } else if (start.basis === 'after') {\n if (compare(node.row, start.row) > 0) {\n started = true;\n }\n }\n }\n if (started) {\n yield node;\n }\n }\n}\n\n/**\n * Takes an iterator and overlay.\n * Splices the overlay into the iterator at the correct position.\n *\n * @param startAt - if there is a lower bound to the stream. If the lower bound of the stream\n * is above the overlay, the overlay will be skipped.\n * @param rows - the stream into which the overlay should be spliced\n * @param constraint - constraint that was applied to the rowIterator and should\n * also be applied to the overlay.\n * @param overlay - the overlay values to splice in\n * @param compare - the comparator to use to find the position for the overlay\n */\nexport function* generateWithOverlay(\n startAt: Row | undefined,\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n const overlays = computeOverlays(\n startAt,\n constraint,\n overlayToApply,\n compare,\n filterPredicate,\n );\n yield* generateWithOverlayInner(rows, overlays, compare);\n}\n\nfunction computeOverlays(\n startAt: Row | undefined,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n): Overlays {\n let overlays: Overlays = {\n add: undefined,\n remove: undefined,\n };\n switch (overlay?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlay.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: overlay.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n\n if (startAt) {\n overlays = overlaysForStartAt(overlays, startAt, compare);\n }\n\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n\n return overlays;\n}\n\nexport {overlaysForStartAt as overlaysForStartAtForTest};\n\nfunction overlaysForStartAt(\n {add, remove}: Overlays,\n startAt: Row,\n compare: Comparator,\n): Overlays {\n const undefinedIfBeforeStartAt = (row: Row | undefined) =>\n row === undefined || compare(row, startAt) < 0 ? undefined : row;\n return {\n add: undefinedIfBeforeStartAt(add),\n remove: undefinedIfBeforeStartAt(remove),\n };\n}\n\nexport {overlaysForConstraint as overlaysForConstraintForTest};\n\nfunction overlaysForConstraint(\n {add, remove}: Overlays,\n constraint: Constraint,\n): Overlays {\n const undefinedIfDoesntMatchConstraint = (row: Row | undefined) =>\n row === undefined || !constraintMatchesRow(constraint, row)\n ? undefined\n : row;\n\n return {\n add: undefinedIfDoesntMatchConstraint(add),\n remove: undefinedIfDoesntMatchConstraint(remove),\n };\n}\n\nfunction overlaysForFilterPredicate(\n {add, remove}: Overlays,\n filterPredicate: (row: Row) => boolean | undefined,\n): Overlays {\n const undefinedIfDoesntMatchFilter = (row: Row | undefined) =>\n row === undefined || !filterPredicate(row) ? undefined : row;\n\n return {\n add: undefinedIfDoesntMatchFilter(add),\n remove: undefinedIfDoesntMatchFilter(remove),\n };\n}\n\nexport function* generateWithOverlayInner(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n compare: (r1: Row, r2: Row) => number,\n) {\n let addOverlayYielded = false;\n let removeOverlaySkipped = false;\n for (const row of rowIterator) {\n if (!addOverlayYielded && overlays.add) {\n const cmp = compare(overlays.add, row);\n if (cmp < 0) {\n addOverlayYielded = true;\n yield {row: overlays.add, relationships: {}};\n }\n }\n\n if (!removeOverlaySkipped && overlays.remove) {\n const cmp = compare(overlays.remove, row);\n if (cmp === 0) {\n removeOverlaySkipped = true;\n continue;\n }\n }\n yield {row, relationships: {}};\n }\n\n if (!addOverlayYielded && overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n}\n\n/**\n * Like {@link generateWithOverlay} but for unordered streams.\n * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly\n * at the start, and suppresses add/new-edit rows inline by PK match.\n */\nexport function* generateWithOverlayUnordered(\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n primaryKey: PrimaryKey,\n filterPredicate?: (row: Row) => boolean,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n let overlays: Overlays = {add: undefined, remove: undefined};\n switch (overlayToApply?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlayToApply.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: overlayToApply.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n yield* generateWithOverlayInnerUnordered(rows, overlays, primaryKey);\n}\n\nexport function* generateWithOverlayInnerUnordered(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n primaryKey: PrimaryKey,\n) {\n // Eager inject: yield the add overlay at the start (row not yet in storage)\n if (overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n // Stream with inline suppress: skip the remove overlay (row still in storage)\n let removeSkipped = false;\n for (const row of rowIterator) {\n if (\n !removeSkipped &&\n overlays.remove &&\n rowMatchesPK(overlays.remove, row, primaryKey)\n ) {\n removeSkipped = true;\n continue;\n }\n yield {row, relationships: {}};\n }\n}\n\nfunction rowMatchesPK(a: Row, b: Row, primaryKey: PrimaryKey): boolean {\n for (const key of primaryKey) {\n if (!valuesEqual(a[key], b[key])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * A location to begin scanning an index from. Can either be a specific value\n * or the min or max possible value for the type. This is used to start a scan\n * at the beginning of the rows matching a constraint.\n */\ntype Bound = Value | MinValue | MaxValue;\ntype RowBound = Record<string, Bound>;\nconst minValue = Symbol('min-value');\ntype MinValue = typeof minValue;\nconst maxValue = Symbol('max-value');\ntype MaxValue = typeof maxValue;\n\nfunction makeBoundComparator(sort: Ordering): Comparator {\n // Pre-extract the first two keys/directions to avoid per-call array access.\n // All paths share one function literal (single SFI) so the BTree comparator call site\n // stays monomorphic across indexes with different sort orderings, preventing V8 IC deopt.\n // Even a 2-SFI split (e.g. separate len=1 path) creates a polymorphic IC that\n // measurably regresses performance, so we keep a single return body.\n const len = sort.length;\n const k0 = sort[0][0];\n const a0 = sort[0][1] === 'asc';\n const k1 = len > 1 ? sort[1][0] : '';\n const a1 = len > 1 ? sort[1][1] === 'asc' : true;\n\n return (a: RowBound, b: RowBound) => {\n const c0 = a0 ? compareBounds(a[k0], b[k0]) : compareBounds(b[k0], a[k0]);\n if (len === 1 || c0 !== 0) return c0;\n const c1 = a1 ? compareBounds(a[k1], b[k1]) : compareBounds(b[k1], a[k1]);\n if (len === 2 || c1 !== 0) return c1;\n // Hot! Do not use destructuring\n for (let i = 2; i < len; i++) {\n const cmp = compareBounds(a[sort[i][0]], b[sort[i][0]]);\n if (cmp !== 0) return sort[i][1] === 'asc' ? cmp : -cmp;\n }\n return 0;\n };\n}\n\nfunction compareBounds(a: Bound, b: Bound): number {\n if (a === b) {\n return 0;\n }\n // Use typeof to guard the Symbol sentinel checks first. This gives V8 a\n // clear type discriminant so the common non-symbol path compiles as a\n // specialised numeric/string fast-path without a Smi deopt when the\n // minValue/maxValue sentinel symbols appear.\n if (typeof a === 'symbol') {\n return a === minValue ? -1 : 1;\n }\n if (typeof b === 'symbol') {\n return b === minValue ? 1 : -1;\n }\n return compareValues(a, b);\n}\n\nfunction* generateRows(\n data: BTreeSet<Row>,\n scanStart: RowBound | undefined,\n reverse: boolean | undefined,\n) {\n yield* data[reverse ? 'valuesFromReversed' : 'valuesFrom'](\n scanStart as Row | undefined,\n );\n}\n\nexport function stringify(change: SourceChange) {\n return JSON.stringify(change, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v,\n );\n}\n"],"mappings":"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"}
|
|
@@ -9,6 +9,14 @@ export type SourceSchema = {
|
|
|
9
9
|
readonly tableName: string;
|
|
10
10
|
readonly columns: Record<string, SchemaValue>;
|
|
11
11
|
readonly primaryKey: PrimaryKey;
|
|
12
|
+
/**
|
|
13
|
+
* Column sets for `UNIQUE` indexes on the table, including the one
|
|
14
|
+
* covering the primary key. Each entry is pre-sorted. Populated when
|
|
15
|
+
* the underlying source has introspectable schema (e.g. `TableSource`).
|
|
16
|
+
* When absent, consumers should assume no unique indexes are known and
|
|
17
|
+
* fall back to safe behavior.
|
|
18
|
+
*/
|
|
19
|
+
readonly uniqueIndexes?: readonly PrimaryKey[] | undefined;
|
|
12
20
|
readonly relationships: {
|
|
13
21
|
[key: string]: SourceSchema;
|
|
14
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAE,MAAM,EAAC,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAC1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,WAAW,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;KAAC,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAQ3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACtC,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAE,MAAM,EAAC,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAC1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,WAAW,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,CAAC;IAC3D,QAAQ,CAAC,aAAa,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;KAAC,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAQ3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACtC,CAAC"}
|
package/out/zql/src/ivm/take.js
CHANGED
|
@@ -2,8 +2,8 @@ import { assert, unreachable } from "../../../shared/src/asserts.js";
|
|
|
2
2
|
import { hasOwn } from "../../../shared/src/has-own.js";
|
|
3
3
|
import { throwOutput } from "./operator.js";
|
|
4
4
|
import { makeAddChange, makeRemoveChange } from "./change.js";
|
|
5
|
-
import { compareValues } from "./data.js";
|
|
6
5
|
import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
|
|
6
|
+
import { compareValues } from "./data.js";
|
|
7
7
|
//#region ../zql/src/ivm/take.ts
|
|
8
8
|
var MAX_BOUND_KEY = "maxBound";
|
|
9
9
|
/**
|
|
@@ -405,6 +405,6 @@ function makePartitionKeyComparator(partitionKey) {
|
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
407
|
//#endregion
|
|
408
|
-
export { Take };
|
|
408
|
+
export { Take, constraintMatchesPartitionKey, makePartitionKeyComparator };
|
|
409
409
|
|
|
410
410
|
//# sourceMappingURL=take.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutator-registry.js","names":[],"sources":["../../../../../zql/src/mutate/mutator-registry.ts"],"sourcesContent":["import {\n deepMerge,\n isPlainObject,\n type DeepMerge,\n} from '../../../shared/src/deep-merge.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {\n getValueAtPath,\n iterateLeaves,\n} from '../../../shared/src/object-traversal.ts';\nimport type {\n BaseDefaultSchema,\n DefaultSchema,\n} from '../../../zero-types/src/default-types.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {validateInput} from '../query/validate-input.ts';\nimport type {Transaction} from './custom.ts';\nimport {\n isMutator,\n isMutatorDefinition,\n type AnyMutator,\n type AnyMutatorDefinition,\n type MutateRequest,\n type MutateRequestTypes,\n type Mutator,\n type MutatorDefinition,\n type MutatorDefinitionFunction,\n type MutatorTypes,\n} from './mutator.ts';\n\n/**\n * Creates a MutatorRegistry from a tree of MutatorDefinitions,\n * optionally extending a base MutatorRegistry.\n *\n * @example\n * ```ts\n * // Create a new registry\n * const mutators = defineMutators({\n * user: {\n * create: defineMutator(...),\n * delete: defineMutator(...),\n * },\n * post: {\n * publish: defineMutator(...),\n * },\n * });\n *\n * // Extend an existing registry (e.g., for server-side overrides)\n * const serverMutators = defineMutators(mutators, {\n * user: {\n * create: defineMutator(...), // overrides mutators.user.create\n * },\n * // post.publish is inherited from mutators\n * });\n *\n * // Access mutators by path\n * const mr = mutators.user.create({name: 'Alice'});\n *\n * // Execute on client\n * zero.mutate(mr);\n *\n * // Execute on server\n * mr.mutator.fn({tx, ctx, args: mr.args});\n *\n * // Lookup by name (for server-side dispatch)\n * const mutator = getMutator(mutators, 'user.create');\n * ```\n */\nexport function defineMutators<\n // let MD infer freely so defaults aren't erased by a MutatorDefinitions constraint\n const MD,\n S extends BaseDefaultSchema = DefaultSchema,\n>(\n // we assert types here for clear error messages\n definitions: MD & AssertMutatorDefinitions<MD>,\n): MutatorRegistry<EnsureMutatorDefinitions<MD>, S>;\n\nexport function defineMutators<\n // same as MD above, but for TBase and TOverrides\n const TBase,\n const TOverrides,\n S extends BaseDefaultSchema = DefaultSchema,\n>(\n base:\n | MutatorRegistry<EnsureMutatorDefinitions<TBase>, S>\n | (TBase & AssertMutatorDefinitions<TBase>),\n overrides: TOverrides & AssertMutatorDefinitions<TOverrides>,\n): MutatorRegistry<\n DeepMerge<\n EnsureMutatorDefinitions<TBase>,\n EnsureMutatorDefinitions<TOverrides>,\n AnyMutatorDefinition\n >,\n S\n>;\n\nexport function defineMutators(\n definitionsOrBase: MutatorDefinitions | AnyMutatorRegistry,\n maybeOverrides?: MutatorDefinitions,\n): AnyMutatorRegistry {\n function processDefinitions(\n definitions: MutatorDefinitions,\n path: string[],\n ): Record<string | symbol, unknown> {\n const result: Record<string | symbol, unknown> = {\n ['~']: 'MutatorRegistry',\n };\n\n for (const [key, value] of Object.entries(definitions)) {\n path.push(key);\n const name = path.join('.');\n\n if (isMutatorDefinition(value)) {\n result[key] = createMutator(name, value);\n } else {\n // Nested definitions\n result[key] = processDefinitions(value, path);\n }\n path.pop();\n }\n\n return result;\n }\n\n if (maybeOverrides !== undefined) {\n // Merge base and overrides\n let base: Record<string | symbol, unknown>;\n if (!isMutatorRegistry(definitionsOrBase)) {\n base = processDefinitions(definitionsOrBase, []);\n } else {\n base = definitionsOrBase;\n }\n\n const processed = processDefinitions(maybeOverrides, []);\n\n const merged = deepMerge(base, processed, isMutatorLeaf);\n merged['~'] = 'MutatorRegistry';\n return merged as AnyMutatorRegistry;\n }\n\n return processDefinitions(\n definitionsOrBase as MutatorDefinitions,\n [],\n ) as AnyMutatorRegistry;\n}\n\nconst isMutatorLeaf = (value: unknown): boolean =>\n !isPlainObject(value) || isMutator(value);\n\n/**\n * Creates a function that can be used to define mutators with a specific schema.\n */\nexport function defineMutatorsWithType<\n TSchema extends Schema,\n>(): TypedDefineMutators<TSchema> {\n return defineMutators;\n}\n\n/**\n * The return type of defineMutatorsWithType. A function matching the\n * defineMutators overloads but with Schema pre-bound.\n */\ntype TypedDefineMutators<S extends Schema> = {\n // Single definitions\n <MD>(\n definitions: MD & AssertMutatorDefinitions<MD>,\n ): MutatorRegistry<EnsureMutatorDefinitions<MD>, S>;\n\n // Base and overrides\n <TBase, TOverrides>(\n base:\n | MutatorRegistry<EnsureMutatorDefinitions<TBase>, S>\n | (TBase & AssertMutatorDefinitions<TBase>),\n overrides: TOverrides,\n ): MutatorRegistry<\n DeepMerge<\n EnsureMutatorDefinitions<TBase>,\n EnsureMutatorDefinitions<TOverrides>,\n AnyMutatorDefinition\n >,\n S\n >;\n};\n\nexport type AssertMutatorDefinitions<MD> = MD extends MutatorDefinitions\n ? unknown\n : never;\n\nexport type EnsureMutatorDefinitions<MD> = MD extends MutatorDefinitions\n ? MD\n : never;\n\n/**\n * Checks if a value is a MutatorRegistry.\n */\nexport function isMutatorRegistry(value: unknown): value is AnyMutatorRegistry {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<string, unknown>)['~'] === 'MutatorRegistry'\n );\n}\n\nexport type MutatorRegistryTypes<TSchema extends Schema> = 'MutatorRegistry' & {\n readonly $schema: TSchema;\n};\n\n/**\n * The result of defineMutators(). A tree of Mutators with a tag for detection.\n */\nexport type MutatorRegistry<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n> = ToMutatorTree<MD, TSchema> & {\n ['~']: MutatorRegistryTypes<TSchema>;\n};\n\n/**\n * A branded type for use in type constraints. Use this instead of\n * `MutatorRegistry<S, C, any>` to avoid TypeScript drilling into\n * the complex ToMutatorTree structure and hitting variance issues.\n */\nexport type AnyMutatorRegistry = {\n ['~']: MutatorRegistryTypes<Schema>;\n [key: string]: unknown;\n};\n\n/**\n * Transforms a MutatorDefinitions into a tree of Mutators.\n * Each MutatorDefinition becomes a Mutator at the same path.\n * Uses TInput for the callable args (TOutput is only used internally for validation).\n */\nexport type ToMutatorTree<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n> = {\n readonly [K in keyof MD]: MD[K] extends AnyMutatorDefinition\n ? // pull types from the phantom property\n Mutator<\n MD[K]['~']['$input'],\n TSchema,\n MD[K]['~']['$context'],\n MD[K]['~']['$wrappedTransaction']\n >\n : MD[K] extends MutatorDefinitions\n ? ToMutatorTree<MD[K], TSchema>\n : never;\n};\n\nexport type FromMutatorTree<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n> = {\n readonly [K in keyof MD]: MD[K] extends AnyMutatorDefinition\n ? // pull types from the phantom property\n Mutator<\n ReadonlyJSONValue | undefined, // intentionally left as generic to avoid variance issues\n TSchema,\n MD[K]['~']['$context'],\n MD[K]['~']['$wrappedTransaction']\n >\n : MD[K] extends MutatorDefinitions\n ? FromMutatorTree<MD[K], TSchema>\n : never;\n}[keyof MD];\n\n/**\n * A tree of MutatorDefinitions, possibly nested.\n */\nexport type MutatorDefinitions = {\n readonly [key: string]: AnyMutatorDefinition | MutatorDefinitions;\n};\n\nfunction createMutator<\n ArgsInput extends ReadonlyJSONValue | undefined,\n ArgsOutput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n C,\n TWrappedTransaction,\n>(\n name: string,\n definition: MutatorDefinition<ArgsInput, ArgsOutput, C, TWrappedTransaction>,\n): Mutator<ArgsInput, TSchema, C, TWrappedTransaction> {\n const {validator} = definition;\n\n // fn takes ReadonlyJSONValue args because it's called during rebase (from\n // stored JSON) and on the server (from wire format). Validation happens here.\n const fn: MutatorDefinitionFunction<\n ArgsInput,\n C,\n Transaction<TSchema, TWrappedTransaction>\n > = async options => {\n const validatedArgs = validator\n ? validateInput(name, options.args, validator, 'mutator')\n : (options.args as unknown as ArgsOutput);\n await definition.fn({\n args: validatedArgs,\n ctx: options.ctx,\n tx: options.tx,\n });\n };\n\n const mutator = (\n args: ArgsInput,\n ): MutateRequest<ArgsInput, TSchema, C, TWrappedTransaction> => ({\n args,\n '~': 'MutateRequest' as MutateRequestTypes<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >,\n 'mutator': mutator as unknown as Mutator<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >,\n });\n\n mutator.mutatorName = name;\n mutator.fn = fn;\n mutator['~'] = 'Mutator' as unknown as MutatorTypes<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >;\n\n return mutator as unknown as Mutator<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >;\n}\n\nexport function* iterateMutators(\n registry: AnyMutatorRegistry,\n): Iterable<AnyMutator> {\n yield* iterateLeaves(registry, isMutator);\n}\n\n/**\n * Gets a Mutator by its dot-separated name from a MutatorRegistry.\n * Returns undefined if not found.\n */\nexport function getMutator<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n>(\n registry: MutatorRegistry<MD, TSchema>,\n name: string,\n): FromMutatorTree<MD, TSchema> | undefined {\n const m = getValueAtPath(registry, name, '.');\n return m as FromMutatorTree<MD, TSchema> | undefined;\n}\n\n/**\n * Gets a Mutator by its dot-separated name from a MutatorRegistry.\n * Throws if not found.\n */\nexport function mustGetMutator<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n>(\n registry: MutatorRegistry<MD, TSchema>,\n name: string,\n): FromMutatorTree<MD, TSchema> {\n const mutator = getMutator(registry, name);\n if (mutator === undefined) {\n throw new Error(`Mutator not found: ${name}`);\n }\n return mutator;\n}\n"],"mappings":";;;;;AAgGA,SAAgB,eACd,mBACA,gBACoB;CACpB,SAAS,mBACP,aACA,MACkC;EAClC,MAAM,SAA2C,GAC9C,MAAM,mBACR;AAED,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,EAAE;AACtD,QAAK,KAAK,IAAI;GACd,MAAM,OAAO,KAAK,KAAK,IAAI;AAE3B,OAAI,oBAAoB,MAAM,CAC5B,QAAO,OAAO,cAAc,MAAM,MAAM;OAGxC,QAAO,OAAO,mBAAmB,OAAO,KAAK;AAE/C,QAAK,KAAK;;AAGZ,SAAO;;AAGT,KAAI,mBAAmB,KAAA,GAAW;EAEhC,IAAI;AACJ,MAAI,CAAC,kBAAkB,kBAAkB,CACvC,QAAO,mBAAmB,mBAAmB,EAAE,CAAC;MAEhD,QAAO;EAGT,MAAM,YAAY,mBAAmB,gBAAgB,EAAE,CAAC;EAExD,MAAM,SAAS,UAAU,MAAM,WAAW,cAAc;AACxD,SAAO,OAAO;AACd,SAAO;;AAGT,QAAO,mBACL,mBACA,EAAE,CACH;;AAGH,IAAM,iBAAiB,UACrB,CAAC,cAAc,MAAM,IAAI,UAAU,MAAM;;;;AAK3C,SAAgB,yBAEkB;AAChC,QAAO;;;;;AAwCT,SAAgB,kBAAkB,OAA6C;AAC7E,QACE,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,SAAS;;AA0EhD,SAAS,cAOP,MACA,YACqD;CACrD,MAAM,EAAC,cAAa;CAIpB,MAAM,KAIF,OAAM,YAAW;EACnB,MAAM,gBAAgB,YAClB,cAAc,MAAM,QAAQ,MAAM,WAAW,UAAU,GACtD,QAAQ;AACb,QAAM,WAAW,GAAG;GAClB,MAAM;GACN,KAAK,QAAQ;GACb,IAAI,QAAQ;GACb,CAAC;;CAGJ,MAAM,WACJ,UAC+D;EAC/D;EACA,KAAK;EAML,WAAW;EAMZ;AAED,SAAQ,cAAc;AACtB,SAAQ,KAAK;AACb,SAAQ,OAAO;AAOf,QAAO;;AAQT,UAAiB,gBACf,UACsB;AACtB,QAAO,cAAc,UAAU,UAAU;;;;;;AAO3C,SAAgB,WAId,UACA,MAC0C;AAE1C,QADU,eAAe,UAAU,MAAM,IAAI;;;;;;AAQ/C,SAAgB,eAId,UACA,MAC8B;CAC9B,MAAM,UAAU,WAAW,UAAU,KAAK;AAC1C,KAAI,YAAY,KAAA,EACd,OAAM,IAAI,MAAM,sBAAsB,OAAO;AAE/C,QAAO"}
|
|
1
|
+
{"version":3,"file":"mutator-registry.js","names":[],"sources":["../../../../../zql/src/mutate/mutator-registry.ts"],"sourcesContent":["import {\n deepMerge,\n isPlainObject,\n type DeepMerge,\n} from '../../../shared/src/deep-merge.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {\n getValueAtPath,\n iterateLeaves,\n} from '../../../shared/src/object-traversal.ts';\nimport type {\n BaseDefaultSchema,\n DefaultSchema,\n} from '../../../zero-types/src/default-types.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {validateInput} from '../query/validate-input.ts';\nimport type {Transaction} from './custom.ts';\nimport {\n isMutator,\n isMutatorDefinition,\n type AnyMutator,\n type AnyMutatorDefinition,\n type MutateRequest,\n type MutateRequestTypes,\n type Mutator,\n type MutatorDefinition,\n type MutatorExecutionFunction,\n type MutatorTypes,\n} from './mutator.ts';\n\n/**\n * Creates a MutatorRegistry from a tree of MutatorDefinitions,\n * optionally extending a base MutatorRegistry.\n *\n * @example\n * ```ts\n * // Create a new registry\n * const mutators = defineMutators({\n * user: {\n * create: defineMutator(...),\n * delete: defineMutator(...),\n * },\n * post: {\n * publish: defineMutator(...),\n * },\n * });\n *\n * // Extend an existing registry (e.g., for server-side overrides)\n * const serverMutators = defineMutators(mutators, {\n * user: {\n * create: defineMutator(...), // overrides mutators.user.create\n * },\n * // post.publish is inherited from mutators\n * });\n *\n * // Access mutators by path\n * const mr = mutators.user.create({name: 'Alice'});\n *\n * // Execute on client\n * zero.mutate(mr);\n *\n * // Execute on server\n * mr.mutator.fn({tx, ctx, args: mr.args});\n *\n * // Lookup by name (for server-side dispatch)\n * const mutator = getMutator(mutators, 'user.create');\n * ```\n */\nexport function defineMutators<\n // let MD infer freely so defaults aren't erased by a MutatorDefinitions constraint\n const MD,\n S extends BaseDefaultSchema = DefaultSchema,\n>(\n // we assert types here for clear error messages\n definitions: MD & AssertMutatorDefinitions<MD>,\n): MutatorRegistry<EnsureMutatorDefinitions<MD>, S>;\n\nexport function defineMutators<\n // same as MD above, but for TBase and TOverrides\n const TBase,\n const TOverrides,\n S extends BaseDefaultSchema = DefaultSchema,\n>(\n base:\n | MutatorRegistry<EnsureMutatorDefinitions<TBase>, S>\n | (TBase & AssertMutatorDefinitions<TBase>),\n overrides: TOverrides & AssertMutatorDefinitions<TOverrides>,\n): MutatorRegistry<\n DeepMerge<\n EnsureMutatorDefinitions<TBase>,\n EnsureMutatorDefinitions<TOverrides>,\n AnyMutatorDefinition\n >,\n S\n>;\n\nexport function defineMutators(\n definitionsOrBase: MutatorDefinitions | AnyMutatorRegistry,\n maybeOverrides?: MutatorDefinitions,\n): AnyMutatorRegistry {\n function processDefinitions(\n definitions: MutatorDefinitions,\n path: string[],\n ): Record<string | symbol, unknown> {\n const result: Record<string | symbol, unknown> = {\n ['~']: 'MutatorRegistry',\n };\n\n for (const [key, value] of Object.entries(definitions)) {\n path.push(key);\n const name = path.join('.');\n\n if (isMutatorDefinition(value)) {\n result[key] = createMutator(name, value);\n } else {\n // Nested definitions\n result[key] = processDefinitions(value, path);\n }\n path.pop();\n }\n\n return result;\n }\n\n if (maybeOverrides !== undefined) {\n // Merge base and overrides\n let base: Record<string | symbol, unknown>;\n if (!isMutatorRegistry(definitionsOrBase)) {\n base = processDefinitions(definitionsOrBase, []);\n } else {\n base = definitionsOrBase;\n }\n\n const processed = processDefinitions(maybeOverrides, []);\n\n const merged = deepMerge(base, processed, isMutatorLeaf);\n merged['~'] = 'MutatorRegistry';\n return merged as AnyMutatorRegistry;\n }\n\n return processDefinitions(\n definitionsOrBase as MutatorDefinitions,\n [],\n ) as AnyMutatorRegistry;\n}\n\nconst isMutatorLeaf = (value: unknown): boolean =>\n !isPlainObject(value) || isMutator(value);\n\n/**\n * Creates a function that can be used to define mutators with a specific schema.\n */\nexport function defineMutatorsWithType<\n TSchema extends Schema,\n>(): TypedDefineMutators<TSchema> {\n return defineMutators;\n}\n\n/**\n * The return type of defineMutatorsWithType. A function matching the\n * defineMutators overloads but with Schema pre-bound.\n */\ntype TypedDefineMutators<S extends Schema> = {\n // Single definitions\n <MD>(\n definitions: MD & AssertMutatorDefinitions<MD>,\n ): MutatorRegistry<EnsureMutatorDefinitions<MD>, S>;\n\n // Base and overrides\n <TBase, TOverrides>(\n base:\n | MutatorRegistry<EnsureMutatorDefinitions<TBase>, S>\n | (TBase & AssertMutatorDefinitions<TBase>),\n overrides: TOverrides,\n ): MutatorRegistry<\n DeepMerge<\n EnsureMutatorDefinitions<TBase>,\n EnsureMutatorDefinitions<TOverrides>,\n AnyMutatorDefinition\n >,\n S\n >;\n};\n\nexport type AssertMutatorDefinitions<MD> = MD extends MutatorDefinitions\n ? unknown\n : never;\n\nexport type EnsureMutatorDefinitions<MD> = MD extends MutatorDefinitions\n ? MD\n : never;\n\n/**\n * Checks if a value is a MutatorRegistry.\n */\nexport function isMutatorRegistry(value: unknown): value is AnyMutatorRegistry {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<string, unknown>)['~'] === 'MutatorRegistry'\n );\n}\n\nexport type MutatorRegistryTypes<TSchema extends Schema> = 'MutatorRegistry' & {\n readonly $schema: TSchema;\n};\n\n/**\n * The result of defineMutators(). A tree of Mutators with a tag for detection.\n */\nexport type MutatorRegistry<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n> = ToMutatorTree<MD, TSchema> & {\n ['~']: MutatorRegistryTypes<TSchema>;\n};\n\n/**\n * A branded type for use in type constraints. Use this instead of\n * `MutatorRegistry<S, C, any>` to avoid TypeScript drilling into\n * the complex ToMutatorTree structure and hitting variance issues.\n */\nexport type AnyMutatorRegistry = {\n ['~']: MutatorRegistryTypes<Schema>;\n [key: string]: unknown;\n};\n\n/**\n * Transforms a MutatorDefinitions into a tree of Mutators.\n * Each MutatorDefinition becomes a Mutator at the same path.\n * Uses TInput for the callable args (TOutput is only used internally for validation).\n */\nexport type ToMutatorTree<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n> = {\n readonly [K in keyof MD]: MD[K] extends AnyMutatorDefinition\n ? // pull types from the phantom property\n Mutator<\n MD[K]['~']['$input'],\n TSchema,\n MD[K]['~']['$context'],\n MD[K]['~']['$wrappedTransaction']\n >\n : MD[K] extends MutatorDefinitions\n ? ToMutatorTree<MD[K], TSchema>\n : never;\n};\n\nexport type FromMutatorTree<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n> = {\n readonly [K in keyof MD]: MD[K] extends AnyMutatorDefinition\n ? // pull types from the phantom property\n Mutator<\n ReadonlyJSONValue | undefined, // intentionally left as generic to avoid variance issues\n TSchema,\n MD[K]['~']['$context'],\n MD[K]['~']['$wrappedTransaction']\n >\n : MD[K] extends MutatorDefinitions\n ? FromMutatorTree<MD[K], TSchema>\n : never;\n}[keyof MD];\n\n/**\n * A tree of MutatorDefinitions, possibly nested.\n */\nexport type MutatorDefinitions = {\n readonly [key: string]: AnyMutatorDefinition | MutatorDefinitions;\n};\n\nfunction createMutator<\n ArgsInput extends ReadonlyJSONValue | undefined,\n ArgsOutput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n C,\n TWrappedTransaction,\n>(\n name: string,\n definition: MutatorDefinition<ArgsInput, ArgsOutput, C, TWrappedTransaction>,\n): Mutator<ArgsInput, TSchema, C, TWrappedTransaction> {\n const {validator} = definition;\n\n // fn takes ReadonlyJSONValue args because it's called during rebase (from\n // stored JSON) and on the server (from wire format). Validation happens here.\n const fn: MutatorExecutionFunction<\n ArgsInput,\n C,\n Transaction<TSchema, TWrappedTransaction>\n > = async options => {\n const validatedArgs = validator\n ? validateInput(name, options.args, validator, 'mutator')\n : (options.args as unknown as ArgsOutput);\n await definition.fn({\n args: validatedArgs,\n ctx: options.ctx as C,\n tx: options.tx,\n });\n };\n\n const mutator = (\n args: ArgsInput,\n ): MutateRequest<ArgsInput, TSchema, C, TWrappedTransaction> => ({\n args,\n '~': 'MutateRequest' as MutateRequestTypes<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >,\n 'mutator': mutator as unknown as Mutator<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >,\n });\n\n mutator.mutatorName = name;\n mutator.fn = fn;\n mutator['~'] = 'Mutator' as unknown as MutatorTypes<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >;\n\n return mutator as unknown as Mutator<\n ArgsInput,\n TSchema,\n C,\n TWrappedTransaction\n >;\n}\n\nexport function* iterateMutators(\n registry: AnyMutatorRegistry,\n): Iterable<AnyMutator> {\n yield* iterateLeaves(registry, isMutator);\n}\n\n/**\n * Gets a Mutator by its dot-separated name from a MutatorRegistry.\n * Returns undefined if not found.\n */\nexport function getMutator<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n>(\n registry: MutatorRegistry<MD, TSchema>,\n name: string,\n): FromMutatorTree<MD, TSchema> | undefined {\n const m = getValueAtPath(registry, name, '.');\n return m as FromMutatorTree<MD, TSchema> | undefined;\n}\n\n/**\n * Gets a Mutator by its dot-separated name from a MutatorRegistry.\n * Throws if not found.\n */\nexport function mustGetMutator<\n MD extends MutatorDefinitions,\n TSchema extends Schema,\n>(\n registry: MutatorRegistry<MD, TSchema>,\n name: string,\n): FromMutatorTree<MD, TSchema> {\n const mutator = getMutator(registry, name);\n if (mutator === undefined) {\n throw new Error(`Mutator not found: ${name}`);\n }\n return mutator;\n}\n"],"mappings":";;;;;AAgGA,SAAgB,eACd,mBACA,gBACoB;CACpB,SAAS,mBACP,aACA,MACkC;EAClC,MAAM,SAA2C,GAC9C,MAAM,mBACR;AAED,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,EAAE;AACtD,QAAK,KAAK,IAAI;GACd,MAAM,OAAO,KAAK,KAAK,IAAI;AAE3B,OAAI,oBAAoB,MAAM,CAC5B,QAAO,OAAO,cAAc,MAAM,MAAM;OAGxC,QAAO,OAAO,mBAAmB,OAAO,KAAK;AAE/C,QAAK,KAAK;;AAGZ,SAAO;;AAGT,KAAI,mBAAmB,KAAA,GAAW;EAEhC,IAAI;AACJ,MAAI,CAAC,kBAAkB,kBAAkB,CACvC,QAAO,mBAAmB,mBAAmB,EAAE,CAAC;MAEhD,QAAO;EAGT,MAAM,YAAY,mBAAmB,gBAAgB,EAAE,CAAC;EAExD,MAAM,SAAS,UAAU,MAAM,WAAW,cAAc;AACxD,SAAO,OAAO;AACd,SAAO;;AAGT,QAAO,mBACL,mBACA,EAAE,CACH;;AAGH,IAAM,iBAAiB,UACrB,CAAC,cAAc,MAAM,IAAI,UAAU,MAAM;;;;AAK3C,SAAgB,yBAEkB;AAChC,QAAO;;;;;AAwCT,SAAgB,kBAAkB,OAA6C;AAC7E,QACE,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,SAAS;;AA0EhD,SAAS,cAOP,MACA,YACqD;CACrD,MAAM,EAAC,cAAa;CAIpB,MAAM,KAIF,OAAM,YAAW;EACnB,MAAM,gBAAgB,YAClB,cAAc,MAAM,QAAQ,MAAM,WAAW,UAAU,GACtD,QAAQ;AACb,QAAM,WAAW,GAAG;GAClB,MAAM;GACN,KAAK,QAAQ;GACb,IAAI,QAAQ;GACb,CAAC;;CAGJ,MAAM,WACJ,UAC+D;EAC/D;EACA,KAAK;EAML,WAAW;EAMZ;AAED,SAAQ,cAAc;AACtB,SAAQ,KAAK;AACb,SAAQ,OAAO;AAOf,QAAO;;AAQT,UAAiB,gBACf,UACsB;AACtB,QAAO,cAAc,UAAU,UAAU;;;;;;AAO3C,SAAgB,WAId,UACA,MAC0C;AAE1C,QADU,eAAe,UAAU,MAAM,IAAI;;;;;;AAQ/C,SAAgB,eAId,UACA,MAC8B;CAC9B,MAAM,UAAU,WAAW,UAAU,KAAK;AAC1C,KAAI,YAAY,KAAA,EACd,OAAM,IAAI,MAAM,sBAAsB,OAAO;AAE/C,QAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
import type { ReadonlyJSONValue } from '../../../shared/src/json.ts';
|
|
3
|
-
import type { DefaultContext, DefaultSchema, DefaultWrappedTransaction } from '../../../zero-types/src/default-types.ts';
|
|
3
|
+
import type { DefaultContext, DefaultSchema, DefaultWrappedTransaction, IsUnknown } from '../../../zero-types/src/default-types.ts';
|
|
4
4
|
import type { Schema } from '../../../zero-types/src/schema.ts';
|
|
5
5
|
import type { AnyTransaction, Transaction } from './custom.ts';
|
|
6
6
|
export type MutatorDefinitionTypes<TInput extends ReadonlyJSONValue | undefined, TOutput, TContext, TWrappedTransaction> = 'MutatorDefinition' & {
|
|
@@ -36,6 +36,15 @@ export type MutatorDefinitionFunction<TOutput extends ReadonlyJSONValue | undefi
|
|
|
36
36
|
ctx: TContext;
|
|
37
37
|
tx: TTransaction;
|
|
38
38
|
}) => Promise<void>;
|
|
39
|
+
export type MutatorExecutionFunction<TOutput extends ReadonlyJSONValue | undefined, TContext, TTransaction> = (options: IsUnknown<TContext> extends true ? {
|
|
40
|
+
args: TOutput;
|
|
41
|
+
tx: TTransaction;
|
|
42
|
+
ctx?: TContext;
|
|
43
|
+
} : {
|
|
44
|
+
args: TOutput;
|
|
45
|
+
tx: TTransaction;
|
|
46
|
+
ctx: TContext;
|
|
47
|
+
}) => Promise<void>;
|
|
39
48
|
export type MutatorTypes<TInput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TContext, TWrappedTransaction> = 'Mutator' & {
|
|
40
49
|
readonly $input: TInput;
|
|
41
50
|
readonly $schema: TSchema;
|
|
@@ -58,7 +67,7 @@ export type Mutator<TInput extends ReadonlyJSONValue | undefined, TSchema extend
|
|
|
58
67
|
* during rebase (from stored JSON) and on the server (from wire format).
|
|
59
68
|
* Validation happens internally before the recipe function runs.
|
|
60
69
|
*/
|
|
61
|
-
readonly 'fn':
|
|
70
|
+
readonly 'fn': MutatorExecutionFunction<TInput, TContext, Transaction<TSchema, TWrappedTransaction>>;
|
|
62
71
|
readonly '~': MutatorTypes<TInput, TSchema, TContext, TWrappedTransaction>;
|
|
63
72
|
} & MutatorCallable<TInput, TSchema, TContext, TWrappedTransaction>;
|
|
64
73
|
type MutatorCallable<TInput extends ReadonlyJSONValue | undefined, TSchema extends Schema, TContext, TWrappedTransaction> = [TInput] extends [undefined] ? () => MutateRequest<TInput, TSchema, TContext, TWrappedTransaction> : undefined extends TInput ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutator.d.ts","sourceRoot":"","sources":["../../../../../zql/src/mutate/mutator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAEnE,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,yBAAyB,
|
|
1
|
+
{"version":3,"file":"mutator.d.ts","sourceRoot":"","sources":["../../../../../zql/src/mutate/mutator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutator.js","names":[],"sources":["../../../../../zql/src/mutate/mutator.ts"],"sourcesContent":["import type {StandardSchemaV1} from '@standard-schema/spec';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n DefaultContext,\n DefaultSchema,\n DefaultWrappedTransaction,\n} from '../../../zero-types/src/default-types.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {AnyTransaction, Transaction} from './custom.ts';\n\n// ----------------------------------------------------------------------------\n// defineMutator\n// ----------------------------------------------------------------------------\n\nexport type MutatorDefinitionTypes<\n TInput extends ReadonlyJSONValue | undefined,\n TOutput,\n TContext,\n TWrappedTransaction,\n> = 'MutatorDefinition' & {\n readonly $input: TInput;\n readonly $output: TOutput;\n readonly $context: TContext;\n readonly $wrappedTransaction: TWrappedTransaction;\n};\n\nexport type MutatorDefinition<\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n> = {\n readonly 'fn': MutatorDefinitionFunction<TOutput, TContext, AnyTransaction>;\n readonly 'validator': StandardSchemaV1<TInput, TOutput> | undefined;\n readonly '~': MutatorDefinitionTypes<\n TInput,\n TOutput,\n TContext,\n TWrappedTransaction\n >;\n};\n\n// oxlint-disable-next-line no-explicit-any\nexport type AnyMutatorDefinition = MutatorDefinition<any, any, any, any>;\n\nexport function isMutatorDefinition(f: unknown): f is AnyMutatorDefinition {\n return (\n typeof f === 'object' &&\n f !== null &&\n (f as {['~']?: unknown})['~'] === 'MutatorDefinition'\n );\n}\n\n// Overload for no validator\nexport function defineMutator<\n TInput extends ReadonlyJSONValue | undefined = ReadonlyJSONValue | undefined,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n>(\n mutator: MutatorDefinitionFunction<\n TInput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n): MutatorDefinition<TInput, TInput, TContext, TWrappedTransaction>;\n\n// Overload for validator\nexport function defineMutator<\n TInput extends ReadonlyJSONValue | undefined = undefined,\n TOutput extends ReadonlyJSONValue | undefined = TInput,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n>(\n validator: StandardSchemaV1<TInput, TOutput>,\n mutator: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n): MutatorDefinition<TInput, TOutput, TContext, TWrappedTransaction>;\n\n// Implementation\nexport function defineMutator<\n TInput extends ReadonlyJSONValue | undefined = undefined,\n TOutput extends ReadonlyJSONValue | undefined = TInput,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n>(\n validatorOrMutator:\n | StandardSchemaV1<TInput, TOutput>\n | MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n mutator?: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n): MutatorDefinition<TInput, TOutput, TContext, TWrappedTransaction> {\n let validator: StandardSchemaV1<TInput, TOutput> | undefined;\n let actualMutator: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >;\n\n if ('~standard' in validatorOrMutator) {\n // defineMutator(validator, mutator)\n validator = validatorOrMutator;\n actualMutator = must(mutator);\n } else {\n // defineMutator(mutator) - no validator\n validator = undefined;\n actualMutator = validatorOrMutator;\n }\n\n const mutatorDefinition: MutatorDefinition<\n TInput,\n TOutput,\n TContext,\n TWrappedTransaction\n > = {\n 'fn': actualMutator as MutatorDefinitionFunction<\n TOutput,\n TContext,\n AnyTransaction\n >,\n 'validator': validator,\n '~': 'MutatorDefinition' as unknown as MutatorDefinitionTypes<\n TInput,\n TOutput,\n TContext,\n TWrappedTransaction\n >,\n };\n return mutatorDefinition;\n}\n\n// intentionally not using DefaultSchema, DefaultContext, or DefaultWrappedTransaction\nexport function defineMutatorWithType<\n TSchema extends Schema,\n TContext = unknown,\n TWrappedTransaction = unknown,\n>(): TypedDefineMutator<TSchema, TContext, TWrappedTransaction> {\n return defineMutator;\n}\n\n/**\n * The return type of defineMutatorWithType. A function matching the\n * defineMutator overloads but with Schema, Context, and WrappedTransaction\n * pre-bound.\n *\n * This is used as a workaround to using DefaultTypes (e.g. when using\n * multiple Zero instances).\n */\ntype TypedDefineMutator<\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = {\n // Without validator\n <TArgs extends ReadonlyJSONValue | undefined>(\n mutator: MutatorDefinitionFunction<\n TArgs,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n ): MutatorDefinition<TArgs, TArgs, TContext, TWrappedTransaction>;\n\n // With validator\n <\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n >(\n validator: StandardSchemaV1<TInput, TOutput>,\n mutator: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n ): MutatorDefinition<TInput, TOutput, TContext, TWrappedTransaction>;\n};\n\nexport type MutatorDefinitionFunction<\n TOutput extends ReadonlyJSONValue | undefined,\n TContext,\n TTransaction,\n> = (options: {\n args: TOutput;\n ctx: TContext;\n tx: TTransaction;\n}) => Promise<void>;\n\n// ----------------------------------------------------------------------------\n// Mutator and MutateRequest types\n// ----------------------------------------------------------------------------\n\nexport type MutatorTypes<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = 'Mutator' & {\n readonly $input: TInput;\n readonly $schema: TSchema;\n readonly $context: TContext;\n readonly $wrappedTransaction: TWrappedTransaction;\n};\n\n/**\n * A callable wrapper around a MutatorDefinition, created by `defineMutators()`.\n *\n * Accessed like `mutators.foo.bar`, and called to create a MutateRequest:\n * `mutators.foo.bar(42)` returns a `MutateRequest`.\n *\n * The `fn` property is used for execution and takes raw JSON args (for rebase\n * and server wire format cases) that are validated internally.\n */\nexport type Mutator<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n> = {\n readonly 'mutatorName': string;\n /**\n * Execute the mutation. Args are ReadonlyJSONValue because this is called\n * during rebase (from stored JSON) and on the server (from wire format).\n * Validation happens internally before the recipe function runs.\n */\n readonly 'fn': MutatorDefinitionFunction<\n TInput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >;\n readonly '~': MutatorTypes<TInput, TSchema, TContext, TWrappedTransaction>;\n} & MutatorCallable<TInput, TSchema, TContext, TWrappedTransaction>;\n\n// Helper type for the callable part of Mutator\n// When TInput is undefined, the function is callable with 0 args\n// When TInput includes undefined (optional), args is optional\n// Otherwise, args is required\ntype MutatorCallable<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = [TInput] extends [undefined]\n ? () => MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>\n : undefined extends TInput\n ? {\n (): MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>;\n (\n args?: TInput,\n ): MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>;\n }\n : {\n (\n args: TInput,\n ): MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>;\n };\n\n// oxlint-disable-next-line no-explicit-any\nexport type AnyMutator = Mutator<any, any, any, any>;\n\n/**\n * Checks if a value is a Mutator (the result of processing a MutatorDefinition\n * through defineMutators).\n */\nexport function isMutator<S extends Schema>(\n value: unknown,\n // oxlint-disable-next-line no-explicit-any\n): value is Mutator<any, S, any, any> {\n return (\n typeof value === 'function' &&\n typeof (value as {mutatorName?: unknown}).mutatorName === 'string' &&\n typeof (value as {fn?: unknown}).fn === 'function'\n );\n}\n\nexport type MutateRequestTypes<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = 'MutateRequest' & {\n readonly $input: TInput;\n readonly $schema: TSchema;\n readonly $context: TContext;\n readonly $wrappedTransaction: TWrappedTransaction;\n};\n\n/**\n * The result of calling a Mutator with arguments.\n *\n * Created by `mutators.foo.bar(42)`, executed by `zero.mutate(mr)` on the client\n * or `mr.mutator.fn({tx, ctx, args: mr.args})` on the server.\n */\nexport type MutateRequest<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n> = {\n readonly 'mutator': Mutator<TInput, TSchema, TContext, TWrappedTransaction>;\n readonly 'args': TInput;\n readonly '~': MutateRequestTypes<\n TInput,\n TSchema,\n TContext,\n TWrappedTransaction\n >;\n};\n"],"mappings":";;AA8CA,SAAgB,oBAAoB,GAAuC;AACzE,QACE,OAAO,MAAM,YACb,MAAM,QACL,EAAwB,SAAS;;AAmCtC,SAAgB,cAOd,oBAOA,SAKmE;CACnE,IAAI;CACJ,IAAI;AAMJ,KAAI,eAAe,oBAAoB;AAErC,cAAY;AACZ,kBAAgB,KAAK,QAAQ;QACxB;AAEL,cAAY,KAAA;AACZ,kBAAgB;;AAsBlB,QAdI;EACF,MAAM;EAKN,aAAa;EACb,KAAK;EAMN;;AAKH,SAAgB,wBAIgD;AAC9D,QAAO;;;;;;AA6HT,SAAgB,UACd,OAEoC;AACpC,QACE,OAAO,UAAU,cACjB,OAAQ,MAAkC,gBAAgB,YAC1D,OAAQ,MAAyB,OAAO"}
|
|
1
|
+
{"version":3,"file":"mutator.js","names":[],"sources":["../../../../../zql/src/mutate/mutator.ts"],"sourcesContent":["import type {StandardSchemaV1} from '@standard-schema/spec';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n DefaultContext,\n DefaultSchema,\n DefaultWrappedTransaction,\n IsUnknown,\n} from '../../../zero-types/src/default-types.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {AnyTransaction, Transaction} from './custom.ts';\n\n// ----------------------------------------------------------------------------\n// defineMutator\n// ----------------------------------------------------------------------------\n\nexport type MutatorDefinitionTypes<\n TInput extends ReadonlyJSONValue | undefined,\n TOutput,\n TContext,\n TWrappedTransaction,\n> = 'MutatorDefinition' & {\n readonly $input: TInput;\n readonly $output: TOutput;\n readonly $context: TContext;\n readonly $wrappedTransaction: TWrappedTransaction;\n};\n\nexport type MutatorDefinition<\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n> = {\n readonly 'fn': MutatorDefinitionFunction<TOutput, TContext, AnyTransaction>;\n readonly 'validator': StandardSchemaV1<TInput, TOutput> | undefined;\n readonly '~': MutatorDefinitionTypes<\n TInput,\n TOutput,\n TContext,\n TWrappedTransaction\n >;\n};\n\n// oxlint-disable-next-line no-explicit-any\nexport type AnyMutatorDefinition = MutatorDefinition<any, any, any, any>;\n\nexport function isMutatorDefinition(f: unknown): f is AnyMutatorDefinition {\n return (\n typeof f === 'object' &&\n f !== null &&\n (f as {['~']?: unknown})['~'] === 'MutatorDefinition'\n );\n}\n\n// Overload for no validator\nexport function defineMutator<\n TInput extends ReadonlyJSONValue | undefined = ReadonlyJSONValue | undefined,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n>(\n mutator: MutatorDefinitionFunction<\n TInput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n): MutatorDefinition<TInput, TInput, TContext, TWrappedTransaction>;\n\n// Overload for validator\nexport function defineMutator<\n TInput extends ReadonlyJSONValue | undefined = undefined,\n TOutput extends ReadonlyJSONValue | undefined = TInput,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n>(\n validator: StandardSchemaV1<TInput, TOutput>,\n mutator: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n): MutatorDefinition<TInput, TOutput, TContext, TWrappedTransaction>;\n\n// Implementation\nexport function defineMutator<\n TInput extends ReadonlyJSONValue | undefined = undefined,\n TOutput extends ReadonlyJSONValue | undefined = TInput,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n>(\n validatorOrMutator:\n | StandardSchemaV1<TInput, TOutput>\n | MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n mutator?: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n): MutatorDefinition<TInput, TOutput, TContext, TWrappedTransaction> {\n let validator: StandardSchemaV1<TInput, TOutput> | undefined;\n let actualMutator: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >;\n\n if ('~standard' in validatorOrMutator) {\n // defineMutator(validator, mutator)\n validator = validatorOrMutator;\n actualMutator = must(mutator);\n } else {\n // defineMutator(mutator) - no validator\n validator = undefined;\n actualMutator = validatorOrMutator;\n }\n\n const mutatorDefinition: MutatorDefinition<\n TInput,\n TOutput,\n TContext,\n TWrappedTransaction\n > = {\n 'fn': actualMutator as MutatorDefinitionFunction<\n TOutput,\n TContext,\n AnyTransaction\n >,\n 'validator': validator,\n '~': 'MutatorDefinition' as unknown as MutatorDefinitionTypes<\n TInput,\n TOutput,\n TContext,\n TWrappedTransaction\n >,\n };\n return mutatorDefinition;\n}\n\n// intentionally not using DefaultSchema, DefaultContext, or DefaultWrappedTransaction\nexport function defineMutatorWithType<\n TSchema extends Schema,\n TContext = unknown,\n TWrappedTransaction = unknown,\n>(): TypedDefineMutator<TSchema, TContext, TWrappedTransaction> {\n return defineMutator;\n}\n\n/**\n * The return type of defineMutatorWithType. A function matching the\n * defineMutator overloads but with Schema, Context, and WrappedTransaction\n * pre-bound.\n *\n * This is used as a workaround to using DefaultTypes (e.g. when using\n * multiple Zero instances).\n */\ntype TypedDefineMutator<\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = {\n // Without validator\n <TArgs extends ReadonlyJSONValue | undefined>(\n mutator: MutatorDefinitionFunction<\n TArgs,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n ): MutatorDefinition<TArgs, TArgs, TContext, TWrappedTransaction>;\n\n // With validator\n <\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n >(\n validator: StandardSchemaV1<TInput, TOutput>,\n mutator: MutatorDefinitionFunction<\n TOutput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >,\n ): MutatorDefinition<TInput, TOutput, TContext, TWrappedTransaction>;\n};\n\nexport type MutatorDefinitionFunction<\n TOutput extends ReadonlyJSONValue | undefined,\n TContext,\n TTransaction,\n> = (options: {\n args: TOutput;\n ctx: TContext;\n tx: TTransaction;\n}) => Promise<void>;\n\nexport type MutatorExecutionFunction<\n TOutput extends ReadonlyJSONValue | undefined,\n TContext,\n TTransaction,\n> = (\n options: IsUnknown<TContext> extends true\n ? {args: TOutput; tx: TTransaction; ctx?: TContext}\n : {args: TOutput; tx: TTransaction; ctx: TContext},\n) => Promise<void>;\n\n// ----------------------------------------------------------------------------\n// Mutator and MutateRequest types\n// ----------------------------------------------------------------------------\n\nexport type MutatorTypes<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = 'Mutator' & {\n readonly $input: TInput;\n readonly $schema: TSchema;\n readonly $context: TContext;\n readonly $wrappedTransaction: TWrappedTransaction;\n};\n\n/**\n * A callable wrapper around a MutatorDefinition, created by `defineMutators()`.\n *\n * Accessed like `mutators.foo.bar`, and called to create a MutateRequest:\n * `mutators.foo.bar(42)` returns a `MutateRequest`.\n *\n * The `fn` property is used for execution and takes raw JSON args (for rebase\n * and server wire format cases) that are validated internally.\n */\nexport type Mutator<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n> = {\n readonly 'mutatorName': string;\n /**\n * Execute the mutation. Args are ReadonlyJSONValue because this is called\n * during rebase (from stored JSON) and on the server (from wire format).\n * Validation happens internally before the recipe function runs.\n */\n readonly 'fn': MutatorExecutionFunction<\n TInput,\n TContext,\n Transaction<TSchema, TWrappedTransaction>\n >;\n readonly '~': MutatorTypes<TInput, TSchema, TContext, TWrappedTransaction>;\n} & MutatorCallable<TInput, TSchema, TContext, TWrappedTransaction>;\n\n// Helper type for the callable part of Mutator\n// When TInput is undefined, the function is callable with 0 args\n// When TInput includes undefined (optional), args is optional\n// Otherwise, args is required\ntype MutatorCallable<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = [TInput] extends [undefined]\n ? () => MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>\n : undefined extends TInput\n ? {\n (): MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>;\n (\n args?: TInput,\n ): MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>;\n }\n : {\n (\n args: TInput,\n ): MutateRequest<TInput, TSchema, TContext, TWrappedTransaction>;\n };\n\n// oxlint-disable-next-line no-explicit-any\nexport type AnyMutator = Mutator<any, any, any, any>;\n\n/**\n * Checks if a value is a Mutator (the result of processing a MutatorDefinition\n * through defineMutators).\n */\nexport function isMutator<S extends Schema>(\n value: unknown,\n // oxlint-disable-next-line no-explicit-any\n): value is Mutator<any, S, any, any> {\n return (\n typeof value === 'function' &&\n typeof (value as {mutatorName?: unknown}).mutatorName === 'string' &&\n typeof (value as {fn?: unknown}).fn === 'function'\n );\n}\n\nexport type MutateRequestTypes<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema,\n TContext,\n TWrappedTransaction,\n> = 'MutateRequest' & {\n readonly $input: TInput;\n readonly $schema: TSchema;\n readonly $context: TContext;\n readonly $wrappedTransaction: TWrappedTransaction;\n};\n\n/**\n * The result of calling a Mutator with arguments.\n *\n * Created by `mutators.foo.bar(42)`, executed by `zero.mutate(mr)` on the client\n * or `mr.mutator.fn({tx, ctx, args: mr.args})` on the server.\n */\nexport type MutateRequest<\n TInput extends ReadonlyJSONValue | undefined,\n TSchema extends Schema = DefaultSchema,\n TContext = DefaultContext,\n TWrappedTransaction = DefaultWrappedTransaction,\n> = {\n readonly 'mutator': Mutator<TInput, TSchema, TContext, TWrappedTransaction>;\n readonly 'args': TInput;\n readonly '~': MutateRequestTypes<\n TInput,\n TSchema,\n TContext,\n TWrappedTransaction\n >;\n};\n"],"mappings":";;AA+CA,SAAgB,oBAAoB,GAAuC;AACzE,QACE,OAAO,MAAM,YACb,MAAM,QACL,EAAwB,SAAS;;AAmCtC,SAAgB,cAOd,oBAOA,SAKmE;CACnE,IAAI;CACJ,IAAI;AAMJ,KAAI,eAAe,oBAAoB;AAErC,cAAY;AACZ,kBAAgB,KAAK,QAAQ;QACxB;AAEL,cAAY,KAAA;AACZ,kBAAgB;;AAsBlB,QAdI;EACF,MAAM;EAKN,aAAa;EACb,KAAK;EAMN;;AAKH,SAAgB,wBAIgD;AAC9D,QAAO;;;;;;AAuIT,SAAgB,UACd,OAEoC;AACpC,QACE,OAAO,UAAU,cACjB,OAAQ,MAAkC,gBAAgB,YAC1D,OAAQ,MAAyB,OAAO"}
|