@rocicorp/zero 1.3.0-canary.0 → 1.3.0-canary.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/analyze-query/src/bin-analyze.js +7 -7
- package/out/analyze-query/src/bin-transform.js +3 -3
- package/out/ast-to-zql/src/bin.js +2 -2
- package/out/shared/src/logging.d.ts.map +1 -1
- package/out/shared/src/logging.js +1 -1
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/options.d.ts.map +1 -1
- package/out/shared/src/options.js +1 -1
- package/out/shared/src/options.js.map +1 -1
- package/out/zero/package.js +90 -88
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/zero-cache-dev.js +1 -1
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero/src/zero-out.js +1 -1
- package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js +2 -2
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +5 -14
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/network.d.ts +1 -1
- package/out/zero-cache/src/config/network.d.ts.map +1 -1
- package/out/zero-cache/src/config/network.js +1 -1
- package/out/zero-cache/src/config/network.js.map +1 -1
- package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +6 -3
- 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 +0 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js +3 -3
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/observability/events.d.ts.map +1 -1
- package/out/zero-cache/src/observability/events.js +1 -1
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/scripts/decommission.js +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +2 -2
- package/out/zero-cache/src/scripts/permissions.js +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +2 -2
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +19 -8
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/logging.js +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +22 -22
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/mutator.js +1 -1
- package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
- package/out/zero-cache/src/server/reaper.js +3 -3
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.js +2 -2
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +7 -7
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +3 -3
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +1 -1
- 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 +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +2 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +8 -5
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-monitor.js +2 -2
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +14 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +61 -2
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/heapz.d.ts.map +1 -1
- package/out/zero-cache/src/services/heapz.js +1 -1
- package/out/zero-cache/src/services/heapz.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +2 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +9 -6
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts +15 -4
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +31 -31
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +28 -28
- package/out/zero-cache/src/services/replicator/change-processor.js +2 -2
- package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +2 -2
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +2 -2
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/statz.d.ts.map +1 -1
- package/out/zero-cache/src/services/statz.js +2 -2
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +2 -2
- 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.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +6 -16
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +29 -37
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +2 -2
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +2 -2
- 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 +6 -6
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts +1 -1
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
- package/out/zero-cache/src/workers/connection.d.ts +1 -1
- package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/mutator.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 +2 -2
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.d.ts.map +1 -1
- package/out/zero-client/src/client/crud-impl.js +4 -13
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js +4 -13
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +0 -4
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-protocol/src/error.d.ts.map +1 -1
- package/out/zero-protocol/src/error.js +1 -1
- package/out/zero-protocol/src/error.js.map +1 -1
- package/out/zero-solid/src/solid-view.d.ts.map +1 -1
- package/out/zero-solid/src/solid-view.js +13 -13
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/array-view.d.ts.map +1 -1
- package/out/zql/src/ivm/array-view.js +26 -1
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/change-index-enum.d.ts +9 -0
- package/out/zql/src/ivm/change-index-enum.d.ts.map +1 -0
- package/out/zql/src/ivm/change-index.d.ts +5 -0
- package/out/zql/src/ivm/change-index.d.ts.map +1 -0
- package/out/zql/src/ivm/change-type-enum.d.ts +9 -0
- package/out/zql/src/ivm/change-type-enum.d.ts.map +1 -0
- package/out/zql/src/ivm/change-type.d.ts +5 -0
- package/out/zql/src/ivm/change-type.d.ts.map +1 -0
- package/out/zql/src/ivm/change.d.ts +20 -22
- package/out/zql/src/ivm/change.d.ts.map +1 -1
- package/out/zql/src/ivm/change.js +33 -0
- package/out/zql/src/ivm/change.js.map +1 -0
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +27 -38
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.d.ts +3 -2
- package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.d.ts +1 -1
- package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-out.js +1 -1
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +3 -3
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter-push.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-push.js +7 -7
- package/out/zql/src/ivm/filter-push.js.map +1 -1
- package/out/zql/src/ivm/filter.d.ts +1 -1
- package/out/zql/src/ivm/filter.d.ts.map +1 -1
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +49 -58
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-utils.d.ts +2 -6
- package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
- package/out/zql/src/ivm/join-utils.js +25 -25
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.d.ts.map +1 -1
- package/out/zql/src/ivm/join.js +32 -51
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +5 -10
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +51 -59
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.d.ts +3 -2
- package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js +98 -122
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/skip.d.ts +1 -1
- package/out/zql/src/ivm/skip.d.ts.map +1 -1
- package/out/zql/src/ivm/skip.js +2 -2
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/source-change-index-enum.d.ts +7 -0
- package/out/zql/src/ivm/source-change-index-enum.d.ts.map +1 -0
- package/out/zql/src/ivm/source-change-index.d.ts +5 -0
- package/out/zql/src/ivm/source-change-index.d.ts.map +1 -0
- package/out/zql/src/ivm/source.d.ts +11 -13
- package/out/zql/src/ivm/source.d.ts.map +1 -1
- package/out/zql/src/ivm/source.js +26 -0
- package/out/zql/src/ivm/source.js.map +1 -0
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +27 -50
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts +2 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +3 -3
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js +1 -1
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-debug.d.ts +2 -2
- package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-graph.d.ts +1 -1
- package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zqlite/src/internal/sql-inline.d.ts.map +1 -1
- package/out/zqlite/src/internal/sql-inline.js.map +1 -1
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +11 -11
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +94 -92
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ivm-branch.js","names":["#sources","#tables"],"sources":["../../../../../zero-client/src/client/ivm-branch.ts"],"sourcesContent":["import type {\n InternalDiff,\n InternalDiffOperation,\n NoIndexDiff,\n} from '../../../replicache/src/btree/node.ts';\nimport type {LazyStore} from '../../../replicache/src/dag/lazy-store.ts';\nimport {type Read, type Store} from '../../../replicache/src/dag/store.ts';\nimport {readFromHash} from '../../../replicache/src/db/read.ts';\nimport * as FormatVersion from '../../../replicache/src/format-version-enum.ts';\nimport type {Hash} from '../../../replicache/src/hash.ts';\nimport type {ZeroReadOptions} from '../../../replicache/src/replicache-options.ts';\nimport {diffBinarySearch} from '../../../replicache/src/subscriptions.ts';\nimport type {DiffsMap} from '../../../replicache/src/sync/diff.ts';\nimport {diff} from '../../../replicache/src/sync/diff.ts';\nimport {using, withRead} from '../../../replicache/src/with-transactions.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {wrapIterable} from '../../../shared/src/iterables.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {TableSchema} from '../../../zero-schema/src/table-schema.ts';\nimport {MemorySource} from '../../../zql/src/ivm/memory-source.ts';\nimport {consume} from '../../../zql/src/ivm/stream.ts';\nimport {ENTITIES_KEY_PREFIX, sourceNameFromKey} from './keys.ts';\n\n/**\n * Replicache needs to rebase mutations onto different\n * commits of it's b-tree. These mutations can have reads\n * in them and those reads must be run against the IVM sources.\n *\n * To ensure the reads get the correct state, the IVM\n * sources need to reflect the state of the commit\n * being rebased onto. `IVMSourceBranch` allows us to:\n * 1. fork the IVM sources\n * 2. patch them up to match the desired head\n * 3. run the reads against the forked sources\n *\n * (2) is expected to be a cheap operation as there should only\n * ever be a few outstanding diffs to apply given Zero is meant\n * to be run in a connected state.\n */\nexport class IVMSourceBranch {\n readonly #sources: Map<string, MemorySource | undefined>;\n readonly #tables: Record<string, TableSchema>;\n hash: Hash | undefined;\n\n constructor(\n tables: Record<string, TableSchema>,\n hash?: Hash,\n sources: Map<string, MemorySource | undefined> = new Map(),\n ) {\n this.#tables = tables;\n this.#sources = sources;\n this.hash = hash;\n }\n\n getSource(name: string): MemorySource | undefined {\n if (this.#sources.has(name)) {\n return this.#sources.get(name);\n }\n\n const schema = this.#tables[name];\n const source = schema\n ? new MemorySource(name, schema.columns, schema.primaryKey)\n : undefined;\n this.#sources.set(name, source);\n return source;\n }\n\n clear() {\n this.#sources.clear();\n }\n\n /**\n * Mutates the current branch, advancing it to the new head\n * by applying the given diffs.\n */\n advance(expectedHead: Hash | undefined, newHead: Hash, diffs: NoIndexDiff) {\n assert(\n this.hash === expectedHead,\n () =>\n `Expected head must match the main head. Got: ${this.hash}, expected: ${expectedHead}`,\n );\n\n applyDiffs(diffs, this);\n this.hash = newHead;\n }\n\n /**\n * Fork the branch and patch it up to match the desired head.\n */\n async forkToHead(\n store: LazyStore,\n desiredHead: Hash,\n readOptions?: ZeroReadOptions,\n ): Promise<IVMSourceBranch> {\n const fork = this.fork();\n\n if (fork.hash === desiredHead) {\n return fork;\n }\n\n await patchBranch(desiredHead, store, fork, readOptions);\n fork.hash = desiredHead;\n return fork;\n }\n\n /**\n * Creates a new IVMSourceBranch that is a copy of the current one.\n * This is a cheap operation since the b-trees are shared until a write is performed\n * and then only the modified nodes are copied.\n *\n * IVM branches are forked when we need to rebase mutations.\n * The mutations modify the fork rather than original branch.\n */\n fork() {\n return new IVMSourceBranch(\n this.#tables,\n this.hash,\n new Map(\n wrapIterable(this.#sources.entries()).map(([name, source]) => [\n name,\n source?.fork(),\n ]),\n ),\n );\n }\n}\n\nexport async function initFromStore(\n branch: IVMSourceBranch,\n hash: Hash,\n store: Store,\n) {\n const diffs: InternalDiffOperation[] = [];\n await withRead(store, async dagRead => {\n const read = await readFromHash(hash, dagRead, FormatVersion.Latest);\n for await (const entry of read.map.scan(ENTITIES_KEY_PREFIX)) {\n if (!entry[0].startsWith(ENTITIES_KEY_PREFIX)) {\n break;\n }\n diffs.push({\n op: 'add',\n key: entry[0],\n newValue: entry[1],\n });\n }\n });\n\n branch.advance(undefined, hash, diffs);\n}\n\nasync function patchBranch(\n desiredHead: Hash,\n store: LazyStore,\n fork: IVMSourceBranch,\n readOptions: ZeroReadOptions | undefined,\n) {\n const diffs = await computeDiffs(\n must(fork.hash),\n desiredHead,\n store,\n readOptions,\n );\n if (!diffs) {\n return;\n }\n applyDiffs(diffs, fork);\n}\n\nasync function computeDiffs(\n startHash: Hash,\n endHash: Hash,\n store: LazyStore,\n readOptions: ZeroReadOptions | undefined,\n): Promise<InternalDiff | undefined> {\n const readFn = (dagRead: Read) =>\n diff(\n startHash,\n endHash,\n dagRead,\n {\n shouldComputeDiffs: () => true,\n shouldComputeDiffsForIndex(_name) {\n return false;\n },\n },\n FormatVersion.Latest,\n );\n\n let diffs: DiffsMap;\n if (readOptions?.openLazySourceRead) {\n diffs = await using(store.read(readOptions.openLazySourceRead), readFn);\n } else if (readOptions?.openLazyRead) {\n diffs = await readFn(readOptions.openLazyRead);\n } else {\n diffs = await withRead(store, readFn);\n }\n\n return diffs.get('');\n}\n\nfunction applyDiffs(diffs: NoIndexDiff, branch: IVMSourceBranch) {\n for (\n let i = diffBinarySearch(diffs, ENTITIES_KEY_PREFIX, diff => diff.key);\n i < diffs.length;\n i++\n ) {\n const diff = diffs[i];\n const {key} = diff;\n if (!key.startsWith(ENTITIES_KEY_PREFIX)) {\n break;\n }\n const name = sourceNameFromKey(key);\n const source = must(branch.getSource(name));\n switch (diff.op) {\n case 'del':\n consume(
|
|
1
|
+
{"version":3,"file":"ivm-branch.js","names":["#sources","#tables"],"sources":["../../../../../zero-client/src/client/ivm-branch.ts"],"sourcesContent":["import type {\n InternalDiff,\n InternalDiffOperation,\n NoIndexDiff,\n} from '../../../replicache/src/btree/node.ts';\nimport type {LazyStore} from '../../../replicache/src/dag/lazy-store.ts';\nimport {type Read, type Store} from '../../../replicache/src/dag/store.ts';\nimport {readFromHash} from '../../../replicache/src/db/read.ts';\nimport * as FormatVersion from '../../../replicache/src/format-version-enum.ts';\nimport type {Hash} from '../../../replicache/src/hash.ts';\nimport type {ZeroReadOptions} from '../../../replicache/src/replicache-options.ts';\nimport {diffBinarySearch} from '../../../replicache/src/subscriptions.ts';\nimport type {DiffsMap} from '../../../replicache/src/sync/diff.ts';\nimport {diff} from '../../../replicache/src/sync/diff.ts';\nimport {using, withRead} from '../../../replicache/src/with-transactions.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {wrapIterable} from '../../../shared/src/iterables.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {TableSchema} from '../../../zero-schema/src/table-schema.ts';\nimport {MemorySource} from '../../../zql/src/ivm/memory-source.ts';\nimport {consume} from '../../../zql/src/ivm/stream.ts';\nimport {ENTITIES_KEY_PREFIX, sourceNameFromKey} from './keys.ts';\n\nimport {\n makeSourceChangeAdd,\n makeSourceChangeEdit,\n makeSourceChangeRemove,\n} from '../../../zql/src/ivm/source.ts';\n/**\n * Replicache needs to rebase mutations onto different\n * commits of it's b-tree. These mutations can have reads\n * in them and those reads must be run against the IVM sources.\n *\n * To ensure the reads get the correct state, the IVM\n * sources need to reflect the state of the commit\n * being rebased onto. `IVMSourceBranch` allows us to:\n * 1. fork the IVM sources\n * 2. patch them up to match the desired head\n * 3. run the reads against the forked sources\n *\n * (2) is expected to be a cheap operation as there should only\n * ever be a few outstanding diffs to apply given Zero is meant\n * to be run in a connected state.\n */\nexport class IVMSourceBranch {\n readonly #sources: Map<string, MemorySource | undefined>;\n readonly #tables: Record<string, TableSchema>;\n hash: Hash | undefined;\n\n constructor(\n tables: Record<string, TableSchema>,\n hash?: Hash,\n sources: Map<string, MemorySource | undefined> = new Map(),\n ) {\n this.#tables = tables;\n this.#sources = sources;\n this.hash = hash;\n }\n\n getSource(name: string): MemorySource | undefined {\n if (this.#sources.has(name)) {\n return this.#sources.get(name);\n }\n\n const schema = this.#tables[name];\n const source = schema\n ? new MemorySource(name, schema.columns, schema.primaryKey)\n : undefined;\n this.#sources.set(name, source);\n return source;\n }\n\n clear() {\n this.#sources.clear();\n }\n\n /**\n * Mutates the current branch, advancing it to the new head\n * by applying the given diffs.\n */\n advance(expectedHead: Hash | undefined, newHead: Hash, diffs: NoIndexDiff) {\n assert(\n this.hash === expectedHead,\n () =>\n `Expected head must match the main head. Got: ${this.hash}, expected: ${expectedHead}`,\n );\n\n applyDiffs(diffs, this);\n this.hash = newHead;\n }\n\n /**\n * Fork the branch and patch it up to match the desired head.\n */\n async forkToHead(\n store: LazyStore,\n desiredHead: Hash,\n readOptions?: ZeroReadOptions,\n ): Promise<IVMSourceBranch> {\n const fork = this.fork();\n\n if (fork.hash === desiredHead) {\n return fork;\n }\n\n await patchBranch(desiredHead, store, fork, readOptions);\n fork.hash = desiredHead;\n return fork;\n }\n\n /**\n * Creates a new IVMSourceBranch that is a copy of the current one.\n * This is a cheap operation since the b-trees are shared until a write is performed\n * and then only the modified nodes are copied.\n *\n * IVM branches are forked when we need to rebase mutations.\n * The mutations modify the fork rather than original branch.\n */\n fork() {\n return new IVMSourceBranch(\n this.#tables,\n this.hash,\n new Map(\n wrapIterable(this.#sources.entries()).map(([name, source]) => [\n name,\n source?.fork(),\n ]),\n ),\n );\n }\n}\n\nexport async function initFromStore(\n branch: IVMSourceBranch,\n hash: Hash,\n store: Store,\n) {\n const diffs: InternalDiffOperation[] = [];\n await withRead(store, async dagRead => {\n const read = await readFromHash(hash, dagRead, FormatVersion.Latest);\n for await (const entry of read.map.scan(ENTITIES_KEY_PREFIX)) {\n if (!entry[0].startsWith(ENTITIES_KEY_PREFIX)) {\n break;\n }\n diffs.push({\n op: 'add',\n key: entry[0],\n newValue: entry[1],\n });\n }\n });\n\n branch.advance(undefined, hash, diffs);\n}\n\nasync function patchBranch(\n desiredHead: Hash,\n store: LazyStore,\n fork: IVMSourceBranch,\n readOptions: ZeroReadOptions | undefined,\n) {\n const diffs = await computeDiffs(\n must(fork.hash),\n desiredHead,\n store,\n readOptions,\n );\n if (!diffs) {\n return;\n }\n applyDiffs(diffs, fork);\n}\n\nasync function computeDiffs(\n startHash: Hash,\n endHash: Hash,\n store: LazyStore,\n readOptions: ZeroReadOptions | undefined,\n): Promise<InternalDiff | undefined> {\n const readFn = (dagRead: Read) =>\n diff(\n startHash,\n endHash,\n dagRead,\n {\n shouldComputeDiffs: () => true,\n shouldComputeDiffsForIndex(_name) {\n return false;\n },\n },\n FormatVersion.Latest,\n );\n\n let diffs: DiffsMap;\n if (readOptions?.openLazySourceRead) {\n diffs = await using(store.read(readOptions.openLazySourceRead), readFn);\n } else if (readOptions?.openLazyRead) {\n diffs = await readFn(readOptions.openLazyRead);\n } else {\n diffs = await withRead(store, readFn);\n }\n\n return diffs.get('');\n}\n\nfunction applyDiffs(diffs: NoIndexDiff, branch: IVMSourceBranch) {\n for (\n let i = diffBinarySearch(diffs, ENTITIES_KEY_PREFIX, diff => diff.key);\n i < diffs.length;\n i++\n ) {\n const diff = diffs[i];\n const {key} = diff;\n if (!key.startsWith(ENTITIES_KEY_PREFIX)) {\n break;\n }\n const name = sourceNameFromKey(key);\n const source = must(branch.getSource(name));\n switch (diff.op) {\n case 'del':\n consume(source.push(makeSourceChangeRemove(diff.oldValue as Row)));\n break;\n case 'add':\n consume(source.push(makeSourceChangeAdd(diff.newValue as Row)));\n break;\n case 'change':\n consume(\n source.push(\n makeSourceChangeEdit(diff.newValue as Row, diff.oldValue as Row),\n ),\n );\n break;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,IAAa,kBAAb,MAAa,gBAAgB;CAC3B;CACA;CACA;CAEA,YACE,QACA,MACA,0BAAiD,IAAI,KAAK,EAC1D;AACA,QAAA,SAAe;AACf,QAAA,UAAgB;AAChB,OAAK,OAAO;;CAGd,UAAU,MAAwC;AAChD,MAAI,MAAA,QAAc,IAAI,KAAK,CACzB,QAAO,MAAA,QAAc,IAAI,KAAK;EAGhC,MAAM,SAAS,MAAA,OAAa;EAC5B,MAAM,SAAS,SACX,IAAI,aAAa,MAAM,OAAO,SAAS,OAAO,WAAW,GACzD,KAAA;AACJ,QAAA,QAAc,IAAI,MAAM,OAAO;AAC/B,SAAO;;CAGT,QAAQ;AACN,QAAA,QAAc,OAAO;;;;;;CAOvB,QAAQ,cAAgC,SAAe,OAAoB;AACzE,SACE,KAAK,SAAS,oBAEZ,gDAAgD,KAAK,KAAK,cAAc,eAC3E;AAED,aAAW,OAAO,KAAK;AACvB,OAAK,OAAO;;;;;CAMd,MAAM,WACJ,OACA,aACA,aAC0B;EAC1B,MAAM,OAAO,KAAK,MAAM;AAExB,MAAI,KAAK,SAAS,YAChB,QAAO;AAGT,QAAM,YAAY,aAAa,OAAO,MAAM,YAAY;AACxD,OAAK,OAAO;AACZ,SAAO;;;;;;;;;;CAWT,OAAO;AACL,SAAO,IAAI,gBACT,MAAA,QACA,KAAK,MACL,IAAI,IACF,aAAa,MAAA,QAAc,SAAS,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,CAC5D,MACA,QAAQ,MAAM,CACf,CAAC,CACH,CACF;;;AA2BL,eAAe,YACb,aACA,OACA,MACA,aACA;CACA,MAAM,QAAQ,MAAM,aAClB,KAAK,KAAK,KAAK,EACf,aACA,OACA,YACD;AACD,KAAI,CAAC,MACH;AAEF,YAAW,OAAO,KAAK;;AAGzB,eAAe,aACb,WACA,SACA,OACA,aACmC;CACnC,MAAM,UAAU,YACd,KACE,WACA,SACA,SACA;EACE,0BAA0B;EAC1B,2BAA2B,OAAO;AAChC,UAAO;;EAEV,EACD,EACD;CAEH,IAAI;AACJ,KAAI,aAAa,mBACf,SAAQ,MAAM,MAAM,MAAM,KAAK,YAAY,mBAAmB,EAAE,OAAO;UAC9D,aAAa,aACtB,SAAQ,MAAM,OAAO,YAAY,aAAa;KAE9C,SAAQ,MAAM,SAAS,OAAO,OAAO;AAGvC,QAAO,MAAM,IAAI,GAAG;;AAGtB,SAAS,WAAW,OAAoB,QAAyB;AAC/D,MACE,IAAI,IAAI,iBAAiB,OAAA,OAA4B,SAAQ,KAAK,IAAI,EACtE,IAAI,MAAM,QACV,KACA;EACA,MAAM,OAAO,MAAM;EACnB,MAAM,EAAC,QAAO;AACd,MAAI,CAAC,IAAI,WAAA,KAA+B,CACtC;EAEF,MAAM,OAAO,kBAAkB,IAAI;EACnC,MAAM,SAAS,KAAK,OAAO,UAAU,KAAK,CAAC;AAC3C,UAAQ,KAAK,IAAb;GACE,KAAK;AACH,YAAQ,OAAO,KAAK,uBAAuB,KAAK,SAAgB,CAAC,CAAC;AAClE;GACF,KAAK;AACH,YAAQ,OAAO,KAAK,oBAAoB,KAAK,SAAgB,CAAC,CAAC;AAC/D;GACF,KAAK;AACH,YACE,OAAO,KACL,qBAAqB,KAAK,UAAiB,KAAK,SAAgB,CACjE,CACF;AACD"}
|
|
@@ -49,10 +49,6 @@ export type ZeroOptions<S extends BaseDefaultSchema = DefaultSchema, MD extends
|
|
|
49
49
|
*
|
|
50
50
|
* Omit this, or set to `null`, for logged-out clients.
|
|
51
51
|
*
|
|
52
|
-
* This is passed to the query/mutate endpoints via an `X-User-ID` header and
|
|
53
|
-
* must be used to validate claimed user identity against the provided auth
|
|
54
|
-
* on the server (either token or cookie auth).
|
|
55
|
-
*
|
|
56
52
|
* Each userID gets its own client-side storage so that the app can switch
|
|
57
53
|
* between users without losing state.
|
|
58
54
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qCAAqC,CAAC;AACvE,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACd,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,6CAA6C,CAAC;AACpF,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,aAAa,CAAC;AACnD,OAAO,EAAC,sBAAsB,EAAC,MAAM,gCAAgC,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,WAAW,CACrB,CAAC,SAAS,iBAAiB,GAAG,aAAa,EAC3C,EAAE,SAAS,iBAAiB,GAAG,SAAS,GAAG,SAAS,EACpD,CAAC,SAAS,kBAAkB,GAAG,cAAc,IAC3C;IACF;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAErC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAEnC;;;;;;;;;;;;;;;;;OAiBG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAEjC
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","names":[],"sources":["../../../../../zero-client/src/client/options.ts"],"sourcesContent":["import type {LogLevel} from '@rocicorp/logger';\nimport type {StoreProvider} from '../../../replicache/src/kv/store.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {\n BaseDefaultContext,\n BaseDefaultSchema,\n DefaultContext,\n DefaultSchema,\n} from '../../../zero-types/src/default-types.ts';\nimport type {AnyMutatorRegistry} from '../../../zql/src/mutate/mutator-registry.ts';\nimport type {CustomMutatorDefs} from './custom.ts';\nimport {UpdateNeededReasonType} from './update-needed-reason-type.ts';\n\n/**\n * Configuration for {@linkcode Zero}.\n */\nexport type ZeroOptions<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n C extends BaseDefaultContext = DefaultContext,\n> = {\n /**\n * URL to the zero-cache. This can be a simple hostname, e.g.\n * - \"https://myapp-myteam.zero.ms\"\n * or a prefix with a single path component, e.g.\n * - \"https://myapp-myteam.zero.ms/zero\"\n * - \"https://myapp-myteam.zero.ms/db\"\n *\n * The latter is useful for configuring routing rules (e.g. \"/zero/\\*\") when\n * the zero-cache is hosted on the same domain as the application. **Note that\n * only a single path segment is allowed (e.g. it cannot be \"/proxy/zero/\\*\")**.\n */\n cacheURL?: string | null | undefined;\n\n /**\n * @deprecated Use {@linkcode cacheURL} instead.\n */\n server?: string | null | undefined;\n\n /**\n * A token to identify and authenticate the user.\n *\n * Set `auth` to `null` or `undefined` if there is no logged in user.\n *\n * The call to `connect` is handled automatically by the ZeroProvider component\n * for React and SolidJS when the `auth` prop changes.\n *\n * Transitions between authenticated and logged-out states recreate the Zero\n * instance instead.\n *\n * When `auth` changes while connected, Zero refreshes server-side auth context\n * and re-transforms queries without reconnecting.\n *\n * When a 401 or 403 HTTP status code is received from your server, Zero will\n * transition to the `needs-auth` connection state. The app should call\n * `zero.connection.connect({auth: newToken})` with a new token to reconnect.\n */\n auth?: string | null | undefined;\n\n /**\n * A unique identifier for the user.\n *\n * Omit this, or set to `null`, for logged-out clients.\n *\n * This is passed to the query/mutate endpoints via an `X-User-ID` header and\n * must be used to validate claimed user identity against the provided auth\n * on the server (either token or cookie auth).\n *\n * Each userID gets its own client-side storage so that the app can switch\n * between users without losing state.\n */\n userID?: string | null | undefined;\n\n /**\n * Distinguishes the storage used by this Zero instance from that of other\n * instances with the same userID. Useful in the case where the app wants to\n * have multiple Zero instances for the same user for different parts of the\n * app.\n */\n storageKey?: string | undefined;\n\n /**\n * Determines the level of detail at which Zero logs messages about\n * its operation. Messages are logged to the `console`.\n *\n * When this is set to `'debug'`, `'info'` and `'error'` messages are also\n * logged. When set to `'info'`, `'info'` and `'error'` but not\n * `'debug'` messages are logged. When set to `'error'` only `'error'`\n * messages are logged.\n *\n * Default is `'error'`.\n */\n logLevel?: LogLevel | undefined;\n\n /**\n * This defines the schema of the tables used in Zero and their relationships\n * to one another.\n */\n schema: S;\n\n /**\n * `mutators` is a map of custom mutator definitions. The keys are\n * namespaces or names of the mutators. The values are the mutator\n * implementations. Client side mutators must be idempotent as a\n * mutation can be rebased multiple times when folding in authoritative\n * changes from the server to the client.\n *\n * Define mutators using the `defineMutator` function to create type-safe,\n * parameterized mutations. Mutators can be top-level or grouped in namespaces.\n *\n * @example\n * ```ts\n * import {defineMutator} from '@rocicorp/zero';\n *\n * const z = new Zero({\n * schema,\n * userID,\n * mutators: {\n * // Top-level mutator\n * increment: defineMutator(({tx, args}: {tx: Transaction<Schema>, args: {id: string}}) =>\n * tx.mutate.counter.update({id: args.id, value: tx.query.counter.where('id', '=', args.id).value + 1})\n * ),\n * // Namespace with multiple mutators\n * issues: {\n * create: defineMutator(({tx, args}: {tx: Transaction<Schema>, args: {title: string}}) =>\n * tx.mutate.issues.insert({id: nanoid(), title: args.title, status: 'open'})\n * ),\n * close: defineMutator(({tx, args}: {tx: Transaction<Schema>, args: {id: string}}) =>\n * tx.mutate.issues.update({id: args.id, status: 'closed'})\n * ),\n * },\n * },\n * });\n *\n * // Usage\n * await z.mutate.increment({id: 'counter-1'}).client;\n * await z.mutate.issues.create({title: 'New issue'}).client;\n * await z.mutate.issues.close({id: 'issue-123'}).client;\n * ```\n */\n mutators?: MD extends CustomMutatorDefs ? MD : AnyMutatorRegistry | undefined;\n\n /**\n * Custom URL for mutation requests sent to your API server.\n * If not provided, uses the default configured in zero-cache.\n */\n mutateURL?: string | undefined;\n\n /**\n * Custom headers to include in mutation requests sent to your API server.\n * These headers are passed through zero-cache to the mutate endpoint.\n */\n mutateHeaders?: Record<string, string> | undefined;\n\n /**\n * Custom URL for query requests sent to your API server.\n * If not provided, uses the default configured in zero-cache.\n *\n * @deprecated Use {@linkcode queryURL} instead.\n */\n getQueriesURL?: string | undefined;\n\n /**\n * Custom URL for query requests sent to your API server.\n * If not provided, uses the default configured in zero-cache.\n */\n queryURL?: string | undefined;\n\n /**\n * Custom headers to include in query requests sent to your API server.\n * These headers are passed through zero-cache to the query endpoint.\n */\n queryHeaders?: Record<string, string> | undefined;\n\n /**\n * Optional callback that returns a W3C `traceparent` header value for\n * distributed tracing. Called before sending WebSocket messages that\n * trigger API server calls (`push`, `changeDesiredQueries`,\n * `initConnection`).\n *\n * This enables end-to-end trace correlation from your frontend through\n * zero-cache to your API server.\n *\n * @example\n * ```ts\n * import {propagation, context} from '@opentelemetry/api';\n *\n * new Zero({\n * getTraceparent: () => {\n * const carrier: Record<string, string> = {};\n * propagation.inject(context.active(), carrier);\n * return carrier.traceparent;\n * },\n * });\n * ```\n */\n getTraceparent?: (() => string | undefined) | undefined;\n\n /**\n * `onOnlineChange` is called when the Zero instance's online status changes.\n *\n * @deprecated Use {@linkcode Connection.state.subscribe} on the Zero instance instead. e.g.\n * ```ts\n * const zero = new Zero({...});\n * zero.connection.state.subscribe((state) => {\n * console.log('Connection state:', state.name);\n * });\n * ```\n *\n * Or use a hook like {@linkcode useConnectionState} to subscribe to state changes.\n */\n onOnlineChange?: ((online: boolean) => void) | undefined;\n\n /**\n * `onUpdateNeeded` is called when a client code update is needed.\n *\n * See {@link UpdateNeededReason} for why updates can be needed.\n *\n * The default behavior is to reload the page (using `location.reload()`).\n * Provide your own function to prevent the page from\n * reloading automatically. You may want to display a toast to inform the end\n * user there is a new version of your app available and prompt them to\n * refresh.\n */\n onUpdateNeeded?: ((reason: UpdateNeededReason) => void) | undefined;\n\n /**\n * `onClientStateNotFound` is called when this client is no longer able\n * to sync with the zero-cache due to missing synchronization state. This\n * can be because:\n * - the local persistent synchronization state has been garbage collected.\n * This can happen if the client has no pending mutations and has not been\n * used for a while (e.g. the client's tab has been hidden for a long time).\n * - the zero-cache fails to find the server side synchronization state for\n * this client.\n *\n * The default behavior is to reload the page (using `location.reload()`).\n * Provide your own function to prevent the page from reloading automatically.\n */\n onClientStateNotFound?: (() => void) | undefined;\n\n /**\n * The number of milliseconds to wait before disconnecting a Zero\n * instance whose tab has become hidden.\n *\n * Instances in hidden tabs are disconnected to save resources.\n *\n * Default is 5 minutes.\n */\n hiddenTabDisconnectDelay?: number | undefined;\n\n /**\n * The number of milliseconds to wait before disconnecting a Zero\n * instance when the connection to the server has timed out.\n *\n * Default is 1 minute.\n */\n disconnectTimeoutMs?: number | undefined;\n\n /**\n * The timeout in milliseconds for ping operations. This value is used for:\n * - How long to wait in idle before sending a ping to the server\n * - How long to wait for a pong response after sending a ping\n *\n * Total time to detect a dead connection is 2 × pingTimeoutMs.\n *\n * Default is 5_000.\n */\n pingTimeoutMs?: number | undefined;\n\n /**\n * Determines what kind of storage implementation to use on the client.\n *\n * Defaults to `'idb'` which means that Zero uses an IndexedDB storage\n * implementation. This allows the data to be persisted on the client and\n * enables faster syncs between application restarts.\n *\n * By setting this to `'mem'`, Zero uses an in memory storage and\n * the data is not persisted on the client.\n *\n * You can also set this to a function that is used to create new KV stores,\n * allowing a custom implementation of the underlying storage layer.\n */\n kvStore?: 'mem' | 'idb' | StoreProvider | undefined;\n\n /**\n * The maximum number of bytes to allow in a single header.\n *\n * Zero adds some extra information to headers on initialization if possible.\n * This speeds up data synchronization. This number should be kept less than\n * or equal to the maximum header size allowed by the zero-cache and any load\n * balancers.\n *\n * Default value: 8kb.\n */\n maxHeaderLength?: number | undefined;\n\n /**\n * The maximum amount of milliseconds to wait for a materialization to\n * complete (including network/server time) before printing a warning to the\n * console.\n *\n * Default value: 5_000.\n */\n slowMaterializeThreshold?: number | undefined;\n\n /**\n * UI rendering libraries will often provide a utility for batching multiple\n * state updates into a single render. Some examples are React's\n * `unstable_batchedUpdates`, and solid-js's `batch`.\n *\n * This option enables integrating these batch utilities with Zero.\n *\n * When `batchViewUpdates` is provided, Zero will call it whenever\n * it updates query view state with an `applyViewUpdates` function\n * that performs the actual state updates.\n *\n * Zero updates query view state when:\n * 1. creating a new view\n * 2. updating all existing queries' views to a new consistent state\n *\n * When creating a new view, that single view's creation will be wrapped\n * in a `batchViewUpdates` call.\n *\n * When updating existing queries, all queries will be updated in a single\n * `batchViewUpdates` call, so that the transition to the new consistent\n * state can be done in a single render.\n *\n * Implementations must always call `applyViewUpdates` synchronously.\n */\n batchViewUpdates?: ((applyViewUpdates: () => void) => void) | undefined;\n\n /**\n * The maximum number of recent queries, no longer subscribed to by a preload\n * or view, to continue syncing.\n *\n * Defaults is 0.\n *\n * @deprecated Use ttl instead\n */\n maxRecentQueries?: number | undefined;\n\n /**\n * Changes to queries are sent to server in batches. This option controls\n * the number of milliseconds to wait before sending the next batch.\n *\n * Defaults is 10.\n */\n queryChangeThrottleMs?: number | undefined;\n\n /**\n * Context is passed to queries when they are executed.\n */\n context?: C | undefined;\n} & (unknown extends DefaultContext\n ? {}\n : {\n context: C;\n });\n\n/**\n * @deprecated Use {@link ZeroOptions} instead.\n */\nexport type ZeroAdvancedOptions<\n S extends BaseDefaultSchema,\n MD extends CustomMutatorDefs | undefined,\n Context extends BaseDefaultContext,\n> = ZeroOptions<S, MD, Context>;\n\ntype UpdateNeededReasonBase = {\n message?: string;\n};\n\nexport type UpdateNeededReason =\n | ({type: UpdateNeededReasonType.NewClientGroup} & UpdateNeededReasonBase)\n | ({\n type: UpdateNeededReasonType.VersionNotSupported;\n } & UpdateNeededReasonBase)\n | ({\n type: UpdateNeededReasonType.SchemaVersionNotSupported;\n } & UpdateNeededReasonBase);\n\nexport const updateNeededReasonTypeSchema: v.Type<UpdateNeededReason['type']> =\n v.literalUnion(\n UpdateNeededReasonType.NewClientGroup,\n UpdateNeededReasonType.VersionNotSupported,\n UpdateNeededReasonType.SchemaVersionNotSupported,\n );\n"],"mappings":";;;AA8XA,IAAa,+BACX,aACE,gBACA,qBACA,0BACD"}
|
|
1
|
+
{"version":3,"file":"options.js","names":[],"sources":["../../../../../zero-client/src/client/options.ts"],"sourcesContent":["import type {LogLevel} from '@rocicorp/logger';\nimport type {StoreProvider} from '../../../replicache/src/kv/store.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {\n BaseDefaultContext,\n BaseDefaultSchema,\n DefaultContext,\n DefaultSchema,\n} from '../../../zero-types/src/default-types.ts';\nimport type {AnyMutatorRegistry} from '../../../zql/src/mutate/mutator-registry.ts';\nimport type {CustomMutatorDefs} from './custom.ts';\nimport {UpdateNeededReasonType} from './update-needed-reason-type.ts';\n\n/**\n * Configuration for {@linkcode Zero}.\n */\nexport type ZeroOptions<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n C extends BaseDefaultContext = DefaultContext,\n> = {\n /**\n * URL to the zero-cache. This can be a simple hostname, e.g.\n * - \"https://myapp-myteam.zero.ms\"\n * or a prefix with a single path component, e.g.\n * - \"https://myapp-myteam.zero.ms/zero\"\n * - \"https://myapp-myteam.zero.ms/db\"\n *\n * The latter is useful for configuring routing rules (e.g. \"/zero/\\*\") when\n * the zero-cache is hosted on the same domain as the application. **Note that\n * only a single path segment is allowed (e.g. it cannot be \"/proxy/zero/\\*\")**.\n */\n cacheURL?: string | null | undefined;\n\n /**\n * @deprecated Use {@linkcode cacheURL} instead.\n */\n server?: string | null | undefined;\n\n /**\n * A token to identify and authenticate the user.\n *\n * Set `auth` to `null` or `undefined` if there is no logged in user.\n *\n * The call to `connect` is handled automatically by the ZeroProvider component\n * for React and SolidJS when the `auth` prop changes.\n *\n * Transitions between authenticated and logged-out states recreate the Zero\n * instance instead.\n *\n * When `auth` changes while connected, Zero refreshes server-side auth context\n * and re-transforms queries without reconnecting.\n *\n * When a 401 or 403 HTTP status code is received from your server, Zero will\n * transition to the `needs-auth` connection state. The app should call\n * `zero.connection.connect({auth: newToken})` with a new token to reconnect.\n */\n auth?: string | null | undefined;\n\n /**\n * A unique identifier for the user.\n *\n * Omit this, or set to `null`, for logged-out clients.\n *\n * Each userID gets its own client-side storage so that the app can switch\n * between users without losing state.\n */\n userID?: string | null | undefined;\n\n /**\n * Distinguishes the storage used by this Zero instance from that of other\n * instances with the same userID. Useful in the case where the app wants to\n * have multiple Zero instances for the same user for different parts of the\n * app.\n */\n storageKey?: string | undefined;\n\n /**\n * Determines the level of detail at which Zero logs messages about\n * its operation. Messages are logged to the `console`.\n *\n * When this is set to `'debug'`, `'info'` and `'error'` messages are also\n * logged. When set to `'info'`, `'info'` and `'error'` but not\n * `'debug'` messages are logged. When set to `'error'` only `'error'`\n * messages are logged.\n *\n * Default is `'error'`.\n */\n logLevel?: LogLevel | undefined;\n\n /**\n * This defines the schema of the tables used in Zero and their relationships\n * to one another.\n */\n schema: S;\n\n /**\n * `mutators` is a map of custom mutator definitions. The keys are\n * namespaces or names of the mutators. The values are the mutator\n * implementations. Client side mutators must be idempotent as a\n * mutation can be rebased multiple times when folding in authoritative\n * changes from the server to the client.\n *\n * Define mutators using the `defineMutator` function to create type-safe,\n * parameterized mutations. Mutators can be top-level or grouped in namespaces.\n *\n * @example\n * ```ts\n * import {defineMutator} from '@rocicorp/zero';\n *\n * const z = new Zero({\n * schema,\n * userID,\n * mutators: {\n * // Top-level mutator\n * increment: defineMutator(({tx, args}: {tx: Transaction<Schema>, args: {id: string}}) =>\n * tx.mutate.counter.update({id: args.id, value: tx.query.counter.where('id', '=', args.id).value + 1})\n * ),\n * // Namespace with multiple mutators\n * issues: {\n * create: defineMutator(({tx, args}: {tx: Transaction<Schema>, args: {title: string}}) =>\n * tx.mutate.issues.insert({id: nanoid(), title: args.title, status: 'open'})\n * ),\n * close: defineMutator(({tx, args}: {tx: Transaction<Schema>, args: {id: string}}) =>\n * tx.mutate.issues.update({id: args.id, status: 'closed'})\n * ),\n * },\n * },\n * });\n *\n * // Usage\n * await z.mutate.increment({id: 'counter-1'}).client;\n * await z.mutate.issues.create({title: 'New issue'}).client;\n * await z.mutate.issues.close({id: 'issue-123'}).client;\n * ```\n */\n mutators?: MD extends CustomMutatorDefs ? MD : AnyMutatorRegistry | undefined;\n\n /**\n * Custom URL for mutation requests sent to your API server.\n * If not provided, uses the default configured in zero-cache.\n */\n mutateURL?: string | undefined;\n\n /**\n * Custom headers to include in mutation requests sent to your API server.\n * These headers are passed through zero-cache to the mutate endpoint.\n */\n mutateHeaders?: Record<string, string> | undefined;\n\n /**\n * Custom URL for query requests sent to your API server.\n * If not provided, uses the default configured in zero-cache.\n *\n * @deprecated Use {@linkcode queryURL} instead.\n */\n getQueriesURL?: string | undefined;\n\n /**\n * Custom URL for query requests sent to your API server.\n * If not provided, uses the default configured in zero-cache.\n */\n queryURL?: string | undefined;\n\n /**\n * Custom headers to include in query requests sent to your API server.\n * These headers are passed through zero-cache to the query endpoint.\n */\n queryHeaders?: Record<string, string> | undefined;\n\n /**\n * Optional callback that returns a W3C `traceparent` header value for\n * distributed tracing. Called before sending WebSocket messages that\n * trigger API server calls (`push`, `changeDesiredQueries`,\n * `initConnection`).\n *\n * This enables end-to-end trace correlation from your frontend through\n * zero-cache to your API server.\n *\n * @example\n * ```ts\n * import {propagation, context} from '@opentelemetry/api';\n *\n * new Zero({\n * getTraceparent: () => {\n * const carrier: Record<string, string> = {};\n * propagation.inject(context.active(), carrier);\n * return carrier.traceparent;\n * },\n * });\n * ```\n */\n getTraceparent?: (() => string | undefined) | undefined;\n\n /**\n * `onOnlineChange` is called when the Zero instance's online status changes.\n *\n * @deprecated Use {@linkcode Connection.state.subscribe} on the Zero instance instead. e.g.\n * ```ts\n * const zero = new Zero({...});\n * zero.connection.state.subscribe((state) => {\n * console.log('Connection state:', state.name);\n * });\n * ```\n *\n * Or use a hook like {@linkcode useConnectionState} to subscribe to state changes.\n */\n onOnlineChange?: ((online: boolean) => void) | undefined;\n\n /**\n * `onUpdateNeeded` is called when a client code update is needed.\n *\n * See {@link UpdateNeededReason} for why updates can be needed.\n *\n * The default behavior is to reload the page (using `location.reload()`).\n * Provide your own function to prevent the page from\n * reloading automatically. You may want to display a toast to inform the end\n * user there is a new version of your app available and prompt them to\n * refresh.\n */\n onUpdateNeeded?: ((reason: UpdateNeededReason) => void) | undefined;\n\n /**\n * `onClientStateNotFound` is called when this client is no longer able\n * to sync with the zero-cache due to missing synchronization state. This\n * can be because:\n * - the local persistent synchronization state has been garbage collected.\n * This can happen if the client has no pending mutations and has not been\n * used for a while (e.g. the client's tab has been hidden for a long time).\n * - the zero-cache fails to find the server side synchronization state for\n * this client.\n *\n * The default behavior is to reload the page (using `location.reload()`).\n * Provide your own function to prevent the page from reloading automatically.\n */\n onClientStateNotFound?: (() => void) | undefined;\n\n /**\n * The number of milliseconds to wait before disconnecting a Zero\n * instance whose tab has become hidden.\n *\n * Instances in hidden tabs are disconnected to save resources.\n *\n * Default is 5 minutes.\n */\n hiddenTabDisconnectDelay?: number | undefined;\n\n /**\n * The number of milliseconds to wait before disconnecting a Zero\n * instance when the connection to the server has timed out.\n *\n * Default is 1 minute.\n */\n disconnectTimeoutMs?: number | undefined;\n\n /**\n * The timeout in milliseconds for ping operations. This value is used for:\n * - How long to wait in idle before sending a ping to the server\n * - How long to wait for a pong response after sending a ping\n *\n * Total time to detect a dead connection is 2 × pingTimeoutMs.\n *\n * Default is 5_000.\n */\n pingTimeoutMs?: number | undefined;\n\n /**\n * Determines what kind of storage implementation to use on the client.\n *\n * Defaults to `'idb'` which means that Zero uses an IndexedDB storage\n * implementation. This allows the data to be persisted on the client and\n * enables faster syncs between application restarts.\n *\n * By setting this to `'mem'`, Zero uses an in memory storage and\n * the data is not persisted on the client.\n *\n * You can also set this to a function that is used to create new KV stores,\n * allowing a custom implementation of the underlying storage layer.\n */\n kvStore?: 'mem' | 'idb' | StoreProvider | undefined;\n\n /**\n * The maximum number of bytes to allow in a single header.\n *\n * Zero adds some extra information to headers on initialization if possible.\n * This speeds up data synchronization. This number should be kept less than\n * or equal to the maximum header size allowed by the zero-cache and any load\n * balancers.\n *\n * Default value: 8kb.\n */\n maxHeaderLength?: number | undefined;\n\n /**\n * The maximum amount of milliseconds to wait for a materialization to\n * complete (including network/server time) before printing a warning to the\n * console.\n *\n * Default value: 5_000.\n */\n slowMaterializeThreshold?: number | undefined;\n\n /**\n * UI rendering libraries will often provide a utility for batching multiple\n * state updates into a single render. Some examples are React's\n * `unstable_batchedUpdates`, and solid-js's `batch`.\n *\n * This option enables integrating these batch utilities with Zero.\n *\n * When `batchViewUpdates` is provided, Zero will call it whenever\n * it updates query view state with an `applyViewUpdates` function\n * that performs the actual state updates.\n *\n * Zero updates query view state when:\n * 1. creating a new view\n * 2. updating all existing queries' views to a new consistent state\n *\n * When creating a new view, that single view's creation will be wrapped\n * in a `batchViewUpdates` call.\n *\n * When updating existing queries, all queries will be updated in a single\n * `batchViewUpdates` call, so that the transition to the new consistent\n * state can be done in a single render.\n *\n * Implementations must always call `applyViewUpdates` synchronously.\n */\n batchViewUpdates?: ((applyViewUpdates: () => void) => void) | undefined;\n\n /**\n * The maximum number of recent queries, no longer subscribed to by a preload\n * or view, to continue syncing.\n *\n * Defaults is 0.\n *\n * @deprecated Use ttl instead\n */\n maxRecentQueries?: number | undefined;\n\n /**\n * Changes to queries are sent to server in batches. This option controls\n * the number of milliseconds to wait before sending the next batch.\n *\n * Defaults is 10.\n */\n queryChangeThrottleMs?: number | undefined;\n\n /**\n * Context is passed to queries when they are executed.\n */\n context?: C | undefined;\n} & (unknown extends DefaultContext\n ? {}\n : {\n context: C;\n });\n\n/**\n * @deprecated Use {@link ZeroOptions} instead.\n */\nexport type ZeroAdvancedOptions<\n S extends BaseDefaultSchema,\n MD extends CustomMutatorDefs | undefined,\n Context extends BaseDefaultContext,\n> = ZeroOptions<S, MD, Context>;\n\ntype UpdateNeededReasonBase = {\n message?: string;\n};\n\nexport type UpdateNeededReason =\n | ({type: UpdateNeededReasonType.NewClientGroup} & UpdateNeededReasonBase)\n | ({\n type: UpdateNeededReasonType.VersionNotSupported;\n } & UpdateNeededReasonBase)\n | ({\n type: UpdateNeededReasonType.SchemaVersionNotSupported;\n } & UpdateNeededReasonBase);\n\nexport const updateNeededReasonTypeSchema: v.Type<UpdateNeededReason['type']> =\n v.literalUnion(\n UpdateNeededReasonType.NewClientGroup,\n UpdateNeededReasonType.VersionNotSupported,\n UpdateNeededReasonType.SchemaVersionNotSupported,\n );\n"],"mappings":";;;AA0XA,IAAa,+BACX,aACE,gBACA,qBACA,0BACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../../../zero-protocol/src/error.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../../../zero-protocol/src/error.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,4BAA4B,CAAC;AAChD,OAAO,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAmC1C,QAAA,MAAM,iBAAiB;;;;;;;aAcrB,CAAC;AAKH,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAK7C,CAAC;AAaF,eAAO,MAAM,oBAAoB;;;IAR/B;;;OAGG;;;;;;;;;;;;IAHH;;;OAGG;;;;;;;;;;;;;;IAHH;;;OAGG;;;;;;;;;eA8BJ,CAAC;AAYF,eAAO,MAAM,yBAAyB;;;IAPpC;;OAEG;;;;;;;;;IAFH;;OAEG;;;;;;;;;;;IAFH;;OAEG;;;;;;eA4BJ,CAAC;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;IAtE1B;;;OAGG;;;;;;;;;;;;IAHH;;;OAGG;;;;;;;;;;;;;;IAHH;;;OAGG;;;;;;;;;;;;IAmCH;;OAEG;;;;;;;;;IAFH;;OAEG;;;;;;;;;;;IAFH;;OAEG;;;;;;iBAmCJ,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,kBAAkB,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAGlD,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAEhD;;GAEG;AACH,qBAAa,aAAa,CACxB,KAAK,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,CACrC,SAAQ,KAAK;IACb,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;gBAEV,SAAS,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY;IAMhD,IAAI,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,CAEpB;CACF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAEtE"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { literalUnion, valita_exports } from "../../shared/src/valita.js";
|
|
2
2
|
import { jsonSchema } from "../../shared/src/json-schema.js";
|
|
3
|
-
import { mutationIDSchema } from "./mutation-id.js";
|
|
4
3
|
import { AuthInvalidated, ClientNotFound, Internal, InvalidConnectionRequest, InvalidConnectionRequestBaseCookie, InvalidConnectionRequestClientDeleted, InvalidConnectionRequestLastMutationID, InvalidMessage, InvalidPush, MutationFailed, MutationRateLimited, PushFailed, Rebalance, Rehome, SchemaVersionNotSupported, ServerOverloaded, TransformFailed, Unauthorized, VersionNotSupported } from "./error-kind-enum.js";
|
|
5
4
|
import { Server, ZeroCache } from "./error-origin-enum.js";
|
|
6
5
|
import { Database, HTTP, Internal as Internal$1, OutOfOrderMutation, Parse, Timeout, UnsupportedPushVersion } from "./error-reason-enum.js";
|
|
6
|
+
import { mutationIDSchema } from "./mutation-id.js";
|
|
7
7
|
//#region ../zero-protocol/src/error.ts
|
|
8
8
|
var basicErrorKindSchema = literalUnion(AuthInvalidated, ClientNotFound, InvalidConnectionRequest, InvalidConnectionRequestBaseCookie, InvalidConnectionRequestLastMutationID, InvalidConnectionRequestClientDeleted, InvalidMessage, InvalidPush, MutationRateLimited, MutationFailed, Unauthorized, VersionNotSupported, SchemaVersionNotSupported, Internal);
|
|
9
9
|
var basicErrorBodySchema = valita_exports.object({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","names":[],"sources":["../../../../zero-protocol/src/error.ts"],"sourcesContent":["import {jsonSchema} from '../../shared/src/json-schema.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {
|
|
1
|
+
{"version":3,"file":"error.js","names":[],"sources":["../../../../zero-protocol/src/error.ts"],"sourcesContent":["import {jsonSchema} from '../../shared/src/json-schema.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {ErrorKind} from './error-kind.ts';\nimport {ErrorOrigin} from './error-origin.ts';\nimport {ErrorReason} from './error-reason.ts';\nimport {mutationIDSchema} from './mutation-id.ts';\n\nconst basicErrorKindSchema = v.literalUnion(\n ErrorKind.AuthInvalidated,\n ErrorKind.ClientNotFound,\n ErrorKind.InvalidConnectionRequest,\n ErrorKind.InvalidConnectionRequestBaseCookie,\n ErrorKind.InvalidConnectionRequestLastMutationID,\n ErrorKind.InvalidConnectionRequestClientDeleted,\n ErrorKind.InvalidMessage,\n ErrorKind.InvalidPush,\n ErrorKind.MutationRateLimited,\n ErrorKind.MutationFailed,\n ErrorKind.Unauthorized,\n ErrorKind.VersionNotSupported,\n ErrorKind.SchemaVersionNotSupported,\n ErrorKind.Internal,\n);\n\nconst basicErrorBodySchema = v.object({\n kind: basicErrorKindSchema,\n message: v.string(),\n // this is optional for backwards compatibility\n origin: v.literalUnion(ErrorOrigin.Server, ErrorOrigin.ZeroCache).optional(),\n});\n\nconst backoffErrorKindSchema = v.literalUnion(\n ErrorKind.Rebalance,\n ErrorKind.Rehome,\n ErrorKind.ServerOverloaded,\n);\n\nconst backoffBodySchema = v.object({\n kind: backoffErrorKindSchema,\n message: v.string(),\n minBackoffMs: v.number().optional(),\n maxBackoffMs: v.number().optional(),\n // Query parameters to send in the next reconnect. In the event of\n // a conflict, these will be overridden by the parameters used by\n // the client; it is the responsibility of the server to avoid\n // parameter name conflicts.\n //\n // The parameters will only be added to the immediately following\n // reconnect, and not after that.\n reconnectParams: v.record(v.string()).optional(),\n origin: v.literal(ErrorOrigin.ZeroCache).optional(),\n});\n\nconst pushFailedErrorKindSchema = v.literal(ErrorKind.PushFailed);\nconst transformFailedErrorKindSchema = v.literal(ErrorKind.TransformFailed);\n\nexport const errorKindSchema: v.Type<ErrorKind> = v.union(\n basicErrorKindSchema,\n backoffErrorKindSchema,\n pushFailedErrorKindSchema,\n transformFailedErrorKindSchema,\n);\n\nconst pushFailedBaseSchema = v.object({\n kind: pushFailedErrorKindSchema,\n details: jsonSchema.optional(),\n /**\n * The mutationIDs of the mutations that failed to process.\n * This can be a subset of the mutationIDs in the request.\n */\n mutationIDs: v.array(mutationIDSchema),\n message: v.string(),\n});\n\nexport const pushFailedBodySchema = v.union(\n pushFailedBaseSchema.extend({\n origin: v.literal(ErrorOrigin.Server),\n reason: v.literalUnion(\n ErrorReason.Database,\n ErrorReason.Parse,\n ErrorReason.OutOfOrderMutation,\n ErrorReason.UnsupportedPushVersion,\n ErrorReason.Internal,\n ),\n }),\n pushFailedBaseSchema.extend({\n origin: v.literal(ErrorOrigin.ZeroCache),\n reason: v.literal(ErrorReason.HTTP),\n status: v.number(),\n bodyPreview: v.string().optional(),\n }),\n pushFailedBaseSchema.extend({\n origin: v.literal(ErrorOrigin.ZeroCache),\n reason: v.literalUnion(\n ErrorReason.Timeout,\n ErrorReason.Parse,\n ErrorReason.Internal,\n ),\n }),\n);\n\nconst transformFailedBaseSchema = v.object({\n kind: transformFailedErrorKindSchema,\n details: jsonSchema.optional(),\n /**\n * The queryIDs of the queries that failed to transform.\n */\n queryIDs: v.array(v.string()),\n message: v.string(),\n});\n\nexport const transformFailedBodySchema = v.union(\n transformFailedBaseSchema.extend({\n origin: v.literal(ErrorOrigin.Server),\n reason: v.literalUnion(\n ErrorReason.Database,\n ErrorReason.Parse,\n ErrorReason.Internal,\n ),\n }),\n transformFailedBaseSchema.extend({\n origin: v.literal(ErrorOrigin.ZeroCache),\n reason: v.literal(ErrorReason.HTTP),\n status: v.number(),\n bodyPreview: v.string().optional(),\n }),\n transformFailedBaseSchema.extend({\n origin: v.literal(ErrorOrigin.ZeroCache),\n reason: v.literalUnion(\n ErrorReason.Timeout,\n ErrorReason.Parse,\n ErrorReason.Internal,\n ),\n }),\n);\n\nexport const errorBodySchema = v.union(\n basicErrorBodySchema,\n backoffBodySchema,\n pushFailedBodySchema,\n transformFailedBodySchema,\n);\n\nexport type BackoffBody = v.Infer<typeof backoffBodySchema>;\nexport type PushFailedBody = v.Infer<typeof pushFailedBodySchema>;\nexport type TransformFailedBody = v.Infer<typeof transformFailedBodySchema>;\nexport type ErrorBody = v.Infer<typeof errorBodySchema>;\n\nexport const errorMessageSchema: v.Type<ErrorMessage> = v.tuple([\n v.literal('error'),\n errorBodySchema,\n]);\n\nexport type ErrorMessage = ['error', ErrorBody];\n\n/**\n * Represents an error used across zero-client, zero-cache, and zero-server.\n */\nexport class ProtocolError<\n const T extends ErrorBody = ErrorBody,\n> extends Error {\n readonly errorBody: T;\n\n constructor(errorBody: T, options?: ErrorOptions) {\n super(errorBody.message, options);\n this.name = 'ProtocolError';\n this.errorBody = errorBody;\n }\n\n get kind(): T['kind'] {\n return this.errorBody.kind;\n }\n}\n\nexport function isProtocolError(error: unknown): error is ProtocolError {\n return error instanceof ProtocolError;\n}\n"],"mappings":";;;;;;;AAOA,IAAM,uBAAuB,aAC3B,iBACA,gBACA,0BACA,oCACA,wCACA,uCACA,gBACA,aACA,qBACA,gBACA,cACA,qBACA,2BACA,SACD;AAED,IAAM,uBAAuB,eAAE,OAAO;CACpC,MAAM;CACN,SAAS,eAAE,QAAQ;CAEnB,QAAQ,aAAe,QAAoB,UAAsB,CAAC,UAAU;CAC7E,CAAC;AAEF,IAAM,yBAAyB,aAC7B,WACA,QACA,iBACD;AAED,IAAM,oBAAoB,eAAE,OAAO;CACjC,MAAM;CACN,SAAS,eAAE,QAAQ;CACnB,cAAc,eAAE,QAAQ,CAAC,UAAU;CACnC,cAAc,eAAE,QAAQ,CAAC,UAAU;CAQnC,iBAAiB,eAAE,OAAO,eAAE,QAAQ,CAAC,CAAC,UAAU;CAChD,QAAQ,eAAE,QAAQ,UAAsB,CAAC,UAAU;CACpD,CAAC;AAEF,IAAM,4BAA4B,eAAE,QAAQ,WAAqB;AACjE,IAAM,iCAAiC,eAAE,QAAQ,gBAA0B;AAE3E,IAAa,kBAAqC,eAAE,MAClD,sBACA,wBACA,2BACA,+BACD;AAED,IAAM,uBAAuB,eAAE,OAAO;CACpC,MAAM;CACN,SAAS,WAAW,UAAU;CAK9B,aAAa,eAAE,MAAM,iBAAiB;CACtC,SAAS,eAAE,QAAQ;CACpB,CAAC;AAEF,IAAa,uBAAuB,eAAE,MACpC,qBAAqB,OAAO;CAC1B,QAAQ,eAAE,QAAQ,OAAmB;CACrC,QAAQ,aACN,UACA,OACA,oBACA,wBACA,WACD;CACF,CAAC,EACF,qBAAqB,OAAO;CAC1B,QAAQ,eAAE,QAAQ,UAAsB;CACxC,QAAQ,eAAE,QAAQ,KAAiB;CACnC,QAAQ,eAAE,QAAQ;CAClB,aAAa,eAAE,QAAQ,CAAC,UAAU;CACnC,CAAC,EACF,qBAAqB,OAAO;CAC1B,QAAQ,eAAE,QAAQ,UAAsB;CACxC,QAAQ,aACN,SACA,OACA,WACD;CACF,CAAC,CACH;AAED,IAAM,4BAA4B,eAAE,OAAO;CACzC,MAAM;CACN,SAAS,WAAW,UAAU;CAI9B,UAAU,eAAE,MAAM,eAAE,QAAQ,CAAC;CAC7B,SAAS,eAAE,QAAQ;CACpB,CAAC;AAEF,IAAa,4BAA4B,eAAE,MACzC,0BAA0B,OAAO;CAC/B,QAAQ,eAAE,QAAQ,OAAmB;CACrC,QAAQ,aACN,UACA,OACA,WACD;CACF,CAAC,EACF,0BAA0B,OAAO;CAC/B,QAAQ,eAAE,QAAQ,UAAsB;CACxC,QAAQ,eAAE,QAAQ,KAAiB;CACnC,QAAQ,eAAE,QAAQ;CAClB,aAAa,eAAE,QAAQ,CAAC,UAAU;CACnC,CAAC,EACF,0BAA0B,OAAO;CAC/B,QAAQ,eAAE,QAAQ,UAAsB;CACxC,QAAQ,aACN,SACA,OACA,WACD;CACF,CAAC,CACH;AAED,IAAa,kBAAkB,eAAE,MAC/B,sBACA,mBACA,sBACA,0BACD;AAOD,IAAa,qBAA2C,eAAE,MAAM,CAC9D,eAAE,QAAQ,QAAQ,EAClB,gBACD,CAAC;;;;AAOF,IAAa,gBAAb,cAEU,MAAM;CACd;CAEA,YAAY,WAAc,SAAwB;AAChD,QAAM,UAAU,SAAS,QAAQ;AACjC,OAAK,OAAO;AACZ,OAAK,YAAY;;CAGnB,IAAI,OAAkB;AACpB,SAAO,KAAK,UAAU;;;AAI1B,SAAgB,gBAAgB,OAAwC;AACtE,QAAO,iBAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solid-view.d.ts","sourceRoot":"","sources":["../../../../zero-solid/src/solid-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"solid-view.d.ts","sourceRoot":"","sources":["../../../../zero-solid/src/solid-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,gBAAgB,CAAC;AAUzE,OAAO,EAEL,KAAK,MAAM,EACX,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,MAAM,EACX,KAAK,KAAK,EAEV,KAAK,MAAM,EACX,KAAK,KAAK,EAEV,KAAK,kBAAkB,EACvB,KAAK,MAAM,EAEX,KAAK,GAAG,EACT,MAAM,WAAW,CAAC;AAEnB,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;AAEhD,eAAO,MAAM,QAAQ,EAAE,kBAAsD,CAAC;AAC9E,eAAO,MAAM,OAAO,EAAE,kBAAqD,CAAC;AAE5E,qBAAa,SAAU,YAAW,MAAM;;gBAqBpC,KAAK,EAAE,KAAK,EACZ,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,EAC7C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,IAAI,EACrB,aAAa,EAAE,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,EAClD,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,EAC7B,QAAQ,EAAE,gBAAgB,CAAC,KAAK,CAAC,EACjC,KAAK,EAAE,MAAM,IAAI;IAiEnB,OAAO,IAAI,IAAI;IA2Bf,IAAI,CAAC,MAAM,EAAE,MAAM;IAwDnB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI;CAG1B;AAsDD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,gBAAgB,CAAC,KAAK,CAAC,EACjC,KAAK,CAAC,EAAE,MAAM,IAAI,IAGhB,MAAM,SAAS,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,EAC/C,OAAO,SAAS,MAAM,EACtB,OAAO,UAEC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,SAChC,KAAK,UACJ,MAAM,aACH,MAAM,IAAI,uBACA,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,iBAC9B,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,aACvC,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,eAiBhC"}
|
|
@@ -69,7 +69,7 @@ var SolidView = class {
|
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
71
|
push(change) {
|
|
72
|
-
if (this.#builderRoot) this.#applyChangeToRoot(change, this.#builderRoot);
|
|
72
|
+
if (this.#builderRoot) this.#applyChangeToRoot(materializeRelationships(change), this.#builderRoot);
|
|
73
73
|
else this.#pendingChanges.push(materializeRelationships(change));
|
|
74
74
|
return emptyArray;
|
|
75
75
|
}
|
|
@@ -93,27 +93,27 @@ var SolidView = class {
|
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
95
|
function materializeRelationships(change) {
|
|
96
|
-
switch (change
|
|
97
|
-
case
|
|
96
|
+
switch (change[0]) {
|
|
97
|
+
case 0: return {
|
|
98
98
|
type: "add",
|
|
99
|
-
node: materializeNodeRelationships(change
|
|
99
|
+
node: materializeNodeRelationships(change[1])
|
|
100
100
|
};
|
|
101
|
-
case
|
|
101
|
+
case 1: return {
|
|
102
102
|
type: "remove",
|
|
103
|
-
node: materializeNodeRelationships(change
|
|
103
|
+
node: materializeNodeRelationships(change[1])
|
|
104
104
|
};
|
|
105
|
-
case
|
|
105
|
+
case 3: return {
|
|
106
106
|
type: "child",
|
|
107
|
-
node: { row: change.
|
|
107
|
+
node: { row: change[1].row },
|
|
108
108
|
child: {
|
|
109
|
-
relationshipName: change.
|
|
110
|
-
change: materializeRelationships(change.
|
|
109
|
+
relationshipName: change[2].relationshipName,
|
|
110
|
+
change: materializeRelationships(change[2].change)
|
|
111
111
|
}
|
|
112
112
|
};
|
|
113
|
-
case
|
|
113
|
+
case 2: return {
|
|
114
114
|
type: "edit",
|
|
115
|
-
node: { row: change.
|
|
116
|
-
oldNode: { row: change.
|
|
115
|
+
node: { row: change[1].row },
|
|
116
|
+
oldNode: { row: change[2].row }
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solid-view.js","names":["#input","#format","#onDestroy","#retry","#updateTTL","#onTransactionCommit","#createEmptyRoot","#applyChangesToRoot","#setState","#makeError","#builderRoot","#applyChanges","#pendingChanges","#applyChangeToRoot"],"sources":["../../../../zero-solid/src/solid-view.ts"],"sourcesContent":["import {produce, reconcile, type SetStoreFunction} from 'solid-js/store';\nimport {emptyArray} from '../../shared/src/sentinels.ts';\nimport {\n applyChange,\n idSymbol,\n skipYields,\n type ViewChange,\n} from './bindings.ts';\nimport {\n type AnyViewFactory,\n type Change,\n type Entry,\n type ErroredQuery,\n type Format,\n type Input,\n type Node,\n type Output,\n type Query,\n type QueryErrorDetails,\n type QueryResultDetails,\n type Schema,\n type Stream,\n type TTL,\n} from './zero.ts';\n\nexport type State = [Entry, QueryResultDetails];\n\nexport const COMPLETE: QueryResultDetails = Object.freeze({type: 'complete'});\nexport const UNKNOWN: QueryResultDetails = Object.freeze({type: 'unknown'});\n\nexport class SolidView implements Output {\n readonly #input: Input;\n readonly #format: Format;\n readonly #onDestroy: () => void;\n readonly #retry: () => void;\n\n #setState: SetStoreFunction<State>;\n\n // Optimization: if the store is currently empty we build up\n // the view on a plain old JS object stored at #builderRoot, and return\n // that for the new state on transaction commit. This avoids building up\n // large views from scratch via solid produce. The proxy object used by\n // solid produce is slow and in this case we don't care about solid tracking\n // the fine grained changes (everything has changed, it's all new). For a\n // test case with a view with 3000 rows, each row having 2 children, this\n // optimization reduced #applyChanges time from 743ms to 133ms.\n #builderRoot: Entry | undefined;\n #pendingChanges: ViewChange[] = [];\n readonly #updateTTL: (ttl: TTL) => void;\n\n constructor(\n input: Input,\n onTransactionCommit: (cb: () => void) => void,\n format: Format,\n onDestroy: () => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n setState: SetStoreFunction<State>,\n retry: () => void,\n ) {\n this.#input = input;\n onTransactionCommit(this.#onTransactionCommit);\n this.#format = format;\n this.#onDestroy = onDestroy;\n this.#updateTTL = updateTTL;\n this.#retry = retry;\n\n input.setOutput(this);\n\n const initialRoot = this.#createEmptyRoot();\n this.#applyChangesToRoot(\n skipYields(input.fetch({})),\n node => ({type: 'add', node}),\n initialRoot,\n );\n\n this.#setState = setState;\n this.#setState(\n reconcile(\n [\n initialRoot,\n queryComplete === true\n ? COMPLETE\n : 'error' in queryComplete\n ? this.#makeError(queryComplete)\n : UNKNOWN,\n ],\n {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n },\n ),\n );\n\n if (isEmptyRoot(initialRoot)) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n\n if (queryComplete !== true && !('error' in queryComplete)) {\n void queryComplete\n .then(() => {\n this.#setState(prev => [prev[0], COMPLETE]);\n })\n .catch((error: ErroredQuery) => {\n this.#setState(prev => [prev[0], this.#makeError(error)]);\n });\n }\n }\n\n #makeError(error: ErroredQuery): QueryErrorDetails {\n const message = error.message ?? 'An unknown error occurred';\n return {\n type: 'error',\n retry: this.#retry,\n refetch: this.#retry,\n error: {\n type: error.error,\n message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n }\n\n destroy(): void {\n this.#onDestroy();\n }\n\n #onTransactionCommit = () => {\n const builderRoot = this.#builderRoot;\n if (builderRoot) {\n if (!isEmptyRoot(builderRoot)) {\n this.#setState(\n 0,\n reconcile(builderRoot, {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n }),\n );\n this.#setState(prev => [builderRoot, prev[1]]);\n this.#builderRoot = undefined;\n }\n } else {\n try {\n this.#applyChanges(this.#pendingChanges, c => c);\n } finally {\n this.#pendingChanges = [];\n }\n }\n };\n\n push(change: Change) {\n // Delay updating the solid store state until the transaction commit\n // (because each update of the solid store is quite expensive). If\n // this.#builderRoot is defined apply the changes to it (we are building\n // from an empty root), otherwise queue the changes to be applied\n // using produce at the end of the transaction but read the relationships\n // now as they are only valid to read when the push is received.\n if (this.#builderRoot) {\n this.#applyChangeToRoot(change, this.#builderRoot);\n } else {\n this.#pendingChanges.push(materializeRelationships(change));\n }\n return emptyArray;\n }\n\n #applyChanges<T>(changes: Iterable<T>, mapper: (v: T) => ViewChange): void {\n this.#setState(\n produce((draftState: State) => {\n this.#applyChangesToRoot<T>(changes, mapper, draftState[0]);\n if (isEmptyRoot(draftState[0])) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n }),\n );\n }\n\n #applyChangesToRoot<T>(\n changes: Iterable<T>,\n mapper: (v: T) => ViewChange,\n root: Entry,\n ) {\n for (const change of changes) {\n this.#applyChangeToRoot(mapper(change), root);\n }\n }\n\n #applyChangeToRoot(change: ViewChange, root: Entry) {\n applyChange(\n root,\n change,\n this.#input.getSchema(),\n '',\n this.#format,\n true /* withIDs */,\n );\n }\n\n #createEmptyRoot(): Entry {\n return {\n '': this.#format.singular ? undefined : [],\n };\n }\n\n updateTTL(ttl: TTL): void {\n this.#updateTTL(ttl);\n }\n}\n\nfunction materializeRelationships(change: Change): ViewChange {\n switch (change.type) {\n case 'add':\n return {type: 'add', node: materializeNodeRelationships(change.node)};\n case 'remove':\n return {type: 'remove', node: materializeNodeRelationships(change.node)};\n case 'child':\n return {\n type: 'child',\n node: {row: change.node.row},\n child: {\n relationshipName: change.child.relationshipName,\n change: materializeRelationships(change.child.change),\n },\n };\n case 'edit':\n return {\n type: 'edit',\n node: {row: change.node.row},\n oldNode: {row: change.oldNode.row},\n };\n }\n}\n\nfunction materializeNodeRelationships(node: Node): Node {\n const relationships: Record<string, () => Stream<Node>> = {};\n for (const relationship in node.relationships) {\n const materialized: Node[] = [];\n for (const n of skipYields(node.relationships[relationship]())) {\n materialized.push(materializeNodeRelationships(n));\n }\n relationships[relationship] = () => materialized;\n }\n return {\n row: node.row,\n relationships,\n };\n}\n\nfunction isEmptyRoot(entry: Entry) {\n const data = entry[''];\n return data === undefined || (Array.isArray(data) && data.length === 0);\n}\n\nexport function createSolidViewFactory(\n setState: SetStoreFunction<State>,\n retry?: () => void,\n) {\n function solidViewFactory<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends Schema,\n TReturn,\n >(\n _query: Query<TTable, TSchema, TReturn>,\n input: Input,\n format: Format,\n onDestroy: () => void,\n onTransactionCommit: (cb: () => void) => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n return new SolidView(\n input,\n onTransactionCommit,\n format,\n onDestroy,\n queryComplete,\n updateTTL,\n setState,\n retry || (() => {}),\n );\n }\n\n solidViewFactory satisfies AnyViewFactory;\n\n return solidViewFactory;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"solid-view.js","names":["#input","#format","#onDestroy","#retry","#updateTTL","#onTransactionCommit","#createEmptyRoot","#applyChangesToRoot","#setState","#makeError","#builderRoot","#applyChanges","#pendingChanges","#applyChangeToRoot"],"sources":["../../../../zero-solid/src/solid-view.ts"],"sourcesContent":["import {produce, reconcile, type SetStoreFunction} from 'solid-js/store';\nimport {emptyArray} from '../../shared/src/sentinels.ts';\nimport {ChangeIndex} from '../../zql/src/ivm/change-index.ts';\nimport {ChangeType} from '../../zql/src/ivm/change-type.ts';\nimport {\n applyChange,\n idSymbol,\n skipYields,\n type ViewChange,\n} from './bindings.ts';\nimport {\n type AnyViewFactory,\n type Change,\n type Entry,\n type ErroredQuery,\n type Format,\n type Input,\n type Node,\n type Output,\n type Query,\n type QueryErrorDetails,\n type QueryResultDetails,\n type Schema,\n type Stream,\n type TTL,\n} from './zero.ts';\n\nexport type State = [Entry, QueryResultDetails];\n\nexport const COMPLETE: QueryResultDetails = Object.freeze({type: 'complete'});\nexport const UNKNOWN: QueryResultDetails = Object.freeze({type: 'unknown'});\n\nexport class SolidView implements Output {\n readonly #input: Input;\n readonly #format: Format;\n readonly #onDestroy: () => void;\n readonly #retry: () => void;\n\n #setState: SetStoreFunction<State>;\n\n // Optimization: if the store is currently empty we build up\n // the view on a plain old JS object stored at #builderRoot, and return\n // that for the new state on transaction commit. This avoids building up\n // large views from scratch via solid produce. The proxy object used by\n // solid produce is slow and in this case we don't care about solid tracking\n // the fine grained changes (everything has changed, it's all new). For a\n // test case with a view with 3000 rows, each row having 2 children, this\n // optimization reduced #applyChanges time from 743ms to 133ms.\n #builderRoot: Entry | undefined;\n #pendingChanges: ViewChange[] = [];\n readonly #updateTTL: (ttl: TTL) => void;\n\n constructor(\n input: Input,\n onTransactionCommit: (cb: () => void) => void,\n format: Format,\n onDestroy: () => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n setState: SetStoreFunction<State>,\n retry: () => void,\n ) {\n this.#input = input;\n onTransactionCommit(this.#onTransactionCommit);\n this.#format = format;\n this.#onDestroy = onDestroy;\n this.#updateTTL = updateTTL;\n this.#retry = retry;\n\n input.setOutput(this);\n\n const initialRoot = this.#createEmptyRoot();\n this.#applyChangesToRoot(\n skipYields(input.fetch({})),\n node => ({type: 'add', node}),\n initialRoot,\n );\n\n this.#setState = setState;\n this.#setState(\n reconcile(\n [\n initialRoot,\n queryComplete === true\n ? COMPLETE\n : 'error' in queryComplete\n ? this.#makeError(queryComplete)\n : UNKNOWN,\n ],\n {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n },\n ),\n );\n\n if (isEmptyRoot(initialRoot)) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n\n if (queryComplete !== true && !('error' in queryComplete)) {\n void queryComplete\n .then(() => {\n this.#setState(prev => [prev[0], COMPLETE]);\n })\n .catch((error: ErroredQuery) => {\n this.#setState(prev => [prev[0], this.#makeError(error)]);\n });\n }\n }\n\n #makeError(error: ErroredQuery): QueryErrorDetails {\n const message = error.message ?? 'An unknown error occurred';\n return {\n type: 'error',\n retry: this.#retry,\n refetch: this.#retry,\n error: {\n type: error.error,\n message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n }\n\n destroy(): void {\n this.#onDestroy();\n }\n\n #onTransactionCommit = () => {\n const builderRoot = this.#builderRoot;\n if (builderRoot) {\n if (!isEmptyRoot(builderRoot)) {\n this.#setState(\n 0,\n reconcile(builderRoot, {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n }),\n );\n this.#setState(prev => [builderRoot, prev[1]]);\n this.#builderRoot = undefined;\n }\n } else {\n try {\n this.#applyChanges(this.#pendingChanges, c => c);\n } finally {\n this.#pendingChanges = [];\n }\n }\n };\n\n push(change: Change) {\n // Delay updating the solid store state until the transaction commit\n // (because each update of the solid store is quite expensive). If\n // this.#builderRoot is defined apply the changes to it (we are building\n // from an empty root), otherwise queue the changes to be applied\n // using produce at the end of the transaction but read the relationships\n // now as they are only valid to read when the push is received.\n if (this.#builderRoot) {\n this.#applyChangeToRoot(\n materializeRelationships(change),\n this.#builderRoot,\n );\n } else {\n this.#pendingChanges.push(materializeRelationships(change));\n }\n return emptyArray;\n }\n\n #applyChanges<T>(changes: Iterable<T>, mapper: (v: T) => ViewChange): void {\n this.#setState(\n produce((draftState: State) => {\n this.#applyChangesToRoot<T>(changes, mapper, draftState[0]);\n if (isEmptyRoot(draftState[0])) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n }),\n );\n }\n\n #applyChangesToRoot<T>(\n changes: Iterable<T>,\n mapper: (v: T) => ViewChange,\n root: Entry,\n ) {\n for (const change of changes) {\n this.#applyChangeToRoot(mapper(change), root);\n }\n }\n\n #applyChangeToRoot(change: ViewChange, root: Entry) {\n applyChange(\n root,\n change,\n this.#input.getSchema(),\n '',\n this.#format,\n true /* withIDs */,\n );\n }\n\n #createEmptyRoot(): Entry {\n return {\n '': this.#format.singular ? undefined : [],\n };\n }\n\n updateTTL(ttl: TTL): void {\n this.#updateTTL(ttl);\n }\n}\n\nfunction materializeRelationships(change: Change): ViewChange {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n return {\n type: 'add',\n node: materializeNodeRelationships(change[ChangeIndex.NODE]),\n };\n case ChangeType.REMOVE:\n return {\n type: 'remove',\n node: materializeNodeRelationships(change[ChangeIndex.NODE]),\n };\n case ChangeType.CHILD:\n return {\n type: 'child',\n node: {row: change[ChangeIndex.NODE].row},\n child: {\n relationshipName: change[ChangeIndex.CHILD_DATA].relationshipName,\n change: materializeRelationships(\n change[ChangeIndex.CHILD_DATA].change,\n ),\n },\n };\n case ChangeType.EDIT:\n return {\n type: 'edit',\n node: {row: change[ChangeIndex.NODE].row},\n oldNode: {row: change[ChangeIndex.OLD_NODE].row},\n };\n }\n}\n\nfunction materializeNodeRelationships(node: Node): Node {\n const relationships: Record<string, () => Stream<Node>> = {};\n for (const relationship in node.relationships) {\n const materialized: Node[] = [];\n for (const n of skipYields(node.relationships[relationship]())) {\n materialized.push(materializeNodeRelationships(n));\n }\n relationships[relationship] = () => materialized;\n }\n return {\n row: node.row,\n relationships,\n };\n}\n\nfunction isEmptyRoot(entry: Entry) {\n const data = entry[''];\n return data === undefined || (Array.isArray(data) && data.length === 0);\n}\n\nexport function createSolidViewFactory(\n setState: SetStoreFunction<State>,\n retry?: () => void,\n) {\n function solidViewFactory<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends Schema,\n TReturn,\n >(\n _query: Query<TTable, TSchema, TReturn>,\n input: Input,\n format: Format,\n onDestroy: () => void,\n onTransactionCommit: (cb: () => void) => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n return new SolidView(\n input,\n onTransactionCommit,\n format,\n onDestroy,\n queryComplete,\n updateTTL,\n setState,\n retry || (() => {}),\n );\n }\n\n solidViewFactory satisfies AnyViewFactory;\n\n return solidViewFactory;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../../zql/src/builder/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAE3D,OAAO,KAAK,EACV,GAAG,EAGH,SAAS,EAIT,WAAW,EAEX,QAAQ,EAIT,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,oCAAoC,CAAC;AAC5D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAI1E,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAC;AAIpC,OAAO,KAAK,EAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,EAAC,MAAM,EAAE,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAK1D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../../zql/src/builder/builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.js","names":[],"sources":["../../../../../zql/src/builder/builder.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n AST,\n ColumnReference,\n CompoundKey,\n Condition,\n Conjunction,\n CorrelatedSubquery,\n CorrelatedSubqueryCondition,\n Disjunction,\n LiteralValue,\n Ordering,\n Parameter,\n SimpleCondition,\n ValuePosition,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {Exists} from '../ivm/exists.ts';\nimport {FanIn} from '../ivm/fan-in.ts';\nimport {FanOut} from '../ivm/fan-out.ts';\nimport {\n buildFilterPipeline,\n type FilterInput,\n} from '../ivm/filter-operators.ts';\nimport {Filter} from '../ivm/filter.ts';\nimport {FlippedJoin} from '../ivm/flipped-join.ts';\nimport {Join} from '../ivm/join.ts';\nimport type {Input, InputBase, Storage} from '../ivm/operator.ts';\nimport {Skip} from '../ivm/skip.ts';\nimport type {Source, SourceInput} from '../ivm/source.ts';\nimport {Take} from '../ivm/take.ts';\nimport {UnionFanIn} from '../ivm/union-fan-in.ts';\nimport {UnionFanOut} from '../ivm/union-fan-out.ts';\nimport {planQuery} from '../planner/planner-builder.ts';\nimport type {ConnectionCostModel} from '../planner/planner-connection.ts';\nimport {completeOrdering} from '../query/complete-ordering.ts';\nimport type {PlanDebugger} from '../planner/planner-debug.ts';\nimport type {DebugDelegate} from './debug-delegate.ts';\nimport {createPredicate, type NoSubqueryCondition} from './filter.ts';\n\nexport type StaticQueryParameters = {\n authData: Record<string, JSONValue>;\n preMutationRow?: Row | undefined;\n};\n\n/**\n * Interface required of caller to buildPipeline. Connects to constructed\n * pipeline to delegate environment to provide sources and storage.\n */\nexport interface BuilderDelegate {\n readonly applyFiltersAnyway?: boolean | undefined;\n debug?: DebugDelegate | undefined;\n\n /**\n * When true, allows NOT EXISTS conditions in queries.\n * Defaults to false.\n *\n * We only set this to true on the server.\n * The client-side query engine cannot support NOT EXISTS because:\n * 1. Zero only syncs a subset of data to the client\n * 2. On the client, we can't distinguish between a row not existing vs.\n * a row not being synced to the client\n * 3. NOT EXISTS requires complete knowledge of what doesn't exist\n */\n readonly enableNotExists?: boolean | undefined;\n\n /**\n * Called once for each source needed by the AST.\n * Might be called multiple times with same tableName. It is OK to return\n * same storage instance in that case.\n */\n getSource(tableName: string): Source | undefined;\n\n /**\n * Called once for each operator that requires storage. Should return a new\n * unique storage object for each call.\n */\n createStorage(name: string): Storage;\n\n decorateInput(input: Input, name: string): Input;\n\n addEdge(source: InputBase, dest: InputBase): void;\n\n decorateFilterInput(input: FilterInput, name: string): FilterInput;\n\n decorateSourceInput(input: SourceInput, queryID: string): Input;\n\n /**\n * The AST is mapped on-the-wire between client and server names.\n *\n * There is no \"wire\" for zqlite tests so this function is provided\n * to allow tests to remap the AST.\n */\n mapAst?: ((ast: AST) => AST) | undefined;\n}\n\n/**\n * Builds a pipeline from an AST. Caller must provide a delegate to create source\n * and storage interfaces as necessary.\n *\n * Usage:\n *\n * ```ts\n * class MySink implements Output {\n * readonly #input: Input;\n *\n * constructor(input: Input) {\n * this.#input = input;\n * input.setOutput(this);\n * }\n *\n * push(change: Change, _: Operator) {\n * console.log(change);\n * }\n * }\n *\n * const input = buildPipeline(ast, myDelegate, hash(ast));\n * const sink = new MySink(input);\n * ```\n */\nexport function buildPipeline(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n costModel?: ConnectionCostModel,\n lc?: LogContext,\n planDebugger?: PlanDebugger,\n): Input {\n ast = delegate.mapAst ? delegate.mapAst(ast) : ast;\n ast = completeOrdering(\n ast,\n tableName => must(delegate.getSource(tableName)).tableSchema.primaryKey,\n );\n\n if (costModel) {\n ast = planQuery(ast, costModel, planDebugger, lc);\n }\n return buildPipelineInternal(ast, delegate, queryID, '');\n}\n\nexport function bindStaticParameters(\n ast: AST,\n staticQueryParameters: StaticQueryParameters | undefined,\n) {\n const visit = (node: AST): AST => ({\n ...node,\n where: node.where ? bindCondition(node.where) : undefined,\n related: node.related?.map(sq => ({\n ...sq,\n subquery: visit(sq.subquery),\n })),\n });\n\n function bindCondition(condition: Condition): Condition {\n if (condition.type === 'simple') {\n return {\n ...condition,\n left: bindValue(condition.left),\n right: bindValue(condition.right) as Exclude<\n ValuePosition,\n ColumnReference\n >,\n };\n }\n if (condition.type === 'correlatedSubquery') {\n return {\n ...condition,\n related: {\n ...condition.related,\n subquery: visit(condition.related.subquery),\n },\n };\n }\n\n return {\n ...condition,\n conditions: condition.conditions.map(bindCondition),\n };\n }\n\n const bindValue = (value: ValuePosition): ValuePosition => {\n if (isParameter(value)) {\n const anchor = must(\n staticQueryParameters,\n 'Static query params do not exist',\n )[value.anchor];\n const resolvedValue = resolveField(anchor, value.field);\n return {\n type: 'literal',\n value: resolvedValue as LiteralValue,\n };\n }\n return value;\n };\n\n return visit(ast);\n}\n\nfunction resolveField(\n anchor: Record<string, JSONValue> | Row | undefined,\n field: string | string[],\n): unknown {\n if (anchor === undefined) {\n return null;\n }\n\n if (Array.isArray(field)) {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return field.reduce((acc, f) => (acc as any)?.[f], anchor) ?? null;\n }\n\n return anchor[field] ?? null;\n}\n\nfunction isParameter(value: ValuePosition): value is Parameter {\n return value.type === 'static';\n}\n\nconst EXISTS_LIMIT = 3;\nconst PERMISSIONS_EXISTS_LIMIT = 1;\n\n/**\n * Checks if a condition tree contains any NOT EXISTS operations.\n * Recursively checks AND/OR branches but does not recurse into nested subqueries\n * (those are checked when buildPipelineInternal processes them).\n */\nexport function assertNoNotExists(condition: Condition): void {\n switch (condition.type) {\n case 'simple':\n return;\n\n case 'correlatedSubquery':\n if (condition.op === 'NOT EXISTS') {\n throw new Error(\n 'not(exists()) is not supported on the client - see https://bugs.rocicorp.dev/issue/3438',\n );\n }\n return;\n\n case 'and':\n case 'or':\n for (const c of condition.conditions) {\n assertNoNotExists(c);\n }\n return;\n default:\n unreachable(condition);\n }\n}\n\nfunction buildPipelineInternal(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n name: string,\n partitionKey?: CompoundKey,\n): Input {\n const source = delegate.getSource(ast.table);\n if (!source) {\n throw new Error(`Source not found: ${ast.table}`);\n }\n\n ast = uniquifyCorrelatedSubqueryConditionAliases(ast);\n\n if (!delegate.enableNotExists && ast.where) {\n assertNoNotExists(ast.where);\n }\n\n const csqConditions = gatherCorrelatedSubqueryQueryConditions(ast.where);\n const splitEditKeys: Set<string> = partitionKey\n ? new Set(partitionKey)\n : new Set();\n const aliases = new Set<string>();\n for (const csq of csqConditions) {\n aliases.add(csq.related.subquery.alias || '');\n for (const key of csq.related.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n if (ast.related) {\n for (const csq of ast.related) {\n for (const key of csq.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n }\n const conn = source.connect(\n must(ast.orderBy),\n ast.where,\n splitEditKeys,\n delegate.debug,\n );\n\n let end: Input = delegate.decorateSourceInput(conn, queryID);\n end = delegate.decorateInput(end, `${name}:source(${ast.table})`);\n const {fullyAppliedFilters} = conn;\n\n if (ast.start) {\n const skip = new Skip(end, ast.start);\n delegate.addEdge(end, skip);\n end = delegate.decorateInput(skip, `${name}:skip)`);\n }\n\n for (const csqCondition of csqConditions) {\n // flipped EXISTS are handled in applyWhere\n if (!csqCondition.flip) {\n end = applyCorrelatedSubQuery(\n {\n ...csqCondition.related,\n subquery: {\n ...csqCondition.related.subquery,\n limit:\n csqCondition.related.system === 'permissions'\n ? PERMISSIONS_EXISTS_LIMIT\n : EXISTS_LIMIT,\n },\n },\n delegate,\n queryID,\n end,\n name,\n true,\n );\n }\n }\n\n if (ast.where && (!fullyAppliedFilters || delegate.applyFiltersAnyway)) {\n end = applyWhere(end, ast.where, delegate, name);\n }\n\n if (ast.limit !== undefined) {\n const takeName = `${name}:take`;\n const take = new Take(\n end,\n delegate.createStorage(takeName),\n ast.limit,\n partitionKey,\n );\n delegate.addEdge(end, take);\n end = delegate.decorateInput(take, takeName);\n }\n\n if (ast.related) {\n // Dedupe by alias - last one wins (LWW), like limit(5).limit(10)\n const byAlias = new Map<string, CorrelatedSubquery>();\n for (const csq of ast.related) {\n byAlias.set(csq.subquery.alias ?? '', csq);\n }\n for (const csq of byAlias.values()) {\n end = applyCorrelatedSubQuery(csq, delegate, queryID, end, name, false);\n }\n }\n\n return end;\n}\n\nfunction applyWhere(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): Input {\n if (!conditionIncludesFlippedSubqueryAtAnyLevel(condition)) {\n return buildFilterPipeline(input, delegate, filterInput =>\n applyFilter(filterInput, condition, delegate, name),\n );\n }\n\n return applyFilterWithFlips(input, condition, delegate, name);\n}\n\nfunction applyFilterWithFlips(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): Input {\n let end = input;\n assert(condition.type !== 'simple', 'Simple conditions cannot have flips');\n\n switch (condition.type) {\n case 'and': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n if (withoutFlipped.length > 0) {\n end = buildFilterPipeline(input, delegate, filterInput =>\n applyAnd(\n filterInput,\n {\n type: 'and',\n conditions: withoutFlipped,\n },\n delegate,\n name,\n ),\n );\n }\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n for (const cond of withFlipped) {\n end = applyFilterWithFlips(end, cond, delegate, name);\n }\n break;\n }\n case 'or': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n\n const ufo = new UnionFanOut(end);\n delegate.addEdge(end, ufo);\n end = delegate.decorateInput(ufo, `${name}:ufo`);\n\n const branches: Input[] = [];\n if (withoutFlipped.length > 0) {\n branches.push(\n buildFilterPipeline(end, delegate, filterInput =>\n applyOr(\n filterInput,\n {\n type: 'or',\n conditions: withoutFlipped,\n },\n delegate,\n name,\n ),\n ),\n );\n }\n\n for (const cond of withFlipped) {\n branches.push(applyFilterWithFlips(end, cond, delegate, name));\n }\n\n const ufi = new UnionFanIn(ufo, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ufi);\n }\n end = delegate.decorateInput(ufi, `${name}:ufi`);\n\n break;\n }\n case 'correlatedSubquery': {\n const sq = condition.related;\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n '',\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n );\n const flippedJoin = new FlippedJoin({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: must(\n sq.subquery.alias,\n 'Subquery must have an alias',\n ),\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n });\n delegate.addEdge(end, flippedJoin);\n delegate.addEdge(child, flippedJoin);\n end = delegate.decorateInput(\n flippedJoin,\n `${name}:flipped-join(${sq.subquery.alias})`,\n );\n break;\n }\n }\n\n return end;\n}\n\nfunction applyFilter(\n input: FilterInput,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n switch (condition.type) {\n case 'and':\n return applyAnd(input, condition, delegate, name);\n case 'or':\n return applyOr(input, condition, delegate, name);\n case 'correlatedSubquery':\n return applyCorrelatedSubqueryCondition(input, condition, delegate, name);\n case 'simple':\n return applySimpleCondition(input, delegate, condition);\n }\n}\n\nfunction applyAnd(\n input: FilterInput,\n condition: Conjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n for (const subCondition of condition.conditions) {\n input = applyFilter(input, subCondition, delegate, name);\n }\n return input;\n}\n\nexport function applyOr(\n input: FilterInput,\n condition: Disjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n const [subqueryConditions, otherConditions] =\n groupSubqueryConditions(condition);\n // if there are no subquery conditions, no fan-in / fan-out is needed\n if (subqueryConditions.length === 0) {\n const filter = new Filter(\n input,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(input, filter);\n return filter;\n }\n\n const fanOut = new FanOut(input);\n delegate.addEdge(input, fanOut);\n const branches = subqueryConditions.map(subCondition =>\n applyFilter(fanOut, subCondition, delegate, name),\n );\n if (otherConditions.length > 0) {\n const filter = new Filter(\n fanOut,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(fanOut, filter);\n branches.push(filter);\n }\n const ret = new FanIn(fanOut, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ret);\n }\n fanOut.setFanIn(ret);\n return ret;\n}\n\nexport function groupSubqueryConditions(condition: Disjunction) {\n const partitioned: [\n subqueryConditions: Condition[],\n otherConditions: NoSubqueryCondition[],\n ] = [[], []];\n for (const subCondition of condition.conditions) {\n if (isNotAndDoesNotContainSubquery(subCondition)) {\n partitioned[1].push(subCondition);\n } else {\n partitioned[0].push(subCondition);\n }\n }\n return partitioned;\n}\n\nexport function isNotAndDoesNotContainSubquery(\n condition: Condition,\n): condition is NoSubqueryCondition {\n if (condition.type === 'correlatedSubquery') {\n return false;\n }\n if (condition.type === 'simple') {\n return true;\n }\n return condition.conditions.every(isNotAndDoesNotContainSubquery);\n}\n\nfunction applySimpleCondition(\n input: FilterInput,\n delegate: BuilderDelegate,\n condition: SimpleCondition,\n): FilterInput {\n const filter = new Filter(input, createPredicate(condition));\n delegate.decorateFilterInput(\n filter,\n `${valuePosName(condition.left)}:${condition.op}:${valuePosName(condition.right)}`,\n );\n delegate.addEdge(input, filter);\n return filter;\n}\n\nfunction valuePosName(left: ValuePosition) {\n switch (left.type) {\n case 'static':\n return left.field;\n case 'literal':\n return left.value;\n case 'column':\n return left.name;\n }\n}\n\nfunction applyCorrelatedSubQuery(\n sq: CorrelatedSubquery,\n delegate: BuilderDelegate,\n queryID: string,\n end: Input,\n name: string,\n fromCondition: boolean,\n) {\n // TODO: we only omit the join if the CSQ if from a condition since\n // we want to create an empty array for `related` fields that are `limit(0)`\n if (sq.subquery.limit === 0 && fromCondition) {\n return end;\n }\n\n assert(sq.subquery.alias, 'Subquery must have an alias');\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n queryID,\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n );\n\n const joinName = `${name}:join(${sq.subquery.alias})`;\n const join = new Join({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: sq.subquery.alias,\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n });\n delegate.addEdge(end, join);\n delegate.addEdge(child, join);\n return delegate.decorateInput(join, joinName);\n}\n\nfunction applyCorrelatedSubqueryCondition(\n input: FilterInput,\n condition: CorrelatedSubqueryCondition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n assert(\n condition.op === 'EXISTS' || condition.op === 'NOT EXISTS',\n 'Expected EXISTS or NOT EXISTS operator',\n );\n if (condition.related.subquery.limit === 0) {\n if (condition.op === 'EXISTS') {\n const filter = new Filter(input, () => false);\n delegate.addEdge(input, filter);\n return filter;\n }\n const filter = new Filter(input, () => true);\n delegate.addEdge(input, filter);\n return filter;\n }\n const existsName = `${name}:exists(${condition.related.subquery.alias})`;\n const exists = new Exists(\n input,\n must(condition.related.subquery.alias),\n condition.related.correlation.parentField,\n condition.op,\n );\n delegate.addEdge(input, exists);\n return delegate.decorateFilterInput(exists, existsName);\n}\n\nfunction gatherCorrelatedSubqueryQueryConditions(\n condition: Condition | undefined,\n) {\n const csqs: CorrelatedSubqueryCondition[] = [];\n const gather = (condition: Condition) => {\n if (condition.type === 'correlatedSubquery') {\n csqs.push(condition);\n return;\n }\n if (condition.type === 'and' || condition.type === 'or') {\n for (const c of condition.conditions) {\n gather(c);\n }\n return;\n }\n };\n if (condition) {\n gather(condition);\n }\n return csqs;\n}\n\nexport function assertOrderingIncludesPK(\n ordering: Ordering,\n pk: PrimaryKey,\n): void {\n // oxlint-disable-next-line unicorn/prefer-set-has -- Array is more appropriate here for small collections\n const orderingFields = ordering.map(([field]) => field);\n const missingFields = pk.filter(pkField => !orderingFields.includes(pkField));\n\n if (missingFields.length > 0) {\n throw new Error(\n `Ordering must include all primary key fields. Missing: ${missingFields.join(\n ', ',\n )}. ZQL automatically appends primary key fields to the ordering if they are missing \n so a common cause of this error is a casing mismatch between Postgres and ZQL.\n E.g., \"userid\" vs \"userID\".\n You may want to add double-quotes around your Postgres column names to prevent Postgres from lower-casing them:\n https://www.postgresql.org/docs/current/sql-syntax-lexical.htm`,\n );\n }\n}\n\nfunction uniquifyCorrelatedSubqueryConditionAliases(ast: AST): AST {\n if (!ast.where) {\n return ast;\n }\n const {where} = ast;\n if (where.type !== 'and' && where.type !== 'or') {\n return ast;\n }\n\n let count = 0;\n const uniquifyCorrelatedSubquery = (csqc: CorrelatedSubqueryCondition) => ({\n ...csqc,\n related: {\n ...csqc.related,\n subquery: {\n ...csqc.related.subquery,\n alias: (csqc.related.subquery.alias ?? '') + '_' + count++,\n },\n },\n });\n\n const uniquify = (cond: Condition): Condition => {\n if (cond.type === 'simple') {\n return cond;\n } else if (cond.type === 'correlatedSubquery') {\n return uniquifyCorrelatedSubquery(cond);\n }\n const conditions = [];\n for (const c of cond.conditions) {\n conditions.push(uniquify(c));\n }\n return {\n type: cond.type,\n conditions,\n };\n };\n\n const result = {\n ...ast,\n where: uniquify(where),\n };\n return result;\n}\n\nexport function conditionIncludesFlippedSubqueryAtAnyLevel(\n cond: Condition,\n): boolean {\n if (cond.type === 'correlatedSubquery') {\n return !!cond.flip;\n }\n if (cond.type === 'and' || cond.type === 'or') {\n return cond.conditions.some(c =>\n conditionIncludesFlippedSubqueryAtAnyLevel(c),\n );\n }\n // simple conditions don't have flips\n return false;\n}\n\nexport function partitionBranches(\n conditions: readonly Condition[],\n predicate: (c: Condition) => boolean,\n) {\n const matched: Condition[] = [];\n const notMatched: Condition[] = [];\n for (const c of conditions) {\n if (predicate(c)) {\n matched.push(c);\n } else {\n notMatched.push(c);\n }\n }\n return [matched, notMatched] as const;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builder.js","names":[],"sources":["../../../../../zql/src/builder/builder.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n AST,\n ColumnReference,\n CompoundKey,\n Condition,\n Conjunction,\n CorrelatedSubquery,\n CorrelatedSubqueryCondition,\n Disjunction,\n LiteralValue,\n Ordering,\n Parameter,\n SimpleCondition,\n ValuePosition,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {Exists} from '../ivm/exists.ts';\nimport {FanIn} from '../ivm/fan-in.ts';\nimport {FanOut} from '../ivm/fan-out.ts';\nimport {\n buildFilterPipeline,\n type FilterInput,\n} from '../ivm/filter-operators.ts';\nimport {Filter} from '../ivm/filter.ts';\nimport {FlippedJoin} from '../ivm/flipped-join.ts';\nimport {Join} from '../ivm/join.ts';\nimport type {Input, InputBase, Storage} from '../ivm/operator.ts';\nimport {Skip} from '../ivm/skip.ts';\nimport type {Source, SourceInput} from '../ivm/source.ts';\nimport {Take} from '../ivm/take.ts';\nimport {UnionFanIn} from '../ivm/union-fan-in.ts';\nimport {UnionFanOut} from '../ivm/union-fan-out.ts';\nimport {planQuery} from '../planner/planner-builder.ts';\nimport type {ConnectionCostModel} from '../planner/planner-connection.ts';\nimport type {PlanDebugger} from '../planner/planner-debug.ts';\nimport {completeOrdering} from '../query/complete-ordering.ts';\nimport type {DebugDelegate} from './debug-delegate.ts';\nimport {createPredicate, type NoSubqueryCondition} from './filter.ts';\n\nexport type StaticQueryParameters = {\n authData: Record<string, JSONValue>;\n preMutationRow?: Row | undefined;\n};\n\n/**\n * Interface required of caller to buildPipeline. Connects to constructed\n * pipeline to delegate environment to provide sources and storage.\n */\nexport interface BuilderDelegate {\n readonly applyFiltersAnyway?: boolean | undefined;\n debug?: DebugDelegate | undefined;\n\n /**\n * When true, allows NOT EXISTS conditions in queries.\n * Defaults to false.\n *\n * We only set this to true on the server.\n * The client-side query engine cannot support NOT EXISTS because:\n * 1. Zero only syncs a subset of data to the client\n * 2. On the client, we can't distinguish between a row not existing vs.\n * a row not being synced to the client\n * 3. NOT EXISTS requires complete knowledge of what doesn't exist\n */\n readonly enableNotExists?: boolean | undefined;\n\n /**\n * Called once for each source needed by the AST.\n * Might be called multiple times with same tableName. It is OK to return\n * same storage instance in that case.\n */\n getSource(tableName: string): Source | undefined;\n\n /**\n * Called once for each operator that requires storage. Should return a new\n * unique storage object for each call.\n */\n createStorage(name: string): Storage;\n\n decorateInput(input: Input, name: string): Input;\n\n addEdge(source: InputBase, dest: InputBase): void;\n\n decorateFilterInput(input: FilterInput, name: string): FilterInput;\n\n decorateSourceInput(input: SourceInput, queryID: string): Input;\n\n /**\n * The AST is mapped on-the-wire between client and server names.\n *\n * There is no \"wire\" for zqlite tests so this function is provided\n * to allow tests to remap the AST.\n */\n mapAst?: ((ast: AST) => AST) | undefined;\n}\n\n/**\n * Builds a pipeline from an AST. Caller must provide a delegate to create source\n * and storage interfaces as necessary.\n *\n * Usage:\n *\n * ```ts\n * class MySink implements Output {\n * readonly #input: Input;\n *\n * constructor(input: Input) {\n * this.#input = input;\n * input.setOutput(this);\n * }\n *\n * push(change: Change, _: Operator) {\n * console.log(change);\n * }\n * }\n *\n * const input = buildPipeline(ast, myDelegate, hash(ast));\n * const sink = new MySink(input);\n * ```\n */\nexport function buildPipeline(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n costModel?: ConnectionCostModel,\n lc?: LogContext,\n planDebugger?: PlanDebugger,\n): Input {\n ast = delegate.mapAst ? delegate.mapAst(ast) : ast;\n ast = completeOrdering(\n ast,\n tableName => must(delegate.getSource(tableName)).tableSchema.primaryKey,\n );\n\n if (costModel) {\n ast = planQuery(ast, costModel, planDebugger, lc);\n }\n return buildPipelineInternal(ast, delegate, queryID, '');\n}\n\nexport function bindStaticParameters(\n ast: AST,\n staticQueryParameters: StaticQueryParameters | undefined,\n) {\n const visit = (node: AST): AST => ({\n ...node,\n where: node.where ? bindCondition(node.where) : undefined,\n related: node.related?.map(sq => ({\n ...sq,\n subquery: visit(sq.subquery),\n })),\n });\n\n function bindCondition(condition: Condition): Condition {\n if (condition.type === 'simple') {\n return {\n ...condition,\n left: bindValue(condition.left),\n right: bindValue(condition.right) as Exclude<\n ValuePosition,\n ColumnReference\n >,\n };\n }\n if (condition.type === 'correlatedSubquery') {\n return {\n ...condition,\n related: {\n ...condition.related,\n subquery: visit(condition.related.subquery),\n },\n };\n }\n\n return {\n ...condition,\n conditions: condition.conditions.map(bindCondition),\n };\n }\n\n const bindValue = (value: ValuePosition): ValuePosition => {\n if (isParameter(value)) {\n const anchor = must(\n staticQueryParameters,\n 'Static query params do not exist',\n )[value.anchor];\n const resolvedValue = resolveField(anchor, value.field);\n return {\n type: 'literal',\n value: resolvedValue as LiteralValue,\n };\n }\n return value;\n };\n\n return visit(ast);\n}\n\nfunction resolveField(\n anchor: Record<string, JSONValue> | Row | undefined,\n field: string | string[],\n): unknown {\n if (anchor === undefined) {\n return null;\n }\n\n if (Array.isArray(field)) {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return field.reduce((acc, f) => (acc as any)?.[f], anchor) ?? null;\n }\n\n return anchor[field] ?? null;\n}\n\nfunction isParameter(value: ValuePosition): value is Parameter {\n return value.type === 'static';\n}\n\nconst EXISTS_LIMIT = 3;\nconst PERMISSIONS_EXISTS_LIMIT = 1;\n\n/**\n * Checks if a condition tree contains any NOT EXISTS operations.\n * Recursively checks AND/OR branches but does not recurse into nested subqueries\n * (those are checked when buildPipelineInternal processes them).\n */\nexport function assertNoNotExists(condition: Condition): void {\n switch (condition.type) {\n case 'simple':\n return;\n\n case 'correlatedSubquery':\n if (condition.op === 'NOT EXISTS') {\n throw new Error(\n 'not(exists()) is not supported on the client - see https://bugs.rocicorp.dev/issue/3438',\n );\n }\n return;\n\n case 'and':\n case 'or':\n for (const c of condition.conditions) {\n assertNoNotExists(c);\n }\n return;\n default:\n unreachable(condition);\n }\n}\n\nfunction buildPipelineInternal(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n name: string,\n partitionKey?: CompoundKey,\n): Input {\n const source = delegate.getSource(ast.table);\n if (!source) {\n throw new Error(`Source not found: ${ast.table}`);\n }\n\n ast = uniquifyCorrelatedSubqueryConditionAliases(ast);\n\n if (!delegate.enableNotExists && ast.where) {\n assertNoNotExists(ast.where);\n }\n\n const csqConditions = gatherCorrelatedSubqueryQueryConditions(ast.where);\n const splitEditKeys: Set<string> = partitionKey\n ? new Set(partitionKey)\n : new Set();\n const aliases = new Set<string>();\n for (const csq of csqConditions) {\n aliases.add(csq.related.subquery.alias || '');\n for (const key of csq.related.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n if (ast.related) {\n for (const csq of ast.related) {\n for (const key of csq.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n }\n const conn = source.connect(\n must(ast.orderBy),\n ast.where,\n splitEditKeys,\n delegate.debug,\n );\n\n let end: Input = delegate.decorateSourceInput(conn, queryID);\n end = delegate.decorateInput(end, `${name}:source(${ast.table})`);\n const {fullyAppliedFilters} = conn;\n\n if (ast.start) {\n const skip = new Skip(end, ast.start);\n delegate.addEdge(end, skip);\n end = delegate.decorateInput(skip, `${name}:skip)`);\n }\n\n for (const csqCondition of csqConditions) {\n // flipped EXISTS are handled in applyWhere\n if (!csqCondition.flip) {\n end = applyCorrelatedSubQuery(\n {\n ...csqCondition.related,\n subquery: {\n ...csqCondition.related.subquery,\n limit:\n csqCondition.related.system === 'permissions'\n ? PERMISSIONS_EXISTS_LIMIT\n : EXISTS_LIMIT,\n },\n },\n delegate,\n queryID,\n end,\n name,\n true,\n );\n }\n }\n\n if (ast.where && (!fullyAppliedFilters || delegate.applyFiltersAnyway)) {\n end = applyWhere(end, ast.where, delegate, name);\n }\n\n if (ast.limit !== undefined) {\n const takeName = `${name}:take`;\n const take = new Take(\n end,\n delegate.createStorage(takeName),\n ast.limit,\n partitionKey,\n );\n delegate.addEdge(end, take);\n end = delegate.decorateInput(take, takeName);\n }\n\n if (ast.related) {\n // Dedupe by alias - last one wins (LWW), like limit(5).limit(10)\n const byAlias = new Map<string, CorrelatedSubquery>();\n for (const csq of ast.related) {\n byAlias.set(csq.subquery.alias ?? '', csq);\n }\n for (const csq of byAlias.values()) {\n end = applyCorrelatedSubQuery(csq, delegate, queryID, end, name, false);\n }\n }\n\n return end;\n}\n\nfunction applyWhere(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): Input {\n if (!conditionIncludesFlippedSubqueryAtAnyLevel(condition)) {\n return buildFilterPipeline(input, delegate, filterInput =>\n applyFilter(filterInput, condition, delegate, name),\n );\n }\n\n return applyFilterWithFlips(input, condition, delegate, name);\n}\n\nfunction applyFilterWithFlips(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): Input {\n let end = input;\n assert(condition.type !== 'simple', 'Simple conditions cannot have flips');\n\n switch (condition.type) {\n case 'and': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n if (withoutFlipped.length > 0) {\n end = buildFilterPipeline(input, delegate, filterInput =>\n applyAnd(\n filterInput,\n {\n type: 'and',\n conditions: withoutFlipped,\n },\n delegate,\n name,\n ),\n );\n }\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n for (const cond of withFlipped) {\n end = applyFilterWithFlips(end, cond, delegate, name);\n }\n break;\n }\n case 'or': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n\n const ufo = new UnionFanOut(end);\n delegate.addEdge(end, ufo);\n end = delegate.decorateInput(ufo, `${name}:ufo`);\n\n const branches: Input[] = [];\n if (withoutFlipped.length > 0) {\n branches.push(\n buildFilterPipeline(end, delegate, filterInput =>\n applyOr(\n filterInput,\n {\n type: 'or',\n conditions: withoutFlipped,\n },\n delegate,\n name,\n ),\n ),\n );\n }\n\n for (const cond of withFlipped) {\n branches.push(applyFilterWithFlips(end, cond, delegate, name));\n }\n\n const ufi = new UnionFanIn(ufo, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ufi);\n }\n end = delegate.decorateInput(ufi, `${name}:ufi`);\n\n break;\n }\n case 'correlatedSubquery': {\n const sq = condition.related;\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n '',\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n );\n const flippedJoin = new FlippedJoin({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: must(\n sq.subquery.alias,\n 'Subquery must have an alias',\n ),\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n });\n delegate.addEdge(end, flippedJoin);\n delegate.addEdge(child, flippedJoin);\n end = delegate.decorateInput(\n flippedJoin,\n `${name}:flipped-join(${sq.subquery.alias})`,\n );\n break;\n }\n }\n\n return end;\n}\n\nfunction applyFilter(\n input: FilterInput,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n switch (condition.type) {\n case 'and':\n return applyAnd(input, condition, delegate, name);\n case 'or':\n return applyOr(input, condition, delegate, name);\n case 'correlatedSubquery':\n return applyCorrelatedSubqueryCondition(input, condition, delegate, name);\n case 'simple':\n return applySimpleCondition(input, delegate, condition);\n }\n}\n\nfunction applyAnd(\n input: FilterInput,\n condition: Conjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n for (const subCondition of condition.conditions) {\n input = applyFilter(input, subCondition, delegate, name);\n }\n return input;\n}\n\nexport function applyOr(\n input: FilterInput,\n condition: Disjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n const [subqueryConditions, otherConditions] =\n groupSubqueryConditions(condition);\n // if there are no subquery conditions, no fan-in / fan-out is needed\n if (subqueryConditions.length === 0) {\n const filter = new Filter(\n input,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(input, filter);\n return filter;\n }\n\n const fanOut = new FanOut(input);\n delegate.addEdge(input, fanOut);\n const branches = subqueryConditions.map(subCondition =>\n applyFilter(fanOut, subCondition, delegate, name),\n );\n if (otherConditions.length > 0) {\n const filter = new Filter(\n fanOut,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(fanOut, filter);\n branches.push(filter);\n }\n const ret = new FanIn(fanOut, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ret);\n }\n fanOut.setFanIn(ret);\n return ret;\n}\n\nexport function groupSubqueryConditions(condition: Disjunction) {\n const partitioned: [\n subqueryConditions: Condition[],\n otherConditions: NoSubqueryCondition[],\n ] = [[], []];\n for (const subCondition of condition.conditions) {\n if (isNotAndDoesNotContainSubquery(subCondition)) {\n partitioned[1].push(subCondition);\n } else {\n partitioned[0].push(subCondition);\n }\n }\n return partitioned;\n}\n\nexport function isNotAndDoesNotContainSubquery(\n condition: Condition,\n): condition is NoSubqueryCondition {\n if (condition.type === 'correlatedSubquery') {\n return false;\n }\n if (condition.type === 'simple') {\n return true;\n }\n return condition.conditions.every(isNotAndDoesNotContainSubquery);\n}\n\nfunction applySimpleCondition(\n input: FilterInput,\n delegate: BuilderDelegate,\n condition: SimpleCondition,\n): FilterInput {\n const filter = new Filter(input, createPredicate(condition));\n delegate.decorateFilterInput(\n filter,\n `${valuePosName(condition.left)}:${condition.op}:${valuePosName(condition.right)}`,\n );\n delegate.addEdge(input, filter);\n return filter;\n}\n\nfunction valuePosName(left: ValuePosition) {\n switch (left.type) {\n case 'static':\n return left.field;\n case 'literal':\n return left.value;\n case 'column':\n return left.name;\n }\n}\n\nfunction applyCorrelatedSubQuery(\n sq: CorrelatedSubquery,\n delegate: BuilderDelegate,\n queryID: string,\n end: Input,\n name: string,\n fromCondition: boolean,\n) {\n // TODO: we only omit the join if the CSQ if from a condition since\n // we want to create an empty array for `related` fields that are `limit(0)`\n if (sq.subquery.limit === 0 && fromCondition) {\n return end;\n }\n\n assert(sq.subquery.alias, 'Subquery must have an alias');\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n queryID,\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n );\n\n const joinName = `${name}:join(${sq.subquery.alias})`;\n const join = new Join({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: sq.subquery.alias,\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n });\n delegate.addEdge(end, join);\n delegate.addEdge(child, join);\n return delegate.decorateInput(join, joinName);\n}\n\nfunction applyCorrelatedSubqueryCondition(\n input: FilterInput,\n condition: CorrelatedSubqueryCondition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n assert(\n condition.op === 'EXISTS' || condition.op === 'NOT EXISTS',\n 'Expected EXISTS or NOT EXISTS operator',\n );\n if (condition.related.subquery.limit === 0) {\n if (condition.op === 'EXISTS') {\n const filter = new Filter(input, () => false);\n delegate.addEdge(input, filter);\n return filter;\n }\n const filter = new Filter(input, () => true);\n delegate.addEdge(input, filter);\n return filter;\n }\n const existsName = `${name}:exists(${condition.related.subquery.alias})`;\n const exists = new Exists(\n input,\n must(condition.related.subquery.alias),\n condition.related.correlation.parentField,\n condition.op,\n );\n delegate.addEdge(input, exists);\n return delegate.decorateFilterInput(exists, existsName);\n}\n\nfunction gatherCorrelatedSubqueryQueryConditions(\n condition: Condition | undefined,\n) {\n const csqs: CorrelatedSubqueryCondition[] = [];\n const gather = (condition: Condition) => {\n if (condition.type === 'correlatedSubquery') {\n csqs.push(condition);\n return;\n }\n if (condition.type === 'and' || condition.type === 'or') {\n for (const c of condition.conditions) {\n gather(c);\n }\n return;\n }\n };\n if (condition) {\n gather(condition);\n }\n return csqs;\n}\n\nexport function assertOrderingIncludesPK(\n ordering: Ordering,\n pk: PrimaryKey,\n): void {\n // oxlint-disable-next-line unicorn/prefer-set-has -- Array is more appropriate here for small collections\n const orderingFields = ordering.map(([field]) => field);\n const missingFields = pk.filter(pkField => !orderingFields.includes(pkField));\n\n if (missingFields.length > 0) {\n throw new Error(\n `Ordering must include all primary key fields. Missing: ${missingFields.join(\n ', ',\n )}. ZQL automatically appends primary key fields to the ordering if they are missing \n so a common cause of this error is a casing mismatch between Postgres and ZQL.\n E.g., \"userid\" vs \"userID\".\n You may want to add double-quotes around your Postgres column names to prevent Postgres from lower-casing them:\n https://www.postgresql.org/docs/current/sql-syntax-lexical.htm`,\n );\n }\n}\n\nfunction uniquifyCorrelatedSubqueryConditionAliases(ast: AST): AST {\n if (!ast.where) {\n return ast;\n }\n const {where} = ast;\n if (where.type !== 'and' && where.type !== 'or') {\n return ast;\n }\n\n let count = 0;\n const uniquifyCorrelatedSubquery = (csqc: CorrelatedSubqueryCondition) => ({\n ...csqc,\n related: {\n ...csqc.related,\n subquery: {\n ...csqc.related.subquery,\n alias: (csqc.related.subquery.alias ?? '') + '_' + count++,\n },\n },\n });\n\n const uniquify = (cond: Condition): Condition => {\n if (cond.type === 'simple') {\n return cond;\n } else if (cond.type === 'correlatedSubquery') {\n return uniquifyCorrelatedSubquery(cond);\n }\n const conditions = [];\n for (const c of cond.conditions) {\n conditions.push(uniquify(c));\n }\n return {\n type: cond.type,\n conditions,\n };\n };\n\n const result = {\n ...ast,\n where: uniquify(where),\n };\n return result;\n}\n\nexport function conditionIncludesFlippedSubqueryAtAnyLevel(\n cond: Condition,\n): boolean {\n if (cond.type === 'correlatedSubquery') {\n return !!cond.flip;\n }\n if (cond.type === 'and' || cond.type === 'or') {\n return cond.conditions.some(c =>\n conditionIncludesFlippedSubqueryAtAnyLevel(c),\n );\n }\n // simple conditions don't have flips\n return false;\n}\n\nexport function partitionBranches(\n conditions: readonly Condition[],\n predicate: (c: Condition) => boolean,\n) {\n const matched: Condition[] = [];\n const notMatched: Condition[] = [];\n for (const c of conditions) {\n if (predicate(c)) {\n matched.push(c);\n } else {\n notMatched.push(c);\n }\n }\n return [matched, notMatched] as const;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array-view.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/array-view.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,iBAAiB,CAAC;AACzC,OAAO,KAAK,EAAC,QAAQ,EAAc,SAAS,EAAC,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"array-view.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/array-view.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,iBAAiB,CAAC;AACzC,OAAO,KAAK,EAAC,QAAQ,EAAc,SAAS,EAAC,MAAM,wBAAwB,CAAC;AAG5E,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAAa,KAAK,KAAK,EAAE,KAAK,MAAM,EAAC,MAAM,eAAe,CAAC;AAGlE,OAAO,KAAK,EAAQ,MAAM,EAAE,IAAI,EAAC,MAAM,WAAW,CAAC;AA0BnD;;;;;;;;;;GAUG;AACH,qBAAa,SAAS,CAAC,CAAC,SAAS,IAAI,CAAE,YAAW,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;;IAUpE,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;gBAQlC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,EAClD,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI;IA6B/B,IAAI,IAAI,IACmB,CAAC,CAC3B;IAED,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAqBjC,OAAO;IAkBP,IAAI,CAAC,MAAM,EAAE,MAAM;IAYnB,KAAK;IAQL,SAAS,CAAC,GAAG,EAAE,GAAG;CAGnB"}
|
|
@@ -3,6 +3,31 @@ import { emptyArray } from "../../../shared/src/sentinels.js";
|
|
|
3
3
|
import { skipYields } from "./operator.js";
|
|
4
4
|
import { applyChange } from "./view-apply-change.js";
|
|
5
5
|
//#region ../zql/src/ivm/array-view.ts
|
|
6
|
+
function changeToViewChange(change) {
|
|
7
|
+
switch (change[0]) {
|
|
8
|
+
case 0: return {
|
|
9
|
+
type: "add",
|
|
10
|
+
node: change[1]
|
|
11
|
+
};
|
|
12
|
+
case 1: return {
|
|
13
|
+
type: "remove",
|
|
14
|
+
node: change[1]
|
|
15
|
+
};
|
|
16
|
+
case 3: return {
|
|
17
|
+
type: "child",
|
|
18
|
+
node: change[1],
|
|
19
|
+
child: {
|
|
20
|
+
relationshipName: change[2].relationshipName,
|
|
21
|
+
change: changeToViewChange(change[2].change)
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
case 2: return {
|
|
25
|
+
type: "edit",
|
|
26
|
+
node: change[1],
|
|
27
|
+
oldNode: change[2]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
6
31
|
/**
|
|
7
32
|
* Implements a materialized view of the output of an operator.
|
|
8
33
|
*
|
|
@@ -76,7 +101,7 @@ var ArrayView = class {
|
|
|
76
101
|
}
|
|
77
102
|
push(change) {
|
|
78
103
|
this.#dirty = true;
|
|
79
|
-
applyChange(this.#root, change, this.#schema, "", this.#format);
|
|
104
|
+
applyChange(this.#root, changeToViewChange(change), this.#schema, "", this.#format);
|
|
80
105
|
return emptyArray;
|
|
81
106
|
}
|
|
82
107
|
flush() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array-view.js","names":["#input","#listeners","#schema","#format","#root","#updateTTL","#resultType","#error","#fireListeners","#hydrate","#fireListener","#dirty"],"sources":["../../../../../zql/src/ivm/array-view.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Immutable} from '../../../shared/src/immutable.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {ErroredQuery} from '../../../zero-protocol/src/custom-queries.ts';\nimport type {TTL} from '../query/ttl.ts';\nimport type {Listener, ResultType, TypedView} from '../query/typed-view.ts';\nimport type {Change} from './change.ts';\nimport {skipYields, type Input, type Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {applyChange} from './view-apply-change.ts';\nimport type {Entry, Format, View} from './view.ts';\n\n/**\n * Implements a materialized view of the output of an operator.\n *\n * It might seem more efficient to use an immutable b-tree for the\n * materialization, but it's not so clear. Inserts in the middle are\n * asymptotically slower in an array, but can often be done with zero\n * allocations, where changes to the b-tree will often require several allocs.\n *\n * Also the plain array view is more convenient for consumers since you can dump\n * it into console to see what it is, rather than having to iterate it.\n */\nexport class ArrayView<V extends View> implements Output, TypedView<V> {\n readonly #input: Input;\n readonly #listeners = new Set<Listener<V>>();\n readonly #schema: SourceSchema;\n readonly #format: Format;\n\n // Synthetic \"root\" entry that has a single \"\" relationship, so that we can\n // treat all changes, including the root change, generically.\n readonly #root: Entry;\n\n onDestroy: (() => void) | undefined;\n\n #dirty = false;\n #resultType: ResultType = 'unknown';\n #error: ErroredQuery | undefined;\n readonly #updateTTL: (ttl: TTL) => void;\n\n constructor(\n input: Input,\n format: Format,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n this.#input = input;\n this.#schema = input.getSchema();\n this.#format = format;\n this.#updateTTL = updateTTL;\n this.#root = {'': format.singular ? undefined : []};\n input.setOutput(this);\n\n if (queryComplete === true) {\n this.#resultType = 'complete';\n } else if ('error' in queryComplete) {\n this.#resultType = 'error';\n this.#error = queryComplete;\n } else {\n void queryComplete\n .then(() => {\n this.#resultType = 'complete';\n this.#fireListeners();\n })\n .catch(e => {\n this.#resultType = 'error';\n this.#error = e;\n this.#fireListeners();\n });\n }\n this.#hydrate();\n }\n\n get data() {\n return this.#root[''] as V;\n }\n\n addListener(listener: Listener<V>) {\n assert(!this.#listeners.has(listener), 'Listener already registered');\n this.#listeners.add(listener);\n\n this.#fireListener(listener);\n\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n #fireListeners() {\n for (const listener of this.#listeners) {\n this.#fireListener(listener);\n }\n }\n\n #fireListener(listener: Listener<V>) {\n listener(this.data as Immutable<V>, this.#resultType, this.#error);\n }\n\n destroy() {\n this.onDestroy?.();\n }\n\n #hydrate() {\n this.#dirty = true;\n for (const node of skipYields(this.#input.fetch({}))) {\n applyChange(\n this.#root,\n {type: 'add', node},\n this.#schema,\n '',\n this.#format,\n );\n }\n this.flush();\n }\n\n push(change: Change) {\n this.#dirty = true;\n applyChange(this.#root
|
|
1
|
+
{"version":3,"file":"array-view.js","names":["#input","#listeners","#schema","#format","#root","#updateTTL","#resultType","#error","#fireListeners","#hydrate","#fireListener","#dirty"],"sources":["../../../../../zql/src/ivm/array-view.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Immutable} from '../../../shared/src/immutable.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {ErroredQuery} from '../../../zero-protocol/src/custom-queries.ts';\nimport type {TTL} from '../query/ttl.ts';\nimport type {Listener, ResultType, TypedView} from '../query/typed-view.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {skipYields, type Input, type Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {applyChange, type ViewChange} from './view-apply-change.ts';\nimport type {Entry, Format, View} from './view.ts';\n\nfunction changeToViewChange(change: Change): ViewChange {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n return {type: 'add', node: change[ChangeIndex.NODE]};\n case ChangeType.REMOVE:\n return {type: 'remove', node: change[ChangeIndex.NODE]};\n case ChangeType.CHILD:\n return {\n type: 'child',\n node: change[ChangeIndex.NODE],\n child: {\n relationshipName: change[ChangeIndex.CHILD_DATA].relationshipName,\n change: changeToViewChange(change[ChangeIndex.CHILD_DATA].change),\n },\n };\n case ChangeType.EDIT:\n return {\n type: 'edit',\n node: change[ChangeIndex.NODE],\n oldNode: change[ChangeIndex.OLD_NODE],\n };\n }\n}\n\n/**\n * Implements a materialized view of the output of an operator.\n *\n * It might seem more efficient to use an immutable b-tree for the\n * materialization, but it's not so clear. Inserts in the middle are\n * asymptotically slower in an array, but can often be done with zero\n * allocations, where changes to the b-tree will often require several allocs.\n *\n * Also the plain array view is more convenient for consumers since you can dump\n * it into console to see what it is, rather than having to iterate it.\n */\nexport class ArrayView<V extends View> implements Output, TypedView<V> {\n readonly #input: Input;\n readonly #listeners = new Set<Listener<V>>();\n readonly #schema: SourceSchema;\n readonly #format: Format;\n\n // Synthetic \"root\" entry that has a single \"\" relationship, so that we can\n // treat all changes, including the root change, generically.\n readonly #root: Entry;\n\n onDestroy: (() => void) | undefined;\n\n #dirty = false;\n #resultType: ResultType = 'unknown';\n #error: ErroredQuery | undefined;\n readonly #updateTTL: (ttl: TTL) => void;\n\n constructor(\n input: Input,\n format: Format,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n this.#input = input;\n this.#schema = input.getSchema();\n this.#format = format;\n this.#updateTTL = updateTTL;\n this.#root = {'': format.singular ? undefined : []};\n input.setOutput(this);\n\n if (queryComplete === true) {\n this.#resultType = 'complete';\n } else if ('error' in queryComplete) {\n this.#resultType = 'error';\n this.#error = queryComplete;\n } else {\n void queryComplete\n .then(() => {\n this.#resultType = 'complete';\n this.#fireListeners();\n })\n .catch(e => {\n this.#resultType = 'error';\n this.#error = e;\n this.#fireListeners();\n });\n }\n this.#hydrate();\n }\n\n get data() {\n return this.#root[''] as V;\n }\n\n addListener(listener: Listener<V>) {\n assert(!this.#listeners.has(listener), 'Listener already registered');\n this.#listeners.add(listener);\n\n this.#fireListener(listener);\n\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n #fireListeners() {\n for (const listener of this.#listeners) {\n this.#fireListener(listener);\n }\n }\n\n #fireListener(listener: Listener<V>) {\n listener(this.data as Immutable<V>, this.#resultType, this.#error);\n }\n\n destroy() {\n this.onDestroy?.();\n }\n\n #hydrate() {\n this.#dirty = true;\n for (const node of skipYields(this.#input.fetch({}))) {\n applyChange(\n this.#root,\n {type: 'add', node},\n this.#schema,\n '',\n this.#format,\n );\n }\n this.flush();\n }\n\n push(change: Change) {\n this.#dirty = true;\n applyChange(\n this.#root,\n changeToViewChange(change),\n this.#schema,\n '',\n this.#format,\n );\n return emptyArray;\n }\n\n flush() {\n if (!this.#dirty) {\n return;\n }\n this.#dirty = false;\n this.#fireListeners();\n }\n\n updateTTL(ttl: TTL) {\n this.#updateTTL(ttl);\n }\n}\n"],"mappings":";;;;;AAcA,SAAS,mBAAmB,QAA4B;AACtD,SAAQ,OAAO,IAAf;EACE,KAAK,EACH,QAAO;GAAC,MAAM;GAAO,MAAM,OAAO;GAAkB;EACtD,KAAK,EACH,QAAO;GAAC,MAAM;GAAU,MAAM,OAAO;GAAkB;EACzD,KAAK,EACH,QAAO;GACL,MAAM;GACN,MAAM,OAAO;GACb,OAAO;IACL,kBAAkB,OAAO,GAAwB;IACjD,QAAQ,mBAAmB,OAAO,GAAwB,OAAO;IAClE;GACF;EACH,KAAK,EACH,QAAO;GACL,MAAM;GACN,MAAM,OAAO;GACb,SAAS,OAAO;GACjB;;;;;;;;;;;;;;AAeP,IAAa,YAAb,MAAuE;CACrE;CACA,6BAAsB,IAAI,KAAkB;CAC5C;CACA;CAIA;CAEA;CAEA,SAAS;CACT,cAA0B;CAC1B;CACA;CAEA,YACE,OACA,QACA,eACA,WACA;AACA,QAAA,QAAc;AACd,QAAA,SAAe,MAAM,WAAW;AAChC,QAAA,SAAe;AACf,QAAA,YAAkB;AAClB,QAAA,OAAa,EAAC,IAAI,OAAO,WAAW,KAAA,IAAY,EAAE,EAAC;AACnD,QAAM,UAAU,KAAK;AAErB,MAAI,kBAAkB,KACpB,OAAA,aAAmB;WACV,WAAW,eAAe;AACnC,SAAA,aAAmB;AACnB,SAAA,QAAc;QAET,eACF,WAAW;AACV,SAAA,aAAmB;AACnB,SAAA,eAAqB;IACrB,CACD,OAAM,MAAK;AACV,SAAA,aAAmB;AACnB,SAAA,QAAc;AACd,SAAA,eAAqB;IACrB;AAEN,QAAA,SAAe;;CAGjB,IAAI,OAAO;AACT,SAAO,MAAA,KAAW;;CAGpB,YAAY,UAAuB;AACjC,SAAO,CAAC,MAAA,UAAgB,IAAI,SAAS,EAAE,8BAA8B;AACrE,QAAA,UAAgB,IAAI,SAAS;AAE7B,QAAA,aAAmB,SAAS;AAE5B,eAAa;AACX,SAAA,UAAgB,OAAO,SAAS;;;CAIpC,iBAAiB;AACf,OAAK,MAAM,YAAY,MAAA,UACrB,OAAA,aAAmB,SAAS;;CAIhC,cAAc,UAAuB;AACnC,WAAS,KAAK,MAAsB,MAAA,YAAkB,MAAA,MAAY;;CAGpE,UAAU;AACR,OAAK,aAAa;;CAGpB,WAAW;AACT,QAAA,QAAc;AACd,OAAK,MAAM,QAAQ,WAAW,MAAA,MAAY,MAAM,EAAE,CAAC,CAAC,CAClD,aACE,MAAA,MACA;GAAC,MAAM;GAAO;GAAK,EACnB,MAAA,QACA,IACA,MAAA,OACD;AAEH,OAAK,OAAO;;CAGd,KAAK,QAAgB;AACnB,QAAA,QAAc;AACd,cACE,MAAA,MACA,mBAAmB,OAAO,EAC1B,MAAA,QACA,IACA,MAAA,OACD;AACD,SAAO;;CAGT,QAAQ;AACN,MAAI,CAAC,MAAA,MACH;AAEF,QAAA,QAAc;AACd,QAAA,eAAqB;;CAGvB,UAAU,KAAU;AAClB,QAAA,UAAgB,IAAI"}
|