@rocicorp/zero 1.10.0-canary.2 → 1.10.0-canary.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/replicache/src/btree/read.d.ts +12 -1
- package/out/replicache/src/btree/read.d.ts.map +1 -1
- package/out/replicache/src/btree/read.js +24 -12
- package/out/replicache/src/btree/read.js.map +1 -1
- package/out/replicache/src/db/rebase.d.ts +14 -2
- package/out/replicache/src/db/rebase.d.ts.map +1 -1
- package/out/replicache/src/db/rebase.js +27 -5
- package/out/replicache/src/db/rebase.js.map +1 -1
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +27 -4
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +11 -0
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +43 -3
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/persist/persist.d.ts.map +1 -1
- package/out/replicache/src/persist/persist.js +12 -5
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js +6 -2
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +14 -2
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/replicache-options.d.ts +9 -0
- package/out/replicache/src/replicache-options.d.ts.map +1 -1
- package/out/replicache/src/transactions.d.ts.map +1 -1
- package/out/replicache/src/transactions.js +1 -1
- package/out/replicache/src/transactions.js.map +1 -1
- package/out/shared/src/error.d.ts +11 -0
- package/out/shared/src/error.d.ts.map +1 -1
- package/out/shared/src/error.js +35 -1
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/hash.d.ts.map +1 -1
- package/out/shared/src/hash.js +105 -10
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/map.d.ts +7 -2
- package/out/shared/src/map.d.ts.map +1 -1
- package/out/shared/src/map.js.map +1 -1
- package/out/shared/src/promise-race.d.ts +14 -0
- package/out/shared/src/promise-race.d.ts.map +1 -1
- package/out/shared/src/promise-race.js +32 -1
- package/out/shared/src/promise-race.js.map +1 -1
- package/out/zero/package.js +2 -3
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +8 -1
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/normalize.d.ts +5 -0
- package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
- package/out/zero-cache/src/config/normalize.js +16 -3
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +36 -7
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +121 -46
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/db/create.d.ts.map +1 -1
- package/out/zero-cache/src/db/create.js +4 -1
- package/out/zero-cache/src/db/create.js.map +1 -1
- package/out/zero-cache/src/db/index-predicate.d.ts +4 -0
- package/out/zero-cache/src/db/index-predicate.d.ts.map +1 -0
- package/out/zero-cache/src/db/index-predicate.js +46 -0
- package/out/zero-cache/src/db/index-predicate.js.map +1 -0
- package/out/zero-cache/src/db/lite-tables.d.ts +10 -2
- package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js +8 -3
- 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 +5 -3
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/specs.d.ts +41 -0
- package/out/zero-cache/src/db/specs.d.ts.map +1 -1
- package/out/zero-cache/src/db/specs.js +30 -3
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.d.ts +5 -15
- package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js +39 -73
- package/out/zero-cache/src/db/transaction-pool.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 +27 -15
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +4 -4
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +8 -4
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +1 -1
- package/out/zero-cache/src/server/worker-urls.d.ts +0 -1
- package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js +1 -2
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +1 -0
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts +12 -2
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -3
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +10 -41
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +3 -2
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +42 -16
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +34 -2
- 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 +47 -19
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js +20 -11
- package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +1 -0
- 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 +58 -80
- 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 +20 -0
- 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 +43 -3
- 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/replication-slots.d.ts +24 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +35 -5
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +1513 -229
- 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 +2 -2
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +19 -0
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +581 -0
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +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 +15 -6
- 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 +137 -20
- 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 +82 -13
- 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 +128 -21
- 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 +5 -5
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +2 -0
- 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/downstream.d.ts +3 -0
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +1 -4
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +11 -18
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +8 -3
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js +4 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts +34 -0
- package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js +67 -0
- package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +2 -4
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +8 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +34 -13
- 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 +165 -36
- 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 +4 -9
- 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.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +12 -3
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +11 -5
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts +11 -0
- package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/shard-sampling.js +19 -0
- package/out/zero-cache/src/services/change-streamer/shard-sampling.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +11 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +38 -5
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +25 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +95 -12
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts +83 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +417 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts +73 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js +157 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +8 -3
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +63 -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 +217 -49
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +8 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +10 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +29 -0
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +154 -0
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -0
- package/out/zero-cache/src/services/http-service.d.ts +1 -0
- package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/http-service.js +7 -3
- package/out/zero-cache/src/services/http-service.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 +16 -7
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/config-v5.yml +15 -1
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +131 -0
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -0
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +70 -0
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -0
- package/out/zero-cache/src/services/litestream/litestream-controller.js +101 -0
- package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -0
- package/out/zero-cache/src/services/litestream/metrics.d.ts +1 -0
- package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/metrics.js +4 -1
- package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-db.d.ts +10 -0
- package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-db.js +15 -1
- package/out/zero-cache/src/services/replicator/change-log-db.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 +18 -21
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +2 -6
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js +2 -2
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +3 -2
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +8 -6
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +27 -2
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js +3 -2
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +24 -5
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +3 -2
- 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 +25 -17
- 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.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +14 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +3 -3
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +6 -2
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/downstream.d.ts +11 -0
- package/out/zero-cache/src/types/downstream.d.ts.map +1 -0
- package/out/zero-cache/src/types/pg.d.ts +15 -2
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +21 -8
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.d.ts +18 -0
- package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -0
- package/out/zero-cache/src/types/poke-chunk.js +57 -0
- package/out/zero-cache/src/types/poke-chunk.js.map +1 -0
- package/out/zero-cache/src/types/processes.js +1 -4
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts +4 -9
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/subscription.d.ts +2 -11
- package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
- package/out/zero-cache/src/types/subscription.js +4 -25
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/workers/connection.d.ts +22 -4
- package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connection.js +112 -14
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts +1 -0
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +4 -2
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +46 -0
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/context.d.ts +15 -0
- package/out/zero-client/src/client/context.d.ts.map +1 -1
- package/out/zero-client/src/client/context.js +44 -0
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js +19 -12
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.d.ts +7 -2
- package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js +52 -2
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-client/src/client/zero-rep.d.ts +1 -0
- package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-rep.js +9 -6
- package/out/zero-client/src/client/zero-rep.js.map +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +98 -25
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.d.ts +1 -1
- package/out/zero-protocol/src/down.d.ts +1 -1
- package/out/zero-protocol/src/error.d.ts +4 -4
- package/out/zero-protocol/src/inspect-down.js +1 -1
- package/out/zero-protocol/src/mutate-server.d.ts +2 -2
- package/out/zero-protocol/src/poke.d.ts +89 -7
- package/out/zero-protocol/src/poke.d.ts.map +1 -1
- package/out/zero-protocol/src/poke.js +16 -8
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts +12 -7
- package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +1 -1
- package/out/zero-protocol/src/query-server.d.ts +2 -2
- package/out/zql/src/ivm/array-view.d.ts.map +1 -1
- package/out/zql/src/ivm/array-view.js +5 -3
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js +8 -3
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/deferred-input.d.ts +44 -0
- package/out/zql/src/ivm/deferred-input.d.ts.map +1 -0
- package/out/zql/src/ivm/deferred-input.js +88 -0
- package/out/zql/src/ivm/deferred-input.js.map +1 -0
- package/out/zql/src/ivm/memory-source.d.ts +10 -2
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +19 -16
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/stream.d.ts +0 -1
- package/out/zql/src/ivm/stream.d.ts.map +1 -1
- package/out/zql/src/ivm/stream.js +1 -7
- package/out/zql/src/ivm/stream.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +11 -2
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +6 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts +6 -0
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +73 -10
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-delegate.d.ts +22 -0
- package/out/zql/src/query/query-delegate.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js +2 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js +1 -0
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +3 -2
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +2 -3
- package/out/zero-cache/src/server/backup-watermark-poller.js +0 -31
- package/out/zero-cache/src/server/backup-watermark-poller.js.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts +0 -57
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js +0 -137
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replicator.js","names":[],"sources":["../../../../../zero-cache/src/workers/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport type {ReplicaOptions} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {upgradeReplica} from '../services/change-source/common/replica-schema.ts';\nimport {deleteChangeLogDB} from '../services/replicator/change-log-db.ts';\nimport {Notifier} from '../services/replicator/notifier.ts';\nimport type {\n ReplicaState,\n ReplicaStateNotifier,\n Replicator,\n} from '../services/replicator/replicator.ts';\nimport {\n getAscendingEvents,\n recordEvent,\n} from '../services/replicator/schema/replication-state.ts';\nimport {\n applyPragmas,\n type PragmaConfig,\n} from '../services/replicator/write-worker-client.ts';\nimport type {Worker} from '../types/processes.ts';\n\nexport const replicaFileModeSchema = v.literalUnion(\n 'serving',\n 'serving-copy',\n 'backup',\n);\n\nexport type ReplicaFileMode = v.Infer<typeof replicaFileModeSchema>;\n\nexport type WalMode = 'wal' | 'wal2';\n\nexport function replicaFileName(replicaFile: string, mode: ReplicaFileMode) {\n return mode === 'serving-copy' ? `${replicaFile}-serving-copy` : replicaFile;\n}\n\n/**\n * Whether a replicator worker deletes the change-log file beside its replica at\n * startup.\n *\n * The change log belongs to the change-streamer, so this only ever cleans up a\n * file that nothing is writing. The predicate is deliberately the config flag\n * and nothing else: keying it on anything derived per replica -- a file mode, or\n * the retired `logsChangeStream` -- would make every replicator start unlink the\n * change-streamer's live log, because no replicator writes it any more. Paths\n * coincide whenever `fileMode !== 'serving-copy'`, so that hazard is real in the\n * shipped no-`backupURL` configuration.\n *\n * Deleting is safe when nothing writes the log: it is excluded from the\n * litestream backup, only the writer purges it, and re-enabling the writer\n * reseeds at the resume watermark.\n */\nexport function replicatorDeletesStaleChangeLog(\n sqliteChangeLogMode: string,\n): boolean {\n return sqliteChangeLogMode === 'off';\n}\n\n/**\n * The `mode=off` cleanup itself: deletes the change-log file beside `dbPath`\n * when {@link replicatorDeletesStaleChangeLog} says nothing writes it, and\n * reports whether it did. The decision and the delete live in one function so\n * that the guard test covers the call the replicator actually makes, not just\n * the predicate.\n */\nexport function deleteStaleChangeLog(\n sqliteChangeLogMode: string,\n dbPath: string,\n): boolean {\n if (!replicatorDeletesStaleChangeLog(sqliteChangeLogMode)) {\n return false;\n }\n deleteChangeLogDB(dbPath);\n return true;\n}\n\nconst MILLIS_PER_HOUR = 1000 * 60 * 60;\nconst MB = 1024 * 1024;\n\nasync function prepare(\n lc: LogContext,\n {file, vacuumIntervalHours}: ReplicaOptions,\n walMode: WalMode,\n mode: ReplicaFileMode,\n): Promise<{file: string; walMode: WalMode}> {\n // Perform any upgrades to the replica in case the backup is an\n // earlier version.\n await upgradeReplica(lc, `${mode}-replica`, file);\n\n const replica = new Database(lc, file);\n try {\n // Start by folding any (e.g. restored) WAL(2) files into the main db.\n await setJournalMode(lc, replica, 'delete');\n\n const [{page_size: pageSize}] = replica.pragma<{page_size: number}>(\n 'page_size',\n );\n const [{page_count: pageCount}] = replica.pragma<{page_count: number}>(\n 'page_count',\n );\n const [{freelist_count: freelistCount}] = replica.pragma<{\n freelist_count: number;\n }>('freelist_count');\n\n const dbSize = ((pageCount * pageSize) / MB).toFixed(2);\n const freelistSize = ((freelistCount * pageSize) / MB).toFixed(2);\n\n // TODO: Consider adding a freelist size or ratio based vacuum trigger.\n lc.info?.(`Size of db ${file}: ${dbSize} MB (${freelistSize} MB freeable)`);\n\n // Check for the VACUUM threshold.\n const events = getAscendingEvents(replica);\n lc.debug?.(`Runtime events for db ${file}`, {events});\n if (vacuumIntervalHours !== undefined) {\n const millisSinceLastEvent =\n Date.now() - (events.at(-1)?.timestamp.getTime() ?? 0);\n if (millisSinceLastEvent / MILLIS_PER_HOUR > vacuumIntervalHours) {\n lc.info?.(`Performing maintenance cleanup on ${file}`);\n const t0 = performance.now();\n replica.unsafeMode(true);\n replica.pragma('journal_mode = OFF');\n replica.exec('VACUUM');\n recordEvent(replica, 'vacuum');\n replica.unsafeMode(false);\n const t1 = performance.now();\n lc.info?.(`VACUUM completed (${t1 - t0} ms)`);\n }\n }\n\n await setJournalMode(lc, replica, walMode);\n\n const pragmas = getPragmaConfig(mode);\n applyPragmas(replica, pragmas);\n\n replica.pragma('optimize = 0x10002');\n lc.info?.(`optimized ${file}`);\n } finally {\n replica.close();\n }\n return {file, walMode};\n}\n\n// Setting the journal_mode requires an exclusive lock on the replica.\n// Add resilience against random replica reads (for stats, etc.) by\n// retrying if the database is locked. Note that the busy_timeout doesn't\n// work here.\nasync function setJournalMode(\n lc: LogContext,\n replica: Database,\n mode: 'delete' | 'wal' | 'wal2',\n) {\n lc.info?.(`setting ${replica.name} to ${mode} mode`);\n let err: unknown;\n for (let i = 0; i < 5; i++) {\n try {\n replica.pragma(`journal_mode = ${mode}`);\n return;\n } catch (e) {\n lc.warn?.(`error setting journal_mode to ${mode} (attempt ${i + 1})`, e);\n err = e;\n }\n await sleep(500);\n }\n throw err;\n}\n\n/**\n * Returns the PragmaConfig for a given replica file mode.\n * This is used by both the main thread (setupReplica) and\n * the write worker thread to apply the same pragma settings.\n */\nexport function getPragmaConfig(mode: ReplicaFileMode): PragmaConfig {\n return {\n busyTimeout: 30000,\n analysisLimit: 1000,\n walAutocheckpoint: mode === 'backup' ? 0 : undefined,\n };\n}\n\nexport function setupReplica(\n lc: LogContext,\n mode: ReplicaFileMode,\n replicaOptions: ReplicaOptions,\n) {\n lc.info?.(`setting up ${mode} replica`);\n\n switch (mode) {\n case 'backup':\n return prepare(lc, replicaOptions, 'wal', mode);\n\n case 'serving-copy': {\n // In 'serving-copy' mode, the original file is being used for 'backup'\n // mode, so we make a copy for servicing sync requests.\n const {file} = replicaOptions;\n const copyLocation = replicaFileName(file, mode);\n deleteLiteDB(copyLocation);\n\n const start = Date.now();\n lc.info?.(`copying ${file} to ${copyLocation}`);\n const replica = new Database(lc, file);\n replica.prepare(`VACUUM INTO ?`).run(copyLocation);\n replica.close();\n lc.info?.(`finished copy (${Date.now() - start} ms)`);\n\n return prepare(lc, {...replicaOptions, file: copyLocation}, 'wal2', mode);\n }\n\n case 'serving':\n return prepare(lc, replicaOptions, 'wal2', mode);\n\n default:\n throw new Error(`Invalid ReplicaMode ${mode}`);\n }\n}\n\nexport function setUpMessageHandlers(\n lc: LogContext,\n replicator: Replicator,\n parent: Worker,\n) {\n handleSubscriptionsFrom(lc, parent, replicator);\n}\n\ntype Notification = ['notify', ReplicaState];\n\nexport function handleSubscriptionsFrom(\n lc: LogContext,\n subscriber: Worker,\n notifier: ReplicaStateNotifier,\n) {\n subscriber.onMessageType('subscribe', async () => {\n const subscription = notifier.subscribe();\n\n subscriber.on('close', () => {\n lc.debug?.(`closing replication subscription from ${subscriber.pid}`);\n subscription.cancel();\n });\n\n for await (const msg of subscription) {\n try {\n subscriber.send<Notification>(['notify', msg]);\n } catch (e) {\n const log =\n e instanceof Error &&\n 'code' in e &&\n // This can happen in a race condition if the subscribing process\n // is closed before the 'close' message is processed.\n e.code === 'ERR_IPC_CHANNEL_CLOSED'\n ? 'warn'\n : 'error';\n\n lc[log]?.(\n `error sending replicator notification to ${subscriber.pid}: ${String(e)}`,\n e,\n );\n }\n }\n });\n}\n\n/**\n * Creates a Notifier to relay notifications the notifier of another Worker.\n * This does not send the initial subscription message. Use {@link subscribeTo}\n * to initiate the subscription.\n */\nexport function createNotifierFrom(\n _lc: LogContext,\n source: Worker,\n onNotify?: (state: ReplicaState) => void,\n): Notifier {\n const notifier = new Notifier();\n source.onMessageType<Notification>('notify', msg => {\n onNotify?.(msg);\n void notifier.notifySubscribers(msg);\n });\n return notifier;\n}\n\nexport function subscribeTo(_lc: LogContext, source: Worker) {\n source.send(['subscribe', {}]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"replicator.js","names":[],"sources":["../../../../../zero-cache/src/workers/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport type {ReplicaOptions} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {upgradeReplica} from '../services/change-source/common/replica-schema.ts';\nimport {deleteChangeLogDB} from '../services/replicator/change-log-db.ts';\nimport {Notifier} from '../services/replicator/notifier.ts';\nimport type {\n ReplicaState,\n ReplicaStateNotifier,\n Replicator,\n} from '../services/replicator/replicator.ts';\nimport {\n getAscendingEvents,\n recordEvent,\n} from '../services/replicator/schema/replication-state.ts';\nimport {\n applyPragmas,\n type PragmaConfig,\n} from '../services/replicator/write-worker-client.ts';\nimport type {Worker} from '../types/processes.ts';\n\nexport const replicaFileModeSchema = v.literalUnion(\n 'serving',\n 'serving-copy',\n 'backup',\n);\n\nexport type ReplicaFileMode = v.Infer<typeof replicaFileModeSchema>;\n\nexport type WalMode = 'wal' | 'wal2';\n\nexport function replicaFileName(replicaFile: string, mode: ReplicaFileMode) {\n return mode === 'serving-copy' ? `${replicaFile}-serving-copy` : replicaFile;\n}\n\n/**\n * Whether a replicator worker deletes the change-log file beside its replica at\n * startup.\n *\n * The change log belongs to the change-streamer, so this only ever cleans up a\n * file that nothing is writing. The predicate is deliberately the config flag\n * and nothing else: keying it on anything derived per replica -- a file mode, or\n * the retired `logsChangeStream` -- would make every replicator start unlink the\n * change-streamer's live log, because no replicator writes it any more. Paths\n * coincide whenever `fileMode !== 'serving-copy'`, so that hazard is real in the\n * shipped no-`backupURL` configuration.\n *\n * Deleting is safe when nothing writes the log: it is excluded from the\n * litestream backup, only the writer purges it, and re-enabling the writer\n * reseeds at the resume watermark.\n */\nexport function replicatorDeletesStaleChangeLog(\n sqliteChangeLogMode: string,\n): boolean {\n return sqliteChangeLogMode === 'off';\n}\n\n/**\n * The `mode=off` cleanup itself: deletes the change-log file beside `dbPath`\n * when {@link replicatorDeletesStaleChangeLog} says nothing writes it, and\n * reports whether it did. The decision and the delete live in one function so\n * that the guard test covers the call the replicator actually makes, not just\n * the predicate.\n */\nexport function deleteStaleChangeLog(\n sqliteChangeLogMode: string,\n dbPath: string,\n): boolean {\n if (!replicatorDeletesStaleChangeLog(sqliteChangeLogMode)) {\n return false;\n }\n deleteChangeLogDB(dbPath);\n return true;\n}\n\nconst MILLIS_PER_HOUR = 1000 * 60 * 60;\nconst MB = 1024 * 1024;\n\nasync function prepare(\n lc: LogContext,\n {file, vacuumIntervalHours}: ReplicaOptions,\n walMode: WalMode,\n mode: ReplicaFileMode,\n): Promise<{file: string; walMode: WalMode; pageSize: number}> {\n // Perform any upgrades to the replica in case the backup is an\n // earlier version.\n await upgradeReplica(lc, `${mode}-replica`, file);\n\n const replica = new Database(lc, file);\n let pageSize: number;\n try {\n // Start by folding any (e.g. restored) WAL(2) files into the main db.\n await setJournalMode(lc, replica, 'delete');\n\n [{page_size: pageSize}] = replica.pragma<{page_size: number}>('page_size');\n const [{page_count: pageCount}] = replica.pragma<{page_count: number}>(\n 'page_count',\n );\n const [{freelist_count: freelistCount}] = replica.pragma<{\n freelist_count: number;\n }>('freelist_count');\n\n const dbSize = ((pageCount * pageSize) / MB).toFixed(2);\n const freelistSize = ((freelistCount * pageSize) / MB).toFixed(2);\n\n // TODO: Consider adding a freelist size or ratio based vacuum trigger.\n lc.info?.(`Size of db ${file}: ${dbSize} MB (${freelistSize} MB freeable)`);\n\n // Check for the VACUUM threshold.\n const events = getAscendingEvents(replica);\n lc.debug?.(`Runtime events for db ${file}`, {events});\n if (vacuumIntervalHours !== undefined) {\n const millisSinceLastEvent =\n Date.now() - (events.at(-1)?.timestamp.getTime() ?? 0);\n if (millisSinceLastEvent / MILLIS_PER_HOUR > vacuumIntervalHours) {\n lc.info?.(`Performing maintenance cleanup on ${file}`);\n const t0 = performance.now();\n replica.unsafeMode(true);\n replica.pragma('journal_mode = OFF');\n replica.exec('VACUUM');\n recordEvent(replica, 'vacuum');\n replica.unsafeMode(false);\n const t1 = performance.now();\n lc.info?.(`VACUUM completed (${t1 - t0} ms)`);\n }\n }\n\n await setJournalMode(lc, replica, walMode);\n\n const pragmas = getPragmaConfig(mode);\n applyPragmas(replica, pragmas);\n\n replica.pragma('optimize = 0x10002');\n lc.info?.(`optimized ${file}`);\n } finally {\n replica.close();\n }\n return {file, walMode, pageSize};\n}\n\n// Setting the journal_mode requires an exclusive lock on the replica.\n// Add resilience against random replica reads (for stats, etc.) by\n// retrying if the database is locked. Note that the busy_timeout doesn't\n// work here.\nasync function setJournalMode(\n lc: LogContext,\n replica: Database,\n mode: 'delete' | 'wal' | 'wal2',\n) {\n lc.info?.(`setting ${replica.name} to ${mode} mode`);\n let err: unknown;\n for (let i = 0; i < 5; i++) {\n try {\n replica.pragma(`journal_mode = ${mode}`);\n return;\n } catch (e) {\n lc.warn?.(`error setting journal_mode to ${mode} (attempt ${i + 1})`, e);\n err = e;\n }\n await sleep(500);\n }\n throw err;\n}\n\n/**\n * Returns the PragmaConfig for a given replica file mode.\n * This is used by both the main thread (setupReplica) and\n * the write worker thread to apply the same pragma settings.\n */\nexport function getPragmaConfig(mode: ReplicaFileMode): PragmaConfig {\n return {\n busyTimeout: 30000,\n analysisLimit: 1000,\n walAutocheckpoint: mode === 'backup' ? 0 : undefined,\n };\n}\n\nexport function setupReplica(\n lc: LogContext,\n mode: ReplicaFileMode,\n replicaOptions: ReplicaOptions,\n) {\n lc.info?.(`setting up ${mode} replica`);\n\n switch (mode) {\n case 'backup':\n return prepare(lc, replicaOptions, 'wal', mode);\n\n case 'serving-copy': {\n // In 'serving-copy' mode, the original file is being used for 'backup'\n // mode, so we make a copy for servicing sync requests.\n const {file} = replicaOptions;\n const copyLocation = replicaFileName(file, mode);\n deleteLiteDB(copyLocation);\n\n const start = Date.now();\n lc.info?.(`copying ${file} to ${copyLocation}`);\n const replica = new Database(lc, file);\n replica.prepare(`VACUUM INTO ?`).run(copyLocation);\n replica.close();\n lc.info?.(`finished copy (${Date.now() - start} ms)`);\n\n return prepare(lc, {...replicaOptions, file: copyLocation}, 'wal2', mode);\n }\n\n case 'serving':\n return prepare(lc, replicaOptions, 'wal2', mode);\n\n default:\n throw new Error(`Invalid ReplicaMode ${mode}`);\n }\n}\n\nexport function setUpMessageHandlers(\n lc: LogContext,\n replicator: Replicator,\n parent: Worker,\n) {\n handleSubscriptionsFrom(lc, parent, replicator);\n}\n\ntype Notification = ['notify', ReplicaState];\n\nexport function handleSubscriptionsFrom(\n lc: LogContext,\n subscriber: Worker,\n notifier: ReplicaStateNotifier,\n) {\n subscriber.onMessageType('subscribe', async () => {\n const subscription = notifier.subscribe();\n\n subscriber.on('close', () => {\n lc.debug?.(`closing replication subscription from ${subscriber.pid}`);\n subscription.cancel();\n });\n\n for await (const msg of subscription) {\n try {\n subscriber.send<Notification>(['notify', msg]);\n } catch (e) {\n const log =\n e instanceof Error &&\n 'code' in e &&\n // This can happen in a race condition if the subscribing process\n // is closed before the 'close' message is processed.\n e.code === 'ERR_IPC_CHANNEL_CLOSED'\n ? 'warn'\n : 'error';\n\n lc[log]?.(\n `error sending replicator notification to ${subscriber.pid}: ${String(e)}`,\n e,\n );\n }\n }\n });\n}\n\n/**\n * Creates a Notifier to relay notifications the notifier of another Worker.\n * This does not send the initial subscription message. Use {@link subscribeTo}\n * to initiate the subscription.\n */\nexport function createNotifierFrom(\n _lc: LogContext,\n source: Worker,\n onNotify?: (state: ReplicaState) => void,\n): Notifier {\n const notifier = new Notifier();\n source.onMessageType<Notification>('notify', msg => {\n onNotify?.(msg);\n void notifier.notifySubscribers(msg);\n });\n return notifier;\n}\n\nexport function subscribeTo(_lc: LogContext, source: Worker) {\n source.send(['subscribe', {}]);\n}\n"],"mappings":";;;;;;;;;;AAwBA,IAAa,wBAAwB,aACnC,WACA,gBACA,QACF;AAMA,SAAgB,gBAAgB,aAAqB,MAAuB;CAC1E,OAAO,SAAS,iBAAiB,GAAG,YAAY,iBAAiB;AACnE;;;;;;;;;;;;;;;;;AAkBA,SAAgB,gCACd,qBACS;CACT,OAAO,wBAAwB;AACjC;;;;;;;;AASA,SAAgB,qBACd,qBACA,QACS;CACT,IAAI,CAAC,gCAAgC,mBAAmB,GACtD,OAAO;CAET,kBAAkB,MAAM;CACxB,OAAO;AACT;AAEA,IAAM,kBAAkB,MAAO,KAAK;AACpC,IAAM,KAAK,OAAO;AAElB,eAAe,QACb,IACA,EAAC,MAAM,uBACP,SACA,MAC6D;CAG7D,MAAM,eAAe,IAAI,GAAG,KAAK,WAAW,IAAI;CAEhD,MAAM,UAAU,IAAI,SAAS,IAAI,IAAI;CACrC,IAAI;CACJ,IAAI;EAEF,MAAM,eAAe,IAAI,SAAS,QAAQ;EAE1C,CAAC,CAAC,WAAW,aAAa,QAAQ,OAA4B,WAAW;EACzE,MAAM,CAAC,EAAC,YAAY,eAAc,QAAQ,OACxC,YACF;EACA,MAAM,CAAC,EAAC,gBAAgB,mBAAkB,QAAQ,OAE/C,gBAAgB;EAEnB,MAAM,UAAW,YAAY,WAAY,IAAI,QAAQ,CAAC;EACtD,MAAM,gBAAiB,gBAAgB,WAAY,IAAI,QAAQ,CAAC;EAGhE,GAAG,OAAO,cAAc,KAAK,IAAI,OAAO,OAAO,aAAa,cAAc;EAG1E,MAAM,SAAS,mBAAmB,OAAO;EACzC,GAAG,QAAQ,yBAAyB,QAAQ,EAAC,OAAM,CAAC;EACpD,IAAI,wBAAwB,KAAA;QAExB,KAAK,IAAI,KAAK,OAAO,GAAG,EAAE,GAAG,UAAU,QAAQ,KAAK,MAC3B,kBAAkB,qBAAqB;IAChE,GAAG,OAAO,qCAAqC,MAAM;IACrD,MAAM,KAAK,YAAY,IAAI;IAC3B,QAAQ,WAAW,IAAI;IACvB,QAAQ,OAAO,oBAAoB;IACnC,QAAQ,KAAK,QAAQ;IACrB,YAAY,SAAS,QAAQ;IAC7B,QAAQ,WAAW,KAAK;IACxB,MAAM,KAAK,YAAY,IAAI;IAC3B,GAAG,OAAO,qBAAqB,KAAK,GAAG,KAAK;GAC9C;;EAGF,MAAM,eAAe,IAAI,SAAS,OAAO;EAGzC,aAAa,SADG,gBAAgB,IACV,CAAO;EAE7B,QAAQ,OAAO,oBAAoB;EACnC,GAAG,OAAO,aAAa,MAAM;CAC/B,UAAU;EACR,QAAQ,MAAM;CAChB;CACA,OAAO;EAAC;EAAM;EAAS;CAAQ;AACjC;AAMA,eAAe,eACb,IACA,SACA,MACA;CACA,GAAG,OAAO,WAAW,QAAQ,KAAK,MAAM,KAAK,MAAM;CACnD,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,IAAI;GACF,QAAQ,OAAO,kBAAkB,MAAM;GACvC;EACF,SAAS,GAAG;GACV,GAAG,OAAO,iCAAiC,KAAK,YAAY,IAAI,EAAE,IAAI,CAAC;GACvE,MAAM;EACR;EACA,MAAM,MAAM,GAAG;CACjB;CACA,MAAM;AACR;;;;;;AAOA,SAAgB,gBAAgB,MAAqC;CACnE,OAAO;EACL,aAAa;EACb,eAAe;EACf,mBAAmB,SAAS,WAAW,IAAI,KAAA;CAC7C;AACF;AAEA,SAAgB,aACd,IACA,MACA,gBACA;CACA,GAAG,OAAO,cAAc,KAAK,SAAS;CAEtC,QAAQ,MAAR;EACE,KAAK,UACH,OAAO,QAAQ,IAAI,gBAAgB,OAAO,IAAI;EAEhD,KAAK,gBAAgB;GAGnB,MAAM,EAAC,SAAQ;GACf,MAAM,eAAe,gBAAgB,MAAM,IAAI;GAC/C,aAAa,YAAY;GAEzB,MAAM,QAAQ,KAAK,IAAI;GACvB,GAAG,OAAO,WAAW,KAAK,MAAM,cAAc;GAC9C,MAAM,UAAU,IAAI,SAAS,IAAI,IAAI;GACrC,QAAQ,QAAQ,eAAe,EAAE,IAAI,YAAY;GACjD,QAAQ,MAAM;GACd,GAAG,OAAO,kBAAkB,KAAK,IAAI,IAAI,MAAM,KAAK;GAEpD,OAAO,QAAQ,IAAI;IAAC,GAAG;IAAgB,MAAM;GAAY,GAAG,QAAQ,IAAI;EAC1E;EAEA,KAAK,WACH,OAAO,QAAQ,IAAI,gBAAgB,QAAQ,IAAI;EAEjD,SACE,MAAM,IAAI,MAAM,uBAAuB,MAAM;CACjD;AACF;AAEA,SAAgB,qBACd,IACA,YACA,QACA;CACA,wBAAwB,IAAI,QAAQ,UAAU;AAChD;AAIA,SAAgB,wBACd,IACA,YACA,UACA;CACA,WAAW,cAAc,aAAa,YAAY;EAChD,MAAM,eAAe,SAAS,UAAU;EAExC,WAAW,GAAG,eAAe;GAC3B,GAAG,QAAQ,yCAAyC,WAAW,KAAK;GACpE,aAAa,OAAO;EACtB,CAAC;EAED,WAAW,MAAM,OAAO,cACtB,IAAI;GACF,WAAW,KAAmB,CAAC,UAAU,GAAG,CAAC;EAC/C,SAAS,GAAG;GAUV,GARE,aAAa,SACb,UAAU,KAGV,EAAE,SAAS,2BACP,SACA,WAGJ,4CAA4C,WAAW,IAAI,IAAI,OAAO,CAAC,KACvE,CACF;EACF;CAEJ,CAAC;AACH;;;;;;AAOA,SAAgB,mBACd,KACA,QACA,UACU;CACV,MAAM,WAAW,IAAI,SAAS;CAC9B,OAAO,cAA4B,WAAU,QAAO;EAClD,WAAW,GAAG;EACd,SAAc,kBAAkB,GAAG;CACrC,CAAC;CACD,OAAO;AACT;AAEA,SAAgB,YAAY,KAAiB,QAAgB;CAC3D,OAAO,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;AAC/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,qCAAqC,CAAC;AAEjE,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,sBAAsB,GAC9B;IACE,IAAI,EAAE,gBAAgB,CAAC,YAAY,CAAC;IACpC,MAAM,EAAE,kBAAkB,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,UAAU,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CAChC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;CAClC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;IACjC,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC;IAC7B,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEN,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,QAAA,MAAM,eAAe,yBAGkB,CAAC;AAExC,KAAK,wBAAwB,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AACjE,KAAK,8BAA8B,GAAG,OAAO,CAC3C,sBAAsB,EACtB;IAAC,IAAI,EAAE,wBAAwB,CAAA;CAAC,CACjC,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;
|
|
1
|
+
{"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,qCAAqC,CAAC;AAEjE,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,sBAAsB,GAC9B;IACE,IAAI,EAAE,gBAAgB,CAAC,YAAY,CAAC;IACpC,MAAM,EAAE,kBAAkB,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,UAAU,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CAChC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;CAClC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;IACjC,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC;IAC7B,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEN,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,QAAA,MAAM,eAAe,yBAGkB,CAAC;AAExC,KAAK,wBAAwB,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AACjE,KAAK,8BAA8B,GAAG,OAAO,CAC3C,sBAAsB,EACtB;IAAC,IAAI,EAAE,wBAAwB,CAAA;CAAC,CACjC,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;gBAuC7D,OAAO,EAAE,wBAAwB;IAkB7C,IAAI,KAAK,IAAI,sBAAsB,CAElC;IAED;;OAEG;IACH,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO;IAIrC;;;OAGG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;;OAGG;IACH,MAAM,CAAC,eAAe,CACpB,KAAK,EAAE,sBAAsB,GAC5B,KAAK,IAAI,8BAA8B;IAM1C;;;;OAIG;IACH,qBAAqB,IAAI,OAAO;IAIhC;;;OAGG;IACH,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAIrD,cAAc,IAAI,IAAI;IAQtB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IActC;;;;OAIG;IACH,wBAAwB,IAAI,OAAO;IAWnC;;;;;;;;;;OAUG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG;QAC9B,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IA0DD;;;;;OAKG;IACH,SAAS,IAAI;QAAC,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;KAAC;IAsBhE;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG;QACxC,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IA6BD;;;;;;OAMG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG;QAC5B,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IAuBD;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG;QACxB,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IAuBD;;;OAGG;IACH,MAAM;IAqBG,OAAO,QAAO,IAAI,CAGzB;CAqHH;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,sBAAsB,SAmBnE,CAAC"}
|
|
@@ -30,6 +30,11 @@ var ConnectionManager = class ConnectionManager extends Subscribable {
|
|
|
30
30
|
*/
|
|
31
31
|
#timeoutCheckIntervalMs;
|
|
32
32
|
/**
|
|
33
|
+
* `Date.now()` at the last timeout-interval tick, used to notice that the
|
|
34
|
+
* JavaScript event loop was not running. See {@linkcode #creditFrozenTime}.
|
|
35
|
+
*/
|
|
36
|
+
#lastTimeoutCheckAt;
|
|
37
|
+
/**
|
|
33
38
|
* Resolver used to signal waiting callers when the state changes.
|
|
34
39
|
*/
|
|
35
40
|
#stateChangeResolver = resolver();
|
|
@@ -44,6 +49,7 @@ var ConnectionManager = class ConnectionManager extends Subscribable {
|
|
|
44
49
|
disconnectAt: now + this.#disconnectTimeout
|
|
45
50
|
};
|
|
46
51
|
this.#connectingStartedAt = now;
|
|
52
|
+
this.#lastTimeoutCheckAt = now;
|
|
47
53
|
this.#maybeStartTimeoutInterval();
|
|
48
54
|
}
|
|
49
55
|
get state() {
|
|
@@ -253,6 +259,44 @@ var ConnectionManager = class ConnectionManager extends Subscribable {
|
|
|
253
259
|
return this.#nextStatePromise();
|
|
254
260
|
}
|
|
255
261
|
/**
|
|
262
|
+
* Pushes the disconnect deadline forward by however long the event loop was
|
|
263
|
+
* not running.
|
|
264
|
+
*
|
|
265
|
+
* `disconnectAt` is an absolute wall-clock instant, but what it is meant to
|
|
266
|
+
* bound is time spent *actually retrying*. Whenever the loop is frozen --- a
|
|
267
|
+
* suspended React Native app, a sleeping laptop, a throttled background tab
|
|
268
|
+
* --- wall-clock keeps advancing while no reconnect attempt can run, so
|
|
269
|
+
* without this the first tick after resume reports `Offline` having given the
|
|
270
|
+
* client zero live seconds to connect.
|
|
271
|
+
*
|
|
272
|
+
* This interval is its own detector: a tick that lands late by more than a
|
|
273
|
+
* full period means we were not running in between. A merely busy JS thread
|
|
274
|
+
* can also delay a tick, and we cannot tell the two apart from lateness
|
|
275
|
+
* alone, but the costs are asymmetric --- crediting a busy period back just
|
|
276
|
+
* grants a bit more time to connect, whereas failing to credit a real freeze
|
|
277
|
+
* produces a user-visible bogus disconnect --- so we credit.
|
|
278
|
+
*
|
|
279
|
+
* The whole gap is credited, not `elapsed` minus a period. An overdue
|
|
280
|
+
* `setInterval` callback runs as soon as the loop resumes rather than waiting
|
|
281
|
+
* out another period, so `elapsed` is already about the frozen duration;
|
|
282
|
+
* netting off a period would charge up to a full period of frozen time
|
|
283
|
+
* against the deadline. Freezing just after a tick would then leave the
|
|
284
|
+
* deadline exactly at `now` on resume and disconnect immediately, which is
|
|
285
|
+
* the very thing this is here to prevent.
|
|
286
|
+
*/
|
|
287
|
+
#creditFrozenTime() {
|
|
288
|
+
const now = Date.now();
|
|
289
|
+
const elapsed = now - this.#lastTimeoutCheckAt;
|
|
290
|
+
this.#lastTimeoutCheckAt = now;
|
|
291
|
+
if (elapsed <= 2 * this.#timeoutCheckIntervalMs) return;
|
|
292
|
+
const frozenMs = elapsed;
|
|
293
|
+
if (this.#connectingStartedAt !== void 0) this.#connectingStartedAt += frozenMs;
|
|
294
|
+
if (this.#state.name === "connecting") this.#state = {
|
|
295
|
+
...this.#state,
|
|
296
|
+
disconnectAt: this.#state.disconnectAt + frozenMs
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
256
300
|
* Check if we should transition from connecting to disconnected due to timeout.
|
|
257
301
|
* Returns true if the transition happened.
|
|
258
302
|
*/
|
|
@@ -269,7 +313,9 @@ var ConnectionManager = class ConnectionManager extends Subscribable {
|
|
|
269
313
|
}
|
|
270
314
|
#maybeStartTimeoutInterval() {
|
|
271
315
|
if (this.#timeoutInterval !== void 0) return;
|
|
316
|
+
this.#lastTimeoutCheckAt = Date.now();
|
|
272
317
|
this.#timeoutInterval = setInterval(() => {
|
|
318
|
+
this.#creditFrozenTime();
|
|
273
319
|
this.#checkTimeout();
|
|
274
320
|
}, this.#timeoutCheckIntervalMs);
|
|
275
321
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-manager.js","names":["#disconnectTimeout","#timeoutCheckIntervalMs","#state","#connectingStartedAt","#maybeStartTimeoutInterval","#nextStatePromise","#connectRequestResolver","#consumeConnectRequest","#publishStateAndGetPromise","#maybeStopTimeoutInterval","#publishState","#resolveNextStateWaiters","#stateChangeResolver","#timeoutInterval","#checkTimeout"],"sources":["../../../../../zero-client/src/client/connection-manager.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {Subscribable} from '../../../shared/src/subscribable.ts';\nimport {ClientErrorKind} from './client-error-kind.ts';\nimport {ConnectionStatus} from './connection-status.ts';\nimport {\n ClientError,\n isClientError,\n type AuthError,\n type ClosedError,\n type DisconnectedReason,\n type ZeroError,\n} from './error.ts';\n\nconst DEFAULT_TIMEOUT_CHECK_INTERVAL_MS = 1_000;\n\nexport type ConnectionManagerState =\n | {\n name: ConnectionStatus.Disconnected;\n reason: DisconnectedReason;\n }\n | {\n name: ConnectionStatus.Connecting;\n attempt: number;\n disconnectAt: number;\n reason?: ZeroError | undefined;\n }\n | {\n name: ConnectionStatus.Connected;\n }\n | {\n name: ConnectionStatus.NeedsAuth;\n reason: AuthError;\n }\n | {\n name: ConnectionStatus.Error;\n reason: ZeroError;\n }\n | {\n name: ConnectionStatus.Closed;\n reason: ClosedError;\n };\n\nexport type ConnectionManagerOptions = {\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n disconnectTimeout: number;\n /**\n * How frequently we check whether the connecting timeout has elapsed.\n * Defaults to 1 second.\n */\n timeoutCheckIntervalMs?: number | undefined;\n};\n\nconst TERMINAL_STATES = [\n ConnectionStatus.NeedsAuth,\n ConnectionStatus.Error,\n] as const satisfies ConnectionStatus[];\n\ntype TerminalConnectionStatus = (typeof TERMINAL_STATES)[number];\ntype TerminalConnectionManagerState = Extract<\n ConnectionManagerState,\n {name: TerminalConnectionStatus}\n>;\n\nexport class ConnectionManager extends Subscribable<ConnectionManagerState> {\n #state: ConnectionManagerState;\n #connectRequestResolver: Resolver<void> | undefined = resolver();\n\n /**\n * The timestamp when we first started trying to connect.\n * This is used to track the retry window.\n * Reset to undefined when we successfully connect or when we transition to disconnected.\n */\n #connectingStartedAt: number | undefined;\n\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n #disconnectTimeout: number;\n\n /**\n * Handle for the timeout interval that periodically checks whether we've\n * exceeded the allowed connecting window.\n */\n #timeoutInterval: ReturnType<typeof setInterval> | undefined;\n\n /**\n * Interval duration for checking whether the connecting timeout has elapsed.\n */\n #timeoutCheckIntervalMs: number;\n\n /**\n * Resolver used to signal waiting callers when the state changes.\n */\n #stateChangeResolver: Resolver<ConnectionManagerState> = resolver();\n\n constructor(options: ConnectionManagerOptions) {\n super();\n\n const now = Date.now();\n\n this.#disconnectTimeout = options.disconnectTimeout;\n this.#timeoutCheckIntervalMs =\n options.timeoutCheckIntervalMs ?? DEFAULT_TIMEOUT_CHECK_INTERVAL_MS;\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 0,\n disconnectAt: now + this.#disconnectTimeout,\n };\n this.#connectingStartedAt = now;\n this.#maybeStartTimeoutInterval();\n }\n\n get state(): ConnectionManagerState {\n return this.#state;\n }\n\n /**\n * Returns true if the current state is equal to the given status.\n */\n is(status: ConnectionStatus): boolean {\n return this.#state.name === status;\n }\n\n /**\n * Returns true if the current state is a terminal state\n * that can be recovered from by calling connect().\n */\n isInTerminalState(): boolean {\n return ConnectionManager.isTerminalState(this.#state);\n }\n\n /**\n * Returns true if the given status is a terminal state\n * that can be recovered from by calling connect().\n */\n static isTerminalState(\n state: ConnectionManagerState,\n ): state is TerminalConnectionManagerState {\n return (TERMINAL_STATES as readonly ConnectionStatus[]).includes(\n state.name,\n );\n }\n\n /**\n * Returns true if the run loop should continue.\n * The run loop continues in all states except closed.\n * In needs-auth and error states, the run loop pauses and waits for connect() to be called.\n */\n shouldContinueRunLoop(): boolean {\n return this.#state.name !== ConnectionStatus.Closed;\n }\n\n /**\n * Waits for the next state change.\n * @returns A promise that resolves when the next state change occurs.\n */\n waitForStateChange(): Promise<ConnectionManagerState> {\n return this.#nextStatePromise();\n }\n\n requestConnect(): void {\n if (this.#connectRequestResolver === undefined) {\n return;\n }\n this.#connectRequestResolver.resolve();\n this.#connectRequestResolver = undefined;\n }\n\n waitForConnectRequest(): Promise<void> {\n return this.#connectRequestResolver === undefined\n ? Promise.resolve()\n : this.#connectRequestResolver.promise;\n }\n\n #consumeConnectRequest(): boolean {\n if (this.#connectRequestResolver !== undefined) {\n return false;\n }\n this.#connectRequestResolver = resolver();\n return true;\n }\n\n /**\n * Consume a pending connect request and resume connecting.\n *\n * @returns true if a pending request was handled.\n */\n resumeFromConnectRequest(): boolean {\n if (!this.isInTerminalState()) {\n return false;\n }\n if (!this.#consumeConnectRequest()) {\n return false;\n }\n this.connecting();\n return true;\n }\n\n /**\n * Transition to connecting state.\n *\n * This starts the timeout timer, but if we've entered disconnected state,\n * we stay there and continue retrying.\n *\n * If we're disconnected due to a hidden tab, allow a transition back to\n * connecting when visibility returns.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connecting(reason?: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n const isHiddenDisconnect =\n this.#state.name === ConnectionStatus.Disconnected &&\n this.#state.reason.kind === ClientErrorKind.Hidden;\n\n // we cannot intentionally transition from disconnected to connecting\n // unless the disconnection was due to a hidden tab\n if (\n this.#state.name === ConnectionStatus.Disconnected &&\n !isHiddenDisconnect\n ) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n const now = Date.now();\n\n if (isHiddenDisconnect) {\n // Reset the retry window after a hidden-tab disconnect.\n this.#connectingStartedAt = undefined;\n }\n\n // If we're already connecting, increment the attempt counter\n if (this.#state.name === ConnectionStatus.Connecting) {\n this.#state = {\n ...this.#state,\n attempt: this.#state.attempt + 1,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n // Starting a new connecting session\n // If #connectingStartedAt is undefined, this is a fresh start - set it to now\n // If it's already set, we're retrying within the same retry window, so keep it\n if (this.#connectingStartedAt === undefined) {\n this.#connectingStartedAt = now;\n }\n\n const disconnectAt = this.#connectingStartedAt + this.#disconnectTimeout;\n\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 1,\n disconnectAt,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n /**\n * Transition to connected state.\n * This resets the connecting timeout timer.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connected(): {nextStatePromise: Promise<ConnectionManagerState>} {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already connected, no-op\n if (this.#state.name === ConnectionStatus.Connected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer on successful connection\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Connected,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to disconnected state.\n * This is called when the timeout expires.\n * The run loop will continue trying to reconnect.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n disconnected(reason: DisconnectedReason): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already disconnected, no-op\n if (this.#state.name === ConnectionStatus.Disconnected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // When transitioning from connected to disconnected, we've lost a connection\n // we previously had. Clear the timeout timer so we can start a fresh timeout window.\n if (this.#state.name === ConnectionStatus.Connected) {\n this.#connectingStartedAt = undefined;\n }\n // When transitioning from connecting to disconnected (e.g., due to timeout),\n // we keep the start time to maintain the context that we've been trying for a while.\n\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Disconnected,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to needs-auth state.\n * This pauses the run loop until connect() is called with new credentials.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n needsAuth(reason: AuthError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in needs-auth state, no-op\n if (this.#state.name === ConnectionStatus.NeedsAuth) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.NeedsAuth,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to error state.\n * This pauses the run loop until connect() is called.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n error(reason: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in error state, no-op\n if (this.#state.name === ConnectionStatus.Error) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Error,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to closed state.\n * This is terminal - no further transitions are allowed.\n */\n closed() {\n // Already closed, no-op\n if (this.#state.name === ConnectionStatus.Closed) {\n return;\n }\n\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Closed,\n reason: new ClientError({\n kind: ClientErrorKind.ClientClosed,\n message: 'Zero was explicitly closed by calling zero.close()',\n }),\n };\n this.#publishState();\n this.cleanup();\n return;\n }\n\n override cleanup = (): void => {\n this._listeners.clear();\n this.#resolveNextStateWaiters();\n };\n\n #resolveNextStateWaiters(): void {\n this.#stateChangeResolver.resolve(this.#state);\n this.#stateChangeResolver = resolver();\n }\n\n #publishState(): void {\n this.notify(this.#state);\n this.#resolveNextStateWaiters();\n }\n\n #nextStatePromise(): Promise<ConnectionManagerState> {\n return this.#stateChangeResolver.promise;\n }\n\n #publishStateAndGetPromise(): Promise<ConnectionManagerState> {\n this.#publishState();\n return this.#nextStatePromise();\n }\n\n /**\n * Check if we should transition from connecting to disconnected due to timeout.\n * Returns true if the transition happened.\n */\n #checkTimeout(): boolean {\n if (this.#state.name !== ConnectionStatus.Connecting) {\n return false;\n }\n\n const now = Date.now();\n if (now >= this.#state.disconnectAt) {\n this.disconnected(\n new ClientError({\n kind: ClientErrorKind.Offline,\n message: `Zero was unable to connect for ${Math.floor(this.#disconnectTimeout / 1_000)} seconds and was disconnected`,\n }),\n );\n return true;\n }\n\n return false;\n }\n\n #maybeStartTimeoutInterval(): void {\n if (this.#timeoutInterval !== undefined) {\n return;\n }\n this.#timeoutInterval = setInterval(() => {\n this.#checkTimeout();\n }, this.#timeoutCheckIntervalMs);\n }\n\n #maybeStopTimeoutInterval(): void {\n if (this.#timeoutInterval === undefined) {\n return;\n }\n clearInterval(this.#timeoutInterval);\n this.#timeoutInterval = undefined;\n }\n}\n\n/**\n * Used to trigger the catch block when a terminal state is reached.\n *\n * @param state - The current connection state.\n */\nexport const throwIfConnectionError = (state: ConnectionManagerState) => {\n if (\n ConnectionManager.isTerminalState(state) ||\n state.name === ConnectionStatus.Closed ||\n ((state.name === ConnectionStatus.Connecting ||\n state.name === ConnectionStatus.Disconnected) &&\n state.reason)\n ) {\n if (\n isClientError(state.reason) &&\n (state.reason.kind === ClientErrorKind.ConnectTimeout ||\n state.reason.kind === ClientErrorKind.AbruptClose ||\n state.reason.kind === ClientErrorKind.CleanClose ||\n state.reason.kind === ClientErrorKind.Hidden)\n ) {\n return;\n }\n throw state.reason;\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connection-manager.js","names":["#disconnectTimeout","#timeoutCheckIntervalMs","#state","#connectingStartedAt","#lastTimeoutCheckAt","#maybeStartTimeoutInterval","#nextStatePromise","#connectRequestResolver","#consumeConnectRequest","#publishStateAndGetPromise","#maybeStopTimeoutInterval","#publishState","#resolveNextStateWaiters","#stateChangeResolver","#timeoutInterval","#creditFrozenTime","#checkTimeout"],"sources":["../../../../../zero-client/src/client/connection-manager.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {Subscribable} from '../../../shared/src/subscribable.ts';\nimport {ClientErrorKind} from './client-error-kind.ts';\nimport {ConnectionStatus} from './connection-status.ts';\nimport {\n ClientError,\n isClientError,\n type AuthError,\n type ClosedError,\n type DisconnectedReason,\n type ZeroError,\n} from './error.ts';\n\nconst DEFAULT_TIMEOUT_CHECK_INTERVAL_MS = 1_000;\n\nexport type ConnectionManagerState =\n | {\n name: ConnectionStatus.Disconnected;\n reason: DisconnectedReason;\n }\n | {\n name: ConnectionStatus.Connecting;\n attempt: number;\n disconnectAt: number;\n reason?: ZeroError | undefined;\n }\n | {\n name: ConnectionStatus.Connected;\n }\n | {\n name: ConnectionStatus.NeedsAuth;\n reason: AuthError;\n }\n | {\n name: ConnectionStatus.Error;\n reason: ZeroError;\n }\n | {\n name: ConnectionStatus.Closed;\n reason: ClosedError;\n };\n\nexport type ConnectionManagerOptions = {\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n disconnectTimeout: number;\n /**\n * How frequently we check whether the connecting timeout has elapsed.\n * Defaults to 1 second.\n */\n timeoutCheckIntervalMs?: number | undefined;\n};\n\nconst TERMINAL_STATES = [\n ConnectionStatus.NeedsAuth,\n ConnectionStatus.Error,\n] as const satisfies ConnectionStatus[];\n\ntype TerminalConnectionStatus = (typeof TERMINAL_STATES)[number];\ntype TerminalConnectionManagerState = Extract<\n ConnectionManagerState,\n {name: TerminalConnectionStatus}\n>;\n\nexport class ConnectionManager extends Subscribable<ConnectionManagerState> {\n #state: ConnectionManagerState;\n #connectRequestResolver: Resolver<void> | undefined = resolver();\n\n /**\n * The timestamp when we first started trying to connect.\n * This is used to track the retry window.\n * Reset to undefined when we successfully connect or when we transition to disconnected.\n */\n #connectingStartedAt: number | undefined;\n\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n #disconnectTimeout: number;\n\n /**\n * Handle for the timeout interval that periodically checks whether we've\n * exceeded the allowed connecting window.\n */\n #timeoutInterval: ReturnType<typeof setInterval> | undefined;\n\n /**\n * Interval duration for checking whether the connecting timeout has elapsed.\n */\n #timeoutCheckIntervalMs: number;\n\n /**\n * `Date.now()` at the last timeout-interval tick, used to notice that the\n * JavaScript event loop was not running. See {@linkcode #creditFrozenTime}.\n */\n #lastTimeoutCheckAt: number;\n\n /**\n * Resolver used to signal waiting callers when the state changes.\n */\n #stateChangeResolver: Resolver<ConnectionManagerState> = resolver();\n\n constructor(options: ConnectionManagerOptions) {\n super();\n\n const now = Date.now();\n\n this.#disconnectTimeout = options.disconnectTimeout;\n this.#timeoutCheckIntervalMs =\n options.timeoutCheckIntervalMs ?? DEFAULT_TIMEOUT_CHECK_INTERVAL_MS;\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 0,\n disconnectAt: now + this.#disconnectTimeout,\n };\n this.#connectingStartedAt = now;\n this.#lastTimeoutCheckAt = now;\n this.#maybeStartTimeoutInterval();\n }\n\n get state(): ConnectionManagerState {\n return this.#state;\n }\n\n /**\n * Returns true if the current state is equal to the given status.\n */\n is(status: ConnectionStatus): boolean {\n return this.#state.name === status;\n }\n\n /**\n * Returns true if the current state is a terminal state\n * that can be recovered from by calling connect().\n */\n isInTerminalState(): boolean {\n return ConnectionManager.isTerminalState(this.#state);\n }\n\n /**\n * Returns true if the given status is a terminal state\n * that can be recovered from by calling connect().\n */\n static isTerminalState(\n state: ConnectionManagerState,\n ): state is TerminalConnectionManagerState {\n return (TERMINAL_STATES as readonly ConnectionStatus[]).includes(\n state.name,\n );\n }\n\n /**\n * Returns true if the run loop should continue.\n * The run loop continues in all states except closed.\n * In needs-auth and error states, the run loop pauses and waits for connect() to be called.\n */\n shouldContinueRunLoop(): boolean {\n return this.#state.name !== ConnectionStatus.Closed;\n }\n\n /**\n * Waits for the next state change.\n * @returns A promise that resolves when the next state change occurs.\n */\n waitForStateChange(): Promise<ConnectionManagerState> {\n return this.#nextStatePromise();\n }\n\n requestConnect(): void {\n if (this.#connectRequestResolver === undefined) {\n return;\n }\n this.#connectRequestResolver.resolve();\n this.#connectRequestResolver = undefined;\n }\n\n waitForConnectRequest(): Promise<void> {\n return this.#connectRequestResolver === undefined\n ? Promise.resolve()\n : this.#connectRequestResolver.promise;\n }\n\n #consumeConnectRequest(): boolean {\n if (this.#connectRequestResolver !== undefined) {\n return false;\n }\n this.#connectRequestResolver = resolver();\n return true;\n }\n\n /**\n * Consume a pending connect request and resume connecting.\n *\n * @returns true if a pending request was handled.\n */\n resumeFromConnectRequest(): boolean {\n if (!this.isInTerminalState()) {\n return false;\n }\n if (!this.#consumeConnectRequest()) {\n return false;\n }\n this.connecting();\n return true;\n }\n\n /**\n * Transition to connecting state.\n *\n * This starts the timeout timer, but if we've entered disconnected state,\n * we stay there and continue retrying.\n *\n * If we're disconnected due to a hidden tab, allow a transition back to\n * connecting when visibility returns.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connecting(reason?: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n const isHiddenDisconnect =\n this.#state.name === ConnectionStatus.Disconnected &&\n this.#state.reason.kind === ClientErrorKind.Hidden;\n\n // we cannot intentionally transition from disconnected to connecting\n // unless the disconnection was due to a hidden tab\n if (\n this.#state.name === ConnectionStatus.Disconnected &&\n !isHiddenDisconnect\n ) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n const now = Date.now();\n\n if (isHiddenDisconnect) {\n // Reset the retry window after a hidden-tab disconnect.\n this.#connectingStartedAt = undefined;\n }\n\n // If we're already connecting, increment the attempt counter\n if (this.#state.name === ConnectionStatus.Connecting) {\n this.#state = {\n ...this.#state,\n attempt: this.#state.attempt + 1,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n // Starting a new connecting session\n // If #connectingStartedAt is undefined, this is a fresh start - set it to now\n // If it's already set, we're retrying within the same retry window, so keep it\n if (this.#connectingStartedAt === undefined) {\n this.#connectingStartedAt = now;\n }\n\n const disconnectAt = this.#connectingStartedAt + this.#disconnectTimeout;\n\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 1,\n disconnectAt,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n /**\n * Transition to connected state.\n * This resets the connecting timeout timer.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connected(): {nextStatePromise: Promise<ConnectionManagerState>} {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already connected, no-op\n if (this.#state.name === ConnectionStatus.Connected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer on successful connection\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Connected,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to disconnected state.\n * This is called when the timeout expires.\n * The run loop will continue trying to reconnect.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n disconnected(reason: DisconnectedReason): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already disconnected, no-op\n if (this.#state.name === ConnectionStatus.Disconnected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // When transitioning from connected to disconnected, we've lost a connection\n // we previously had. Clear the timeout timer so we can start a fresh timeout window.\n if (this.#state.name === ConnectionStatus.Connected) {\n this.#connectingStartedAt = undefined;\n }\n // When transitioning from connecting to disconnected (e.g., due to timeout),\n // we keep the start time to maintain the context that we've been trying for a while.\n\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Disconnected,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to needs-auth state.\n * This pauses the run loop until connect() is called with new credentials.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n needsAuth(reason: AuthError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in needs-auth state, no-op\n if (this.#state.name === ConnectionStatus.NeedsAuth) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.NeedsAuth,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to error state.\n * This pauses the run loop until connect() is called.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n error(reason: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in error state, no-op\n if (this.#state.name === ConnectionStatus.Error) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Error,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to closed state.\n * This is terminal - no further transitions are allowed.\n */\n closed() {\n // Already closed, no-op\n if (this.#state.name === ConnectionStatus.Closed) {\n return;\n }\n\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Closed,\n reason: new ClientError({\n kind: ClientErrorKind.ClientClosed,\n message: 'Zero was explicitly closed by calling zero.close()',\n }),\n };\n this.#publishState();\n this.cleanup();\n return;\n }\n\n override cleanup = (): void => {\n this._listeners.clear();\n this.#resolveNextStateWaiters();\n };\n\n #resolveNextStateWaiters(): void {\n this.#stateChangeResolver.resolve(this.#state);\n this.#stateChangeResolver = resolver();\n }\n\n #publishState(): void {\n this.notify(this.#state);\n this.#resolveNextStateWaiters();\n }\n\n #nextStatePromise(): Promise<ConnectionManagerState> {\n return this.#stateChangeResolver.promise;\n }\n\n #publishStateAndGetPromise(): Promise<ConnectionManagerState> {\n this.#publishState();\n return this.#nextStatePromise();\n }\n\n /**\n * Pushes the disconnect deadline forward by however long the event loop was\n * not running.\n *\n * `disconnectAt` is an absolute wall-clock instant, but what it is meant to\n * bound is time spent *actually retrying*. Whenever the loop is frozen --- a\n * suspended React Native app, a sleeping laptop, a throttled background tab\n * --- wall-clock keeps advancing while no reconnect attempt can run, so\n * without this the first tick after resume reports `Offline` having given the\n * client zero live seconds to connect.\n *\n * This interval is its own detector: a tick that lands late by more than a\n * full period means we were not running in between. A merely busy JS thread\n * can also delay a tick, and we cannot tell the two apart from lateness\n * alone, but the costs are asymmetric --- crediting a busy period back just\n * grants a bit more time to connect, whereas failing to credit a real freeze\n * produces a user-visible bogus disconnect --- so we credit.\n *\n * The whole gap is credited, not `elapsed` minus a period. An overdue\n * `setInterval` callback runs as soon as the loop resumes rather than waiting\n * out another period, so `elapsed` is already about the frozen duration;\n * netting off a period would charge up to a full period of frozen time\n * against the deadline. Freezing just after a tick would then leave the\n * deadline exactly at `now` on resume and disconnect immediately, which is\n * the very thing this is here to prevent.\n */\n #creditFrozenTime(): void {\n const now = Date.now();\n const elapsed = now - this.#lastTimeoutCheckAt;\n this.#lastTimeoutCheckAt = now;\n\n // Ordinary scheduling jitter, not a freeze.\n if (elapsed <= 2 * this.#timeoutCheckIntervalMs) {\n return;\n }\n const frozenMs = elapsed;\n\n // Advance the window's origin so that a later `connecting()` that starts a\n // fresh session recomputes a deadline that also excludes the frozen time.\n if (this.#connectingStartedAt !== undefined) {\n this.#connectingStartedAt += frozenMs;\n }\n\n if (this.#state.name === ConnectionStatus.Connecting) {\n // Deliberately not published: the status has not changed, and waking the\n // run loop's `waitForStateChange` racers here would interrupt an\n // in-flight connect attempt for no reason. Readers of `state` still see\n // the corrected deadline.\n this.#state = {\n ...this.#state,\n disconnectAt: this.#state.disconnectAt + frozenMs,\n };\n }\n }\n\n /**\n * Check if we should transition from connecting to disconnected due to timeout.\n * Returns true if the transition happened.\n */\n #checkTimeout(): boolean {\n if (this.#state.name !== ConnectionStatus.Connecting) {\n return false;\n }\n\n const now = Date.now();\n if (now >= this.#state.disconnectAt) {\n this.disconnected(\n new ClientError({\n kind: ClientErrorKind.Offline,\n message: `Zero was unable to connect for ${Math.floor(this.#disconnectTimeout / 1_000)} seconds and was disconnected`,\n }),\n );\n return true;\n }\n\n return false;\n }\n\n #maybeStartTimeoutInterval(): void {\n if (this.#timeoutInterval !== undefined) {\n return;\n }\n this.#lastTimeoutCheckAt = Date.now();\n this.#timeoutInterval = setInterval(() => {\n this.#creditFrozenTime();\n this.#checkTimeout();\n }, this.#timeoutCheckIntervalMs);\n }\n\n #maybeStopTimeoutInterval(): void {\n if (this.#timeoutInterval === undefined) {\n return;\n }\n clearInterval(this.#timeoutInterval);\n this.#timeoutInterval = undefined;\n }\n}\n\n/**\n * Used to trigger the catch block when a terminal state is reached.\n *\n * @param state - The current connection state.\n */\nexport const throwIfConnectionError = (state: ConnectionManagerState) => {\n if (\n ConnectionManager.isTerminalState(state) ||\n state.name === ConnectionStatus.Closed ||\n ((state.name === ConnectionStatus.Connecting ||\n state.name === ConnectionStatus.Disconnected) &&\n state.reason)\n ) {\n if (\n isClientError(state.reason) &&\n (state.reason.kind === ClientErrorKind.ConnectTimeout ||\n state.reason.kind === ClientErrorKind.AbruptClose ||\n state.reason.kind === ClientErrorKind.CleanClose ||\n state.reason.kind === ClientErrorKind.Hidden)\n ) {\n return;\n }\n throw state.reason;\n }\n};\n"],"mappings":"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"}
|
|
@@ -39,6 +39,21 @@ export declare class ZeroContext extends QueryDelegateBase {
|
|
|
39
39
|
applyFiltersAnyway?: boolean | undefined;
|
|
40
40
|
debug?: DebugDelegate | undefined;
|
|
41
41
|
getSource(name: string): Source | undefined;
|
|
42
|
+
get pipelinesReady(): boolean;
|
|
43
|
+
onPipelinesReady(cb: () => void): () => void;
|
|
44
|
+
/**
|
|
45
|
+
* Stop building pipelines for materialized queries until
|
|
46
|
+
* {@link markPipelinesReady} is called. Views materialized in the meantime
|
|
47
|
+
* are empty and `unknown`, exactly as they would be over empty sources.
|
|
48
|
+
*/
|
|
49
|
+
deferPipelines(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Build and hydrate every pipeline deferred since {@link deferPipelines},
|
|
52
|
+
* in materialization order, as a single view-update batch. A pipeline that
|
|
53
|
+
* fails to build is logged and its view reports an error; the rest are
|
|
54
|
+
* still built.
|
|
55
|
+
*/
|
|
56
|
+
markPipelinesReady(): void;
|
|
42
57
|
mapAst(ast: AST): AST;
|
|
43
58
|
decorateSourceInput(input: SourceInput, queryID: string): Input;
|
|
44
59
|
onTransactionCommit(cb: CommitListener): () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,uCAAuC,CAAC;AACvE,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,iCAAiC,CAAC;AAE1D,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAE3D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,4CAA4C,CAAC;AAC9E,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAC,MAAM,EAAE,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAExE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,4CAA4C,CAAC;AAChF,OAAO,EAAC,iBAAiB,EAAC,MAAM,+CAA+C,CAAC;AAChF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAC,KAAK,eAAe,EAAC,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;AACjD,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;AACvD,MAAM,MAAM,iBAAiB,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;AAC7D,MAAM,MAAM,iBAAiB,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAE3D;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,iBAAiB;;IAShD,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,iBAAiB,EAAE,WAAW,CAAC;IACxC,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,8 @@ var ZeroContext = class extends QueryDelegateBase {
|
|
|
19
19
|
flushQueryChanges;
|
|
20
20
|
#batchViewUpdates;
|
|
21
21
|
#commitListeners = /* @__PURE__ */ new Set();
|
|
22
|
+
#pipelinesReady = true;
|
|
23
|
+
#pendingPipelines = /* @__PURE__ */ new Set();
|
|
22
24
|
assertValidRunOptions;
|
|
23
25
|
/**
|
|
24
26
|
* Client-side queries start out as "unknown" and are then updated to
|
|
@@ -44,6 +46,48 @@ var ZeroContext = class extends QueryDelegateBase {
|
|
|
44
46
|
getSource(name) {
|
|
45
47
|
return this.#mainSources.getSource(name);
|
|
46
48
|
}
|
|
49
|
+
get pipelinesReady() {
|
|
50
|
+
return this.#pipelinesReady;
|
|
51
|
+
}
|
|
52
|
+
onPipelinesReady(cb) {
|
|
53
|
+
assert(!this.#pipelinesReady, "onPipelinesReady called while pipelines are ready");
|
|
54
|
+
this.#pendingPipelines.add(cb);
|
|
55
|
+
return () => {
|
|
56
|
+
this.#pendingPipelines.delete(cb);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Stop building pipelines for materialized queries until
|
|
61
|
+
* {@link markPipelinesReady} is called. Views materialized in the meantime
|
|
62
|
+
* are empty and `unknown`, exactly as they would be over empty sources.
|
|
63
|
+
*/
|
|
64
|
+
deferPipelines() {
|
|
65
|
+
this.#pipelinesReady = false;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build and hydrate every pipeline deferred since {@link deferPipelines},
|
|
69
|
+
* in materialization order, as a single view-update batch. A pipeline that
|
|
70
|
+
* fails to build is logged and its view reports an error; the rest are
|
|
71
|
+
* still built.
|
|
72
|
+
*/
|
|
73
|
+
markPipelinesReady() {
|
|
74
|
+
if (this.#pipelinesReady) return;
|
|
75
|
+
this.#pipelinesReady = true;
|
|
76
|
+
if (this.#pendingPipelines.size === 0) return;
|
|
77
|
+
const pending = [...this.#pendingPipelines];
|
|
78
|
+
this.#pendingPipelines.clear();
|
|
79
|
+
this.batchViewUpdates(() => {
|
|
80
|
+
try {
|
|
81
|
+
for (const attach of pending) try {
|
|
82
|
+
attach();
|
|
83
|
+
} catch (e) {
|
|
84
|
+
this.#lc.error?.(Internal, "Failed to build a deferred query pipeline", e);
|
|
85
|
+
}
|
|
86
|
+
} finally {
|
|
87
|
+
this.#endTransaction();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
47
91
|
mapAst(ast) {
|
|
48
92
|
return ast;
|
|
49
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":["#lc","#mainSources","#batchViewUpdates","#commitListeners","#endTransaction"],"sources":["../../../../../zero-client/src/client/context.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NoIndexDiff} from '../../../replicache/src/btree/node.ts';\nimport type {Hash} from '../../../replicache/src/hash.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport type {DebugDelegate} from '../../../zql/src/builder/debug-delegate.ts';\nimport type {Input} from '../../../zql/src/ivm/operator.ts';\nimport type {Source, SourceInput} from '../../../zql/src/ivm/source.ts';\nimport {MeasurePushOperator} from '../../../zql/src/query/measure-push-operator.ts';\nimport type {MetricsDelegate} from '../../../zql/src/query/metrics-delegate.ts';\nimport {QueryDelegateBase} from '../../../zql/src/query/query-delegate-base.ts';\nimport type {CommitListener} from '../../../zql/src/query/query-delegate.ts';\nimport type {RunOptions} from '../../../zql/src/query/query.ts';\nimport {type IVMSourceBranch} from './ivm-branch.ts';\nimport type {QueryManager} from './query-manager.ts';\n\nexport type AddQuery = QueryManager['addLegacy'];\nexport type AddCustomQuery = QueryManager['addCustom'];\n\nexport type UpdateQuery = QueryManager['updateLegacy'];\nexport type UpdateCustomQuery = QueryManager['updateCustom'];\nexport type FlushQueryChanges = QueryManager['flushBatch'];\n\n/**\n * ZeroContext glues together zql and Replicache. It listens to changes in\n * Replicache data and pushes them into IVM and on tells the server about new\n * queries.\n */\nexport class ZeroContext extends QueryDelegateBase {\n readonly #lc: LogContext;\n\n // It is a bummer to have to maintain separate MemorySources here and copy the\n // data in from the Replicache db. But we want the data to be accessible via\n // pipelines *synchronously* and the core Replicache infra is all async. So\n // that needs to be fixed.\n readonly #mainSources: IVMSourceBranch;\n\n readonly addServerQuery: AddQuery;\n readonly addCustomQuery: AddCustomQuery;\n readonly updateServerQuery: UpdateQuery;\n readonly updateCustomQuery: UpdateCustomQuery;\n readonly flushQueryChanges: () => void;\n readonly #batchViewUpdates: (applyViewUpdates: () => void) => void;\n readonly #commitListeners: Set<CommitListener> = new Set();\n\n readonly assertValidRunOptions: (options?: RunOptions) => void;\n\n /**\n * Client-side queries start out as \"unknown\" and are then updated to\n * \"complete\" once the server has sent back the query result.\n */\n readonly defaultQueryComplete = false;\n\n readonly addMetric: MetricsDelegate['addMetric'];\n\n constructor(\n lc: LogContext,\n mainSources: IVMSourceBranch,\n addQuery: AddQuery,\n addCustomQuery: AddCustomQuery,\n updateQuery: UpdateQuery,\n updateCustomQuery: UpdateCustomQuery,\n flushQueryChanges: () => void,\n batchViewUpdates: (applyViewUpdates: () => void) => void,\n addMetric: MetricsDelegate['addMetric'],\n assertValidRunOptions: (options?: RunOptions) => void,\n ) {\n super();\n this.#lc = lc;\n this.#mainSources = mainSources;\n this.addServerQuery = addQuery;\n this.updateServerQuery = updateQuery;\n this.updateCustomQuery = updateCustomQuery;\n this.#batchViewUpdates = batchViewUpdates;\n this.assertValidRunOptions = assertValidRunOptions;\n this.addCustomQuery = addCustomQuery;\n this.flushQueryChanges = flushQueryChanges;\n this.addMetric = addMetric;\n }\n\n applyFiltersAnyway?: boolean | undefined;\n\n debug?: DebugDelegate | undefined;\n\n getSource(name: string): Source | undefined {\n return this.#mainSources.getSource(name);\n }\n\n mapAst(ast: AST): AST {\n return ast;\n }\n\n override decorateSourceInput(input: SourceInput, queryID: string): Input {\n return new MeasurePushOperator(input, queryID, this, 'query-update-client');\n }\n\n onTransactionCommit(cb: CommitListener): () => void {\n this.#commitListeners.add(cb);\n return () => {\n this.#commitListeners.delete(cb);\n };\n }\n\n override batchViewUpdates<T>(applyViewUpdates: () => T) {\n let result: T | undefined;\n let viewChangesPerformed = false;\n this.#batchViewUpdates(() => {\n result = applyViewUpdates();\n viewChangesPerformed = true;\n });\n assert(\n viewChangesPerformed,\n 'batchViewUpdates must call applyViewUpdates synchronously.',\n );\n return result as T;\n }\n\n processChanges(\n expectedHead: Hash | undefined,\n newHead: Hash,\n changes: NoIndexDiff,\n ) {\n this.batchViewUpdates(() => {\n try {\n this.#mainSources.advance(expectedHead, newHead, changes);\n } finally {\n this.#endTransaction();\n }\n });\n }\n\n #endTransaction() {\n for (const listener of this.#commitListeners) {\n try {\n listener();\n } catch (e) {\n // We should not fatal the inner-workings of Zero due to the user's application\n // code throwing an error.\n // Hence we wrap notifications in a try-catch block.\n this.#lc.error?.(\n ErrorKind.Internal,\n 'Failed notifying a commit listener of IVM updates',\n e,\n );\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AA6BA,IAAa,cAAb,cAAiC,kBAAkB;CACjD;CAMA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA,mCAAiD,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"context.js","names":["#lc","#mainSources","#batchViewUpdates","#commitListeners","#pendingPipelines","#pipelinesReady","#endTransaction"],"sources":["../../../../../zero-client/src/client/context.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NoIndexDiff} from '../../../replicache/src/btree/node.ts';\nimport type {Hash} from '../../../replicache/src/hash.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport type {DebugDelegate} from '../../../zql/src/builder/debug-delegate.ts';\nimport type {Input} from '../../../zql/src/ivm/operator.ts';\nimport type {Source, SourceInput} from '../../../zql/src/ivm/source.ts';\nimport {MeasurePushOperator} from '../../../zql/src/query/measure-push-operator.ts';\nimport type {MetricsDelegate} from '../../../zql/src/query/metrics-delegate.ts';\nimport {QueryDelegateBase} from '../../../zql/src/query/query-delegate-base.ts';\nimport type {CommitListener} from '../../../zql/src/query/query-delegate.ts';\nimport type {RunOptions} from '../../../zql/src/query/query.ts';\nimport {type IVMSourceBranch} from './ivm-branch.ts';\nimport type {QueryManager} from './query-manager.ts';\n\nexport type AddQuery = QueryManager['addLegacy'];\nexport type AddCustomQuery = QueryManager['addCustom'];\n\nexport type UpdateQuery = QueryManager['updateLegacy'];\nexport type UpdateCustomQuery = QueryManager['updateCustom'];\nexport type FlushQueryChanges = QueryManager['flushBatch'];\n\n/**\n * ZeroContext glues together zql and Replicache. It listens to changes in\n * Replicache data and pushes them into IVM and on tells the server about new\n * queries.\n */\nexport class ZeroContext extends QueryDelegateBase {\n readonly #lc: LogContext;\n\n // It is a bummer to have to maintain separate MemorySources here and copy the\n // data in from the Replicache db. But we want the data to be accessible via\n // pipelines *synchronously* and the core Replicache infra is all async. So\n // that needs to be fixed.\n readonly #mainSources: IVMSourceBranch;\n\n readonly addServerQuery: AddQuery;\n readonly addCustomQuery: AddCustomQuery;\n readonly updateServerQuery: UpdateQuery;\n readonly updateCustomQuery: UpdateCustomQuery;\n readonly flushQueryChanges: () => void;\n readonly #batchViewUpdates: (applyViewUpdates: () => void) => void;\n readonly #commitListeners: Set<CommitListener> = new Set();\n\n // Pipeline construction is deferred between `deferPipelines()` and\n // `markPipelinesReady()`. Zero calls the former at construction and the\n // latter once the replica has been loaded into the IVM sources, so cold\n // boot loads the sources once instead of pushing every row through every\n // already-materialized pipeline.\n #pipelinesReady = true;\n readonly #pendingPipelines: Set<() => void> = new Set();\n\n readonly assertValidRunOptions: (options?: RunOptions) => void;\n\n /**\n * Client-side queries start out as \"unknown\" and are then updated to\n * \"complete\" once the server has sent back the query result.\n */\n readonly defaultQueryComplete = false;\n\n readonly addMetric: MetricsDelegate['addMetric'];\n\n constructor(\n lc: LogContext,\n mainSources: IVMSourceBranch,\n addQuery: AddQuery,\n addCustomQuery: AddCustomQuery,\n updateQuery: UpdateQuery,\n updateCustomQuery: UpdateCustomQuery,\n flushQueryChanges: () => void,\n batchViewUpdates: (applyViewUpdates: () => void) => void,\n addMetric: MetricsDelegate['addMetric'],\n assertValidRunOptions: (options?: RunOptions) => void,\n ) {\n super();\n this.#lc = lc;\n this.#mainSources = mainSources;\n this.addServerQuery = addQuery;\n this.updateServerQuery = updateQuery;\n this.updateCustomQuery = updateCustomQuery;\n this.#batchViewUpdates = batchViewUpdates;\n this.assertValidRunOptions = assertValidRunOptions;\n this.addCustomQuery = addCustomQuery;\n this.flushQueryChanges = flushQueryChanges;\n this.addMetric = addMetric;\n }\n\n applyFiltersAnyway?: boolean | undefined;\n\n debug?: DebugDelegate | undefined;\n\n getSource(name: string): Source | undefined {\n return this.#mainSources.getSource(name);\n }\n\n override get pipelinesReady(): boolean {\n return this.#pipelinesReady;\n }\n\n override onPipelinesReady(cb: () => void): () => void {\n assert(\n !this.#pipelinesReady,\n 'onPipelinesReady called while pipelines are ready',\n );\n this.#pendingPipelines.add(cb);\n return () => {\n this.#pendingPipelines.delete(cb);\n };\n }\n\n /**\n * Stop building pipelines for materialized queries until\n * {@link markPipelinesReady} is called. Views materialized in the meantime\n * are empty and `unknown`, exactly as they would be over empty sources.\n */\n deferPipelines(): void {\n this.#pipelinesReady = false;\n }\n\n /**\n * Build and hydrate every pipeline deferred since {@link deferPipelines},\n * in materialization order, as a single view-update batch. A pipeline that\n * fails to build is logged and its view reports an error; the rest are\n * still built.\n */\n markPipelinesReady(): void {\n if (this.#pipelinesReady) {\n return;\n }\n this.#pipelinesReady = true;\n if (this.#pendingPipelines.size === 0) {\n return;\n }\n const pending = [...this.#pendingPipelines];\n this.#pendingPipelines.clear();\n this.batchViewUpdates(() => {\n try {\n for (const attach of pending) {\n try {\n attach();\n } catch (e) {\n // The failing view has already been marked as errored; keep\n // building the others rather than aborting startup.\n this.#lc.error?.(\n ErrorKind.Internal,\n 'Failed to build a deferred query pipeline',\n e,\n );\n }\n }\n } finally {\n this.#endTransaction();\n }\n });\n }\n\n mapAst(ast: AST): AST {\n return ast;\n }\n\n override decorateSourceInput(input: SourceInput, queryID: string): Input {\n return new MeasurePushOperator(input, queryID, this, 'query-update-client');\n }\n\n onTransactionCommit(cb: CommitListener): () => void {\n this.#commitListeners.add(cb);\n return () => {\n this.#commitListeners.delete(cb);\n };\n }\n\n override batchViewUpdates<T>(applyViewUpdates: () => T) {\n let result: T | undefined;\n let viewChangesPerformed = false;\n this.#batchViewUpdates(() => {\n result = applyViewUpdates();\n viewChangesPerformed = true;\n });\n assert(\n viewChangesPerformed,\n 'batchViewUpdates must call applyViewUpdates synchronously.',\n );\n return result as T;\n }\n\n processChanges(\n expectedHead: Hash | undefined,\n newHead: Hash,\n changes: NoIndexDiff,\n ) {\n this.batchViewUpdates(() => {\n try {\n this.#mainSources.advance(expectedHead, newHead, changes);\n } finally {\n this.#endTransaction();\n }\n });\n }\n\n #endTransaction() {\n for (const listener of this.#commitListeners) {\n try {\n listener();\n } catch (e) {\n // We should not fatal the inner-workings of Zero due to the user's application\n // code throwing an error.\n // Hence we wrap notifications in a try-catch block.\n this.#lc.error?.(\n ErrorKind.Internal,\n 'Failed notifying a commit listener of IVM updates',\n e,\n );\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AA6BA,IAAa,cAAb,cAAiC,kBAAkB;CACjD;CAMA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA,mCAAiD,IAAI,IAAI;CAOzD,kBAAkB;CAClB,oCAA8C,IAAI,IAAI;CAEtD;;;;;CAMA,uBAAgC;CAEhC;CAEA,YACE,IACA,aACA,UACA,gBACA,aACA,mBACA,mBACA,kBACA,WACA,uBACA;EACA,MAAM;EACN,KAAKA,MAAM;EACX,KAAKC,eAAe;EACpB,KAAK,iBAAiB;EACtB,KAAK,oBAAoB;EACzB,KAAK,oBAAoB;EACzB,KAAKC,oBAAoB;EACzB,KAAK,wBAAwB;EAC7B,KAAK,iBAAiB;EACtB,KAAK,oBAAoB;EACzB,KAAK,YAAY;CACnB;CAEA;CAEA;CAEA,UAAU,MAAkC;EAC1C,OAAO,KAAKD,aAAa,UAAU,IAAI;CACzC;CAEA,IAAa,iBAA0B;EACrC,OAAO,KAAKI;CACd;CAEA,iBAA0B,IAA4B;EACpD,OACE,CAAC,KAAKA,iBACN,mDACF;EACA,KAAKD,kBAAkB,IAAI,EAAE;EAC7B,aAAa;GACX,KAAKA,kBAAkB,OAAO,EAAE;EAClC;CACF;;;;;;CAOA,iBAAuB;EACrB,KAAKC,kBAAkB;CACzB;;;;;;;CAQA,qBAA2B;EACzB,IAAI,KAAKA,iBACP;EAEF,KAAKA,kBAAkB;EACvB,IAAI,KAAKD,kBAAkB,SAAS,GAClC;EAEF,MAAM,UAAU,CAAC,GAAG,KAAKA,iBAAiB;EAC1C,KAAKA,kBAAkB,MAAM;EAC7B,KAAK,uBAAuB;GAC1B,IAAI;IACF,KAAK,MAAM,UAAU,SACnB,IAAI;KACF,OAAO;IACT,SAAS,GAAG;KAGV,KAAKJ,IAAI,QACP,UACA,6CACA,CACF;IACF;GAEJ,UAAU;IACR,KAAKM,gBAAgB;GACvB;EACF,CAAC;CACH;CAEA,OAAO,KAAe;EACpB,OAAO;CACT;CAEA,oBAA6B,OAAoB,SAAwB;EACvE,OAAO,IAAI,oBAAoB,OAAO,SAAS,MAAM,qBAAqB;CAC5E;CAEA,oBAAoB,IAAgC;EAClD,KAAKH,iBAAiB,IAAI,EAAE;EAC5B,aAAa;GACX,KAAKA,iBAAiB,OAAO,EAAE;EACjC;CACF;CAEA,iBAA6B,kBAA2B;EACtD,IAAI;EACJ,IAAI,uBAAuB;EAC3B,KAAKD,wBAAwB;GAC3B,SAAS,iBAAiB;GAC1B,uBAAuB;EACzB,CAAC;EACD,OACE,sBACA,4DACF;EACA,OAAO;CACT;CAEA,eACE,cACA,SACA,SACA;EACA,KAAK,uBAAuB;GAC1B,IAAI;IACF,KAAKD,aAAa,QAAQ,cAAc,SAAS,OAAO;GAC1D,UAAU;IACR,KAAKK,gBAAgB;GACvB;EACF,CAAC;CACH;CAEA,kBAAkB;EAChB,KAAK,MAAM,YAAY,KAAKH,kBAC1B,IAAI;GACF,SAAS;EACX,SAAS,GAAG;GAIV,KAAKH,IAAI,QACP,UACA,qDACA,CACF;EACF;CAEJ;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-inspector.d.ts","sourceRoot":"","sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,+CAA+C,CAAC;AAGlF,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAU,KAAK,eAAe,EAAC,MAAM,mCAAmC,CAAC;AAChF,OAAO,KAAK,MAAM,MAAM,kCAAkC,CAAC;AAC3D,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uDAAuD,CAAC;AAC9F,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,sCAAsC,CAAC;AAC9D,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,uCAAuC,CAAC;AAC/D,OAAO,
|
|
1
|
+
{"version":3,"file":"lazy-inspector.d.ts","sourceRoot":"","sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { withRead } from "../../../../replicache/src/with-transactions.js";
|
|
|
5
5
|
import { getClientGroup } from "../../../../replicache/src/persist/client-groups.js";
|
|
6
6
|
import { getClient, getClients } from "../../../../replicache/src/persist/clients.js";
|
|
7
7
|
import { asQueryInternals } from "../../../../zql/src/query/query-internals.js";
|
|
8
|
-
import { inspectAnalyzeQueryDownSchema, inspectAuthenticatedDownSchema, inspectMetricsDownSchema, inspectQueriesDownSchema, inspectVersionDownSchema } from "../../../../zero-protocol/src/inspect-down.js";
|
|
8
|
+
import { inspectAnalyzeQueryDownSchema, inspectAuthenticatedDownSchema, inspectErrorDownSchema, inspectMetricsDownSchema, inspectQueriesDownSchema, inspectVersionDownSchema } from "../../../../zero-protocol/src/inspect-down.js";
|
|
9
9
|
import { nanoid } from "../../util/nanoid.js";
|
|
10
10
|
import "../keys.js";
|
|
11
11
|
import { Client } from "./client.js";
|
|
@@ -35,23 +35,30 @@ function rpcNoAuthTry(socket, arg, downSchema) {
|
|
|
35
35
|
return new Promise((resolve, reject) => {
|
|
36
36
|
const id = nanoid();
|
|
37
37
|
const f = (ev) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (typeof ev.data !== "string") return;
|
|
39
|
+
let msg;
|
|
40
|
+
try {
|
|
41
|
+
msg = JSON.parse(ev.data);
|
|
42
|
+
} catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!Array.isArray(msg) || msg[0] !== "inspect") return;
|
|
46
|
+
const body = msg[1];
|
|
47
|
+
if (body.id !== id) return;
|
|
48
|
+
const errorRes = test(body, inspectErrorDownSchema);
|
|
49
|
+
if (errorRes.ok) reject(new Error(errorRes.value.value));
|
|
50
|
+
else {
|
|
42
51
|
const res = test(body, downSchema);
|
|
43
|
-
if (res.ok)
|
|
44
|
-
else resolve(res.value.value);
|
|
52
|
+
if (res.ok) resolve(res.value.value);
|
|
45
53
|
else {
|
|
46
54
|
const authRes = test(body, inspectAuthenticatedDownSchema);
|
|
47
55
|
if (authRes.ok) {
|
|
48
56
|
assert(authRes.value.value === false, "Expected unauthenticated response");
|
|
49
57
|
reject(new UnauthenticatedError());
|
|
50
|
-
}
|
|
51
|
-
reject(res.error);
|
|
58
|
+
} else reject(res.error);
|
|
52
59
|
}
|
|
53
|
-
socket.removeEventListener("message", f);
|
|
54
60
|
}
|
|
61
|
+
socket.removeEventListener("message", f);
|
|
55
62
|
};
|
|
56
63
|
socket.addEventListener("message", f);
|
|
57
64
|
socket.send(JSON.stringify(["inspect", {
|
|
@@ -154,7 +161,7 @@ function clientMap(delegate, clientID) {
|
|
|
154
161
|
return withDagRead(delegate, async (dagRead) => {
|
|
155
162
|
const tree = await getBTree(dagRead, clientID);
|
|
156
163
|
const map = /* @__PURE__ */ new Map();
|
|
157
|
-
for await (const [key, value] of tree.scan("")) map.set(key, value);
|
|
164
|
+
for await (const [key, value] of tree.scan("", true)) map.set(key, value);
|
|
158
165
|
return map;
|
|
159
166
|
});
|
|
160
167
|
}
|
|
@@ -163,7 +170,7 @@ function clientRows(delegate, clientID, tableName) {
|
|
|
163
170
|
const prefix = "e/" + tableName + "/";
|
|
164
171
|
const tree = await getBTree(dagRead, clientID);
|
|
165
172
|
const rows = [];
|
|
166
|
-
for await (const [key, value] of tree.scan(prefix)) {
|
|
173
|
+
for await (const [key, value] of tree.scan(prefix, false)) {
|
|
167
174
|
if (!key.startsWith(prefix)) break;
|
|
168
175
|
rows.push(value);
|
|
169
176
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-inspector.js","names":[],"sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"sourcesContent":["import type {BTreeRead} from '../../../../replicache/src/btree/read.ts';\nimport type {Read} from '../../../../replicache/src/dag/store.ts';\nimport {readFromHash} from '../../../../replicache/src/db/read.ts';\nimport * as FormatVersion from '../../../../replicache/src/format-version-enum.ts';\nimport {getClientGroup} from '../../../../replicache/src/persist/client-groups.ts';\nimport {\n getClient,\n getClients,\n type ClientMap,\n} from '../../../../replicache/src/persist/clients.ts';\nimport type {ReplicacheImpl} from '../../../../replicache/src/replicache-impl.ts';\nimport {withRead} from '../../../../replicache/src/with-transactions.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../../shared/src/json.ts';\nimport {TDigest, type ReadonlyTDigest} from '../../../../shared/src/tdigest.ts';\nimport * as valita from '../../../../shared/src/valita.ts';\nimport type {AnalyzeQueryResult} from '../../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport {\n inspectAnalyzeQueryDownSchema,\n inspectAuthenticatedDownSchema,\n inspectMetricsDownSchema,\n inspectQueriesDownSchema,\n inspectVersionDownSchema,\n type InspectDownBody,\n type InspectQueryRow,\n type QueryServerMetrics as QueryServerMetricsJSON,\n} from '../../../../zero-protocol/src/inspect-down.ts';\nimport type {\n AnalyzeQueryOptions,\n InspectUpBody,\n} from '../../../../zero-protocol/src/inspect-up.ts';\nimport type {\n ClientMetricMap,\n ServerMetricMap,\n} from '../../../../zql/src/query/metrics-delegate.ts';\nimport type {QueryDelegate} from '../../../../zql/src/query/query-delegate.ts';\nimport {asQueryInternals} from '../../../../zql/src/query/query-internals.ts';\nimport type {AnyQuery} from '../../../../zql/src/query/query.ts';\nimport {nanoid} from '../../util/nanoid.ts';\nimport {ENTITIES_KEY_PREFIX} from '../keys.ts';\nimport type {MutatorDefs} from '../replicache-types.ts';\nimport {Client} from './client.ts';\nimport {createHTMLPasswordPrompt} from './html-dialog-prompt.ts';\nimport {type Lazy} from './inspector.ts';\nimport {Query} from './query.ts';\n\nexport type GetWebSocket = () => Promise<WebSocket>;\n\nexport type Metrics = {\n readonly [K in keyof (ClientMetricMap & ServerMetricMap)]: ReadonlyTDigest;\n};\n\ntype DistributiveOmit<T, K extends string> = T extends object\n ? Omit<T, K>\n : never;\n\nexport async function rpc<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n try {\n return await rpcNoAuthTry(socket, arg, downSchema);\n } catch (e) {\n if (e instanceof UnauthenticatedError) {\n const password = await createHTMLPasswordPrompt('Enter password:');\n if (password) {\n // Do authenticate rpc\n const authRes = await rpcNoAuthTry(\n socket,\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n if (authRes) {\n // If authentication is successful, retry the original RPC\n return rpcNoAuthTry(socket, arg, downSchema);\n }\n }\n throw new Error('Authentication failed');\n }\n throw e;\n }\n}\n\nfunction rpcNoAuthTry<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n return new Promise((resolve, reject) => {\n const id = nanoid();\n const f = (ev: MessageEvent) => {\n const msg = JSON.parse(ev.data);\n if (msg[0] === 'inspect') {\n const body = msg[1];\n if (body.id !== id) {\n return;\n }\n const res = valita.test(body, downSchema);\n if (res.ok) {\n if (res.value.op === 'error') {\n reject(new Error(res.value.value));\n } else {\n resolve(res.value.value);\n }\n } else {\n // Check if we got un authenticated/false response\n const authRes = valita.test(body, inspectAuthenticatedDownSchema);\n if (authRes.ok) {\n // Handle authenticated response\n assert(\n authRes.value.value === false,\n 'Expected unauthenticated response',\n );\n reject(new UnauthenticatedError());\n }\n\n reject(res.error);\n }\n socket.removeEventListener('message', f);\n }\n };\n socket.addEventListener('message', f);\n socket.send(JSON.stringify(['inspect', {...arg, id}]));\n });\n} // T extends forces T to be resolved\n\nexport function mergeMetrics(\n clientMetrics: QueryClientMetrics | undefined,\n serverMetrics: QueryServerMetricsJSON | null | undefined,\n): ClientMetrics & ServerMetrics {\n const cm = clientMetrics ?? newClientMetrics();\n return {\n ...convertClientMetrics(cm),\n ...(serverMetrics\n ? convertServerMetrics(serverMetrics)\n : newServerMetrics()),\n };\n}\n\nfunction newClientMetrics(): QueryClientMetrics {\n return {\n 'query-materialization-client': undefined,\n 'query-materialization-end-to-end': undefined,\n 'query-update-client': new TDigest(),\n };\n}\n\nfunction convertClientMetrics(metrics: QueryClientMetrics): ClientMetrics {\n const hydrateDigest = new TDigest();\n if (metrics['query-materialization-client'] !== undefined) {\n hydrateDigest.add(metrics['query-materialization-client']);\n }\n const totalDigest = new TDigest();\n if (metrics['query-materialization-end-to-end'] !== undefined) {\n totalDigest.add(metrics['query-materialization-end-to-end']);\n }\n return {\n 'query-materialization-client': hydrateDigest,\n 'query-materialization-end-to-end': totalDigest,\n 'query-update-client': metrics['query-update-client'] as TDigest,\n };\n}\n\nfunction newServerMetrics(): ServerMetrics {\n return {\n 'query-materialization-server': new TDigest(),\n 'query-update-server': new TDigest(),\n };\n}\n\nfunction convertServerMetrics(metrics: QueryServerMetricsJSON): ServerMetrics {\n const hydrateMs = metrics['query-hydration-server-ms'];\n const hydrateDigest = new TDigest();\n if (hydrateMs !== undefined) {\n hydrateDigest.add(hydrateMs);\n }\n return {\n 'query-materialization-server': hydrateDigest,\n 'query-update-server': TDigest.fromJSON(metrics['query-update-server']),\n };\n}\n\nexport async function inspectorMetrics(\n delegate: ExtendedInspectorDelegate,\n): Promise<Metrics> {\n const clientMetrics = delegate.metrics;\n const serverMetricsJSON = await rpc(\n await delegate.getSocket(),\n {op: 'metrics'},\n inspectMetricsDownSchema,\n );\n return {\n ...(clientMetrics ?? newClientMetrics()),\n 'query-materialization-server': TDigest.fromJSON(\n serverMetricsJSON['query-materialization-server'],\n ),\n 'query-update-server': TDigest.fromJSON(\n serverMetricsJSON['query-update-server'],\n ),\n };\n}\n\nexport function inspectorClients(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead => clients(delegate, dagRead));\n}\n\nexport function inspectorClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead),\n );\n}\n\nasync function withDagRead<T>(\n delegate: ExtendedInspectorDelegate,\n f: (dagRead: Read) => Promise<T>,\n): Promise<T> {\n const {rep} = delegate;\n await rep.refresh();\n await rep.persist();\n return withRead(rep.perdag, f);\n}\n\nasync function getBTree(dagRead: Read, clientID: string): Promise<BTreeRead> {\n const client = await getClient(clientID, dagRead);\n assert(client, `Client not found: ${clientID}`);\n const {clientGroupID} = client;\n const clientGroup = await getClientGroup(clientGroupID, dagRead);\n assert(clientGroup, `Client group not found: ${clientGroupID}`);\n const dbRead = await readFromHash(\n clientGroup.headHash,\n dagRead,\n FormatVersion.Latest,\n );\n return dbRead.map;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype MapEntry<T extends ReadonlyMap<any, any>> =\n T extends ReadonlyMap<infer K, infer V> ? readonly [K, V] : never;\n\nasync function clients(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const clients = await getClients(dagRead);\n return [...clients.entries()]\n .filter(predicate)\n .map(\n ([clientID, {clientGroupID}]) =>\n new Client(delegate, clientID, clientGroupID),\n );\n}\n\nasync function clientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const allClients = await clients(delegate, dagRead, predicate);\n const clientsWithQueries: Client[] = [];\n await Promise.all(\n allClients.map(async client => {\n const queries = await client.queries();\n if (queries.length > 0) {\n clientsWithQueries.push(client);\n }\n }),\n );\n return clientsWithQueries;\n}\n\nexport async function clientGroupClients(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clients(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport async function clientGroupClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport function clientGroupQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries'});\n}\nexport function clientMap(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Map<string, ReadonlyJSONValue>> {\n return withDagRead(delegate, async dagRead => {\n const tree = await getBTree(dagRead, clientID);\n const map = new Map<string, ReadonlyJSONValue>();\n for await (const [key, value] of tree.scan('')) {\n map.set(key, value);\n }\n return map;\n });\n}\n\nexport function clientRows(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n tableName: string,\n): Promise<Row[]> {\n return withDagRead(delegate, async dagRead => {\n const prefix = ENTITIES_KEY_PREFIX + tableName + '/';\n const tree = await getBTree(dagRead, clientID);\n const rows: Row[] = [];\n for await (const [key, value] of tree.scan(prefix)) {\n if (!key.startsWith(prefix)) {\n break;\n }\n rows.push(value as Row);\n }\n return rows;\n });\n}\n\nexport async function serverVersion(\n delegate: ExtendedInspectorDelegate,\n): Promise<string> {\n return rpc(\n await delegate.getSocket(),\n {op: 'version'},\n inspectVersionDownSchema,\n );\n}\n\nexport function clientQueries(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries', clientID});\n}\n\nasync function queries(\n delegate: ExtendedInspectorDelegate,\n arg: {op: 'queries'; clientID?: string},\n): Promise<Query[]> {\n const rows: InspectQueryRow[] = await rpc(\n await delegate.getSocket(),\n arg,\n inspectQueriesDownSchema,\n );\n const queries = rows.map(row => new Query(row, delegate, delegate.getSocket));\n queries.sort((a, b) => (b.hydrateServer ?? 0) - (a.hydrateServer ?? 0));\n return queries;\n}\n\nexport async function analyzeQuery(\n delegate: ExtendedInspectorDelegate,\n query: AnyQuery,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n const qi = asQueryInternals(query);\n const {customQueryID} = qi;\n const queryParameters = customQueryID\n ? {name: customQueryID.name, args: customQueryID.args}\n : {ast: delegate.mapClientASTToServer(qi.ast)};\n\n return rpc(\n await delegate.getSocket(),\n {\n op: 'analyze-query',\n ...queryParameters,\n options,\n },\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeServerAST(\n delegate: ExtendedInspectorDelegate,\n ast: AST,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', ast, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeNamedQuery(\n delegate: ExtendedInspectorDelegate,\n name: string,\n args: ReadonlyArray<ReadonlyJSONValue>,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', name, args, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\n/**\n * Sends an `authenticate` op to the server. Returns `true` if the password\n * is accepted (or the server is in a development mode that bypasses the\n * check). Use this from Node contexts where the default interactive HTML\n * prompt used by other inspector RPCs is not available.\n */\nexport async function authenticate(\n delegate: ExtendedInspectorDelegate,\n password: string,\n): Promise<boolean> {\n return rpc(\n await delegate.getSocket(),\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n}\n\nclass UnauthenticatedError extends Error {}\n\nexport interface InspectorDelegate {\n getQueryMetrics(hash: string): QueryClientMetrics | undefined;\n getAST(queryID: string): AST | undefined;\n readonly metrics: ClientMetrics;\n mapClientASTToServer(ast: AST): AST;\n setQueryEvictedCallback(\n cb: (hash: string, ast: AST, metrics: QueryClientMetrics) => void,\n ): void;\n}\n\nexport interface ExtendedInspectorDelegate extends InspectorDelegate {\n readonly rep: Rep;\n readonly getSocket: () => Promise<WebSocket>;\n readonly queryDelegate: QueryDelegate;\n lazy: Promise<Lazy>;\n}\n\nexport type Rep = ReplicacheImpl<MutatorDefs>;\n\nexport type QueryClientMetrics = {\n readonly 'query-materialization-client': number | undefined;\n readonly 'query-materialization-end-to-end': number | undefined;\n readonly 'query-update-client': ReadonlyTDigest;\n};\n\nexport type ClientMetrics = {\n readonly [K in keyof ClientMetricMap]: ReadonlyTDigest;\n};\n\nexport type ServerMetrics = {\n readonly [K in keyof ServerMetricMap]: ReadonlyTDigest;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lazy-inspector.js","names":[],"sources":["../../../../../../zero-client/src/client/inspector/lazy-inspector.ts"],"sourcesContent":["import type {BTreeRead} from '../../../../replicache/src/btree/read.ts';\nimport type {Read} from '../../../../replicache/src/dag/store.ts';\nimport {readFromHash} from '../../../../replicache/src/db/read.ts';\nimport * as FormatVersion from '../../../../replicache/src/format-version-enum.ts';\nimport {getClientGroup} from '../../../../replicache/src/persist/client-groups.ts';\nimport {\n getClient,\n getClients,\n type ClientMap,\n} from '../../../../replicache/src/persist/clients.ts';\nimport type {ReplicacheImpl} from '../../../../replicache/src/replicache-impl.ts';\nimport {withRead} from '../../../../replicache/src/with-transactions.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../../shared/src/json.ts';\nimport {TDigest, type ReadonlyTDigest} from '../../../../shared/src/tdigest.ts';\nimport * as valita from '../../../../shared/src/valita.ts';\nimport type {AnalyzeQueryResult} from '../../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport {\n inspectAnalyzeQueryDownSchema,\n inspectAuthenticatedDownSchema,\n inspectErrorDownSchema,\n inspectMetricsDownSchema,\n inspectQueriesDownSchema,\n inspectVersionDownSchema,\n type InspectDownBody,\n type InspectQueryRow,\n type QueryServerMetrics as QueryServerMetricsJSON,\n} from '../../../../zero-protocol/src/inspect-down.ts';\nimport type {\n AnalyzeQueryOptions,\n InspectUpBody,\n} from '../../../../zero-protocol/src/inspect-up.ts';\nimport type {\n ClientMetricMap,\n ServerMetricMap,\n} from '../../../../zql/src/query/metrics-delegate.ts';\nimport type {QueryDelegate} from '../../../../zql/src/query/query-delegate.ts';\nimport {asQueryInternals} from '../../../../zql/src/query/query-internals.ts';\nimport type {AnyQuery} from '../../../../zql/src/query/query.ts';\nimport {nanoid} from '../../util/nanoid.ts';\nimport {ENTITIES_KEY_PREFIX} from '../keys.ts';\nimport type {MutatorDefs} from '../replicache-types.ts';\nimport {Client} from './client.ts';\nimport {createHTMLPasswordPrompt} from './html-dialog-prompt.ts';\nimport {type Lazy} from './inspector.ts';\nimport {Query} from './query.ts';\n\nexport type GetWebSocket = () => Promise<WebSocket>;\n\nexport type Metrics = {\n readonly [K in keyof (ClientMetricMap & ServerMetricMap)]: ReadonlyTDigest;\n};\n\ntype DistributiveOmit<T, K extends string> = T extends object\n ? Omit<T, K>\n : never;\n\nexport async function rpc<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n try {\n return await rpcNoAuthTry(socket, arg, downSchema);\n } catch (e) {\n if (e instanceof UnauthenticatedError) {\n const password = await createHTMLPasswordPrompt('Enter password:');\n if (password) {\n // Do authenticate rpc\n const authRes = await rpcNoAuthTry(\n socket,\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n if (authRes) {\n // If authentication is successful, retry the original RPC\n return rpcNoAuthTry(socket, arg, downSchema);\n }\n }\n throw new Error('Authentication failed');\n }\n throw e;\n }\n}\n\nfunction rpcNoAuthTry<T extends InspectDownBody>(\n socket: WebSocket,\n arg: DistributiveOmit<InspectUpBody, 'id'>,\n downSchema: valita.Type<T>,\n): Promise<T['value']> {\n return new Promise((resolve, reject) => {\n const id = nanoid();\n const f = (ev: MessageEvent) => {\n if (typeof ev.data !== 'string') {\n // Binary messages (such as poke chunks) are not inspector messages.\n return;\n }\n let msg: unknown;\n try {\n msg = JSON.parse(ev.data);\n } catch {\n return;\n }\n if (!Array.isArray(msg) || msg[0] !== 'inspect') {\n return;\n }\n const body = msg[1];\n if (body.id !== id) {\n return;\n }\n const errorRes = valita.test(body, inspectErrorDownSchema);\n if (errorRes.ok) {\n reject(new Error(errorRes.value.value));\n } else {\n const res = valita.test(body, downSchema);\n if (res.ok) {\n resolve(res.value.value);\n } else {\n // Check if we got un authenticated/false response\n const authRes = valita.test(body, inspectAuthenticatedDownSchema);\n if (authRes.ok) {\n // Handle authenticated response\n assert(\n authRes.value.value === false,\n 'Expected unauthenticated response',\n );\n reject(new UnauthenticatedError());\n } else {\n reject(res.error);\n }\n }\n }\n socket.removeEventListener('message', f);\n };\n socket.addEventListener('message', f);\n socket.send(JSON.stringify(['inspect', {...arg, id}]));\n });\n} // T extends forces T to be resolved\n\nexport function mergeMetrics(\n clientMetrics: QueryClientMetrics | undefined,\n serverMetrics: QueryServerMetricsJSON | null | undefined,\n): ClientMetrics & ServerMetrics {\n const cm = clientMetrics ?? newClientMetrics();\n return {\n ...convertClientMetrics(cm),\n ...(serverMetrics\n ? convertServerMetrics(serverMetrics)\n : newServerMetrics()),\n };\n}\n\nfunction newClientMetrics(): QueryClientMetrics {\n return {\n 'query-materialization-client': undefined,\n 'query-materialization-end-to-end': undefined,\n 'query-update-client': new TDigest(),\n };\n}\n\nfunction convertClientMetrics(metrics: QueryClientMetrics): ClientMetrics {\n const hydrateDigest = new TDigest();\n if (metrics['query-materialization-client'] !== undefined) {\n hydrateDigest.add(metrics['query-materialization-client']);\n }\n const totalDigest = new TDigest();\n if (metrics['query-materialization-end-to-end'] !== undefined) {\n totalDigest.add(metrics['query-materialization-end-to-end']);\n }\n return {\n 'query-materialization-client': hydrateDigest,\n 'query-materialization-end-to-end': totalDigest,\n 'query-update-client': metrics['query-update-client'] as TDigest,\n };\n}\n\nfunction newServerMetrics(): ServerMetrics {\n return {\n 'query-materialization-server': new TDigest(),\n 'query-update-server': new TDigest(),\n };\n}\n\nfunction convertServerMetrics(metrics: QueryServerMetricsJSON): ServerMetrics {\n const hydrateMs = metrics['query-hydration-server-ms'];\n const hydrateDigest = new TDigest();\n if (hydrateMs !== undefined) {\n hydrateDigest.add(hydrateMs);\n }\n return {\n 'query-materialization-server': hydrateDigest,\n 'query-update-server': TDigest.fromJSON(metrics['query-update-server']),\n };\n}\n\nexport async function inspectorMetrics(\n delegate: ExtendedInspectorDelegate,\n): Promise<Metrics> {\n const clientMetrics = delegate.metrics;\n const serverMetricsJSON = await rpc(\n await delegate.getSocket(),\n {op: 'metrics'},\n inspectMetricsDownSchema,\n );\n return {\n ...(clientMetrics ?? newClientMetrics()),\n 'query-materialization-server': TDigest.fromJSON(\n serverMetricsJSON['query-materialization-server'],\n ),\n 'query-update-server': TDigest.fromJSON(\n serverMetricsJSON['query-update-server'],\n ),\n };\n}\n\nexport function inspectorClients(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead => clients(delegate, dagRead));\n}\n\nexport function inspectorClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Client[]> {\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead),\n );\n}\n\nasync function withDagRead<T>(\n delegate: ExtendedInspectorDelegate,\n f: (dagRead: Read) => Promise<T>,\n): Promise<T> {\n const {rep} = delegate;\n await rep.refresh();\n await rep.persist();\n return withRead(rep.perdag, f);\n}\n\nasync function getBTree(dagRead: Read, clientID: string): Promise<BTreeRead> {\n const client = await getClient(clientID, dagRead);\n assert(client, `Client not found: ${clientID}`);\n const {clientGroupID} = client;\n const clientGroup = await getClientGroup(clientGroupID, dagRead);\n assert(clientGroup, `Client group not found: ${clientGroupID}`);\n const dbRead = await readFromHash(\n clientGroup.headHash,\n dagRead,\n FormatVersion.Latest,\n );\n return dbRead.map;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype MapEntry<T extends ReadonlyMap<any, any>> =\n T extends ReadonlyMap<infer K, infer V> ? readonly [K, V] : never;\n\nasync function clients(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const clients = await getClients(dagRead);\n return [...clients.entries()]\n .filter(predicate)\n .map(\n ([clientID, {clientGroupID}]) =>\n new Client(delegate, clientID, clientGroupID),\n );\n}\n\nasync function clientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n dagRead: Read,\n predicate: (entry: MapEntry<ClientMap>) => boolean = () => true,\n): Promise<Client[]> {\n const allClients = await clients(delegate, dagRead, predicate);\n const clientsWithQueries: Client[] = [];\n await Promise.all(\n allClients.map(async client => {\n const queries = await client.queries();\n if (queries.length > 0) {\n clientsWithQueries.push(client);\n }\n }),\n );\n return clientsWithQueries;\n}\n\nexport async function clientGroupClients(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clients(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport async function clientGroupClientsWithQueries(\n delegate: ExtendedInspectorDelegate,\n clientGroupID: Promise<string> | string,\n): Promise<Client[]> {\n const id = await clientGroupID;\n return withDagRead(delegate, dagRead =>\n clientsWithQueries(delegate, dagRead, ([_, v]) => v.clientGroupID === id),\n );\n}\n\nexport function clientGroupQueries(\n delegate: ExtendedInspectorDelegate,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries'});\n}\nexport function clientMap(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Map<string, ReadonlyJSONValue>> {\n return withDagRead(delegate, async dagRead => {\n const tree = await getBTree(dagRead, clientID);\n const map = new Map<string, ReadonlyJSONValue>();\n for await (const [key, value] of tree.scan('', true)) {\n map.set(key, value);\n }\n return map;\n });\n}\n\nexport function clientRows(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n tableName: string,\n): Promise<Row[]> {\n return withDagRead(delegate, async dagRead => {\n const prefix = ENTITIES_KEY_PREFIX + tableName + '/';\n const tree = await getBTree(dagRead, clientID);\n const rows: Row[] = [];\n for await (const [key, value] of tree.scan(prefix, false)) {\n if (!key.startsWith(prefix)) {\n break;\n }\n rows.push(value as Row);\n }\n return rows;\n });\n}\n\nexport async function serverVersion(\n delegate: ExtendedInspectorDelegate,\n): Promise<string> {\n return rpc(\n await delegate.getSocket(),\n {op: 'version'},\n inspectVersionDownSchema,\n );\n}\n\nexport function clientQueries(\n delegate: ExtendedInspectorDelegate,\n clientID: string,\n): Promise<Query[]> {\n return queries(delegate, {op: 'queries', clientID});\n}\n\nasync function queries(\n delegate: ExtendedInspectorDelegate,\n arg: {op: 'queries'; clientID?: string},\n): Promise<Query[]> {\n const rows: InspectQueryRow[] = await rpc(\n await delegate.getSocket(),\n arg,\n inspectQueriesDownSchema,\n );\n const queries = rows.map(row => new Query(row, delegate, delegate.getSocket));\n queries.sort((a, b) => (b.hydrateServer ?? 0) - (a.hydrateServer ?? 0));\n return queries;\n}\n\nexport async function analyzeQuery(\n delegate: ExtendedInspectorDelegate,\n query: AnyQuery,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n const qi = asQueryInternals(query);\n const {customQueryID} = qi;\n const queryParameters = customQueryID\n ? {name: customQueryID.name, args: customQueryID.args}\n : {ast: delegate.mapClientASTToServer(qi.ast)};\n\n return rpc(\n await delegate.getSocket(),\n {\n op: 'analyze-query',\n ...queryParameters,\n options,\n },\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeServerAST(\n delegate: ExtendedInspectorDelegate,\n ast: AST,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', ast, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\nexport async function analyzeNamedQuery(\n delegate: ExtendedInspectorDelegate,\n name: string,\n args: ReadonlyArray<ReadonlyJSONValue>,\n options?: AnalyzeQueryOptions,\n): Promise<AnalyzeQueryResult> {\n return rpc(\n await delegate.getSocket(),\n {op: 'analyze-query', name, args, options},\n inspectAnalyzeQueryDownSchema,\n );\n}\n\n/**\n * Sends an `authenticate` op to the server. Returns `true` if the password\n * is accepted (or the server is in a development mode that bypasses the\n * check). Use this from Node contexts where the default interactive HTML\n * prompt used by other inspector RPCs is not available.\n */\nexport async function authenticate(\n delegate: ExtendedInspectorDelegate,\n password: string,\n): Promise<boolean> {\n return rpc(\n await delegate.getSocket(),\n {op: 'authenticate', value: password},\n inspectAuthenticatedDownSchema,\n );\n}\n\nclass UnauthenticatedError extends Error {}\n\nexport interface InspectorDelegate {\n getQueryMetrics(hash: string): QueryClientMetrics | undefined;\n getAST(queryID: string): AST | undefined;\n readonly metrics: ClientMetrics;\n mapClientASTToServer(ast: AST): AST;\n setQueryEvictedCallback(\n cb: (hash: string, ast: AST, metrics: QueryClientMetrics) => void,\n ): void;\n}\n\nexport interface ExtendedInspectorDelegate extends InspectorDelegate {\n readonly rep: Rep;\n readonly getSocket: () => Promise<WebSocket>;\n readonly queryDelegate: QueryDelegate;\n lazy: Promise<Lazy>;\n}\n\nexport type Rep = ReplicacheImpl<MutatorDefs>;\n\nexport type QueryClientMetrics = {\n readonly 'query-materialization-client': number | undefined;\n readonly 'query-materialization-end-to-end': number | undefined;\n readonly 'query-update-client': ReadonlyTDigest;\n};\n\nexport type ClientMetrics = {\n readonly [K in keyof ClientMetricMap]: ReadonlyTDigest;\n};\n\nexport type ServerMetrics = {\n readonly [K in keyof ServerMetricMap]: ReadonlyTDigest;\n};\n"],"mappings":"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"}
|