@rocicorp/zero 0.26.0-canary.0 → 0.26.0-canary.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/out/replicache/src/persist/collect-idb-databases.d.ts +4 -4
- package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +22 -19
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/refresh.d.ts.map +1 -1
- package/out/replicache/src/persist/refresh.js +0 -8
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/process-scheduler.d.ts +23 -0
- package/out/replicache/src/process-scheduler.d.ts.map +1 -1
- package/out/replicache/src/process-scheduler.js +50 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +8 -0
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +11 -2
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/shared/src/custom-key-map.d.ts +4 -4
- package/out/shared/src/custom-key-map.d.ts.map +1 -1
- package/out/shared/src/custom-key-map.js.map +1 -1
- package/out/shared/src/falsy.d.ts +3 -0
- package/out/shared/src/falsy.d.ts.map +1 -0
- package/out/shared/src/iterables.d.ts +6 -8
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js +13 -7
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/options.d.ts +1 -0
- package/out/shared/src/options.d.ts.map +1 -1
- package/out/shared/src/options.js +5 -1
- package/out/shared/src/options.js.map +1 -1
- package/out/zero/package.json.js +1 -1
- package/out/zero/src/adapters/drizzle.js +1 -2
- package/out/zero/src/adapters/prisma.d.ts +2 -0
- package/out/zero/src/adapters/prisma.d.ts.map +1 -0
- package/out/zero/src/adapters/prisma.js +6 -0
- package/out/zero/src/adapters/prisma.js.map +1 -0
- package/out/zero/src/pg.js +4 -7
- package/out/zero/src/react.js +3 -1
- package/out/zero/src/react.js.map +1 -1
- package/out/zero/src/server.js +5 -8
- package/out/zero/src/zero-cache-dev.js +7 -3
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.d.ts +3 -2
- package/out/zero-cache/src/auth/load-permissions.d.ts.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js +14 -8
- package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts +6 -0
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +16 -3
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +54 -9
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +80 -20
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +3 -0
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +26 -0
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js +1 -1
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +9 -3
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration.js +9 -3
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/specs.d.ts +4 -3
- package/out/zero-cache/src/db/specs.d.ts.map +1 -1
- package/out/zero-cache/src/db/specs.js +4 -1
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js +9 -3
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/observability/events.d.ts.map +1 -1
- package/out/zero-cache/src/observability/events.js +15 -5
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +10 -2
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts +1 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +11 -30
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/main.js +1 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/priority-op.d.ts +8 -0
- package/out/zero-cache/src/server/priority-op.d.ts.map +1 -0
- package/out/zero-cache/src/server/priority-op.js +29 -0
- package/out/zero-cache/src/server/priority-op.js.map +1 -0
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +10 -10
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/analyze.js +1 -1
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +4 -7
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +68 -13
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +7 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +7 -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/schema/ddl.d.ts +125 -180
- 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 +1 -10
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +26 -12
- 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.d.ts +36 -90
- package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js +51 -14
- 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/shard.d.ts +31 -36
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +25 -17
- 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 +2 -2
- 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 +2 -4
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +158 -53
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js +55 -10
- 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 +210 -72
- 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.js +4 -2
- package/out/zero-cache/src/services/change-source/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/replica-schema.js +20 -4
- package/out/zero-cache/src/services/change-source/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +6 -4
- 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.d.ts +71 -25
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +1 -0
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +6 -5
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +17 -6
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +2 -0
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +14 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/heapz.d.ts.map +1 -1
- package/out/zero-cache/src/services/heapz.js +1 -0
- package/out/zero-cache/src/services/heapz.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +3 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/config.yml +1 -0
- package/out/zero-cache/src/services/mutagen/error.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/error.js +4 -1
- package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +4 -4
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +10 -24
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +8 -6
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +130 -19
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +24 -31
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +4 -4
- 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 +38 -36
- package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
- package/out/zero-cache/src/services/{change-source → replicator/schema}/column-metadata.d.ts +3 -3
- package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts.map +1 -0
- package/out/zero-cache/src/services/{change-source → replicator/schema}/column-metadata.js +3 -3
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -0
- package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +3 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.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/statz.d.ts.map +1 -1
- package/out/zero-cache/src/services/statz.js +1 -0
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +5 -6
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +5 -23
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +65 -44
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +0 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +23 -6
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +14 -22
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +46 -67
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +22 -11
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +0 -2
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +3 -11
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +6 -4
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +216 -243
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/lexi-version.d.ts.map +1 -1
- package/out/zero-cache/src/types/lexi-version.js +4 -1
- package/out/zero-cache/src/types/lexi-version.js.map +1 -1
- package/out/zero-cache/src/types/lite.d.ts.map +1 -1
- package/out/zero-cache/src/types/lite.js +8 -2
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/shards.js +1 -1
- package/out/zero-cache/src/types/shards.js.map +1 -1
- package/out/zero-cache/src/types/sql.d.ts +5 -0
- package/out/zero-cache/src/types/sql.d.ts.map +1 -1
- package/out/zero-cache/src/types/sql.js +5 -1
- package/out/zero-cache/src/types/sql.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/workers/connect-params.d.ts +1 -1
- package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connect-params.js +2 -3
- package/out/zero-cache/src/workers/connect-params.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +2 -5
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +15 -10
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +17 -10
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.d.ts +8 -0
- package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +33 -0
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js +6 -3
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.js +1 -0
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/error.js +1 -1
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.js +15 -1
- package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +11 -1
- 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.d.ts +4 -0
- package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/query-manager.js +7 -0
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts +5 -5
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +53 -8
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.d.ts +1 -0
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.d.ts +4 -0
- package/out/zero-protocol/src/connect.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.js +3 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
- package/out/zero-protocol/src/protocol-version.js +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +16 -0
- package/out/zero-protocol/src/push.d.ts.map +1 -1
- package/out/zero-protocol/src/push.js +25 -1
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/up.d.ts +2 -0
- package/out/zero-protocol/src/up.d.ts.map +1 -1
- package/out/zero-react/src/mod.d.ts +3 -1
- package/out/zero-react/src/mod.d.ts.map +1 -1
- package/out/zero-react/src/paging-reducer.d.ts +61 -0
- package/out/zero-react/src/paging-reducer.d.ts.map +1 -0
- package/out/zero-react/src/paging-reducer.js +77 -0
- package/out/zero-react/src/paging-reducer.js.map +1 -0
- package/out/zero-react/src/use-query.d.ts +11 -1
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-query.js +13 -11
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/use-rows.d.ts +39 -0
- package/out/zero-react/src/use-rows.d.ts.map +1 -0
- package/out/zero-react/src/use-rows.js +130 -0
- package/out/zero-react/src/use-rows.js.map +1 -0
- package/out/zero-react/src/use-zero-virtualizer.d.ts +122 -0
- package/out/zero-react/src/use-zero-virtualizer.d.ts.map +1 -0
- package/out/zero-react/src/use-zero-virtualizer.js +342 -0
- package/out/zero-react/src/use-zero-virtualizer.js.map +1 -0
- package/out/zero-react/src/zero-provider.js +1 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-server/src/adapters/drizzle.d.ts +18 -18
- package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
- package/out/zero-server/src/adapters/drizzle.js +8 -22
- package/out/zero-server/src/adapters/drizzle.js.map +1 -1
- package/out/zero-server/src/adapters/pg.d.ts +19 -13
- package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
- package/out/zero-server/src/adapters/pg.js.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.d.ts +19 -13
- package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-server/src/adapters/prisma.d.ts +66 -0
- package/out/zero-server/src/adapters/prisma.d.ts.map +1 -0
- package/out/zero-server/src/adapters/prisma.js +63 -0
- package/out/zero-server/src/adapters/prisma.js.map +1 -0
- package/out/zero-server/src/custom.js +1 -15
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/mod.d.ts +9 -8
- package/out/zero-server/src/mod.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.d.ts +2 -1
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +39 -4
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.js +1 -1
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/schema.d.ts.map +1 -1
- package/out/zero-server/src/schema.js +4 -1
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zero-server/src/zql-database.d.ts.map +1 -1
- package/out/zero-server/src/zql-database.js +18 -0
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/mod.d.ts +1 -1
- package/out/zero-solid/src/mod.d.ts.map +1 -1
- package/out/zero-solid/src/solid-view.js +1 -0
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-query.d.ts +10 -1
- package/out/zero-solid/src/use-query.d.ts.map +1 -1
- package/out/zero-solid/src/use-query.js +22 -5
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-zero.js +1 -1
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zql/src/ivm/constraint.d.ts.map +1 -1
- package/out/zql/src/ivm/constraint.js +4 -1
- package/out/zql/src/ivm/constraint.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +4 -1
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
- package/out/zql/src/ivm/join-utils.js +8 -2
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +12 -3
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js +25 -2
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/stream.d.ts.map +1 -1
- package/out/zql/src/ivm/stream.js +1 -1
- package/out/zql/src/ivm/stream.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +24 -6
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +12 -3
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/mutate/mutator.js +4 -4
- package/out/zql/src/mutate/mutator.js.map +1 -1
- package/out/zql/src/query/create-builder.js +3 -5
- package/out/zql/src/query/create-builder.js.map +1 -1
- package/out/zql/src/query/query-registry.js +4 -4
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +1 -2
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +8 -4
- package/out/zero-cache/src/services/change-source/column-metadata.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/column-metadata.js.map +0 -1
- package/out/zero-cache/src/types/schema-versions.d.ts +0 -12
- package/out/zero-cache/src/types/schema-versions.d.ts.map +0 -1
- package/out/zero-cache/src/types/schema-versions.js +0 -28
- package/out/zero-cache/src/types/schema-versions.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zero-config.js","sources":["../../../../../zero-cache/src/config/zero-config.ts"],"sourcesContent":["/**\n * These types represent the _compiled_ config whereas `define-config` types represent the _source_ config.\n */\n\nimport type {LogContext} from '@rocicorp/logger';\nimport {logOptions} from '../../../otel/src/log-options.ts';\nimport {\n parseOptions,\n type Config,\n type ParseOptions,\n} from '../../../shared/src/options.ts';\nimport * as v from '../../../shared/src/valita.ts';\n// @circular-dep-ignore - importing package.json for version info only\nimport packageJson from '../../../zero/package.json' with {type: 'json'};\nimport {runtimeDebugFlags} from '../../../zql/src/builder/debug-delegate.ts';\nimport {singleProcessMode} from '../types/processes.ts';\nimport {\n ALLOWED_APP_ID_CHARACTERS,\n INVALID_APP_ID_MESSAGE,\n} from '../types/shards.ts';\nimport {\n assertNormalized,\n isDevelopmentMode,\n type NormalizedZeroConfig,\n} from './normalize.ts';\nexport type {LogConfig} from '../../../otel/src/log-options.ts';\n\nexport const appOptions = {\n id: {\n type: v\n .string()\n .default('zero')\n .assert(id => ALLOWED_APP_ID_CHARACTERS.test(id), INVALID_APP_ID_MESSAGE),\n desc: [\n 'Unique identifier for the app.',\n '',\n 'Multiple zero-cache apps can run on a single upstream database, each of which',\n 'is isolated from the others, with its own permissions, sharding (future feature),',\n 'and change/cvr databases.',\n '',\n 'The metadata of an app is stored in an upstream schema with the same name,',\n 'e.g. \"zero\", and the metadata for each app shard, e.g. client and mutation',\n 'ids, is stored in the \"\\\\{app-id\\\\}_\\\\{#\\\\}\" schema. (Currently there is only a single',\n '\"0\" shard, but this will change with sharding).',\n '',\n 'The CVR and Change data are managed in schemas named \"\\\\{app-id\\\\}_\\\\{shard-num\\\\}/cvr\"',\n 'and \"\\\\{app-id\\\\}_\\\\{shard-num\\\\}/cdc\", respectively, allowing multiple apps and shards',\n 'to share the same database instance (e.g. a Postgres \"cluster\") for CVR and Change management.',\n '',\n 'Due to constraints on replication slot names, an App ID may only consist of',\n 'lower-case letters, numbers, and the underscore character.',\n '',\n 'Note that this option is used by both {bold zero-cache} and {bold zero-deploy-permissions}.',\n ],\n },\n\n publications: {\n type: v.array(v.string()).optional(() => []),\n desc: [\n `Postgres {bold PUBLICATION}s that define the tables and columns to`,\n `replicate. Publication names may not begin with an underscore,`,\n `as zero reserves that prefix for internal use.`,\n ``,\n `If unspecified, zero-cache will create and use an internal publication that`,\n `publishes all tables in the {bold public} schema, i.e.:`,\n ``,\n `CREATE PUBLICATION _\\\\{app-id\\\\}_public_0 FOR TABLES IN SCHEMA public;`,\n ``,\n `Note that changing the set of publications will result in resyncing the replica,`,\n `which may involve downtime (replication lag) while the new replica is initializing.`,\n `To change the set of publications without disrupting an existing app, a new app`,\n `should be created.`,\n ],\n },\n};\n\nexport const shardOptions = {\n id: {\n type: v\n .string()\n .assert(() => {\n throw new Error(\n `ZERO_SHARD_ID is no longer an option. Please use ZERO_APP_ID instead.`,\n // TODO: Link to release / migration notes?\n );\n })\n .optional(),\n hidden: true,\n },\n\n num: {\n type: v.number().default(0),\n desc: [\n `The shard number (from 0 to NUM_SHARDS) of the App. zero will eventually`,\n `support data sharding as a first-class primitive; until then, deploying`,\n `multiple shard-nums creates functionally identical shards. Until sharding is`,\n `actually meaningful, this flag is hidden but available for testing.`,\n ],\n hidden: true,\n },\n};\n\nconst replicaOptions = {\n file: {\n type: v.string().default('zero.db'),\n desc: [\n `File path to the SQLite replica that zero-cache maintains.`,\n `This can be lost, but if it is, zero-cache will have to re-replicate next`,\n `time it starts up.`,\n ],\n },\n\n vacuumIntervalHours: {\n type: v.number().optional(),\n desc: [\n `Performs a VACUUM at server startup if the specified number of hours has elapsed`,\n `since the last VACUUM (or initial-sync). The VACUUM operation is heavyweight`,\n `and requires double the size of the db in disk space. If unspecified, VACUUM`,\n `operations are not performed.`,\n ],\n },\n\n pageCacheSizeKib: {\n type: v.number().optional(),\n desc: [\n `The SQLite page cache size in kibibytes (KiB) for view-syncer connections.`,\n `The page cache stores recently accessed database pages in memory to reduce disk I/O.`,\n `Larger cache sizes improve performance for workloads that fit in cache.`,\n `If unspecified, SQLite's default (~2 MB) is used.`,\n `Note that the effective memory use of this setting will be:`,\n `2 * cache_size * num_cores as each connection to the replica gets its own cache`,\n `and each core maintains 2 connections.`,\n ],\n },\n};\n\nexport type ReplicaOptions = Config<typeof replicaOptions>;\n\nconst perUserMutationLimit = {\n max: {\n type: v.number().optional(),\n desc: [\n `The maximum mutations per user within the specified {bold windowMs}.`,\n `If unset, no rate limiting is enforced.`,\n ],\n },\n windowMs: {\n type: v.number().default(60_000),\n desc: [\n `The sliding window over which the {bold perUserMutationLimitMax} is enforced.`,\n ],\n },\n};\n\nexport type RateLimit = Config<typeof perUserMutationLimit>;\n\nconst authOptions = {\n jwk: {\n type: v.string().optional(),\n desc: [\n `A public key in JWK format used to verify JWTs. Only one of {bold jwk}, {bold jwksUrl} and {bold secret} may be set.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n jwksUrl: {\n type: v.string().optional(),\n desc: [\n `A URL that returns a JWK set used to verify JWTs. Only one of {bold jwk}, {bold jwksUrl} and {bold secret} may be set.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n secret: {\n type: v.string().optional(),\n desc: [\n `A symmetric key used to verify JWTs. Only one of {bold jwk}, {bold jwksUrl} and {bold secret} may be set.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n};\n\nconst makeMutatorQueryOptions = (\n replacement: 'mutate-url' | 'query-url' | undefined,\n suffix: string,\n) => ({\n url: {\n type: v.array(v.string()).optional(), // optional until we remove CRUD mutations\n desc: [\n `The URL of the API server to which zero-cache will ${suffix}.`,\n ``,\n `{bold IMPORTANT:} URLs are matched using {bold URLPattern}, a standard Web API.`,\n ``,\n `{bold Pattern Syntax:}`,\n ` URLPattern uses a simple and intuitive syntax similar to Express routes.`,\n ` Wildcards and named parameters make it easy to match multiple URLs.`,\n ``,\n `{bold Basic Examples:}`,\n ` Exact URL match:`,\n ` \"https://api.example.com/mutate\"`,\n ` `,\n ` Any subdomain using wildcard:`,\n ` \"https://*.example.com/mutate\"`,\n ` `,\n ` Multiple subdomain levels:`,\n ` \"https://*.*.example.com/mutate\"`,\n ` `,\n ` Any path under a domain:`,\n ` \"https://api.example.com/*\"`,\n ` `,\n ` Named path parameters:`,\n ` \"https://api.example.com/:version/mutate\"`,\n ` ↳ Matches \"https://api.example.com/v1/mutate\", \"https://api.example.com/v2/mutate\", etc.`,\n ``,\n `{bold Advanced Patterns:}`,\n ` Optional path segments:`,\n ` \"https://api.example.com/:path?\"`,\n ` `,\n ` Regex in segments (for specific patterns):`,\n ` \"https://api.example.com/:version(v\\\\\\\\d+)/mutate\"`,\n ` ↳ Matches only \"v\" followed by digits`,\n ``,\n `{bold Multiple patterns:}`,\n ` [\"https://api1.example.com/mutate\", \"https://api2.example.com/mutate\"]`,\n ``,\n `{bold Note:} Query parameters and URL fragments (#) are automatically ignored during matching.`,\n ``,\n `For full URLPattern syntax, see: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern`,\n ],\n ...(replacement\n ? {deprecated: [`Use {bold ${replacement}} instead.`]}\n : {}),\n },\n apiKey: {\n type: v.string().optional(),\n desc: [\n `An optional secret used to authorize zero-cache to call the API server handling writes.`,\n ],\n ...(replacement\n ? {deprecated: [`Use {bold ${replacement}-api-key} instead.`]}\n : {}),\n },\n forwardCookies: {\n type: v.boolean().default(false),\n desc: [\n `If true, zero-cache will forward cookies from the request.`,\n `This is useful for passing authentication cookies to the API server.`,\n `If false, cookies are not forwarded.`,\n ],\n ...(replacement\n ? {deprecated: [`Use {bold ${replacement}-forward-cookies} instead.`]}\n : {}),\n },\n});\n\nconst mutateOptions = makeMutatorQueryOptions(undefined, 'push mutations');\nconst pushOptions = makeMutatorQueryOptions('mutate-url', 'push mutations');\nconst queryOptions = makeMutatorQueryOptions(undefined, 'send synced queries');\nconst getQueriesOptions = makeMutatorQueryOptions(\n 'query-url',\n 'send synced queries',\n);\n\n/** @deprecated */\nexport type AuthConfig = Config<typeof authOptions>;\n\n// Note: --help will list flags in the order in which they are defined here,\n// so order the fields such that the important (e.g. required) ones are first.\n// (Exported for testing)\nexport const zeroOptions = {\n upstream: {\n db: {\n type: v.string(),\n desc: [\n `The \"upstream\" authoritative postgres database.`,\n `In the future we will support other types of upstream besides PG.`,\n ],\n },\n\n type: {\n type: v.literalUnion('pg', 'custom').default('pg'),\n desc: [\n `The meaning of the {bold upstream-db} depends on the upstream type:`,\n `* {bold pg}: The connection database string, e.g. \"postgres://...\"`,\n `* {bold custom}: The base URI of the change source \"endpoint, e.g.`,\n ` \"https://my-change-source.dev/changes/v0/stream?apiKey=...\"`,\n ],\n hidden: true, // TODO: Unhide when ready to officially support.\n },\n\n maxConns: {\n type: v.number().default(20),\n desc: [\n `The maximum number of connections to open to the upstream database`,\n `for committing mutations. This is divided evenly amongst sync workers.`,\n `In addition to this number, zero-cache uses one connection for the`,\n `replication stream.`,\n ``,\n `Note that this number must allow for at least one connection per`,\n `sync worker, or zero-cache will fail to start. See {bold num-sync-workers}`,\n ],\n },\n\n maxConnsPerWorker: {\n type: v.number().optional(),\n hidden: true, // Passed from main thread to sync workers\n },\n },\n\n /** @deprecated */\n push: pushOptions,\n mutate: mutateOptions,\n /** @deprecated */\n getQueries: getQueriesOptions,\n query: queryOptions,\n\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n `The Postgres database used to store CVRs. CVRs (client view records) keep track`,\n `of the data synced to clients in order to determine the diff to send on reconnect.`,\n `If unspecified, the {bold upstream-db} will be used.`,\n ],\n },\n\n maxConns: {\n type: v.number().default(30),\n desc: [\n `The maximum number of connections to open to the CVR database.`,\n `This is divided evenly amongst sync workers.`,\n ``,\n `Note that this number must allow for at least one connection per`,\n `sync worker, or zero-cache will fail to start. See {bold num-sync-workers}`,\n ],\n },\n\n maxConnsPerWorker: {\n type: v.number().optional(),\n hidden: true, // Passed from main thread to sync workers\n },\n\n garbageCollectionInactivityThresholdHours: {\n type: v.number().default(48),\n desc: [\n `The duration after which an inactive CVR is eligible for garbage collection.`,\n `Note that garbage collection is an incremental, periodic process which does not`,\n `necessarily purge all eligible CVRs immediately.`,\n ],\n },\n\n garbageCollectionInitialIntervalSeconds: {\n type: v.number().default(60),\n desc: [\n `The initial interval at which to check and garbage collect inactive CVRs.`,\n `This interval is increased exponentially (up to 16 minutes) when there is`,\n `nothing to purge.`,\n ],\n },\n\n garbageCollectionInitialBatchSize: {\n type: v.number().default(25),\n desc: [\n `The initial number of CVRs to purge per garbage collection interval.`,\n `This number is increased linearly if the rate of new CVRs exceeds the rate of`,\n `purged CVRs, in order to reach a steady state.`,\n ``,\n `Setting this to 0 effectively disables CVR garbage collection.`,\n ],\n },\n },\n\n queryHydrationStats: {\n type: v.boolean().optional(),\n desc: [\n `Track and log the number of rows considered by query hydrations which`,\n `take longer than {bold log-slow-hydrate-threshold} milliseconds.`,\n `This is useful for debugging and performance tuning.`,\n ],\n },\n\n enableQueryPlanner: {\n type: v.boolean().default(true),\n desc: [\n `Enable the query planner for optimizing ZQL queries.`,\n ``,\n `The query planner analyzes and optimizes query execution by determining`,\n `the most efficient join strategies.`,\n ``,\n `You can disable the planner if it is picking bad strategies.`,\n ],\n },\n\n yieldThresholdMs: {\n type: v.number().default(10),\n desc: [\n `The maximum amount of time in milliseconds that a sync worker will`,\n `spend in IVM (processing query hydration and advancement) before yielding`,\n `to the event loop. Lower values increase responsiveness and fairness at`,\n `the cost of reduced throughput.`,\n ],\n },\n\n change: {\n db: {\n type: v.string().optional(),\n desc: [\n `The Postgres database used to store recent replication log entries, in order`,\n `to sync multiple view-syncers without requiring multiple replication slots on`,\n `the upstream database. If unspecified, the {bold upstream-db} will be used.`,\n ],\n },\n\n maxConns: {\n type: v.number().default(5),\n desc: [\n `The maximum number of connections to open to the change database.`,\n `This is used by the {bold change-streamer} for catching up`,\n `{bold zero-cache} replication subscriptions.`,\n ],\n },\n },\n\n replica: replicaOptions,\n\n log: logOptions,\n\n app: appOptions,\n\n shard: shardOptions,\n\n /** @deprecated */\n auth: authOptions,\n\n port: {\n type: v.number().default(4848),\n desc: [`The port for sync connections.`],\n },\n\n changeStreamer: {\n uri: {\n type: v.string().optional(),\n desc: [\n `When set, connects to the {bold change-streamer} at the given URI.`,\n `In a multi-node setup, this should be specified in {bold view-syncer} options,`,\n `pointing to the {bold replication-manager} URI, which runs a {bold change-streamer}`,\n `on port 4849.`,\n ],\n },\n\n mode: {\n type: v.literalUnion('dedicated', 'discover').default('dedicated'),\n desc: [\n `As an alternative to {bold ZERO_CHANGE_STREAMER_URI}, the {bold ZERO_CHANGE_STREAMER_MODE}`,\n `can be set to \"{bold discover}\" to instruct the {bold view-syncer} to connect to the `,\n `ip address registered by the {bold replication-manager} upon startup.`,\n ``,\n `This may not work in all networking configurations, e.g. certain private `,\n `networking or port forwarding configurations. Using the {bold ZERO_CHANGE_STREAMER_URI}`,\n `with an explicit routable hostname is recommended instead.`,\n ``,\n `Note: This option is ignored if the {bold ZERO_CHANGE_STREAMER_URI} is set.`,\n ],\n },\n\n port: {\n type: v.number().optional(),\n desc: [\n `The port on which the {bold change-streamer} runs. This is an internal`,\n `protocol between the {bold replication-manager} and {bold view-syncers}, which`,\n `runs in the same process tree in local development or a single-node configuration.`,\n ``,\n `If unspecified, defaults to {bold --port} + 1.`,\n ],\n },\n\n /** @deprecated */\n address: {\n type: v.string().optional(),\n deprecated: [\n `Set the {bold ZERO_CHANGE_STREAMER_URI} on view-syncers instead.`,\n ],\n hidden: true,\n },\n\n /** @deprecated */\n protocol: {\n type: v.literalUnion('ws', 'wss').default('ws'),\n deprecated: [\n `Set the {bold ZERO_CHANGE_STREAMER_URI} on view-syncers instead.`,\n ],\n hidden: true,\n },\n\n discoveryInterfacePreferences: {\n type: v.array(v.string()).default([\n 'eth', // linux\n 'en', // macbooks\n ]),\n desc: [\n `The name prefixes to prefer when introspecting the network interfaces to determine`,\n `the externally reachable IP address for change-streamer discovery. This defaults`,\n `to commonly used names for standard ethernet interfaces in order to prevent selecting`,\n `special interfaces such as those for VPNs.`,\n ],\n // More confusing than it's worth to advertise this. The default list should be\n // adjusted to make things work for all environments; it is controlled as a\n // hidden flag as an emergency to unblock people with outlier network configs.\n hidden: true,\n },\n\n startupDelayMs: {\n type: v.number().default(15000),\n desc: [\n `The delay to wait before the change-streamer takes over the replication stream`,\n `(i.e. the handoff during replication-manager updates), to allow loadbalancers to register`,\n `the task as healthy based on healthcheck parameters. Note that if a change stream request`,\n `is received during this interval, the delay will be canceled and the takeover will happen`,\n `immediately, since the incoming request indicates that the task is registered as a target.`,\n ],\n },\n },\n\n taskID: {\n type: v.string().optional(),\n desc: [\n `Globally unique identifier for the zero-cache instance.`,\n ``,\n `Setting this to a platform specific task identifier can be useful for debugging.`,\n `If unspecified, zero-cache will attempt to extract the TaskARN if run from within`,\n `an AWS ECS container, and otherwise use a random string.`,\n ],\n },\n\n perUserMutationLimit,\n\n numSyncWorkers: {\n type: v.number().optional(),\n desc: [\n `The number of processes to use for view syncing.`,\n `Leave this unset to use the maximum available parallelism.`,\n `If set to 0, the server runs without sync workers, which is the`,\n `configuration for running the {bold replication-manager}.`,\n ],\n },\n\n autoReset: {\n type: v.boolean().default(true),\n desc: [\n `Automatically wipe and resync the replica when replication is halted.`,\n `This situation can occur for configurations in which the upstream database`,\n `provider prohibits event trigger creation, preventing the zero-cache from`,\n `being able to correctly replicate schema changes. For such configurations,`,\n `an upstream schema change will instead result in halting replication with an`,\n `error indicating that the replica needs to be reset.`,\n ``,\n `When {bold auto-reset} is enabled, zero-cache will respond to such situations`,\n `by shutting down, and when restarted, resetting the replica and all synced `,\n `clients. This is a heavy-weight operation and can result in user-visible`,\n `slowness or downtime if compute resources are scarce.`,\n ],\n },\n\n adminPassword: {\n type: v.string().optional(),\n desc: [\n `A password used to administer zero-cache server, for example to access the`,\n `/statz endpoint.`,\n '',\n 'A password is optional in development mode but {bold required in production} mode.',\n ],\n },\n\n websocketCompression: {\n type: v.boolean().default(false),\n desc: [\n 'Enable WebSocket per-message deflate compression.',\n '',\n 'Compression can reduce bandwidth usage for sync traffic but',\n 'increases CPU usage on both client and server. Disabled by default.',\n '',\n 'See: https://github.com/websockets/ws#websocket-compression',\n ],\n },\n\n websocketCompressionOptions: {\n type: v.string().optional(),\n desc: [\n 'JSON string containing WebSocket compression options.',\n '',\n 'Only used if websocketCompression is enabled.',\n '',\n 'Example: \\\\{\"zlibDeflateOptions\":\\\\{\"level\":3\\\\},\"threshold\":1024\\\\}',\n '',\n 'See https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback for available options.',\n ],\n },\n\n litestream: {\n executable: {\n type: v.string().optional(),\n desc: [`Path to the {bold litestream} executable.`],\n },\n\n configPath: {\n type: v.string().default('./src/services/litestream/config.yml'),\n desc: [\n `Path to the litestream yaml config file. zero-cache will run this with its`,\n `environment variables, which can be referenced in the file via $\\\\{ENV\\\\}`,\n `substitution, for example:`,\n `* {bold ZERO_REPLICA_FILE} for the db path`,\n `* {bold ZERO_LITESTREAM_BACKUP_LOCATION} for the db replica url`,\n `* {bold ZERO_LITESTREAM_LOG_LEVEL} for the log level`,\n `* {bold ZERO_LOG_FORMAT} for the log type`,\n ],\n },\n\n logLevel: {\n type: v.literalUnion('debug', 'info', 'warn', 'error').default('warn'),\n },\n\n backupURL: {\n type: v.string().optional(),\n desc: [\n `The location of the litestream backup, usually an {bold s3://} URL.`,\n `This is only consulted by the {bold replication-manager}.`,\n `{bold view-syncers} receive this information from the {bold replication-manager}.`,\n ],\n },\n\n port: {\n type: v.number().optional(),\n desc: [\n `Port on which litestream exports metrics, used to determine the replication`,\n `watermark up to which it is safe to purge change log records.`,\n ``,\n `If unspecified, defaults to {bold --port} + 2.`,\n ],\n },\n\n checkpointThresholdMB: {\n type: v.number().default(40),\n desc: [\n `The size of the WAL file at which to perform an SQlite checkpoint to apply`,\n `the writes in the WAL to the main database file. Each checkpoint creates`,\n `a new WAL segment file that will be backed up by litestream. Smaller thresholds`,\n `may improve read performance, at the expense of creating more files to download`,\n `when restoring the replica from the backup.`,\n ],\n },\n\n minCheckpointPageCount: {\n type: v.number().optional(),\n desc: [\n `The WAL page count at which SQLite attempts a PASSIVE checkpoint, which`,\n `transfers pages to the main database file without blocking writers.`,\n `Defaults to {bold checkpointThresholdMB * 250} (since SQLite page size is 4KB).`,\n ],\n },\n\n maxCheckpointPageCount: {\n type: v.number().optional(),\n desc: [\n `The WAL page count at which SQLite performs a RESTART checkpoint, which`,\n `blocks writers until complete. Defaults to {bold minCheckpointPageCount * 10}.`,\n `Set to {bold 0} to disable RESTART checkpoints entirely.`,\n ],\n },\n\n incrementalBackupIntervalMinutes: {\n type: v.number().default(15),\n desc: [\n `The interval between incremental backups of the replica. Shorter intervals`,\n `reduce the amount of change history that needs to be replayed when catching`,\n `up a new view-syncer, at the expense of increasing the number of files needed`,\n `to download for the initial litestream restore.`,\n ],\n },\n\n snapshotBackupIntervalHours: {\n type: v.number().default(12),\n desc: [\n `The interval between snapshot backups of the replica. Snapshot backups`,\n `make a full copy of the database to a new litestream generation. This`,\n `improves restore time at the expense of bandwidth. Applications with a`,\n `large database and low write rate can increase this interval to reduce`,\n `network usage for backups (litestream defaults to 24 hours).`,\n ],\n },\n\n restoreParallelism: {\n type: v.number().default(48),\n desc: [\n `The number of WAL files to download in parallel when performing the`,\n `initial restore of the replica from the backup.`,\n ],\n },\n\n multipartConcurrency: {\n type: v.number().default(48),\n desc: [\n `The number of parts (of size {bold --litestream-multipart-size} bytes)`,\n `to upload or download in parallel when backing up or restoring the snapshot.`,\n ],\n },\n\n multipartSize: {\n type: v.number().default(16 * 1024 * 1024),\n desc: [\n `The size of each part when uploading or downloading the snapshot with`,\n `{bold --multipart-concurrency}. Note that up to {bold concurrency * size}`,\n `bytes of memory are used when backing up or restoring the snapshot.`,\n ],\n },\n },\n\n storageDBTmpDir: {\n type: v.string().optional(),\n desc: [\n `tmp directory for IVM operator storage. Leave unset to use os.tmpdir()`,\n ],\n },\n\n initialSync: {\n tableCopyWorkers: {\n type: v.number().default(5),\n desc: [\n `The number of parallel workers used to copy tables during initial sync.`,\n `Each worker uses a database connection and will buffer up to (approximately)`,\n `10 MB of table data in memory during initial sync. Increasing the number of`,\n `workers may improve initial sync speed; however, note that local disk throughput`,\n `(i.e. IOPS), upstream CPU, and network bandwidth may also be bottlenecks.`,\n ],\n },\n\n profileCopy: {\n type: v.boolean().optional(),\n hidden: true,\n desc: [\n `Takes a cpu profile during the copy phase initial-sync, storing it as a JSON file`,\n `initial-copy.cpuprofile in the tmp directory.`,\n ],\n },\n },\n\n /** @deprecated */\n targetClientRowCount: {\n type: v.number().default(20_000),\n deprecated: [\n 'This option is no longer used and will be removed in a future version.',\n 'The client-side cache no longer enforces a row limit. Instead, TTL-based expiration',\n 'automatically manages cache size to prevent unbounded growth.',\n ],\n hidden: true,\n },\n\n lazyStartup: {\n type: v.boolean().default(false),\n desc: [\n 'Delay starting the majority of zero-cache until first request.',\n '',\n 'This is mainly intended to avoid connecting to Postgres replication stream',\n 'until the first request is received, which can be useful i.e., for preview instances.',\n '',\n 'Currently only supported in single-node mode.',\n ],\n },\n\n serverVersion: {\n type: v.string().optional(),\n desc: [`The version string outputted to logs when the server starts up.`],\n },\n\n enableTelemetry: {\n type: v.boolean().default(true),\n desc: [\n `Set to false to opt out of telemetry collection.`,\n ``,\n `This helps us improve Zero by collecting anonymous usage data.`,\n `Setting the DO_NOT_TRACK environment variable also disables telemetry.`,\n ],\n },\n\n cloudEvent: {\n sinkEnv: {\n type: v.string().optional(),\n desc: [\n `ENV variable containing a URI to a CloudEvents sink. When set, ZeroEvents`,\n `will be published to the sink as the {bold data} field of CloudEvents.`,\n `The {bold source} field of the CloudEvents will be set to the {bold ZERO_TASK_ID},`,\n `along with any extension attributes specified by the {bold ZERO_CLOUD_EVENT_EXTENSION_OVERRIDES_ENV}.`,\n ``,\n `This configuration is modeled to easily integrate with a knative K_SINK binding,`,\n `(i.e. https://github.com/knative/eventing/blob/main/docs/spec/sources.md#sinkbinding).`,\n `However, any CloudEvents sink can be used.`,\n ],\n },\n\n extensionOverridesEnv: {\n type: v.string().optional(),\n desc: [\n `ENV variable containing a JSON stringified object with an {bold extensions} field`,\n `containing attributes that should be added or overridden on outbound CloudEvents.`,\n ``,\n `This configuration is modeled to easily integrate with a knative K_CE_OVERRIDES binding,`,\n `(i.e. https://github.com/knative/eventing/blob/main/docs/spec/sources.md#sinkbinding).`,\n ],\n },\n },\n};\n\nexport type ZeroConfig = Config<typeof zeroOptions>;\n\nexport const ZERO_ENV_VAR_PREFIX = 'ZERO_';\n\nlet loadedConfig: Config<typeof zeroOptions> | undefined;\n\nexport function getZeroConfig(\n opts: Omit<ParseOptions, 'envNamePrefix'> = {},\n): ZeroConfig {\n if (!loadedConfig || singleProcessMode()) {\n loadedConfig = parseOptions(zeroOptions, {\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n emitDeprecationWarnings: false, // overridden at the top level parse\n ...opts,\n });\n\n if (loadedConfig.queryHydrationStats) {\n runtimeDebugFlags.trackRowCountsVended = true;\n }\n }\n return loadedConfig;\n}\n\n/**\n * Same as {@link getZeroConfig}, with an additional check that the\n * config has already been normalized (i.e. by the top level server/runner).\n */\nexport function getNormalizedZeroConfig(\n opts: Omit<ParseOptions, 'envNamePrefix'> = {},\n): NormalizedZeroConfig {\n const config = getZeroConfig(opts);\n assertNormalized(config);\n return config;\n}\n\n/**\n * Gets the server version from the config if provided. Otherwise it gets it\n * from the Zero package.json.\n */\nexport function getServerVersion(\n config: Pick<ZeroConfig, 'serverVersion'> | undefined,\n): string {\n return config?.serverVersion ?? packageJson.version;\n}\n\nexport function isAdminPasswordValid(\n lc: LogContext,\n config: Pick<NormalizedZeroConfig, 'adminPassword'>,\n password: string | undefined,\n) {\n // If development mode, password is optional\n // We use process.env.NODE_ENV === 'development' as a sign that we're in\n // development mode, rather than a custom env var like ZERO_DEVELOPMENT_MODE,\n // because NODE_ENV is more standard and is already used by many tools.\n // Note that if NODE_ENV is not set, we assume production mode.\n\n if (!password && !config.adminPassword && isDevelopmentMode()) {\n warnOnce(\n lc,\n 'No admin password set; allowing access in development mode only',\n );\n return true;\n }\n\n if (!config.adminPassword) {\n lc.warn?.('No admin password set; denying access');\n return false;\n }\n\n if (password !== config.adminPassword) {\n lc.warn?.('Invalid admin password');\n return false;\n }\n\n lc.debug?.('Admin password accepted');\n return true;\n}\n\nlet hasWarned = false;\n\nfunction warnOnce(lc: LogContext, msg: string) {\n if (!hasWarned) {\n lc.warn?.(msg);\n hasWarned = true;\n }\n}\n\n// For testing purposes - reset the warning state\nexport function resetWarnOnceState() {\n hasWarned = false;\n}\n"],"names":["v.string","v.array","v.number","v.boolean","v.literalUnion"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"zero-config.js","sources":["../../../../../zero-cache/src/config/zero-config.ts"],"sourcesContent":["/**\n * These types represent the _compiled_ config whereas `define-config` types represent the _source_ config.\n */\n\nimport {timingSafeEqual} from 'node:crypto';\nimport type {LogContext} from '@rocicorp/logger';\nimport {logOptions} from '../../../otel/src/log-options.ts';\nimport {\n flagToEnv,\n parseOptions,\n type Config,\n type ParseOptions,\n} from '../../../shared/src/options.ts';\nimport * as v from '../../../shared/src/valita.ts';\n// @circular-dep-ignore - importing package.json for version info only\nimport packageJson from '../../../zero/package.json' with {type: 'json'};\nimport {runtimeDebugFlags} from '../../../zql/src/builder/debug-delegate.ts';\nimport {singleProcessMode} from '../types/processes.ts';\nimport {\n ALLOWED_APP_ID_CHARACTERS,\n INVALID_APP_ID_MESSAGE,\n} from '../types/shards.ts';\nimport {\n assertNormalized,\n isDevelopmentMode,\n type NormalizedZeroConfig,\n} from './normalize.ts';\nexport type {LogConfig} from '../../../otel/src/log-options.ts';\n\nexport const ZERO_ENV_VAR_PREFIX = 'ZERO_';\n\n// Technically, any threshold is fine because the point of back pressure\n// is to adjust the rate of incoming messages, and the size of the pending\n// work queue does not affect that mechanism.\n//\n// However, it is theoretically possible to exceed the available memory if\n// the size of changes is very large. This threshold can be improved by\n// roughly measuring the size of the enqueued contents and setting the\n// threshold based on available memory.\n//\n// TODO: switch to a message size-based thresholding when migrating over\n// to stringified JSON messages, which will bound the computation involved\n// in measuring the size of row messages.\nexport const DEFAULT_BACK_PRESSURE_THRESHOLD = 100_000;\n\nexport const appOptions = {\n id: {\n type: v\n .string()\n .default('zero')\n .assert(id => ALLOWED_APP_ID_CHARACTERS.test(id), INVALID_APP_ID_MESSAGE),\n desc: [\n 'Unique identifier for the app.',\n '',\n 'Multiple zero-cache apps can run on a single upstream database, each of which',\n 'is isolated from the others, with its own permissions, sharding (future feature),',\n 'and change/cvr databases.',\n '',\n 'The metadata of an app is stored in an upstream schema with the same name,',\n 'e.g. \"zero\", and the metadata for each app shard, e.g. client and mutation',\n 'ids, is stored in the \"\\\\{app-id\\\\}_\\\\{#\\\\}\" schema. (Currently there is only a single',\n '\"0\" shard, but this will change with sharding).',\n '',\n 'The CVR and Change data are managed in schemas named \"\\\\{app-id\\\\}_\\\\{shard-num\\\\}/cvr\"',\n 'and \"\\\\{app-id\\\\}_\\\\{shard-num\\\\}/cdc\", respectively, allowing multiple apps and shards',\n 'to share the same database instance (e.g. a Postgres \"cluster\") for CVR and Change management.',\n '',\n 'Due to constraints on replication slot names, an App ID may only consist of',\n 'lower-case letters, numbers, and the underscore character.',\n '',\n 'Note that this option is used by both {bold zero-cache} and {bold zero-deploy-permissions}.',\n ],\n },\n\n publications: {\n type: v.array(v.string()).optional(() => []),\n desc: [\n `Postgres {bold PUBLICATION}s that define the tables and columns to`,\n `replicate. Publication names may not begin with an underscore,`,\n `as zero reserves that prefix for internal use.`,\n ``,\n `If unspecified, zero-cache will create and use an internal publication that`,\n `publishes all tables in the {bold public} schema, i.e.:`,\n ``,\n `CREATE PUBLICATION _\\\\{app-id\\\\}_public_0 FOR TABLES IN SCHEMA public;`,\n ``,\n `Note that changing the set of publications will result in resyncing the replica,`,\n `which may involve downtime (replication lag) while the new replica is initializing.`,\n `To change the set of publications without disrupting an existing app, a new app`,\n `should be created.`,\n ],\n },\n};\n\nexport const shardOptions = {\n id: {\n type: v\n .string()\n .assert(() => {\n throw new Error(\n `ZERO_SHARD_ID is no longer an option. Please use ZERO_APP_ID instead.`,\n // TODO: Link to release / migration notes?\n );\n })\n .optional(),\n hidden: true,\n },\n\n num: {\n type: v.number().default(0),\n desc: [\n `The shard number (from 0 to NUM_SHARDS) of the App. zero will eventually`,\n `support data sharding as a first-class primitive; until then, deploying`,\n `multiple shard-nums creates functionally identical shards. Until sharding is`,\n `actually meaningful, this flag is hidden but available for testing.`,\n ],\n hidden: true,\n },\n};\n\nconst replicaOptions = {\n file: {\n type: v.string().default('zero.db'),\n desc: [\n `File path to the SQLite replica that zero-cache maintains.`,\n `This can be lost, but if it is, zero-cache will have to re-replicate next`,\n `time it starts up.`,\n ],\n },\n\n vacuumIntervalHours: {\n type: v.number().optional(),\n desc: [\n `Performs a VACUUM at server startup if the specified number of hours has elapsed`,\n `since the last VACUUM (or initial-sync). The VACUUM operation is heavyweight`,\n `and requires double the size of the db in disk space. If unspecified, VACUUM`,\n `operations are not performed.`,\n ],\n },\n};\n\nexport type ReplicaOptions = Config<typeof replicaOptions>;\n\nconst perUserMutationLimit = {\n max: {\n type: v.number().optional(),\n desc: [\n `The maximum mutations per user within the specified {bold windowMs}.`,\n `If unset, no rate limiting is enforced.`,\n ],\n },\n windowMs: {\n type: v.number().default(60_000),\n desc: [\n `The sliding window over which the {bold perUserMutationLimitMax} is enforced.`,\n ],\n },\n};\n\nexport type RateLimit = Config<typeof perUserMutationLimit>;\n\nconst authOptions = {\n jwk: {\n type: v.string().optional(),\n desc: [\n `A public key in JWK format used to verify JWTs. Only one of {bold jwk}, {bold jwksUrl} and {bold secret} may be set.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n jwksUrl: {\n type: v.string().optional(),\n desc: [\n `A URL that returns a JWK set used to verify JWTs. Only one of {bold jwk}, {bold jwksUrl} and {bold secret} may be set.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n secret: {\n type: v.string().optional(),\n desc: [\n `A symmetric key used to verify JWTs. Only one of {bold jwk}, {bold jwksUrl} and {bold secret} may be set.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n issuer: {\n type: v.string().optional(),\n desc: [\n `Expected issuer ({bold iss} claim) for JWT validation.`,\n `If set, tokens with a different or missing issuer will be rejected.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n audience: {\n type: v.string().optional(),\n desc: [\n `Expected audience ({bold aud} claim) for JWT validation.`,\n `If set, tokens with a different or missing audience will be rejected.`,\n ],\n deprecated: [\n `Use cookie-based authentication or an auth token instead - see https://zero.rocicorp.dev/docs/auth.`,\n ],\n },\n};\n\nconst makeDeprecationMessage = (flag: string) =>\n `Use {bold ${flagToEnv(ZERO_ENV_VAR_PREFIX, flag)}} (or {bold --${flag}}) instead.`;\n\nconst makeMutatorQueryOptions = (\n replacement: 'mutate' | 'query' | undefined,\n suffix: string,\n) => ({\n url: {\n type: v.array(v.string()).optional(), // optional until we remove CRUD mutations\n desc: [\n `The URL of the API server to which zero-cache will ${suffix}.`,\n ``,\n `{bold IMPORTANT:} URLs are matched using {bold URLPattern}, a standard Web API.`,\n ``,\n `{bold Pattern Syntax:}`,\n ` URLPattern uses a simple and intuitive syntax similar to Express routes.`,\n ` Wildcards and named parameters make it easy to match multiple URLs.`,\n ``,\n `{bold Basic Examples:}`,\n ` Exact URL match:`,\n ` \"https://api.example.com/mutate\"`,\n ` `,\n ` Any subdomain using wildcard:`,\n ` \"https://*.example.com/mutate\"`,\n ` `,\n ` Multiple subdomain levels:`,\n ` \"https://*.*.example.com/mutate\"`,\n ` `,\n ` Any path under a domain:`,\n ` \"https://api.example.com/*\"`,\n ` `,\n ` Named path parameters:`,\n ` \"https://api.example.com/:version/mutate\"`,\n ` ↳ Matches \"https://api.example.com/v1/mutate\", \"https://api.example.com/v2/mutate\", etc.`,\n ``,\n `{bold Advanced Patterns:}`,\n ` Optional path segments:`,\n ` \"https://api.example.com/:path?\"`,\n ` `,\n ` Regex in segments (for specific patterns):`,\n ` \"https://api.example.com/:version(v\\\\\\\\d+)/mutate\"`,\n ` ↳ Matches only \"v\" followed by digits`,\n ``,\n `{bold Multiple patterns:}`,\n ` [\"https://api1.example.com/mutate\", \"https://api2.example.com/mutate\"]`,\n ``,\n `{bold Note:} Query parameters and URL fragments (#) are automatically ignored during matching.`,\n ``,\n `For full URLPattern syntax, see: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern`,\n ],\n ...(replacement\n ? {deprecated: [makeDeprecationMessage(`${replacement}-url`)]}\n : {}),\n },\n apiKey: {\n type: v.string().optional(),\n desc: [\n `An optional secret used to authorize zero-cache to call the API server handling writes.`,\n ],\n ...(replacement\n ? {deprecated: [makeDeprecationMessage(`${replacement}-api-key`)]}\n : {}),\n },\n forwardCookies: {\n type: v.boolean().default(false),\n desc: [\n `If true, zero-cache will forward cookies from the request.`,\n `This is useful for passing authentication cookies to the API server.`,\n `If false, cookies are not forwarded.`,\n ],\n ...(replacement\n ? {deprecated: [makeDeprecationMessage(`${replacement}-forward-cookies`)]}\n : {}),\n },\n allowedClientHeaders: {\n type: v.array(v.string()).optional(),\n desc: [\n `A list of header names that clients are allowed to set via custom headers.`,\n `If specified, only headers in this list will be forwarded to the ${suffix === 'push mutations' ? 'push' : 'query'} URL.`,\n `Header names are case-insensitive.`,\n `If not specified, no client-provided headers are forwarded (secure by default).`,\n `Example: ZERO_${replacement ? replacement.toUpperCase() : suffix === 'push mutations' ? 'MUTATE' : 'QUERY'}_ALLOWED_CLIENT_HEADERS=x-request-id,x-correlation-id`,\n ],\n ...(replacement\n ? {\n deprecated: [\n makeDeprecationMessage(`${replacement}-allowed-client-headers`),\n ],\n }\n : {}),\n },\n});\n\nconst mutateOptions = makeMutatorQueryOptions(undefined, 'push mutations');\nconst pushOptions = makeMutatorQueryOptions('mutate', 'push mutations');\nconst queryOptions = makeMutatorQueryOptions(undefined, 'send synced queries');\nconst getQueriesOptions = makeMutatorQueryOptions(\n 'query',\n 'send synced queries',\n);\n\n/** @deprecated */\nexport type AuthConfig = Config<typeof authOptions>;\n\n// Note: --help will list flags in the order in which they are defined here,\n// so order the fields such that the important (e.g. required) ones are first.\n// (Exported for testing)\nexport const zeroOptions = {\n upstream: {\n db: {\n type: v.string(),\n desc: [\n `The \"upstream\" authoritative postgres database.`,\n `In the future we will support other types of upstream besides PG.`,\n ],\n },\n\n type: {\n type: v.literalUnion('pg', 'custom').default('pg'),\n desc: [\n `The meaning of the {bold upstream-db} depends on the upstream type:`,\n `* {bold pg}: The connection database string, e.g. \"postgres://...\"`,\n `* {bold custom}: The base URI of the change source \"endpoint, e.g.`,\n ` \"https://my-change-source.dev/changes/v0/stream?apiKey=...\"`,\n ],\n hidden: true, // TODO: Unhide when ready to officially support.\n },\n\n maxConns: {\n type: v.number().default(20),\n desc: [\n `The maximum number of connections to open to the upstream database`,\n `for committing mutations. This is divided evenly amongst sync workers.`,\n `In addition to this number, zero-cache uses one connection for the`,\n `replication stream.`,\n ``,\n `Note that this number must allow for at least one connection per`,\n `sync worker, or zero-cache will fail to start. See {bold num-sync-workers}`,\n ],\n },\n\n maxConnsPerWorker: {\n type: v.number().optional(),\n hidden: true, // Passed from main thread to sync workers\n },\n },\n\n /** @deprecated */\n push: pushOptions,\n mutate: mutateOptions,\n /** @deprecated */\n getQueries: getQueriesOptions,\n query: queryOptions,\n\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n `The Postgres database used to store CVRs. CVRs (client view records) keep track`,\n `of the data synced to clients in order to determine the diff to send on reconnect.`,\n `If unspecified, the {bold upstream-db} will be used.`,\n ],\n },\n\n maxConns: {\n type: v.number().default(30),\n desc: [\n `The maximum number of connections to open to the CVR database.`,\n `This is divided evenly amongst sync workers.`,\n ``,\n `Note that this number must allow for at least one connection per`,\n `sync worker, or zero-cache will fail to start. See {bold num-sync-workers}`,\n ],\n },\n\n maxConnsPerWorker: {\n type: v.number().optional(),\n hidden: true, // Passed from main thread to sync workers\n },\n\n garbageCollectionInactivityThresholdHours: {\n type: v.number().default(48),\n desc: [\n `The duration after which an inactive CVR is eligible for garbage collection.`,\n `Note that garbage collection is an incremental, periodic process which does not`,\n `necessarily purge all eligible CVRs immediately.`,\n ],\n },\n\n garbageCollectionInitialIntervalSeconds: {\n type: v.number().default(60),\n desc: [\n `The initial interval at which to check and garbage collect inactive CVRs.`,\n `This interval is increased exponentially (up to 16 minutes) when there is`,\n `nothing to purge.`,\n ],\n },\n\n garbageCollectionInitialBatchSize: {\n type: v.number().default(25),\n desc: [\n `The initial number of CVRs to purge per garbage collection interval.`,\n `This number is increased linearly if the rate of new CVRs exceeds the rate of`,\n `purged CVRs, in order to reach a steady state.`,\n ``,\n `Setting this to 0 effectively disables CVR garbage collection.`,\n ],\n },\n },\n\n queryHydrationStats: {\n type: v.boolean().optional(),\n desc: [\n `Track and log the number of rows considered by query hydrations which`,\n `take longer than {bold log-slow-hydrate-threshold} milliseconds.`,\n `This is useful for debugging and performance tuning.`,\n ],\n },\n\n enableQueryPlanner: {\n type: v.boolean().default(true),\n desc: [\n `Enable the query planner for optimizing ZQL queries.`,\n ``,\n `The query planner analyzes and optimizes query execution by determining`,\n `the most efficient join strategies.`,\n ``,\n `You can disable the planner if it is picking bad strategies.`,\n ],\n },\n\n yieldThresholdMs: {\n type: v.number().default(10),\n desc: [\n `The maximum amount of time in milliseconds that a sync worker will`,\n `spend in IVM (processing query hydration and advancement) before yielding`,\n `to the event loop. Lower values increase responsiveness and fairness at`,\n `the cost of reduced throughput.`,\n ],\n },\n\n change: {\n db: {\n type: v.string().optional(),\n desc: [\n `The Postgres database used to store recent replication log entries, in order`,\n `to sync multiple view-syncers without requiring multiple replication slots on`,\n `the upstream database. If unspecified, the {bold upstream-db} will be used.`,\n ],\n },\n\n maxConns: {\n type: v.number().default(5),\n desc: [\n `The maximum number of connections to open to the change database.`,\n `This is used by the {bold change-streamer} for catching up`,\n `{bold zero-cache} replication subscriptions.`,\n ],\n },\n },\n\n replica: replicaOptions,\n\n log: logOptions,\n\n app: appOptions,\n\n shard: shardOptions,\n\n /** @deprecated */\n auth: authOptions,\n\n port: {\n type: v.number().default(4848),\n desc: [`The port for sync connections.`],\n },\n\n changeStreamer: {\n uri: {\n type: v.string().optional(),\n desc: [\n `When set, connects to the {bold change-streamer} at the given URI.`,\n `In a multi-node setup, this should be specified in {bold view-syncer} options,`,\n `pointing to the {bold replication-manager} URI, which runs a {bold change-streamer}`,\n `on port 4849.`,\n ],\n },\n\n mode: {\n type: v.literalUnion('dedicated', 'discover').default('dedicated'),\n desc: [\n `As an alternative to {bold ZERO_CHANGE_STREAMER_URI}, the {bold ZERO_CHANGE_STREAMER_MODE}`,\n `can be set to \"{bold discover}\" to instruct the {bold view-syncer} to connect to the `,\n `ip address registered by the {bold replication-manager} upon startup.`,\n ``,\n `This may not work in all networking configurations, e.g. certain private `,\n `networking or port forwarding configurations. Using the {bold ZERO_CHANGE_STREAMER_URI}`,\n `with an explicit routable hostname is recommended instead.`,\n ``,\n `Note: This option is ignored if the {bold ZERO_CHANGE_STREAMER_URI} is set.`,\n ],\n },\n\n port: {\n type: v.number().optional(),\n desc: [\n `The port on which the {bold change-streamer} runs. This is an internal`,\n `protocol between the {bold replication-manager} and {bold view-syncers}, which`,\n `runs in the same process tree in local development or a single-node configuration.`,\n ``,\n `If unspecified, defaults to {bold --port} + 1.`,\n ],\n },\n\n /** @deprecated */\n address: {\n type: v.string().optional(),\n deprecated: [\n `Set the {bold ZERO_CHANGE_STREAMER_URI} on view-syncers instead.`,\n ],\n hidden: true,\n },\n\n /** @deprecated */\n protocol: {\n type: v.literalUnion('ws', 'wss').default('ws'),\n deprecated: [\n `Set the {bold ZERO_CHANGE_STREAMER_URI} on view-syncers instead.`,\n ],\n hidden: true,\n },\n\n discoveryInterfacePreferences: {\n type: v.array(v.string()).default([\n 'eth', // linux\n 'en', // macbooks\n ]),\n desc: [\n `The name prefixes to prefer when introspecting the network interfaces to determine`,\n `the externally reachable IP address for change-streamer discovery. This defaults`,\n `to commonly used names for standard ethernet interfaces in order to prevent selecting`,\n `special interfaces such as those for VPNs.`,\n ],\n // More confusing than it's worth to advertise this. The default list should be\n // adjusted to make things work for all environments; it is controlled as a\n // hidden flag as an emergency to unblock people with outlier network configs.\n hidden: true,\n },\n\n startupDelayMs: {\n type: v.number().default(15000),\n desc: [\n `The delay to wait before the change-streamer takes over the replication stream`,\n `(i.e. the handoff during replication-manager updates), to allow loadbalancers to register`,\n `the task as healthy based on healthcheck parameters. Note that if a change stream request`,\n `is received during this interval, the delay will be canceled and the takeover will happen`,\n `immediately, since the incoming request indicates that the task is registered as a target.`,\n ],\n },\n\n backPressureThreshold: {\n type: v.number().default(DEFAULT_BACK_PRESSURE_THRESHOLD),\n desc: [\n `The maximum number of queued changes before back pressure is applied to the`,\n `change source. When the queue exceeds this threshold, the change-streamer pauses`,\n `consumption from upstream until the queue drops to 90% of the threshold.`,\n ``,\n `Increasing this value may improve throughput at the cost of higher memory usage.`,\n ],\n },\n },\n\n taskID: {\n type: v.string().optional(),\n desc: [\n `Globally unique identifier for the zero-cache instance.`,\n ``,\n `Setting this to a platform specific task identifier can be useful for debugging.`,\n `If unspecified, zero-cache will attempt to extract the TaskARN if run from within`,\n `an AWS ECS container, and otherwise use a random string.`,\n ],\n },\n\n perUserMutationLimit,\n\n numSyncWorkers: {\n type: v.number().optional(),\n desc: [\n `The number of processes to use for view syncing.`,\n `Leave this unset to use the maximum available parallelism.`,\n `If set to 0, the server runs without sync workers, which is the`,\n `configuration for running the {bold replication-manager}.`,\n ],\n },\n\n autoReset: {\n type: v.boolean().default(true),\n desc: [\n `Automatically wipe and resync the replica when replication is halted.`,\n `This situation can occur for configurations in which the upstream database`,\n `provider prohibits event trigger creation, preventing the zero-cache from`,\n `being able to correctly replicate schema changes. For such configurations,`,\n `an upstream schema change will instead result in halting replication with an`,\n `error indicating that the replica needs to be reset.`,\n ``,\n `When {bold auto-reset} is enabled, zero-cache will respond to such situations`,\n `by shutting down, and when restarted, resetting the replica and all synced `,\n `clients. This is a heavy-weight operation and can result in user-visible`,\n `slowness or downtime if compute resources are scarce.`,\n ],\n },\n\n adminPassword: {\n type: v.string().optional(),\n desc: [\n `A password used to administer zero-cache server, for example to access the`,\n `/statz endpoint.`,\n '',\n 'A password is optional in development mode but {bold required in production} mode.',\n ],\n },\n\n websocketCompression: {\n type: v.boolean().default(false),\n desc: [\n 'Enable WebSocket per-message deflate compression.',\n '',\n 'Compression can reduce bandwidth usage for sync traffic but',\n 'increases CPU usage on both client and server. Disabled by default.',\n '',\n 'See: https://github.com/websockets/ws#websocket-compression',\n ],\n },\n\n websocketCompressionOptions: {\n type: v.string().optional(),\n desc: [\n 'JSON string containing WebSocket compression options.',\n '',\n 'Only used if websocketCompression is enabled.',\n '',\n 'Example: \\\\{\"zlibDeflateOptions\":\\\\{\"level\":3\\\\},\"threshold\":1024\\\\}',\n '',\n 'See https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback for available options.',\n ],\n },\n\n websocketMaxPayloadBytes: {\n type: v.number().default(10 * 1024 * 1024),\n desc: [\n 'Maximum size of incoming WebSocket messages in bytes.',\n '',\n 'Messages exceeding this limit are rejected before parsing.',\n 'Default: 10MB (10 * 1024 * 1024 = 10485760)',\n ],\n },\n\n litestream: {\n executable: {\n type: v.string().optional(),\n desc: [`Path to the {bold litestream} executable.`],\n },\n\n configPath: {\n type: v.string().default('./src/services/litestream/config.yml'),\n desc: [\n `Path to the litestream yaml config file. zero-cache will run this with its`,\n `environment variables, which can be referenced in the file via $\\\\{ENV\\\\}`,\n `substitution, for example:`,\n `* {bold ZERO_REPLICA_FILE} for the db path`,\n `* {bold ZERO_LITESTREAM_BACKUP_LOCATION} for the db replica url`,\n `* {bold ZERO_LITESTREAM_LOG_LEVEL} for the log level`,\n `* {bold ZERO_LOG_FORMAT} for the log type`,\n ],\n },\n\n logLevel: {\n type: v.literalUnion('debug', 'info', 'warn', 'error').default('warn'),\n },\n\n backupURL: {\n type: v.string().optional(),\n desc: [\n `The location of the litestream backup, usually an {bold s3://} URL.`,\n `This is only consulted by the {bold replication-manager}.`,\n `{bold view-syncers} receive this information from the {bold replication-manager}.`,\n ],\n },\n\n endpoint: {\n type: v.string().optional(),\n desc: [\n `The S3-compatible endpoint URL to use for the litestream backup. Only required for non-AWS services.`,\n `The {bold replication-manager} and {bold view-syncers} must have the same endpoint.`,\n ],\n },\n\n port: {\n type: v.number().optional(),\n desc: [\n `Port on which litestream exports metrics, used to determine the replication`,\n `watermark up to which it is safe to purge change log records.`,\n ``,\n `If unspecified, defaults to {bold --port} + 2.`,\n ],\n },\n\n checkpointThresholdMB: {\n type: v.number().default(40),\n desc: [\n `The size of the WAL file at which to perform an SQlite checkpoint to apply`,\n `the writes in the WAL to the main database file. Each checkpoint creates`,\n `a new WAL segment file that will be backed up by litestream. Smaller thresholds`,\n `may improve read performance, at the expense of creating more files to download`,\n `when restoring the replica from the backup.`,\n ],\n },\n\n minCheckpointPageCount: {\n type: v.number().optional(),\n desc: [\n `The WAL page count at which SQLite attempts a PASSIVE checkpoint, which`,\n `transfers pages to the main database file without blocking writers.`,\n `Defaults to {bold checkpointThresholdMB * 250} (since SQLite page size is 4KB).`,\n ],\n },\n\n maxCheckpointPageCount: {\n type: v.number().optional(),\n desc: [\n `The WAL page count at which SQLite performs a RESTART checkpoint, which`,\n `blocks writers until complete. Defaults to {bold minCheckpointPageCount * 10}.`,\n `Set to {bold 0} to disable RESTART checkpoints entirely.`,\n ],\n },\n\n incrementalBackupIntervalMinutes: {\n type: v.number().default(15),\n desc: [\n `The interval between incremental backups of the replica. Shorter intervals`,\n `reduce the amount of change history that needs to be replayed when catching`,\n `up a new view-syncer, at the expense of increasing the number of files needed`,\n `to download for the initial litestream restore.`,\n ],\n },\n\n snapshotBackupIntervalHours: {\n type: v.number().default(12),\n desc: [\n `The interval between snapshot backups of the replica. Snapshot backups`,\n `make a full copy of the database to a new litestream generation. This`,\n `improves restore time at the expense of bandwidth. Applications with a`,\n `large database and low write rate can increase this interval to reduce`,\n `network usage for backups (litestream defaults to 24 hours).`,\n ],\n },\n\n restoreParallelism: {\n type: v.number().default(48),\n desc: [\n `The number of WAL files to download in parallel when performing the`,\n `initial restore of the replica from the backup.`,\n ],\n },\n\n multipartConcurrency: {\n type: v.number().default(48),\n desc: [\n `The number of parts (of size {bold --litestream-multipart-size} bytes)`,\n `to upload or download in parallel when backing up or restoring the snapshot.`,\n ],\n },\n\n multipartSize: {\n type: v.number().default(16 * 1024 * 1024),\n desc: [\n `The size of each part when uploading or downloading the snapshot with`,\n `{bold --multipart-concurrency}. Note that up to {bold concurrency * size}`,\n `bytes of memory are used when backing up or restoring the snapshot.`,\n ],\n },\n },\n\n storageDBTmpDir: {\n type: v.string().optional(),\n desc: [\n `tmp directory for IVM operator storage. Leave unset to use os.tmpdir()`,\n ],\n },\n\n initialSync: {\n tableCopyWorkers: {\n type: v.number().default(5),\n desc: [\n `The number of parallel workers used to copy tables during initial sync.`,\n `Each worker uses a database connection and will buffer up to (approximately)`,\n `10 MB of table data in memory during initial sync. Increasing the number of`,\n `workers may improve initial sync speed; however, note that local disk throughput`,\n `(i.e. IOPS), upstream CPU, and network bandwidth may also be bottlenecks.`,\n ],\n },\n\n profileCopy: {\n type: v.boolean().optional(),\n hidden: true,\n desc: [\n `Takes a cpu profile during the copy phase initial-sync, storing it as a JSON file`,\n `initial-copy.cpuprofile in the tmp directory.`,\n ],\n },\n },\n\n /** @deprecated */\n targetClientRowCount: {\n type: v.number().default(20_000),\n deprecated: [\n 'This option is no longer used and will be removed in a future version.',\n 'The client-side cache no longer enforces a row limit. Instead, TTL-based expiration',\n 'automatically manages cache size to prevent unbounded growth.',\n ],\n hidden: true,\n },\n\n lazyStartup: {\n type: v.boolean().default(false),\n desc: [\n 'Delay starting the majority of zero-cache until first request.',\n '',\n 'This is mainly intended to avoid connecting to Postgres replication stream',\n 'until the first request is received, which can be useful i.e., for preview instances.',\n '',\n 'Currently only supported in single-node mode.',\n ],\n },\n\n serverVersion: {\n type: v.string().optional(),\n desc: [`The version string outputted to logs when the server starts up.`],\n },\n\n enableTelemetry: {\n type: v.boolean().default(true),\n desc: [\n `Set to false to opt out of telemetry collection.`,\n ``,\n `This helps us improve Zero by collecting anonymous usage data.`,\n `Setting the DO_NOT_TRACK environment variable also disables telemetry.`,\n ],\n },\n\n cloudEvent: {\n sinkEnv: {\n type: v.string().optional(),\n desc: [\n `ENV variable containing a URI to a CloudEvents sink. When set, ZeroEvents`,\n `will be published to the sink as the {bold data} field of CloudEvents.`,\n `The {bold source} field of the CloudEvents will be set to the {bold ZERO_TASK_ID},`,\n `along with any extension attributes specified by the {bold ZERO_CLOUD_EVENT_EXTENSION_OVERRIDES_ENV}.`,\n ``,\n `This configuration is modeled to easily integrate with a knative K_SINK binding,`,\n `(i.e. https://github.com/knative/eventing/blob/main/docs/spec/sources.md#sinkbinding).`,\n `However, any CloudEvents sink can be used.`,\n ],\n },\n\n extensionOverridesEnv: {\n type: v.string().optional(),\n desc: [\n `ENV variable containing a JSON stringified object with an {bold extensions} field`,\n `containing attributes that should be added or overridden on outbound CloudEvents.`,\n ``,\n `This configuration is modeled to easily integrate with a knative K_CE_OVERRIDES binding,`,\n `(i.e. https://github.com/knative/eventing/blob/main/docs/spec/sources.md#sinkbinding).`,\n ],\n },\n },\n};\n\nexport type ZeroConfig = Config<typeof zeroOptions>;\n\nlet loadedConfig: Config<typeof zeroOptions> | undefined;\n\nexport function getZeroConfig(\n opts: Omit<ParseOptions, 'envNamePrefix'> = {},\n): ZeroConfig {\n if (!loadedConfig || singleProcessMode()) {\n loadedConfig = parseOptions(zeroOptions, {\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n emitDeprecationWarnings: false, // overridden at the top level parse\n ...opts,\n });\n\n if (loadedConfig.queryHydrationStats) {\n runtimeDebugFlags.trackRowCountsVended = true;\n }\n }\n return loadedConfig;\n}\n\n/**\n * Same as {@link getZeroConfig}, with an additional check that the\n * config has already been normalized (i.e. by the top level server/runner).\n */\nexport function getNormalizedZeroConfig(\n opts: Omit<ParseOptions, 'envNamePrefix'> = {},\n): NormalizedZeroConfig {\n const config = getZeroConfig(opts);\n assertNormalized(config);\n return config;\n}\n\n/**\n * Gets the server version from the config if provided. Otherwise it gets it\n * from the Zero package.json.\n */\nexport function getServerVersion(\n config: Pick<ZeroConfig, 'serverVersion'> | undefined,\n): string {\n return config?.serverVersion ?? packageJson.version;\n}\n\nexport function isAdminPasswordValid(\n lc: LogContext,\n config: Pick<NormalizedZeroConfig, 'adminPassword'>,\n password: string | undefined,\n) {\n // If development mode, password is optional\n // We use process.env.NODE_ENV === 'development' as a sign that we're in\n // development mode, rather than a custom env var like ZERO_DEVELOPMENT_MODE,\n // because NODE_ENV is more standard and is already used by many tools.\n // Note that if NODE_ENV is not set, we assume production mode.\n\n if (!password && !config.adminPassword && isDevelopmentMode()) {\n warnOnce(\n lc,\n 'No admin password set; allowing access in development mode only',\n );\n return true;\n }\n\n if (!config.adminPassword) {\n lc.warn?.('No admin password set; denying access');\n return false;\n }\n\n // Use constant-time comparison to prevent timing attacks\n const passwordBuffer = Buffer.from(password ?? '');\n const configBuffer = Buffer.from(config.adminPassword);\n\n // Handle length mismatch in constant time\n if (passwordBuffer.length !== configBuffer.length) {\n // Perform dummy comparison to maintain constant timing\n timingSafeEqual(configBuffer, configBuffer);\n lc.warn?.('Invalid admin password');\n return false;\n }\n\n if (!timingSafeEqual(passwordBuffer, configBuffer)) {\n lc.warn?.('Invalid admin password');\n return false;\n }\n\n lc.debug?.('Admin password accepted');\n return true;\n}\n\nlet hasWarned = false;\n\nfunction warnOnce(lc: LogContext, msg: string) {\n if (!hasWarned) {\n lc.warn?.(msg);\n hasWarned = true;\n }\n}\n\n// For testing purposes - reset the warning state\nexport function resetWarnOnceState() {\n hasWarned = false;\n}\n"],"names":["v.string","v.array","v.number","v.boolean","v.literalUnion"],"mappings":"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"}
|
|
@@ -15,8 +15,11 @@ import { type ShardID } from '../types/shards.ts';
|
|
|
15
15
|
export declare function compileUrlPattern(pattern: string): URLPattern;
|
|
16
16
|
export type HeaderOptions = {
|
|
17
17
|
apiKey?: string | undefined;
|
|
18
|
+
customHeaders?: Record<string, string> | undefined;
|
|
19
|
+
allowedClientHeaders?: readonly string[] | undefined;
|
|
18
20
|
token?: string | undefined;
|
|
19
21
|
cookie?: string | undefined;
|
|
22
|
+
origin?: string | undefined;
|
|
20
23
|
};
|
|
21
24
|
export declare const getBodyPreview: (res: Response, lc: LogContext, level: LogLevel) => Promise<string | undefined>;
|
|
22
25
|
export declare function fetchFromAPIServer<TValidator extends Type>(validator: TValidator, source: 'push' | 'transform', lc: LogContext, url: string, isUserUrl: boolean, allowedUrlPatterns: URLPattern[], shard: ShardID, headerOptions: HeaderOptions, body: ReadonlyJSONValue): Promise<import("../../../shared/src/valita.ts").Infer<TValidator>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/custom/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAC3D,OAAO,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAC,KAAK,IAAI,EAAC,MAAM,+BAA+B,CAAC;AAMxD,OAAO,EAAiB,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAIhE;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAQ7D;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/custom/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAC3D,OAAO,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAC,KAAK,IAAI,EAAC,MAAM,+BAA+B,CAAC;AAMxD,OAAO,EAAiB,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAIhE;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAQ7D;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACnD,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;AA4BF,eAAO,MAAM,cAAc,GACzB,KAAK,QAAQ,EACb,IAAI,UAAU,EACd,OAAO,QAAQ,KACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAe5B,CAAC;AAEF,wBAAsB,kBAAkB,CAAC,UAAU,SAAS,IAAI,EAC9D,SAAS,EAAE,UAAU,EACrB,MAAM,EAAE,MAAM,GAAG,WAAW,EAC5B,EAAE,EAAE,UAAU,EACd,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,OAAO,EAClB,kBAAkB,EAAE,UAAU,EAAE,EAChC,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,aAAa,EAC5B,IAAI,EAAE,iBAAiB,sEA4KxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,UAAU,EAAE,GAC/B,OAAO,CAOT"}
|
|
@@ -18,6 +18,22 @@ function compileUrlPattern(pattern) {
|
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
function filterCustomHeaders(headers, allowedHeaders) {
|
|
22
|
+
if (!headers) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
if (!allowedHeaders || allowedHeaders.length === 0) {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
const allowed = new Set(allowedHeaders.map((h) => h.toLowerCase()));
|
|
29
|
+
const filtered = {};
|
|
30
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
31
|
+
if (allowed.has(key.toLowerCase())) {
|
|
32
|
+
filtered[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return filtered;
|
|
36
|
+
}
|
|
21
37
|
const getBodyPreview = async (res, lc, level) => {
|
|
22
38
|
try {
|
|
23
39
|
const body = await res.clone().text();
|
|
@@ -60,12 +76,22 @@ async function fetchFromAPIServer(validator, source, lc, url, isUserUrl, allowed
|
|
|
60
76
|
if (headerOptions.apiKey) {
|
|
61
77
|
headers["X-Api-Key"] = headerOptions.apiKey;
|
|
62
78
|
}
|
|
79
|
+
Object.assign(
|
|
80
|
+
headers,
|
|
81
|
+
filterCustomHeaders(
|
|
82
|
+
headerOptions.customHeaders,
|
|
83
|
+
headerOptions.allowedClientHeaders
|
|
84
|
+
)
|
|
85
|
+
);
|
|
63
86
|
if (headerOptions.token) {
|
|
64
87
|
headers["Authorization"] = `Bearer ${headerOptions.token}`;
|
|
65
88
|
}
|
|
66
89
|
if (headerOptions.cookie) {
|
|
67
90
|
headers["Cookie"] = headerOptions.cookie;
|
|
68
91
|
}
|
|
92
|
+
if (headerOptions.origin) {
|
|
93
|
+
headers["Origin"] = headerOptions.origin;
|
|
94
|
+
}
|
|
69
95
|
const urlObj = new URL(url);
|
|
70
96
|
const params = new URLSearchParams(urlObj.search);
|
|
71
97
|
for (const reserved of reservedParams) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.js","sources":["../../../../../zero-cache/src/custom/fetch.ts"],"sourcesContent":["import type {LogContext, LogLevel} from '@rocicorp/logger';\nimport 'urlpattern-polyfill';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {getErrorMessage} from '../../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {type Type} from '../../../shared/src/valita.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 {isProtocolError} from '../../../zero-protocol/src/error.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {upstreamSchema, type ShardID} from '../types/shards.ts';\n\nconst reservedParams = ['schema', 'appID'];\n\n/**\n * Compiles and validates a URLPattern from configuration.\n *\n * Patterns must be full URLs (e.g., \"https://api.example.com/endpoint\").\n * URLPattern automatically sets search and hash to wildcard ('*'),\n * which means query parameters and fragments are ignored during matching.\n *\n * @throws Error if the pattern is an invalid URLPattern\n */\nexport function compileUrlPattern(pattern: string): URLPattern {\n try {\n return new URLPattern(pattern);\n } catch (e) {\n throw new Error(\n `Invalid URLPattern in URL configuration: \"${pattern}\". Error: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n}\n\nexport type HeaderOptions = {\n apiKey?: string | undefined;\n token?: string | undefined;\n cookie?: string | undefined;\n};\n\nexport const getBodyPreview = async (\n res: Response,\n lc: LogContext,\n level: LogLevel,\n): Promise<string | undefined> => {\n try {\n const body = await res.clone().text();\n if (body.length > 512) {\n return body.slice(0, 512) + '...';\n }\n return body;\n } catch (e) {\n lc[level]?.('failed to get body preview', {\n url: res.url,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n\n return undefined;\n};\n\nexport async function fetchFromAPIServer<TValidator extends Type>(\n validator: TValidator,\n source: 'push' | 'transform',\n lc: LogContext,\n url: string,\n isUserUrl: boolean,\n allowedUrlPatterns: URLPattern[],\n shard: ShardID,\n headerOptions: HeaderOptions,\n body: ReadonlyJSONValue,\n) {\n lc.debug?.('fetchFromAPIServer called', {\n url,\n });\n\n if (!urlMatch(url, allowedUrlPatterns)) {\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `URL \"${url}\" is not allowed by the ZERO_MUTATE_URL configuration`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `URL \"${url}\" is not allowed by the ZERO_QUERY_URL configuration`,\n queryIDs: [],\n },\n );\n }\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (headerOptions.apiKey) {\n headers['X-Api-Key'] = headerOptions.apiKey;\n }\n if (headerOptions.token) {\n headers['Authorization'] = `Bearer ${headerOptions.token}`;\n }\n if (headerOptions.cookie) {\n headers['Cookie'] = headerOptions.cookie;\n }\n\n const urlObj = new URL(url);\n const params = new URLSearchParams(urlObj.search);\n\n for (const reserved of reservedParams) {\n assert(\n !params.has(reserved),\n `The push URL cannot contain the reserved query param \"${reserved}\"`,\n );\n }\n\n params.append('schema', upstreamSchema(shard));\n params.append('appID', shard.appID);\n\n urlObj.search = params.toString();\n\n const finalUrl = urlObj.toString();\n\n // Errors from a user-specified url are treated 4xx errors\n // (e.g. bad request) as they are developer driven and should not\n // trigger error-log based alerts.\n const errLevel: LogLevel = isUserUrl ? 'warn' : 'error';\n try {\n const response = await fetch(finalUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const bodyPreview = await getBodyPreview(response, lc, errLevel);\n\n // Bad Gateway or Gateway Timeout indicate the server was not reached\n const level =\n response.status === 502 || response.status === 504 ? errLevel : 'info';\n lc[level]?.('fetch from API server returned non-OK status', {\n url: finalUrl,\n status: response.status,\n bodyPreview,\n });\n\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n status: response.status,\n bodyPreview,\n message: `Fetch from API server returned non-OK status ${response.status}`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n status: response.status,\n bodyPreview,\n message: `Fetch from API server returned non-OK status ${response.status}`,\n queryIDs: [],\n },\n );\n }\n\n try {\n const json = await response.json();\n\n return validator.parse(json);\n } catch (error) {\n lc[errLevel]?.('failed to parse response', {\n url: finalUrl,\n error,\n });\n\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Parse,\n message: `Failed to parse response from API server: ${getErrorMessage(error)}`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Parse,\n message: `Failed to parse response from API server: ${getErrorMessage(error)}`,\n queryIDs: [],\n },\n 'error',\n {cause: error},\n );\n }\n } catch (error) {\n if (isProtocolError(error)) {\n throw error;\n }\n\n lc[errLevel]?.('failed to fetch from API server with unknown error', {\n url: finalUrl,\n error,\n });\n\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `Fetch from API server failed with unknown error: ${getErrorMessage(error)}`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `Fetch from API server failed with unknown error: ${getErrorMessage(error)}`,\n queryIDs: [],\n },\n 'error',\n {cause: error},\n );\n }\n}\n\n/**\n * Returns true if the url matches one of the allowedUrlPatterns.\n *\n * URLPattern automatically ignores query parameters and hash fragments during matching\n * because it sets search and hash to wildcard ('*') by default.\n *\n * Example URLPattern patterns:\n * - \"https://api.example.com/endpoint\" - Exact match for a specific URL\n * - \"https://*.example.com/endpoint\" - Matches any single subdomain (e.g., \"https://api.example.com/endpoint\")\n * - \"https://*.*.example.com/endpoint\" - Matches two subdomains (e.g., \"https://api.v1.example.com/endpoint\")\n * - \"https://api.example.com/*\" - Matches any path under /\n * - \"https://api.example.com/:version/endpoint\" - Matches with named parameter (e.g., \"https://api.example.com/v1/endpoint\")\n */\nexport function urlMatch(\n url: string,\n allowedUrlPatterns: URLPattern[],\n): boolean {\n for (const pattern of allowedUrlPatterns) {\n if (pattern.test(url)) {\n return true;\n }\n }\n return false;\n}\n"],"names":["ErrorKind.PushFailed","ErrorOrigin.ZeroCache","ErrorReason.Internal","ErrorKind.TransformFailed","ErrorReason.HTTP","ErrorReason.Parse"],"mappings":";;;;;;;;;;AAaA,MAAM,iBAAiB,CAAC,UAAU,OAAO;AAWlC,SAAS,kBAAkB,SAA6B;AAC7D,MAAI;AACF,WAAO,IAAI,WAAW,OAAO;AAAA,EAC/B,SAAS,GAAG;AACV,UAAM,IAAI;AAAA,MACR,6CAA6C,OAAO,aAAa,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,IAAA;AAAA,EAE/G;AACF;AAQO,MAAM,iBAAiB,OAC5B,KACA,IACA,UACgC;AAChC,MAAI;AACF,UAAM,OAAO,MAAM,IAAI,MAAA,EAAQ,KAAA;AAC/B,QAAI,KAAK,SAAS,KAAK;AACrB,aAAO,KAAK,MAAM,GAAG,GAAG,IAAI;AAAA,IAC9B;AACA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,OAAG,KAAK,IAAI,8BAA8B;AAAA,MACxC,KAAK,IAAI;AAAA,MACT,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,IAAA,CACjD;AAAA,EACH;AAEA,SAAO;AACT;AAEA,eAAsB,mBACpB,WACA,QACA,IACA,KACA,WACA,oBACA,OACA,eACA,MACA;AACA,KAAG,QAAQ,6BAA6B;AAAA,IACtC;AAAA,EAAA,CACD;AAED,MAAI,CAAC,SAAS,KAAK,kBAAkB,GAAG;AACtC,UAAM,IAAI;AAAA,MACR,WAAW,SACP;AAAA,QACE,MAAMA;AAAAA,QACN,QAAQC;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,QAAQ,GAAG;AAAA,QACpB,aAAa,CAAA;AAAA,MAAC,IAEhB;AAAA,QACE,MAAMC;AAAAA,QACN,QAAQF;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,QAAQ,GAAG;AAAA,QACpB,UAAU,CAAA;AAAA,MAAC;AAAA,IACb;AAAA,EAER;AACA,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,EAAA;AAGlB,MAAI,cAAc,QAAQ;AACxB,YAAQ,WAAW,IAAI,cAAc;AAAA,EACvC;AACA,MAAI,cAAc,OAAO;AACvB,YAAQ,eAAe,IAAI,UAAU,cAAc,KAAK;AAAA,EAC1D;AACA,MAAI,cAAc,QAAQ;AACxB,YAAQ,QAAQ,IAAI,cAAc;AAAA,EACpC;AAEA,QAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,QAAM,SAAS,IAAI,gBAAgB,OAAO,MAAM;AAEhD,aAAW,YAAY,gBAAgB;AACrC;AAAA,MACE,CAAC,OAAO,IAAI,QAAQ;AAAA,MACpB,yDAAyD,QAAQ;AAAA,IAAA;AAAA,EAErE;AAEA,SAAO,OAAO,UAAU,eAAe,KAAK,CAAC;AAC7C,SAAO,OAAO,SAAS,MAAM,KAAK;AAElC,SAAO,SAAS,OAAO,SAAA;AAEvB,QAAM,WAAW,OAAO,SAAA;AAKxB,QAAM,WAAqB,YAAY,SAAS;AAChD,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACrC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAAA,CAC1B;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,cAAc,MAAM,eAAe,UAAU,IAAI,QAAQ;AAG/D,YAAM,QACJ,SAAS,WAAW,OAAO,SAAS,WAAW,MAAM,WAAW;AAClE,SAAG,KAAK,IAAI,gDAAgD;AAAA,QAC1D,KAAK;AAAA,QACL,QAAQ,SAAS;AAAA,QACjB;AAAA,MAAA,CACD;AAED,YAAM,IAAI;AAAA,QACR,WAAW,SACP;AAAA,UACE,MAAMF;AAAAA,UACN,QAAQC;AAAAA,UACR,QAAQG;AAAAA,UACR,QAAQ,SAAS;AAAA,UACjB;AAAA,UACA,SAAS,gDAAgD,SAAS,MAAM;AAAA,UACxE,aAAa,CAAA;AAAA,QAAC,IAEhB;AAAA,UACE,MAAMD;AAAAA,UACN,QAAQF;AAAAA,UACR,QAAQG;AAAAA,UACR,QAAQ,SAAS;AAAA,UACjB;AAAA,UACA,SAAS,gDAAgD,SAAS,MAAM;AAAA,UACxE,UAAU,CAAA;AAAA,QAAC;AAAA,MACb;AAAA,IAER;AAEA,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,KAAA;AAE5B,aAAO,UAAU,MAAM,IAAI;AAAA,IAC7B,SAAS,OAAO;AACd,SAAG,QAAQ,IAAI,4BAA4B;AAAA,QACzC,KAAK;AAAA,QACL;AAAA,MAAA,CACD;AAED,YAAM,IAAI;AAAA,QACR,WAAW,SACP;AAAA,UACE,MAAMJ;AAAAA,UACN,QAAQC;AAAAA,UACR,QAAQI;AAAAA,UACR,SAAS,6CAA6C,gBAAgB,KAAK,CAAC;AAAA,UAC5E,aAAa,CAAA;AAAA,QAAC,IAEhB;AAAA,UACE,MAAMF;AAAAA,UACN,QAAQF;AAAAA,UACR,QAAQI;AAAAA,UACR,SAAS,6CAA6C,gBAAgB,KAAK,CAAC;AAAA,UAC5E,UAAU,CAAA;AAAA,QAAC;AAAA,QAEjB;AAAA,QACA,EAAC,OAAO,MAAA;AAAA,MAAK;AAAA,IAEjB;AAAA,EACF,SAAS,OAAO;AACd,QAAI,gBAAgB,KAAK,GAAG;AAC1B,YAAM;AAAA,IACR;AAEA,OAAG,QAAQ,IAAI,sDAAsD;AAAA,MACnE,KAAK;AAAA,MACL;AAAA,IAAA,CACD;AAED,UAAM,IAAI;AAAA,MACR,WAAW,SACP;AAAA,QACE,MAAML;AAAAA,QACN,QAAQC;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,oDAAoD,gBAAgB,KAAK,CAAC;AAAA,QACnF,aAAa,CAAA;AAAA,MAAC,IAEhB;AAAA,QACE,MAAMC;AAAAA,QACN,QAAQF;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,oDAAoD,gBAAgB,KAAK,CAAC;AAAA,QACnF,UAAU,CAAA;AAAA,MAAC;AAAA,MAEjB;AAAA,MACA,EAAC,OAAO,MAAA;AAAA,IAAK;AAAA,EAEjB;AACF;AAeO,SAAS,SACd,KACA,oBACS;AACT,aAAW,WAAW,oBAAoB;AACxC,QAAI,QAAQ,KAAK,GAAG,GAAG;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;"}
|
|
1
|
+
{"version":3,"file":"fetch.js","sources":["../../../../../zero-cache/src/custom/fetch.ts"],"sourcesContent":["import type {LogContext, LogLevel} from '@rocicorp/logger';\nimport 'urlpattern-polyfill';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {getErrorMessage} from '../../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {type Type} from '../../../shared/src/valita.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 {isProtocolError} from '../../../zero-protocol/src/error.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {upstreamSchema, type ShardID} from '../types/shards.ts';\n\nconst reservedParams = ['schema', 'appID'];\n\n/**\n * Compiles and validates a URLPattern from configuration.\n *\n * Patterns must be full URLs (e.g., \"https://api.example.com/endpoint\").\n * URLPattern automatically sets search and hash to wildcard ('*'),\n * which means query parameters and fragments are ignored during matching.\n *\n * @throws Error if the pattern is an invalid URLPattern\n */\nexport function compileUrlPattern(pattern: string): URLPattern {\n try {\n return new URLPattern(pattern);\n } catch (e) {\n throw new Error(\n `Invalid URLPattern in URL configuration: \"${pattern}\". Error: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n}\n\nexport type HeaderOptions = {\n apiKey?: string | undefined;\n customHeaders?: Record<string, string> | undefined;\n allowedClientHeaders?: readonly string[] | undefined;\n token?: string | undefined;\n cookie?: string | undefined;\n origin?: string | undefined;\n};\n\n/**\n * Filters custom headers based on the allowed headers list.\n * If no allowedHeaders list is specified, no custom headers are forwarded (secure by default).\n * Header names are compared case-insensitively.\n */\nfunction filterCustomHeaders(\n headers: Record<string, string> | undefined,\n allowedHeaders: readonly string[] | undefined,\n): Record<string, string> {\n if (!headers) {\n return {};\n }\n if (!allowedHeaders || allowedHeaders.length === 0) {\n return {};\n }\n\n const allowed = new Set(allowedHeaders.map(h => h.toLowerCase()));\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (allowed.has(key.toLowerCase())) {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n\nexport const getBodyPreview = async (\n res: Response,\n lc: LogContext,\n level: LogLevel,\n): Promise<string | undefined> => {\n try {\n const body = await res.clone().text();\n if (body.length > 512) {\n return body.slice(0, 512) + '...';\n }\n return body;\n } catch (e) {\n lc[level]?.('failed to get body preview', {\n url: res.url,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n\n return undefined;\n};\n\nexport async function fetchFromAPIServer<TValidator extends Type>(\n validator: TValidator,\n source: 'push' | 'transform',\n lc: LogContext,\n url: string,\n isUserUrl: boolean,\n allowedUrlPatterns: URLPattern[],\n shard: ShardID,\n headerOptions: HeaderOptions,\n body: ReadonlyJSONValue,\n) {\n lc.debug?.('fetchFromAPIServer called', {\n url,\n });\n\n if (!urlMatch(url, allowedUrlPatterns)) {\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `URL \"${url}\" is not allowed by the ZERO_MUTATE_URL configuration`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `URL \"${url}\" is not allowed by the ZERO_QUERY_URL configuration`,\n queryIDs: [],\n },\n );\n }\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (headerOptions.apiKey) {\n headers['X-Api-Key'] = headerOptions.apiKey;\n }\n Object.assign(\n headers,\n filterCustomHeaders(\n headerOptions.customHeaders,\n headerOptions.allowedClientHeaders,\n ),\n );\n if (headerOptions.token) {\n headers['Authorization'] = `Bearer ${headerOptions.token}`;\n }\n if (headerOptions.cookie) {\n headers['Cookie'] = headerOptions.cookie;\n }\n if (headerOptions.origin) {\n headers['Origin'] = headerOptions.origin;\n }\n\n const urlObj = new URL(url);\n const params = new URLSearchParams(urlObj.search);\n\n for (const reserved of reservedParams) {\n assert(\n !params.has(reserved),\n `The push URL cannot contain the reserved query param \"${reserved}\"`,\n );\n }\n\n params.append('schema', upstreamSchema(shard));\n params.append('appID', shard.appID);\n\n urlObj.search = params.toString();\n\n const finalUrl = urlObj.toString();\n\n // Errors from a user-specified url are treated 4xx errors\n // (e.g. bad request) as they are developer driven and should not\n // trigger error-log based alerts.\n const errLevel: LogLevel = isUserUrl ? 'warn' : 'error';\n try {\n const response = await fetch(finalUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const bodyPreview = await getBodyPreview(response, lc, errLevel);\n\n // Bad Gateway or Gateway Timeout indicate the server was not reached\n const level =\n response.status === 502 || response.status === 504 ? errLevel : 'info';\n lc[level]?.('fetch from API server returned non-OK status', {\n url: finalUrl,\n status: response.status,\n bodyPreview,\n });\n\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n status: response.status,\n bodyPreview,\n message: `Fetch from API server returned non-OK status ${response.status}`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.HTTP,\n status: response.status,\n bodyPreview,\n message: `Fetch from API server returned non-OK status ${response.status}`,\n queryIDs: [],\n },\n );\n }\n\n try {\n const json = await response.json();\n\n return validator.parse(json);\n } catch (error) {\n lc[errLevel]?.('failed to parse response', {\n url: finalUrl,\n error,\n });\n\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Parse,\n message: `Failed to parse response from API server: ${getErrorMessage(error)}`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Parse,\n message: `Failed to parse response from API server: ${getErrorMessage(error)}`,\n queryIDs: [],\n },\n 'error',\n {cause: error},\n );\n }\n } catch (error) {\n if (isProtocolError(error)) {\n throw error;\n }\n\n lc[errLevel]?.('failed to fetch from API server with unknown error', {\n url: finalUrl,\n error,\n });\n\n throw new ProtocolErrorWithLevel(\n source === 'push'\n ? {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `Fetch from API server failed with unknown error: ${getErrorMessage(error)}`,\n mutationIDs: [],\n }\n : {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.ZeroCache,\n reason: ErrorReason.Internal,\n message: `Fetch from API server failed with unknown error: ${getErrorMessage(error)}`,\n queryIDs: [],\n },\n 'error',\n {cause: error},\n );\n }\n}\n\n/**\n * Returns true if the url matches one of the allowedUrlPatterns.\n *\n * URLPattern automatically ignores query parameters and hash fragments during matching\n * because it sets search and hash to wildcard ('*') by default.\n *\n * Example URLPattern patterns:\n * - \"https://api.example.com/endpoint\" - Exact match for a specific URL\n * - \"https://*.example.com/endpoint\" - Matches any single subdomain (e.g., \"https://api.example.com/endpoint\")\n * - \"https://*.*.example.com/endpoint\" - Matches two subdomains (e.g., \"https://api.v1.example.com/endpoint\")\n * - \"https://api.example.com/*\" - Matches any path under /\n * - \"https://api.example.com/:version/endpoint\" - Matches with named parameter (e.g., \"https://api.example.com/v1/endpoint\")\n */\nexport function urlMatch(\n url: string,\n allowedUrlPatterns: URLPattern[],\n): boolean {\n for (const pattern of allowedUrlPatterns) {\n if (pattern.test(url)) {\n return true;\n }\n }\n return false;\n}\n"],"names":["ErrorKind.PushFailed","ErrorOrigin.ZeroCache","ErrorReason.Internal","ErrorKind.TransformFailed","ErrorReason.HTTP","ErrorReason.Parse"],"mappings":";;;;;;;;;;AAaA,MAAM,iBAAiB,CAAC,UAAU,OAAO;AAWlC,SAAS,kBAAkB,SAA6B;AAC7D,MAAI;AACF,WAAO,IAAI,WAAW,OAAO;AAAA,EAC/B,SAAS,GAAG;AACV,UAAM,IAAI;AAAA,MACR,6CAA6C,OAAO,aAAa,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,IAAA;AAAA,EAE/G;AACF;AAgBA,SAAS,oBACP,SACA,gBACwB;AACxB,MAAI,CAAC,SAAS;AACZ,WAAO,CAAA;AAAA,EACT;AACA,MAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG;AAClD,WAAO,CAAA;AAAA,EACT;AAEA,QAAM,UAAU,IAAI,IAAI,eAAe,IAAI,CAAA,MAAK,EAAE,YAAA,CAAa,CAAC;AAChE,QAAM,WAAmC,CAAA;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,QAAQ,IAAI,IAAI,YAAA,CAAa,GAAG;AAClC,eAAS,GAAG,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,iBAAiB,OAC5B,KACA,IACA,UACgC;AAChC,MAAI;AACF,UAAM,OAAO,MAAM,IAAI,MAAA,EAAQ,KAAA;AAC/B,QAAI,KAAK,SAAS,KAAK;AACrB,aAAO,KAAK,MAAM,GAAG,GAAG,IAAI;AAAA,IAC9B;AACA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,OAAG,KAAK,IAAI,8BAA8B;AAAA,MACxC,KAAK,IAAI;AAAA,MACT,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,IAAA,CACjD;AAAA,EACH;AAEA,SAAO;AACT;AAEA,eAAsB,mBACpB,WACA,QACA,IACA,KACA,WACA,oBACA,OACA,eACA,MACA;AACA,KAAG,QAAQ,6BAA6B;AAAA,IACtC;AAAA,EAAA,CACD;AAED,MAAI,CAAC,SAAS,KAAK,kBAAkB,GAAG;AACtC,UAAM,IAAI;AAAA,MACR,WAAW,SACP;AAAA,QACE,MAAMA;AAAAA,QACN,QAAQC;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,QAAQ,GAAG;AAAA,QACpB,aAAa,CAAA;AAAA,MAAC,IAEhB;AAAA,QACE,MAAMC;AAAAA,QACN,QAAQF;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,QAAQ,GAAG;AAAA,QACpB,UAAU,CAAA;AAAA,MAAC;AAAA,IACb;AAAA,EAER;AACA,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,EAAA;AAGlB,MAAI,cAAc,QAAQ;AACxB,YAAQ,WAAW,IAAI,cAAc;AAAA,EACvC;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,cAAc;AAAA,MACd,cAAc;AAAA,IAAA;AAAA,EAChB;AAEF,MAAI,cAAc,OAAO;AACvB,YAAQ,eAAe,IAAI,UAAU,cAAc,KAAK;AAAA,EAC1D;AACA,MAAI,cAAc,QAAQ;AACxB,YAAQ,QAAQ,IAAI,cAAc;AAAA,EACpC;AACA,MAAI,cAAc,QAAQ;AACxB,YAAQ,QAAQ,IAAI,cAAc;AAAA,EACpC;AAEA,QAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,QAAM,SAAS,IAAI,gBAAgB,OAAO,MAAM;AAEhD,aAAW,YAAY,gBAAgB;AACrC;AAAA,MACE,CAAC,OAAO,IAAI,QAAQ;AAAA,MACpB,yDAAyD,QAAQ;AAAA,IAAA;AAAA,EAErE;AAEA,SAAO,OAAO,UAAU,eAAe,KAAK,CAAC;AAC7C,SAAO,OAAO,SAAS,MAAM,KAAK;AAElC,SAAO,SAAS,OAAO,SAAA;AAEvB,QAAM,WAAW,OAAO,SAAA;AAKxB,QAAM,WAAqB,YAAY,SAAS;AAChD,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACrC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAAA,CAC1B;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,cAAc,MAAM,eAAe,UAAU,IAAI,QAAQ;AAG/D,YAAM,QACJ,SAAS,WAAW,OAAO,SAAS,WAAW,MAAM,WAAW;AAClE,SAAG,KAAK,IAAI,gDAAgD;AAAA,QAC1D,KAAK;AAAA,QACL,QAAQ,SAAS;AAAA,QACjB;AAAA,MAAA,CACD;AAED,YAAM,IAAI;AAAA,QACR,WAAW,SACP;AAAA,UACE,MAAMF;AAAAA,UACN,QAAQC;AAAAA,UACR,QAAQG;AAAAA,UACR,QAAQ,SAAS;AAAA,UACjB;AAAA,UACA,SAAS,gDAAgD,SAAS,MAAM;AAAA,UACxE,aAAa,CAAA;AAAA,QAAC,IAEhB;AAAA,UACE,MAAMD;AAAAA,UACN,QAAQF;AAAAA,UACR,QAAQG;AAAAA,UACR,QAAQ,SAAS;AAAA,UACjB;AAAA,UACA,SAAS,gDAAgD,SAAS,MAAM;AAAA,UACxE,UAAU,CAAA;AAAA,QAAC;AAAA,MACb;AAAA,IAER;AAEA,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,KAAA;AAE5B,aAAO,UAAU,MAAM,IAAI;AAAA,IAC7B,SAAS,OAAO;AACd,SAAG,QAAQ,IAAI,4BAA4B;AAAA,QACzC,KAAK;AAAA,QACL;AAAA,MAAA,CACD;AAED,YAAM,IAAI;AAAA,QACR,WAAW,SACP;AAAA,UACE,MAAMJ;AAAAA,UACN,QAAQC;AAAAA,UACR,QAAQI;AAAAA,UACR,SAAS,6CAA6C,gBAAgB,KAAK,CAAC;AAAA,UAC5E,aAAa,CAAA;AAAA,QAAC,IAEhB;AAAA,UACE,MAAMF;AAAAA,UACN,QAAQF;AAAAA,UACR,QAAQI;AAAAA,UACR,SAAS,6CAA6C,gBAAgB,KAAK,CAAC;AAAA,UAC5E,UAAU,CAAA;AAAA,QAAC;AAAA,QAEjB;AAAA,QACA,EAAC,OAAO,MAAA;AAAA,MAAK;AAAA,IAEjB;AAAA,EACF,SAAS,OAAO;AACd,QAAI,gBAAgB,KAAK,GAAG;AAC1B,YAAM;AAAA,IACR;AAEA,OAAG,QAAQ,IAAI,sDAAsD;AAAA,MACnE,KAAK;AAAA,MACL;AAAA,IAAA,CACD;AAED,UAAM,IAAI;AAAA,MACR,WAAW,SACP;AAAA,QACE,MAAML;AAAAA,QACN,QAAQC;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,oDAAoD,gBAAgB,KAAK,CAAC;AAAA,QACnF,aAAa,CAAA;AAAA,MAAC,IAEhB;AAAA,QACE,MAAMC;AAAAA,QACN,QAAQF;AAAAA,QACR,QAAQC;AAAAA,QACR,SAAS,oDAAoD,gBAAgB,KAAK,CAAC;AAAA,QACnF,UAAU,CAAA;AAAA,MAAC;AAAA,MAEjB;AAAA,MACA,EAAC,OAAO,MAAA;AAAA,IAAK;AAAA,EAEjB;AACF;AAeO,SAAS,SACd,KACA,oBACS;AACT,aAAW,WAAW,oBAAoB;AACxC,QAAI,QAAQ,KAAK,GAAG,GAAG;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;"}
|
|
@@ -2,7 +2,7 @@ import { must } from "../../../shared/src/must.js";
|
|
|
2
2
|
import { difference } from "../../../shared/src/set-utils.js";
|
|
3
3
|
import { parse } from "../../../shared/src/valita.js";
|
|
4
4
|
import { primaryKeySchema } from "../../../zero-protocol/src/primary-key.js";
|
|
5
|
-
import { ColumnMetadataStore, metadataToLiteTypeString } from "../services/
|
|
5
|
+
import { ColumnMetadataStore, metadataToLiteTypeString } from "../services/replicator/schema/column-metadata.js";
|
|
6
6
|
import { liteTypeToZqlValueType, nullableUpstream, mapLiteDataTypeToZqlSchemaValue, isArray, isEnum } from "../types/lite.js";
|
|
7
7
|
import { Enum, Base } from "./postgres-type-class-enum.js";
|
|
8
8
|
function listTables(db) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lite-tables.js","sources":["../../../../../zero-cache/src/db/lite-tables.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {difference} from '../../../shared/src/set-utils.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {primaryKeySchema} from '../../../zero-protocol/src/primary-key.ts';\nimport type {Database} from '../../../zqlite/src/db.ts';\nimport {\n ColumnMetadataStore,\n metadataToLiteTypeString,\n} from '../services/change-source/column-metadata.ts';\nimport {\n isArray,\n isEnum,\n liteTypeToZqlValueType,\n mapLiteDataTypeToZqlSchemaValue,\n nullableUpstream,\n} from '../types/lite.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport type {\n LiteAndZqlSpec,\n LiteIndexSpec,\n LiteTableSpec,\n MutableLiteIndexSpec,\n MutableLiteTableSpec,\n} from './specs.ts';\n\ntype ColumnInfo = {\n table: string;\n name: string;\n type: string;\n notNull: number;\n dflt: string | null;\n keyPos: number;\n};\n\nexport function listTables(db: Database): LiteTableSpec[] {\n const metadataStore = ColumnMetadataStore.getInstance(db);\n\n const columns = db\n .prepare(\n `\n SELECT\n m.name as \"table\",\n p.name as name,\n p.type as type,\n p.\"notnull\" as \"notNull\",\n p.dflt_value as \"dflt\",\n p.pk as keyPos\n FROM sqlite_master as m\n LEFT JOIN pragma_table_info(m.name) as p\n WHERE m.type = 'table'\n AND m.name NOT LIKE 'sqlite_%'\n AND m.name NOT LIKE '_zero.%'\n AND m.name NOT LIKE '_litestream_%'\n `,\n )\n .all() as ColumnInfo[];\n\n const tables: LiteTableSpec[] = [];\n let table: MutableLiteTableSpec | undefined;\n\n columns.forEach(col => {\n if (col.table !== table?.name) {\n // New table\n table = {\n name: col.table,\n columns: {},\n };\n tables.push(table);\n }\n\n // Try to read from metadata table first, fall back to SQLite column type\n let dataType: string;\n let elemPgTypeClass:\n | typeof PostgresTypeClass.Base\n | typeof PostgresTypeClass.Enum\n | null;\n\n const metadata = metadataStore?.getColumn(col.table, col.name);\n if (metadata) {\n // Read from metadata table and convert to pipe notation\n dataType = metadataToLiteTypeString(metadata);\n elemPgTypeClass = metadata.isArray\n ? metadata.isEnum\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n } else {\n // Fall back to reading from SQLite column type (pipe notation)\n dataType = col.type;\n elemPgTypeClass = isArray(col.type)\n ? isEnum(col.type)\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n }\n\n table.columns[col.name] = {\n pos: Object.keys(table.columns).length + 1,\n dataType,\n characterMaximumLength: null,\n notNull: col.notNull !== 0,\n dflt: col.dflt,\n elemPgTypeClass,\n };\n if (col.keyPos) {\n table.primaryKey ??= [];\n while (table.primaryKey.length < col.keyPos) {\n table.primaryKey.push('');\n }\n table.primaryKey[col.keyPos - 1] = col.name;\n }\n });\n\n return tables;\n}\n\nexport function listIndexes(db: Database): LiteIndexSpec[] {\n const indexes = db\n .prepare(\n `SELECT \n idx.name as indexName, \n idx.tbl_name as tableName, \n info.\"unique\" as \"unique\",\n col.name as column,\n CASE WHEN col.desc = 0 THEN 'ASC' ELSE 'DESC' END as dir\n FROM sqlite_master as idx\n JOIN pragma_index_list(idx.tbl_name) AS info ON info.name = idx.name\n JOIN pragma_index_xinfo(idx.name) as col\n WHERE idx.type = 'index' AND \n col.key = 1 AND\n idx.tbl_name NOT LIKE '_zero.%'\n ORDER BY idx.name, col.seqno ASC`,\n )\n .all() as {\n indexName: string;\n tableName: string;\n unique: number;\n column: string;\n dir: 'ASC' | 'DESC';\n }[];\n\n const ret: MutableLiteIndexSpec[] = [];\n for (const {indexName: name, tableName, unique, column, dir} of indexes) {\n if (ret.at(-1)?.name === name) {\n // Aggregate multiple column names into the array.\n must(ret.at(-1)).columns[column] = dir;\n } else {\n ret.push({\n tableName,\n name,\n columns: {[column]: dir},\n unique: unique !== 0,\n });\n }\n }\n\n return ret;\n}\n\n/**\n * Computes a TableSpec \"view\" of the replicated data that is\n * suitable for processing / consumption for the client. This\n * includes:\n * * excluding tables without a PRIMARY KEY or UNIQUE INDEX\n * * excluding columns with types that are not supported by ZQL\n * * choosing columns to use as the primary key amongst those\n * in unique indexes\n *\n * @param tableSpecs an optional map to reset and populate\n * @param fullTables an optional map to receive the full table specs,\n * which may include tables and columns that are not synced to\n * the client because they lack a primary key or are of unsupported\n * data types.\n */\nexport function computeZqlSpecs(\n lc: LogContext,\n replica: Database,\n tableSpecs: Map<string, LiteAndZqlSpec> = new Map(),\n fullTables?: Map<string, LiteTableSpec>,\n): Map<string, LiteAndZqlSpec> {\n tableSpecs.clear();\n fullTables?.clear();\n\n const uniqueColumns = new Map<string, string[][]>();\n for (const {tableName, columns} of listIndexes(replica).filter(\n idx => idx.unique,\n )) {\n if (!uniqueColumns.has(tableName)) {\n uniqueColumns.set(tableName, []);\n }\n uniqueColumns.get(tableName)?.push(Object.keys(columns));\n }\n\n listTables(replica).forEach(fullTable => {\n fullTables?.set(fullTable.name, fullTable);\n\n // Only include columns for which the mapped ZQL Value is defined.\n const visibleColumns = Object.entries(fullTable.columns).filter(\n ([, {dataType}]) => liteTypeToZqlValueType(dataType),\n );\n const notNullColumns = new Set(\n visibleColumns\n .filter(\n ([col, {dataType}]) =>\n !nullableUpstream(dataType) || fullTable.primaryKey?.includes(col),\n )\n .map(([col]) => col),\n );\n\n // Collect all columns that are part of a unique index.\n const allKeyColumns = new Set<string>();\n\n const uniqueKeys = uniqueColumns.get(fullTable.name) ?? [];\n // Examine all column combinations that can serve as a primary key.\n const keys = uniqueKeys.filter(key => {\n if (difference(new Set(key), notNullColumns).size > 0) {\n return false; // Exclude indexes over non-visible columns.\n }\n for (const col of key) {\n allKeyColumns.add(col);\n }\n return true;\n });\n if (keys.length === 0) {\n // Only include tables with a row key.\n lc.debug?.(\n `not syncing table ${fullTable.name} because it has no primary key`,\n );\n return;\n }\n // Pick the \"best\" (i.e. shortest) key for default IVM operations.\n const primaryKey = keys.sort(keyCmp)[0];\n\n const tableSpec = {\n ...fullTable,\n columns: Object.fromEntries(visibleColumns),\n // normalize (sort) keys to minimize creating new objects.\n // See row-key.ts: normalizedKeyOrder()\n primaryKey: v.parse(primaryKey.sort(), primaryKeySchema),\n uniqueKeys: uniqueKeys.map(key => v.parse(key.sort(), primaryKeySchema)),\n allPotentialPrimaryKeys: keys.map(key =>\n v.parse(key.sort(), primaryKeySchema),\n ),\n };\n\n tableSpecs.set(tableSpec.name, {\n tableSpec,\n zqlSpec: Object.fromEntries(\n Object.entries(tableSpec.columns).map(([name, {dataType}]) => [\n name,\n mapLiteDataTypeToZqlSchemaValue(dataType),\n ]),\n ),\n });\n });\n return tableSpecs;\n}\n\nexport function mustGetTableSpec(\n tableSpecs: Map<string, LiteAndZqlSpec>,\n tableName: string,\n): LiteAndZqlSpec {\n const tableSpec = tableSpecs.get(tableName);\n if (!tableSpec) {\n throw new Error(\n `table '${tableName}' is not one of: ${[...tableSpecs.keys()]\n .filter(t => !t.includes('.') && !t.startsWith('_litestream_'))\n .sort()}. ` +\n `Check the spelling and ensure that the table has a primary key.`,\n );\n }\n return tableSpec;\n}\n\n// Deterministic comparator for favoring shorter row keys.\nfunction keyCmp(a: string[], b: string[]) {\n if (a.length !== b.length) {\n return a.length - b.length; // Fewer columns are better.\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] < b[i]) {\n return -1;\n }\n if (a[i] > b[i]) {\n return 1;\n }\n }\n return 0;\n}\n"],"names":["PostgresTypeClass.Enum","PostgresTypeClass.Base","v.parse"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lite-tables.js","sources":["../../../../../zero-cache/src/db/lite-tables.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {difference} from '../../../shared/src/set-utils.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {primaryKeySchema} from '../../../zero-protocol/src/primary-key.ts';\nimport type {Database} from '../../../zqlite/src/db.ts';\nimport {\n ColumnMetadataStore,\n metadataToLiteTypeString,\n} from '../services/replicator/schema/column-metadata.ts';\nimport {\n isArray,\n isEnum,\n liteTypeToZqlValueType,\n mapLiteDataTypeToZqlSchemaValue,\n nullableUpstream,\n} from '../types/lite.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport type {\n LiteAndZqlSpec,\n LiteIndexSpec,\n LiteTableSpec,\n MutableLiteIndexSpec,\n MutableLiteTableSpec,\n} from './specs.ts';\n\ntype ColumnInfo = {\n table: string;\n name: string;\n type: string;\n notNull: number;\n dflt: string | null;\n keyPos: number;\n};\n\nexport function listTables(db: Database): LiteTableSpec[] {\n const metadataStore = ColumnMetadataStore.getInstance(db);\n\n const columns = db\n .prepare(\n `\n SELECT\n m.name as \"table\",\n p.name as name,\n p.type as type,\n p.\"notnull\" as \"notNull\",\n p.dflt_value as \"dflt\",\n p.pk as keyPos\n FROM sqlite_master as m\n LEFT JOIN pragma_table_info(m.name) as p\n WHERE m.type = 'table'\n AND m.name NOT LIKE 'sqlite_%'\n AND m.name NOT LIKE '_zero.%'\n AND m.name NOT LIKE '_litestream_%'\n `,\n )\n .all() as ColumnInfo[];\n\n const tables: LiteTableSpec[] = [];\n let table: MutableLiteTableSpec | undefined;\n\n columns.forEach(col => {\n if (col.table !== table?.name) {\n // New table\n table = {\n name: col.table,\n columns: {},\n };\n tables.push(table);\n }\n\n // Try to read from metadata table first, fall back to SQLite column type\n let dataType: string;\n let elemPgTypeClass:\n | typeof PostgresTypeClass.Base\n | typeof PostgresTypeClass.Enum\n | null;\n\n const metadata = metadataStore?.getColumn(col.table, col.name);\n if (metadata) {\n // Read from metadata table and convert to pipe notation\n dataType = metadataToLiteTypeString(metadata);\n elemPgTypeClass = metadata.isArray\n ? metadata.isEnum\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n } else {\n // Fall back to reading from SQLite column type (pipe notation)\n dataType = col.type;\n elemPgTypeClass = isArray(col.type)\n ? isEnum(col.type)\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n }\n\n table.columns[col.name] = {\n pos: Object.keys(table.columns).length + 1,\n dataType,\n characterMaximumLength: null,\n notNull: col.notNull !== 0,\n dflt: col.dflt,\n elemPgTypeClass,\n };\n if (col.keyPos) {\n table.primaryKey ??= [];\n while (table.primaryKey.length < col.keyPos) {\n table.primaryKey.push('');\n }\n table.primaryKey[col.keyPos - 1] = col.name;\n }\n });\n\n return tables;\n}\n\nexport function listIndexes(db: Database): LiteIndexSpec[] {\n const indexes = db\n .prepare(\n `SELECT \n idx.name as indexName, \n idx.tbl_name as tableName, \n info.\"unique\" as \"unique\",\n col.name as column,\n CASE WHEN col.desc = 0 THEN 'ASC' ELSE 'DESC' END as dir\n FROM sqlite_master as idx\n JOIN pragma_index_list(idx.tbl_name) AS info ON info.name = idx.name\n JOIN pragma_index_xinfo(idx.name) as col\n WHERE idx.type = 'index' AND \n col.key = 1 AND\n idx.tbl_name NOT LIKE '_zero.%'\n ORDER BY idx.name, col.seqno ASC`,\n )\n .all() as {\n indexName: string;\n tableName: string;\n unique: number;\n column: string;\n dir: 'ASC' | 'DESC';\n }[];\n\n const ret: MutableLiteIndexSpec[] = [];\n for (const {indexName: name, tableName, unique, column, dir} of indexes) {\n if (ret.at(-1)?.name === name) {\n // Aggregate multiple column names into the array.\n must(ret.at(-1)).columns[column] = dir;\n } else {\n ret.push({\n tableName,\n name,\n columns: {[column]: dir},\n unique: unique !== 0,\n });\n }\n }\n\n return ret;\n}\n\n/**\n * Computes a TableSpec \"view\" of the replicated data that is\n * suitable for processing / consumption for the client. This\n * includes:\n * * excluding tables without a PRIMARY KEY or UNIQUE INDEX\n * * excluding columns with types that are not supported by ZQL\n * * choosing columns to use as the primary key amongst those\n * in unique indexes\n *\n * @param tableSpecs an optional map to reset and populate\n * @param fullTables an optional map to receive the full table specs,\n * which may include tables and columns that are not synced to\n * the client because they lack a primary key or are of unsupported\n * data types.\n */\nexport function computeZqlSpecs(\n lc: LogContext,\n replica: Database,\n tableSpecs: Map<string, LiteAndZqlSpec> = new Map(),\n fullTables?: Map<string, LiteTableSpec>,\n): Map<string, LiteAndZqlSpec> {\n tableSpecs.clear();\n fullTables?.clear();\n\n const uniqueColumns = new Map<string, string[][]>();\n for (const {tableName, columns} of listIndexes(replica).filter(\n idx => idx.unique,\n )) {\n if (!uniqueColumns.has(tableName)) {\n uniqueColumns.set(tableName, []);\n }\n uniqueColumns.get(tableName)?.push(Object.keys(columns));\n }\n\n listTables(replica).forEach(fullTable => {\n fullTables?.set(fullTable.name, fullTable);\n\n // Only include columns for which the mapped ZQL Value is defined.\n const visibleColumns = Object.entries(fullTable.columns).filter(\n ([, {dataType}]) => liteTypeToZqlValueType(dataType),\n );\n const notNullColumns = new Set(\n visibleColumns\n .filter(\n ([col, {dataType}]) =>\n !nullableUpstream(dataType) || fullTable.primaryKey?.includes(col),\n )\n .map(([col]) => col),\n );\n\n // Collect all columns that are part of a unique index.\n const allKeyColumns = new Set<string>();\n\n const uniqueKeys = uniqueColumns.get(fullTable.name) ?? [];\n // Examine all column combinations that can serve as a primary key.\n const keys = uniqueKeys.filter(key => {\n if (difference(new Set(key), notNullColumns).size > 0) {\n return false; // Exclude indexes over non-visible columns.\n }\n for (const col of key) {\n allKeyColumns.add(col);\n }\n return true;\n });\n if (keys.length === 0) {\n // Only include tables with a row key.\n lc.debug?.(\n `not syncing table ${fullTable.name} because it has no primary key`,\n );\n return;\n }\n // Pick the \"best\" (i.e. shortest) key for default IVM operations.\n const primaryKey = keys.sort(keyCmp)[0];\n\n const tableSpec = {\n ...fullTable,\n columns: Object.fromEntries(visibleColumns),\n // normalize (sort) keys to minimize creating new objects.\n // See row-key.ts: normalizedKeyOrder()\n primaryKey: v.parse(primaryKey.sort(), primaryKeySchema),\n uniqueKeys: uniqueKeys.map(key => v.parse(key.sort(), primaryKeySchema)),\n allPotentialPrimaryKeys: keys.map(key =>\n v.parse(key.sort(), primaryKeySchema),\n ),\n };\n\n tableSpecs.set(tableSpec.name, {\n tableSpec,\n zqlSpec: Object.fromEntries(\n Object.entries(tableSpec.columns).map(([name, {dataType}]) => [\n name,\n mapLiteDataTypeToZqlSchemaValue(dataType),\n ]),\n ),\n });\n });\n return tableSpecs;\n}\n\nexport function mustGetTableSpec(\n tableSpecs: Map<string, LiteAndZqlSpec>,\n tableName: string,\n): LiteAndZqlSpec {\n const tableSpec = tableSpecs.get(tableName);\n if (!tableSpec) {\n throw new Error(\n `table '${tableName}' is not one of: ${[...tableSpecs.keys()]\n .filter(t => !t.includes('.') && !t.startsWith('_litestream_'))\n .sort()}. ` +\n `Check the spelling and ensure that the table has a primary key.`,\n );\n }\n return tableSpec;\n}\n\n// Deterministic comparator for favoring shorter row keys.\nfunction keyCmp(a: string[], b: string[]) {\n if (a.length !== b.length) {\n return a.length - b.length; // Fewer columns are better.\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] < b[i]) {\n return -1;\n }\n if (a[i] > b[i]) {\n return 1;\n }\n }\n return 0;\n}\n"],"names":["PostgresTypeClass.Enum","PostgresTypeClass.Base","v.parse"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-lite.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/migration-lite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,KAAK,EAAC,QAAQ,IAAI,EAAE,EAAC,MAAM,2BAA2B,CAAC;AAG9D,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEpE;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,SAAS,EACzB,uBAAuB,EAAE,uBAAuB,GAC/C,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"migration-lite.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/migration-lite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,KAAK,EAAC,QAAQ,IAAI,EAAE,EAAC,MAAM,2BAA2B,CAAC;AAG9D,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEpE;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,SAAS,EACzB,uBAAuB,EAAE,uBAAuB,GAC/C,OAAO,CAAC,IAAI,CAAC,CAoHf;AAaD,eAAO,MAAM,cAAc;IACzB;;;;;;OAMG;;IAGH;;;;;;OAMG;;IAGH;;;;OAIG;;aAEH,CAAC;AAGH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAG5D,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,cAAc,CAmBxD"}
|
|
@@ -59,7 +59,10 @@ async function runSchemaMigrations(log, debugName, dbPath, setupMigration, incre
|
|
|
59
59
|
let versions2 = getVersionHistory(tx);
|
|
60
60
|
if (versions2.dataVersion < dest) {
|
|
61
61
|
versions2 = await runMigration(log, tx, versions2, dest, migration);
|
|
62
|
-
assert(
|
|
62
|
+
assert(
|
|
63
|
+
versions2.dataVersion === dest,
|
|
64
|
+
() => `Migration did not reach target version: expected ${dest}, got ${versions2.dataVersion}`
|
|
65
|
+
);
|
|
63
66
|
}
|
|
64
67
|
return versions2;
|
|
65
68
|
});
|
|
@@ -72,7 +75,10 @@ async function runSchemaMigrations(log, debugName, dbPath, setupMigration, incre
|
|
|
72
75
|
}
|
|
73
76
|
db.pragma("synchronous = NORMAL");
|
|
74
77
|
db.unsafeMode(false);
|
|
75
|
-
assert(
|
|
78
|
+
assert(
|
|
79
|
+
versions.dataVersion === codeVersion,
|
|
80
|
+
() => `Final dataVersion (${versions.dataVersion}) does not match codeVersion (${codeVersion})`
|
|
81
|
+
);
|
|
76
82
|
log.info?.(
|
|
77
83
|
`Running ${debugName} at schema v${codeVersion} (${Date.now() - start} ms)`
|
|
78
84
|
);
|
|
@@ -133,7 +139,7 @@ function getVersionHistory(db) {
|
|
|
133
139
|
return result ?? { dataVersion: 0, schemaVersion: 0, minSafeVersion: 0 };
|
|
134
140
|
}
|
|
135
141
|
function updateVersionHistory(log, db, prev, newVersion, minSafeVersion) {
|
|
136
|
-
assert(newVersion > 0);
|
|
142
|
+
assert(newVersion > 0, "newVersion must be positive");
|
|
137
143
|
const meta = {
|
|
138
144
|
...prev,
|
|
139
145
|
dataVersion: newVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-lite.js","sources":["../../../../../zero-cache/src/db/migration-lite.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {Database as Db} from '../../../zqlite/src/db.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\n\ntype Operations = (log: LogContext, tx: Db) => Promise<void> | void;\n\n/**\n * Encapsulates the logic for setting up or upgrading to a new schema. After the\n * Migration code successfully completes, {@link runSchemaMigrations}\n * will update the schema version and commit the transaction.\n */\nexport type Migration = {\n /**\n * Perform database operations that create or alter table structure. This is\n * called at most once during lifetime of the application. If a `migrateData()`\n * operation is defined, that will be performed after `migrateSchema()` succeeds.\n */\n migrateSchema?: Operations;\n\n /**\n * Perform database operations to migrate data to the new schema. This is\n * called after `migrateSchema()` (if defined), and may be called again\n * to re-migrate data after the server was rolled back to an earlier version,\n * and rolled forward again.\n *\n * Consequently, the logic in `migrateData()` must be idempotent.\n */\n migrateData?: Operations;\n\n /**\n * Sets the `minSafeVersion` to the specified value, prohibiting running\n * any earlier code versions.\n */\n minSafeVersion?: number;\n};\n\n/**\n * Mapping of incremental migrations to move from the previous old code\n * version to next one. Versions must be non-zero.\n *\n * The schema resulting from performing incremental migrations should be\n * equivalent to that of the `setupMigration` on a blank database.\n *\n * The highest destinationVersion of this map denotes the current\n * \"code version\", and is also used as the destination version when\n * running the initial setup migration on a blank database.\n */\nexport type IncrementalMigrationMap = {\n [destinationVersion: number]: Migration;\n};\n\n/**\n * Ensures that the schema is compatible with the current code, updating and\n * migrating the schema if necessary.\n */\nexport async function runSchemaMigrations(\n log: LogContext,\n debugName: string,\n dbPath: string,\n setupMigration: Migration,\n incrementalMigrationMap: IncrementalMigrationMap,\n): Promise<void> {\n const start = Date.now();\n log = log.withContext(\n 'initSchema',\n randInt(0, Number.MAX_SAFE_INTEGER).toString(36),\n );\n const db = new Database(log, dbPath);\n\n try {\n const versionMigrations = sorted(incrementalMigrationMap);\n assert(\n versionMigrations.length,\n `Must specify a at least one version migration`,\n );\n assert(\n versionMigrations[0][0] > 0,\n `Versions must be non-zero positive numbers`,\n );\n const codeVersion = versionMigrations[versionMigrations.length - 1][0];\n log.info?.(\n `Checking schema for compatibility with ${debugName} at schema v${codeVersion}`,\n );\n\n let versions = await runTransaction(log, db, tx => {\n const versions = getVersionHistory(tx);\n if (codeVersion < versions.minSafeVersion) {\n throw new Error(\n `Cannot run ${debugName} at schema v${codeVersion} because rollback limit is v${versions.minSafeVersion}`,\n );\n }\n\n if (versions.dataVersion > codeVersion) {\n log.info?.(\n `Data is at v${versions.dataVersion}. Resetting to v${codeVersion}`,\n );\n return updateVersionHistory(log, tx, versions, codeVersion);\n }\n return versions;\n });\n\n if (versions.dataVersion < codeVersion) {\n db.unsafeMode(true); // Enables journal_mode = OFF\n db.pragma('locking_mode = EXCLUSIVE');\n db.pragma('foreign_keys = OFF');\n db.pragma('journal_mode = OFF');\n db.pragma('synchronous = OFF');\n // Unfortunately, AUTO_VACUUM is not compatible with BEGIN CONCURRENT,\n // so it is not an option for the replica file.\n // https://sqlite.org/forum/forumpost/25f183416a\n // db.pragma('auto_vacuum = INCREMENTAL');\n\n const migrations =\n versions.dataVersion === 0\n ? // For the empty database v0, only run the setup migration.\n ([[codeVersion, setupMigration]] as const)\n : versionMigrations;\n\n for (const [dest, migration] of migrations) {\n if (versions.dataVersion < dest) {\n log.info?.(\n `Migrating schema from v${versions.dataVersion} to v${dest}`,\n );\n void log.flush(); // Flush logs before each migration to help debug crash-y migrations.\n\n versions = await runTransaction(log, db, async tx => {\n // Fetch meta from within the transaction to make the migration atomic.\n let versions = getVersionHistory(tx);\n if (versions.dataVersion < dest) {\n versions = await runMigration(log, tx, versions, dest, migration);\n assert(versions.dataVersion === dest);\n }\n return versions;\n });\n }\n }\n\n db.exec('ANALYZE main');\n log.info?.('ANALYZE completed');\n } else {\n // Run optimize whenever opening an sqlite db file as recommended in\n // https://www.sqlite.org/pragma.html#pragma_optimize\n // It is important to run the same initialization steps as is done\n // in the view-syncer (i.e. when preparing database for serving\n // replication) so that any corruption detected in the view-syncer is\n // similarly detected in the change-streamer, facilitating an eventual\n // recovery by resyncing the replica anew.\n db.pragma('optimize = 0x10002');\n\n // TODO: Investigate running `integrity_check` or `quick_check` as well,\n // provided that they are not inordinately expensive on large databases.\n }\n\n db.pragma('synchronous = NORMAL');\n db.unsafeMode(false);\n\n assert(versions.dataVersion === codeVersion);\n log.info?.(\n `Running ${debugName} at schema v${codeVersion} (${\n Date.now() - start\n } ms)`,\n );\n } catch (e) {\n log.error?.('Error in ensureSchemaMigrated', e);\n throw e;\n } finally {\n db.close();\n void log.flush(); // Flush the logs but do not block server progress on it.\n }\n}\n\nfunction sorted(\n incrementalMigrationMap: IncrementalMigrationMap,\n): [number, Migration][] {\n const versionMigrations: [number, Migration][] = [];\n for (const [v, m] of Object.entries(incrementalMigrationMap)) {\n versionMigrations.push([Number(v), m]);\n }\n return versionMigrations.sort(([a], [b]) => a - b);\n}\n\n// Exposed for tests.\nexport const versionHistory = v.object({\n /**\n * The `schemaVersion` is highest code version that has ever been run\n * on the database, and is used to delineate the structure of the tables\n * in the database. A schemaVersion only moves forward; rolling back to\n * an earlier (safe) code version does not revert schema changes that\n * have already been applied.\n */\n schemaVersion: v.number(),\n\n /**\n * The data version is the code version of the latest server that ran.\n * Note that this may be less than the schemaVersion in the case that\n * a server is rolled back to an earlier version after a schema change.\n * In such a case, data (but not schema), may need to be re-migrated\n * when rolling forward again.\n */\n dataVersion: v.number(),\n\n /**\n * The minimum code version that is safe to run. This is used when\n * a schema migration is not backwards compatible with an older version\n * of the code.\n */\n minSafeVersion: v.number(),\n});\n\n// Exposed for tests.\nexport type VersionHistory = v.Infer<typeof versionHistory>;\n\n// Exposed for tests\nexport function getVersionHistory(db: Db): VersionHistory {\n // Note: The `lock` column transparently ensures that at most one row exists.\n db.prepare(\n `\n CREATE TABLE IF NOT EXISTS \"_zero.versionHistory\" (\n dataVersion INTEGER NOT NULL,\n schemaVersion INTEGER NOT NULL,\n minSafeVersion INTEGER NOT NULL,\n\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n `,\n ).run();\n const result = db\n .prepare(\n 'SELECT dataVersion, schemaVersion, minSafeVersion FROM \"_zero.versionHistory\"',\n )\n .get() as VersionHistory;\n return result ?? {dataVersion: 0, schemaVersion: 0, minSafeVersion: 0};\n}\n\nfunction updateVersionHistory(\n log: LogContext,\n db: Db,\n prev: VersionHistory,\n newVersion: number,\n minSafeVersion?: number,\n): VersionHistory {\n assert(newVersion > 0);\n const meta = {\n ...prev,\n dataVersion: newVersion,\n // The schemaVersion never moves backwards.\n schemaVersion: Math.max(newVersion, prev.schemaVersion),\n minSafeVersion: getMinSafeVersion(log, prev, minSafeVersion),\n } satisfies VersionHistory;\n\n db.prepare(\n `\n INSERT INTO \"_zero.versionHistory\" (dataVersion, schemaVersion, minSafeVersion, lock)\n VALUES (@dataVersion, @schemaVersion, @minSafeVersion, 1)\n ON CONFLICT (lock) DO UPDATE\n SET dataVersion=@dataVersion,\n schemaVersion=@schemaVersion,\n minSafeVersion=@minSafeVersion\n `,\n ).run(meta);\n\n return meta;\n}\n\nasync function runMigration(\n log: LogContext,\n tx: Db,\n versions: VersionHistory,\n destinationVersion: number,\n migration: Migration,\n): Promise<VersionHistory> {\n if (versions.schemaVersion < destinationVersion) {\n await migration.migrateSchema?.(log, tx);\n }\n if (versions.dataVersion < destinationVersion) {\n await migration.migrateData?.(log, tx);\n }\n return updateVersionHistory(\n log,\n tx,\n versions,\n destinationVersion,\n migration.minSafeVersion,\n );\n}\n\n/**\n * Bumps the rollback limit [[toAtLeast]] the specified version.\n * Leaves the rollback limit unchanged if it is equal or greater.\n */\nfunction getMinSafeVersion(\n log: LogContext,\n current: VersionHistory,\n proposedSafeVersion?: number,\n): number {\n if (proposedSafeVersion === undefined) {\n return current.minSafeVersion;\n }\n if (current.minSafeVersion >= proposedSafeVersion) {\n // The rollback limit must never move backwards.\n log.debug?.(\n `rollback limit is already at ${current.minSafeVersion}, ` +\n `don't need to bump to ${proposedSafeVersion}`,\n );\n return current.minSafeVersion;\n }\n log.info?.(\n `bumping rollback limit from ${current.minSafeVersion} to ${proposedSafeVersion}`,\n );\n return proposedSafeVersion;\n}\n\n// Note: We use a custom transaction wrapper (instead of db.begin(...)) in order\n// to support async operations within the transaction.\nasync function runTransaction<T>(\n log: LogContext,\n db: Db,\n tx: (db: Db) => Promise<T> | T,\n): Promise<T> {\n db.prepare('BEGIN EXCLUSIVE').run();\n try {\n const result = await tx(db);\n db.prepare('COMMIT').run();\n return result;\n } catch (e) {\n log.error?.('Aborted transaction due to error', e);\n db.prepare('ROLLBACK').run();\n throw e;\n }\n}\n"],"names":["versions","v","v.object","v.number"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"migration-lite.js","sources":["../../../../../zero-cache/src/db/migration-lite.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {Database as Db} from '../../../zqlite/src/db.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\n\ntype Operations = (log: LogContext, tx: Db) => Promise<void> | void;\n\n/**\n * Encapsulates the logic for setting up or upgrading to a new schema. After the\n * Migration code successfully completes, {@link runSchemaMigrations}\n * will update the schema version and commit the transaction.\n */\nexport type Migration = {\n /**\n * Perform database operations that create or alter table structure. This is\n * called at most once during lifetime of the application. If a `migrateData()`\n * operation is defined, that will be performed after `migrateSchema()` succeeds.\n */\n migrateSchema?: Operations;\n\n /**\n * Perform database operations to migrate data to the new schema. This is\n * called after `migrateSchema()` (if defined), and may be called again\n * to re-migrate data after the server was rolled back to an earlier version,\n * and rolled forward again.\n *\n * Consequently, the logic in `migrateData()` must be idempotent.\n */\n migrateData?: Operations;\n\n /**\n * Sets the `minSafeVersion` to the specified value, prohibiting running\n * any earlier code versions.\n */\n minSafeVersion?: number;\n};\n\n/**\n * Mapping of incremental migrations to move from the previous old code\n * version to next one. Versions must be non-zero.\n *\n * The schema resulting from performing incremental migrations should be\n * equivalent to that of the `setupMigration` on a blank database.\n *\n * The highest destinationVersion of this map denotes the current\n * \"code version\", and is also used as the destination version when\n * running the initial setup migration on a blank database.\n */\nexport type IncrementalMigrationMap = {\n [destinationVersion: number]: Migration;\n};\n\n/**\n * Ensures that the schema is compatible with the current code, updating and\n * migrating the schema if necessary.\n */\nexport async function runSchemaMigrations(\n log: LogContext,\n debugName: string,\n dbPath: string,\n setupMigration: Migration,\n incrementalMigrationMap: IncrementalMigrationMap,\n): Promise<void> {\n const start = Date.now();\n log = log.withContext(\n 'initSchema',\n randInt(0, Number.MAX_SAFE_INTEGER).toString(36),\n );\n const db = new Database(log, dbPath);\n\n try {\n const versionMigrations = sorted(incrementalMigrationMap);\n assert(\n versionMigrations.length,\n `Must specify a at least one version migration`,\n );\n assert(\n versionMigrations[0][0] > 0,\n `Versions must be non-zero positive numbers`,\n );\n const codeVersion = versionMigrations[versionMigrations.length - 1][0];\n log.info?.(\n `Checking schema for compatibility with ${debugName} at schema v${codeVersion}`,\n );\n\n let versions = await runTransaction(log, db, tx => {\n const versions = getVersionHistory(tx);\n if (codeVersion < versions.minSafeVersion) {\n throw new Error(\n `Cannot run ${debugName} at schema v${codeVersion} because rollback limit is v${versions.minSafeVersion}`,\n );\n }\n\n if (versions.dataVersion > codeVersion) {\n log.info?.(\n `Data is at v${versions.dataVersion}. Resetting to v${codeVersion}`,\n );\n return updateVersionHistory(log, tx, versions, codeVersion);\n }\n return versions;\n });\n\n if (versions.dataVersion < codeVersion) {\n db.unsafeMode(true); // Enables journal_mode = OFF\n db.pragma('locking_mode = EXCLUSIVE');\n db.pragma('foreign_keys = OFF');\n db.pragma('journal_mode = OFF');\n db.pragma('synchronous = OFF');\n // Unfortunately, AUTO_VACUUM is not compatible with BEGIN CONCURRENT,\n // so it is not an option for the replica file.\n // https://sqlite.org/forum/forumpost/25f183416a\n // db.pragma('auto_vacuum = INCREMENTAL');\n\n const migrations =\n versions.dataVersion === 0\n ? // For the empty database v0, only run the setup migration.\n ([[codeVersion, setupMigration]] as const)\n : versionMigrations;\n\n for (const [dest, migration] of migrations) {\n if (versions.dataVersion < dest) {\n log.info?.(\n `Migrating schema from v${versions.dataVersion} to v${dest}`,\n );\n void log.flush(); // Flush logs before each migration to help debug crash-y migrations.\n\n versions = await runTransaction(log, db, async tx => {\n // Fetch meta from within the transaction to make the migration atomic.\n let versions = getVersionHistory(tx);\n if (versions.dataVersion < dest) {\n versions = await runMigration(log, tx, versions, dest, migration);\n assert(\n versions.dataVersion === dest,\n () =>\n `Migration did not reach target version: expected ${dest}, got ${versions.dataVersion}`,\n );\n }\n return versions;\n });\n }\n }\n\n db.exec('ANALYZE main');\n log.info?.('ANALYZE completed');\n } else {\n // Run optimize whenever opening an sqlite db file as recommended in\n // https://www.sqlite.org/pragma.html#pragma_optimize\n // It is important to run the same initialization steps as is done\n // in the view-syncer (i.e. when preparing database for serving\n // replication) so that any corruption detected in the view-syncer is\n // similarly detected in the change-streamer, facilitating an eventual\n // recovery by resyncing the replica anew.\n db.pragma('optimize = 0x10002');\n\n // TODO: Investigate running `integrity_check` or `quick_check` as well,\n // provided that they are not inordinately expensive on large databases.\n }\n\n db.pragma('synchronous = NORMAL');\n db.unsafeMode(false);\n\n assert(\n versions.dataVersion === codeVersion,\n () =>\n `Final dataVersion (${versions.dataVersion}) does not match codeVersion (${codeVersion})`,\n );\n log.info?.(\n `Running ${debugName} at schema v${codeVersion} (${\n Date.now() - start\n } ms)`,\n );\n } catch (e) {\n log.error?.('Error in ensureSchemaMigrated', e);\n throw e;\n } finally {\n db.close();\n void log.flush(); // Flush the logs but do not block server progress on it.\n }\n}\n\nfunction sorted(\n incrementalMigrationMap: IncrementalMigrationMap,\n): [number, Migration][] {\n const versionMigrations: [number, Migration][] = [];\n for (const [v, m] of Object.entries(incrementalMigrationMap)) {\n versionMigrations.push([Number(v), m]);\n }\n return versionMigrations.sort(([a], [b]) => a - b);\n}\n\n// Exposed for tests.\nexport const versionHistory = v.object({\n /**\n * The `schemaVersion` is highest code version that has ever been run\n * on the database, and is used to delineate the structure of the tables\n * in the database. A schemaVersion only moves forward; rolling back to\n * an earlier (safe) code version does not revert schema changes that\n * have already been applied.\n */\n schemaVersion: v.number(),\n\n /**\n * The data version is the code version of the latest server that ran.\n * Note that this may be less than the schemaVersion in the case that\n * a server is rolled back to an earlier version after a schema change.\n * In such a case, data (but not schema), may need to be re-migrated\n * when rolling forward again.\n */\n dataVersion: v.number(),\n\n /**\n * The minimum code version that is safe to run. This is used when\n * a schema migration is not backwards compatible with an older version\n * of the code.\n */\n minSafeVersion: v.number(),\n});\n\n// Exposed for tests.\nexport type VersionHistory = v.Infer<typeof versionHistory>;\n\n// Exposed for tests\nexport function getVersionHistory(db: Db): VersionHistory {\n // Note: The `lock` column transparently ensures that at most one row exists.\n db.prepare(\n `\n CREATE TABLE IF NOT EXISTS \"_zero.versionHistory\" (\n dataVersion INTEGER NOT NULL,\n schemaVersion INTEGER NOT NULL,\n minSafeVersion INTEGER NOT NULL,\n\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n `,\n ).run();\n const result = db\n .prepare(\n 'SELECT dataVersion, schemaVersion, minSafeVersion FROM \"_zero.versionHistory\"',\n )\n .get() as VersionHistory;\n return result ?? {dataVersion: 0, schemaVersion: 0, minSafeVersion: 0};\n}\n\nfunction updateVersionHistory(\n log: LogContext,\n db: Db,\n prev: VersionHistory,\n newVersion: number,\n minSafeVersion?: number,\n): VersionHistory {\n assert(newVersion > 0, 'newVersion must be positive');\n const meta = {\n ...prev,\n dataVersion: newVersion,\n // The schemaVersion never moves backwards.\n schemaVersion: Math.max(newVersion, prev.schemaVersion),\n minSafeVersion: getMinSafeVersion(log, prev, minSafeVersion),\n } satisfies VersionHistory;\n\n db.prepare(\n `\n INSERT INTO \"_zero.versionHistory\" (dataVersion, schemaVersion, minSafeVersion, lock)\n VALUES (@dataVersion, @schemaVersion, @minSafeVersion, 1)\n ON CONFLICT (lock) DO UPDATE\n SET dataVersion=@dataVersion,\n schemaVersion=@schemaVersion,\n minSafeVersion=@minSafeVersion\n `,\n ).run(meta);\n\n return meta;\n}\n\nasync function runMigration(\n log: LogContext,\n tx: Db,\n versions: VersionHistory,\n destinationVersion: number,\n migration: Migration,\n): Promise<VersionHistory> {\n if (versions.schemaVersion < destinationVersion) {\n await migration.migrateSchema?.(log, tx);\n }\n if (versions.dataVersion < destinationVersion) {\n await migration.migrateData?.(log, tx);\n }\n return updateVersionHistory(\n log,\n tx,\n versions,\n destinationVersion,\n migration.minSafeVersion,\n );\n}\n\n/**\n * Bumps the rollback limit [[toAtLeast]] the specified version.\n * Leaves the rollback limit unchanged if it is equal or greater.\n */\nfunction getMinSafeVersion(\n log: LogContext,\n current: VersionHistory,\n proposedSafeVersion?: number,\n): number {\n if (proposedSafeVersion === undefined) {\n return current.minSafeVersion;\n }\n if (current.minSafeVersion >= proposedSafeVersion) {\n // The rollback limit must never move backwards.\n log.debug?.(\n `rollback limit is already at ${current.minSafeVersion}, ` +\n `don't need to bump to ${proposedSafeVersion}`,\n );\n return current.minSafeVersion;\n }\n log.info?.(\n `bumping rollback limit from ${current.minSafeVersion} to ${proposedSafeVersion}`,\n );\n return proposedSafeVersion;\n}\n\n// Note: We use a custom transaction wrapper (instead of db.begin(...)) in order\n// to support async operations within the transaction.\nasync function runTransaction<T>(\n log: LogContext,\n db: Db,\n tx: (db: Db) => Promise<T> | T,\n): Promise<T> {\n db.prepare('BEGIN EXCLUSIVE').run();\n try {\n const result = await tx(db);\n db.prepare('COMMIT').run();\n return result;\n } catch (e) {\n log.error?.('Aborted transaction due to error', e);\n db.prepare('ROLLBACK').run();\n throw e;\n }\n}\n"],"names":["versions","v","v.object","v.number"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAGrC,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AAExB,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,EACd,cAAc,EAAE,SAAS,EACzB,uBAAuB,EAAE,uBAAuB,GAC/C,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAGrC,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AAExB,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC;IAEzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,EACd,cAAc,EAAE,SAAS,EACzB,uBAAuB,EAAE,uBAAuB,GAC/C,OAAO,CAAC,IAAI,CAAC,CAsFf;AAaD,eAAO,MAAM,cAAc;IACzB;;;;;;OAMG;;IAGH;;;;;;OAMG;;IAGH;;;;OAIG;;aAEH,CAAC;AAGH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAG5D,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,QAAQ,CAAC,GAAG,EACjB,UAAU,EAAE,MAAM,iBAcnB;AASD,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,QAAQ,CAAC,GAAG,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,UAAQ,GACb,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAuBhC"}
|
|
@@ -57,14 +57,20 @@ async function runSchemaMigrations(log, debugName, schemaName, db, setupMigratio
|
|
|
57
57
|
dest,
|
|
58
58
|
migration
|
|
59
59
|
);
|
|
60
|
-
assert(
|
|
60
|
+
assert(
|
|
61
|
+
versions2.dataVersion === dest,
|
|
62
|
+
() => `Migration did not reach target version: expected ${dest}, got ${versions2.dataVersion}`
|
|
63
|
+
);
|
|
61
64
|
}
|
|
62
65
|
return versions2;
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
|
-
assert(
|
|
70
|
+
assert(
|
|
71
|
+
versions.dataVersion === codeVersion,
|
|
72
|
+
() => `Final dataVersion (${versions.dataVersion}) does not match codeVersion (${codeVersion})`
|
|
73
|
+
);
|
|
68
74
|
log.info?.(`Running ${debugName} at schema v${codeVersion}`);
|
|
69
75
|
} catch (e) {
|
|
70
76
|
log.error?.("Error in ensureSchemaMigrated", e);
|
|
@@ -141,7 +147,7 @@ async function getVersionHistory(sql, schemaName, create = false) {
|
|
|
141
147
|
return parse(rows[0], versionHistory);
|
|
142
148
|
}
|
|
143
149
|
async function updateVersionHistory(log, sql, schemaName, prev, newVersion, minSafeVersion) {
|
|
144
|
-
assert(newVersion > 0);
|
|
150
|
+
assert(newVersion > 0, "newVersion must be positive");
|
|
145
151
|
const versions = {
|
|
146
152
|
dataVersion: newVersion,
|
|
147
153
|
// The schemaVersion never moves backwards.
|