@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
|
@@ -25,7 +25,18 @@ export declare class BTreeRead implements AsyncIterable<Entry<FrozenJSONValue>>
|
|
|
25
25
|
get(key: string): Promise<FrozenJSONValue | undefined>;
|
|
26
26
|
has(key: string): Promise<boolean>;
|
|
27
27
|
isEmpty(): Promise<boolean>;
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* We don't do any encoding of the key in the map, so we have no way of
|
|
30
|
+
* determining from an entry.key alone whether it is a regular key or an
|
|
31
|
+
* encoded IndexKey in an index map. Without encoding regular map keys the
|
|
32
|
+
* caller has to deal with encoding and decoding the keys for the index map.
|
|
33
|
+
*
|
|
34
|
+
* @param prefetch Read sibling nodes in parallel before traversing them so
|
|
35
|
+
* stores can batch the reads. This can read nodes that the caller never
|
|
36
|
+
* visits. Enable it only when the caller expects to consume most of the scan
|
|
37
|
+
* or explicitly accepts that extra work.
|
|
38
|
+
*/
|
|
39
|
+
scan(fromKey: string, prefetch: boolean): AsyncIterableIterator<Entry<FrozenJSONValue>>;
|
|
29
40
|
keys(): AsyncIterableIterator<string>;
|
|
30
41
|
entries(): AsyncIterableIterator<Entry<FrozenJSONValue>>;
|
|
31
42
|
[Symbol.asyncIterator](): AsyncIterableIterator<Entry<FrozenJSONValue>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/btree/read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AAGtD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAC,KAAK,IAAI,EAAY,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAC,YAAY,EAAE,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAC9D,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAU3B,MAAM,WAAW,CAAC;AASnB,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,qBAAa,SAAU,YAAW,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACrE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,gBAAgB,CAAC,CACzD;IAEZ,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IAClC,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC;IACjD,QAAQ,EAAE,IAAI,CAAC;IACf,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IACpD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;gBAG/B,OAAO,EAAE,IAAI,EACb,aAAa,EAAE,aAAa,EAC5B,IAAI,GAAE,IAAgB,EACtB,YAAY,GAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAuB,EAC3D,eAAe,SAAmB;IAS9B,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,YAAY,GAAG,gBAAgB,CAAC;IA0B7D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAStD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMlC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/btree/read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AAGtD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAC,KAAK,IAAI,EAAY,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAC,YAAY,EAAE,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAC9D,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAU3B,MAAM,WAAW,CAAC;AASnB,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,qBAAa,SAAU,YAAW,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACrE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,gBAAgB,CAAC,CACzD;IAEZ,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IAClC,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC;IACjD,QAAQ,EAAE,IAAI,CAAC;IACf,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IACpD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;gBAG/B,OAAO,EAAE,IAAI,EACb,aAAa,EAAE,aAAa,EAC5B,IAAI,GAAE,IAAgB,EACtB,YAAY,GAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAuB,EAC3D,eAAe,SAAmB;IAS9B,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,YAAY,GAAG,gBAAgB,CAAC;IA0B7D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAStD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMlC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAUjC;;;;;;;;;;OAUG;IACH,IAAI,CACF,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,GAChB,qBAAqB,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAczC,IAAI,IAAI,qBAAqB,CAAC,MAAM,CAAC;IAKrC,OAAO,IAAI,qBAAqB,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAK/D,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAIhE,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,qBAAqB,CAAC,qBAAqB,CAAC;CAO3E;AAyMD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,SAAS,EACd,EAAE,EAAE,KAAK,GAAG,KAAK,GAChB,OAAO,CAAC,YAAY,CAAC,CAmBvB"}
|
|
@@ -42,12 +42,22 @@ var BTreeRead = class {
|
|
|
42
42
|
if (this.rootHash !== rootHash) return this.isEmpty();
|
|
43
43
|
return node.entries.length === 0;
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* We don't do any encoding of the key in the map, so we have no way of
|
|
47
|
+
* determining from an entry.key alone whether it is a regular key or an
|
|
48
|
+
* encoded IndexKey in an index map. Without encoding regular map keys the
|
|
49
|
+
* caller has to deal with encoding and decoding the keys for the index map.
|
|
50
|
+
*
|
|
51
|
+
* @param prefetch Read sibling nodes in parallel before traversing them so
|
|
52
|
+
* stores can batch the reads. This can read nodes that the caller never
|
|
53
|
+
* visits. Enable it only when the caller expects to consume most of the scan
|
|
54
|
+
* or explicitly accepts that extra work.
|
|
55
|
+
*/
|
|
56
|
+
scan(fromKey, prefetch) {
|
|
46
57
|
return scanForHash(this.rootHash, () => this.rootHash, this.rootHash, fromKey, async (hash) => {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
});
|
|
58
|
+
const { level, isMutable, entries } = await this.getNode(hash);
|
|
59
|
+
return [level, isMutable ? entries.slice() : entries];
|
|
60
|
+
}, prefetch);
|
|
51
61
|
}
|
|
52
62
|
async *keys() {
|
|
53
63
|
yield* (await this.getNode(this.rootHash)).keys(this);
|
|
@@ -126,20 +136,22 @@ function* diffEntries(lastEntries, currentEntries) {
|
|
|
126
136
|
newValue: currentEntries[j][1]
|
|
127
137
|
};
|
|
128
138
|
}
|
|
129
|
-
async function* scanForHash(expectedRootHash, getRootHash, hash, fromKey, readNode) {
|
|
139
|
+
async function* scanForHash(expectedRootHash, getRootHash, hash, fromKey, readNode, prefetch) {
|
|
130
140
|
if (hash === emptyHash) return;
|
|
131
141
|
const data = await readNode(hash);
|
|
132
142
|
const entries = data[1];
|
|
133
143
|
let i = 0;
|
|
134
144
|
if (fromKey) i = binarySearch(fromKey, entries);
|
|
135
|
-
if (data[0] > 0)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
if (data[0] > 0) {
|
|
146
|
+
if (prefetch) await Promise.all(entries.slice(i).map((entry) => readNode(entry[1]).catch(() => void 0)));
|
|
147
|
+
for (; i < entries.length; i++) {
|
|
148
|
+
yield* scanForHash(expectedRootHash, getRootHash, entries[i][1], fromKey, readNode, prefetch);
|
|
149
|
+
fromKey = "";
|
|
150
|
+
}
|
|
151
|
+
} else for (; i < entries.length; i++) {
|
|
140
152
|
const rootHash = getRootHash();
|
|
141
153
|
if (expectedRootHash !== rootHash) {
|
|
142
|
-
yield* scanForHash(rootHash, getRootHash, rootHash, entries[i][0], readNode);
|
|
154
|
+
yield* scanForHash(rootHash, getRootHash, rootHash, entries[i][0], readNode, prefetch);
|
|
143
155
|
return;
|
|
144
156
|
}
|
|
145
157
|
yield entries[i];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","names":[],"sources":["../../../../../replicache/src/btree/read.ts"],"sourcesContent":["import type {Enum} from '../../../shared/src/enum.ts';\nimport {deepEqual} from '../../../shared/src/json.ts';\nimport {getSizeOfEntry} from '../../../shared/src/size-of-value.ts';\nimport type {Read} from '../dag/store.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\nimport type {DataNodeImpl, InternalNodeImpl} from './node.ts';\nimport {\n type Entry,\n type InternalDiff,\n type InternalDiffOperation,\n NODE_ENTRIES,\n NODE_LEVEL,\n binarySearch,\n binarySearchFound,\n emptyDataNodeImpl,\n findLeaf,\n isDataNodeImpl,\n newNodeImpl,\n parseBTreeNode,\n} from './node.ts';\nimport {\n SPLICE_ADDED,\n SPLICE_AT,\n SPLICE_FROM,\n SPLICE_REMOVED,\n computeSplices,\n} from './splice.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\n/**\n * The size of the header of a node. (If we had compile time\n * constants we would have used that).\n *\n * There is a test ensuring this is correct.\n */\nexport const NODE_HEADER_SIZE = 11;\n\nexport class BTreeRead implements AsyncIterable<Entry<FrozenJSONValue>> {\n protected readonly _cache: Map<Hash, DataNodeImpl | InternalNodeImpl> =\n new Map();\n\n protected readonly _dagRead: Read;\n protected readonly _formatVersion: FormatVersion;\n rootHash: Hash;\n readonly getEntrySize: <K, V>(k: K, v: V) => number;\n readonly chunkHeaderSize: number;\n\n constructor(\n dagRead: Read,\n formatVersion: FormatVersion,\n root: Hash = emptyHash,\n getEntrySize: <K, V>(k: K, v: V) => number = getSizeOfEntry,\n chunkHeaderSize = NODE_HEADER_SIZE,\n ) {\n this._dagRead = dagRead;\n this._formatVersion = formatVersion;\n this.rootHash = root;\n this.getEntrySize = getEntrySize;\n this.chunkHeaderSize = chunkHeaderSize;\n }\n\n async getNode(hash: Hash): Promise<DataNodeImpl | InternalNodeImpl> {\n if (hash === emptyHash) {\n return emptyDataNodeImpl;\n }\n\n const cached = this._cache.get(hash);\n if (cached) {\n return cached;\n }\n\n const chunk = await this._dagRead.mustGetChunk(hash);\n const data = parseBTreeNode(\n chunk.data,\n this._formatVersion,\n this.getEntrySize,\n );\n const impl = newNodeImpl(\n data[NODE_ENTRIES] as Entry<FrozenJSONValue>[],\n hash,\n data[NODE_LEVEL],\n false,\n );\n this._cache.set(hash, impl);\n return impl;\n }\n\n async get(key: string): Promise<FrozenJSONValue | undefined> {\n const leaf = await findLeaf(key, this.rootHash, this, this.rootHash);\n const index = binarySearch(key, leaf.entries);\n if (!binarySearchFound(index, leaf.entries, key)) {\n return undefined;\n }\n return leaf.entries[index][1];\n }\n\n async has(key: string): Promise<boolean> {\n const leaf = await findLeaf(key, this.rootHash, this, this.rootHash);\n const index = binarySearch(key, leaf.entries);\n return binarySearchFound(index, leaf.entries, key);\n }\n\n async isEmpty(): Promise<boolean> {\n const {rootHash} = this;\n const node = await this.getNode(this.rootHash);\n // The root hash has changed, so the tree has been modified.\n if (this.rootHash !== rootHash) {\n return this.isEmpty();\n }\n return node.entries.length === 0;\n }\n\n // We don't do any encoding of the key in the map, so we have no way of\n // determining from an entry.key alone whether it is a regular key or an\n // encoded IndexKey in an index map. Without encoding regular map keys the\n // caller has to deal with encoding and decoding the keys for the index map.\n scan(fromKey: string): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n return scanForHash(\n this.rootHash,\n () => this.rootHash,\n this.rootHash,\n fromKey,\n async hash => {\n const cached = await this.getNode(hash);\n if (cached) {\n return [\n cached.level,\n cached.isMutable ? cached.entries.slice() : cached.entries,\n ];\n }\n const chunk = await this._dagRead.mustGetChunk(hash);\n return parseBTreeNode(\n chunk.data,\n this._formatVersion,\n this.getEntrySize,\n );\n },\n );\n }\n\n async *keys(): AsyncIterableIterator<string> {\n const node = await this.getNode(this.rootHash);\n yield* node.keys(this);\n }\n\n async *entries(): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n const node = await this.getNode(this.rootHash);\n yield* node.entriesIter(this);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n return this.entries();\n }\n\n async *diff(last: BTreeRead): AsyncIterableIterator<InternalDiffOperation> {\n const [currentNode, lastNode] = await Promise.all([\n this.getNode(this.rootHash),\n last.getNode(last.rootHash),\n ]);\n yield* diffNodes(lastNode, currentNode, last, this);\n }\n}\n\nasync function* diffNodes(\n last: InternalNodeImpl | DataNodeImpl,\n current: InternalNodeImpl | DataNodeImpl,\n lastTree: BTreeRead,\n currentTree: BTreeRead,\n): AsyncIterableIterator<InternalDiffOperation> {\n if (last.level > current.level) {\n // merge all of last's children into a new node\n // We know last is an internal node because level > 0.\n const lastChild = (await (last as InternalNodeImpl).getCompositeChildren(\n 0,\n last.entries.length,\n lastTree,\n )) as InternalNodeImpl;\n yield* diffNodes(lastChild, current, lastTree, currentTree);\n return;\n }\n\n if (current.level > last.level) {\n // We know current is an internal node because level > 0.\n const currentChild = (await (\n current as InternalNodeImpl\n ).getCompositeChildren(\n 0,\n current.entries.length,\n currentTree,\n )) as InternalNodeImpl;\n yield* diffNodes(last, currentChild, lastTree, currentTree);\n return;\n }\n\n if (isDataNodeImpl(last) && isDataNodeImpl(current)) {\n yield* diffEntries(\n (last as DataNodeImpl).entries,\n (current as DataNodeImpl).entries,\n );\n return;\n }\n\n // Now we have two internal nodes with the same level. We compute the diff as\n // splices for the internal node entries. We then flatten these and call diff\n // recursively.\n const initialSplices = computeSplices(\n (last as InternalNodeImpl).entries,\n (current as InternalNodeImpl).entries,\n );\n for (const splice of initialSplices) {\n const [lastChild, currentChild] = await Promise.all([\n (last as InternalNodeImpl).getCompositeChildren(\n splice[SPLICE_AT],\n splice[SPLICE_REMOVED],\n lastTree,\n ),\n (current as InternalNodeImpl).getCompositeChildren(\n splice[SPLICE_FROM],\n splice[SPLICE_ADDED],\n currentTree,\n ),\n ]);\n yield* diffNodes(lastChild, currentChild, lastTree, currentTree);\n }\n}\n\nfunction* diffEntries(\n lastEntries: readonly Entry<FrozenJSONValue>[],\n currentEntries: readonly Entry<FrozenJSONValue>[],\n): IterableIterator<InternalDiffOperation> {\n const lastLength = lastEntries.length;\n const currentLength = currentEntries.length;\n let i = 0;\n let j = 0;\n while (i < lastLength && j < currentLength) {\n const lastKey = lastEntries[i][0];\n const currentKey = currentEntries[j][0];\n if (lastKey === currentKey) {\n if (!deepEqual(lastEntries[i][1], currentEntries[j][1])) {\n yield {\n op: 'change',\n key: lastKey,\n oldValue: lastEntries[i][1],\n newValue: currentEntries[j][1],\n };\n }\n i++;\n j++;\n } else if (lastKey < currentKey) {\n yield {\n op: 'del',\n key: lastKey,\n oldValue: lastEntries[i][1],\n };\n i++;\n } else {\n yield {\n op: 'add',\n key: currentKey,\n newValue: currentEntries[j][1],\n };\n j++;\n }\n }\n for (; i < lastLength; i++) {\n yield {\n op: 'del',\n key: lastEntries[i][0],\n oldValue: lastEntries[i][1],\n };\n }\n for (; j < currentLength; j++) {\n yield {\n op: 'add',\n key: currentEntries[j][0],\n newValue: currentEntries[j][1],\n };\n }\n}\n\n// Redefine the type here to allow the optional size in the tuple.\ntype ReadNodeResult = readonly [\n level: number,\n data: readonly Entry<FrozenJSONValue>[] | readonly Entry<Hash>[],\n];\n\ntype ReadNode = (hash: Hash) => Promise<ReadNodeResult>;\n\nasync function* scanForHash(\n expectedRootHash: Hash,\n getRootHash: () => Hash,\n hash: Hash,\n fromKey: string,\n readNode: ReadNode,\n): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n if (hash === emptyHash) {\n return;\n }\n\n const data = await readNode(hash);\n const entries = data[NODE_ENTRIES];\n let i = 0;\n if (fromKey) {\n i = binarySearch(fromKey, entries);\n }\n if (data[NODE_LEVEL] > 0) {\n for (; i < entries.length; i++) {\n yield* scanForHash(\n expectedRootHash,\n getRootHash,\n (entries[i] as Entry<Hash>)[1],\n fromKey,\n readNode,\n );\n fromKey = '';\n }\n } else {\n for (; i < entries.length; i++) {\n const rootHash = getRootHash();\n // If rootHash changed then we start a new iterator from the key.\n if (expectedRootHash !== rootHash) {\n yield* scanForHash(\n rootHash,\n getRootHash,\n rootHash,\n entries[i][0],\n readNode,\n );\n return;\n }\n yield entries[i] as Entry<FrozenJSONValue>;\n }\n }\n}\n\nexport async function allEntriesAsDiff(\n map: BTreeRead,\n op: 'add' | 'del',\n): Promise<InternalDiff> {\n const diff: InternalDiffOperation[] = [];\n const make: (entry: Entry<FrozenJSONValue>) => InternalDiffOperation =\n op === 'add'\n ? entry => ({\n op: 'add',\n key: entry[0],\n newValue: entry[1],\n })\n : entry => ({\n op: 'del',\n key: entry[0],\n oldValue: entry[1],\n });\n\n for await (const entry of map.entries()) {\n diff.push(make(entry));\n }\n return diff;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"read.js","names":[],"sources":["../../../../../replicache/src/btree/read.ts"],"sourcesContent":["import type {Enum} from '../../../shared/src/enum.ts';\nimport {deepEqual} from '../../../shared/src/json.ts';\nimport {getSizeOfEntry} from '../../../shared/src/size-of-value.ts';\nimport type {Read} from '../dag/store.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\nimport type {DataNodeImpl, InternalNodeImpl} from './node.ts';\nimport {\n type Entry,\n type InternalDiff,\n type InternalDiffOperation,\n NODE_ENTRIES,\n NODE_LEVEL,\n binarySearch,\n binarySearchFound,\n emptyDataNodeImpl,\n findLeaf,\n isDataNodeImpl,\n newNodeImpl,\n parseBTreeNode,\n} from './node.ts';\nimport {\n SPLICE_ADDED,\n SPLICE_AT,\n SPLICE_FROM,\n SPLICE_REMOVED,\n computeSplices,\n} from './splice.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\n/**\n * The size of the header of a node. (If we had compile time\n * constants we would have used that).\n *\n * There is a test ensuring this is correct.\n */\nexport const NODE_HEADER_SIZE = 11;\n\nexport class BTreeRead implements AsyncIterable<Entry<FrozenJSONValue>> {\n protected readonly _cache: Map<Hash, DataNodeImpl | InternalNodeImpl> =\n new Map();\n\n protected readonly _dagRead: Read;\n protected readonly _formatVersion: FormatVersion;\n rootHash: Hash;\n readonly getEntrySize: <K, V>(k: K, v: V) => number;\n readonly chunkHeaderSize: number;\n\n constructor(\n dagRead: Read,\n formatVersion: FormatVersion,\n root: Hash = emptyHash,\n getEntrySize: <K, V>(k: K, v: V) => number = getSizeOfEntry,\n chunkHeaderSize = NODE_HEADER_SIZE,\n ) {\n this._dagRead = dagRead;\n this._formatVersion = formatVersion;\n this.rootHash = root;\n this.getEntrySize = getEntrySize;\n this.chunkHeaderSize = chunkHeaderSize;\n }\n\n async getNode(hash: Hash): Promise<DataNodeImpl | InternalNodeImpl> {\n if (hash === emptyHash) {\n return emptyDataNodeImpl;\n }\n\n const cached = this._cache.get(hash);\n if (cached) {\n return cached;\n }\n\n const chunk = await this._dagRead.mustGetChunk(hash);\n const data = parseBTreeNode(\n chunk.data,\n this._formatVersion,\n this.getEntrySize,\n );\n const impl = newNodeImpl(\n data[NODE_ENTRIES] as Entry<FrozenJSONValue>[],\n hash,\n data[NODE_LEVEL],\n false,\n );\n this._cache.set(hash, impl);\n return impl;\n }\n\n async get(key: string): Promise<FrozenJSONValue | undefined> {\n const leaf = await findLeaf(key, this.rootHash, this, this.rootHash);\n const index = binarySearch(key, leaf.entries);\n if (!binarySearchFound(index, leaf.entries, key)) {\n return undefined;\n }\n return leaf.entries[index][1];\n }\n\n async has(key: string): Promise<boolean> {\n const leaf = await findLeaf(key, this.rootHash, this, this.rootHash);\n const index = binarySearch(key, leaf.entries);\n return binarySearchFound(index, leaf.entries, key);\n }\n\n async isEmpty(): Promise<boolean> {\n const {rootHash} = this;\n const node = await this.getNode(this.rootHash);\n // The root hash has changed, so the tree has been modified.\n if (this.rootHash !== rootHash) {\n return this.isEmpty();\n }\n return node.entries.length === 0;\n }\n\n /**\n * We don't do any encoding of the key in the map, so we have no way of\n * determining from an entry.key alone whether it is a regular key or an\n * encoded IndexKey in an index map. Without encoding regular map keys the\n * caller has to deal with encoding and decoding the keys for the index map.\n *\n * @param prefetch Read sibling nodes in parallel before traversing them so\n * stores can batch the reads. This can read nodes that the caller never\n * visits. Enable it only when the caller expects to consume most of the scan\n * or explicitly accepts that extra work.\n */\n scan(\n fromKey: string,\n prefetch: boolean,\n ): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n return scanForHash(\n this.rootHash,\n () => this.rootHash,\n this.rootHash,\n fromKey,\n async hash => {\n const {level, isMutable, entries} = await this.getNode(hash);\n return [level, isMutable ? entries.slice() : entries];\n },\n prefetch,\n );\n }\n\n async *keys(): AsyncIterableIterator<string> {\n const node = await this.getNode(this.rootHash);\n yield* node.keys(this);\n }\n\n async *entries(): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n const node = await this.getNode(this.rootHash);\n yield* node.entriesIter(this);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n return this.entries();\n }\n\n async *diff(last: BTreeRead): AsyncIterableIterator<InternalDiffOperation> {\n const [currentNode, lastNode] = await Promise.all([\n this.getNode(this.rootHash),\n last.getNode(last.rootHash),\n ]);\n yield* diffNodes(lastNode, currentNode, last, this);\n }\n}\n\nasync function* diffNodes(\n last: InternalNodeImpl | DataNodeImpl,\n current: InternalNodeImpl | DataNodeImpl,\n lastTree: BTreeRead,\n currentTree: BTreeRead,\n): AsyncIterableIterator<InternalDiffOperation> {\n if (last.level > current.level) {\n // merge all of last's children into a new node\n // We know last is an internal node because level > 0.\n const lastChild = (await (last as InternalNodeImpl).getCompositeChildren(\n 0,\n last.entries.length,\n lastTree,\n )) as InternalNodeImpl;\n yield* diffNodes(lastChild, current, lastTree, currentTree);\n return;\n }\n\n if (current.level > last.level) {\n // We know current is an internal node because level > 0.\n const currentChild = (await (\n current as InternalNodeImpl\n ).getCompositeChildren(\n 0,\n current.entries.length,\n currentTree,\n )) as InternalNodeImpl;\n yield* diffNodes(last, currentChild, lastTree, currentTree);\n return;\n }\n\n if (isDataNodeImpl(last) && isDataNodeImpl(current)) {\n yield* diffEntries(\n (last as DataNodeImpl).entries,\n (current as DataNodeImpl).entries,\n );\n return;\n }\n\n // Now we have two internal nodes with the same level. We compute the diff as\n // splices for the internal node entries. We then flatten these and call diff\n // recursively.\n const initialSplices = computeSplices(\n (last as InternalNodeImpl).entries,\n (current as InternalNodeImpl).entries,\n );\n for (const splice of initialSplices) {\n const [lastChild, currentChild] = await Promise.all([\n (last as InternalNodeImpl).getCompositeChildren(\n splice[SPLICE_AT],\n splice[SPLICE_REMOVED],\n lastTree,\n ),\n (current as InternalNodeImpl).getCompositeChildren(\n splice[SPLICE_FROM],\n splice[SPLICE_ADDED],\n currentTree,\n ),\n ]);\n yield* diffNodes(lastChild, currentChild, lastTree, currentTree);\n }\n}\n\nfunction* diffEntries(\n lastEntries: readonly Entry<FrozenJSONValue>[],\n currentEntries: readonly Entry<FrozenJSONValue>[],\n): IterableIterator<InternalDiffOperation> {\n const lastLength = lastEntries.length;\n const currentLength = currentEntries.length;\n let i = 0;\n let j = 0;\n while (i < lastLength && j < currentLength) {\n const lastKey = lastEntries[i][0];\n const currentKey = currentEntries[j][0];\n if (lastKey === currentKey) {\n if (!deepEqual(lastEntries[i][1], currentEntries[j][1])) {\n yield {\n op: 'change',\n key: lastKey,\n oldValue: lastEntries[i][1],\n newValue: currentEntries[j][1],\n };\n }\n i++;\n j++;\n } else if (lastKey < currentKey) {\n yield {\n op: 'del',\n key: lastKey,\n oldValue: lastEntries[i][1],\n };\n i++;\n } else {\n yield {\n op: 'add',\n key: currentKey,\n newValue: currentEntries[j][1],\n };\n j++;\n }\n }\n for (; i < lastLength; i++) {\n yield {\n op: 'del',\n key: lastEntries[i][0],\n oldValue: lastEntries[i][1],\n };\n }\n for (; j < currentLength; j++) {\n yield {\n op: 'add',\n key: currentEntries[j][0],\n newValue: currentEntries[j][1],\n };\n }\n}\n\n// Redefine the type here to allow the optional size in the tuple.\ntype ReadNodeResult = readonly [\n level: number,\n data: readonly Entry<FrozenJSONValue>[] | readonly Entry<Hash>[],\n];\n\ntype ReadNode = (hash: Hash) => Promise<ReadNodeResult>;\n\nasync function* scanForHash(\n expectedRootHash: Hash,\n getRootHash: () => Hash,\n hash: Hash,\n fromKey: string,\n readNode: ReadNode,\n prefetch: boolean,\n): AsyncIterableIterator<Entry<FrozenJSONValue>> {\n if (hash === emptyHash) {\n return;\n }\n\n const data = await readNode(hash);\n const entries = data[NODE_ENTRIES];\n let i = 0;\n if (fromKey) {\n i = binarySearch(fromKey, entries);\n }\n if (data[NODE_LEVEL] > 0) {\n if (prefetch) {\n // Preload children in parallel so the backing store can coalesce their\n // reads. This can waste work when a caller stops before visiting every\n // child, so callers must explicitly opt in when they expect to consume\n // the full scan. Zero currently accepts that tradeoff: startup scans the\n // entire cached entity range into IVM memory before becoming ready (see\n // `ZeroRep.init` in packages/zero-client/src/client/zero-rep.ts), and\n // client views are intended to remain small. If views commonly outgrow\n // the 100 MB `LAZY_STORE_SOURCE_CHUNK_CACHE_SIZE_LIMIT`, reconsider the\n // preload window, although batching may still be a useful general\n // optimization. Longer term, the Replicache/IVM boundary should also be\n // revisited because materializing entities into `MemorySource`\n // duplicates B-tree storage.\n //\n // The serial scan below preserves traversal order and remains the\n // authoritative read, so speculative failures are ignored here.\n await Promise.all(\n entries\n .slice(i)\n .map(entry =>\n readNode((entry as Entry<Hash>)[1]).catch(() => undefined),\n ),\n );\n }\n for (; i < entries.length; i++) {\n yield* scanForHash(\n expectedRootHash,\n getRootHash,\n (entries[i] as Entry<Hash>)[1],\n fromKey,\n readNode,\n prefetch,\n );\n fromKey = '';\n }\n } else {\n for (; i < entries.length; i++) {\n const rootHash = getRootHash();\n // If rootHash changed then we start a new iterator from the key.\n if (expectedRootHash !== rootHash) {\n yield* scanForHash(\n rootHash,\n getRootHash,\n rootHash,\n entries[i][0],\n readNode,\n prefetch,\n );\n return;\n }\n yield entries[i] as Entry<FrozenJSONValue>;\n }\n }\n}\n\nexport async function allEntriesAsDiff(\n map: BTreeRead,\n op: 'add' | 'del',\n): Promise<InternalDiff> {\n const diff: InternalDiffOperation[] = [];\n const make: (entry: Entry<FrozenJSONValue>) => InternalDiffOperation =\n op === 'add'\n ? entry => ({\n op: 'add',\n key: entry[0],\n newValue: entry[1],\n })\n : entry => ({\n op: 'del',\n key: entry[0],\n oldValue: entry[1],\n });\n\n for await (const entry of map.entries()) {\n diff.push(make(entry));\n }\n return diff;\n}\n"],"mappings":";;;;;AAwCA,IAAa,YAAb,MAAwE;CACtE,yBACE,IAAI,IAAI;CAEV;CACA;CACA;CACA;CACA;CAEA,YACE,SACA,eACA,OAAa,WACb,eAA6C,gBAC7C,kBAAA,IACA;EACA,KAAK,WAAW;EAChB,KAAK,iBAAiB;EACtB,KAAK,WAAW;EAChB,KAAK,eAAe;EACpB,KAAK,kBAAkB;CACzB;CAEA,MAAM,QAAQ,MAAsD;EAClE,IAAI,SAAS,WACX,OAAO;EAGT,MAAM,SAAS,KAAK,OAAO,IAAI,IAAI;EACnC,IAAI,QACF,OAAO;EAIT,MAAM,OAAO,gBACX,MAFkB,KAAK,SAAS,aAAa,IAAI,GAE3C,MACN,KAAK,gBACL,KAAK,YACP;EACA,MAAM,OAAO,YACX,KAAA,IACA,MACA,KAAA,IACA,KACF;EACA,KAAK,OAAO,IAAI,MAAM,IAAI;EAC1B,OAAO;CACT;CAEA,MAAM,IAAI,KAAmD;EAC3D,MAAM,OAAO,MAAM,SAAS,KAAK,KAAK,UAAU,MAAM,KAAK,QAAQ;EACnE,MAAM,QAAQ,aAAa,KAAK,KAAK,OAAO;EAC5C,IAAI,CAAC,kBAAkB,OAAO,KAAK,SAAS,GAAG,GAC7C;EAEF,OAAO,KAAK,QAAQ,OAAO;CAC7B;CAEA,MAAM,IAAI,KAA+B;EACvC,MAAM,OAAO,MAAM,SAAS,KAAK,KAAK,UAAU,MAAM,KAAK,QAAQ;EAEnE,OAAO,kBADO,aAAa,KAAK,KAAK,OACZ,GAAO,KAAK,SAAS,GAAG;CACnD;CAEA,MAAM,UAA4B;EAChC,MAAM,EAAC,aAAY;EACnB,MAAM,OAAO,MAAM,KAAK,QAAQ,KAAK,QAAQ;EAE7C,IAAI,KAAK,aAAa,UACpB,OAAO,KAAK,QAAQ;EAEtB,OAAO,KAAK,QAAQ,WAAW;CACjC;;;;;;;;;;;;CAaA,KACE,SACA,UAC+C;EAC/C,OAAO,YACL,KAAK,gBACC,KAAK,UACX,KAAK,UACL,SACA,OAAM,SAAQ;GACZ,MAAM,EAAC,OAAO,WAAW,YAAW,MAAM,KAAK,QAAQ,IAAI;GAC3D,OAAO,CAAC,OAAO,YAAY,QAAQ,MAAM,IAAI,OAAO;EACtD,GACA,QACF;CACF;CAEA,OAAO,OAAsC;EAE3C,QAAO,MADY,KAAK,QAAQ,KAAK,QAAQ,GACjC,KAAK,IAAI;CACvB;CAEA,OAAO,UAAyD;EAE9D,QAAO,MADY,KAAK,QAAQ,KAAK,QAAQ,GACjC,YAAY,IAAI;CAC9B;CAEA,CAAC,OAAO,iBAAgE;EACtE,OAAO,KAAK,QAAQ;CACtB;CAEA,OAAO,KAAK,MAA+D;EACzE,MAAM,CAAC,aAAa,YAAY,MAAM,QAAQ,IAAI,CAChD,KAAK,QAAQ,KAAK,QAAQ,GAC1B,KAAK,QAAQ,KAAK,QAAQ,CAC5B,CAAC;EACD,OAAO,UAAU,UAAU,aAAa,MAAM,IAAI;CACpD;AACF;AAEA,gBAAgB,UACd,MACA,SACA,UACA,aAC8C;CAC9C,IAAI,KAAK,QAAQ,QAAQ,OAAO;EAQ9B,OAAO,UAAU,MALS,KAA0B,qBAClD,GACA,KAAK,QAAQ,QACb,QACF,GAC4B,SAAS,UAAU,WAAW;EAC1D;CACF;CAEA,IAAI,QAAQ,QAAQ,KAAK,OAAO;EAS9B,OAAO,UAAU,MAAM,MANrB,QACA,qBACA,GACA,QAAQ,QAAQ,QAChB,WACF,GACqC,UAAU,WAAW;EAC1D;CACF;CAEA,IAAI,eAAe,IAAI,KAAK,eAAe,OAAO,GAAG;EACnD,OAAO,YACJ,KAAsB,SACtB,QAAyB,OAC5B;EACA;CACF;CAKA,MAAM,iBAAiB,eACpB,KAA0B,SAC1B,QAA6B,OAChC;CACA,KAAK,MAAM,UAAU,gBAAgB;EACnC,MAAM,CAAC,WAAW,gBAAgB,MAAM,QAAQ,IAAI,CACjD,KAA0B,qBACzB,OAAA,IACA,OAAA,IACA,QACF,GACC,QAA6B,qBAC5B,OAAA,IACA,OAAA,IACA,WACF,CACF,CAAC;EACD,OAAO,UAAU,WAAW,cAAc,UAAU,WAAW;CACjE;AACF;AAEA,UAAU,YACR,aACA,gBACyC;CACzC,MAAM,aAAa,YAAY;CAC/B,MAAM,gBAAgB,eAAe;CACrC,IAAI,IAAI;CACR,IAAI,IAAI;CACR,OAAO,IAAI,cAAc,IAAI,eAAe;EAC1C,MAAM,UAAU,YAAY,GAAG;EAC/B,MAAM,aAAa,eAAe,GAAG;EACrC,IAAI,YAAY,YAAY;GAC1B,IAAI,CAAC,UAAU,YAAY,GAAG,IAAI,eAAe,GAAG,EAAE,GACpD,MAAM;IACJ,IAAI;IACJ,KAAK;IACL,UAAU,YAAY,GAAG;IACzB,UAAU,eAAe,GAAG;GAC9B;GAEF;GACA;EACF,OAAO,IAAI,UAAU,YAAY;GAC/B,MAAM;IACJ,IAAI;IACJ,KAAK;IACL,UAAU,YAAY,GAAG;GAC3B;GACA;EACF,OAAO;GACL,MAAM;IACJ,IAAI;IACJ,KAAK;IACL,UAAU,eAAe,GAAG;GAC9B;GACA;EACF;CACF;CACA,OAAO,IAAI,YAAY,KACrB,MAAM;EACJ,IAAI;EACJ,KAAK,YAAY,GAAG;EACpB,UAAU,YAAY,GAAG;CAC3B;CAEF,OAAO,IAAI,eAAe,KACxB,MAAM;EACJ,IAAI;EACJ,KAAK,eAAe,GAAG;EACvB,UAAU,eAAe,GAAG;CAC9B;AAEJ;AAUA,gBAAgB,YACd,kBACA,aACA,MACA,SACA,UACA,UAC+C;CAC/C,IAAI,SAAS,WACX;CAGF,MAAM,OAAO,MAAM,SAAS,IAAI;CAChC,MAAM,UAAU,KAAA;CAChB,IAAI,IAAI;CACR,IAAI,SACF,IAAI,aAAa,SAAS,OAAO;CAEnC,IAAI,KAAA,KAAmB,GAAG;EACxB,IAAI,UAgBF,MAAM,QAAQ,IACZ,QACG,MAAM,CAAC,EACP,KAAI,UACH,SAAU,MAAsB,EAAE,EAAE,YAAY,KAAA,CAAS,CAC3D,CACJ;EAEF,OAAO,IAAI,QAAQ,QAAQ,KAAK;GAC9B,OAAO,YACL,kBACA,aACC,QAAQ,GAAmB,IAC5B,SACA,UACA,QACF;GACA,UAAU;EACZ;CACF,OACE,OAAO,IAAI,QAAQ,QAAQ,KAAK;EAC9B,MAAM,WAAW,YAAY;EAE7B,IAAI,qBAAqB,UAAU;GACjC,OAAO,YACL,UACA,aACA,UACA,QAAQ,GAAG,IACX,UACA,QACF;GACA;EACF;EACA,MAAM,QAAQ;CAChB;AAEJ;AAEA,eAAsB,iBACpB,KACA,IACuB;CACvB,MAAM,OAAgC,CAAC;CACvC,MAAM,OACJ,OAAO,SACH,WAAU;EACR,IAAI;EACJ,KAAK,MAAM;EACX,UAAU,MAAM;CAClB,MACA,WAAU;EACR,IAAI;EACJ,KAAK,MAAM;EACX,UAAU,MAAM;CAClB;CAEN,WAAW,MAAM,SAAS,IAAI,QAAQ,GACpC,KAAK,KAAK,KAAK,KAAK,CAAC;CAEvB,OAAO;AACT"}
|
|
@@ -8,7 +8,19 @@ import type { ClientID } from '../sync/ids.ts';
|
|
|
8
8
|
import type { MutatorDefs } from '../types.ts';
|
|
9
9
|
import { type Commit, type LocalMeta, type Meta } from './commit.ts';
|
|
10
10
|
type FormatVersion = Enum<typeof FormatVersion>;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The result of replaying one mutation.
|
|
13
|
+
*
|
|
14
|
+
* `zeroData` is the Zero transaction data to use for the next mutation in the
|
|
15
|
+
* same replay. Each mutation runs against its own fork. On success that fork is
|
|
16
|
+
* returned, and if the mutator throws the caller gets back the one it passed
|
|
17
|
+
* in, without the failed mutation's IVM writes. See `rebaseMutation`.
|
|
18
|
+
*/
|
|
19
|
+
export type RebaseResult<T> = {
|
|
20
|
+
result: T;
|
|
21
|
+
zeroData: ZeroTxData | undefined;
|
|
22
|
+
};
|
|
23
|
+
export declare function rebaseMutationAndPutCommit(mutation: Commit<LocalMeta>, dagWrite: DagWrite, basis: Hash, mutators: MutatorDefs, lc: LogContext, mutationClientID: ClientID, formatVersion: FormatVersion, zeroData: ZeroTxData | undefined): Promise<RebaseResult<Commit<Meta>>>;
|
|
24
|
+
export declare function rebaseMutationAndCommit(mutation: Commit<LocalMeta>, dagWrite: DagWrite, basis: Hash, headName: string, mutators: MutatorDefs, lc: LogContext, mutationClientID: ClientID, formatVersion: FormatVersion, zeroData: ZeroTxData | undefined): Promise<RebaseResult<Hash>>;
|
|
13
25
|
export {};
|
|
14
26
|
//# sourceMappingURL=rebase.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rebase.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/db/rebase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AACtD,OAAO,KAAK,EAAC,KAAK,IAAI,QAAQ,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AACrC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAE7C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,KAAK,MAAM,EACX,KAAK,SAAS,EAEd,KAAK,IAAI,EAIV,MAAM,aAAa,CAAC;AAIrB,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"rebase.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/db/rebase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AACtD,OAAO,KAAK,EAAC,KAAK,IAAI,QAAQ,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AACrC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAE7C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,KAAK,MAAM,EACX,KAAK,SAAS,EAEd,KAAK,IAAI,EAIV,MAAM,aAAa,CAAC;AAIrB,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,MAAM,EAAE,CAAC,CAAC;IACV,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;CAClC,CAAC;AA8GF,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,UAAU,EAGd,gBAAgB,EAAE,QAAQ,EAC1B,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,GAC/B,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAYrC;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,UAAU,EAGd,gBAAgB,EAAE,QAAQ,EAC1B,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,GAC/B,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAY7B"}
|
|
@@ -14,15 +14,37 @@ async function rebaseMutation(mutation, dagWrite, basisHash, mutators, lc, mutat
|
|
|
14
14
|
const nextMutationID = await (await commitFromHash(basisHash, dagWrite)).getNextMutationID(mutationClientID, dagWrite);
|
|
15
15
|
if (nextMutationID !== localMeta.mutationID) throw new Error(`Inconsistent mutation ID: original: ${localMeta.mutationID}, next: ${nextMutationID} - mutationClientID: ${mutationClientID} mutatorName: ${name}`);
|
|
16
16
|
if (formatVersion >= 5) assertLocalMetaDD31(localMeta);
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const newWrite = () => newWriteLocal(basisHash, name, args, mutation.chunk.hash, dagWrite, localMeta.timestamp, mutationClientID, formatVersion);
|
|
18
|
+
const dbWrite = await newWrite();
|
|
19
|
+
const txData = zeroData?.fork();
|
|
20
|
+
const tx = new WriteTransactionImpl(mutationClientID, await dbWrite.getMutationID(), "rebase", txData, dbWrite, lc);
|
|
21
|
+
try {
|
|
22
|
+
await mutatorImpl(tx, args);
|
|
23
|
+
} catch (e) {
|
|
24
|
+
lc.info?.(`Rebase of mutator ${name} threw, abandoning its prediction`, e);
|
|
25
|
+
return {
|
|
26
|
+
result: await newWrite(),
|
|
27
|
+
zeroData
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
result: dbWrite,
|
|
32
|
+
zeroData: txData
|
|
33
|
+
};
|
|
20
34
|
}
|
|
21
35
|
async function rebaseMutationAndPutCommit(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData) {
|
|
22
|
-
|
|
36
|
+
const { result: tx, zeroData: next } = await rebaseMutation(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData);
|
|
37
|
+
return {
|
|
38
|
+
result: await tx.putCommit(),
|
|
39
|
+
zeroData: next
|
|
40
|
+
};
|
|
23
41
|
}
|
|
24
42
|
async function rebaseMutationAndCommit(mutation, dagWrite, basis, headName, mutators, lc, mutationClientID, formatVersion, zeroData) {
|
|
25
|
-
|
|
43
|
+
const { result: dbWrite, zeroData: next } = await rebaseMutation(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData);
|
|
44
|
+
return {
|
|
45
|
+
result: await dbWrite.commit(headName),
|
|
46
|
+
zeroData: next
|
|
47
|
+
};
|
|
26
48
|
}
|
|
27
49
|
//#endregion
|
|
28
50
|
export { rebaseMutationAndCommit, rebaseMutationAndPutCommit };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rebase.js","names":[],"sources":["../../../../../replicache/src/db/rebase.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroTxData} from '../replicache-options.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {WriteTransactionImpl} from '../transactions.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {\n type Commit,\n type LocalMeta,\n type LocalMetaDD31,\n type Meta,\n assertLocalMetaDD31,\n commitFromHash,\n isLocalMetaDD31,\n} from './commit.ts';\nimport type {Write} from './write.ts';\nimport {newWriteLocal} from './write.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nasync function rebaseMutation(\n mutation: Commit<LocalMetaDD31>,\n dagWrite: DagWrite,\n basisHash: Hash,\n mutators: MutatorDefs,\n lc: LogContext,\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Write
|
|
1
|
+
{"version":3,"file":"rebase.js","names":[],"sources":["../../../../../replicache/src/db/rebase.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroTxData} from '../replicache-options.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {WriteTransactionImpl} from '../transactions.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {\n type Commit,\n type LocalMeta,\n type LocalMetaDD31,\n type Meta,\n assertLocalMetaDD31,\n commitFromHash,\n isLocalMetaDD31,\n} from './commit.ts';\nimport type {Write} from './write.ts';\nimport {newWriteLocal} from './write.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\n/**\n * The result of replaying one mutation.\n *\n * `zeroData` is the Zero transaction data to use for the next mutation in the\n * same replay. Each mutation runs against its own fork. On success that fork is\n * returned, and if the mutator throws the caller gets back the one it passed\n * in, without the failed mutation's IVM writes. See `rebaseMutation`.\n */\nexport type RebaseResult<T> = {\n result: T;\n zeroData: ZeroTxData | undefined;\n};\n\nasync function rebaseMutation(\n mutation: Commit<LocalMetaDD31>,\n dagWrite: DagWrite,\n basisHash: Hash,\n mutators: MutatorDefs,\n lc: LogContext,\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<RebaseResult<Write>> {\n const localMeta = mutation.meta;\n const name = localMeta.mutatorName;\n if (isLocalMetaDD31(localMeta)) {\n assert(\n localMeta.clientID === mutationClientID,\n 'mutationClientID must match clientID of LocalMeta',\n );\n }\n const maybeMutatorImpl = mutators[name];\n if (!maybeMutatorImpl) {\n // Developers must not remove mutator names from code deployed with the\n // same schemaVersion because Replicache needs to be able to replay\n // mutations during pull.\n //\n // If we detect that this has happened, stub in a no-op mutator so that at\n // least sync can move forward. Note that the server-side mutation will\n // still get sent. This doesn't remove the queued local mutation, it just\n // removes its visible effects.\n lc.error?.(`Cannot rebase unknown mutator ${name}`);\n }\n const mutatorImpl =\n maybeMutatorImpl ||\n (async () => {\n // no op\n });\n\n const args = localMeta.mutatorArgsJSON;\n\n const basisCommit = await commitFromHash(basisHash, dagWrite);\n const nextMutationID = await basisCommit.getNextMutationID(\n mutationClientID,\n dagWrite,\n );\n if (nextMutationID !== localMeta.mutationID) {\n throw new Error(\n `Inconsistent mutation ID: original: ${localMeta.mutationID}, next: ${nextMutationID} - mutationClientID: ${mutationClientID} mutatorName: ${name}`,\n );\n }\n\n if (formatVersion >= FormatVersion.DD31) {\n assertLocalMetaDD31(localMeta);\n }\n\n const newWrite = () =>\n newWriteLocal(\n basisHash,\n name,\n args,\n mutation.chunk.hash,\n dagWrite,\n localMeta.timestamp,\n mutationClientID,\n formatVersion,\n );\n\n const dbWrite = await newWrite();\n\n // Run the mutator against a fork so that if it throws, its IVM writes are\n // discarded along with its `Write`.\n const txData = zeroData?.fork();\n\n const tx = new WriteTransactionImpl(\n mutationClientID,\n await dbWrite.getMutationID(),\n 'rebase',\n txData,\n dbWrite,\n lc,\n );\n\n try {\n await mutatorImpl(tx, args);\n } catch (e) {\n // A mutator can throw here without anything being wrong: it is being run\n // again against a newer server snapshot, so a check that passed when the\n // user first ran it can fail now. Rethrowing fails the whole rebase, which\n // in Zero fails the poke and disconnects the client. The mutation stays\n // pending, so the next poke fails the same way.\n //\n // Only the local prediction is dropped. The mutation is still pending and\n // is still sent to the server, which decides whether it applied.\n //\n // `dbWrite` is dropped rather than closed: it shares `dagWrite` with the\n // caller. Dropping it is enough because a `Write` does not modify anything\n // outside itself and only writes chunks when it is committed.\n //\n // Engine errors raised inside the mutator are retired the same way, rather\n // than being sorted out here. A client whose state was garbage collected\n // throws ChunkNotFoundError from its reads, and that is detected on its\n // next mutation, where replicache-impl converts it to a\n // ClientStateNotFoundError and calls onClientStateNotFound.\n lc.info?.(`Rebase of mutator ${name} threw, abandoning its prediction`, e);\n return {result: await newWrite(), zeroData};\n }\n\n return {result: dbWrite, zeroData: txData};\n}\n\nexport async function rebaseMutationAndPutCommit(\n mutation: Commit<LocalMeta>,\n dagWrite: DagWrite,\n basis: Hash,\n mutators: MutatorDefs,\n lc: LogContext,\n // TODO(greg): mutationClientID can be retrieved from mutation if LocalMeta\n // is a LocalMetaDD31. As part of DD31 cleanup we can remove this arg.\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<RebaseResult<Commit<Meta>>> {\n const {result: tx, zeroData: next} = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return {result: await tx.putCommit(), zeroData: next};\n}\n\nexport async function rebaseMutationAndCommit(\n mutation: Commit<LocalMeta>,\n dagWrite: DagWrite,\n basis: Hash,\n headName: string,\n mutators: MutatorDefs,\n lc: LogContext,\n // TODO(greg): mutationClientID can be retrieved from mutation if LocalMeta\n // is a LocalMetaDD31. As part of DD31 cleanup we can remove this arg.\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<RebaseResult<Hash>> {\n const {result: dbWrite, zeroData: next} = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return {result: await dbWrite.commit(headName), zeroData: next};\n}\n"],"mappings":";;;;;AAqCA,eAAe,eACb,UACA,UACA,WACA,UACA,IACA,kBACA,eACA,UAC8B;CAC9B,MAAM,YAAY,SAAS;CAC3B,MAAM,OAAO,UAAU;CACvB,IAAI,gBAAgB,SAAS,GAC3B,OACE,UAAU,aAAa,kBACvB,mDACF;CAEF,MAAM,mBAAmB,SAAS;CAClC,IAAI,CAAC,kBASH,GAAG,QAAQ,iCAAiC,MAAM;CAEpD,MAAM,cACJ,qBACC,YAAY,CAEb;CAEF,MAAM,OAAO,UAAU;CAGvB,MAAM,iBAAiB,OAAM,MADH,eAAe,WAAW,QAAQ,GACnB,kBACvC,kBACA,QACF;CACA,IAAI,mBAAmB,UAAU,YAC/B,MAAM,IAAI,MACR,uCAAuC,UAAU,WAAW,UAAU,eAAe,uBAAuB,iBAAiB,gBAAgB,MAC/I;CAGF,IAAI,iBAAiB,GACnB,oBAAoB,SAAS;CAG/B,MAAM,iBACJ,cACE,WACA,MACA,MACA,SAAS,MAAM,MACf,UACA,UAAU,WACV,kBACA,aACF;CAEF,MAAM,UAAU,MAAM,SAAS;CAI/B,MAAM,SAAS,UAAU,KAAK;CAE9B,MAAM,KAAK,IAAI,qBACb,kBACA,MAAM,QAAQ,cAAc,GAC5B,UACA,QACA,SACA,EACF;CAEA,IAAI;EACF,MAAM,YAAY,IAAI,IAAI;CAC5B,SAAS,GAAG;EAmBV,GAAG,OAAO,qBAAqB,KAAK,oCAAoC,CAAC;EACzE,OAAO;GAAC,QAAQ,MAAM,SAAS;GAAG;EAAQ;CAC5C;CAEA,OAAO;EAAC,QAAQ;EAAS,UAAU;CAAM;AAC3C;AAEA,eAAsB,2BACpB,UACA,UACA,OACA,UACA,IAGA,kBACA,eACA,UACqC;CACrC,MAAM,EAAC,QAAQ,IAAI,UAAU,SAAQ,MAAM,eACzC,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,QACF;CACA,OAAO;EAAC,QAAQ,MAAM,GAAG,UAAU;EAAG,UAAU;CAAI;AACtD;AAEA,eAAsB,wBACpB,UACA,UACA,OACA,UACA,UACA,IAGA,kBACA,eACA,UAC6B;CAC7B,MAAM,EAAC,QAAQ,SAAS,UAAU,SAAQ,MAAM,eAC9C,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,QACF;CACA,OAAO;EAAC,QAAQ,MAAM,QAAQ,OAAO,QAAQ;EAAG,UAAU;CAAI;AAChE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.js","names":["#dagWrite","#basis","#meta","#clientID","#formatVersion","#generateDiffs"],"sources":["../../../../../replicache/src/db/write.ts"],"sourcesContent":["import '../process-env.ts';\nimport type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {diff} from '../btree/diff.ts';\nimport type {InternalDiff} from '../btree/node.ts';\nimport {BTreeRead, allEntriesAsDiff} from '../btree/read.ts';\nimport {BTreeWrite} from '../btree/write.ts';\nimport type {FrozenCookie} from '../cookies.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\nimport type {DiffComputationConfig} from '../sync/diff.ts';\nimport {DiffsMap} from '../sync/diff.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport type {Commit} from './commit.ts';\nimport {\n type Meta as CommitMeta,\n type IndexRecord,\n type Meta,\n baseSnapshotHashFromHash,\n commitFromHash,\n newLocalDD31 as commitNewLocalDD31,\n newSnapshotDD31 as commitNewSnapshotDD31,\n getMutationID,\n} from './commit.ts';\nimport * as IndexOperation from './index-operation-enum.ts';\nimport type {IndexRead} from './index.ts';\nimport {IndexWrite, indexValue} from './index.ts';\nimport * as MetaType from './meta-type-enum.ts';\nimport {Read, readIndexesForRead} from './read.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class Write extends Read {\n readonly #dagWrite: DagWrite;\n readonly #basis: Commit<CommitMeta> | undefined;\n readonly #meta: CommitMeta;\n\n declare map: BTreeWrite;\n\n declare readonly indexes: Map<string, IndexWrite>;\n readonly #clientID: ClientID;\n readonly #formatVersion: FormatVersion;\n\n constructor(\n dagWrite: DagWrite,\n map: BTreeWrite,\n basis: Commit<CommitMeta> | undefined,\n meta: CommitMeta,\n indexes: Map<string, IndexWrite>,\n clientID: ClientID,\n formatVersion: FormatVersion,\n ) {\n // TypeScript has trouble\n super(dagWrite, map, indexes);\n this.#dagWrite = dagWrite;\n this.#basis = basis;\n this.#meta = meta;\n this.#clientID = clientID;\n this.#formatVersion = formatVersion;\n\n // TODO(arv): if (DEBUG) { ...\n if (basis === undefined) {\n assert(\n meta.basisHash === emptyHash,\n 'Expected basisHash to be emptyHash when basis is undefined',\n );\n } else {\n assert(\n meta.basisHash === basis.chunk.hash,\n 'Expected meta.basisHash to equal basis.chunk.hash',\n );\n }\n }\n\n /**\n * The value needs to be frozen since it is kept in memory and used later for\n * comparison as well as returned in `get`.\n */\n async put(\n lc: LogContext,\n key: string,\n value: FrozenJSONValue,\n ): Promise<void> {\n if (!process.env.DISABLE_REPLICACHE_INDEXES && this.indexes.size > 0) {\n await updateIndexes(lc, this.map, this.indexes, key, value);\n }\n await this.map.put(key, value);\n }\n\n /**\n * Inserts or updates multiple entries in a single bulk operation. More\n * efficient than calling {@link put} in a loop because it uses\n * {@link BTreeWrite.putMany} to build or merge tree nodes in one pass.\n *\n * `entries` must be sorted in ascending key order.\n */\n async putMany(\n lc: LogContext,\n entries: ReadonlyArray<readonly [string, FrozenJSONValue]>,\n ): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n\n // Update indexes for all entries\n if (!process.env.DISABLE_REPLICACHE_INDEXES && this.indexes.size > 0) {\n // TODO(arv): Indexes can use the optimizePatch pattern too.\n for (const [key, value] of entries) {\n await updateIndexes(lc, this.map, this.indexes, key, value);\n }\n }\n\n // BTreeWrite.putMany handles both empty and non-empty trees optimally\n await this.map.putMany(entries);\n }\n\n getMutationID(): Promise<number> {\n return getMutationID(this.#clientID, this.#dagWrite, this.#meta);\n }\n\n async del(lc: LogContext, key: string): Promise<boolean> {\n if (!process.env.DISABLE_REPLICACHE_INDEXES && this.indexes.size > 0) {\n await updateIndexes(lc, this.map, this.indexes, key, undefined);\n }\n return this.map.del(key);\n }\n\n async clear(): Promise<void> {\n await this.map.clear();\n const ps = [];\n for (const idx of this.indexes.values()) {\n ps.push(idx.clear());\n }\n await Promise.all(ps);\n }\n\n async putCommit(): Promise<Commit<CommitMeta>> {\n const valueHash = await this.map.flush();\n const indexRecords: IndexRecord[] = [];\n\n for (const index of this.indexes.values()) {\n const valueHash = await index.flush();\n const indexRecord: IndexRecord = {\n definition: index.meta.definition,\n valueHash,\n };\n indexRecords.push(indexRecord);\n }\n\n let commit: Commit<Meta>;\n const meta = this.#meta;\n switch (meta.type) {\n case MetaType.LocalDD31: {\n assert(\n this.#formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for LocalDD31 commit',\n );\n const {\n basisHash,\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n timestamp,\n } = meta;\n commit = commitNewLocalDD31(\n this.#dagWrite.createChunk,\n basisHash,\n await baseSnapshotHashFromHash(basisHash, this.#dagWrite),\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n valueHash,\n indexRecords,\n timestamp,\n this.#clientID,\n );\n break;\n }\n\n case MetaType.SnapshotDD31: {\n assert(\n this.#formatVersion > FormatVersion.DD31,\n 'Expected formatVersion > DD31 for SnapshotDD31 commit',\n );\n const {basisHash, lastMutationIDs, cookieJSON} = meta;\n commit = commitNewSnapshotDD31(\n this.#dagWrite.createChunk,\n basisHash,\n lastMutationIDs,\n cookieJSON,\n valueHash,\n indexRecords,\n );\n break;\n }\n }\n await this.#dagWrite.putChunk(commit.chunk);\n return commit;\n }\n\n // Return value is the hash of the new commit.\n async commit(headName: string): Promise<Hash> {\n const commit = await this.putCommit();\n const commitHash = commit.chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return commitHash;\n }\n\n async commitWithDiffs(\n headName: string,\n diffConfig: DiffComputationConfig,\n ): Promise<[Hash, DiffsMap]> {\n const commit = this.putCommit();\n const diffMap = await this.#generateDiffs(diffConfig);\n const commitHash = (await commit).chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return [commitHash, diffMap];\n }\n\n async #generateDiffs(diffConfig: DiffComputationConfig): Promise<DiffsMap> {\n const diffsMap = new DiffsMap();\n if (!diffConfig.shouldComputeDiffs()) {\n return diffsMap;\n }\n\n let valueDiff: InternalDiff = [];\n if (this.#basis) {\n const basisMap = new BTreeRead(\n this.#dagWrite,\n this.#formatVersion,\n this.#basis.valueHash,\n );\n valueDiff = await diff(basisMap, this.map);\n }\n diffsMap.set('', valueDiff);\n if (process.env.DISABLE_REPLICACHE_INDEXES) {\n return diffsMap;\n }\n let basisIndexes: Map<string, IndexRead>;\n if (this.#basis) {\n basisIndexes = readIndexesForRead(\n this.#basis,\n this.#dagWrite,\n this.#formatVersion,\n );\n } else {\n basisIndexes = new Map();\n }\n\n for (const [name, index] of this.indexes) {\n if (!diffConfig.shouldComputeDiffsForIndex(name)) {\n continue;\n }\n const basisIndex = basisIndexes.get(name);\n assert(index !== basisIndex, 'Expected index to differ from basisIndex');\n\n const indexDiffResult = await (basisIndex\n ? diff(basisIndex.map, index.map)\n : // No basis. All keys are new.\n allEntriesAsDiff(index.map, 'add'));\n diffsMap.set(name, indexDiffResult);\n }\n\n // Handle indexes in basisIndex but not in this.indexes. All keys are\n // deleted.\n for (const [name, basisIndex] of basisIndexes) {\n if (\n !this.indexes.has(name) &&\n diffConfig.shouldComputeDiffsForIndex(name)\n ) {\n const indexDiffResult = await allEntriesAsDiff(basisIndex.map, 'del');\n diffsMap.set(name, indexDiffResult);\n }\n }\n return diffsMap;\n }\n\n close(): void {\n this.#dagWrite.release();\n }\n}\n\nexport async function newWriteLocal(\n basisHash: Hash,\n mutatorName: string,\n mutatorArgsJSON: FrozenJSONValue,\n originalHash: Hash | null,\n dagWrite: DagWrite,\n timestamp: number,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n const mutationID = await basis.getNextMutationID(clientID, dagWrite);\n const indexes = readIndexesForWrite(basis, dagWrite, formatVersion);\n assert(\n formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for newWriteLocal',\n );\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n\n {\n type: MetaType.LocalDD31,\n basisHash,\n baseSnapshotHash: await baseSnapshotHashFromHash(basisHash, dagWrite),\n mutatorName,\n mutatorArgsJSON,\n mutationID,\n originalHash,\n timestamp,\n clientID,\n },\n indexes,\n clientID,\n formatVersion,\n );\n}\n\nexport async function newWriteSnapshotDD31(\n basisHash: Hash,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n dagWrite: DagWrite,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n {basisHash, type: MetaType.SnapshotDD31, lastMutationIDs, cookieJSON},\n readIndexesForWrite(basis, dagWrite, formatVersion),\n clientID,\n formatVersion,\n );\n}\n\nexport async function updateIndexes(\n lc: LogContext,\n map: BTreeRead,\n indexes: Map<string, IndexWrite>,\n key: string,\n newVal: FrozenJSONValue | undefined,\n): Promise<void> {\n let oldValPromise: Promise<FrozenJSONValue | undefined> | undefined;\n\n const ps: Promise<void>[] = [];\n for (const idx of indexes.values()) {\n const {keyPrefix} = idx.meta.definition;\n if (!keyPrefix || key.startsWith(keyPrefix)) {\n const oldVal = await (oldValPromise ??= map.get(key));\n if (oldVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Remove,\n key,\n oldVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n if (newVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Add,\n key,\n newVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n }\n }\n await Promise.all(ps);\n}\n\nexport function readIndexesForWrite(\n commit: Commit<CommitMeta>,\n dagWrite: DagWrite,\n formatVersion: FormatVersion,\n): Map<string, IndexWrite> {\n const m: Map<string, IndexWrite> = new Map();\n if (process.env.DISABLE_REPLICACHE_INDEXES) {\n return m;\n }\n for (const index of commit.indexes) {\n m.set(\n index.definition.name,\n new IndexWrite(\n index,\n new BTreeWrite(dagWrite, formatVersion, index.valueHash),\n ),\n );\n }\n return m;\n}\n\nexport async function createIndexBTree(\n lc: LogContext,\n dagWrite: DagWrite,\n valueMap: BTreeRead,\n prefix: string,\n jsonPointer: string,\n allowEmpty: boolean,\n formatVersion: FormatVersion,\n): Promise<BTreeWrite> {\n const indexMap = new BTreeWrite(dagWrite, formatVersion);\n for await (const entry of valueMap.scan(prefix)) {\n const key = entry[0];\n if (!key.startsWith(prefix)) {\n break;\n }\n await indexValue(\n lc,\n indexMap,\n IndexOperation.Add,\n key,\n entry[1],\n jsonPointer,\n allowEmpty,\n );\n }\n return indexMap;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"write.js","names":["#dagWrite","#basis","#meta","#clientID","#formatVersion","#generateDiffs"],"sources":["../../../../../replicache/src/db/write.ts"],"sourcesContent":["import '../process-env.ts';\nimport type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {diff} from '../btree/diff.ts';\nimport type {InternalDiff} from '../btree/node.ts';\nimport {BTreeRead, allEntriesAsDiff} from '../btree/read.ts';\nimport {BTreeWrite} from '../btree/write.ts';\nimport type {FrozenCookie} from '../cookies.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\nimport type {DiffComputationConfig} from '../sync/diff.ts';\nimport {DiffsMap} from '../sync/diff.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport type {Commit} from './commit.ts';\nimport {\n type Meta as CommitMeta,\n type IndexRecord,\n type Meta,\n baseSnapshotHashFromHash,\n commitFromHash,\n newLocalDD31 as commitNewLocalDD31,\n newSnapshotDD31 as commitNewSnapshotDD31,\n getMutationID,\n} from './commit.ts';\nimport * as IndexOperation from './index-operation-enum.ts';\nimport type {IndexRead} from './index.ts';\nimport {IndexWrite, indexValue} from './index.ts';\nimport * as MetaType from './meta-type-enum.ts';\nimport {Read, readIndexesForRead} from './read.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class Write extends Read {\n readonly #dagWrite: DagWrite;\n readonly #basis: Commit<CommitMeta> | undefined;\n readonly #meta: CommitMeta;\n\n declare map: BTreeWrite;\n\n declare readonly indexes: Map<string, IndexWrite>;\n readonly #clientID: ClientID;\n readonly #formatVersion: FormatVersion;\n\n constructor(\n dagWrite: DagWrite,\n map: BTreeWrite,\n basis: Commit<CommitMeta> | undefined,\n meta: CommitMeta,\n indexes: Map<string, IndexWrite>,\n clientID: ClientID,\n formatVersion: FormatVersion,\n ) {\n // TypeScript has trouble\n super(dagWrite, map, indexes);\n this.#dagWrite = dagWrite;\n this.#basis = basis;\n this.#meta = meta;\n this.#clientID = clientID;\n this.#formatVersion = formatVersion;\n\n // TODO(arv): if (DEBUG) { ...\n if (basis === undefined) {\n assert(\n meta.basisHash === emptyHash,\n 'Expected basisHash to be emptyHash when basis is undefined',\n );\n } else {\n assert(\n meta.basisHash === basis.chunk.hash,\n 'Expected meta.basisHash to equal basis.chunk.hash',\n );\n }\n }\n\n /**\n * The value needs to be frozen since it is kept in memory and used later for\n * comparison as well as returned in `get`.\n */\n async put(\n lc: LogContext,\n key: string,\n value: FrozenJSONValue,\n ): Promise<void> {\n if (!process.env.DISABLE_REPLICACHE_INDEXES && this.indexes.size > 0) {\n await updateIndexes(lc, this.map, this.indexes, key, value);\n }\n await this.map.put(key, value);\n }\n\n /**\n * Inserts or updates multiple entries in a single bulk operation. More\n * efficient than calling {@link put} in a loop because it uses\n * {@link BTreeWrite.putMany} to build or merge tree nodes in one pass.\n *\n * `entries` must be sorted in ascending key order.\n */\n async putMany(\n lc: LogContext,\n entries: ReadonlyArray<readonly [string, FrozenJSONValue]>,\n ): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n\n // Update indexes for all entries\n if (!process.env.DISABLE_REPLICACHE_INDEXES && this.indexes.size > 0) {\n // TODO(arv): Indexes can use the optimizePatch pattern too.\n for (const [key, value] of entries) {\n await updateIndexes(lc, this.map, this.indexes, key, value);\n }\n }\n\n // BTreeWrite.putMany handles both empty and non-empty trees optimally\n await this.map.putMany(entries);\n }\n\n getMutationID(): Promise<number> {\n return getMutationID(this.#clientID, this.#dagWrite, this.#meta);\n }\n\n async del(lc: LogContext, key: string): Promise<boolean> {\n if (!process.env.DISABLE_REPLICACHE_INDEXES && this.indexes.size > 0) {\n await updateIndexes(lc, this.map, this.indexes, key, undefined);\n }\n return this.map.del(key);\n }\n\n async clear(): Promise<void> {\n await this.map.clear();\n const ps = [];\n for (const idx of this.indexes.values()) {\n ps.push(idx.clear());\n }\n await Promise.all(ps);\n }\n\n async putCommit(): Promise<Commit<CommitMeta>> {\n const valueHash = await this.map.flush();\n const indexRecords: IndexRecord[] = [];\n\n for (const index of this.indexes.values()) {\n const valueHash = await index.flush();\n const indexRecord: IndexRecord = {\n definition: index.meta.definition,\n valueHash,\n };\n indexRecords.push(indexRecord);\n }\n\n let commit: Commit<Meta>;\n const meta = this.#meta;\n switch (meta.type) {\n case MetaType.LocalDD31: {\n assert(\n this.#formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for LocalDD31 commit',\n );\n const {\n basisHash,\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n timestamp,\n } = meta;\n commit = commitNewLocalDD31(\n this.#dagWrite.createChunk,\n basisHash,\n await baseSnapshotHashFromHash(basisHash, this.#dagWrite),\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n valueHash,\n indexRecords,\n timestamp,\n this.#clientID,\n );\n break;\n }\n\n case MetaType.SnapshotDD31: {\n assert(\n this.#formatVersion > FormatVersion.DD31,\n 'Expected formatVersion > DD31 for SnapshotDD31 commit',\n );\n const {basisHash, lastMutationIDs, cookieJSON} = meta;\n commit = commitNewSnapshotDD31(\n this.#dagWrite.createChunk,\n basisHash,\n lastMutationIDs,\n cookieJSON,\n valueHash,\n indexRecords,\n );\n break;\n }\n }\n await this.#dagWrite.putChunk(commit.chunk);\n return commit;\n }\n\n // Return value is the hash of the new commit.\n async commit(headName: string): Promise<Hash> {\n const commit = await this.putCommit();\n const commitHash = commit.chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return commitHash;\n }\n\n async commitWithDiffs(\n headName: string,\n diffConfig: DiffComputationConfig,\n ): Promise<[Hash, DiffsMap]> {\n const commit = this.putCommit();\n const diffMap = await this.#generateDiffs(diffConfig);\n const commitHash = (await commit).chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return [commitHash, diffMap];\n }\n\n async #generateDiffs(diffConfig: DiffComputationConfig): Promise<DiffsMap> {\n const diffsMap = new DiffsMap();\n if (!diffConfig.shouldComputeDiffs()) {\n return diffsMap;\n }\n\n let valueDiff: InternalDiff = [];\n if (this.#basis) {\n const basisMap = new BTreeRead(\n this.#dagWrite,\n this.#formatVersion,\n this.#basis.valueHash,\n );\n valueDiff = await diff(basisMap, this.map);\n }\n diffsMap.set('', valueDiff);\n if (process.env.DISABLE_REPLICACHE_INDEXES) {\n return diffsMap;\n }\n let basisIndexes: Map<string, IndexRead>;\n if (this.#basis) {\n basisIndexes = readIndexesForRead(\n this.#basis,\n this.#dagWrite,\n this.#formatVersion,\n );\n } else {\n basisIndexes = new Map();\n }\n\n for (const [name, index] of this.indexes) {\n if (!diffConfig.shouldComputeDiffsForIndex(name)) {\n continue;\n }\n const basisIndex = basisIndexes.get(name);\n assert(index !== basisIndex, 'Expected index to differ from basisIndex');\n\n const indexDiffResult = await (basisIndex\n ? diff(basisIndex.map, index.map)\n : // No basis. All keys are new.\n allEntriesAsDiff(index.map, 'add'));\n diffsMap.set(name, indexDiffResult);\n }\n\n // Handle indexes in basisIndex but not in this.indexes. All keys are\n // deleted.\n for (const [name, basisIndex] of basisIndexes) {\n if (\n !this.indexes.has(name) &&\n diffConfig.shouldComputeDiffsForIndex(name)\n ) {\n const indexDiffResult = await allEntriesAsDiff(basisIndex.map, 'del');\n diffsMap.set(name, indexDiffResult);\n }\n }\n return diffsMap;\n }\n\n close(): void {\n this.#dagWrite.release();\n }\n}\n\nexport async function newWriteLocal(\n basisHash: Hash,\n mutatorName: string,\n mutatorArgsJSON: FrozenJSONValue,\n originalHash: Hash | null,\n dagWrite: DagWrite,\n timestamp: number,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n const mutationID = await basis.getNextMutationID(clientID, dagWrite);\n const indexes = readIndexesForWrite(basis, dagWrite, formatVersion);\n assert(\n formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for newWriteLocal',\n );\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n\n {\n type: MetaType.LocalDD31,\n basisHash,\n baseSnapshotHash: await baseSnapshotHashFromHash(basisHash, dagWrite),\n mutatorName,\n mutatorArgsJSON,\n mutationID,\n originalHash,\n timestamp,\n clientID,\n },\n indexes,\n clientID,\n formatVersion,\n );\n}\n\nexport async function newWriteSnapshotDD31(\n basisHash: Hash,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n dagWrite: DagWrite,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n {basisHash, type: MetaType.SnapshotDD31, lastMutationIDs, cookieJSON},\n readIndexesForWrite(basis, dagWrite, formatVersion),\n clientID,\n formatVersion,\n );\n}\n\nexport async function updateIndexes(\n lc: LogContext,\n map: BTreeRead,\n indexes: Map<string, IndexWrite>,\n key: string,\n newVal: FrozenJSONValue | undefined,\n): Promise<void> {\n let oldValPromise: Promise<FrozenJSONValue | undefined> | undefined;\n\n const ps: Promise<void>[] = [];\n for (const idx of indexes.values()) {\n const {keyPrefix} = idx.meta.definition;\n if (!keyPrefix || key.startsWith(keyPrefix)) {\n const oldVal = await (oldValPromise ??= map.get(key));\n if (oldVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Remove,\n key,\n oldVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n if (newVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Add,\n key,\n newVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n }\n }\n await Promise.all(ps);\n}\n\nexport function readIndexesForWrite(\n commit: Commit<CommitMeta>,\n dagWrite: DagWrite,\n formatVersion: FormatVersion,\n): Map<string, IndexWrite> {\n const m: Map<string, IndexWrite> = new Map();\n if (process.env.DISABLE_REPLICACHE_INDEXES) {\n return m;\n }\n for (const index of commit.indexes) {\n m.set(\n index.definition.name,\n new IndexWrite(\n index,\n new BTreeWrite(dagWrite, formatVersion, index.valueHash),\n ),\n );\n }\n return m;\n}\n\nexport async function createIndexBTree(\n lc: LogContext,\n dagWrite: DagWrite,\n valueMap: BTreeRead,\n prefix: string,\n jsonPointer: string,\n allowEmpty: boolean,\n formatVersion: FormatVersion,\n): Promise<BTreeWrite> {\n const indexMap = new BTreeWrite(dagWrite, formatVersion);\n for await (const entry of valueMap.scan(prefix, false)) {\n const key = entry[0];\n if (!key.startsWith(prefix)) {\n break;\n }\n await indexValue(\n lc,\n indexMap,\n IndexOperation.Add,\n key,\n entry[1],\n jsonPointer,\n allowEmpty,\n );\n }\n return indexMap;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../../../replicache/src/kv/expo-sqlite/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../../../replicache/src/kv/expo-sqlite/store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAGV,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,CAAC;AAExD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,sBAAsB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,CAAC,EAAE,sBAAsB,GAC5B,aAAa,CAMf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SQLiteStore, dropStore } from "../sqlite-store.js";
|
|
2
|
+
import { Lock } from "@rocicorp/lock";
|
|
2
3
|
import { deleteDatabaseSync, openDatabaseSync } from "expo-sqlite";
|
|
3
4
|
//#region ../replicache/src/kv/expo-sqlite/store.ts
|
|
4
5
|
function dropExpoSQLiteStore(name, opts) {
|
|
@@ -15,16 +16,38 @@ function expoSQLiteStoreProvider(opts) {
|
|
|
15
16
|
drop: (name) => dropExpoSQLiteStore(name, opts)
|
|
16
17
|
};
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* An expo-sqlite prepared statement is a stateful cursor over a single
|
|
21
|
+
* `sqlite3_stmt`: `executeForRawResultAsync` resets, rebinds and steps the
|
|
22
|
+
* first row, and the returned result's `getAllAsync` then steps the *remaining*
|
|
23
|
+
* rows of whatever the statement is currently bound to. Both are separate
|
|
24
|
+
* native round trips.
|
|
25
|
+
*
|
|
26
|
+
* `SQLiteStore` shares one prepared statement per SQL across all concurrent
|
|
27
|
+
* readers, so without serialization reader B's `executeForRawResultAsync` can
|
|
28
|
+
* rebind the statement inside reader A's await gap, and A's `getAllAsync` then
|
|
29
|
+
* returns B's rows (and B is starved of them). That yields wrong or missing
|
|
30
|
+
* values and, when a ref count delta is computed from such a read and written,
|
|
31
|
+
* a persistently corrupted store.
|
|
32
|
+
*
|
|
33
|
+
* Every call on a statement is therefore run under a per-statement lock so the
|
|
34
|
+
* execute + fetch pair is atomic with respect to other users of that statement.
|
|
35
|
+
*/
|
|
18
36
|
var ExpoSQLitePreparedStatement = class {
|
|
19
37
|
#statement;
|
|
38
|
+
#lock = new Lock();
|
|
20
39
|
constructor(statement) {
|
|
21
40
|
this.#statement = statement;
|
|
22
41
|
}
|
|
23
|
-
|
|
24
|
-
|
|
42
|
+
exec(params) {
|
|
43
|
+
return this.#lock.withLock(async () => {
|
|
44
|
+
await this.#statement.executeForRawResultAsync(params);
|
|
45
|
+
});
|
|
25
46
|
}
|
|
26
|
-
|
|
27
|
-
return
|
|
47
|
+
all(params) {
|
|
48
|
+
return this.#lock.withLock(async () => {
|
|
49
|
+
return await (await this.#statement.executeForRawResultAsync(params)).getAllAsync();
|
|
50
|
+
});
|
|
28
51
|
}
|
|
29
52
|
};
|
|
30
53
|
var ExpoSQLiteDatabase = class {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","names":["#statement","#db","#filename","#statements"],"sources":["../../../../../../replicache/src/kv/expo-sqlite/store.ts"],"sourcesContent":["import {\n deleteDatabaseSync,\n openDatabaseSync,\n type SQLiteDatabase as DB,\n type SQLiteStatement,\n} from 'expo-sqlite';\nimport type {\n PreparedStatement,\n SQLiteDatabase,\n SQLiteStoreOptions,\n} from '../sqlite-store.ts';\nimport {dropStore, SQLiteStore} from '../sqlite-store.ts';\nimport type {StoreProvider} from '../store.ts';\n\nexport type ExpoSQLiteStoreOptions = SQLiteStoreOptions;\n\nexport function dropExpoSQLiteStore(\n name: string,\n opts?: ExpoSQLiteStoreOptions,\n): Promise<void> {\n return dropStore(name, filename => new ExpoSQLiteDatabase(filename), opts);\n}\n\n/**\n * Creates a StoreProvider for SQLite-based stores using expo-sqlite.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n */\nexport function expoSQLiteStoreProvider(\n opts?: ExpoSQLiteStoreOptions,\n): StoreProvider {\n return {\n create: name =>\n new SQLiteStore(name, name => new ExpoSQLiteDatabase(name), opts),\n drop: name => dropExpoSQLiteStore(name, opts),\n };\n}\n\nclass ExpoSQLitePreparedStatement implements PreparedStatement {\n readonly #statement: SQLiteStatement;\n\n constructor(statement: SQLiteStatement) {\n this.#statement = statement;\n }\n\n
|
|
1
|
+
{"version":3,"file":"store.js","names":["#statement","#lock","#db","#filename","#statements"],"sources":["../../../../../../replicache/src/kv/expo-sqlite/store.ts"],"sourcesContent":["import {Lock} from '@rocicorp/lock';\nimport {\n deleteDatabaseSync,\n openDatabaseSync,\n type SQLiteDatabase as DB,\n type SQLiteStatement,\n} from 'expo-sqlite';\nimport type {\n PreparedStatement,\n SQLiteDatabase,\n SQLiteStoreOptions,\n} from '../sqlite-store.ts';\nimport {dropStore, SQLiteStore} from '../sqlite-store.ts';\nimport type {StoreProvider} from '../store.ts';\n\nexport type ExpoSQLiteStoreOptions = SQLiteStoreOptions;\n\nexport function dropExpoSQLiteStore(\n name: string,\n opts?: ExpoSQLiteStoreOptions,\n): Promise<void> {\n return dropStore(name, filename => new ExpoSQLiteDatabase(filename), opts);\n}\n\n/**\n * Creates a StoreProvider for SQLite-based stores using expo-sqlite.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n */\nexport function expoSQLiteStoreProvider(\n opts?: ExpoSQLiteStoreOptions,\n): StoreProvider {\n return {\n create: name =>\n new SQLiteStore(name, name => new ExpoSQLiteDatabase(name), opts),\n drop: name => dropExpoSQLiteStore(name, opts),\n };\n}\n\n/**\n * An expo-sqlite prepared statement is a stateful cursor over a single\n * `sqlite3_stmt`: `executeForRawResultAsync` resets, rebinds and steps the\n * first row, and the returned result's `getAllAsync` then steps the *remaining*\n * rows of whatever the statement is currently bound to. Both are separate\n * native round trips.\n *\n * `SQLiteStore` shares one prepared statement per SQL across all concurrent\n * readers, so without serialization reader B's `executeForRawResultAsync` can\n * rebind the statement inside reader A's await gap, and A's `getAllAsync` then\n * returns B's rows (and B is starved of them). That yields wrong or missing\n * values and, when a ref count delta is computed from such a read and written,\n * a persistently corrupted store.\n *\n * Every call on a statement is therefore run under a per-statement lock so the\n * execute + fetch pair is atomic with respect to other users of that statement.\n */\nclass ExpoSQLitePreparedStatement implements PreparedStatement {\n readonly #statement: SQLiteStatement;\n readonly #lock = new Lock();\n\n constructor(statement: SQLiteStatement) {\n this.#statement = statement;\n }\n\n exec(params: string[]): Promise<void> {\n return this.#lock.withLock(async () => {\n await this.#statement.executeForRawResultAsync(params);\n });\n }\n\n all(params: string[]): Promise<unknown[][]> {\n return this.#lock.withLock(async () => {\n const result = await this.#statement.executeForRawResultAsync(params);\n // Must be awaited inside the lock so that no other caller can reset the\n // statement before all rows have been stepped.\n return (await result.getAllAsync()) as unknown[][];\n });\n }\n}\n\nclass ExpoSQLiteDatabase implements SQLiteDatabase {\n readonly #db: DB;\n readonly #filename: string;\n readonly #statements: Set<SQLiteStatement> = new Set();\n\n constructor(filename: string) {\n this.#filename = filename;\n this.#db = openDatabaseSync(filename);\n }\n\n close(): void {\n for (const stmt of this.#statements) {\n stmt.finalizeSync();\n }\n this.#db.closeSync();\n }\n\n destroy(): void {\n deleteDatabaseSync(this.#filename);\n }\n\n prepare(sql: string): PreparedStatement {\n const statement = this.#db.prepareSync(sql);\n this.#statements.add(statement);\n return new ExpoSQLitePreparedStatement(statement);\n }\n\n execSync(sql: string): void {\n this.#db.execSync(sql);\n }\n}\n"],"mappings":";;;;AAiBA,SAAgB,oBACd,MACA,MACe;CACf,OAAO,UAAU,OAAM,aAAY,IAAI,mBAAmB,QAAQ,GAAG,IAAI;AAC3E;;;;;;AAOA,SAAgB,wBACd,MACe;CACf,OAAO;EACL,SAAQ,SACN,IAAI,YAAY,OAAM,SAAQ,IAAI,mBAAmB,IAAI,GAAG,IAAI;EAClE,OAAM,SAAQ,oBAAoB,MAAM,IAAI;CAC9C;AACF;;;;;;;;;;;;;;;;;;AAmBA,IAAM,8BAAN,MAA+D;CAC7D;CACA,QAAiB,IAAI,KAAK;CAE1B,YAAY,WAA4B;EACtC,KAAKA,aAAa;CACpB;CAEA,KAAK,QAAiC;EACpC,OAAO,KAAKC,MAAM,SAAS,YAAY;GACrC,MAAM,KAAKD,WAAW,yBAAyB,MAAM;EACvD,CAAC;CACH;CAEA,IAAI,QAAwC;EAC1C,OAAO,KAAKC,MAAM,SAAS,YAAY;GAIrC,OAAQ,OAAM,MAHO,KAAKD,WAAW,yBAAyB,MAAM,GAG/C,YAAY;EACnC,CAAC;CACH;AACF;AAEA,IAAM,qBAAN,MAAmD;CACjD;CACA;CACA,8BAA6C,IAAI,IAAI;CAErD,YAAY,UAAkB;EAC5B,KAAKG,YAAY;EACjB,KAAKD,MAAM,iBAAiB,QAAQ;CACtC;CAEA,QAAc;EACZ,KAAK,MAAM,QAAQ,KAAKE,aACtB,KAAK,aAAa;EAEpB,KAAKF,IAAI,UAAU;CACrB;CAEA,UAAgB;EACd,mBAAmB,KAAKC,SAAS;CACnC;CAEA,QAAQ,KAAgC;EACtC,MAAM,YAAY,KAAKD,IAAI,YAAY,GAAG;EAC1C,KAAKE,YAAY,IAAI,SAAS;EAC9B,OAAO,IAAI,4BAA4B,SAAS;CAClD;CAEA,SAAS,KAAmB;EAC1B,KAAKF,IAAI,SAAS,GAAG;CACvB;AACF"}
|
|
@@ -3,6 +3,13 @@ import type { Read, Store, Write } from './store.ts';
|
|
|
3
3
|
import { WriteImplBase } from './write-impl-base.ts';
|
|
4
4
|
/**
|
|
5
5
|
* A SQLite prepared statement.
|
|
6
|
+
*
|
|
7
|
+
* `SQLiteStore` prepares one statement per SQL and shares it across all
|
|
8
|
+
* concurrent readers, so implementations must make each call atomic with
|
|
9
|
+
* respect to other callers of the same statement: `all()` must not let another
|
|
10
|
+
* `exec()`/`all()` rebind or reset the underlying statement between executing
|
|
11
|
+
* it and fetching its rows. Delegates whose native API splits execute and fetch
|
|
12
|
+
* into separate round trips (e.g. expo-sqlite) must serialize per statement.
|
|
6
13
|
*/
|
|
7
14
|
export interface PreparedStatement {
|
|
8
15
|
exec(params: string[]): Promise<void>;
|
|
@@ -47,6 +54,10 @@ export type PreparedStatements = {
|
|
|
47
54
|
getMany: PreparedStatement;
|
|
48
55
|
put: PreparedStatement;
|
|
49
56
|
del: PreparedStatement;
|
|
57
|
+
/** Multi-row INSERT with the values bound as parameters, n rows wide. */
|
|
58
|
+
putN: (n: number) => PreparedStatement;
|
|
59
|
+
/** Multi-key DELETE with the keys bound as parameters, n keys wide. */
|
|
60
|
+
delN: (n: number) => PreparedStatement;
|
|
50
61
|
};
|
|
51
62
|
export interface SQLiteStoreOptions {
|
|
52
63
|
busyTimeout?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { deepFreeze } from "../frozen-json.js";
|
|
2
2
|
import { maybeTransactionIsClosedRejection, throwIfStoreClosed, transactionError } from "./throw-if-closed.js";
|
|
3
3
|
import { WriteImplBase, deleteSentinel } from "./write-impl-base.js";
|
|
4
|
+
import { getOrInsertComputed } from "../../../shared/src/map.js";
|
|
4
5
|
import { RWLock } from "@rocicorp/lock";
|
|
5
6
|
//#region ../replicache/src/kv/sqlite-store.ts
|
|
6
7
|
/**
|
|
@@ -58,6 +59,39 @@ function resolveFilename(name, opts) {
|
|
|
58
59
|
return dir ? `${dir}/${safe}` : safe;
|
|
59
60
|
}
|
|
60
61
|
/**
|
|
62
|
+
* Widest batch we bind in one statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is
|
|
63
|
+
* 32766, and a put costs two parameters per row, so this is far below the cap;
|
|
64
|
+
* it exists to bound how many distinct statements we prepare and cache.
|
|
65
|
+
*/
|
|
66
|
+
var MAX_BATCH = 128;
|
|
67
|
+
/** `repeatList('?', 3)` -> `'?,?,?'`. */
|
|
68
|
+
function repeatList(item, n) {
|
|
69
|
+
return `${item},`.repeat(n).slice(0, -1);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Prepares (and caches) a statement of each width on demand. Callers only ever
|
|
73
|
+
* ask for powers of two, so the cache holds at most log2(MAX_BATCH)+1 entries
|
|
74
|
+
* however many rows a commit turns out to have.
|
|
75
|
+
*/
|
|
76
|
+
function batchStatements(delegate, sqlFor) {
|
|
77
|
+
const cache = /* @__PURE__ */ new Map();
|
|
78
|
+
return (n) => getOrInsertComputed(cache, n, () => delegate.prepare(sqlFor(n)));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Runs `items` through `getStatement` in power-of-two sized batches, so any
|
|
82
|
+
* length is covered by a handful of cached statement widths.
|
|
83
|
+
*/
|
|
84
|
+
async function execInBatches(items, getStatement, toParams) {
|
|
85
|
+
for (let i = 0; i < items.length;) {
|
|
86
|
+
const remaining = Math.min(MAX_BATCH, items.length - i);
|
|
87
|
+
const n = 1 << 31 - Math.clz32(remaining);
|
|
88
|
+
const params = [];
|
|
89
|
+
for (let j = 0; j < n; j++) toParams(items[i + j], params);
|
|
90
|
+
await getStatement(n).exec(params);
|
|
91
|
+
i += n;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
61
95
|
* Common database setup logic shared between expo-sqlite and op-sqlite implementations.
|
|
62
96
|
* Configures SQLite pragmas, creates the entry table, and prepares common statements.
|
|
63
97
|
*/
|
|
@@ -79,7 +113,9 @@ function setupDatabase(delegate, opts) {
|
|
|
79
113
|
getMany: delegate.prepare(`SELECT key, value FROM entry WHERE key IN (SELECT value FROM json_each(?))`),
|
|
80
114
|
put: delegate.prepare(`INSERT OR REPLACE INTO entry (key, value)
|
|
81
115
|
SELECT e.value->>0, e.value->1 FROM json_each(?) e`),
|
|
82
|
-
del: delegate.prepare(`DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`)
|
|
116
|
+
del: delegate.prepare(`DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`),
|
|
117
|
+
putN: batchStatements(delegate, (n) => `INSERT OR REPLACE INTO entry (key, value) VALUES ${repeatList("(?,?)", n)}`),
|
|
118
|
+
delN: batchStatements(delegate, (n) => `DELETE FROM entry WHERE key IN (${repeatList("?", n)})`)
|
|
83
119
|
};
|
|
84
120
|
}
|
|
85
121
|
var CB_STRIDE = 2;
|
|
@@ -209,8 +245,12 @@ var SQLiteWrite = class extends WriteImplBase {
|
|
|
209
245
|
deleteKeys.push(entry[0]);
|
|
210
246
|
this._pending.delete(entry[0]);
|
|
211
247
|
}
|
|
212
|
-
const
|
|
213
|
-
|
|
248
|
+
const putP = this._pending.size > 0 ? execInBatches([...this._pending], this.#preparedStatements.putN, ([key, value], out) => {
|
|
249
|
+
out.push(key, JSON.stringify(value));
|
|
250
|
+
}) : void 0;
|
|
251
|
+
const delP = deleteKeys.length > 0 ? execInBatches(deleteKeys, this.#preparedStatements.delN, (key, out) => {
|
|
252
|
+
out.push(key);
|
|
253
|
+
}) : void 0;
|
|
214
254
|
if (putP) await putP;
|
|
215
255
|
if (delP) await delP;
|
|
216
256
|
this.#dbDelegate.execSync("COMMIT");
|