@rocicorp/zero 1.12.0-canary.0 → 1.12.0-canary.10
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/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
- package/out/analyze-query/src/analyze-cli.d.ts.map +1 -1
- package/out/analyze-query/src/analyze-cli.js +4 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js +4 -3
- package/out/analyze-query/src/bin-transform.js.map +1 -1
- package/out/analyze-query/src/zql-chain.d.ts +23 -0
- package/out/analyze-query/src/zql-chain.d.ts.map +1 -0
- package/out/analyze-query/src/zql-chain.js +162 -0
- package/out/analyze-query/src/zql-chain.js.map +1 -0
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/ast-to-zql/src/bin.js +4 -3
- package/out/ast-to-zql/src/bin.js.map +1 -1
- package/out/ast-to-zql/src/format.js.map +1 -1
- package/out/datadog/src/datadog-log-sink.js +5 -4
- package/out/datadog/src/datadog-log-sink.js.map +1 -1
- package/out/otel/src/log-options.js.map +1 -1
- package/out/replicache/src/btree/node.d.ts +0 -2
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js +14 -12
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/btree/read.js +4 -2
- package/out/replicache/src/btree/read.js.map +1 -1
- package/out/replicache/src/btree/splice.js.map +1 -1
- package/out/replicache/src/btree/write.js +9 -5
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/replicache/src/config.js +1 -2
- package/out/replicache/src/connection-loop.d.ts.map +1 -1
- package/out/replicache/src/connection-loop.js +1 -1
- package/out/replicache/src/connection-loop.js.map +1 -1
- package/out/replicache/src/dag/chunk.js +1 -1
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/lazy-store.js +8 -7
- package/out/replicache/src/dag/lazy-store.js.map +1 -1
- package/out/replicache/src/dag/store-impl.js +1 -1
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/dag/visitor.js.map +1 -1
- package/out/replicache/src/db/commit.d.ts +0 -1
- package/out/replicache/src/db/commit.d.ts.map +1 -1
- package/out/replicache/src/db/commit.js +5 -3
- package/out/replicache/src/db/commit.js.map +1 -1
- package/out/replicache/src/db/read.js +1 -1
- package/out/replicache/src/db/read.js.map +1 -1
- package/out/replicache/src/db/rebase.js.map +1 -1
- package/out/replicache/src/db/write.d.ts +0 -2
- package/out/replicache/src/db/write.d.ts.map +1 -1
- package/out/replicache/src/db/write.js +5 -2
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/deleted-clients.d.ts +0 -2
- package/out/replicache/src/deleted-clients.d.ts.map +1 -1
- package/out/replicache/src/deleted-clients.js +5 -3
- package/out/replicache/src/deleted-clients.js.map +1 -1
- package/out/replicache/src/expo-sqlite.js +1 -2
- package/out/replicache/src/frozen-json.d.ts +0 -1
- package/out/replicache/src/frozen-json.d.ts.map +1 -1
- package/out/replicache/src/frozen-json.js +1 -1
- package/out/replicache/src/frozen-json.js.map +1 -1
- package/out/replicache/src/get-default-puller.js +1 -1
- package/out/replicache/src/hash.js +1 -1
- package/out/replicache/src/hash.js.map +1 -1
- package/out/replicache/src/impl.js +1 -2
- package/out/replicache/src/index-defs.d.ts +0 -2
- package/out/replicache/src/index-defs.d.ts.map +1 -1
- package/out/replicache/src/index-defs.js +5 -3
- package/out/replicache/src/index-defs.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts +0 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store.js +2 -2
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-fallback-store.js +1 -1
- package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
- package/out/replicache/src/kv/mem-store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
- package/out/replicache/src/kv/read-impl.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +8 -4
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/throw-if-closed.d.ts +0 -3
- package/out/replicache/src/kv/throw-if-closed.d.ts.map +1 -1
- package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
- package/out/replicache/src/kv/write-impl-base.js +4 -1
- package/out/replicache/src/kv/write-impl-base.js.map +1 -1
- package/out/replicache/src/kv/write-impl.js.map +1 -1
- package/out/replicache/src/log-options.js +2 -1
- package/out/replicache/src/log-options.js.map +1 -1
- package/out/replicache/src/make-idb-name.js +1 -1
- package/out/replicache/src/new-client-channel.js +9 -7
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/on-persist-channel.js +1 -1
- package/out/replicache/src/on-persist-channel.js.map +1 -1
- package/out/replicache/src/op-sqlite.js +1 -2
- package/out/replicache/src/pending-mutations.js +2 -1
- package/out/replicache/src/pending-mutations.js.map +1 -1
- package/out/replicache/src/persist/client-gc.js +2 -2
- package/out/replicache/src/persist/client-gc.js.map +1 -1
- package/out/replicache/src/persist/client-group-gc.d.ts +0 -7
- package/out/replicache/src/persist/client-group-gc.d.ts.map +1 -1
- package/out/replicache/src/persist/client-group-gc.js +1 -1
- package/out/replicache/src/persist/client-group-gc.js.map +1 -1
- package/out/replicache/src/persist/client-groups.js +1 -1
- package/out/replicache/src/persist/client-groups.js.map +1 -1
- package/out/replicache/src/persist/clients.js +1 -1
- package/out/replicache/src/persist/clients.js.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +5 -4
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
- package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
- package/out/replicache/src/persist/heartbeat.js +2 -2
- package/out/replicache/src/persist/heartbeat.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store.js +1 -1
- package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
- package/out/replicache/src/persist/make-client-id.js +1 -1
- package/out/replicache/src/persist/make-client-id.js.map +1 -1
- package/out/replicache/src/persist/persist.js +3 -2
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js +8 -5
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/process-scheduler.js +1 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +2 -7
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +21 -18
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/scan-iterator.js.map +1 -1
- package/out/replicache/src/scan-options.js +7 -6
- package/out/replicache/src/scan-options.js.map +1 -1
- package/out/replicache/src/sqlite.js +1 -2
- package/out/replicache/src/subscriptions.js +1 -1
- package/out/replicache/src/subscriptions.js.map +1 -1
- package/out/replicache/src/sync/diff.js +3 -1
- package/out/replicache/src/sync/diff.js.map +1 -1
- package/out/replicache/src/sync/ids.js.map +1 -1
- package/out/replicache/src/sync/patch.d.ts +0 -5
- package/out/replicache/src/sync/patch.d.ts.map +1 -1
- package/out/replicache/src/sync/patch.js +2 -4
- package/out/replicache/src/sync/patch.js.map +1 -1
- package/out/replicache/src/sync/pull.d.ts +0 -1
- package/out/replicache/src/sync/pull.d.ts.map +1 -1
- package/out/replicache/src/sync/pull.js +3 -1
- package/out/replicache/src/sync/pull.js.map +1 -1
- package/out/replicache/src/sync/push.d.ts +2 -1
- package/out/replicache/src/sync/push.d.ts.map +1 -1
- package/out/replicache/src/sync/push.js.map +1 -1
- package/out/replicache/src/sync/request-id.js +1 -1
- package/out/replicache/src/sync/request-id.js.map +1 -1
- package/out/replicache/src/transactions.js +1 -1
- package/out/replicache/src/transactions.js.map +1 -1
- package/out/replicache/src/types.d.ts +2 -1
- package/out/replicache/src/types.d.ts.map +1 -1
- package/out/shared/src/arrays.js +4 -1
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/asserts.d.ts +0 -5
- package/out/shared/src/asserts.d.ts.map +1 -1
- package/out/shared/src/asserts.js.map +1 -1
- package/out/shared/src/bigint-json.d.ts +9 -0
- package/out/shared/src/bigint-json.d.ts.map +1 -1
- package/out/shared/src/bigint-json.js +27 -2
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/broadcast-channel.js +1 -1
- package/out/shared/src/browser-env.d.ts +0 -17
- package/out/shared/src/browser-env.d.ts.map +1 -1
- package/out/shared/src/browser-env.js.map +1 -1
- package/out/shared/src/btree-set.js +11 -8
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/shared/src/cache.js.map +1 -1
- package/out/shared/src/config.js +1 -1
- package/out/shared/src/custom-key-map.js.map +1 -1
- package/out/shared/src/custom-key-set.js.map +1 -1
- package/out/shared/src/deep-clone.d.ts +0 -1
- package/out/shared/src/deep-clone.d.ts.map +1 -1
- package/out/shared/src/deep-clone.js.map +1 -1
- package/out/shared/src/document-visible.js +1 -1
- package/out/shared/src/document-visible.js.map +1 -1
- package/out/shared/src/dotenv.js.map +1 -1
- package/out/shared/src/error.js +1 -1
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/expand.d.ts +0 -3
- package/out/shared/src/expand.d.ts.map +1 -1
- package/out/shared/src/hash.js +2 -2
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/immutable.d.ts +1 -1
- package/out/shared/src/immutable.d.ts.map +1 -1
- package/out/shared/src/iterables.d.ts +0 -1
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/json-schema.js.map +1 -1
- package/out/shared/src/json.js.map +1 -1
- package/out/shared/src/logging-test-utils.js.map +1 -1
- package/out/shared/src/logging.js +1 -1
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/navigator.js +1 -1
- package/out/shared/src/options-types.d.ts +1 -1
- package/out/shared/src/options-types.d.ts.map +1 -1
- package/out/shared/src/options.d.ts +2 -3
- package/out/shared/src/options.d.ts.map +1 -1
- package/out/shared/src/options.js +8 -11
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/promise-race.js +1 -1
- package/out/shared/src/promise-race.js.map +1 -1
- package/out/shared/src/queue.js +1 -1
- package/out/shared/src/queue.js.map +1 -1
- package/out/shared/src/record-proxy.js.map +1 -1
- package/out/shared/src/resolved-promises.d.ts +0 -4
- package/out/shared/src/resolved-promises.d.ts.map +1 -1
- package/out/shared/src/resolved-promises.js +0 -1
- package/out/shared/src/resolved-promises.js.map +1 -1
- package/out/shared/src/resolver.d.ts +9 -0
- package/out/shared/src/resolver.d.ts.map +1 -0
- package/out/shared/src/resolver.js +6 -0
- package/out/shared/src/resolver.js.map +1 -0
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/shared/src/size-of-value.js +7 -10
- package/out/shared/src/size-of-value.js.map +1 -1
- package/out/shared/src/sleep.js +1 -1
- package/out/shared/src/sleep.js.map +1 -1
- package/out/shared/src/sorted-entries.js.map +1 -1
- package/out/shared/src/tdigest-schema.js.map +1 -1
- package/out/shared/src/tdigest.d.ts +0 -1
- package/out/shared/src/tdigest.d.ts.map +1 -1
- package/out/shared/src/tdigest.js.map +1 -1
- package/out/shared/src/valita.js +1 -1
- package/out/shared/src/valita.js.map +1 -1
- package/out/z2s/src/compiler.js +5 -7
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/z2s/src/sql.d.ts +1 -5
- package/out/z2s/src/sql.d.ts.map +1 -1
- package/out/z2s/src/sql.js +1 -1
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +210 -189
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/adapters/drizzle.js +1 -2
- package/out/zero/src/adapters/kysely.js +1 -2
- package/out/zero/src/adapters/pg.js +1 -2
- package/out/zero/src/adapters/postgresjs.js +1 -2
- package/out/zero/src/adapters/prisma.js +1 -2
- package/out/zero/src/analyze.js +1 -2
- package/out/zero/src/change-protocol/v0.js +1 -2
- package/out/zero/src/expo-sqlite.js +1 -2
- package/out/zero/src/op-sqlite.js +1 -2
- package/out/zero/src/react-native.js.map +1 -1
- package/out/zero/src/sqlite.js +1 -2
- package/out/zero/src/zero-cache-dev.js +11 -10
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero/src/zero-out.js +2 -1
- package/out/zero/src/zero-out.js.map +1 -1
- package/out/zero-cache/src/auth/auth.js +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/jwt.js +4 -1
- package/out/zero-cache/src/auth/jwt.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js +1 -1
- package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
- package/out/zero-cache/src/auth/read-authorizer.js +3 -2
- package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +15 -19
- package/out/zero-cache/src/auth/write-authorizer.js.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/server-context.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +19 -11
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +47 -10
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +1 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/create.js.map +1 -1
- package/out/zero-cache/src/db/index-predicate.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.d.ts +2 -2
- package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +1 -1
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.js +5 -3
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
- package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy.js +0 -1
- package/out/zero-cache/src/db/pg-copy.js.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
- package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
- package/out/zero-cache/src/db/run-transaction.js.map +1 -1
- package/out/zero-cache/src/db/specs.d.ts +3 -4
- package/out/zero-cache/src/db/specs.d.ts.map +1 -1
- package/out/zero-cache/src/db/specs.js +1 -1
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
- package/out/zero-cache/src/db/statements.d.ts.map +1 -1
- package/out/zero-cache/src/db/statements.js +9 -2
- package/out/zero-cache/src/db/statements.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js +6 -4
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/db/warmup.js.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/observability/log-throttle.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/scripts/decommission.js +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +5 -3
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/scripts/permissions.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +49 -16
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/logging.js +1 -1
- package/out/zero-cache/src/server/logging.js.map +1 -1
- package/out/zero-cache/src/server/main.js +2 -2
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
- package/out/zero-cache/src/server/otel-start.js.map +1 -1
- package/out/zero-cache/src/server/reaper.js +2 -2
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.js +1 -1
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +4 -3
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/runner/runtime.js +2 -1
- package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts +9 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js +29 -2
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
- package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
- package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
- package/out/zero-cache/src/server/syncer.js +10 -5
- 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/server/worker-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/analyze.js +2 -2
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +3 -3
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +4 -4
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +4 -4
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js +52 -27
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +70 -15
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/fork-request.js +54 -0
- package/out/zero-cache/src/services/change-source/pg/fork-request.js.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +9 -7
- 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/binary-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +5 -4
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.d.ts +0 -2
- package/out/zero-cache/src/services/change-source/pg/lsn.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +9 -3
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +0 -12
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +6 -4
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +10 -7
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts +0 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +3 -0
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.js +2 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
- package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +6 -6
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +56 -21
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js +2 -2
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js +2 -3
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +3 -3
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +8 -4
- 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.js +85 -59
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js +1 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +81 -12
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +4 -4
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-poller.js +2 -2
- package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/server-start-metrics.js +42 -0
- package/out/zero-cache/src/services/change-streamer/server-start-metrics.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -2
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +2 -2
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +2 -2
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +4 -6
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +3 -5
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +4 -4
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +8 -7
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
- package/out/zero-cache/src/services/heapz.js +2 -1
- package/out/zero-cache/src/services/heapz.js.map +1 -1
- package/out/zero-cache/src/services/http-service.d.ts +2 -2
- package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/http-service.js +6 -4
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +3 -3
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +4 -4
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
- package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
- package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +3 -4
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +61 -17
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.d.ts +4 -12
- package/out/zero-cache/src/services/plannerz.d.ts.map +1 -1
- package/out/zero-cache/src/services/plannerz.js +6 -7
- package/out/zero-cache/src/services/plannerz.js.map +1 -1
- package/out/zero-cache/src/services/profz.js +5 -3
- package/out/zero-cache/src/services/profz.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-db.js +4 -3
- package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/drain-handler.js +1 -1
- package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +4 -4
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
- package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +2 -2
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts +2 -11
- package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +2 -2
- package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +19 -17
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.js +31 -15
- package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts +0 -7
- package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
- package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js +2 -2
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +3 -3
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +1 -1
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/runner.js.map +1 -1
- package/out/zero-cache/src/services/running-state.js +1 -1
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
- package/out/zero-cache/src/services/statz.d.ts.map +1 -1
- package/out/zero-cache/src/services/statz.js +5 -4
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +71 -24
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.js +46 -6
- package/out/zero-cache/src/services/view-syncer/client-schema.js.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 +2 -2
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +12 -7
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +12 -4
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +2 -2
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +6 -4
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +13 -10
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js.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/schema/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +2 -16
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +12 -8
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +52 -28
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/error-with-level.js.map +1 -1
- package/out/zero-cache/src/types/lexi-version.js.map +1 -1
- package/out/zero-cache/src/types/lite.d.ts +0 -1
- package/out/zero-cache/src/types/lite.d.ts.map +1 -1
- package/out/zero-cache/src/types/lite.js +1 -1
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/pg-data-type.js +1 -1
- package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
- package/out/zero-cache/src/types/pg-types.d.ts +0 -40
- package/out/zero-cache/src/types/pg-types.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg-types.js.map +1 -1
- package/out/zero-cache/src/types/pg.js +6 -4
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.d.ts +0 -1
- package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.js +1 -1
- package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
- package/out/zero-cache/src/types/processes.js +1 -1
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/profiler.js +1 -1
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/types/row-key.d.ts +0 -5
- 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/sql.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js +11 -10
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/strings.js.map +1 -1
- package/out/zero-cache/src/types/subscription.js +1 -1
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/timeout.js +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts +4 -7
- package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js +16 -2
- package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
- package/out/zero-cache/src/types/ws.d.ts +2 -1
- package/out/zero-cache/src/types/ws.d.ts.map +1 -1
- package/out/zero-cache/src/types/ws.js.map +1 -1
- package/out/zero-cache/src/workers/connection.d.ts +1 -2
- package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connection.js +5 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/mutator.js +1 -1
- package/out/zero-cache/src/workers/mutator.js.map +1 -1
- package/out/zero-cache/src/workers/rehome.d.ts +15 -0
- package/out/zero-cache/src/workers/rehome.d.ts.map +1 -0
- package/out/zero-cache/src/workers/rehome.js +36 -0
- package/out/zero-cache/src/workers/rehome.js.map +1 -0
- package/out/zero-cache/src/workers/replicator.js +5 -4
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +9 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +30 -5
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.js +2 -2
- package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/bindings.js +11 -11
- package/out/zero-client/src/client/connection-manager.js +1 -1
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection.js +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.js +1 -1
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/crud.d.ts +2 -8
- package/out/zero-client/src/client/crud.d.ts.map +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/custom.d.ts +0 -2
- package/out/zero-client/src/client/custom.d.ts.map +1 -1
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/enable-analytics.d.ts +0 -1
- package/out/zero-client/src/client/enable-analytics.d.ts.map +1 -1
- package/out/zero-client/src/client/enable-analytics.js +1 -0
- package/out/zero-client/src/client/enable-analytics.js.map +1 -1
- package/out/zero-client/src/client/error.d.ts +4 -5
- package/out/zero-client/src/client/error.d.ts.map +1 -1
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
- package/out/zero-client/src/client/inspector/client.js.map +1 -1
- package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
- package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/query.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js +1 -3
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/keys.d.ts +0 -1
- package/out/zero-client/src/client/keys.d.ts.map +1 -1
- package/out/zero-client/src/client/keys.js +6 -3
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/log-options.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- package/out/zero-client/src/client/metrics.js +3 -5
- package/out/zero-client/src/client/metrics.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js +1 -1
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.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/query-manager.js +2 -2
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/reload-error-handler.js +4 -3
- package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
- package/out/zero-client/src/client/replicache-types.d.ts +3 -2
- package/out/zero-client/src/client/replicache-types.d.ts.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.d.ts +1 -2
- package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-client/src/client/zero-rep.js.map +1 -1
- package/out/zero-client/src/client/zero.d.ts +1 -5
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +20 -12
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.js +23 -23
- package/out/zero-client/src/types/client-state.d.ts +0 -6
- package/out/zero-client/src/types/client-state.d.ts.map +1 -1
- package/out/zero-client/src/util/nanoid.js.map +1 -1
- package/out/zero-permissions/src/permissions.d.ts +1 -12
- package/out/zero-permissions/src/permissions.d.ts.map +1 -1
- package/out/zero-permissions/src/permissions.js +3 -3
- package/out/zero-permissions/src/permissions.js.map +1 -1
- package/out/zero-pg/src/mod.js +9 -9
- package/out/zero-protocol/src/analyze-query-result.js +1 -1
- package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
- package/out/zero-protocol/src/application-error.js.map +1 -1
- package/out/zero-protocol/src/ast.d.ts +0 -4
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js +21 -11
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.d.ts +0 -20
- package/out/zero-protocol/src/change-desired-queries.d.ts.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
- package/out/zero-protocol/src/client-schema.d.ts +2 -5
- package/out/zero-protocol/src/client-schema.d.ts.map +1 -1
- package/out/zero-protocol/src/client-schema.js.map +1 -1
- package/out/zero-protocol/src/close-connection.d.ts +0 -8
- package/out/zero-protocol/src/close-connection.d.ts.map +1 -1
- package/out/zero-protocol/src/close-connection.js.map +1 -1
- package/out/zero-protocol/src/connect.d.ts +0 -12
- package/out/zero-protocol/src/connect.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.d.ts +3 -17
- package/out/zero-protocol/src/custom-queries.d.ts.map +1 -1
- package/out/zero-protocol/src/custom-queries.js.map +1 -1
- package/out/zero-protocol/src/data.d.ts +0 -1
- package/out/zero-protocol/src/data.d.ts.map +1 -1
- package/out/zero-protocol/src/data.js +3 -1
- package/out/zero-protocol/src/data.js.map +1 -1
- package/out/zero-protocol/src/delete-clients.js.map +1 -1
- package/out/zero-protocol/src/down.d.ts +2 -2
- package/out/zero-protocol/src/down.js.map +1 -1
- package/out/zero-protocol/src/error.js.map +1 -1
- package/out/zero-protocol/src/inspect-down.d.ts +82 -84
- package/out/zero-protocol/src/inspect-down.d.ts.map +1 -1
- package/out/zero-protocol/src/inspect-down.js.map +1 -1
- package/out/zero-protocol/src/inspect-up.d.ts +0 -28
- package/out/zero-protocol/src/inspect-up.d.ts.map +1 -1
- package/out/zero-protocol/src/inspect-up.js.map +1 -1
- package/out/zero-protocol/src/mutate-server.d.ts +0 -21
- package/out/zero-protocol/src/mutate-server.d.ts.map +1 -1
- package/out/zero-protocol/src/mutate-server.js.map +1 -1
- package/out/zero-protocol/src/mutation-id.js.map +1 -1
- package/out/zero-protocol/src/mutation-type-enum.d.ts +0 -1
- package/out/zero-protocol/src/mutation-type-enum.d.ts.map +1 -1
- package/out/zero-protocol/src/mutation-type-enum.js.map +1 -1
- package/out/zero-protocol/src/mutation.d.ts +2 -4
- package/out/zero-protocol/src/mutation.d.ts.map +1 -1
- package/out/zero-protocol/src/mutation.js.map +1 -1
- package/out/zero-protocol/src/mutations-patch.d.ts +0 -33
- package/out/zero-protocol/src/mutations-patch.d.ts.map +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/ping.d.ts +0 -2
- package/out/zero-protocol/src/ping.d.ts.map +1 -1
- package/out/zero-protocol/src/ping.js.map +1 -1
- package/out/zero-protocol/src/poke.d.ts +3 -2
- package/out/zero-protocol/src/poke.d.ts.map +1 -1
- package/out/zero-protocol/src/poke.js +1 -1
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/pong.d.ts +0 -2
- package/out/zero-protocol/src/pong.d.ts.map +1 -1
- package/out/zero-protocol/src/pong.js.map +1 -1
- package/out/zero-protocol/src/primary-key.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.js +1 -0
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/pull.d.ts +2 -7
- package/out/zero-protocol/src/pull.d.ts.map +1 -1
- package/out/zero-protocol/src/pull.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +1 -3
- package/out/zero-protocol/src/push.d.ts.map +1 -1
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/queries-patch.d.ts +0 -16
- package/out/zero-protocol/src/queries-patch.d.ts.map +1 -1
- package/out/zero-protocol/src/queries-patch.js +1 -1
- package/out/zero-protocol/src/queries-patch.js.map +1 -1
- package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
- package/out/zero-protocol/src/query-hash.js +2 -2
- package/out/zero-protocol/src/query-server.d.ts +2 -41
- package/out/zero-protocol/src/query-server.d.ts.map +1 -1
- package/out/zero-protocol/src/query-server.js.map +1 -1
- package/out/zero-protocol/src/row-patch.js.map +1 -1
- package/out/zero-protocol/src/up.d.ts +60 -60
- package/out/zero-protocol/src/up.js +1 -1
- package/out/zero-protocol/src/up.js.map +1 -1
- package/out/zero-protocol/src/update-auth.js.map +1 -1
- package/out/zero-protocol/src/version.d.ts +0 -1
- package/out/zero-protocol/src/version.d.ts.map +1 -1
- package/out/zero-protocol/src/version.js.map +1 -1
- package/out/zero-react/src/bindings.js +11 -11
- package/out/zero-react/src/mod.js +5 -5
- package/out/zero-react/src/use-query.js +2 -2
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/zero-provider.d.ts +1 -1
- package/out/zero-react/src/zero-provider.d.ts.map +1 -1
- package/out/zero-react/src/zero-provider.js +2 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-react/src/zero.js +23 -23
- package/out/zero-schema/src/builder/relationship-builder.d.ts +0 -2
- package/out/zero-schema/src/builder/relationship-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
- package/out/zero-schema/src/builder/schema-builder.js +3 -2
- package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
- package/out/zero-schema/src/builder/table-builder.d.ts +1 -1
- package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/table-builder.js +1 -1
- package/out/zero-schema/src/builder/table-builder.js.map +1 -1
- package/out/zero-schema/src/compiled-permissions.js.map +1 -1
- package/out/zero-schema/src/name-mapper.js +7 -5
- package/out/zero-schema/src/name-mapper.js.map +1 -1
- package/out/zero-schema/src/schema-config.d.ts +0 -1
- package/out/zero-schema/src/schema-config.d.ts.map +1 -1
- package/out/zero-schema/src/schema-config.js +1 -1
- package/out/zero-schema/src/schema-config.js.map +1 -1
- package/out/zero-schema/src/table-schema.d.ts +3 -11
- package/out/zero-schema/src/table-schema.d.ts.map +1 -1
- package/out/zero-server/src/adapters/drizzle.js.map +1 -1
- package/out/zero-server/src/adapters/kysely.js.map +1 -1
- package/out/zero-server/src/adapters/pg.js.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-server/src/adapters/prisma.js.map +1 -1
- package/out/zero-server/src/closable-db-transaction.js.map +1 -1
- package/out/zero-server/src/custom.js +1 -1
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/mod.js +8 -8
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.d.ts +2 -4
- package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +2 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-server/src/schema.js +7 -7
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/bindings.js +11 -11
- package/out/zero-solid/src/mod.js +5 -5
- package/out/zero-solid/src/solid-view.d.ts +0 -2
- package/out/zero-solid/src/solid-view.d.ts.map +1 -1
- package/out/zero-solid/src/solid-view.js +1 -1
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-connection-state.js +2 -1
- package/out/zero-solid/src/use-connection-state.js.map +1 -1
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-zero-online.js +2 -1
- package/out/zero-solid/src/use-zero-online.js.map +1 -1
- package/out/zero-solid/src/use-zero.js +3 -2
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zero-solid/src/zero.js +23 -23
- package/out/zero-types/src/name-mapper.d.ts +2 -1
- package/out/zero-types/src/name-mapper.d.ts.map +1 -1
- package/out/zero-types/src/name-mapper.js.map +1 -1
- package/out/zero-types/src/schema-value.d.ts +3 -2
- package/out/zero-types/src/schema-value.d.ts.map +1 -1
- package/out/zero-types/src/schema.d.ts +1 -1
- package/out/zero-types/src/schema.d.ts.map +1 -1
- package/out/zql/src/builder/builder.d.ts +1 -11
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +15 -13
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
- package/out/zql/src/builder/debug-delegate.js.map +1 -1
- package/out/zql/src/builder/filter.d.ts +0 -1
- package/out/zql/src/builder/filter.d.ts.map +1 -1
- package/out/zql/src/builder/filter.js.map +1 -1
- package/out/zql/src/builder/like.js +0 -1
- package/out/zql/src/builder/like.js.map +1 -1
- package/out/zql/src/error.d.ts +0 -5
- package/out/zql/src/error.d.ts.map +1 -1
- package/out/zql/src/error.js.map +1 -1
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +1 -1
- package/out/zql/src/ivm/cap.d.ts.map +1 -1
- package/out/zql/src/ivm/cap.js +12 -4
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/constraint.d.ts +0 -7
- package/out/zql/src/ivm/constraint.d.ts.map +1 -1
- package/out/zql/src/ivm/constraint.js +1 -1
- package/out/zql/src/ivm/constraint.js.map +1 -1
- package/out/zql/src/ivm/data.d.ts +15 -2
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js +15 -1
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/deferred-input.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +32 -22
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +5 -0
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +1 -1
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter-push.js.map +1 -1
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +1 -3
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-index-tap.js.map +1 -1
- package/out/zql/src/ivm/join-index.js.map +1 -1
- package/out/zql/src/ivm/join-utils.js +11 -13
- 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 +6 -3
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +1 -2
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +27 -23
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/memory-storage.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +6 -0
- package/out/zql/src/ivm/operator.d.ts.map +1 -1
- package/out/zql/src/ivm/operator.js +2 -2
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js +0 -1
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/skip-yields.js.map +1 -1
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/stream.d.ts +0 -1
- package/out/zql/src/ivm/stream.d.ts.map +1 -1
- package/out/zql/src/ivm/stream.js.map +1 -1
- package/out/zql/src/ivm/take-gate.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +7 -8
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts +0 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts +5 -35
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/crud.d.ts +1 -3
- package/out/zql/src/mutate/crud.d.ts.map +1 -1
- package/out/zql/src/mutate/crud.js +2 -1
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/mutate/mutator.d.ts +1 -1
- package/out/zql/src/mutate/mutator.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator.js.map +1 -1
- package/out/zql/src/planner/planner-builder.js +1 -1
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-debug.d.ts +10 -9
- 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-fan-in.js.map +1 -1
- package/out/zql/src/planner/planner-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-filter.js.map +1 -1
- package/out/zql/src/planner/planner-graph.js +1 -5
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.js +1 -1
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-node.d.ts +0 -1
- package/out/zql/src/planner/planner-node.d.ts.map +1 -1
- package/out/zql/src/planner/planner-node.js.map +1 -1
- package/out/zql/src/planner/planner-source.js.map +1 -1
- package/out/zql/src/planner/planner-terminus.js.map +1 -1
- package/out/zql/src/planner/planner-warnings.d.ts +3 -3
- package/out/zql/src/planner/planner-warnings.d.ts.map +1 -1
- package/out/zql/src/planner/planner-warnings.js.map +1 -1
- package/out/zql/src/query/create-builder.js +2 -1
- package/out/zql/src/query/create-builder.js.map +1 -1
- package/out/zql/src/query/expression.d.ts +0 -22
- package/out/zql/src/query/expression.d.ts.map +1 -1
- package/out/zql/src/query/expression.js +1 -1
- package/out/zql/src/query/expression.js.map +1 -1
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/out/zql/src/query/named.js +2 -1
- package/out/zql/src/query/named.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts +0 -7
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +1 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-delegate.d.ts +1 -4
- package/out/zql/src/query/query-delegate.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +3 -2
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-internals.d.ts +0 -3
- package/out/zql/src/query/query-internals.d.ts.map +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts +5 -5
- package/out/zql/src/query/query-registry.d.ts.map +1 -1
- package/out/zql/src/query/query-registry.js +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zql/src/query/query-transitions.js +1 -1
- package/out/zql/src/query/query-transitions.js.map +1 -1
- package/out/zql/src/query/query.d.ts +2 -13
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.d.ts +1 -19
- package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +2 -1
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/static-query.js.map +1 -1
- package/out/zql/src/query/ttl.d.ts +2 -3
- package/out/zql/src/query/ttl.d.ts.map +1 -1
- package/out/zql/src/query/ttl.js +8 -8
- package/out/zql/src/query/ttl.js.map +1 -1
- package/out/zql/src/query/validate-input.js.map +1 -1
- package/out/zqlite/src/database-storage.js +195 -26
- package/out/zqlite/src/database-storage.js.map +1 -1
- package/out/zqlite/src/db.d.ts +47 -1
- package/out/zqlite/src/db.d.ts.map +1 -1
- package/out/zqlite/src/db.js +121 -21
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/explain-queries.js.map +1 -1
- package/out/zqlite/src/fetch-sql.d.ts +27 -0
- package/out/zqlite/src/fetch-sql.d.ts.map +1 -0
- package/out/zqlite/src/fetch-sql.js +112 -0
- package/out/zqlite/src/fetch-sql.js.map +1 -0
- package/out/zqlite/src/index-usage.js.map +1 -1
- package/out/zqlite/src/internal/sql-inline.js.map +1 -1
- package/out/zqlite/src/internal/sql.js.map +1 -1
- package/out/zqlite/src/internal/statement-cache.d.ts +7 -0
- package/out/zqlite/src/internal/statement-cache.d.ts.map +1 -1
- package/out/zqlite/src/internal/statement-cache.js +11 -4
- package/out/zqlite/src/internal/statement-cache.js.map +1 -1
- package/out/zqlite/src/mod.js +5 -5
- package/out/zqlite/src/pending-delta.js.map +1 -1
- package/out/zqlite/src/query-builder.d.ts +11 -3
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js +22 -7
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +3 -2
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts +20 -3
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +74 -40
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +39 -39
- package/out/analyze-query/src/bin-analyze.d.ts +0 -2
- package/out/analyze-query/src/bin-analyze.d.ts.map +0 -1
- package/out/analyze-query/src/bin-analyze.js +0 -283
- package/out/analyze-query/src/bin-analyze.js.map +0 -1
- package/out/otel/src/maybe-time.d.ts +0 -3
- package/out/otel/src/maybe-time.d.ts.map +0 -1
- package/out/otel/src/maybe-time.js +0 -15
- package/out/otel/src/maybe-time.js.map +0 -1
- package/out/shared/src/cache.d.ts +0 -22
- package/out/shared/src/cache.d.ts.map +0 -1
- package/out/shared/src/custom-key-map.d.ts +0 -26
- package/out/shared/src/custom-key-map.d.ts.map +0 -1
- package/out/shared/src/custom-key-set.d.ts +0 -25
- package/out/shared/src/custom-key-set.d.ts.map +0 -1
- package/out/shared/src/logging-test-utils.d.ts +0 -12
- package/out/shared/src/logging-test-utils.d.ts.map +0 -1
- package/out/shared/src/sorted-entries.d.ts +0 -2
- package/out/shared/src/sorted-entries.d.ts.map +0 -1
- package/out/zero/src/analyze-query.d.ts +0 -3
- package/out/zero/src/analyze-query.d.ts.map +0 -1
- package/out/zero/src/analyze-query.js +0 -3
- package/out/zero/src/server/change-streamer.d.ts +0 -2
- package/out/zero/src/server/change-streamer.d.ts.map +0 -1
- package/out/zero/src/server/main.d.ts +0 -2
- package/out/zero/src/server/main.d.ts.map +0 -1
- package/out/zero/src/server/reaper.d.ts +0 -2
- package/out/zero/src/server/reaper.d.ts.map +0 -1
- package/out/zero/src/server/replicator.d.ts +0 -2
- package/out/zero/src/server/replicator.d.ts.map +0 -1
- package/out/zero/src/server/syncer.d.ts +0 -2
- package/out/zero/src/server/syncer.d.ts.map +0 -1
- package/out/zero-cache/src/auth/jwt.d.ts +0 -12
- package/out/zero-cache/src/auth/jwt.d.ts.map +0 -1
- package/out/zero-cache/src/auth/load-permissions.d.ts +0 -17
- package/out/zero-cache/src/auth/load-permissions.d.ts.map +0 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts +0 -28
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +0 -1
- package/out/zero-cache/src/config/server-context.d.ts +0 -16
- package/out/zero-cache/src/config/server-context.d.ts.map +0 -1
- package/out/zero-cache/src/custom/fetch.d.ts +0 -38
- package/out/zero-cache/src/custom/fetch.d.ts.map +0 -1
- package/out/zero-cache/src/custom/metrics.d.ts +0 -31
- package/out/zero-cache/src/custom/metrics.d.ts.map +0 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts +0 -68
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +0 -1
- package/out/zero-cache/src/db/create.d.ts +0 -12
- package/out/zero-cache/src/db/create.d.ts.map +0 -1
- package/out/zero-cache/src/db/delete-lite-db.d.ts +0 -2
- package/out/zero-cache/src/db/delete-lite-db.d.ts.map +0 -1
- package/out/zero-cache/src/db/index-predicate.d.ts +0 -4
- package/out/zero-cache/src/db/index-predicate.d.ts.map +0 -1
- package/out/zero-cache/src/db/migration-lite.d.ts +0 -78
- package/out/zero-cache/src/db/migration-lite.d.ts.map +0 -1
- package/out/zero-cache/src/db/migration.d.ts +0 -80
- package/out/zero-cache/src/db/migration.d.ts.map +0 -1
- package/out/zero-cache/src/db/pg-copy-binary.d.ts +0 -101
- package/out/zero-cache/src/db/pg-copy-binary.d.ts.map +0 -1
- package/out/zero-cache/src/db/pg-copy.d.ts +0 -30
- package/out/zero-cache/src/db/pg-copy.d.ts.map +0 -1
- package/out/zero-cache/src/db/pg-type-parser.d.ts +0 -15
- package/out/zero-cache/src/db/pg-type-parser.d.ts.map +0 -1
- package/out/zero-cache/src/db/transaction-pool.d.ts +0 -205
- package/out/zero-cache/src/db/transaction-pool.d.ts.map +0 -1
- package/out/zero-cache/src/db/warmup.d.ts +0 -4
- package/out/zero-cache/src/db/warmup.d.ts.map +0 -1
- package/out/zero-cache/src/observability/events.d.ts +0 -15
- package/out/zero-cache/src/observability/events.d.ts.map +0 -1
- package/out/zero-cache/src/observability/log-throttle.d.ts +0 -44
- package/out/zero-cache/src/observability/log-throttle.d.ts.map +0 -1
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts +0 -21
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +0 -1
- package/out/zero-cache/src/server/change-streamer.d.ts +0 -3
- package/out/zero-cache/src/server/change-streamer.d.ts.map +0 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts +0 -71
- package/out/zero-cache/src/server/inspector-delegate.d.ts.map +0 -1
- package/out/zero-cache/src/server/main.d.ts +0 -3
- package/out/zero-cache/src/server/main.d.ts.map +0 -1
- package/out/zero-cache/src/server/otel-diag-logger.d.ts +0 -16
- package/out/zero-cache/src/server/otel-diag-logger.d.ts.map +0 -1
- package/out/zero-cache/src/server/otel-start.d.ts +0 -3
- package/out/zero-cache/src/server/otel-start.d.ts.map +0 -1
- package/out/zero-cache/src/server/priority-op.d.ts +0 -8
- package/out/zero-cache/src/server/priority-op.d.ts.map +0 -1
- package/out/zero-cache/src/server/reaper.d.ts +0 -3
- package/out/zero-cache/src/server/reaper.d.ts.map +0 -1
- package/out/zero-cache/src/server/replicator.d.ts +0 -10
- package/out/zero-cache/src/server/replicator.d.ts.map +0 -1
- package/out/zero-cache/src/server/slot-keeper.d.ts +0 -26
- package/out/zero-cache/src/server/slot-keeper.d.ts.map +0 -1
- package/out/zero-cache/src/server/syncer.d.ts +0 -3
- package/out/zero-cache/src/server/syncer.d.ts.map +0 -1
- package/out/zero-cache/src/services/analyze.d.ts +0 -9
- package/out/zero-cache/src/services/analyze.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/change-source.d.ts +0 -32
- package/out/zero-cache/src/services/change-source/change-source.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts +0 -57
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts +0 -55
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts +0 -24
- package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +0 -115
- package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +0 -14
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +0 -25
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-metadata.d.ts +0 -14
- package/out/zero-cache/src/services/change-source/pg/backfill-metadata.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +0 -36
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +0 -65
- package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +0 -115
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +0 -155
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts +0 -17
- package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.d.ts +0 -28
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput.types.d.ts +0 -76
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput.types.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +0 -169
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +0 -16
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.d.ts +0 -15
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +0 -127
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +0 -9
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +0 -68
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +0 -72
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +0 -50
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +0 -14
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +0 -18
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +0 -98
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +0 -34
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts +0 -34
- package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +0 -158
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts +0 -19
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +0 -78
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +0 -139
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +0 -586
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts +0 -24
- package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts +0 -15
- package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +0 -49
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts +0 -53
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts +0 -88
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +0 -15
- package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.d.ts +0 -243
- package/out/zero-cache/src/services/change-streamer/restore-admission.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.d.ts +0 -6
- package/out/zero-cache/src/services/change-streamer/schema/init.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +0 -152
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts +0 -11
- package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +0 -92
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +0 -36
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +0 -22
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +0 -99
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts +0 -83
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts +0 -121
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts +0 -73
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +0 -62
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +0 -114
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +0 -276
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +0 -485
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +0 -194
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +0 -52
- package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +0 -1
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.d.ts +0 -57
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts +0 -77
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +0 -78
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/metrics.d.ts +0 -31
- package/out/zero-cache/src/services/litestream/metrics.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/restore-progress.d.ts +0 -24
- package/out/zero-cache/src/services/litestream/restore-progress.d.ts.map +0 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +0 -40
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +0 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +0 -63
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/change-log-db.d.ts +0 -368
- package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +0 -56
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts +0 -37
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/drain-handler.d.ts +0 -82
- package/out/zero-cache/src/services/replicator/drain-handler.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +0 -25
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/notifier.d.ts +0 -9
- package/out/zero-cache/src/services/replicator/notifier.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/replication-status.d.ts +0 -71
- package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +0 -86
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +0 -55
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +0 -132
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts +0 -120
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts.map +0 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +0 -111
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +0 -1
- package/out/zero-cache/src/services/run-ast.d.ts +0 -39
- package/out/zero-cache/src/services/run-ast.d.ts.map +0 -1
- package/out/zero-cache/src/services/runner.d.ts +0 -19
- package/out/zero-cache/src/services/runner.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.d.ts +0 -17
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts +0 -26
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/batch-pacer.d.ts +0 -79
- package/out/zero-cache/src/services/view-syncer/batch-pacer.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +0 -79
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.d.ts +0 -5
- package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +0 -184
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts +0 -22
- package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +0 -128
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +0 -294
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +0 -81
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +0 -45
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts +0 -53
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +0 -33
- package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +0 -51
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +0 -34
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +0 -296
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +0 -7
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/push-cost.d.ts +0 -132
- package/out/zero-cache/src/services/view-syncer/push-cost.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts +0 -55
- package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts +0 -28
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +0 -25
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +0 -89
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts +0 -90
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts +0 -17
- package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts +0 -86
- package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.d.ts +0 -5
- package/out/zero-cache/src/services/view-syncer/schema/init.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +0 -563
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +0 -95
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +0 -412
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/tracer.d.ts +0 -2
- package/out/zero-cache/src/services/view-syncer/tracer.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/ttl-clock.d.ts +0 -10
- package/out/zero-cache/src/services/view-syncer/ttl-clock.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +0 -133
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +0 -1
- package/out/zero-cache/src/types/pg-patch.d.ts +0 -21
- package/out/zero-cache/src/types/pg-patch.d.ts.map +0 -1
- package/out/zero-cache/src/types/pg-versions.d.ts +0 -3
- package/out/zero-cache/src/types/pg-versions.d.ts.map +0 -1
- package/out/zero-cache/src/workers/replicator.d.ts +0 -57
- package/out/zero-cache/src/workers/replicator.d.ts.map +0 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts +0 -14
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +0 -1
- package/out/zero-cache/src/workers/syncer.d.ts +0 -67
- package/out/zero-cache/src/workers/syncer.d.ts.map +0 -1
- package/out/zero-events/src/index.d.ts +0 -27
- package/out/zero-events/src/index.d.ts.map +0 -1
- package/out/zero-events/src/json.d.ts +0 -20
- package/out/zero-events/src/json.d.ts.map +0 -1
- package/out/zero-events/src/status.d.ts +0 -144
- package/out/zero-events/src/status.d.ts.map +0 -1
- package/out/zqlite/src/database-storage.d.ts +0 -24
- package/out/zqlite/src/database-storage.d.ts.map +0 -1
- package/out/zqlite/src/explain-queries.d.ts +0 -4
- package/out/zqlite/src/explain-queries.d.ts.map +0 -1
- package/out/zqlite/src/internal/sql-inline.d.ts +0 -13
- package/out/zqlite/src/internal/sql-inline.d.ts.map +0 -1
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts +0 -72
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +0 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts +0 -17
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +0 -1
- package/out/zqlite/src/sqlite-stat-fanout.d.ts +0 -121
- package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +0 -1
|
@@ -13,10 +13,10 @@ import "../../../../zero-protocol/src/push.js";
|
|
|
13
13
|
import "../../config/zero-config.js";
|
|
14
14
|
import { mutateResponseSchema } from "../../../../zero-protocol/src/mutate-server.js";
|
|
15
15
|
import { getOrCreateCounter } from "../../observability/metrics.js";
|
|
16
|
+
import { ProtocolErrorWithLevel } from "../../types/error-with-level.js";
|
|
16
17
|
import { Queue } from "../../../../shared/src/queue.js";
|
|
17
18
|
import { Subscription } from "../../types/subscription.js";
|
|
18
19
|
import { recordMutation } from "../../server/anonymous-otel-start.js";
|
|
19
|
-
import { ProtocolErrorWithLevel } from "../../types/error-with-level.js";
|
|
20
20
|
import { fetchFromAPIServer } from "../../custom/fetch.js";
|
|
21
21
|
import { authEquals, isAuthErrorBody } from "../../auth/auth.js";
|
|
22
22
|
import { ROOT_CONTEXT, context, propagation } from "@opentelemetry/api";
|
|
@@ -167,6 +167,18 @@ var PushWorker = class {
|
|
|
167
167
|
#lc;
|
|
168
168
|
#config;
|
|
169
169
|
#clients;
|
|
170
|
+
/**
|
|
171
|
+
* For each client, the wsID of its socket whose push most recently failed.
|
|
172
|
+
* Pushes queued behind the failed one on that socket are not sent: they
|
|
173
|
+
* start past the failed mutation, so the API server could only reject them
|
|
174
|
+
* as out of order. The socket is failed (or was already replaced), and the
|
|
175
|
+
* client re-sends every unacknowledged mutation on its next socket.
|
|
176
|
+
*
|
|
177
|
+
* An entry is kept until the socket can no longer queue pushes (it closed or
|
|
178
|
+
* the client reconnected) and the queue has been drained since, so it is not
|
|
179
|
+
* removed while pushes from the failed socket may still be queued.
|
|
180
|
+
*/
|
|
181
|
+
#failedSockets = /* @__PURE__ */ new Map();
|
|
170
182
|
#customMutations = getOrCreateCounter("mutation", "custom", "Number of custom mutations processed");
|
|
171
183
|
#pushes = getOrCreateCounter("mutation", "pushes", "Number of pushes processed by the pusher");
|
|
172
184
|
constructor(config, lc, connContextManager, queue) {
|
|
@@ -201,28 +213,47 @@ var PushWorker = class {
|
|
|
201
213
|
}
|
|
202
214
|
async run() {
|
|
203
215
|
for (;;) {
|
|
216
|
+
const closedSockets = [...this.#failedSockets].filter(([clientID, wsID]) => !this.#connContextManager.getConnectionContext({
|
|
217
|
+
clientID,
|
|
218
|
+
wsID
|
|
219
|
+
}));
|
|
204
220
|
const [pushes, terminate] = combinePushes([await this.#queue.dequeue(), ...this.#queue.drain()]);
|
|
205
221
|
for (const push of pushes) {
|
|
222
|
+
const { clientID, wsID } = push.connCtx;
|
|
223
|
+
if (this.#failedSockets.get(clientID) === wsID) {
|
|
224
|
+
this.#lc.debug?.("Not sending push queued behind a failed push on the same socket; the client re-sends it on its next socket", {
|
|
225
|
+
clientID,
|
|
226
|
+
wsID,
|
|
227
|
+
mutations: push.push.mutations.length
|
|
228
|
+
});
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
206
231
|
const parentContext = push.push.traceparent ? propagation.extract(ROOT_CONTEXT, { traceparent: push.push.traceparent }) : context.active();
|
|
207
232
|
const response = await context.with(parentContext, () => this.#processPush(push));
|
|
208
|
-
await this.#fanOutResponses(response);
|
|
233
|
+
await this.#fanOutResponses(push.connCtx, response);
|
|
209
234
|
}
|
|
235
|
+
for (const [clientID, wsID] of closedSockets) if (this.#failedSockets.get(clientID) === wsID) this.#failedSockets.delete(clientID);
|
|
210
236
|
if (terminate) break;
|
|
211
237
|
}
|
|
212
238
|
}
|
|
213
239
|
/**
|
|
214
|
-
* 1. If the entire `push` fails, we send the error to
|
|
240
|
+
* 1. If the entire `push` fails, we send the error to `origin`, the
|
|
241
|
+
* connection the push was sent on.
|
|
215
242
|
* 2. If the push succeeds, we look for any mutation failure that should cause the connection to terminate
|
|
216
|
-
* and terminate
|
|
243
|
+
* and terminate `origin`.
|
|
244
|
+
*
|
|
245
|
+
* Only `origin` is failed, even when the push carried other clients'
|
|
246
|
+
* mutations (the client group's commit chain includes other tabs' pending
|
|
247
|
+
* mutations). A client tracks what it has sent per socket, so the gap a
|
|
248
|
+
* failed push leaves is on `origin` alone. Other clients send their own
|
|
249
|
+
* mutations on their own sockets, and see any lasting error there.
|
|
217
250
|
*/
|
|
218
|
-
#fanOutResponses(response) {
|
|
251
|
+
#fanOutResponses(origin, response) {
|
|
219
252
|
const connectionTerminations = [];
|
|
220
253
|
if ("kind" in response && response.kind === "PushFailed" || "error" in response) {
|
|
221
254
|
this.#lc.warn?.("The server behind ZERO_MUTATE_URL returned a push error.", response);
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
const client = this.#clients.get(clientID);
|
|
225
|
-
if (!client) continue;
|
|
255
|
+
const mutationIDs = response.mutationIDs ?? [];
|
|
256
|
+
if (mutationIDs.length > 0) {
|
|
226
257
|
if ("error" in response) {
|
|
227
258
|
const pushFailedBody = response.error === "http" ? {
|
|
228
259
|
kind: PushFailed,
|
|
@@ -245,14 +276,12 @@ var PushWorker = class {
|
|
|
245
276
|
mutationIDs,
|
|
246
277
|
message: response.error === "zeroPusher" ? response.details : response.error === "unsupportedSchemaVersion" ? "Unsupported schema version" : "An unknown error occurred while pushing to the API server"
|
|
247
278
|
};
|
|
248
|
-
this.#
|
|
249
|
-
} else this.#
|
|
279
|
+
this.#failOrigin(origin, pushFailedBody);
|
|
280
|
+
} else this.#failOrigin(origin, response);
|
|
250
281
|
}
|
|
251
282
|
} else {
|
|
252
283
|
const groupedMutations = groupBy(response.mutations, (m) => m.id.clientID);
|
|
253
|
-
for (const
|
|
254
|
-
const client = this.#clients.get(clientID);
|
|
255
|
-
if (!client) continue;
|
|
284
|
+
for (const mutations of groupedMutations.values()) {
|
|
256
285
|
let failure;
|
|
257
286
|
let i = 0;
|
|
258
287
|
for (; i < mutations.length; i++) {
|
|
@@ -274,11 +303,25 @@ var PushWorker = class {
|
|
|
274
303
|
}
|
|
275
304
|
}
|
|
276
305
|
if (failure && i < mutations.length - 1) this.#lc.warn?.("push-response contains mutations after a mutation which should fatal the connection");
|
|
277
|
-
if (failure) connectionTerminations.push(() => this.#
|
|
306
|
+
if (failure) connectionTerminations.push(() => this.#failOrigin(origin, failure));
|
|
278
307
|
}
|
|
279
308
|
}
|
|
280
309
|
connectionTerminations.forEach((cb) => cb());
|
|
281
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Fails `origin` after a push sent on it failed, and records it in
|
|
313
|
+
* `#failedSockets` so the pushes it queued behind that one are not sent.
|
|
314
|
+
*
|
|
315
|
+
* If the client has reconnected since sending the push, the old socket is
|
|
316
|
+
* already gone and the client re-sends its mutations on the new one. Failing
|
|
317
|
+
* the new socket would put the client in its error state, rejecting its
|
|
318
|
+
* outstanding mutations, for a failure it has already recovered from.
|
|
319
|
+
*/
|
|
320
|
+
#failOrigin(origin, errorBody) {
|
|
321
|
+
this.#failedSockets.set(origin.clientID, origin.wsID);
|
|
322
|
+
const client = this.#clients.get(origin.clientID);
|
|
323
|
+
if (client?.wsID === origin.wsID) this.#failDownstream(client.downstream, errorBody);
|
|
324
|
+
}
|
|
282
325
|
async #processPush(entry) {
|
|
283
326
|
this.#customMutations.add(entry.push.mutations.length, { clientGroupID: entry.push.clientGroupID });
|
|
284
327
|
this.#pushes.add(1, { clientGroupID: entry.push.clientGroupID });
|
|
@@ -381,7 +424,8 @@ function combinePushes(entries) {
|
|
|
381
424
|
}
|
|
382
425
|
for (const entry of entries) {
|
|
383
426
|
if (entry === "stop" || entry === void 0) return [collect(), true];
|
|
384
|
-
|
|
427
|
+
const key = `${entry.connCtx.clientID}:${entry.connCtx.wsID}:${entry.connCtx.revision}`;
|
|
428
|
+
getOrInsertComputed(pushesByConnection, key, newArray).push(entry);
|
|
385
429
|
}
|
|
386
430
|
return [collect(), false];
|
|
387
431
|
}
|
|
@@ -398,6 +442,6 @@ function assertAreCompatiblePushes(left, right) {
|
|
|
398
442
|
assert(left.connCtx.mutateContext.url === right.connCtx.mutateContext.url, "userPushURL must be the same for all pushes with the same clientID");
|
|
399
443
|
}
|
|
400
444
|
//#endregion
|
|
401
|
-
export { PusherService };
|
|
445
|
+
export { PusherService, combinePushes };
|
|
402
446
|
|
|
403
447
|
//# sourceMappingURL=pusher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pusher.js","names":["#connContextManager","#pusher","#queue","#config","#lc","#isStopped","#refCount","#stopped","#clients","#customMutations","#pushes","#processPush","#fanOutResponses","#failDownstream"],"sources":["../../../../../../zero-cache/src/services/mutagen/pusher.ts"],"sourcesContent":["import {context, propagation, ROOT_CONTEXT} from '@opentelemetry/api';\nimport type {LogContext} from '@rocicorp/logger';\nimport {groupBy, newArray} from '../../../../shared/src/arrays.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {getErrorMessage} from '../../../../shared/src/error.ts';\nimport {getOrInsertComputed} from '../../../../shared/src/map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {Queue} from '../../../../shared/src/queue.ts';\nimport type {Downstream} from '../../../../zero-protocol/src/down.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../../../zero-protocol/src/error-reason.ts';\nimport {\n isProtocolError,\n type PushFailedBody,\n} from '../../../../zero-protocol/src/error.ts';\nimport {\n mutateResponseSchema,\n type MutateResponse,\n} from '../../../../zero-protocol/src/mutate-server.ts';\nimport type {MutationID} from '../../../../zero-protocol/src/mutation-id.ts';\nimport * as MutationType from '../../../../zero-protocol/src/mutation-type-enum.ts';\nimport {CLEANUP_RESULTS_MUTATION_NAME} from '../../../../zero-protocol/src/mutation.ts';\nimport {type PushBody} from '../../../../zero-protocol/src/push.ts';\nimport {authEquals, isAuthErrorBody} from '../../auth/auth.ts';\nimport {type ZeroConfig} from '../../config/zero-config.ts';\nimport {fetchFromAPIServer} from '../../custom/fetch.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport {recordMutation} from '../../server/anonymous-otel-start.ts';\nimport {ProtocolErrorWithLevel} from '../../types/error-with-level.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {HandlerResult, StreamResult} from '../../workers/connection.ts';\nimport type {RefCountedService, Service} from '../service.ts';\nimport type {\n ConnectionContext,\n ConnectionContextManager,\n ConnectionSelector,\n} from '../view-syncer/connection-context-manager.ts';\n\nexport interface Pusher extends RefCountedService {\n initConnection(selector: ConnectionSelector): Source<Downstream>;\n enqueuePush(selector: ConnectionSelector, push: PushBody): HandlerResult;\n ackMutationResponses(\n requester: ConnectionSelector,\n upToID: MutationID,\n ): Promise<void>;\n deleteClientMutations(\n requester: ConnectionSelector,\n clientIDs: string[],\n ): Promise<void>;\n}\n\ntype Config = Pick<ZeroConfig, 'app' | 'shard'>;\n\n/**\n * Receives push messages from zero-client and forwards\n * them the the user's API server.\n *\n * If the user's API server is taking too long to process\n * the push, the PusherService will add the push to a queue\n * and send pushes in bulk the next time the user's API server\n * is available.\n *\n * - One PusherService exists per client group.\n * - Mutations for a given client are always sent in-order\n * - Mutations for different clients in the same group may be interleaved\n */\nexport class PusherService implements Service, Pusher {\n readonly id: string;\n readonly #connContextManager: ConnectionContextManager;\n readonly #pusher: PushWorker;\n readonly #queue: Queue<PusherEntryOrStop>;\n readonly #config: Config;\n readonly #lc: LogContext;\n #stopped: Promise<void> | undefined;\n #refCount = 0;\n #isStopped = false;\n\n constructor(\n appConfig: Config,\n lc: LogContext,\n clientGroupID: string,\n connContextManager: ConnectionContextManager,\n ) {\n this.#connContextManager = connContextManager;\n this.#config = appConfig;\n this.#lc = lc.withContext('component', 'pusherService');\n this.#queue = new Queue();\n this.#pusher = new PushWorker(\n appConfig,\n lc,\n this.#connContextManager,\n this.#queue,\n );\n this.id = clientGroupID;\n }\n\n initConnection(selector: ConnectionSelector): Source<Downstream> {\n return this.#pusher.initConnection(selector);\n }\n\n enqueuePush(\n selector: ConnectionSelector,\n push: PushBody,\n ): Exclude<HandlerResult, StreamResult> {\n const connCtx = this.#connContextManager.getConnectionContext(selector);\n if (connCtx) {\n this.#pusher.enqueuePush(connCtx, push);\n }\n\n return {\n type: 'ok',\n };\n }\n\n async ackMutationResponses(\n requester: ConnectionSelector,\n upToID: MutationID,\n ): Promise<void> {\n const connCtx = this.#connContextManager.getConnectionContext(requester);\n if (!connCtx?.mutateContext?.url) {\n // No push URL configured, skip cleanup\n return;\n }\n\n const cleanupBody: PushBody = {\n clientGroupID: this.id,\n mutations: [\n {\n type: MutationType.Custom,\n id: 0, // Not tracked - this is fire-and-forget\n clientID: upToID.clientID,\n name: CLEANUP_RESULTS_MUTATION_NAME,\n args: [\n {\n type: 'single',\n clientGroupID: this.id,\n clientID: upToID.clientID,\n upToMutationID: upToID.id,\n },\n ],\n timestamp: Date.now(),\n },\n ],\n pushVersion: 1,\n timestamp: Date.now(),\n requestID: `cleanup-${this.id}-${upToID.clientID}-${upToID.id}`,\n };\n\n try {\n await fetchFromAPIServer(\n mutateResponseSchema,\n 'push',\n this.#lc,\n connCtx,\n {appID: this.#config.app.id, shardNum: this.#config.shard.num},\n cleanupBody,\n {operation: 'cleanup', cleanupType: 'single'},\n );\n } catch (e) {\n this.#lc.warn?.('Failed to send cleanup mutation', {\n error: getErrorMessage(e),\n });\n }\n }\n\n /**\n * Bulk cleanup is routed through the requester's push context.\n *\n * This assumes the client group shares a compatible push endpoint/auth\n * context.\n */\n async deleteClientMutations(\n requester: ConnectionSelector,\n clientIDs: string[],\n ): Promise<void> {\n if (clientIDs.length === 0) {\n return;\n }\n\n const connCtx = this.#connContextManager.getConnectionContext(requester);\n if (!connCtx?.mutateContext?.url) {\n // No push URL configured, skip cleanup\n return;\n }\n\n const cleanupBody: PushBody = {\n clientGroupID: this.id,\n mutations: [\n {\n type: MutationType.Custom,\n id: 0, // Not tracked - this is fire-and-forget\n clientID: clientIDs[0], // Use first client as sender\n name: CLEANUP_RESULTS_MUTATION_NAME,\n args: [\n {\n type: 'bulk',\n clientGroupID: this.id,\n clientIDs,\n },\n ],\n timestamp: Date.now(),\n },\n ],\n pushVersion: 1,\n timestamp: Date.now(),\n requestID: `cleanup-bulk-${this.id}-${Date.now()}`,\n };\n\n try {\n await fetchFromAPIServer(\n mutateResponseSchema,\n 'push',\n this.#lc,\n connCtx,\n {appID: this.#config.app.id, shardNum: this.#config.shard.num},\n cleanupBody,\n {operation: 'cleanup', cleanupType: 'bulk'},\n );\n } catch (e) {\n this.#lc.warn?.('Failed to send bulk cleanup mutation', {\n error: getErrorMessage(e),\n });\n }\n }\n\n ref() {\n assert(!this.#isStopped, 'PusherService is already stopped');\n ++this.#refCount;\n }\n\n unref() {\n assert(!this.#isStopped, 'PusherService is already stopped');\n --this.#refCount;\n if (this.#refCount <= 0) {\n void this.stop();\n }\n }\n\n hasRefs(): boolean {\n return this.#refCount > 0;\n }\n\n run(): Promise<void> {\n this.#stopped = this.#pusher.run();\n return this.#stopped;\n }\n\n stop(): Promise<void> {\n if (this.#isStopped) {\n return must(this.#stopped, 'Stop was called before `run`');\n }\n this.#isStopped = true;\n this.#queue.enqueue('stop');\n return must(this.#stopped, 'Stop was called before `run`');\n }\n}\n\ntype PusherEntry = {\n push: PushBody;\n connCtx: ConnectionContext;\n};\ntype PusherEntryOrStop = PusherEntry | 'stop';\n\n/**\n * Awaits items in the queue then drains and sends them all\n * to the user's API server.\n */\nclass PushWorker {\n readonly #connContextManager: ConnectionContextManager;\n readonly #queue: Queue<PusherEntryOrStop>;\n readonly #lc: LogContext;\n readonly #config: Config;\n readonly #clients: Map<\n string,\n {wsID: string; downstream: Subscription<Downstream>}\n >;\n\n readonly #customMutations = getOrCreateCounter(\n 'mutation',\n 'custom',\n 'Number of custom mutations processed',\n );\n readonly #pushes = getOrCreateCounter(\n 'mutation',\n 'pushes',\n 'Number of pushes processed by the pusher',\n );\n\n constructor(\n config: Config,\n lc: LogContext,\n connContextManager: ConnectionContextManager,\n queue: Queue<PusherEntryOrStop>,\n ) {\n this.#lc = lc.withContext('component', 'pusher');\n this.#connContextManager = connContextManager;\n this.#queue = queue;\n this.#config = config;\n this.#clients = new Map();\n }\n\n /**\n * Returns a new downstream stream if the clientID,wsID pair has not been seen before.\n * If a clientID already exists with a different wsID, that client's downstream is cancelled.\n */\n initConnection(selector: ConnectionSelector): Subscription<Downstream> {\n const existing = this.#clients.get(selector.clientID);\n if (existing && existing.wsID === selector.wsID) {\n // already initialized for this socket\n throw new Error('Connection was already initialized');\n }\n\n // client is back on a new connection\n if (existing) {\n existing.downstream.cancel();\n }\n\n const downstream = Subscription.create<Downstream>({\n cleanup: () => {\n this.#clients.delete(selector.clientID);\n },\n });\n this.#clients.set(selector.clientID, {\n wsID: selector.wsID,\n downstream,\n });\n return downstream;\n }\n\n enqueuePush(connCtx: ConnectionContext, push: PushBody) {\n this.#queue.enqueue({\n push,\n connCtx,\n });\n }\n\n async run() {\n for (;;) {\n const task = await this.#queue.dequeue();\n const rest = this.#queue.drain();\n const [pushes, terminate] = combinePushes([task, ...rest]);\n for (const push of pushes) {\n const parentContext = push.push.traceparent\n ? propagation.extract(ROOT_CONTEXT, {\n traceparent: push.push.traceparent,\n })\n : context.active();\n const response = await context.with(parentContext, () =>\n this.#processPush(push),\n );\n await this.#fanOutResponses(response);\n }\n\n if (terminate) {\n break;\n }\n }\n }\n\n /**\n * 1. If the entire `push` fails, we send the error to relevant clients.\n * 2. If the push succeeds, we look for any mutation failure that should cause the connection to terminate\n * and terminate the connection for those clients.\n */\n #fanOutResponses(response: MutateResponse) {\n const connectionTerminations: (() => void)[] = [];\n\n // if the entire push failed, send that to the client.\n if (\n ('kind' in response && response.kind === ErrorKind.PushFailed) ||\n 'error' in response\n ) {\n this.#lc.warn?.(\n 'The server behind ZERO_MUTATE_URL returned a push error.',\n response,\n );\n // TODO(0xcadams): Fanout is keyed only by clientID here. If a response arrives\n // after reconnect or re-auth, `#clients.get(clientID)` may point at a\n // newer wsID/revision and fail the replacement downstream instead.\n const groupedMutationIDs = groupBy(\n response.mutationIDs ?? [],\n m => m.clientID,\n );\n for (const [clientID, mutationIDs] of groupedMutationIDs) {\n const client = this.#clients.get(clientID);\n if (!client) {\n continue;\n }\n\n // We do not resolve mutations on the client if the push fails\n // as those mutations will be retried.\n if ('error' in response) {\n // This error code path will eventually be removed when we\n // no longer support the legacy push error format.\n const pushFailedBody: PushFailedBody =\n response.error === 'http'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n status: response.status,\n bodyPreview: response.details,\n mutationIDs,\n message: `Fetch from API server returned non-OK status ${response.status}`,\n }\n : response.error === 'unsupportedPushVersion'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.UnsupportedPushVersion,\n mutationIDs,\n message: `Unsupported push version`,\n }\n : {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Internal,\n mutationIDs,\n message:\n response.error === 'zeroPusher'\n ? response.details\n : response.error === 'unsupportedSchemaVersion'\n ? 'Unsupported schema version'\n : 'An unknown error occurred while pushing to the API server',\n };\n\n this.#failDownstream(client.downstream, pushFailedBody);\n } else {\n this.#failDownstream(client.downstream, response);\n }\n }\n } else {\n // Look for mutations results that should cause us to terminate the connection\n // TODO(0xcadams): Same stale-routing issue as above: fatal mutation results are\n // still mapped to the current downstream by clientID only.\n const groupedMutations = groupBy(response.mutations, m => m.id.clientID);\n for (const [clientID, mutations] of groupedMutations) {\n const client = this.#clients.get(clientID);\n if (!client) {\n continue;\n }\n\n let failure: PushFailedBody | undefined;\n let i = 0;\n for (; i < mutations.length; i++) {\n const m = mutations[i];\n if ('error' in m.result) {\n this.#lc.warn?.(\n 'The server behind ZERO_MUTATE_URL returned a mutation error.',\n m.result,\n );\n }\n // This error code path will eventually be removed,\n // keeping this for backwards compatibility, but the server\n // should now return a PushFailedBody with the mutationIDs\n if ('error' in m.result && m.result.error === 'oooMutation') {\n failure = {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.OutOfOrderMutation,\n message: 'mutation was out of order',\n details: m.result.details,\n mutationIDs: mutations.map(m => ({\n clientID: m.id.clientID,\n id: m.id.id,\n })),\n };\n break;\n }\n }\n\n if (failure && i < mutations.length - 1) {\n this.#lc.warn?.(\n 'push-response contains mutations after a mutation which should fatal the connection',\n );\n }\n\n if (failure) {\n connectionTerminations.push(() =>\n this.#failDownstream(client.downstream, failure),\n );\n }\n }\n }\n\n connectionTerminations.forEach(cb => cb());\n }\n\n async #processPush(entry: PusherEntry): Promise<MutateResponse> {\n this.#customMutations.add(entry.push.mutations.length, {\n clientGroupID: entry.push.clientGroupID,\n });\n this.#pushes.add(1, {\n clientGroupID: entry.push.clientGroupID,\n });\n\n // Record custom mutations for telemetry\n recordMutation('custom', entry.push.mutations.length);\n\n const url = must(\n entry.connCtx.mutateContext.url,\n 'ZERO_MUTATE_URL is not set',\n );\n\n this.#lc.debug?.(\n 'pushing to',\n url,\n 'with',\n entry.push.mutations.length,\n 'mutations',\n );\n\n let mutationIDs: MutationID[] = [];\n\n try {\n mutationIDs = entry.push.mutations.map(m => ({\n id: m.id,\n clientID: m.clientID,\n }));\n\n const response = await fetchFromAPIServer(\n mutateResponseSchema,\n 'push',\n this.#lc,\n entry.connCtx,\n {\n appID: this.#config.app.id,\n shardNum: this.#config.shard.num,\n },\n entry.push,\n {operation: 'mutate'},\n );\n if (\n ('kind' in response && response.kind === ErrorKind.PushFailed) ||\n 'error' in response\n ) {\n if (isAuthErrorBody(response)) {\n this.#lc.warn?.('Push auth failed; invalidating connection', {\n clientID: entry.connCtx.clientID,\n response: 'kind' in response ? response.message : undefined,\n });\n this.#connContextManager.failConnection(\n entry.connCtx,\n entry.connCtx.revision,\n );\n }\n return response;\n }\n // A successful push also validates this connection's current auth snapshot.\n // That lets later shared work reuse it without trusting stale credentials.\n this.#connContextManager.validateConnection(\n entry.connCtx,\n entry.connCtx.revision,\n 'kind' in response &&\n response.kind === 'MutateResponse' &&\n response?.userID !== undefined\n ? {kind: 'server-validated', validatedUserID: response.userID}\n : {kind: 'client-fallback'},\n );\n return response;\n } catch (e) {\n if (isProtocolError(e) && e.errorBody.kind === ErrorKind.PushFailed) {\n const response = {\n ...e.errorBody,\n mutationIDs,\n } as const satisfies PushFailedBody;\n if (isAuthErrorBody(response)) {\n this.#lc.warn?.('Push auth failed; invalidating connection', {\n clientID: entry.connCtx.clientID,\n response: 'kind' in response ? response.message : undefined,\n });\n this.#connContextManager.failConnection(\n entry.connCtx,\n entry.connCtx.revision,\n );\n }\n return response;\n }\n\n if (isProtocolError(e) && isAuthErrorBody(e.errorBody)) {\n // The push completed far enough for local validation to reject the\n // connection, so invalidate it and surface the result as PushFailed.\n this.#lc.warn?.('Push validation failed; invalidating connection', {\n clientID: entry.connCtx.clientID,\n response: e.message,\n });\n this.#connContextManager.failConnection(\n entry.connCtx,\n entry.connCtx.revision,\n );\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n message: e.message,\n status: 401,\n mutationIDs,\n } as const satisfies PushFailedBody;\n }\n\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `Failed to push: ${getErrorMessage(e)}`,\n mutationIDs,\n } as const satisfies PushFailedBody;\n }\n }\n\n #failDownstream(\n downstream: Subscription<Downstream>,\n errorBody: PushFailedBody,\n ): void {\n downstream.fail(new ProtocolErrorWithLevel(errorBody, 'warn'));\n }\n}\n\n/**\n * Pushes for different clients, sockets, or auth revisions could be interleaved.\n *\n * In order to batch safely, we only combine pushes from the same\n * clientID/wsID/revision snapshot.\n */\nexport function combinePushes(\n entries: readonly (PusherEntryOrStop | undefined)[],\n): [PusherEntry[], boolean] {\n const pushesByConnection = new Map<string, PusherEntry[]>();\n\n function collect() {\n const ret: PusherEntry[] = [];\n for (const entries of pushesByConnection.values()) {\n const composite: PusherEntry = {\n ...entries[0],\n push: {\n ...entries[0].push,\n mutations: [],\n },\n };\n ret.push(composite);\n for (const entry of entries) {\n assertAreCompatiblePushes(composite, entry);\n composite.push.mutations.push(...entry.push.mutations);\n }\n }\n return ret;\n }\n\n for (const entry of entries) {\n if (entry === 'stop' || entry === undefined) {\n return [collect(), true];\n }\n\n const key = `${entry.connCtx.clientID}:${entry.connCtx.wsID}:${entry.connCtx.revision}`;\n getOrInsertComputed(pushesByConnection, key, newArray).push(entry);\n }\n\n return [collect(), false] as const;\n}\n\n// These invariants should always be true for a given clientID.\n// If they are not, we have a bug in the code somewhere.\nfunction assertAreCompatiblePushes(left: PusherEntry, right: PusherEntry) {\n assert(\n left.connCtx.clientID === right.connCtx.clientID,\n 'clientID must be the same for all pushes',\n );\n assert(\n left.connCtx.wsID === right.connCtx.wsID,\n 'wsID must be the same for all pushes',\n );\n assert(\n left.connCtx.revision === right.connCtx.revision,\n 'revision must be the same for all pushes',\n );\n assert(\n authEquals(left.connCtx.auth, right.connCtx.auth),\n 'auth must be the same for all pushes with the same clientID',\n );\n assert(\n left.push.schemaVersion === right.push.schemaVersion,\n 'schemaVersion must be the same for all pushes with the same clientID',\n );\n assert(\n left.push.pushVersion === right.push.pushVersion,\n 'pushVersion must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.mutateContext.headerOptions.cookie ===\n right.connCtx.mutateContext.headerOptions.cookie,\n 'httpCookie must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.mutateContext.headerOptions.origin ===\n right.connCtx.mutateContext.headerOptions.origin,\n 'origin must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.user.id === right.connCtx.user.id,\n 'userID must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.mutateContext.url === right.connCtx.mutateContext.url,\n 'userPushURL must be the same for all pushes with the same clientID',\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pusher.js","names":["MutationType.Custom","ErrorKind.PushFailed","ErrorOrigin.ZeroCache","ErrorReason.HTTP","ErrorOrigin.Server","ErrorReason.UnsupportedPushVersion","ErrorReason.Internal","ErrorReason.OutOfOrderMutation"],"sources":["../../../../../../zero-cache/src/services/mutagen/pusher.ts"],"sourcesContent":["import {context, propagation, ROOT_CONTEXT} from '@opentelemetry/api';\nimport type {LogContext} from '@rocicorp/logger';\nimport {groupBy, newArray} from '../../../../shared/src/arrays.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {getErrorMessage} from '../../../../shared/src/error.ts';\nimport {getOrInsertComputed} from '../../../../shared/src/map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {Queue} from '../../../../shared/src/queue.ts';\nimport type {Downstream} from '../../../../zero-protocol/src/down.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../../../zero-protocol/src/error-reason.ts';\nimport {\n isProtocolError,\n type PushFailedBody,\n} from '../../../../zero-protocol/src/error.ts';\nimport {\n mutateResponseSchema,\n type MutateResponse,\n} from '../../../../zero-protocol/src/mutate-server.ts';\nimport type {MutationID} from '../../../../zero-protocol/src/mutation-id.ts';\nimport * as MutationType from '../../../../zero-protocol/src/mutation-type-enum.ts';\nimport {CLEANUP_RESULTS_MUTATION_NAME} from '../../../../zero-protocol/src/mutation.ts';\nimport {type PushBody} from '../../../../zero-protocol/src/push.ts';\nimport {authEquals, isAuthErrorBody} from '../../auth/auth.ts';\nimport {type ZeroConfig} from '../../config/zero-config.ts';\nimport {fetchFromAPIServer} from '../../custom/fetch.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport {recordMutation} from '../../server/anonymous-otel-start.ts';\nimport {ProtocolErrorWithLevel} from '../../types/error-with-level.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {HandlerResult, StreamResult} from '../../workers/connection.ts';\nimport type {RefCountedService, Service} from '../service.ts';\nimport type {\n ConnectionContext,\n ConnectionContextManager,\n ConnectionSelector,\n} from '../view-syncer/connection-context-manager.ts';\n\nexport interface Pusher extends RefCountedService {\n initConnection(selector: ConnectionSelector): Source<Downstream>;\n enqueuePush(selector: ConnectionSelector, push: PushBody): HandlerResult;\n ackMutationResponses(\n requester: ConnectionSelector,\n upToID: MutationID,\n ): Promise<void>;\n deleteClientMutations(\n requester: ConnectionSelector,\n clientIDs: string[],\n ): Promise<void>;\n}\n\ntype Config = Pick<ZeroConfig, 'app' | 'shard'>;\n\n/**\n * Receives push messages from zero-client and forwards\n * them the the user's API server.\n *\n * If the user's API server is taking too long to process\n * the push, the PusherService will add the push to a queue\n * and send pushes in bulk the next time the user's API server\n * is available.\n *\n * - One PusherService exists per client group.\n * - Mutations for a given client are always sent in-order\n * - Mutations for different clients in the same group may be interleaved\n */\nexport class PusherService implements Service, Pusher {\n readonly id: string;\n readonly #connContextManager: ConnectionContextManager;\n readonly #pusher: PushWorker;\n readonly #queue: Queue<PusherEntryOrStop>;\n readonly #config: Config;\n readonly #lc: LogContext;\n #stopped: Promise<void> | undefined;\n #refCount = 0;\n #isStopped = false;\n\n constructor(\n appConfig: Config,\n lc: LogContext,\n clientGroupID: string,\n connContextManager: ConnectionContextManager,\n ) {\n this.#connContextManager = connContextManager;\n this.#config = appConfig;\n this.#lc = lc.withContext('component', 'pusherService');\n this.#queue = new Queue();\n this.#pusher = new PushWorker(\n appConfig,\n lc,\n this.#connContextManager,\n this.#queue,\n );\n this.id = clientGroupID;\n }\n\n initConnection(selector: ConnectionSelector): Source<Downstream> {\n return this.#pusher.initConnection(selector);\n }\n\n enqueuePush(\n selector: ConnectionSelector,\n push: PushBody,\n ): Exclude<HandlerResult, StreamResult> {\n const connCtx = this.#connContextManager.getConnectionContext(selector);\n if (connCtx) {\n this.#pusher.enqueuePush(connCtx, push);\n }\n\n return {\n type: 'ok',\n };\n }\n\n async ackMutationResponses(\n requester: ConnectionSelector,\n upToID: MutationID,\n ): Promise<void> {\n const connCtx = this.#connContextManager.getConnectionContext(requester);\n if (!connCtx?.mutateContext?.url) {\n // No push URL configured, skip cleanup\n return;\n }\n\n const cleanupBody: PushBody = {\n clientGroupID: this.id,\n mutations: [\n {\n type: MutationType.Custom,\n id: 0, // Not tracked - this is fire-and-forget\n clientID: upToID.clientID,\n name: CLEANUP_RESULTS_MUTATION_NAME,\n args: [\n {\n type: 'single',\n clientGroupID: this.id,\n clientID: upToID.clientID,\n upToMutationID: upToID.id,\n },\n ],\n timestamp: Date.now(),\n },\n ],\n pushVersion: 1,\n timestamp: Date.now(),\n requestID: `cleanup-${this.id}-${upToID.clientID}-${upToID.id}`,\n };\n\n try {\n await fetchFromAPIServer(\n mutateResponseSchema,\n 'push',\n this.#lc,\n connCtx,\n {appID: this.#config.app.id, shardNum: this.#config.shard.num},\n cleanupBody,\n {operation: 'cleanup', cleanupType: 'single'},\n );\n } catch (e) {\n this.#lc.warn?.('Failed to send cleanup mutation', {\n error: getErrorMessage(e),\n });\n }\n }\n\n /**\n * Bulk cleanup is routed through the requester's push context.\n *\n * This assumes the client group shares a compatible push endpoint/auth\n * context.\n */\n async deleteClientMutations(\n requester: ConnectionSelector,\n clientIDs: string[],\n ): Promise<void> {\n if (clientIDs.length === 0) {\n return;\n }\n\n const connCtx = this.#connContextManager.getConnectionContext(requester);\n if (!connCtx?.mutateContext?.url) {\n // No push URL configured, skip cleanup\n return;\n }\n\n const cleanupBody: PushBody = {\n clientGroupID: this.id,\n mutations: [\n {\n type: MutationType.Custom,\n id: 0, // Not tracked - this is fire-and-forget\n clientID: clientIDs[0], // Use first client as sender\n name: CLEANUP_RESULTS_MUTATION_NAME,\n args: [\n {\n type: 'bulk',\n clientGroupID: this.id,\n clientIDs,\n },\n ],\n timestamp: Date.now(),\n },\n ],\n pushVersion: 1,\n timestamp: Date.now(),\n requestID: `cleanup-bulk-${this.id}-${Date.now()}`,\n };\n\n try {\n await fetchFromAPIServer(\n mutateResponseSchema,\n 'push',\n this.#lc,\n connCtx,\n {appID: this.#config.app.id, shardNum: this.#config.shard.num},\n cleanupBody,\n {operation: 'cleanup', cleanupType: 'bulk'},\n );\n } catch (e) {\n this.#lc.warn?.('Failed to send bulk cleanup mutation', {\n error: getErrorMessage(e),\n });\n }\n }\n\n ref() {\n assert(!this.#isStopped, 'PusherService is already stopped');\n ++this.#refCount;\n }\n\n unref() {\n assert(!this.#isStopped, 'PusherService is already stopped');\n --this.#refCount;\n if (this.#refCount <= 0) {\n void this.stop();\n }\n }\n\n hasRefs(): boolean {\n return this.#refCount > 0;\n }\n\n run(): Promise<void> {\n this.#stopped = this.#pusher.run();\n return this.#stopped;\n }\n\n stop(): Promise<void> {\n if (this.#isStopped) {\n return must(this.#stopped, 'Stop was called before `run`');\n }\n this.#isStopped = true;\n this.#queue.enqueue('stop');\n return must(this.#stopped, 'Stop was called before `run`');\n }\n}\n\ntype PusherEntry = {\n push: PushBody;\n connCtx: ConnectionContext;\n};\ntype PusherEntryOrStop = PusherEntry | 'stop';\n\n/**\n * Awaits items in the queue then drains and sends them all\n * to the user's API server.\n */\nclass PushWorker {\n readonly #connContextManager: ConnectionContextManager;\n readonly #queue: Queue<PusherEntryOrStop>;\n readonly #lc: LogContext;\n readonly #config: Config;\n readonly #clients: Map<\n string,\n {wsID: string; downstream: Subscription<Downstream>}\n >;\n /**\n * For each client, the wsID of its socket whose push most recently failed.\n * Pushes queued behind the failed one on that socket are not sent: they\n * start past the failed mutation, so the API server could only reject them\n * as out of order. The socket is failed (or was already replaced), and the\n * client re-sends every unacknowledged mutation on its next socket.\n *\n * An entry is kept until the socket can no longer queue pushes (it closed or\n * the client reconnected) and the queue has been drained since, so it is not\n * removed while pushes from the failed socket may still be queued.\n */\n readonly #failedSockets = new Map<string, string>();\n\n readonly #customMutations = getOrCreateCounter(\n 'mutation',\n 'custom',\n 'Number of custom mutations processed',\n );\n readonly #pushes = getOrCreateCounter(\n 'mutation',\n 'pushes',\n 'Number of pushes processed by the pusher',\n );\n\n constructor(\n config: Config,\n lc: LogContext,\n connContextManager: ConnectionContextManager,\n queue: Queue<PusherEntryOrStop>,\n ) {\n this.#lc = lc.withContext('component', 'pusher');\n this.#connContextManager = connContextManager;\n this.#queue = queue;\n this.#config = config;\n this.#clients = new Map();\n }\n\n /**\n * Returns a new downstream stream if the clientID,wsID pair has not been seen before.\n * If a clientID already exists with a different wsID, that client's downstream is cancelled.\n */\n initConnection(selector: ConnectionSelector): Subscription<Downstream> {\n const existing = this.#clients.get(selector.clientID);\n if (existing && existing.wsID === selector.wsID) {\n // already initialized for this socket\n throw new Error('Connection was already initialized');\n }\n\n // client is back on a new connection\n if (existing) {\n existing.downstream.cancel();\n }\n\n const downstream = Subscription.create<Downstream>({\n cleanup: () => {\n this.#clients.delete(selector.clientID);\n },\n });\n this.#clients.set(selector.clientID, {\n wsID: selector.wsID,\n downstream,\n });\n return downstream;\n }\n\n enqueuePush(connCtx: ConnectionContext, push: PushBody) {\n this.#queue.enqueue({\n push,\n connCtx,\n });\n }\n\n async run() {\n for (;;) {\n // Failed sockets that can no longer queue pushes. Everything they queued\n // is in the next drain, so they are forgotten once it has been handled.\n const closedSockets = [...this.#failedSockets].filter(\n ([clientID, wsID]) =>\n !this.#connContextManager.getConnectionContext({clientID, wsID}),\n );\n const task = await this.#queue.dequeue();\n const rest = this.#queue.drain();\n const [pushes, terminate] = combinePushes([task, ...rest]);\n for (const push of pushes) {\n const {clientID, wsID} = push.connCtx;\n if (this.#failedSockets.get(clientID) === wsID) {\n this.#lc.debug?.(\n 'Not sending push queued behind a failed push on the same socket; the client re-sends it on its next socket',\n {clientID, wsID, mutations: push.push.mutations.length},\n );\n continue;\n }\n const parentContext = push.push.traceparent\n ? propagation.extract(ROOT_CONTEXT, {\n traceparent: push.push.traceparent,\n })\n : context.active();\n const response = await context.with(parentContext, () =>\n this.#processPush(push),\n );\n await this.#fanOutResponses(push.connCtx, response);\n }\n\n for (const [clientID, wsID] of closedSockets) {\n if (this.#failedSockets.get(clientID) === wsID) {\n this.#failedSockets.delete(clientID);\n }\n }\n\n if (terminate) {\n break;\n }\n }\n }\n\n /**\n * 1. If the entire `push` fails, we send the error to `origin`, the\n * connection the push was sent on.\n * 2. If the push succeeds, we look for any mutation failure that should cause the connection to terminate\n * and terminate `origin`.\n *\n * Only `origin` is failed, even when the push carried other clients'\n * mutations (the client group's commit chain includes other tabs' pending\n * mutations). A client tracks what it has sent per socket, so the gap a\n * failed push leaves is on `origin` alone. Other clients send their own\n * mutations on their own sockets, and see any lasting error there.\n */\n #fanOutResponses(origin: ConnectionContext, response: MutateResponse) {\n const connectionTerminations: (() => void)[] = [];\n\n // if the entire push failed, send that to the client.\n if (\n ('kind' in response && response.kind === ErrorKind.PushFailed) ||\n 'error' in response\n ) {\n this.#lc.warn?.(\n 'The server behind ZERO_MUTATE_URL returned a push error.',\n response,\n );\n const mutationIDs = response.mutationIDs ?? [];\n if (mutationIDs.length > 0) {\n // We do not resolve mutations on the client if the push fails\n // as those mutations will be retried.\n if ('error' in response) {\n // This error code path will eventually be removed when we\n // no longer support the legacy push error format.\n const pushFailedBody: PushFailedBody =\n response.error === 'http'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n status: response.status,\n bodyPreview: response.details,\n mutationIDs,\n message: `Fetch from API server returned non-OK status ${response.status}`,\n }\n : response.error === 'unsupportedPushVersion'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.UnsupportedPushVersion,\n mutationIDs,\n message: `Unsupported push version`,\n }\n : {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Internal,\n mutationIDs,\n message:\n response.error === 'zeroPusher'\n ? response.details\n : response.error === 'unsupportedSchemaVersion'\n ? 'Unsupported schema version'\n : 'An unknown error occurred while pushing to the API server',\n };\n\n this.#failOrigin(origin, pushFailedBody);\n } else {\n this.#failOrigin(origin, response);\n }\n }\n } else {\n // Look for mutations results that should cause us to terminate the connection\n const groupedMutations = groupBy(response.mutations, m => m.id.clientID);\n for (const mutations of groupedMutations.values()) {\n let failure: PushFailedBody | undefined;\n let i = 0;\n for (; i < mutations.length; i++) {\n const m = mutations[i];\n if ('error' in m.result) {\n this.#lc.warn?.(\n 'The server behind ZERO_MUTATE_URL returned a mutation error.',\n m.result,\n );\n }\n // This error code path will eventually be removed,\n // keeping this for backwards compatibility, but the server\n // should now return a PushFailedBody with the mutationIDs\n if ('error' in m.result && m.result.error === 'oooMutation') {\n failure = {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.OutOfOrderMutation,\n message: 'mutation was out of order',\n details: m.result.details,\n mutationIDs: mutations.map(m => ({\n clientID: m.id.clientID,\n id: m.id.id,\n })),\n };\n break;\n }\n }\n\n if (failure && i < mutations.length - 1) {\n this.#lc.warn?.(\n 'push-response contains mutations after a mutation which should fatal the connection',\n );\n }\n\n if (failure) {\n connectionTerminations.push(() => this.#failOrigin(origin, failure));\n }\n }\n }\n\n connectionTerminations.forEach(cb => cb());\n }\n\n /**\n * Fails `origin` after a push sent on it failed, and records it in\n * `#failedSockets` so the pushes it queued behind that one are not sent.\n *\n * If the client has reconnected since sending the push, the old socket is\n * already gone and the client re-sends its mutations on the new one. Failing\n * the new socket would put the client in its error state, rejecting its\n * outstanding mutations, for a failure it has already recovered from.\n */\n #failOrigin(origin: ConnectionContext, errorBody: PushFailedBody): void {\n this.#failedSockets.set(origin.clientID, origin.wsID);\n const client = this.#clients.get(origin.clientID);\n if (client?.wsID === origin.wsID) {\n this.#failDownstream(client.downstream, errorBody);\n }\n }\n\n async #processPush(entry: PusherEntry): Promise<MutateResponse> {\n this.#customMutations.add(entry.push.mutations.length, {\n clientGroupID: entry.push.clientGroupID,\n });\n this.#pushes.add(1, {\n clientGroupID: entry.push.clientGroupID,\n });\n\n // Record custom mutations for telemetry\n recordMutation('custom', entry.push.mutations.length);\n\n const url = must(\n entry.connCtx.mutateContext.url,\n 'ZERO_MUTATE_URL is not set',\n );\n\n this.#lc.debug?.(\n 'pushing to',\n url,\n 'with',\n entry.push.mutations.length,\n 'mutations',\n );\n\n let mutationIDs: MutationID[] = [];\n\n try {\n mutationIDs = entry.push.mutations.map(m => ({\n id: m.id,\n clientID: m.clientID,\n }));\n\n const response = await fetchFromAPIServer(\n mutateResponseSchema,\n 'push',\n this.#lc,\n entry.connCtx,\n {\n appID: this.#config.app.id,\n shardNum: this.#config.shard.num,\n },\n entry.push,\n {operation: 'mutate'},\n );\n if (\n ('kind' in response && response.kind === ErrorKind.PushFailed) ||\n 'error' in response\n ) {\n if (isAuthErrorBody(response)) {\n this.#lc.warn?.('Push auth failed; invalidating connection', {\n clientID: entry.connCtx.clientID,\n response: 'kind' in response ? response.message : undefined,\n });\n this.#connContextManager.failConnection(\n entry.connCtx,\n entry.connCtx.revision,\n );\n }\n return response;\n }\n // A successful push also validates this connection's current auth snapshot.\n // That lets later shared work reuse it without trusting stale credentials.\n this.#connContextManager.validateConnection(\n entry.connCtx,\n entry.connCtx.revision,\n 'kind' in response &&\n response.kind === 'MutateResponse' &&\n response?.userID !== undefined\n ? {kind: 'server-validated', validatedUserID: response.userID}\n : {kind: 'client-fallback'},\n );\n return response;\n } catch (e) {\n if (isProtocolError(e) && e.errorBody.kind === ErrorKind.PushFailed) {\n const response = {\n ...e.errorBody,\n mutationIDs,\n } as const satisfies PushFailedBody;\n if (isAuthErrorBody(response)) {\n this.#lc.warn?.('Push auth failed; invalidating connection', {\n clientID: entry.connCtx.clientID,\n response: 'kind' in response ? response.message : undefined,\n });\n this.#connContextManager.failConnection(\n entry.connCtx,\n entry.connCtx.revision,\n );\n }\n return response;\n }\n\n if (isProtocolError(e) && isAuthErrorBody(e.errorBody)) {\n // The push completed far enough for local validation to reject the\n // connection, so invalidate it and surface the result as PushFailed.\n this.#lc.warn?.('Push validation failed; invalidating connection', {\n clientID: entry.connCtx.clientID,\n response: e.message,\n });\n this.#connContextManager.failConnection(\n entry.connCtx,\n entry.connCtx.revision,\n );\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n message: e.message,\n status: 401,\n mutationIDs,\n } as const satisfies PushFailedBody;\n }\n\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `Failed to push: ${getErrorMessage(e)}`,\n mutationIDs,\n } as const satisfies PushFailedBody;\n }\n }\n\n #failDownstream(\n downstream: Subscription<Downstream>,\n errorBody: PushFailedBody,\n ): void {\n downstream.fail(new ProtocolErrorWithLevel(errorBody, 'warn'));\n }\n}\n\n/**\n * Pushes for different clients, sockets, or auth revisions could be interleaved.\n *\n * In order to batch safely, we only combine pushes from the same\n * clientID/wsID/revision snapshot.\n */\nexport function combinePushes(\n entries: readonly (PusherEntryOrStop | undefined)[],\n): [PusherEntry[], boolean] {\n const pushesByConnection = new Map<string, PusherEntry[]>();\n\n function collect() {\n const ret: PusherEntry[] = [];\n for (const entries of pushesByConnection.values()) {\n const composite: PusherEntry = {\n ...entries[0],\n push: {\n ...entries[0].push,\n mutations: [],\n },\n };\n ret.push(composite);\n for (const entry of entries) {\n assertAreCompatiblePushes(composite, entry);\n composite.push.mutations.push(...entry.push.mutations);\n }\n }\n return ret;\n }\n\n for (const entry of entries) {\n if (entry === 'stop' || entry === undefined) {\n return [collect(), true];\n }\n\n const key = `${entry.connCtx.clientID}:${entry.connCtx.wsID}:${entry.connCtx.revision}`;\n getOrInsertComputed(pushesByConnection, key, newArray).push(entry);\n }\n\n return [collect(), false] as const;\n}\n\n// These invariants should always be true for a given clientID.\n// If they are not, we have a bug in the code somewhere.\nfunction assertAreCompatiblePushes(left: PusherEntry, right: PusherEntry) {\n assert(\n left.connCtx.clientID === right.connCtx.clientID,\n 'clientID must be the same for all pushes',\n );\n assert(\n left.connCtx.wsID === right.connCtx.wsID,\n 'wsID must be the same for all pushes',\n );\n assert(\n left.connCtx.revision === right.connCtx.revision,\n 'revision must be the same for all pushes',\n );\n assert(\n authEquals(left.connCtx.auth, right.connCtx.auth),\n 'auth must be the same for all pushes with the same clientID',\n );\n assert(\n left.push.schemaVersion === right.push.schemaVersion,\n 'schemaVersion must be the same for all pushes with the same clientID',\n );\n assert(\n left.push.pushVersion === right.push.pushVersion,\n 'pushVersion must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.mutateContext.headerOptions.cookie ===\n right.connCtx.mutateContext.headerOptions.cookie,\n 'httpCookie must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.mutateContext.headerOptions.origin ===\n right.connCtx.mutateContext.headerOptions.origin,\n 'origin must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.user.id === right.connCtx.user.id,\n 'userID must be the same for all pushes with the same clientID',\n );\n assert(\n left.connCtx.mutateContext.url === right.connCtx.mutateContext.url,\n 'userPushURL must be the same for all pushes with the same clientID',\n );\n}\n"],"mappings":"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"}
|
|
@@ -43,7 +43,7 @@ export type Stat4Sample = {
|
|
|
43
43
|
* hundreds of samples per index, which is a lot of context to spend on data an
|
|
44
44
|
* LLM would only summarize anyway.
|
|
45
45
|
*/
|
|
46
|
-
|
|
46
|
+
type Stat4PrefixSummary = {
|
|
47
47
|
/** Rows equal to the most common sampled key at this depth. */
|
|
48
48
|
maxRowsPerKey: number;
|
|
49
49
|
/** Median rows per sampled key at this depth. */
|
|
@@ -61,7 +61,7 @@ export type Stat4Summary = {
|
|
|
61
61
|
/** One entry per key prefix depth: 1 column, then 2, and so on. */
|
|
62
62
|
perPrefix: Stat4PrefixSummary[];
|
|
63
63
|
};
|
|
64
|
-
|
|
64
|
+
type IndexStats = {
|
|
65
65
|
name: string;
|
|
66
66
|
columns: {
|
|
67
67
|
name: string;
|
|
@@ -74,7 +74,7 @@ export type IndexStats = {
|
|
|
74
74
|
/** The full histogram, only with `?stat4=full`. */
|
|
75
75
|
stat4Samples?: Stat4Sample[] | undefined;
|
|
76
76
|
};
|
|
77
|
-
|
|
77
|
+
type ColumnStats = {
|
|
78
78
|
name: string;
|
|
79
79
|
dataType: string;
|
|
80
80
|
nullable: boolean;
|
|
@@ -82,7 +82,7 @@ export type ColumnStats = {
|
|
|
82
82
|
zqlType: string | null;
|
|
83
83
|
backfilling?: true | undefined;
|
|
84
84
|
};
|
|
85
|
-
|
|
85
|
+
type TableStats = {
|
|
86
86
|
name: string;
|
|
87
87
|
columns: ColumnStats[];
|
|
88
88
|
primaryKey: string[] | undefined;
|
|
@@ -117,14 +117,6 @@ export type PlannerzBundle = {
|
|
|
117
117
|
* @visibleForTesting
|
|
118
118
|
*/
|
|
119
119
|
export declare function parseStat1(raw: string): Stat1;
|
|
120
|
-
/**
|
|
121
|
-
* Builds the `/plannerz` bundle from the replica's catalog, `sqlite_stat*`
|
|
122
|
-
* tables and the server config. No user table is read, so the cost does not
|
|
123
|
-
* depend on how much data the replica holds.
|
|
124
|
-
*
|
|
125
|
-
* @visibleForTesting
|
|
126
|
-
*/
|
|
127
|
-
export declare function buildPlannerzBundle(lc: LogContext, config: ZeroConfig, opts?: BundleOptions): PlannerzBundle;
|
|
128
120
|
export type BundleOptions = {
|
|
129
121
|
/** Include the full `sqlite_stat4` histogram, not just the digest. */
|
|
130
122
|
fullStat4?: boolean | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plannerz.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/plannerz.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAC,QAAQ,EAAC,MAAM,2BAA2B,CAAC;AACnD,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAC,oBAAoB,IAAI,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAuB/E;;;;GAIG;AACH,QAAA,MAAM,KAAK;aACT,UAAU;aAIV,WAAW;aAMX,WAAW;aAOX,qBAAqB;aAUrB,gBAAgB,oBAGd,+CAA+C,UAG/C,wCAAwC;aAE1C,OAAO;CASC,CAAC;AAEX,MAAM,MAAM,KAAK,GAAG;IAClB,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kEAAkE;IAClE,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,yDAAyD;IACzD,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,
|
|
1
|
+
{"version":3,"file":"plannerz.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/plannerz.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getOperatorAccess, getServerVersion } from "../config/zero-config.js";
|
|
2
2
|
import { Database } from "../../../zqlite/src/db.js";
|
|
3
|
-
import { computeZqlSpecsFromLiteSpecs, listIndexes, listTables } from "../db/lite-tables.js";
|
|
4
3
|
import { decodeSampleKinds } from "../../../zqlite/src/sqlite-stat4-sample.js";
|
|
5
|
-
import {
|
|
4
|
+
import { computeZqlSpecsFromLiteSpecs, listIndexes, listTables } from "../db/lite-tables.js";
|
|
6
5
|
import { StatementRunner } from "../db/statements.js";
|
|
7
|
-
import {
|
|
6
|
+
import { getReplicationState } from "./replicator/schema/replication-state.js";
|
|
7
|
+
import _usingCtx from "../../../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/usingCtx.js";
|
|
8
8
|
import auth from "basic-auth";
|
|
9
9
|
//#region ../zero-cache/src/services/plannerz.ts
|
|
10
10
|
/**
|
|
@@ -73,8 +73,6 @@ function parseCounts(raw) {
|
|
|
73
73
|
* Builds the `/plannerz` bundle from the replica's catalog, `sqlite_stat*`
|
|
74
74
|
* tables and the server config. No user table is read, so the cost does not
|
|
75
75
|
* depend on how much data the replica holds.
|
|
76
|
-
*
|
|
77
|
-
* @visibleForTesting
|
|
78
76
|
*/
|
|
79
77
|
function buildPlannerzBundle(lc, config, opts = {}) {
|
|
80
78
|
try {
|
|
@@ -245,7 +243,8 @@ function replicaWatermark(lc, db) {
|
|
|
245
243
|
* * `stat4=full`: includes the full sqlite_stat4 histogram, which is large
|
|
246
244
|
*/
|
|
247
245
|
async function handlePlannerzRequest(lc, config, req, res) {
|
|
248
|
-
|
|
246
|
+
const credentials = auth(req);
|
|
247
|
+
if (getOperatorAccess(lc, config, credentials?.pass) === void 0) {
|
|
249
248
|
res.code(401).header("WWW-Authenticate", "Basic realm=\"Plannerz Protected Area\"").send("Unauthorized");
|
|
250
249
|
return;
|
|
251
250
|
}
|
|
@@ -262,6 +261,6 @@ async function handlePlannerzRequest(lc, config, req, res) {
|
|
|
262
261
|
await res.header("Content-Type", "application/json").send(JSON.stringify(bundle, null, indent));
|
|
263
262
|
}
|
|
264
263
|
//#endregion
|
|
265
|
-
export { handlePlannerzRequest };
|
|
264
|
+
export { buildPlannerzBundleFromDB, handlePlannerzRequest, parseStat1, summarizeStat4 };
|
|
266
265
|
|
|
267
266
|
//# sourceMappingURL=plannerz.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plannerz.js","names":[],"sources":["../../../../../zero-cache/src/services/plannerz.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport auth from 'basic-auth';\nimport type {FastifyReply, FastifyRequest} from 'fastify';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {\n decodeSampleKinds,\n type SampleValueKind,\n} from '../../../zqlite/src/sqlite-stat4-sample.ts';\nimport type {NormalizedZeroConfig as ZeroConfig} from '../config/normalize.ts';\nimport {getOperatorAccess, getServerVersion} from '../config/zero-config.ts';\nimport {\n computeZqlSpecsFromLiteSpecs,\n listIndexes,\n listTables,\n} from '../db/lite-tables.ts';\nimport {StatementRunner} from '../db/statements.ts';\nimport {getReplicationState} from './replicator/schema/replication-state.ts';\n\n/**\n * How the statistics in the replica were gathered. zero-cache only ever runs\n * `PRAGMA optimize`, and always under an `analysis_limit`, which samples rather\n * than scanning. A replica that someone ran `ANALYZE` on by hand has better\n * statistics, which is visible in `sqlite_stat4` having rows at all.\n */\nconst APPROXIMATE_METHOD =\n 'PRAGMA optimize under an analysis_limit. Row counts and per-key averages ' +\n 'are sampled estimates, and sqlite_stat4 is not collected.';\nconst FULL_METHOD =\n 'ANALYZE, with no analysis limit. sqlite_stat4 has rows, so a full ANALYZE ' +\n 'ran against this replica at some point.';\n\n/**\n * A fixed glossary, sent with every bundle. The audience is a language model\n * that has the user's codebase but has never seen these statistics, and would\n * otherwise read a sampled estimate as an exact count.\n */\nconst ABOUT = {\n whatThisIs:\n 'Schema, indexes and SQLite statistics for the SQLite replica that ' +\n 'zero-cache queries. Use it to decide how to rewrite ZQL queries and ' +\n 'which indexes to add. It contains no row data.',\n stat1Format:\n 'sqlite_stat1.stat is \"<rows> <avg rows per distinct 1-column prefix> ' +\n '<... per 2-column prefix> ...\", parsed here into `rows` and ' +\n '`avgRowsPerPrefix`. NULLs count as ordinary values, so a sparse column ' +\n 'looks less selective than it is. Trailing flags such as \"unordered\", ' +\n '\"noskipscan\" and \"sz=N\" are kept in `flags`.',\n stat4Format:\n 'sqlite_stat4 is a per-index histogram. For each sample, `nEq` is the ' +\n 'rows equal to it, `nLt` the rows less than it, and `nDLt` the distinct ' +\n 'values less than it, one entry per key column. A large `nEq` next to a ' +\n 'small `nDLt` means a skewed column, where the average in stat1 hides ' +\n 'the common values. Sample values are never returned: `sample` reports ' +\n 'only each key column’s type.',\n howThePlannerUsesThis: [\n 'Cost comes from SQLite scanstatus estimates for the SQL each source ' +\n 'connection would run, so the statistics here are what the planner sees.',\n 'Join fanout comes from sqlite_stat4, else sqlite_stat1, else a default ' +\n 'of 3. With no stat4 (the usual case) fanout on a nullable foreign key ' +\n 'is overestimated, because stat1 counts NULLs.',\n 'The planner chooses per join whether to run it as a semi-join or to flip ' +\n 'it so the child drives. An index that matches the child’s ' +\n 'constraint plus ordering is what makes a flip cheap.',\n ],\n whatYouCanChange: [\n 'The ZQL query: `whereExists` vs `related`, the order of conditions, and ' +\n 'ordering that matches an existing index.',\n 'A `limit`, which lets the planner stop early.',\n 'Indexes: add them in Postgres, not on the replica. The replica copies ' +\n 'upstream indexes, so a new index reaches it through replication.',\n 'The planner flags in `server.planner`.',\n ],\n caveats: [\n 'Row counts are estimates from the statistics, not COUNT(*). This ' +\n 'endpoint never scans a table.',\n 'Statistics are as of the last time they were gathered, which SQLite does ' +\n 'not record. A table whose size changed a lot since then can be far off.',\n 'Tables in `tables` with `syncable: false` cannot be queried by clients: ' +\n 'they lack a primary key or unique index, or every column has a type ' +\n 'ZQL does not support.',\n ],\n} as const;\n\nexport type Stat1 = {\n /** The raw `sqlite_stat1.stat` text, in case a flag is not parsed here. */\n raw: string;\n /** Estimated rows in the table. */\n rows: number;\n /**\n * Average rows per distinct key prefix: the first entry is for a 1-column\n * prefix, the second for 2 columns, and so on.\n */\n avgRowsPerPrefix: number[];\n /** Non-numeric tokens, e.g. `unordered`, `noskipscan`, `sz=N`. */\n flags: string[];\n};\n\n/** A `sqlite_stat4` sample with its values replaced by their types. */\nexport type Stat4Sample = {\n nEq: number[];\n nLt: number[];\n nDLt: number[];\n /** One entry per key column, plus the trailing rowid. */\n sample: SampleValueKind[];\n};\n\n/**\n * A digest of an index's `sqlite_stat4` histogram, one entry per key prefix\n * depth. This is what the default response carries: a full histogram runs to\n * hundreds of samples per index, which is a lot of context to spend on data an\n * LLM would only summarize anyway.\n */\nexport type Stat4PrefixSummary = {\n /** Rows equal to the most common sampled key at this depth. */\n maxRowsPerKey: number;\n /** Median rows per sampled key at this depth. */\n medianRowsPerKey: number;\n /**\n * Lower bound on distinct keys at this depth, from the last sample's count of\n * distinct values less than it.\n */\n estimatedDistinctKeys: number;\n};\n\nexport type Stat4Summary = {\n samples: number;\n /** The type of each key column, and of the trailing rowid. */\n sampleKinds: SampleValueKind[];\n /** One entry per key prefix depth: 1 column, then 2, and so on. */\n perPrefix: Stat4PrefixSummary[];\n};\n\nexport type IndexStats = {\n name: string;\n columns: {name: string; dir: 'ASC' | 'DESC'}[];\n unique: boolean;\n partial: boolean;\n stat1?: Stat1 | undefined;\n stat4?: Stat4Summary | undefined;\n /** The full histogram, only with `?stat4=full`. */\n stat4Samples?: Stat4Sample[] | undefined;\n};\n\nexport type ColumnStats = {\n name: string;\n dataType: string;\n nullable: boolean;\n /** The ZQL type, or `null` for a column clients cannot see. */\n zqlType: string | null;\n backfilling?: true | undefined;\n};\n\nexport type TableStats = {\n name: string;\n columns: ColumnStats[];\n primaryKey: string[] | undefined;\n estimatedRows: number | undefined;\n syncable: boolean;\n indexes: IndexStats[];\n};\n\nexport type PlannerzBundle = {\n about: typeof ABOUT;\n server: {\n zeroVersion: string;\n sqliteVersion: string;\n replicaWatermark: string | undefined;\n generatedAt: string;\n planner: {\n enableQueryPlanner: boolean;\n enableCorrelatedPredicatePushdown: boolean;\n enablePlannerAwarePushdown: boolean;\n };\n };\n statsQuality: {\n stat1Present: boolean;\n stat4Rows: number;\n method: string;\n tablesWithoutStats: string[];\n };\n tables: TableStats[];\n};\n\n/**\n * Parses a `sqlite_stat1.stat` value.\n *\n * @visibleForTesting\n */\nexport function parseStat1(raw: string): Stat1 {\n const tokens = raw.trim().split(SEPARATOR).filter(Boolean);\n const numbers: number[] = [];\n const flags: string[] = [];\n for (const token of tokens) {\n // Only leading tokens are counts; anything else is a flag, even if it\n // parses as a number (e.g. a future \"sz=N\" spelled differently).\n if (flags.length === 0 && COUNT.test(token)) {\n numbers.push(parseInt(token, 10));\n } else {\n flags.push(token);\n }\n }\n return {\n raw,\n rows: numbers[0] ?? 0,\n avgRowsPerPrefix: numbers.slice(1),\n flags,\n };\n}\n\n/** Whitespace between the counts in a `sqlite_stat1` or `sqlite_stat4` value. */\nconst SEPARATOR = /\\s+/;\n/** A count, as opposed to a trailing flag such as `unordered` or `sz=N`. */\nconst COUNT = /^\\d+$/;\n\ntype Stat1Row = {tbl: string; idx: string | null; stat: string};\ntype Stat4Row = {\n tbl: string;\n idx: string;\n neq: string;\n nlt: string;\n ndlt: string;\n sample: Buffer;\n};\n\nfunction tableExists(db: Database, name: string): boolean {\n return (\n db\n .prepare(\n `SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`,\n )\n .all(name).length > 0\n );\n}\n\nfunction parseCounts(raw: string): number[] {\n return raw\n .trim()\n .split(SEPARATOR)\n .filter(Boolean)\n .map(n => parseInt(n, 10));\n}\n\n/**\n * Builds the `/plannerz` bundle from the replica's catalog, `sqlite_stat*`\n * tables and the server config. No user table is read, so the cost does not\n * depend on how much data the replica holds.\n *\n * @visibleForTesting\n */\nexport function buildPlannerzBundle(\n lc: LogContext,\n config: ZeroConfig,\n opts: BundleOptions = {},\n): PlannerzBundle {\n using db = new Database(lc, config.replica.file, {readonly: true});\n return buildPlannerzBundleFromDB(lc, db, config, opts);\n}\n\nexport type BundleOptions = {\n /** Include the full `sqlite_stat4` histogram, not just the digest. */\n fullStat4?: boolean | undefined;\n};\n\n/** @visibleForTesting */\nexport function buildPlannerzBundleFromDB(\n lc: LogContext,\n db: Database,\n config: ZeroConfig,\n opts: BundleOptions = {},\n): PlannerzBundle {\n const tables = listReplicaTables(lc, db);\n const indexes = listIndexes(db);\n const zqlSpecs = computeZqlSpecsFromLiteSpecs(\n tables,\n indexes,\n {includeBackfillingColumns: false},\n undefined,\n undefined,\n lc,\n );\n\n const stat1Present = tableExists(db, 'sqlite_stat1');\n const stat1ByTableAndIndex = new Map<string, Stat1>();\n const statsByTable = new Map<string, Stat1>();\n if (stat1Present) {\n for (const {tbl, idx, stat} of db\n .prepare(`SELECT tbl, idx, stat FROM sqlite_stat1`)\n .all() as Stat1Row[]) {\n const parsed = parseStat1(stat);\n if (idx !== null) {\n stat1ByTableAndIndex.set(`${tbl}.${idx}`, parsed);\n }\n // Every row for a table starts with the table's row count. A row with a\n // null `idx` is written for a table with no indexes, and is preferred\n // because it is about the table itself.\n if (idx === null || !statsByTable.has(tbl)) {\n statsByTable.set(tbl, parsed);\n }\n }\n }\n\n const stat4Present = tableExists(db, 'sqlite_stat4');\n const stat4ByTableAndIndex = new Map<string, Stat4Sample[]>();\n let stat4Rows = 0;\n if (stat4Present) {\n for (const {tbl, idx, neq, nlt, ndlt, sample} of db\n .prepare(`SELECT tbl, idx, neq, nlt, ndlt, sample FROM sqlite_stat4`)\n .all() as Stat4Row[]) {\n stat4Rows++;\n const samples = stat4ByTableAndIndex.get(`${tbl}.${idx}`) ?? [];\n samples.push({\n nEq: parseCounts(neq),\n nLt: parseCounts(nlt),\n nDLt: parseCounts(ndlt),\n // Values are deliberately dropped here: only the types are reported.\n sample: decodeSampleKinds(sample),\n });\n stat4ByTableAndIndex.set(`${tbl}.${idx}`, samples);\n }\n }\n\n const indexesByTable = new Map<string, IndexStats[]>();\n for (const index of indexes) {\n const key = `${index.tableName}.${index.name}`;\n const stat4 = stat4ByTableAndIndex.get(key);\n const forTable = indexesByTable.get(index.tableName) ?? [];\n forTable.push({\n name: index.name,\n columns: Object.entries(index.columns).map(([name, dir]) => ({\n name,\n dir,\n })),\n unique: index.unique,\n partial: index.partial === true,\n stat1: stat1ByTableAndIndex.get(key),\n stat4: stat4?.length ? summarizeStat4(stat4) : undefined,\n stat4Samples: opts.fullStat4 && stat4?.length ? stat4 : undefined,\n });\n indexesByTable.set(index.tableName, forTable);\n }\n\n const tablesWithoutStats: string[] = [];\n const tableStats = tables.map(table => {\n const zqlSpec = zqlSpecs.get(table.name)?.zqlSpec;\n if (!statsByTable.has(table.name)) {\n tablesWithoutStats.push(table.name);\n }\n const backfilling = new Set(table.backfilling ?? []);\n return {\n name: table.name,\n columns: Object.entries(table.columns).map(([name, spec]) => ({\n name,\n dataType: spec.dataType,\n nullable: !spec.notNull,\n zqlType: zqlSpec?.[name]?.type ?? null,\n ...(backfilling.has(name) ? {backfilling: true as const} : {}),\n })),\n primaryKey: table.primaryKey ? [...table.primaryKey] : undefined,\n estimatedRows: statsByTable.get(table.name)?.rows,\n syncable: zqlSpec !== undefined,\n indexes: indexesByTable.get(table.name) ?? [],\n };\n });\n\n return {\n about: ABOUT,\n server: {\n zeroVersion: getServerVersion(config),\n sqliteVersion: sqliteVersion(db),\n replicaWatermark: replicaWatermark(lc, db),\n generatedAt: new Date().toISOString(),\n planner: {\n enableQueryPlanner: config.enableQueryPlanner,\n // Both default to on, matching services/analyze.ts.\n enableCorrelatedPredicatePushdown:\n config.enableCorrelatedPredicatePushdown !== false,\n enablePlannerAwarePushdown: config.enablePlannerAwarePushdown !== false,\n },\n },\n statsQuality: {\n stat1Present,\n stat4Rows,\n method: stat4Rows > 0 ? FULL_METHOD : APPROXIMATE_METHOD,\n tablesWithoutStats,\n },\n tables: tableStats,\n };\n}\n\n/**\n * Lists the replica's tables, tolerating a replica that has no metadata tables\n * yet. `listTables` reads `_zero.tableMetadata` for backfill status, which does\n * not exist until initial sync has created it. Reporting the schema without\n * backfill status beats failing the whole request.\n */\nfunction listReplicaTables(lc: LogContext, db: Database) {\n try {\n return listTables(db);\n } catch (e) {\n lc.debug?.('reading table metadata failed; listing tables without it', e);\n return listTables(db, true, false);\n }\n}\n\n/**\n * Digests an index's stat4 samples.\n *\n * Note that `nLt` and `nDLt` are stored as text, so SQL cannot order the\n * samples by them numerically. This works off the whole set rather than\n * relying on the samples' order: the highest `nDLt` is a lower bound on the\n * distinct keys, since the sample holding it has that many distinct keys below\n * it plus itself.\n *\n * @visibleForTesting\n */\nexport function summarizeStat4(samples: Stat4Sample[]): Stat4Summary {\n const depth = Math.max(...samples.map(s => s.nEq.length));\n const perPrefix: Stat4PrefixSummary[] = [];\n for (let d = 0; d < depth; d++) {\n const counts = samples\n .map(s => s.nEq[d])\n .filter(n => n !== undefined)\n .sort((a, b) => a - b);\n const maxDistinctBelow = Math.max(\n ...samples.map(s => s.nDLt[d]).filter(n => n !== undefined),\n );\n perPrefix.push({\n maxRowsPerKey: counts.at(-1)!,\n medianRowsPerKey: counts[Math.floor((counts.length - 1) / 2)],\n estimatedDistinctKeys: maxDistinctBelow + 1,\n });\n }\n return {\n samples: samples.length,\n // Every sample of an index describes the same columns.\n sampleKinds: samples[0].sample,\n perPrefix,\n };\n}\n\nfunction sqliteVersion(db: Database): string {\n const [{version}] = db\n .prepare(`SELECT sqlite_version() AS version`)\n .all() as {version: string}[];\n return version;\n}\n\nfunction replicaWatermark(lc: LogContext, db: Database): string | undefined {\n try {\n return getReplicationState(new StatementRunner(db)).stateVersion;\n } catch (e) {\n // A replica that has not finished initial sync has no replication state.\n lc.debug?.('could not read replication state', e);\n return undefined;\n }\n}\n\n/**\n * Serves the replica's schema, indexes and SQLite statistics for consumption by\n * a language model, so that it can advise on query rewrites and indexes.\n *\n * Row data is never returned. See `designs/003_replica_stats_endpoint.md`.\n *\n * This is available to the operator password as well as the admin password\n * (see `--operator-password`). That does not extend to `/plannerz/analyze`,\n * which runs caller-supplied queries against the replica and so must require\n * the admin password (`isAdminPasswordValid`).\n *\n * HTTP query parameters:\n * * `pretty`: indents the JSON\n * * `stat4=full`: includes the full sqlite_stat4 histogram, which is large\n */\nexport async function handlePlannerzRequest(\n lc: LogContext,\n config: ZeroConfig,\n req: FastifyRequest,\n res: FastifyReply,\n) {\n const credentials = auth(req);\n if (getOperatorAccess(lc, config, credentials?.pass) === undefined) {\n void res\n .code(401)\n .header('WWW-Authenticate', 'Basic realm=\"Plannerz Protected Area\"')\n .send('Unauthorized');\n return;\n }\n\n const query = req.query as Record<string, unknown>;\n const indent = query.pretty !== undefined ? 2 : undefined;\n let bundle: PlannerzBundle;\n try {\n bundle = buildPlannerzBundle(lc, config, {\n fullStat4: query.stat4 === 'full',\n });\n } catch (e) {\n lc.error?.('error building plannerz bundle', e);\n await res.code(500).send({error: String(e)});\n return;\n }\n await res\n .header('Content-Type', 'application/json')\n .send(JSON.stringify(bundle, null, indent));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plannerz.js","names":[],"sources":["../../../../../zero-cache/src/services/plannerz.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport auth from 'basic-auth';\nimport type {FastifyReply, FastifyRequest} from 'fastify';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {\n decodeSampleKinds,\n type SampleValueKind,\n} from '../../../zqlite/src/sqlite-stat4-sample.ts';\nimport type {NormalizedZeroConfig as ZeroConfig} from '../config/normalize.ts';\nimport {getOperatorAccess, getServerVersion} from '../config/zero-config.ts';\nimport {\n computeZqlSpecsFromLiteSpecs,\n listIndexes,\n listTables,\n} from '../db/lite-tables.ts';\nimport {StatementRunner} from '../db/statements.ts';\nimport {getReplicationState} from './replicator/schema/replication-state.ts';\n\n/**\n * How the statistics in the replica were gathered. zero-cache only ever runs\n * `PRAGMA optimize`, and always under an `analysis_limit`, which samples rather\n * than scanning. A replica that someone ran `ANALYZE` on by hand has better\n * statistics, which is visible in `sqlite_stat4` having rows at all.\n */\nconst APPROXIMATE_METHOD =\n 'PRAGMA optimize under an analysis_limit. Row counts and per-key averages ' +\n 'are sampled estimates, and sqlite_stat4 is not collected.';\nconst FULL_METHOD =\n 'ANALYZE, with no analysis limit. sqlite_stat4 has rows, so a full ANALYZE ' +\n 'ran against this replica at some point.';\n\n/**\n * A fixed glossary, sent with every bundle. The audience is a language model\n * that has the user's codebase but has never seen these statistics, and would\n * otherwise read a sampled estimate as an exact count.\n */\nconst ABOUT = {\n whatThisIs:\n 'Schema, indexes and SQLite statistics for the SQLite replica that ' +\n 'zero-cache queries. Use it to decide how to rewrite ZQL queries and ' +\n 'which indexes to add. It contains no row data.',\n stat1Format:\n 'sqlite_stat1.stat is \"<rows> <avg rows per distinct 1-column prefix> ' +\n '<... per 2-column prefix> ...\", parsed here into `rows` and ' +\n '`avgRowsPerPrefix`. NULLs count as ordinary values, so a sparse column ' +\n 'looks less selective than it is. Trailing flags such as \"unordered\", ' +\n '\"noskipscan\" and \"sz=N\" are kept in `flags`.',\n stat4Format:\n 'sqlite_stat4 is a per-index histogram. For each sample, `nEq` is the ' +\n 'rows equal to it, `nLt` the rows less than it, and `nDLt` the distinct ' +\n 'values less than it, one entry per key column. A large `nEq` next to a ' +\n 'small `nDLt` means a skewed column, where the average in stat1 hides ' +\n 'the common values. Sample values are never returned: `sample` reports ' +\n 'only each key column’s type.',\n howThePlannerUsesThis: [\n 'Cost comes from SQLite scanstatus estimates for the SQL each source ' +\n 'connection would run, so the statistics here are what the planner sees.',\n 'Join fanout comes from sqlite_stat4, else sqlite_stat1, else a default ' +\n 'of 3. With no stat4 (the usual case) fanout on a nullable foreign key ' +\n 'is overestimated, because stat1 counts NULLs.',\n 'The planner chooses per join whether to run it as a semi-join or to flip ' +\n 'it so the child drives. An index that matches the child’s ' +\n 'constraint plus ordering is what makes a flip cheap.',\n ],\n whatYouCanChange: [\n 'The ZQL query: `whereExists` vs `related`, the order of conditions, and ' +\n 'ordering that matches an existing index.',\n 'A `limit`, which lets the planner stop early.',\n 'Indexes: add them in Postgres, not on the replica. The replica copies ' +\n 'upstream indexes, so a new index reaches it through replication.',\n 'The planner flags in `server.planner`.',\n ],\n caveats: [\n 'Row counts are estimates from the statistics, not COUNT(*). This ' +\n 'endpoint never scans a table.',\n 'Statistics are as of the last time they were gathered, which SQLite does ' +\n 'not record. A table whose size changed a lot since then can be far off.',\n 'Tables in `tables` with `syncable: false` cannot be queried by clients: ' +\n 'they lack a primary key or unique index, or every column has a type ' +\n 'ZQL does not support.',\n ],\n} as const;\n\nexport type Stat1 = {\n /** The raw `sqlite_stat1.stat` text, in case a flag is not parsed here. */\n raw: string;\n /** Estimated rows in the table. */\n rows: number;\n /**\n * Average rows per distinct key prefix: the first entry is for a 1-column\n * prefix, the second for 2 columns, and so on.\n */\n avgRowsPerPrefix: number[];\n /** Non-numeric tokens, e.g. `unordered`, `noskipscan`, `sz=N`. */\n flags: string[];\n};\n\n/** A `sqlite_stat4` sample with its values replaced by their types. */\nexport type Stat4Sample = {\n nEq: number[];\n nLt: number[];\n nDLt: number[];\n /** One entry per key column, plus the trailing rowid. */\n sample: SampleValueKind[];\n};\n\n/**\n * A digest of an index's `sqlite_stat4` histogram, one entry per key prefix\n * depth. This is what the default response carries: a full histogram runs to\n * hundreds of samples per index, which is a lot of context to spend on data an\n * LLM would only summarize anyway.\n */\ntype Stat4PrefixSummary = {\n /** Rows equal to the most common sampled key at this depth. */\n maxRowsPerKey: number;\n /** Median rows per sampled key at this depth. */\n medianRowsPerKey: number;\n /**\n * Lower bound on distinct keys at this depth, from the last sample's count of\n * distinct values less than it.\n */\n estimatedDistinctKeys: number;\n};\n\nexport type Stat4Summary = {\n samples: number;\n /** The type of each key column, and of the trailing rowid. */\n sampleKinds: SampleValueKind[];\n /** One entry per key prefix depth: 1 column, then 2, and so on. */\n perPrefix: Stat4PrefixSummary[];\n};\n\ntype IndexStats = {\n name: string;\n columns: {name: string; dir: 'ASC' | 'DESC'}[];\n unique: boolean;\n partial: boolean;\n stat1?: Stat1 | undefined;\n stat4?: Stat4Summary | undefined;\n /** The full histogram, only with `?stat4=full`. */\n stat4Samples?: Stat4Sample[] | undefined;\n};\n\ntype ColumnStats = {\n name: string;\n dataType: string;\n nullable: boolean;\n /** The ZQL type, or `null` for a column clients cannot see. */\n zqlType: string | null;\n backfilling?: true | undefined;\n};\n\ntype TableStats = {\n name: string;\n columns: ColumnStats[];\n primaryKey: string[] | undefined;\n estimatedRows: number | undefined;\n syncable: boolean;\n indexes: IndexStats[];\n};\n\nexport type PlannerzBundle = {\n about: typeof ABOUT;\n server: {\n zeroVersion: string;\n sqliteVersion: string;\n replicaWatermark: string | undefined;\n generatedAt: string;\n planner: {\n enableQueryPlanner: boolean;\n enableCorrelatedPredicatePushdown: boolean;\n enablePlannerAwarePushdown: boolean;\n };\n };\n statsQuality: {\n stat1Present: boolean;\n stat4Rows: number;\n method: string;\n tablesWithoutStats: string[];\n };\n tables: TableStats[];\n};\n\n/**\n * Parses a `sqlite_stat1.stat` value.\n *\n * @visibleForTesting\n */\nexport function parseStat1(raw: string): Stat1 {\n const tokens = raw.trim().split(SEPARATOR).filter(Boolean);\n const numbers: number[] = [];\n const flags: string[] = [];\n for (const token of tokens) {\n // Only leading tokens are counts; anything else is a flag, even if it\n // parses as a number (e.g. a future \"sz=N\" spelled differently).\n if (flags.length === 0 && COUNT.test(token)) {\n numbers.push(parseInt(token, 10));\n } else {\n flags.push(token);\n }\n }\n return {\n raw,\n rows: numbers[0] ?? 0,\n avgRowsPerPrefix: numbers.slice(1),\n flags,\n };\n}\n\n/** Whitespace between the counts in a `sqlite_stat1` or `sqlite_stat4` value. */\nconst SEPARATOR = /\\s+/;\n/** A count, as opposed to a trailing flag such as `unordered` or `sz=N`. */\nconst COUNT = /^\\d+$/;\n\ntype Stat1Row = {tbl: string; idx: string | null; stat: string};\ntype Stat4Row = {\n tbl: string;\n idx: string;\n neq: string;\n nlt: string;\n ndlt: string;\n sample: Buffer;\n};\n\nfunction tableExists(db: Database, name: string): boolean {\n return (\n db\n .prepare(\n `SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`,\n )\n .all(name).length > 0\n );\n}\n\nfunction parseCounts(raw: string): number[] {\n return raw\n .trim()\n .split(SEPARATOR)\n .filter(Boolean)\n .map(n => parseInt(n, 10));\n}\n\n/**\n * Builds the `/plannerz` bundle from the replica's catalog, `sqlite_stat*`\n * tables and the server config. No user table is read, so the cost does not\n * depend on how much data the replica holds.\n */\nfunction buildPlannerzBundle(\n lc: LogContext,\n config: ZeroConfig,\n opts: BundleOptions = {},\n): PlannerzBundle {\n using db = new Database(lc, config.replica.file, {readonly: true});\n return buildPlannerzBundleFromDB(lc, db, config, opts);\n}\n\nexport type BundleOptions = {\n /** Include the full `sqlite_stat4` histogram, not just the digest. */\n fullStat4?: boolean | undefined;\n};\n\n/** @visibleForTesting */\nexport function buildPlannerzBundleFromDB(\n lc: LogContext,\n db: Database,\n config: ZeroConfig,\n opts: BundleOptions = {},\n): PlannerzBundle {\n const tables = listReplicaTables(lc, db);\n const indexes = listIndexes(db);\n const zqlSpecs = computeZqlSpecsFromLiteSpecs(\n tables,\n indexes,\n {includeBackfillingColumns: false},\n undefined,\n undefined,\n lc,\n );\n\n const stat1Present = tableExists(db, 'sqlite_stat1');\n const stat1ByTableAndIndex = new Map<string, Stat1>();\n const statsByTable = new Map<string, Stat1>();\n if (stat1Present) {\n for (const {tbl, idx, stat} of db\n .prepare(`SELECT tbl, idx, stat FROM sqlite_stat1`)\n .all() as Stat1Row[]) {\n const parsed = parseStat1(stat);\n if (idx !== null) {\n stat1ByTableAndIndex.set(`${tbl}.${idx}`, parsed);\n }\n // Every row for a table starts with the table's row count. A row with a\n // null `idx` is written for a table with no indexes, and is preferred\n // because it is about the table itself.\n if (idx === null || !statsByTable.has(tbl)) {\n statsByTable.set(tbl, parsed);\n }\n }\n }\n\n const stat4Present = tableExists(db, 'sqlite_stat4');\n const stat4ByTableAndIndex = new Map<string, Stat4Sample[]>();\n let stat4Rows = 0;\n if (stat4Present) {\n for (const {tbl, idx, neq, nlt, ndlt, sample} of db\n .prepare(`SELECT tbl, idx, neq, nlt, ndlt, sample FROM sqlite_stat4`)\n .all() as Stat4Row[]) {\n stat4Rows++;\n const samples = stat4ByTableAndIndex.get(`${tbl}.${idx}`) ?? [];\n samples.push({\n nEq: parseCounts(neq),\n nLt: parseCounts(nlt),\n nDLt: parseCounts(ndlt),\n // Values are deliberately dropped here: only the types are reported.\n sample: decodeSampleKinds(sample),\n });\n stat4ByTableAndIndex.set(`${tbl}.${idx}`, samples);\n }\n }\n\n const indexesByTable = new Map<string, IndexStats[]>();\n for (const index of indexes) {\n const key = `${index.tableName}.${index.name}`;\n const stat4 = stat4ByTableAndIndex.get(key);\n const forTable = indexesByTable.get(index.tableName) ?? [];\n forTable.push({\n name: index.name,\n columns: Object.entries(index.columns).map(([name, dir]) => ({\n name,\n dir,\n })),\n unique: index.unique,\n partial: index.partial === true,\n stat1: stat1ByTableAndIndex.get(key),\n stat4: stat4?.length ? summarizeStat4(stat4) : undefined,\n stat4Samples: opts.fullStat4 && stat4?.length ? stat4 : undefined,\n });\n indexesByTable.set(index.tableName, forTable);\n }\n\n const tablesWithoutStats: string[] = [];\n const tableStats = tables.map(table => {\n const zqlSpec = zqlSpecs.get(table.name)?.zqlSpec;\n if (!statsByTable.has(table.name)) {\n tablesWithoutStats.push(table.name);\n }\n const backfilling = new Set(table.backfilling ?? []);\n return {\n name: table.name,\n columns: Object.entries(table.columns).map(([name, spec]) => ({\n name,\n dataType: spec.dataType,\n nullable: !spec.notNull,\n zqlType: zqlSpec?.[name]?.type ?? null,\n ...(backfilling.has(name) ? {backfilling: true as const} : {}),\n })),\n primaryKey: table.primaryKey ? [...table.primaryKey] : undefined,\n estimatedRows: statsByTable.get(table.name)?.rows,\n syncable: zqlSpec !== undefined,\n indexes: indexesByTable.get(table.name) ?? [],\n };\n });\n\n return {\n about: ABOUT,\n server: {\n zeroVersion: getServerVersion(config),\n sqliteVersion: sqliteVersion(db),\n replicaWatermark: replicaWatermark(lc, db),\n generatedAt: new Date().toISOString(),\n planner: {\n enableQueryPlanner: config.enableQueryPlanner,\n // Both default to on, matching services/analyze.ts.\n enableCorrelatedPredicatePushdown:\n config.enableCorrelatedPredicatePushdown !== false,\n enablePlannerAwarePushdown: config.enablePlannerAwarePushdown !== false,\n },\n },\n statsQuality: {\n stat1Present,\n stat4Rows,\n method: stat4Rows > 0 ? FULL_METHOD : APPROXIMATE_METHOD,\n tablesWithoutStats,\n },\n tables: tableStats,\n };\n}\n\n/**\n * Lists the replica's tables, tolerating a replica that has no metadata tables\n * yet. `listTables` reads `_zero.tableMetadata` for backfill status, which does\n * not exist until initial sync has created it. Reporting the schema without\n * backfill status beats failing the whole request.\n */\nfunction listReplicaTables(lc: LogContext, db: Database) {\n try {\n return listTables(db);\n } catch (e) {\n lc.debug?.('reading table metadata failed; listing tables without it', e);\n return listTables(db, true, false);\n }\n}\n\n/**\n * Digests an index's stat4 samples.\n *\n * Note that `nLt` and `nDLt` are stored as text, so SQL cannot order the\n * samples by them numerically. This works off the whole set rather than\n * relying on the samples' order: the highest `nDLt` is a lower bound on the\n * distinct keys, since the sample holding it has that many distinct keys below\n * it plus itself.\n *\n * @visibleForTesting\n */\nexport function summarizeStat4(samples: Stat4Sample[]): Stat4Summary {\n const depth = Math.max(...samples.map(s => s.nEq.length));\n const perPrefix: Stat4PrefixSummary[] = [];\n for (let d = 0; d < depth; d++) {\n const counts = samples\n .map(s => s.nEq[d])\n .filter(n => n !== undefined)\n .sort((a, b) => a - b);\n const maxDistinctBelow = Math.max(\n ...samples.map(s => s.nDLt[d]).filter(n => n !== undefined),\n );\n perPrefix.push({\n maxRowsPerKey: counts.at(-1)!,\n medianRowsPerKey: counts[Math.floor((counts.length - 1) / 2)],\n estimatedDistinctKeys: maxDistinctBelow + 1,\n });\n }\n return {\n samples: samples.length,\n // Every sample of an index describes the same columns.\n sampleKinds: samples[0].sample,\n perPrefix,\n };\n}\n\nfunction sqliteVersion(db: Database): string {\n const [{version}] = db\n .prepare(`SELECT sqlite_version() AS version`)\n .all() as {version: string}[];\n return version;\n}\n\nfunction replicaWatermark(lc: LogContext, db: Database): string | undefined {\n try {\n return getReplicationState(new StatementRunner(db)).stateVersion;\n } catch (e) {\n // A replica that has not finished initial sync has no replication state.\n lc.debug?.('could not read replication state', e);\n return undefined;\n }\n}\n\n/**\n * Serves the replica's schema, indexes and SQLite statistics for consumption by\n * a language model, so that it can advise on query rewrites and indexes.\n *\n * Row data is never returned. See `designs/003_replica_stats_endpoint.md`.\n *\n * This is available to the operator password as well as the admin password\n * (see `--operator-password`). That does not extend to `/plannerz/analyze`,\n * which runs caller-supplied queries against the replica and so must require\n * the admin password (`isAdminPasswordValid`).\n *\n * HTTP query parameters:\n * * `pretty`: indents the JSON\n * * `stat4=full`: includes the full sqlite_stat4 histogram, which is large\n */\nexport async function handlePlannerzRequest(\n lc: LogContext,\n config: ZeroConfig,\n req: FastifyRequest,\n res: FastifyReply,\n) {\n const credentials = auth(req);\n if (getOperatorAccess(lc, config, credentials?.pass) === undefined) {\n void res\n .code(401)\n .header('WWW-Authenticate', 'Basic realm=\"Plannerz Protected Area\"')\n .send('Unauthorized');\n return;\n }\n\n const query = req.query as Record<string, unknown>;\n const indent = query.pretty !== undefined ? 2 : undefined;\n let bundle: PlannerzBundle;\n try {\n bundle = buildPlannerzBundle(lc, config, {\n fullStat4: query.stat4 === 'full',\n });\n } catch (e) {\n lc.error?.('error building plannerz bundle', e);\n await res.code(500).send({error: String(e)});\n return;\n }\n await res\n .header('Content-Type', 'application/json')\n .send(JSON.stringify(bundle, null, indent));\n}\n"],"mappings":"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"}
|
|
@@ -18,7 +18,8 @@ import auth from "basic-auth";
|
|
|
18
18
|
* sources are redacted for `operator` access. See {@link redactProfile}.
|
|
19
19
|
*/
|
|
20
20
|
async function handleProfzRequest(lc, config, req, res, getWorker, forcedWorker, localProcessName = "dispatcher") {
|
|
21
|
-
const
|
|
21
|
+
const credentials = auth(req);
|
|
22
|
+
const access = getOperatorAccess(lc, config, credentials?.pass);
|
|
22
23
|
if (access === void 0) {
|
|
23
24
|
res.code(401).header("WWW-Authenticate", "Basic realm=\"Profz Protected Area\"").send("Unauthorized");
|
|
24
25
|
return;
|
|
@@ -99,7 +100,8 @@ async function handleProfzRequest(lc, config, req, res, getWorker, forcedWorker,
|
|
|
99
100
|
res.header("Content-Type", "application/json").header("Content-Disposition", `attachment; filename="zero-profile-bundle-${Date.now()}.json"`).send(bundle);
|
|
100
101
|
}
|
|
101
102
|
async function handleProfrmzRequest(lc, config, req, res, getWorker) {
|
|
102
|
-
|
|
103
|
+
const credentials = auth(req);
|
|
104
|
+
if (getOperatorAccess(lc, config, credentials?.pass) === void 0) {
|
|
103
105
|
res.code(401).header("WWW-Authenticate", "Basic realm=\"Profrmz Protected Area\"").send("Unauthorized");
|
|
104
106
|
return;
|
|
105
107
|
}
|
|
@@ -158,6 +160,6 @@ function redactProfile(profile) {
|
|
|
158
160
|
};
|
|
159
161
|
}
|
|
160
162
|
//#endregion
|
|
161
|
-
export { handleProfrmzRequest, handleProfzRequest };
|
|
163
|
+
export { handleProfrmzRequest, handleProfzRequest, redactProfile };
|
|
162
164
|
|
|
163
165
|
//# sourceMappingURL=profz.js.map
|