@rocicorp/zero 1.6.0-canary.1 → 1.6.0-canary.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -3
- package/out/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/usingCtx.js +1 -1
- package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js +13 -0
- package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js.map +1 -0
- package/out/_virtual/_rolldown/runtime.js +12 -1
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-analyze.js +6 -1
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/ast-to-zql/src/bin.js.map +1 -1
- package/out/ast-to-zql/src/format.js.map +1 -1
- package/out/datadog/src/datadog-log-sink.js.map +1 -1
- package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js +12 -0
- package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js.map +1 -0
- package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js +11 -0
- package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js.map +1 -0
- package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js +130 -0
- package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js +62 -0
- package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js +353 -0
- package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js +60 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js.map +1 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js +81 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js.map +1 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js +35 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js +167 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js.map +1 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js +288 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js.map +1 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js +177 -0
- package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js.map +1 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js +46 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js +16 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js.map +1 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js +165 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js.map +1 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js +81 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js.map +1 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js +167 -0
- package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js +19 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js +508 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js +104 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js +160 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js +97 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js +131 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js +39 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js +89 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js +13 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js +46 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js +71 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js +226 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js +11 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js +117 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js +151 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js +76 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js +73 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js +35 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js.map +1 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js +118 -0
- package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js.map +1 -0
- package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js +147 -0
- package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js.map +1 -0
- package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js +21 -0
- package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js.map +1 -0
- package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js +84 -0
- package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js.map +1 -0
- package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js +28 -0
- package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js.map +1 -0
- package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js +65 -0
- package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js.map +1 -0
- package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js +107 -0
- package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js.map +1 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js +696 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js +44 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js.map +1 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js +1585 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js.map +1 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js +329 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js.map +1 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js +13 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js.map +1 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js +13 -0
- package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js.map +1 -0
- package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js +131 -0
- package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js.map +1 -0
- package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js +96 -0
- package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js.map +1 -0
- package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js +95 -0
- package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js.map +1 -0
- package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js +18 -0
- package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js.map +1 -0
- package/out/otel/src/enabled.js.map +1 -1
- package/out/otel/src/log-options.js.map +1 -1
- package/out/otel/src/maybe-time.js.map +1 -1
- package/out/otel/src/span.js.map +1 -1
- package/out/replicache/src/async-iterable-to-array.js.map +1 -1
- package/out/replicache/src/bg-interval.js.map +1 -1
- package/out/replicache/src/btree/diff.js.map +1 -1
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/btree/read.js.map +1 -1
- package/out/replicache/src/btree/splice.js.map +1 -1
- package/out/replicache/src/btree/write.js +6 -3
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/replicache/src/call-default-fetch.js.map +1 -1
- package/out/replicache/src/connection-loop-delegates.js.map +1 -1
- package/out/replicache/src/connection-loop.js.map +1 -1
- package/out/replicache/src/cookies.js.map +1 -1
- package/out/replicache/src/dag/chunk.js.map +1 -1
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/key.js.map +1 -1
- package/out/replicache/src/dag/lazy-store.js.map +1 -1
- package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
- package/out/replicache/src/dag/store-impl.js +8 -3
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/dag/store.js.map +1 -1
- package/out/replicache/src/dag/visitor.js.map +1 -1
- package/out/replicache/src/db/commit.js.map +1 -1
- package/out/replicache/src/db/index.js.map +1 -1
- package/out/replicache/src/db/read.js.map +1 -1
- package/out/replicache/src/db/rebase.js.map +1 -1
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/deleted-clients.js.map +1 -1
- package/out/replicache/src/error-responses.js.map +1 -1
- package/out/replicache/src/frozen-json.js.map +1 -1
- package/out/replicache/src/get-default-puller.js.map +1 -1
- package/out/replicache/src/get-default-pusher.js.map +1 -1
- package/out/replicache/src/get-kv-store-provider.js.map +1 -1
- package/out/replicache/src/hash.js.map +1 -1
- package/out/replicache/src/http-request-info.js.map +1 -1
- package/out/replicache/src/index-defs.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +6 -7
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js +6 -6
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/read-impl.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +6 -6
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +107 -22
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/throw-if-closed.d.ts +1 -0
- package/out/replicache/src/kv/throw-if-closed.d.ts.map +1 -1
- package/out/replicache/src/kv/throw-if-closed.js +1 -4
- package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
- package/out/replicache/src/kv/write-impl-base.js.map +1 -1
- package/out/replicache/src/kv/write-impl.js.map +1 -1
- package/out/replicache/src/lazy.js.map +1 -1
- package/out/replicache/src/log-options.js.map +1 -1
- package/out/replicache/src/make-idb-name.js.map +1 -1
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/on-persist-channel.js.map +1 -1
- package/out/replicache/src/patch-operation.js.map +1 -1
- package/out/replicache/src/pending-mutations.js.map +1 -1
- package/out/replicache/src/persist/client-gc.js.map +1 -1
- package/out/replicache/src/persist/client-group-gc.js.map +1 -1
- package/out/replicache/src/persist/client-groups.js +40 -0
- package/out/replicache/src/persist/client-groups.js.map +1 -1
- package/out/replicache/src/persist/clients.js +28 -0
- package/out/replicache/src/persist/clients.js.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
- package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
- package/out/replicache/src/persist/heartbeat.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
- package/out/replicache/src/persist/make-client-id.js.map +1 -1
- package/out/replicache/src/persist/persist.d.ts.map +1 -1
- package/out/replicache/src/persist/persist.js +4 -2
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/pusher.js.map +1 -1
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/report-error.js.map +1 -1
- package/out/replicache/src/request-idle.js.map +1 -1
- package/out/replicache/src/scan-iterator.js.map +1 -1
- package/out/replicache/src/scan-options.js.map +1 -1
- package/out/replicache/src/set-interval-with-signal.js.map +1 -1
- package/out/replicache/src/subscriptions.js.map +1 -1
- package/out/replicache/src/sync/diff.js.map +1 -1
- package/out/replicache/src/sync/ids.js.map +1 -1
- package/out/replicache/src/sync/patch.js.map +1 -1
- package/out/replicache/src/sync/pull-error.js.map +1 -1
- package/out/replicache/src/sync/pull.d.ts.map +1 -1
- package/out/replicache/src/sync/pull.js +9 -6
- package/out/replicache/src/sync/pull.js.map +1 -1
- package/out/replicache/src/sync/push.js.map +1 -1
- package/out/replicache/src/sync/request-id.js.map +1 -1
- package/out/replicache/src/to-error.js.map +1 -1
- package/out/replicache/src/transaction-closed-error.js.map +1 -1
- package/out/replicache/src/transactions.js.map +1 -1
- package/out/replicache/src/with-transactions.js.map +1 -1
- package/out/shared/src/abort-error.js.map +1 -1
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/asserts.js.map +1 -1
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/binary-search.js.map +1 -1
- package/out/shared/src/broadcast-channel.js.map +1 -1
- package/out/shared/src/browser-env.js.map +1 -1
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/shared/src/cache.js.map +1 -1
- package/out/shared/src/centroid.js.map +1 -1
- package/out/shared/src/custom-key-map.js.map +1 -1
- package/out/shared/src/custom-key-set.js.map +1 -1
- package/out/shared/src/deep-clone.js.map +1 -1
- package/out/shared/src/deep-merge.js.map +1 -1
- package/out/shared/src/document-visible.js.map +1 -1
- package/out/shared/src/dotenv.js.map +1 -1
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/json-schema.js.map +1 -1
- package/out/shared/src/json.js.map +1 -1
- package/out/shared/src/logging-test-utils.js.map +1 -1
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/map.d.ts +6 -0
- package/out/shared/src/map.d.ts.map +1 -0
- package/out/shared/src/map.js +39 -0
- package/out/shared/src/map.js.map +1 -0
- package/out/shared/src/must.js.map +1 -1
- package/out/shared/src/object-traversal.js.map +1 -1
- package/out/shared/src/objects.js.map +1 -1
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/parse-big-int.js.map +1 -1
- package/out/shared/src/promise-race.js.map +1 -1
- package/out/shared/src/queue.d.ts.map +1 -1
- package/out/shared/src/queue.js +15 -21
- package/out/shared/src/queue.js.map +1 -1
- package/out/shared/src/rand.js.map +1 -1
- package/out/shared/src/random-uint64.js.map +1 -1
- package/out/shared/src/random-values.js.map +1 -1
- package/out/shared/src/record-proxy.js.map +1 -1
- package/out/shared/src/resolved-promises.js.map +1 -1
- package/out/shared/src/ring-buffer.d.ts +32 -0
- package/out/shared/src/ring-buffer.d.ts.map +1 -0
- package/out/shared/src/ring-buffer.js +109 -0
- package/out/shared/src/ring-buffer.js.map +1 -0
- package/out/shared/src/sentinels.js.map +1 -1
- package/out/shared/src/set-utils.js.map +1 -1
- package/out/shared/src/size-of-value.js.map +1 -1
- package/out/shared/src/sleep.js.map +1 -1
- package/out/shared/src/sorted-entries.js.map +1 -1
- package/out/shared/src/string-compare.js.map +1 -1
- package/out/shared/src/subscribable.js.map +1 -1
- package/out/shared/src/tdigest-schema.js.map +1 -1
- package/out/shared/src/tdigest.js.map +1 -1
- package/out/shared/src/valita.js.map +1 -1
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +23 -23
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/build-schema.js.map +1 -1
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero/src/zero-out.js.map +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/jwt.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
- package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/network.js.map +1 -1
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/server-context.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +5 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/create.js.map +1 -1
- package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +19 -0
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.js +19 -0
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy.js.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
- package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
- package/out/zero-cache/src/db/run-transaction.js.map +1 -1
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/statements.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/db/warmup.js.map +1 -1
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/scripts/decommission.js.map +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/scripts/permissions.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +11 -10
- package/out/zero-cache/src/server/anonymous-otel-start.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 +13 -7
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/logging.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +4 -2
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/mutator.js +4 -2
- package/out/zero-cache/src/server/mutator.js.map +1 -1
- package/out/zero-cache/src/server/otel-diag-logger.js.map +1 -1
- package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
- package/out/zero-cache/src/server/otel-start.js +1 -1
- package/out/zero-cache/src/server/otel-start.js.map +1 -1
- package/out/zero-cache/src/server/priority-op.js.map +1 -1
- package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
- package/out/zero-cache/src/server/reaper.js +6 -4
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +4 -2
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/main.js +2 -1
- package/out/zero-cache/src/server/runner/main.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js +6 -3
- package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +8 -6
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +5 -2
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +2 -2
- 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.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +8 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +13 -12
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +1 -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 +33 -33
- 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.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js +2 -0
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js +21 -25
- package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +15 -0
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/heapz.js.map +1 -1
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +8 -3
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +1 -0
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/runner.js.map +1 -1
- package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/running-state.js +3 -0
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.js +97 -113
- package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +103 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +15 -5
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/configuration-error.d.ts +4 -0
- package/out/zero-cache/src/types/configuration-error.d.ts.map +1 -0
- package/out/zero-cache/src/types/configuration-error.js +11 -0
- package/out/zero-cache/src/types/configuration-error.js.map +1 -0
- package/out/zero-cache/src/types/error-with-level.js.map +1 -1
- package/out/zero-cache/src/types/http.js.map +1 -1
- package/out/zero-cache/src/types/lexi-version.js.map +1 -1
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/names.js.map +1 -1
- package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts +2 -0
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +34 -1
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-cache/src/types/shards.js.map +1 -1
- package/out/zero-cache/src/types/sql.js.map +1 -1
- package/out/zero-cache/src/types/state-version.js.map +1 -1
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/strings.js.map +1 -1
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/timeout.js.map +1 -1
- package/out/zero-cache/src/types/url-params.js.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
- package/out/zero-cache/src/types/ws.js.map +1 -1
- package/out/zero-cache/src/workers/connect-params.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/mutator.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +2 -1
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/custom.js +2 -1
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/enable-analytics.js.map +1 -1
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/http-string.js.map +1 -1
- package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
- package/out/zero-client/src/client/inspector/client.js.map +1 -1
- package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/query.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/log-options.js.map +1 -1
- package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- package/out/zero-client/src/client/metrics.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
- package/out/zero-client/src/client/server-option.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-client/src/client/zero-rep.js.map +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +58 -32
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/util/nanoid.js.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.js +3 -0
- package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
- package/out/zero-protocol/src/application-error.js.map +1 -1
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.js +1 -0
- package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
- package/out/zero-protocol/src/client-schema.js.map +1 -1
- package/out/zero-protocol/src/close-connection.js.map +1 -1
- package/out/zero-protocol/src/connect.js +7 -0
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.js.map +1 -1
- package/out/zero-protocol/src/data.js.map +1 -1
- package/out/zero-protocol/src/delete-clients.js.map +1 -1
- package/out/zero-protocol/src/down.js.map +1 -1
- package/out/zero-protocol/src/error.js +7 -0
- package/out/zero-protocol/src/error.js.map +1 -1
- package/out/zero-protocol/src/inspect-down.js.map +1 -1
- package/out/zero-protocol/src/inspect-up.js +1 -0
- package/out/zero-protocol/src/inspect-up.js.map +1 -1
- package/out/zero-protocol/src/mutate-server.js.map +1 -1
- package/out/zero-protocol/src/mutation-id.js.map +1 -1
- package/out/zero-protocol/src/mutation.js.map +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/ping.js.map +1 -1
- package/out/zero-protocol/src/poke.js +4 -0
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/pong.js.map +1 -1
- package/out/zero-protocol/src/primary-key.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/pull.js.map +1 -1
- package/out/zero-protocol/src/push.js +16 -0
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/queries-patch.js.map +1 -1
- package/out/zero-protocol/src/query-hash.js.map +1 -1
- package/out/zero-protocol/src/query-server.js.map +1 -1
- package/out/zero-protocol/src/row-patch.js.map +1 -1
- package/out/zero-protocol/src/up.js.map +1 -1
- package/out/zero-protocol/src/update-auth.js.map +1 -1
- package/out/zero-protocol/src/version.js.map +1 -1
- package/out/zero-react/src/use-connection-state.js +4 -2
- package/out/zero-react/src/use-connection-state.js.map +1 -1
- package/out/zero-react/src/use-query.js +6 -4
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/use-zero-online.js +4 -2
- package/out/zero-react/src/use-zero-online.js.map +1 -1
- package/out/zero-react/src/zero-provider.js +15 -12
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
- package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
- package/out/zero-schema/src/builder/table-builder.js.map +1 -1
- package/out/zero-schema/src/compiled-permissions.js.map +1 -1
- package/out/zero-schema/src/name-mapper.js.map +1 -1
- package/out/zero-schema/src/permissions.js.map +1 -1
- package/out/zero-schema/src/schema-config.js.map +1 -1
- package/out/zero-server/src/adapters/drizzle.js.map +1 -1
- package/out/zero-server/src/adapters/kysely.js.map +1 -1
- package/out/zero-server/src/adapters/pg.js +1 -1
- package/out/zero-server/src/adapters/pg.js.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-server/src/adapters/prisma.js.map +1 -1
- package/out/zero-server/src/custom.js +2 -1
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/logging.js.map +1 -1
- package/out/zero-server/src/pg-query-executor.js.map +1 -1
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/solid-view.js +1 -1
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-connection-state.js +1 -1
- package/out/zero-solid/src/use-connection-state.js.map +1 -1
- package/out/zero-solid/src/use-query.js +2 -2
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-zero-online.js +1 -1
- package/out/zero-solid/src/use-zero-online.js.map +1 -1
- package/out/zero-solid/src/use-zero.js +1 -1
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zero-types/src/format.js.map +1 -1
- package/out/zero-types/src/name-mapper.js.map +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +18 -7
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/debug-delegate.d.ts +5 -0
- package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
- package/out/zql/src/builder/debug-delegate.js +10 -1
- package/out/zql/src/builder/debug-delegate.js.map +1 -1
- package/out/zql/src/builder/filter.js.map +1 -1
- package/out/zql/src/builder/like.js.map +1 -1
- package/out/zql/src/error.js.map +1 -1
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +32 -0
- package/out/zql/src/ivm/cap.d.ts.map +1 -0
- package/out/zql/src/ivm/cap.js +205 -0
- package/out/zql/src/ivm/cap.js.map +1 -0
- package/out/zql/src/ivm/change.js.map +1 -1
- package/out/zql/src/ivm/constraint.js +1 -1
- package/out/zql/src/ivm/constraint.js.map +1 -1
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter-push.js.map +1 -1
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.d.ts +4 -8
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +59 -63
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.js +1 -1
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/memory-storage.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +1 -1
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/schema.d.ts +0 -8
- package/out/zql/src/ivm/schema.d.ts.map +1 -1
- package/out/zql/src/ivm/skip-yields.js.map +1 -1
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/source.js.map +1 -1
- package/out/zql/src/ivm/stream.js.map +1 -1
- package/out/zql/src/ivm/take.js +2 -2
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +3 -1
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/mutate/custom.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/mutate/mutator.d.ts +12 -3
- package/out/zql/src/mutate/mutator.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator.js.map +1 -1
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-constraint.js.map +1 -1
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-fan-in.js.map +1 -1
- package/out/zql/src/planner/planner-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.d.ts.map +1 -1
- package/out/zql/src/planner/planner-join.js +2 -1
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-node.js.map +1 -1
- package/out/zql/src/planner/planner-source.js.map +1 -1
- package/out/zql/src/planner/planner-terminus.js.map +1 -1
- package/out/zql/src/query/complete-ordering.js.map +1 -1
- package/out/zql/src/query/create-builder.js.map +1 -1
- package/out/zql/src/query/error.js.map +1 -1
- package/out/zql/src/query/escape-like.js.map +1 -1
- package/out/zql/src/query/expression.js.map +1 -1
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/out/zql/src/query/metrics-delegate.js.map +1 -1
- package/out/zql/src/query/named.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-impl.js +1 -1
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts +10 -3
- package/out/zql/src/query/query-registry.d.ts.map +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/static-query.js.map +1 -1
- package/out/zql/src/query/ttl.js.map +1 -1
- package/out/zql/src/query/validate-input.js.map +1 -1
- package/out/zqlite/src/database-storage.js.map +1 -1
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/explain-queries.js.map +1 -1
- package/out/zqlite/src/internal/sql-inline.js.map +1 -1
- package/out/zqlite/src/internal/sql.js.map +1 -1
- package/out/zqlite/src/internal/statement-cache.js.map +1 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +7 -7
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +23 -23
- package/out/zero-client/src/util/socket.d.ts +0 -3
- package/out/zero-client/src/util/socket.d.ts.map +0 -1
- package/out/zero-client/src/util/socket.js +0 -8
- package/out/zero-client/src/util/socket.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-groups.js","names":[],"sources":["../../../../../replicache/src/persist/client-groups.ts"],"sourcesContent":["import {assert, assertObject} from '../../../shared/src/asserts.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport {toRefs} from '../dag/chunk.ts';\nimport type {Read, Write} from '../dag/store.ts';\nimport {deepFreeze, type FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, hashSchema} from '../hash.ts';\nimport {indexDefinitionsEqual, indexDefinitionsSchema} from '../index-defs.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\n\nexport type ClientGroupMap = ReadonlyMap<ClientGroupID, ClientGroup>;\n\nconst clientGroupSchema = valita.readonlyObject({\n /**\n * The hash of the commit in the perdag last persisted to this client group.\n * Should only be updated by clients assigned to this client group.\n */\n headHash: hashSchema,\n\n /**\n * Set of mutator names common to all clients assigned to this client group.\n */\n mutatorNames: valita.readonlyArray(valita.string()),\n\n /**\n * Index definitions common to all clients assigned to this client group.\n */\n indexes: indexDefinitionsSchema,\n\n /**\n * The highest mutation ID of every client assigned to this client group.\n * Should only be updated by clients assigned to this client group. Read by\n * other clients to determine if there are unacknowledged pending mutations\n * for them to try to recover. This is redundant with information in the\n * commit graph at `headHash`, but allows other clients to determine if there\n * are unacknowledged pending mutations without having to load the commit\n * graph.\n */\n mutationIDs: valita.readonlyRecord(valita.number()),\n\n /**\n * The highest lastMutationID received from the server for every client\n * assigned to this client group.\n *\n * Should be updated by the clients assigned to this client group whenever\n * they persist to this client group. Read by other clients to determine if\n * there are unacknowledged pending mutations for them to recover and\n * *updated* by other clients upon successfully recovering pending mutations\n * to avoid redundant pushes of pending mutations.\n *\n * Note: This will be the same as the `lastMutationIDs` of the base snapshot\n * of the client group's commit graph when written by clients assigned to this\n * client group. However, when written by another client recovering mutations\n * it may be different because the other client does not update the commit\n * graph.\n */\n lastServerAckdMutationIDs: valita.record(valita.number()),\n\n /**\n * If the server deletes this client group it can signal that the client group\n * was deleted. If that happens we mark this client group as disabled so that\n * we do not use it again when creating new clients.\n */\n disabled: valita.boolean(),\n});\n\nexport type ClientGroup = valita.Infer<typeof clientGroupSchema>;\n\nexport const CLIENT_GROUPS_HEAD_NAME = 'client-groups';\n\nfunction assertClientGroup(value: unknown): asserts value is ClientGroup {\n valita.assert(value, clientGroupSchema);\n}\n\nfunction chunkDataToClientGroupMap(chunkData: unknown): ClientGroupMap {\n assertObject(chunkData);\n const clientGroups = new Map<ClientGroupID, ClientGroup>();\n for (const [key, value] of Object.entries(chunkData)) {\n if (value !== undefined) {\n assertClientGroup(value);\n clientGroups.set(key, value);\n }\n }\n return clientGroups;\n}\n\nfunction clientGroupMapToChunkData(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): FrozenJSONValue {\n const chunkData: {[id: ClientGroupID]: ClientGroup} = {};\n for (const [clientGroupID, clientGroup] of clientGroups.entries()) {\n dagWrite.assertValidHash(clientGroup.headHash);\n chunkData[clientGroupID] = {\n ...clientGroup,\n mutatorNames: [...clientGroup.mutatorNames.values()],\n };\n }\n return deepFreeze(chunkData);\n}\n\nasync function getClientGroupsAtHash(\n hash: Hash,\n dagRead: Read,\n): Promise<ClientGroupMap> {\n const chunk = await dagRead.getChunk(hash);\n return chunkDataToClientGroupMap(chunk?.data);\n}\n\nexport async function getClientGroups(dagRead: Read): Promise<ClientGroupMap> {\n const hash = await dagRead.getHead(CLIENT_GROUPS_HEAD_NAME);\n if (!hash) {\n return new Map();\n }\n return getClientGroupsAtHash(hash, dagRead);\n}\n\nexport async function setClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n for (const [clientGroupID, clientGroup] of clientGroups) {\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n }\n return setValidatedClientGroups(clientGroups, dagWrite);\n}\n\nexport async function setClientGroup(\n clientGroupID: ClientGroupID,\n clientGroup: ClientGroup,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n const newClientGroups = new Map(currClientGroups);\n newClientGroups.set(clientGroupID, clientGroup);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nexport async function deleteClientGroup(\n clientGroupID: ClientGroupID,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n if (!currClientGroups.has(clientGroupID)) {\n return currClientGroups;\n }\n const newClientGroups = new Map(currClientGroups.entries());\n newClientGroups.delete(clientGroupID);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nfunction validateClientGroupUpdate(\n clientGroup: ClientGroup,\n currClientGroup: ClientGroup | undefined,\n) {\n const mutatorNamesSet = new Set(clientGroup.mutatorNames);\n assert(\n mutatorNamesSet.size === clientGroup.mutatorNames.length,\n \"A client group's mutatorNames must be a set.\",\n );\n if (currClientGroup !== undefined) {\n assert(\n indexDefinitionsEqual(currClientGroup.indexes, clientGroup.indexes),\n \"A client group's index definitions must never change.\",\n );\n assert(\n mutatorNamesEqual(mutatorNamesSet, currClientGroup.mutatorNames),\n \"A client group's mutatorNames must never change.\",\n );\n }\n}\n\nasync function setValidatedClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const chunkData = clientGroupMapToChunkData(clientGroups, dagWrite);\n const refs: Set<Hash> = new Set();\n for (const clientGroup of clientGroups.values()) {\n refs.add(clientGroup.headHash);\n }\n const chunk = dagWrite.createChunk(chunkData, toRefs(refs));\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(CLIENT_GROUPS_HEAD_NAME, chunk.hash);\n return clientGroups;\n}\n\nexport function mutatorNamesEqual(\n mutatorNamesSet: ReadonlySet<string>,\n mutatorNames: readonly string[],\n): boolean {\n if (mutatorNames.length !== mutatorNamesSet.size) {\n return false;\n }\n for (const mutatorName of mutatorNames) {\n if (!mutatorNamesSet.has(mutatorName)) {\n return false;\n }\n }\n return true;\n}\n\nexport async function getClientGroup(\n id: ClientGroupID,\n dagRead: Read,\n): Promise<ClientGroup | undefined> {\n const clientGroups = await getClientGroups(dagRead);\n return clientGroups.get(id);\n}\n\nexport function clientGroupHasPendingMutations(clientGroup: ClientGroup) {\n for (const [clientID, mutationID] of Object.entries(\n clientGroup.mutationIDs,\n )) {\n const lastServerAckdMutationID =\n clientGroup.lastServerAckdMutationIDs[clientID];\n if (\n (lastServerAckdMutationID === undefined && mutationID !== 0) ||\n lastServerAckdMutationID < mutationID\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Marks a client group as disabled. This can happen if the server deletes the\n * client group (servers should not delete clients or client groups but it often\n * happens in practice when developing).\n *\n * A disabled client group prevents pulls and pushes from happening.\n */\nexport async function disableClientGroup(\n clientGroupID: string,\n dagWrite: Write,\n): Promise<void> {\n const clientGroup = await getClientGroup(clientGroupID, dagWrite);\n if (!clientGroup) {\n // No client group matching in the database, so nothing to do.\n return;\n }\n const disabledClientGroup = {\n ...clientGroup,\n disabled: true,\n };\n await setClientGroup(clientGroupID, disabledClientGroup, dagWrite);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client-groups.js","names":[],"sources":["../../../../../replicache/src/persist/client-groups.ts"],"sourcesContent":["import {assert, assertObject} from '../../../shared/src/asserts.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport {toRefs} from '../dag/chunk.ts';\nimport type {Read, Write} from '../dag/store.ts';\nimport {deepFreeze, type FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, hashSchema} from '../hash.ts';\nimport {indexDefinitionsEqual, indexDefinitionsSchema} from '../index-defs.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\n\nexport type ClientGroupMap = ReadonlyMap<ClientGroupID, ClientGroup>;\n\nconst clientGroupSchema = valita.readonlyObject({\n /**\n * The hash of the commit in the perdag last persisted to this client group.\n * Should only be updated by clients assigned to this client group.\n */\n headHash: hashSchema,\n\n /**\n * Set of mutator names common to all clients assigned to this client group.\n */\n mutatorNames: valita.readonlyArray(valita.string()),\n\n /**\n * Index definitions common to all clients assigned to this client group.\n */\n indexes: indexDefinitionsSchema,\n\n /**\n * The highest mutation ID of every client assigned to this client group.\n * Should only be updated by clients assigned to this client group. Read by\n * other clients to determine if there are unacknowledged pending mutations\n * for them to try to recover. This is redundant with information in the\n * commit graph at `headHash`, but allows other clients to determine if there\n * are unacknowledged pending mutations without having to load the commit\n * graph.\n */\n mutationIDs: valita.readonlyRecord(valita.number()),\n\n /**\n * The highest lastMutationID received from the server for every client\n * assigned to this client group.\n *\n * Should be updated by the clients assigned to this client group whenever\n * they persist to this client group. Read by other clients to determine if\n * there are unacknowledged pending mutations for them to recover and\n * *updated* by other clients upon successfully recovering pending mutations\n * to avoid redundant pushes of pending mutations.\n *\n * Note: This will be the same as the `lastMutationIDs` of the base snapshot\n * of the client group's commit graph when written by clients assigned to this\n * client group. However, when written by another client recovering mutations\n * it may be different because the other client does not update the commit\n * graph.\n */\n lastServerAckdMutationIDs: valita.record(valita.number()),\n\n /**\n * If the server deletes this client group it can signal that the client group\n * was deleted. If that happens we mark this client group as disabled so that\n * we do not use it again when creating new clients.\n */\n disabled: valita.boolean(),\n});\n\nexport type ClientGroup = valita.Infer<typeof clientGroupSchema>;\n\nexport const CLIENT_GROUPS_HEAD_NAME = 'client-groups';\n\nfunction assertClientGroup(value: unknown): asserts value is ClientGroup {\n valita.assert(value, clientGroupSchema);\n}\n\nfunction chunkDataToClientGroupMap(chunkData: unknown): ClientGroupMap {\n assertObject(chunkData);\n const clientGroups = new Map<ClientGroupID, ClientGroup>();\n for (const [key, value] of Object.entries(chunkData)) {\n if (value !== undefined) {\n assertClientGroup(value);\n clientGroups.set(key, value);\n }\n }\n return clientGroups;\n}\n\nfunction clientGroupMapToChunkData(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): FrozenJSONValue {\n const chunkData: {[id: ClientGroupID]: ClientGroup} = {};\n for (const [clientGroupID, clientGroup] of clientGroups.entries()) {\n dagWrite.assertValidHash(clientGroup.headHash);\n chunkData[clientGroupID] = {\n ...clientGroup,\n mutatorNames: [...clientGroup.mutatorNames.values()],\n };\n }\n return deepFreeze(chunkData);\n}\n\nasync function getClientGroupsAtHash(\n hash: Hash,\n dagRead: Read,\n): Promise<ClientGroupMap> {\n const chunk = await dagRead.getChunk(hash);\n return chunkDataToClientGroupMap(chunk?.data);\n}\n\nexport async function getClientGroups(dagRead: Read): Promise<ClientGroupMap> {\n const hash = await dagRead.getHead(CLIENT_GROUPS_HEAD_NAME);\n if (!hash) {\n return new Map();\n }\n return getClientGroupsAtHash(hash, dagRead);\n}\n\nexport async function setClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n for (const [clientGroupID, clientGroup] of clientGroups) {\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n }\n return setValidatedClientGroups(clientGroups, dagWrite);\n}\n\nexport async function setClientGroup(\n clientGroupID: ClientGroupID,\n clientGroup: ClientGroup,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n const newClientGroups = new Map(currClientGroups);\n newClientGroups.set(clientGroupID, clientGroup);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nexport async function deleteClientGroup(\n clientGroupID: ClientGroupID,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n if (!currClientGroups.has(clientGroupID)) {\n return currClientGroups;\n }\n const newClientGroups = new Map(currClientGroups.entries());\n newClientGroups.delete(clientGroupID);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nfunction validateClientGroupUpdate(\n clientGroup: ClientGroup,\n currClientGroup: ClientGroup | undefined,\n) {\n const mutatorNamesSet = new Set(clientGroup.mutatorNames);\n assert(\n mutatorNamesSet.size === clientGroup.mutatorNames.length,\n \"A client group's mutatorNames must be a set.\",\n );\n if (currClientGroup !== undefined) {\n assert(\n indexDefinitionsEqual(currClientGroup.indexes, clientGroup.indexes),\n \"A client group's index definitions must never change.\",\n );\n assert(\n mutatorNamesEqual(mutatorNamesSet, currClientGroup.mutatorNames),\n \"A client group's mutatorNames must never change.\",\n );\n }\n}\n\nasync function setValidatedClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const chunkData = clientGroupMapToChunkData(clientGroups, dagWrite);\n const refs: Set<Hash> = new Set();\n for (const clientGroup of clientGroups.values()) {\n refs.add(clientGroup.headHash);\n }\n const chunk = dagWrite.createChunk(chunkData, toRefs(refs));\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(CLIENT_GROUPS_HEAD_NAME, chunk.hash);\n return clientGroups;\n}\n\nexport function mutatorNamesEqual(\n mutatorNamesSet: ReadonlySet<string>,\n mutatorNames: readonly string[],\n): boolean {\n if (mutatorNames.length !== mutatorNamesSet.size) {\n return false;\n }\n for (const mutatorName of mutatorNames) {\n if (!mutatorNamesSet.has(mutatorName)) {\n return false;\n }\n }\n return true;\n}\n\nexport async function getClientGroup(\n id: ClientGroupID,\n dagRead: Read,\n): Promise<ClientGroup | undefined> {\n const clientGroups = await getClientGroups(dagRead);\n return clientGroups.get(id);\n}\n\nexport function clientGroupHasPendingMutations(clientGroup: ClientGroup) {\n for (const [clientID, mutationID] of Object.entries(\n clientGroup.mutationIDs,\n )) {\n const lastServerAckdMutationID =\n clientGroup.lastServerAckdMutationIDs[clientID];\n if (\n (lastServerAckdMutationID === undefined && mutationID !== 0) ||\n lastServerAckdMutationID < mutationID\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Marks a client group as disabled. This can happen if the server deletes the\n * client group (servers should not delete clients or client groups but it often\n * happens in practice when developing).\n *\n * A disabled client group prevents pulls and pushes from happening.\n */\nexport async function disableClientGroup(\n clientGroupID: string,\n dagWrite: Write,\n): Promise<void> {\n const clientGroup = await getClientGroup(clientGroupID, dagWrite);\n if (!clientGroup) {\n // No client group matching in the database, so nothing to do.\n return;\n }\n const disabledClientGroup = {\n ...clientGroup,\n disabled: true,\n };\n await setClientGroup(clientGroupID, disabledClientGroup, dagWrite);\n}\n"],"mappings":"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"}
|
|
@@ -18,13 +18,41 @@ import { makeClientID } from "./make-client-id.js";
|
|
|
18
18
|
var clientV5Schema = readonlyObject({
|
|
19
19
|
heartbeatTimestampMs: valita_exports.number(),
|
|
20
20
|
headHash: hashSchema,
|
|
21
|
+
/**
|
|
22
|
+
* The hash of a commit we are in the middle of refreshing into this client's
|
|
23
|
+
* memdag.
|
|
24
|
+
*/
|
|
21
25
|
tempRefreshHash: hashSchema.nullable(),
|
|
26
|
+
/**
|
|
27
|
+
* ID of this client's perdag client group. This needs to be sent in pull
|
|
28
|
+
* request (to enable syncing all last mutation ids in the client group).
|
|
29
|
+
*/
|
|
22
30
|
clientGroupID: clientGroupIDSchema
|
|
23
31
|
});
|
|
24
32
|
var clientV6Schema = readonlyObject({
|
|
25
33
|
heartbeatTimestampMs: valita_exports.number(),
|
|
34
|
+
/**
|
|
35
|
+
* A set of hashes, which contains:
|
|
36
|
+
* 1. The hash of the last commit this client refreshed from its client group
|
|
37
|
+
* (this is the commit it bootstrapped from until it completes its first
|
|
38
|
+
* refresh).
|
|
39
|
+
* 2. One or more hashes that were added to retain chunks of a commit while it
|
|
40
|
+
* was being refreshed into this client's memdag. (This can be one or more
|
|
41
|
+
* because refresh's cleanup step is a separate transaction and can fail).
|
|
42
|
+
* Upon refresh completing and successfully running its clean up step, this
|
|
43
|
+
* set will contain a single hash: the hash of the last commit this client
|
|
44
|
+
* refreshed.
|
|
45
|
+
*/
|
|
26
46
|
refreshHashes: readonlyArray(hashSchema),
|
|
47
|
+
/**
|
|
48
|
+
* The hash of the last snapshot commit persisted by this client to this
|
|
49
|
+
* client's client group, or null if has never persisted a snapshot.
|
|
50
|
+
*/
|
|
27
51
|
persistHash: hashSchema.nullable(),
|
|
52
|
+
/**
|
|
53
|
+
* ID of this client's perdag client group. This needs to be sent in pull
|
|
54
|
+
* request (to enable syncing all last mutation ids in the client group).
|
|
55
|
+
*/
|
|
28
56
|
clientGroupID: clientGroupIDSchema
|
|
29
57
|
});
|
|
30
58
|
function isClientV6(client) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clients.js","names":[],"sources":["../../../../../replicache/src/persist/clients.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, assertObject} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport {emptyDataNode} from '../btree/node.ts';\nimport {BTreeRead} from '../btree/read.ts';\nimport {type FrozenCookie, compareCookies} from '../cookies.ts';\nimport {type Refs, toRefs} from '../dag/chunk.ts';\nimport type {Read, Store, Write} from '../dag/store.ts';\nimport type {Commit} from '../db/commit.ts';\nimport {\n type ChunkIndexDefinition,\n type IndexRecord,\n type SnapshotMetaDD31,\n assertSnapshotCommitDD31,\n baseSnapshotFromHash,\n chunkIndexDefinitionEqualIgnoreName,\n getRefs,\n newSnapshotCommitDataDD31,\n toChunkIndexDefinition,\n} from '../db/commit.ts';\nimport {createIndexBTree} from '../db/write.ts';\nimport type {DeletedClients} from '../deleted-clients.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport {type FrozenJSONValue, deepFreeze} from '../frozen-json.ts';\nimport {type Hash, hashSchema} from '../hash.ts';\nimport {type IndexDefinitions, indexDefinitionsEqual} from '../index-defs.ts';\nimport {\n type ClientGroupID,\n type ClientID,\n clientGroupIDSchema,\n} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n type ClientGroup,\n getClientGroup,\n getClientGroups,\n mutatorNamesEqual,\n setClientGroup,\n} from './client-groups.ts';\nimport {makeClientID} from './make-client-id.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport type ClientMap = ReadonlyMap<ClientID, ClientV5 | ClientV6>;\n\nconst clientV5Schema = valita.readonlyObject({\n heartbeatTimestampMs: valita.number(),\n\n headHash: hashSchema,\n\n /**\n * The hash of a commit we are in the middle of refreshing into this client's\n * memdag.\n */\n tempRefreshHash: hashSchema.nullable(),\n\n /**\n * ID of this client's perdag client group. This needs to be sent in pull\n * request (to enable syncing all last mutation ids in the client group).\n */\n clientGroupID: clientGroupIDSchema,\n});\n\nexport type ClientV5 = valita.Infer<typeof clientV5Schema>;\n\nconst clientV6Schema = valita.readonlyObject({\n heartbeatTimestampMs: valita.number(),\n\n /**\n * A set of hashes, which contains:\n * 1. The hash of the last commit this client refreshed from its client group\n * (this is the commit it bootstrapped from until it completes its first\n * refresh).\n * 2. One or more hashes that were added to retain chunks of a commit while it\n * was being refreshed into this client's memdag. (This can be one or more\n * because refresh's cleanup step is a separate transaction and can fail).\n * Upon refresh completing and successfully running its clean up step, this\n * set will contain a single hash: the hash of the last commit this client\n * refreshed.\n */\n refreshHashes: valita.readonlyArray(hashSchema),\n\n /**\n * The hash of the last snapshot commit persisted by this client to this\n * client's client group, or null if has never persisted a snapshot.\n */\n persistHash: hashSchema.nullable(),\n\n /**\n * ID of this client's perdag client group. This needs to be sent in pull\n * request (to enable syncing all last mutation ids in the client group).\n */\n clientGroupID: clientGroupIDSchema,\n});\n\nexport type ClientV6 = valita.Infer<typeof clientV6Schema>;\n\nexport type Client = ClientV5 | ClientV6;\n\nfunction isClientV6(client: Client): client is ClientV6 {\n return (client as ClientV6).refreshHashes !== undefined;\n}\n\nexport const CLIENTS_HEAD_NAME = 'clients';\n\nconst clientSchema = valita.union(clientV5Schema, clientV6Schema);\n\nfunction assertClient(value: unknown): asserts value is Client {\n valita.assert(value, clientSchema);\n}\n\nexport function assertClientV6(value: unknown): asserts value is ClientV6 {\n valita.assert(value, clientV6Schema);\n}\n\nfunction chunkDataToClientMap(chunkData: unknown): ClientMap {\n assertObject(chunkData);\n const clients = new Map();\n for (const key in chunkData) {\n if (hasOwn(chunkData, key)) {\n const value = chunkData[key];\n if (value !== undefined) {\n assertClient(value);\n clients.set(key, value);\n }\n }\n }\n return clients;\n}\n\nfunction clientMapToChunkData(\n clients: ClientMap,\n dagWrite: Write,\n): FrozenJSONValue {\n for (const client of clients.values()) {\n if (isClientV6(client)) {\n client.refreshHashes.forEach(dagWrite.assertValidHash);\n if (client.persistHash) {\n dagWrite.assertValidHash(client.persistHash);\n }\n } else {\n dagWrite.assertValidHash(client.headHash);\n if (client.tempRefreshHash) {\n dagWrite.assertValidHash(client.tempRefreshHash);\n }\n }\n }\n return deepFreeze(Object.fromEntries(clients));\n}\n\nexport async function getClients(dagRead: Read): Promise<ClientMap> {\n const hash = await dagRead.getHead(CLIENTS_HEAD_NAME);\n return getClientsAtHash(hash, dagRead);\n}\n\nasync function getClientsAtHash(\n hash: Hash | undefined,\n dagRead: Read,\n): Promise<ClientMap> {\n if (!hash) {\n return new Map();\n }\n const chunk = await dagRead.getChunk(hash);\n return chunkDataToClientMap(chunk?.data);\n}\n\n/**\n * Used to signal that a client does not exist. Maybe it was garbage collected?\n */\nexport class ClientStateNotFoundError extends Error {\n name = 'ClientStateNotFoundError';\n readonly id: string;\n constructor(id: ClientID) {\n super(`Client state not found, id: ${id}`);\n this.id = id;\n }\n}\n\n/**\n * Throws a `ClientStateNotFoundError` if the client does not exist.\n */\nexport async function assertHasClientState(\n id: ClientID,\n dagRead: Read,\n): Promise<void> {\n if (!(await hasClientState(id, dagRead))) {\n throw new ClientStateNotFoundError(id);\n }\n}\n\nexport async function hasClientState(\n id: ClientID,\n dagRead: Read,\n): Promise<boolean> {\n return !!(await getClient(id, dagRead));\n}\n\nexport async function getClient(\n id: ClientID,\n dagRead: Read,\n): Promise<Client | undefined> {\n const clients = await getClients(dagRead);\n return clients.get(id);\n}\n\nexport async function mustGetClient(\n id: ClientID,\n dagRead: Read,\n): Promise<Client> {\n const client = await getClient(id, dagRead);\n if (!client) {\n throw new ClientStateNotFoundError(id);\n }\n return client;\n}\n\ntype InitClientV6Result = [\n client: ClientV6,\n hash: Hash,\n clientMap: ClientMap,\n newClientGroup: boolean,\n];\n\nexport function initClientV6(\n newClientID: ClientID,\n lc: LogContext,\n perdag: Store,\n mutatorNames: string[],\n indexes: IndexDefinitions,\n formatVersion: FormatVersion,\n enableClientGroupForking: boolean,\n): Promise<InitClientV6Result> {\n return withWrite(perdag, async dagWrite => {\n async function setClientsAndClientGroupAndCommit(\n basisHash: Hash | null,\n cookieJSON: FrozenCookie,\n valueHash: Hash,\n indexRecords: readonly IndexRecord[],\n ): Promise<InitClientV6Result> {\n const newSnapshotData = newSnapshotCommitDataDD31(\n basisHash,\n {},\n cookieJSON,\n valueHash,\n indexRecords,\n );\n const chunk = dagWrite.createChunk(\n newSnapshotData,\n getRefs(newSnapshotData),\n );\n\n const newClientGroupID = makeClientID();\n\n const newClient: ClientV6 = {\n heartbeatTimestampMs: Date.now(),\n refreshHashes: [chunk.hash],\n persistHash: null,\n clientGroupID: newClientGroupID,\n };\n\n const newClients = new Map(clients).set(newClientID, newClient);\n\n const clientGroup: ClientGroup = {\n headHash: chunk.hash,\n mutatorNames,\n indexes,\n mutationIDs: {},\n lastServerAckdMutationIDs: {},\n disabled: false,\n };\n\n await Promise.all([\n dagWrite.putChunk(chunk),\n setClients(newClients, dagWrite),\n setClientGroup(newClientGroupID, clientGroup, dagWrite),\n ]);\n\n return [newClient, chunk.hash, newClients, true];\n }\n\n const clients = await getClients(dagWrite);\n\n const res = await findMatchingClient(dagWrite, mutatorNames, indexes);\n if (res.type === FIND_MATCHING_CLIENT_TYPE_HEAD) {\n // We found a client group with matching mutators and indexes. We can\n // reuse it.\n const {clientGroupID, headHash} = res;\n\n const newClient: ClientV6 = {\n clientGroupID,\n refreshHashes: [headHash],\n heartbeatTimestampMs: Date.now(),\n persistHash: null,\n };\n const newClients = new Map(clients).set(newClientID, newClient);\n await setClients(newClients, dagWrite);\n\n return [newClient, headHash, newClients, false];\n }\n\n if (\n !enableClientGroupForking ||\n res.type === FIND_MATCHING_CLIENT_TYPE_NEW\n ) {\n // No client group to fork from. Create empty snapshot.\n const emptyBTreeChunk = dagWrite.createChunk(emptyDataNode, []);\n await dagWrite.putChunk(emptyBTreeChunk);\n\n // Create indexes\n const indexRecords: IndexRecord[] = [];\n\n // At this point the value of replicache is the empty tree so all index\n // maps will also be the empty tree.\n for (const [name, indexDefinition] of Object.entries(indexes)) {\n const chunkIndexDefinition = toChunkIndexDefinition(\n name,\n indexDefinition,\n );\n indexRecords.push({\n definition: chunkIndexDefinition,\n valueHash: emptyBTreeChunk.hash,\n });\n }\n\n return setClientsAndClientGroupAndCommit(\n null,\n null,\n emptyBTreeChunk.hash,\n indexRecords,\n );\n }\n\n // Now we create a new client and client group that we fork from the found\n // snapshot.\n assert(\n res.type === FIND_MATCHING_CLIENT_TYPE_FORK,\n 'Expected result type to be FORK',\n );\n\n const {snapshot} = res;\n\n // Create indexes\n const indexRecords: IndexRecord[] = [];\n const {valueHash, indexes: oldIndexes} = snapshot;\n const map = new BTreeRead(dagWrite, formatVersion, valueHash);\n\n for (const [name, indexDefinition] of Object.entries(indexes)) {\n const {prefix = '', jsonPointer, allowEmpty = false} = indexDefinition;\n const chunkIndexDefinition: ChunkIndexDefinition = {\n name,\n keyPrefix: prefix,\n jsonPointer,\n allowEmpty,\n };\n\n const oldIndex = findMatchingOldIndex(oldIndexes, chunkIndexDefinition);\n if (oldIndex) {\n indexRecords.push({\n definition: chunkIndexDefinition,\n valueHash: oldIndex.valueHash,\n });\n } else {\n const indexBTree = await createIndexBTree(\n lc,\n dagWrite,\n map,\n prefix,\n jsonPointer,\n allowEmpty,\n formatVersion,\n );\n indexRecords.push({\n definition: chunkIndexDefinition,\n valueHash: await indexBTree.flush(),\n });\n }\n }\n\n return setClientsAndClientGroupAndCommit(\n snapshot.meta.basisHash,\n snapshot.meta.cookieJSON,\n snapshot.valueHash,\n indexRecords,\n );\n });\n}\n\nfunction findMatchingOldIndex(\n oldIndexes: readonly IndexRecord[],\n chunkIndexDefinition: ChunkIndexDefinition,\n) {\n return oldIndexes.find(index =>\n chunkIndexDefinitionEqualIgnoreName(index.definition, chunkIndexDefinition),\n );\n}\n\nexport const FIND_MATCHING_CLIENT_TYPE_NEW = 0;\nexport const FIND_MATCHING_CLIENT_TYPE_FORK = 1;\nexport const FIND_MATCHING_CLIENT_TYPE_HEAD = 2;\n\nexport type FindMatchingClientResult =\n | {\n type: typeof FIND_MATCHING_CLIENT_TYPE_NEW;\n }\n | {\n type: typeof FIND_MATCHING_CLIENT_TYPE_FORK;\n snapshot: Commit<SnapshotMetaDD31>;\n }\n | {\n type: typeof FIND_MATCHING_CLIENT_TYPE_HEAD;\n clientGroupID: ClientGroupID;\n headHash: Hash;\n };\n\nexport async function findMatchingClient(\n dagRead: Read,\n mutatorNames: string[],\n indexes: IndexDefinitions,\n): Promise<FindMatchingClientResult> {\n let newestCookie: FrozenCookie | undefined;\n let bestSnapshot: Commit<SnapshotMetaDD31> | undefined;\n const mutatorNamesSet = new Set(mutatorNames);\n\n const clientGroups = await getClientGroups(dagRead);\n for (const [clientGroupID, clientGroup] of clientGroups) {\n if (\n !clientGroup.disabled &&\n mutatorNamesEqual(mutatorNamesSet, clientGroup.mutatorNames) &&\n indexDefinitionsEqual(indexes, clientGroup.indexes)\n ) {\n // exact match\n return {\n type: FIND_MATCHING_CLIENT_TYPE_HEAD,\n clientGroupID,\n headHash: clientGroup.headHash,\n };\n }\n\n const clientGroupSnapshotCommit = await baseSnapshotFromHash(\n clientGroup.headHash,\n dagRead,\n );\n assertSnapshotCommitDD31(clientGroupSnapshotCommit);\n\n const {cookieJSON} = clientGroupSnapshotCommit.meta;\n if (\n newestCookie === undefined ||\n compareCookies(cookieJSON, newestCookie) > 0\n ) {\n newestCookie = cookieJSON;\n bestSnapshot = clientGroupSnapshotCommit;\n }\n }\n\n if (bestSnapshot) {\n return {\n type: FIND_MATCHING_CLIENT_TYPE_FORK,\n snapshot: bestSnapshot,\n };\n }\n\n return {type: FIND_MATCHING_CLIENT_TYPE_NEW};\n}\n\nfunction getRefsForClients(clients: ClientMap): Refs {\n const refs: Set<Hash> = new Set();\n for (const client of clients.values()) {\n if (isClientV6(client)) {\n for (const hash of client.refreshHashes) {\n refs.add(hash);\n }\n if (client.persistHash) {\n refs.add(client.persistHash);\n }\n } else {\n refs.add(client.headHash);\n if (client.tempRefreshHash) {\n refs.add(client.tempRefreshHash);\n }\n }\n }\n return toRefs(refs);\n}\n\nexport async function getClientGroupForClient(\n clientID: ClientID,\n read: Read,\n): Promise<ClientGroup | undefined> {\n const clientGroupID = await getClientGroupIDForClient(clientID, read);\n if (!clientGroupID) {\n return undefined;\n }\n return getClientGroup(clientGroupID, read);\n}\n\nexport async function getClientGroupIDForClient(\n clientID: ClientID,\n read: Read,\n): Promise<ClientGroupID | undefined> {\n const client = await getClient(clientID, read);\n return client?.clientGroupID;\n}\n\n/**\n * Adds a Client to the ClientMap and updates the 'clients' head to point at\n * the updated clients.\n */\nexport async function setClient(\n clientID: ClientID,\n client: Client,\n dagWrite: Write,\n): Promise<Hash> {\n const clients = await getClients(dagWrite);\n const newClients = new Map(clients).set(clientID, client);\n return setClients(newClients, dagWrite);\n}\n\n/**\n * Sets the ClientMap and updates the 'clients' head top point at the new\n * clients.\n */\nexport async function setClients(\n clients: ClientMap,\n dagWrite: Write,\n): Promise<Hash> {\n const chunkData = clientMapToChunkData(clients, dagWrite);\n const chunk = dagWrite.createChunk(chunkData, getRefsForClients(clients));\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(CLIENTS_HEAD_NAME, chunk.hash);\n return chunk.hash;\n}\n\n/**\n * Callback function for when Replicache has deleted one or more clients.\n */\nexport type OnClientsDeleted = (\n deletedClients: DeletedClients,\n) => Promise<void>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"clients.js","names":[],"sources":["../../../../../replicache/src/persist/clients.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, assertObject} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport {emptyDataNode} from '../btree/node.ts';\nimport {BTreeRead} from '../btree/read.ts';\nimport {type FrozenCookie, compareCookies} from '../cookies.ts';\nimport {type Refs, toRefs} from '../dag/chunk.ts';\nimport type {Read, Store, Write} from '../dag/store.ts';\nimport type {Commit} from '../db/commit.ts';\nimport {\n type ChunkIndexDefinition,\n type IndexRecord,\n type SnapshotMetaDD31,\n assertSnapshotCommitDD31,\n baseSnapshotFromHash,\n chunkIndexDefinitionEqualIgnoreName,\n getRefs,\n newSnapshotCommitDataDD31,\n toChunkIndexDefinition,\n} from '../db/commit.ts';\nimport {createIndexBTree} from '../db/write.ts';\nimport type {DeletedClients} from '../deleted-clients.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport {type FrozenJSONValue, deepFreeze} from '../frozen-json.ts';\nimport {type Hash, hashSchema} from '../hash.ts';\nimport {type IndexDefinitions, indexDefinitionsEqual} from '../index-defs.ts';\nimport {\n type ClientGroupID,\n type ClientID,\n clientGroupIDSchema,\n} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n type ClientGroup,\n getClientGroup,\n getClientGroups,\n mutatorNamesEqual,\n setClientGroup,\n} from './client-groups.ts';\nimport {makeClientID} from './make-client-id.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport type ClientMap = ReadonlyMap<ClientID, ClientV5 | ClientV6>;\n\nconst clientV5Schema = valita.readonlyObject({\n heartbeatTimestampMs: valita.number(),\n\n headHash: hashSchema,\n\n /**\n * The hash of a commit we are in the middle of refreshing into this client's\n * memdag.\n */\n tempRefreshHash: hashSchema.nullable(),\n\n /**\n * ID of this client's perdag client group. This needs to be sent in pull\n * request (to enable syncing all last mutation ids in the client group).\n */\n clientGroupID: clientGroupIDSchema,\n});\n\nexport type ClientV5 = valita.Infer<typeof clientV5Schema>;\n\nconst clientV6Schema = valita.readonlyObject({\n heartbeatTimestampMs: valita.number(),\n\n /**\n * A set of hashes, which contains:\n * 1. The hash of the last commit this client refreshed from its client group\n * (this is the commit it bootstrapped from until it completes its first\n * refresh).\n * 2. One or more hashes that were added to retain chunks of a commit while it\n * was being refreshed into this client's memdag. (This can be one or more\n * because refresh's cleanup step is a separate transaction and can fail).\n * Upon refresh completing and successfully running its clean up step, this\n * set will contain a single hash: the hash of the last commit this client\n * refreshed.\n */\n refreshHashes: valita.readonlyArray(hashSchema),\n\n /**\n * The hash of the last snapshot commit persisted by this client to this\n * client's client group, or null if has never persisted a snapshot.\n */\n persistHash: hashSchema.nullable(),\n\n /**\n * ID of this client's perdag client group. This needs to be sent in pull\n * request (to enable syncing all last mutation ids in the client group).\n */\n clientGroupID: clientGroupIDSchema,\n});\n\nexport type ClientV6 = valita.Infer<typeof clientV6Schema>;\n\nexport type Client = ClientV5 | ClientV6;\n\nfunction isClientV6(client: Client): client is ClientV6 {\n return (client as ClientV6).refreshHashes !== undefined;\n}\n\nexport const CLIENTS_HEAD_NAME = 'clients';\n\nconst clientSchema = valita.union(clientV5Schema, clientV6Schema);\n\nfunction assertClient(value: unknown): asserts value is Client {\n valita.assert(value, clientSchema);\n}\n\nexport function assertClientV6(value: unknown): asserts value is ClientV6 {\n valita.assert(value, clientV6Schema);\n}\n\nfunction chunkDataToClientMap(chunkData: unknown): ClientMap {\n assertObject(chunkData);\n const clients = new Map();\n for (const key in chunkData) {\n if (hasOwn(chunkData, key)) {\n const value = chunkData[key];\n if (value !== undefined) {\n assertClient(value);\n clients.set(key, value);\n }\n }\n }\n return clients;\n}\n\nfunction clientMapToChunkData(\n clients: ClientMap,\n dagWrite: Write,\n): FrozenJSONValue {\n for (const client of clients.values()) {\n if (isClientV6(client)) {\n client.refreshHashes.forEach(dagWrite.assertValidHash);\n if (client.persistHash) {\n dagWrite.assertValidHash(client.persistHash);\n }\n } else {\n dagWrite.assertValidHash(client.headHash);\n if (client.tempRefreshHash) {\n dagWrite.assertValidHash(client.tempRefreshHash);\n }\n }\n }\n return deepFreeze(Object.fromEntries(clients));\n}\n\nexport async function getClients(dagRead: Read): Promise<ClientMap> {\n const hash = await dagRead.getHead(CLIENTS_HEAD_NAME);\n return getClientsAtHash(hash, dagRead);\n}\n\nasync function getClientsAtHash(\n hash: Hash | undefined,\n dagRead: Read,\n): Promise<ClientMap> {\n if (!hash) {\n return new Map();\n }\n const chunk = await dagRead.getChunk(hash);\n return chunkDataToClientMap(chunk?.data);\n}\n\n/**\n * Used to signal that a client does not exist. Maybe it was garbage collected?\n */\nexport class ClientStateNotFoundError extends Error {\n name = 'ClientStateNotFoundError';\n readonly id: string;\n constructor(id: ClientID) {\n super(`Client state not found, id: ${id}`);\n this.id = id;\n }\n}\n\n/**\n * Throws a `ClientStateNotFoundError` if the client does not exist.\n */\nexport async function assertHasClientState(\n id: ClientID,\n dagRead: Read,\n): Promise<void> {\n if (!(await hasClientState(id, dagRead))) {\n throw new ClientStateNotFoundError(id);\n }\n}\n\nexport async function hasClientState(\n id: ClientID,\n dagRead: Read,\n): Promise<boolean> {\n return !!(await getClient(id, dagRead));\n}\n\nexport async function getClient(\n id: ClientID,\n dagRead: Read,\n): Promise<Client | undefined> {\n const clients = await getClients(dagRead);\n return clients.get(id);\n}\n\nexport async function mustGetClient(\n id: ClientID,\n dagRead: Read,\n): Promise<Client> {\n const client = await getClient(id, dagRead);\n if (!client) {\n throw new ClientStateNotFoundError(id);\n }\n return client;\n}\n\ntype InitClientV6Result = [\n client: ClientV6,\n hash: Hash,\n clientMap: ClientMap,\n newClientGroup: boolean,\n];\n\nexport function initClientV6(\n newClientID: ClientID,\n lc: LogContext,\n perdag: Store,\n mutatorNames: string[],\n indexes: IndexDefinitions,\n formatVersion: FormatVersion,\n enableClientGroupForking: boolean,\n): Promise<InitClientV6Result> {\n return withWrite(perdag, async dagWrite => {\n async function setClientsAndClientGroupAndCommit(\n basisHash: Hash | null,\n cookieJSON: FrozenCookie,\n valueHash: Hash,\n indexRecords: readonly IndexRecord[],\n ): Promise<InitClientV6Result> {\n const newSnapshotData = newSnapshotCommitDataDD31(\n basisHash,\n {},\n cookieJSON,\n valueHash,\n indexRecords,\n );\n const chunk = dagWrite.createChunk(\n newSnapshotData,\n getRefs(newSnapshotData),\n );\n\n const newClientGroupID = makeClientID();\n\n const newClient: ClientV6 = {\n heartbeatTimestampMs: Date.now(),\n refreshHashes: [chunk.hash],\n persistHash: null,\n clientGroupID: newClientGroupID,\n };\n\n const newClients = new Map(clients).set(newClientID, newClient);\n\n const clientGroup: ClientGroup = {\n headHash: chunk.hash,\n mutatorNames,\n indexes,\n mutationIDs: {},\n lastServerAckdMutationIDs: {},\n disabled: false,\n };\n\n await Promise.all([\n dagWrite.putChunk(chunk),\n setClients(newClients, dagWrite),\n setClientGroup(newClientGroupID, clientGroup, dagWrite),\n ]);\n\n return [newClient, chunk.hash, newClients, true];\n }\n\n const clients = await getClients(dagWrite);\n\n const res = await findMatchingClient(dagWrite, mutatorNames, indexes);\n if (res.type === FIND_MATCHING_CLIENT_TYPE_HEAD) {\n // We found a client group with matching mutators and indexes. We can\n // reuse it.\n const {clientGroupID, headHash} = res;\n\n const newClient: ClientV6 = {\n clientGroupID,\n refreshHashes: [headHash],\n heartbeatTimestampMs: Date.now(),\n persistHash: null,\n };\n const newClients = new Map(clients).set(newClientID, newClient);\n await setClients(newClients, dagWrite);\n\n return [newClient, headHash, newClients, false];\n }\n\n if (\n !enableClientGroupForking ||\n res.type === FIND_MATCHING_CLIENT_TYPE_NEW\n ) {\n // No client group to fork from. Create empty snapshot.\n const emptyBTreeChunk = dagWrite.createChunk(emptyDataNode, []);\n await dagWrite.putChunk(emptyBTreeChunk);\n\n // Create indexes\n const indexRecords: IndexRecord[] = [];\n\n // At this point the value of replicache is the empty tree so all index\n // maps will also be the empty tree.\n for (const [name, indexDefinition] of Object.entries(indexes)) {\n const chunkIndexDefinition = toChunkIndexDefinition(\n name,\n indexDefinition,\n );\n indexRecords.push({\n definition: chunkIndexDefinition,\n valueHash: emptyBTreeChunk.hash,\n });\n }\n\n return setClientsAndClientGroupAndCommit(\n null,\n null,\n emptyBTreeChunk.hash,\n indexRecords,\n );\n }\n\n // Now we create a new client and client group that we fork from the found\n // snapshot.\n assert(\n res.type === FIND_MATCHING_CLIENT_TYPE_FORK,\n 'Expected result type to be FORK',\n );\n\n const {snapshot} = res;\n\n // Create indexes\n const indexRecords: IndexRecord[] = [];\n const {valueHash, indexes: oldIndexes} = snapshot;\n const map = new BTreeRead(dagWrite, formatVersion, valueHash);\n\n for (const [name, indexDefinition] of Object.entries(indexes)) {\n const {prefix = '', jsonPointer, allowEmpty = false} = indexDefinition;\n const chunkIndexDefinition: ChunkIndexDefinition = {\n name,\n keyPrefix: prefix,\n jsonPointer,\n allowEmpty,\n };\n\n const oldIndex = findMatchingOldIndex(oldIndexes, chunkIndexDefinition);\n if (oldIndex) {\n indexRecords.push({\n definition: chunkIndexDefinition,\n valueHash: oldIndex.valueHash,\n });\n } else {\n const indexBTree = await createIndexBTree(\n lc,\n dagWrite,\n map,\n prefix,\n jsonPointer,\n allowEmpty,\n formatVersion,\n );\n indexRecords.push({\n definition: chunkIndexDefinition,\n valueHash: await indexBTree.flush(),\n });\n }\n }\n\n return setClientsAndClientGroupAndCommit(\n snapshot.meta.basisHash,\n snapshot.meta.cookieJSON,\n snapshot.valueHash,\n indexRecords,\n );\n });\n}\n\nfunction findMatchingOldIndex(\n oldIndexes: readonly IndexRecord[],\n chunkIndexDefinition: ChunkIndexDefinition,\n) {\n return oldIndexes.find(index =>\n chunkIndexDefinitionEqualIgnoreName(index.definition, chunkIndexDefinition),\n );\n}\n\nexport const FIND_MATCHING_CLIENT_TYPE_NEW = 0;\nexport const FIND_MATCHING_CLIENT_TYPE_FORK = 1;\nexport const FIND_MATCHING_CLIENT_TYPE_HEAD = 2;\n\nexport type FindMatchingClientResult =\n | {\n type: typeof FIND_MATCHING_CLIENT_TYPE_NEW;\n }\n | {\n type: typeof FIND_MATCHING_CLIENT_TYPE_FORK;\n snapshot: Commit<SnapshotMetaDD31>;\n }\n | {\n type: typeof FIND_MATCHING_CLIENT_TYPE_HEAD;\n clientGroupID: ClientGroupID;\n headHash: Hash;\n };\n\nexport async function findMatchingClient(\n dagRead: Read,\n mutatorNames: string[],\n indexes: IndexDefinitions,\n): Promise<FindMatchingClientResult> {\n let newestCookie: FrozenCookie | undefined;\n let bestSnapshot: Commit<SnapshotMetaDD31> | undefined;\n const mutatorNamesSet = new Set(mutatorNames);\n\n const clientGroups = await getClientGroups(dagRead);\n for (const [clientGroupID, clientGroup] of clientGroups) {\n if (\n !clientGroup.disabled &&\n mutatorNamesEqual(mutatorNamesSet, clientGroup.mutatorNames) &&\n indexDefinitionsEqual(indexes, clientGroup.indexes)\n ) {\n // exact match\n return {\n type: FIND_MATCHING_CLIENT_TYPE_HEAD,\n clientGroupID,\n headHash: clientGroup.headHash,\n };\n }\n\n const clientGroupSnapshotCommit = await baseSnapshotFromHash(\n clientGroup.headHash,\n dagRead,\n );\n assertSnapshotCommitDD31(clientGroupSnapshotCommit);\n\n const {cookieJSON} = clientGroupSnapshotCommit.meta;\n if (\n newestCookie === undefined ||\n compareCookies(cookieJSON, newestCookie) > 0\n ) {\n newestCookie = cookieJSON;\n bestSnapshot = clientGroupSnapshotCommit;\n }\n }\n\n if (bestSnapshot) {\n return {\n type: FIND_MATCHING_CLIENT_TYPE_FORK,\n snapshot: bestSnapshot,\n };\n }\n\n return {type: FIND_MATCHING_CLIENT_TYPE_NEW};\n}\n\nfunction getRefsForClients(clients: ClientMap): Refs {\n const refs: Set<Hash> = new Set();\n for (const client of clients.values()) {\n if (isClientV6(client)) {\n for (const hash of client.refreshHashes) {\n refs.add(hash);\n }\n if (client.persistHash) {\n refs.add(client.persistHash);\n }\n } else {\n refs.add(client.headHash);\n if (client.tempRefreshHash) {\n refs.add(client.tempRefreshHash);\n }\n }\n }\n return toRefs(refs);\n}\n\nexport async function getClientGroupForClient(\n clientID: ClientID,\n read: Read,\n): Promise<ClientGroup | undefined> {\n const clientGroupID = await getClientGroupIDForClient(clientID, read);\n if (!clientGroupID) {\n return undefined;\n }\n return getClientGroup(clientGroupID, read);\n}\n\nexport async function getClientGroupIDForClient(\n clientID: ClientID,\n read: Read,\n): Promise<ClientGroupID | undefined> {\n const client = await getClient(clientID, read);\n return client?.clientGroupID;\n}\n\n/**\n * Adds a Client to the ClientMap and updates the 'clients' head to point at\n * the updated clients.\n */\nexport async function setClient(\n clientID: ClientID,\n client: Client,\n dagWrite: Write,\n): Promise<Hash> {\n const clients = await getClients(dagWrite);\n const newClients = new Map(clients).set(clientID, client);\n return setClients(newClients, dagWrite);\n}\n\n/**\n * Sets the ClientMap and updates the 'clients' head top point at the new\n * clients.\n */\nexport async function setClients(\n clients: ClientMap,\n dagWrite: Write,\n): Promise<Hash> {\n const chunkData = clientMapToChunkData(clients, dagWrite);\n const chunk = dagWrite.createChunk(chunkData, getRefsForClients(clients));\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(CLIENTS_HEAD_NAME, chunk.hash);\n return chunk.hash;\n}\n\n/**\n * Callback function for when Replicache has deleted one or more clients.\n */\nexport type OnClientsDeleted = (\n deletedClients: DeletedClients,\n) => Promise<void>;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect-idb-databases.js","names":[],"sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"sourcesContent":["import type {LogContext, LogLevel, LogSink} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport {StoreImpl} from '../dag/store-impl.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n getDeletedClients,\n mergeDeletedClients,\n normalizeDeletedClients,\n type DeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {getKVStoreProvider} from '../get-kv-store-provider.ts';\nimport {assertHash, newRandomHash} from '../hash.ts';\nimport type {CreateStore, DropStore, StoreProvider} from '../kv/store.ts';\nimport {createLogContext} from '../log-options.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n} from './client-groups.ts';\nimport type {OnClientsDeleted} from './clients.ts';\nimport {getClients} from './clients.ts';\nimport type {IndexedDBDatabase} from './idb-databases-store.ts';\nimport {IDBDatabasesStore} from './idb-databases-store.ts';\n\n/**\n * How frequently to try to collect\n */\nexport const COLLECT_IDB_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * We delay the initial collection to prevent doing it at startup.\n */\nexport const INITIAL_COLLECT_IDB_DELAY = 5 * 60 * 1000; // 5 minutes\n\nexport function initCollectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n kvStoreProvider: StoreProvider,\n collectInterval: number,\n initialCollectDelay: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n): void {\n let initial = true;\n initBgIntervalProcess(\n 'CollectIDBDatabases',\n async () => {\n await collectIDBDatabases(\n idbDatabasesStore,\n Date.now(),\n maxAge,\n kvStoreProvider,\n enableMutationRecovery,\n onClientsDeleted,\n );\n },\n () => {\n if (initial) {\n initial = false;\n return initialCollectDelay;\n }\n return collectInterval;\n },\n lc,\n signal,\n );\n}\n\n/**\n * Collects IDB databases that are no longer needed.\n */\nexport async function collectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n now: number,\n maxAge: number,\n kvStoreProvider: StoreProvider,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n newDagStore = defaultNewDagStore,\n): Promise<void> {\n const databases = await idbDatabasesStore.getDatabases();\n\n const dbs = Object.values(databases);\n const collectResults = await Promise.all(\n dbs.map(\n async db =>\n [\n db.name,\n await gatherDatabaseInfoForCollect(\n db,\n now,\n maxAge,\n enableMutationRecovery,\n kvStoreProvider.create,\n newDagStore,\n ),\n ] as const,\n ),\n );\n\n const dbNamesToRemove: string[] = [];\n const dbNamesToKeep: string[] = [];\n const deletedClientsToRemove: WritableDeletedClients = [];\n for (const [dbName, [canCollect, deletedClients]] of collectResults) {\n if (canCollect) {\n dbNamesToRemove.push(dbName);\n deletedClientsToRemove.push(...deletedClients);\n } else {\n dbNamesToKeep.push(dbName);\n }\n }\n\n const {errors} = await dropDatabases(\n idbDatabasesStore,\n dbNamesToRemove,\n kvStoreProvider.drop,\n );\n if (errors.length) {\n throw errors[0];\n }\n\n if (deletedClientsToRemove.length > 0) {\n // Add the deleted clients to all the dbs that survived the collection.\n let allDeletedClients: DeletedClients = deletedClientsToRemove;\n for (const name of dbNamesToKeep) {\n await withWrite(\n newDagStore(name, kvStoreProvider.create),\n async dagWrite => {\n const newDeletedClients = await addDeletedClients(\n dagWrite,\n deletedClientsToRemove,\n );\n\n allDeletedClients = mergeDeletedClients(\n allDeletedClients,\n newDeletedClients,\n );\n },\n );\n }\n // normalize and dedupe\n const normalizedDeletedClients = normalizeDeletedClients(allDeletedClients);\n\n // Call the callback with the normalized deleted clients\n await onClientsDeleted(normalizedDeletedClients);\n }\n}\n\nasync function dropDatabaseInternal(\n name: string,\n idbDatabasesStore: IDBDatabasesStore,\n kvDropStore: DropStore,\n) {\n await kvDropStore(name);\n await idbDatabasesStore.deleteDatabases([name]);\n}\n\nasync function dropDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n namesToRemove: string[],\n kvDropStore: DropStore,\n): Promise<{dropped: string[]; errors: unknown[]}> {\n // Try to remove the databases in parallel. Don't let a single reject fail the\n // other ones. We will check for failures afterwards.\n const dropStoreResults = await Promise.allSettled(\n namesToRemove.map(async name => {\n await dropDatabaseInternal(name, idbDatabasesStore, kvDropStore);\n return name;\n }),\n );\n\n const dropped: string[] = [];\n const errors: unknown[] = [];\n for (const result of dropStoreResults) {\n if (result.status === 'fulfilled') {\n dropped.push(result.value);\n } else {\n errors.push(result.reason);\n }\n }\n\n return {dropped, errors};\n}\n\nfunction defaultNewDagStore(name: string, kvCreateStore: CreateStore): Store {\n const perKvStore = kvCreateStore(name);\n return new StoreImpl(perKvStore, newRandomHash, assertHash);\n}\n\n/**\n * If any client has a recent heartbeat or there are pending mutations we\n * return `[false]`. Otherwise we return `[true, deletedClients]`.\n */\nfunction gatherDatabaseInfoForCollect(\n db: IndexedDBDatabase,\n now: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n kvCreateStore: CreateStore,\n newDagStore: typeof defaultNewDagStore,\n): MaybePromise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n if (db.replicacheFormatVersion > FormatVersion.Latest) {\n return [false];\n }\n\n // If increase the format version we need to decide how to deal with this\n // logic.\n assert(\n db.replicacheFormatVersion === FormatVersion.DD31 ||\n db.replicacheFormatVersion === FormatVersion.V6 ||\n db.replicacheFormatVersion === FormatVersion.V7,\n () =>\n `Expected replicacheFormatVersion to be DD31, V6, or V7, got ${db.replicacheFormatVersion}`,\n );\n return canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery,\n newDagStore(db.name, kvCreateStore),\n now,\n maxAge,\n );\n}\n\n/**\n * Options for `dropDatabase` and `dropAllDatabases`.\n */\nexport type DropDatabaseOptions = {\n /**\n * Allows providing a custom implementation of the underlying storage layer.\n * Default is `'idb'`.\n */\n kvStore?: 'idb' | 'mem' | StoreProvider | undefined;\n /**\n * Determines how much logging to do. When this is set to `'debug'`,\n * Replicache will also log `'info'` and `'error'` messages. When set to\n * `'info'` we log `'info'` and `'error'` but not `'debug'`. When set to\n * `'error'` we only log `'error'` messages.\n * Default is `'info'`.\n */\n logLevel?: LogLevel | undefined;\n /**\n * Enables custom handling of logs.\n *\n * By default logs are logged to the console. If you would like logs to be\n * sent elsewhere (e.g. to a cloud logging service like DataDog) you can\n * provide an array of {@link LogSink}s. Logs at or above\n * {@link DropDatabaseOptions.logLevel} are sent to each of these {@link LogSink}s.\n * If you would still like logs to go to the console, include\n * `consoleLogSink` in the array.\n *\n * ```ts\n * logSinks: [consoleLogSink, myCloudLogSink],\n * ```\n * Default is `[consoleLogSink]`.\n */\n logSinks?: LogSink[] | undefined;\n};\n\n/**\n * Drops the specified database.\n * @param dbName The name of the database to drop.\n * @param opts Options for dropping the database.\n */\nexport async function dropDatabase(dbName: string, opts?: DropDatabaseOptions) {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabase: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n await dropDatabaseInternal(\n dbName,\n new IDBDatabasesStore(kvStoreProvider.create),\n kvStoreProvider.drop,\n );\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n */\nexport async function dropAllDatabases(opts?: DropDatabaseOptions): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropAllDatabases: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n const store = new IDBDatabasesStore(kvStoreProvider.create);\n const databases = await store.getDatabases();\n const dbNames = Object.values(databases).map(db => db.name);\n return dropDatabases(store, dbNames, kvStoreProvider.drop);\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n *\n * @deprecated Use `dropAllDatabases` instead.\n */\nexport function deleteAllReplicacheData(opts?: DropDatabaseOptions) {\n return dropAllDatabases(opts);\n}\n\n/**\n * If there are pending mutations in any of the clients in this db we return\n * `[false]`. If any client has a recent heartbeat we also return `[false]`.\n * Otherwise we return `[true, deletedClients]`.\n */\nfunction canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery: boolean,\n perdag: Store,\n now: number,\n maxAge: number,\n): Promise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n return withRead(perdag, async read => {\n // If mutation recovery is disabled we do not care if there are pending\n // mutations when we decide if we can collect the database.\n if (enableMutationRecovery) {\n const clientGroups = await getClientGroups(read);\n for (const clientGroup of clientGroups.values()) {\n if (clientGroupHasPendingMutations(clientGroup)) {\n return [false];\n }\n }\n }\n\n const clients = await getClients(read);\n\n // Don't collect if any client has a recent heartbeat (is still active).\n // This is defense in depth - normally lastOpenedTimestampMS is kept fresh\n // by the heartbeat, but this check protects against edge cases.\n for (const [, client] of clients) {\n if (now - client.heartbeatTimestampMs < maxAge) {\n return [false];\n }\n }\n\n const existingDeletedClients = await getDeletedClients(read);\n const deletedClients: WritableDeletedClients = [...existingDeletedClients];\n\n // Add all current clients to the deleted clients list\n for (const [clientID, client] of clients) {\n deletedClients.push({\n clientID,\n clientGroupID: client.clientGroupID,\n });\n }\n\n // The normalization (deduping and sorting) will be done when storing\n return [true, deletedClients];\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collect-idb-databases.js","names":[],"sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"sourcesContent":["import type {LogContext, LogLevel, LogSink} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport {StoreImpl} from '../dag/store-impl.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n getDeletedClients,\n mergeDeletedClients,\n normalizeDeletedClients,\n type DeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {getKVStoreProvider} from '../get-kv-store-provider.ts';\nimport {assertHash, newRandomHash} from '../hash.ts';\nimport type {CreateStore, DropStore, StoreProvider} from '../kv/store.ts';\nimport {createLogContext} from '../log-options.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n} from './client-groups.ts';\nimport type {OnClientsDeleted} from './clients.ts';\nimport {getClients} from './clients.ts';\nimport type {IndexedDBDatabase} from './idb-databases-store.ts';\nimport {IDBDatabasesStore} from './idb-databases-store.ts';\n\n/**\n * How frequently to try to collect\n */\nexport const COLLECT_IDB_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * We delay the initial collection to prevent doing it at startup.\n */\nexport const INITIAL_COLLECT_IDB_DELAY = 5 * 60 * 1000; // 5 minutes\n\nexport function initCollectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n kvStoreProvider: StoreProvider,\n collectInterval: number,\n initialCollectDelay: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n): void {\n let initial = true;\n initBgIntervalProcess(\n 'CollectIDBDatabases',\n async () => {\n await collectIDBDatabases(\n idbDatabasesStore,\n Date.now(),\n maxAge,\n kvStoreProvider,\n enableMutationRecovery,\n onClientsDeleted,\n );\n },\n () => {\n if (initial) {\n initial = false;\n return initialCollectDelay;\n }\n return collectInterval;\n },\n lc,\n signal,\n );\n}\n\n/**\n * Collects IDB databases that are no longer needed.\n */\nexport async function collectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n now: number,\n maxAge: number,\n kvStoreProvider: StoreProvider,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n newDagStore = defaultNewDagStore,\n): Promise<void> {\n const databases = await idbDatabasesStore.getDatabases();\n\n const dbs = Object.values(databases);\n const collectResults = await Promise.all(\n dbs.map(\n async db =>\n [\n db.name,\n await gatherDatabaseInfoForCollect(\n db,\n now,\n maxAge,\n enableMutationRecovery,\n kvStoreProvider.create,\n newDagStore,\n ),\n ] as const,\n ),\n );\n\n const dbNamesToRemove: string[] = [];\n const dbNamesToKeep: string[] = [];\n const deletedClientsToRemove: WritableDeletedClients = [];\n for (const [dbName, [canCollect, deletedClients]] of collectResults) {\n if (canCollect) {\n dbNamesToRemove.push(dbName);\n deletedClientsToRemove.push(...deletedClients);\n } else {\n dbNamesToKeep.push(dbName);\n }\n }\n\n const {errors} = await dropDatabases(\n idbDatabasesStore,\n dbNamesToRemove,\n kvStoreProvider.drop,\n );\n if (errors.length) {\n throw errors[0];\n }\n\n if (deletedClientsToRemove.length > 0) {\n // Add the deleted clients to all the dbs that survived the collection.\n let allDeletedClients: DeletedClients = deletedClientsToRemove;\n for (const name of dbNamesToKeep) {\n await withWrite(\n newDagStore(name, kvStoreProvider.create),\n async dagWrite => {\n const newDeletedClients = await addDeletedClients(\n dagWrite,\n deletedClientsToRemove,\n );\n\n allDeletedClients = mergeDeletedClients(\n allDeletedClients,\n newDeletedClients,\n );\n },\n );\n }\n // normalize and dedupe\n const normalizedDeletedClients = normalizeDeletedClients(allDeletedClients);\n\n // Call the callback with the normalized deleted clients\n await onClientsDeleted(normalizedDeletedClients);\n }\n}\n\nasync function dropDatabaseInternal(\n name: string,\n idbDatabasesStore: IDBDatabasesStore,\n kvDropStore: DropStore,\n) {\n await kvDropStore(name);\n await idbDatabasesStore.deleteDatabases([name]);\n}\n\nasync function dropDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n namesToRemove: string[],\n kvDropStore: DropStore,\n): Promise<{dropped: string[]; errors: unknown[]}> {\n // Try to remove the databases in parallel. Don't let a single reject fail the\n // other ones. We will check for failures afterwards.\n const dropStoreResults = await Promise.allSettled(\n namesToRemove.map(async name => {\n await dropDatabaseInternal(name, idbDatabasesStore, kvDropStore);\n return name;\n }),\n );\n\n const dropped: string[] = [];\n const errors: unknown[] = [];\n for (const result of dropStoreResults) {\n if (result.status === 'fulfilled') {\n dropped.push(result.value);\n } else {\n errors.push(result.reason);\n }\n }\n\n return {dropped, errors};\n}\n\nfunction defaultNewDagStore(name: string, kvCreateStore: CreateStore): Store {\n const perKvStore = kvCreateStore(name);\n return new StoreImpl(perKvStore, newRandomHash, assertHash);\n}\n\n/**\n * If any client has a recent heartbeat or there are pending mutations we\n * return `[false]`. Otherwise we return `[true, deletedClients]`.\n */\nfunction gatherDatabaseInfoForCollect(\n db: IndexedDBDatabase,\n now: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n kvCreateStore: CreateStore,\n newDagStore: typeof defaultNewDagStore,\n): MaybePromise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n if (db.replicacheFormatVersion > FormatVersion.Latest) {\n return [false];\n }\n\n // If increase the format version we need to decide how to deal with this\n // logic.\n assert(\n db.replicacheFormatVersion === FormatVersion.DD31 ||\n db.replicacheFormatVersion === FormatVersion.V6 ||\n db.replicacheFormatVersion === FormatVersion.V7,\n () =>\n `Expected replicacheFormatVersion to be DD31, V6, or V7, got ${db.replicacheFormatVersion}`,\n );\n return canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery,\n newDagStore(db.name, kvCreateStore),\n now,\n maxAge,\n );\n}\n\n/**\n * Options for `dropDatabase` and `dropAllDatabases`.\n */\nexport type DropDatabaseOptions = {\n /**\n * Allows providing a custom implementation of the underlying storage layer.\n * Default is `'idb'`.\n */\n kvStore?: 'idb' | 'mem' | StoreProvider | undefined;\n /**\n * Determines how much logging to do. When this is set to `'debug'`,\n * Replicache will also log `'info'` and `'error'` messages. When set to\n * `'info'` we log `'info'` and `'error'` but not `'debug'`. When set to\n * `'error'` we only log `'error'` messages.\n * Default is `'info'`.\n */\n logLevel?: LogLevel | undefined;\n /**\n * Enables custom handling of logs.\n *\n * By default logs are logged to the console. If you would like logs to be\n * sent elsewhere (e.g. to a cloud logging service like DataDog) you can\n * provide an array of {@link LogSink}s. Logs at or above\n * {@link DropDatabaseOptions.logLevel} are sent to each of these {@link LogSink}s.\n * If you would still like logs to go to the console, include\n * `consoleLogSink` in the array.\n *\n * ```ts\n * logSinks: [consoleLogSink, myCloudLogSink],\n * ```\n * Default is `[consoleLogSink]`.\n */\n logSinks?: LogSink[] | undefined;\n};\n\n/**\n * Drops the specified database.\n * @param dbName The name of the database to drop.\n * @param opts Options for dropping the database.\n */\nexport async function dropDatabase(dbName: string, opts?: DropDatabaseOptions) {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabase: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n await dropDatabaseInternal(\n dbName,\n new IDBDatabasesStore(kvStoreProvider.create),\n kvStoreProvider.drop,\n );\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n */\nexport async function dropAllDatabases(opts?: DropDatabaseOptions): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropAllDatabases: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n const store = new IDBDatabasesStore(kvStoreProvider.create);\n const databases = await store.getDatabases();\n const dbNames = Object.values(databases).map(db => db.name);\n return dropDatabases(store, dbNames, kvStoreProvider.drop);\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n *\n * @deprecated Use `dropAllDatabases` instead.\n */\nexport function deleteAllReplicacheData(opts?: DropDatabaseOptions) {\n return dropAllDatabases(opts);\n}\n\n/**\n * If there are pending mutations in any of the clients in this db we return\n * `[false]`. If any client has a recent heartbeat we also return `[false]`.\n * Otherwise we return `[true, deletedClients]`.\n */\nfunction canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery: boolean,\n perdag: Store,\n now: number,\n maxAge: number,\n): Promise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n return withRead(perdag, async read => {\n // If mutation recovery is disabled we do not care if there are pending\n // mutations when we decide if we can collect the database.\n if (enableMutationRecovery) {\n const clientGroups = await getClientGroups(read);\n for (const clientGroup of clientGroups.values()) {\n if (clientGroupHasPendingMutations(clientGroup)) {\n return [false];\n }\n }\n }\n\n const clients = await getClients(read);\n\n // Don't collect if any client has a recent heartbeat (is still active).\n // This is defense in depth - normally lastOpenedTimestampMS is kept fresh\n // by the heartbeat, but this check protects against edge cases.\n for (const [, client] of clients) {\n if (now - client.heartbeatTimestampMs < maxAge) {\n return [false];\n }\n }\n\n const existingDeletedClients = await getDeletedClients(read);\n const deletedClients: WritableDeletedClients = [...existingDeletedClients];\n\n // Add all current clients to the deleted clients list\n for (const [clientID, client] of clients) {\n deletedClients.push({\n clientID,\n clientGroupID: client.clientGroupID,\n });\n }\n\n // The normalization (deduping and sorting) will be done when storing\n return [true, deletedClients];\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gather-mem-only-visitor.js","names":["#gatheredChunks","#lazyRead"],"sources":["../../../../../replicache/src/persist/gather-mem-only-visitor.ts"],"sourcesContent":["import {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport type {Chunk} from '../dag/chunk.ts';\nimport type {LazyRead} from '../dag/lazy-store.ts';\nimport {Visitor} from '../dag/visitor.ts';\nimport type {Hash} from '../hash.ts';\n\nexport class GatherMemoryOnlyVisitor extends Visitor {\n readonly #gatheredChunks: Map<Hash, Chunk> = new Map();\n readonly #lazyRead: LazyRead;\n\n constructor(dagRead: LazyRead) {\n super(dagRead);\n this.#lazyRead = dagRead;\n }\n\n get gatheredChunks(): ReadonlyMap<Hash, Chunk> {\n return this.#gatheredChunks;\n }\n\n override visit(h: Hash): Promise<void> {\n if (!this.#lazyRead.isMemOnlyChunkHash(h)) {\n // Not a memory-only hash, no need to visit anything else.\n return promiseVoid;\n }\n return super.visit(h);\n }\n\n override visitChunk(chunk: Chunk): Promise<void> {\n this.#gatheredChunks.set(chunk.hash, chunk);\n return super.visitChunk(chunk);\n }\n}\n"],"mappings":";;;AAMA,IAAa,0BAAb,cAA6C,QAAQ;CACnD,kCAA6C,IAAI,
|
|
1
|
+
{"version":3,"file":"gather-mem-only-visitor.js","names":["#gatheredChunks","#lazyRead"],"sources":["../../../../../replicache/src/persist/gather-mem-only-visitor.ts"],"sourcesContent":["import {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport type {Chunk} from '../dag/chunk.ts';\nimport type {LazyRead} from '../dag/lazy-store.ts';\nimport {Visitor} from '../dag/visitor.ts';\nimport type {Hash} from '../hash.ts';\n\nexport class GatherMemoryOnlyVisitor extends Visitor {\n readonly #gatheredChunks: Map<Hash, Chunk> = new Map();\n readonly #lazyRead: LazyRead;\n\n constructor(dagRead: LazyRead) {\n super(dagRead);\n this.#lazyRead = dagRead;\n }\n\n get gatheredChunks(): ReadonlyMap<Hash, Chunk> {\n return this.#gatheredChunks;\n }\n\n override visit(h: Hash): Promise<void> {\n if (!this.#lazyRead.isMemOnlyChunkHash(h)) {\n // Not a memory-only hash, no need to visit anything else.\n return promiseVoid;\n }\n return super.visit(h);\n }\n\n override visitChunk(chunk: Chunk): Promise<void> {\n this.#gatheredChunks.set(chunk.hash, chunk);\n return super.visitChunk(chunk);\n }\n}\n"],"mappings":";;;AAMA,IAAa,0BAAb,cAA6C,QAAQ;CACnD,kCAA6C,IAAI,IAAI;CACrD;CAEA,YAAY,SAAmB;EAC7B,MAAM,OAAO;EACb,KAAKC,YAAY;CACnB;CAEA,IAAI,iBAA2C;EAC7C,OAAO,KAAKD;CACd;CAEA,MAAe,GAAwB;EACrC,IAAI,CAAC,KAAKC,UAAU,mBAAmB,CAAC,GAEtC,OAAO;EAET,OAAO,MAAM,MAAM,CAAC;CACtB;CAEA,WAAoB,OAA6B;EAC/C,KAAKD,gBAAgB,IAAI,MAAM,MAAM,KAAK;EAC1C,OAAO,MAAM,WAAW,KAAK;CAC/B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gather-not-cached-visitor.js","names":["#gatheredChunks","#lazyStore","#gatherSizeLimit","#getSizeOfChunk","#gatheredChunksTotalSize"],"sources":["../../../../../replicache/src/persist/gather-not-cached-visitor.ts"],"sourcesContent":["import {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {getSizeOfValue} from '../../../shared/src/size-of-value.ts';\nimport type {Chunk} from '../dag/chunk.ts';\nimport type {LazyStore} from '../dag/lazy-store.ts';\nimport type {Read} from '../dag/store.ts';\nimport {Visitor} from '../dag/visitor.ts';\nimport type {Hash} from '../hash.ts';\n\nexport type ChunkWithSize = {chunk: Chunk; size: number};\n\nexport class GatherNotCachedVisitor extends Visitor {\n readonly #gatheredChunks: Map<Hash, ChunkWithSize> = new Map();\n #gatheredChunksTotalSize = 0;\n readonly #lazyStore: LazyStore;\n readonly #gatherSizeLimit: number;\n readonly #getSizeOfChunk: (chunk: Chunk) => number;\n\n constructor(\n dagRead: Read,\n lazyStore: LazyStore,\n gatherSizeLimit: number,\n getSizeOfChunk: (chunk: Chunk) => number = getSizeOfValue,\n ) {\n super(dagRead);\n this.#lazyStore = lazyStore;\n this.#gatherSizeLimit = gatherSizeLimit;\n this.#getSizeOfChunk = getSizeOfChunk;\n }\n\n get gatheredChunks(): ReadonlyMap<Hash, ChunkWithSize> {\n return this.#gatheredChunks;\n }\n\n override visit(h: Hash): Promise<void> {\n if (\n this.#gatheredChunksTotalSize >= this.#gatherSizeLimit ||\n this.#lazyStore.isCached(h)\n ) {\n return promiseVoid;\n }\n return super.visit(h);\n }\n\n override visitChunk(chunk: Chunk): Promise<void> {\n if (this.#gatheredChunksTotalSize < this.#gatherSizeLimit) {\n const size = this.#getSizeOfChunk(chunk);\n this.#gatheredChunks.set(chunk.hash, {chunk, size});\n this.#gatheredChunksTotalSize += size;\n }\n\n return super.visitChunk(chunk);\n }\n}\n"],"mappings":";;;;AAUA,IAAa,yBAAb,cAA4C,QAAQ;CAClD,kCAAqD,IAAI,
|
|
1
|
+
{"version":3,"file":"gather-not-cached-visitor.js","names":["#gatheredChunks","#lazyStore","#gatherSizeLimit","#getSizeOfChunk","#gatheredChunksTotalSize"],"sources":["../../../../../replicache/src/persist/gather-not-cached-visitor.ts"],"sourcesContent":["import {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {getSizeOfValue} from '../../../shared/src/size-of-value.ts';\nimport type {Chunk} from '../dag/chunk.ts';\nimport type {LazyStore} from '../dag/lazy-store.ts';\nimport type {Read} from '../dag/store.ts';\nimport {Visitor} from '../dag/visitor.ts';\nimport type {Hash} from '../hash.ts';\n\nexport type ChunkWithSize = {chunk: Chunk; size: number};\n\nexport class GatherNotCachedVisitor extends Visitor {\n readonly #gatheredChunks: Map<Hash, ChunkWithSize> = new Map();\n #gatheredChunksTotalSize = 0;\n readonly #lazyStore: LazyStore;\n readonly #gatherSizeLimit: number;\n readonly #getSizeOfChunk: (chunk: Chunk) => number;\n\n constructor(\n dagRead: Read,\n lazyStore: LazyStore,\n gatherSizeLimit: number,\n getSizeOfChunk: (chunk: Chunk) => number = getSizeOfValue,\n ) {\n super(dagRead);\n this.#lazyStore = lazyStore;\n this.#gatherSizeLimit = gatherSizeLimit;\n this.#getSizeOfChunk = getSizeOfChunk;\n }\n\n get gatheredChunks(): ReadonlyMap<Hash, ChunkWithSize> {\n return this.#gatheredChunks;\n }\n\n override visit(h: Hash): Promise<void> {\n if (\n this.#gatheredChunksTotalSize >= this.#gatherSizeLimit ||\n this.#lazyStore.isCached(h)\n ) {\n return promiseVoid;\n }\n return super.visit(h);\n }\n\n override visitChunk(chunk: Chunk): Promise<void> {\n if (this.#gatheredChunksTotalSize < this.#gatherSizeLimit) {\n const size = this.#getSizeOfChunk(chunk);\n this.#gatheredChunks.set(chunk.hash, {chunk, size});\n this.#gatheredChunksTotalSize += size;\n }\n\n return super.visitChunk(chunk);\n }\n}\n"],"mappings":";;;;AAUA,IAAa,yBAAb,cAA4C,QAAQ;CAClD,kCAAqD,IAAI,IAAI;CAC7D,2BAA2B;CAC3B;CACA;CACA;CAEA,YACE,SACA,WACA,iBACA,iBAA2C,gBAC3C;EACA,MAAM,OAAO;EACb,KAAKC,aAAa;EAClB,KAAKC,mBAAmB;EACxB,KAAKC,kBAAkB;CACzB;CAEA,IAAI,iBAAmD;EACrD,OAAO,KAAKH;CACd;CAEA,MAAe,GAAwB;EACrC,IACE,KAAKI,4BAA4B,KAAKF,oBACtC,KAAKD,WAAW,SAAS,CAAC,GAE1B,OAAO;EAET,OAAO,MAAM,MAAM,CAAC;CACtB;CAEA,WAAoB,OAA6B;EAC/C,IAAI,KAAKG,2BAA2B,KAAKF,kBAAkB;GACzD,MAAM,OAAO,KAAKC,gBAAgB,KAAK;GACvC,KAAKH,gBAAgB,IAAI,MAAM,MAAM;IAAC;IAAO;GAAI,CAAC;GAClD,KAAKI,4BAA4B;EACnC;EAEA,OAAO,MAAM,WAAW,KAAK;CAC/B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat.js","names":[],"sources":["../../../../../replicache/src/persist/heartbeat.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n type ClientMap,\n ClientStateNotFoundError,\n getClients,\n setClients,\n} from './clients.ts';\n\nexport const HEARTBEAT_INTERVAL = 60 * 1000;\n\nexport let latestHeartbeatUpdate: Promise<ClientMap> | undefined;\n\nexport function startHeartbeats(\n clientID: ClientID,\n dagStore: Store,\n onClientStateNotFound: () => void,\n heartbeatIntervalMs: number,\n lc: LogContext,\n signal: AbortSignal,\n): void {\n initBgIntervalProcess(\n 'Heartbeat',\n async () => {\n latestHeartbeatUpdate = writeHeartbeat(clientID, dagStore);\n try {\n return await latestHeartbeatUpdate;\n } catch (e) {\n if (e instanceof ClientStateNotFoundError) {\n onClientStateNotFound();\n return;\n }\n throw e;\n }\n },\n () => heartbeatIntervalMs,\n lc,\n signal,\n );\n}\n\nexport function writeHeartbeat(\n clientID: ClientID,\n dagStore: Store,\n): Promise<ClientMap> {\n return withWrite(dagStore, async dagWrite => {\n const clients = await getClients(dagWrite);\n const client = clients.get(clientID);\n if (!client) {\n throw new ClientStateNotFoundError(clientID);\n }\n\n const newClient = {\n ...client,\n heartbeatTimestampMs: Date.now(),\n };\n const newClients = new Map(clients).set(clientID, newClient);\n\n await setClients(newClients, dagWrite);\n return newClients;\n });\n}\n"],"mappings":";;;;AAYA,IAAa,qBAAqB,KAAK;AAEvC,IAAW;AAEX,SAAgB,gBACd,UACA,UACA,uBACA,qBACA,IACA,QACM;
|
|
1
|
+
{"version":3,"file":"heartbeat.js","names":[],"sources":["../../../../../replicache/src/persist/heartbeat.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n type ClientMap,\n ClientStateNotFoundError,\n getClients,\n setClients,\n} from './clients.ts';\n\nexport const HEARTBEAT_INTERVAL = 60 * 1000;\n\nexport let latestHeartbeatUpdate: Promise<ClientMap> | undefined;\n\nexport function startHeartbeats(\n clientID: ClientID,\n dagStore: Store,\n onClientStateNotFound: () => void,\n heartbeatIntervalMs: number,\n lc: LogContext,\n signal: AbortSignal,\n): void {\n initBgIntervalProcess(\n 'Heartbeat',\n async () => {\n latestHeartbeatUpdate = writeHeartbeat(clientID, dagStore);\n try {\n return await latestHeartbeatUpdate;\n } catch (e) {\n if (e instanceof ClientStateNotFoundError) {\n onClientStateNotFound();\n return;\n }\n throw e;\n }\n },\n () => heartbeatIntervalMs,\n lc,\n signal,\n );\n}\n\nexport function writeHeartbeat(\n clientID: ClientID,\n dagStore: Store,\n): Promise<ClientMap> {\n return withWrite(dagStore, async dagWrite => {\n const clients = await getClients(dagWrite);\n const client = clients.get(clientID);\n if (!client) {\n throw new ClientStateNotFoundError(clientID);\n }\n\n const newClient = {\n ...client,\n heartbeatTimestampMs: Date.now(),\n };\n const newClients = new Map(clients).set(clientID, newClient);\n\n await setClients(newClients, dagWrite);\n return newClients;\n });\n}\n"],"mappings":";;;;AAYA,IAAa,qBAAqB,KAAK;AAEvC,IAAW;AAEX,SAAgB,gBACd,UACA,UACA,uBACA,qBACA,IACA,QACM;CACN,sBACE,aACA,YAAY;EACV,wBAAwB,eAAe,UAAU,QAAQ;EACzD,IAAI;GACF,OAAO,MAAM;EACf,SAAS,GAAG;GACV,IAAI,aAAa,0BAA0B;IACzC,sBAAsB;IACtB;GACF;GACA,MAAM;EACR;CACF,SACM,qBACN,IACA,MACF;AACF;AAEA,SAAgB,eACd,UACA,UACoB;CACpB,OAAO,UAAU,UAAU,OAAM,aAAY;EAC3C,MAAM,UAAU,MAAM,WAAW,QAAQ;EACzC,MAAM,SAAS,QAAQ,IAAI,QAAQ;EACnC,IAAI,CAAC,QACH,MAAM,IAAI,yBAAyB,QAAQ;EAG7C,MAAM,YAAY;GAChB,GAAG;GACH,sBAAsB,KAAK,IAAI;EACjC;EACA,MAAM,aAAa,IAAI,IAAI,OAAO,EAAE,IAAI,UAAU,SAAS;EAE3D,MAAM,WAAW,YAAY,QAAQ;EACrC,OAAO;CACT,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idb-databases-store-db-name.js","names":[],"sources":["../../../../../replicache/src/persist/idb-databases-store-db-name.ts"],"sourcesContent":["import {randomUint64} from '../../../shared/src/random-uint64.ts';\nimport {dropIDBStoreWithMemFallback} from '../kv/idb-store-with-mem-fallback.ts';\n\nconst IDB_DATABASES_VERSION = 0;\nconst IDB_DATABASES_DB_NAME = 'replicache-dbs-v' + IDB_DATABASES_VERSION;\n\nlet testNamespace = '';\n\n/** Namespace db name in test to isolate tests' indexeddb state. */\nexport function setupForTest(): void {\n testNamespace = randomUint64().toString(36);\n}\n\nexport function teardownForTest(): Promise<void> {\n const idbDatabasesDBName = getIDBDatabasesDBName();\n testNamespace = '';\n return dropIDBStoreWithMemFallback(idbDatabasesDBName);\n}\n\nexport function getIDBDatabasesDBName(): string {\n return testNamespace + IDB_DATABASES_DB_NAME;\n}\n"],"mappings":";;AAIA,IAAM,wBAAwB;AAE9B,IAAI,gBAAgB;AAapB,SAAgB,wBAAgC;
|
|
1
|
+
{"version":3,"file":"idb-databases-store-db-name.js","names":[],"sources":["../../../../../replicache/src/persist/idb-databases-store-db-name.ts"],"sourcesContent":["import {randomUint64} from '../../../shared/src/random-uint64.ts';\nimport {dropIDBStoreWithMemFallback} from '../kv/idb-store-with-mem-fallback.ts';\n\nconst IDB_DATABASES_VERSION = 0;\nconst IDB_DATABASES_DB_NAME = 'replicache-dbs-v' + IDB_DATABASES_VERSION;\n\nlet testNamespace = '';\n\n/** Namespace db name in test to isolate tests' indexeddb state. */\nexport function setupForTest(): void {\n testNamespace = randomUint64().toString(36);\n}\n\nexport function teardownForTest(): Promise<void> {\n const idbDatabasesDBName = getIDBDatabasesDBName();\n testNamespace = '';\n return dropIDBStoreWithMemFallback(idbDatabasesDBName);\n}\n\nexport function getIDBDatabasesDBName(): string {\n return testNamespace + IDB_DATABASES_DB_NAME;\n}\n"],"mappings":";;AAIA,IAAM,wBAAwB;AAE9B,IAAI,gBAAgB;AAapB,SAAgB,wBAAgC;CAC9C,OAAO,gBAAgB;AACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idb-databases-store.js","names":["#kvStore","#putDatabase"],"sources":["../../../../../replicache/src/persist/idb-databases-store.ts"],"sourcesContent":["import {\n assert,\n assertNumber,\n assertObject,\n assertString,\n} from '../../../shared/src/asserts.ts';\nimport {getBrowserGlobal} from '../../../shared/src/browser-env.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport type {CreateStore, Read, Store} from '../kv/store.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {getIDBDatabasesDBName} from './idb-databases-store-db-name.ts';\nimport {makeClientID} from './make-client-id.ts';\n\nconst DBS_KEY = 'dbs';\nexport const PROFILE_ID_KEY = 'profileId';\n\n// TODO: make an opaque type\nexport type IndexedDBName = string;\n\nexport type IndexedDBDatabase = {\n readonly name: IndexedDBName;\n readonly replicacheName: string;\n readonly replicacheFormatVersion: number;\n readonly schemaVersion: string;\n /** @deprecated No longer used. Kept for backwards compatibility when reading old data. */\n readonly lastOpenedTimestampMS?: number | undefined;\n};\n\nexport type IndexedDBDatabaseRecord = {\n readonly [name: IndexedDBName]: IndexedDBDatabase;\n};\n\nfunction assertIndexedDBDatabaseRecord(\n value: unknown,\n): asserts value is IndexedDBDatabaseRecord {\n assertObject(value);\n for (const [name, db] of Object.entries(value)) {\n assertString(name);\n assertIndexedDBDatabase(db);\n assert(\n name === db.name,\n () => `Expected record key \"${name}\" to match db.name \"${db.name}\"`,\n );\n }\n}\n\nfunction assertIndexedDBDatabase(\n value: unknown,\n): asserts value is IndexedDBDatabase {\n assertObject(value);\n assertString(value.name);\n assertString(value.replicacheName);\n assertNumber(value.replicacheFormatVersion);\n assertString(value.schemaVersion);\n if (value.lastOpenedTimestampMS !== undefined) {\n assertNumber(value.lastOpenedTimestampMS);\n }\n}\n\nexport class IDBDatabasesStore {\n readonly #kvStore: Store;\n\n constructor(createKVStore: CreateStore) {\n this.#kvStore = createKVStore(getIDBDatabasesDBName());\n }\n\n putDatabase(db: IndexedDBDatabase): Promise<IndexedDBDatabaseRecord> {\n return this.#putDatabase(db);\n }\n\n putDatabaseForTesting(\n db: IndexedDBDatabase,\n ): Promise<IndexedDBDatabaseRecord> {\n return this.#putDatabase(db);\n }\n\n #putDatabase(db: IndexedDBDatabase): Promise<IndexedDBDatabaseRecord> {\n return withWrite(this.#kvStore, async write => {\n const oldDbRecord = await getDatabases(write);\n const dbRecord = {\n ...oldDbRecord,\n [db.name]: db,\n };\n await write.put(DBS_KEY, dbRecord);\n return dbRecord;\n });\n }\n\n clearDatabases(): Promise<void> {\n return withWrite(this.#kvStore, write => write.del(DBS_KEY));\n }\n\n deleteDatabases(names: Iterable<IndexedDBName>): Promise<void> {\n return withWrite(this.#kvStore, async write => {\n const oldDbRecord = await getDatabases(write);\n const dbRecord = {\n ...oldDbRecord,\n };\n for (const name of names) {\n delete dbRecord[name];\n }\n await write.put(DBS_KEY, dbRecord);\n });\n }\n\n getDatabases(): Promise<IndexedDBDatabaseRecord> {\n return withRead(this.#kvStore, getDatabases);\n }\n\n close(): Promise<void> {\n return this.#kvStore.close();\n }\n\n getProfileID(): Promise<string> {\n return withWrite(this.#kvStore, async write => {\n let profileId = await write.get(PROFILE_ID_KEY);\n if (profileId === undefined) {\n // Not in the kv store. Try localStorage in case we are using a non persistent kv store.\n const maybeLocalStorage = getBrowserGlobal('localStorage');\n if (maybeLocalStorage) {\n profileId = maybeLocalStorage.getItem(PROFILE_ID_KEY) ?? undefined;\n }\n\n if (profileId === undefined) {\n // Profile id is 'p' followed by a random number.\n profileId = `p${makeClientID()}`;\n }\n\n await write.put(PROFILE_ID_KEY, profileId);\n if (maybeLocalStorage) {\n maybeLocalStorage.setItem(PROFILE_ID_KEY, profileId);\n }\n }\n assertString(profileId);\n return profileId;\n });\n }\n}\n\nasync function getDatabases(read: Read): Promise<IndexedDBDatabaseRecord> {\n let dbRecord = await read.get(DBS_KEY);\n if (!dbRecord) {\n dbRecord = deepFreeze({});\n }\n assertIndexedDBDatabaseRecord(dbRecord);\n return dbRecord;\n}\n"],"mappings":";;;;;;;AAaA,IAAM,UAAU;AAChB,IAAa,iBAAiB;AAkB9B,SAAS,8BACP,OAC0C;
|
|
1
|
+
{"version":3,"file":"idb-databases-store.js","names":["#kvStore","#putDatabase"],"sources":["../../../../../replicache/src/persist/idb-databases-store.ts"],"sourcesContent":["import {\n assert,\n assertNumber,\n assertObject,\n assertString,\n} from '../../../shared/src/asserts.ts';\nimport {getBrowserGlobal} from '../../../shared/src/browser-env.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport type {CreateStore, Read, Store} from '../kv/store.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {getIDBDatabasesDBName} from './idb-databases-store-db-name.ts';\nimport {makeClientID} from './make-client-id.ts';\n\nconst DBS_KEY = 'dbs';\nexport const PROFILE_ID_KEY = 'profileId';\n\n// TODO: make an opaque type\nexport type IndexedDBName = string;\n\nexport type IndexedDBDatabase = {\n readonly name: IndexedDBName;\n readonly replicacheName: string;\n readonly replicacheFormatVersion: number;\n readonly schemaVersion: string;\n /** @deprecated No longer used. Kept for backwards compatibility when reading old data. */\n readonly lastOpenedTimestampMS?: number | undefined;\n};\n\nexport type IndexedDBDatabaseRecord = {\n readonly [name: IndexedDBName]: IndexedDBDatabase;\n};\n\nfunction assertIndexedDBDatabaseRecord(\n value: unknown,\n): asserts value is IndexedDBDatabaseRecord {\n assertObject(value);\n for (const [name, db] of Object.entries(value)) {\n assertString(name);\n assertIndexedDBDatabase(db);\n assert(\n name === db.name,\n () => `Expected record key \"${name}\" to match db.name \"${db.name}\"`,\n );\n }\n}\n\nfunction assertIndexedDBDatabase(\n value: unknown,\n): asserts value is IndexedDBDatabase {\n assertObject(value);\n assertString(value.name);\n assertString(value.replicacheName);\n assertNumber(value.replicacheFormatVersion);\n assertString(value.schemaVersion);\n if (value.lastOpenedTimestampMS !== undefined) {\n assertNumber(value.lastOpenedTimestampMS);\n }\n}\n\nexport class IDBDatabasesStore {\n readonly #kvStore: Store;\n\n constructor(createKVStore: CreateStore) {\n this.#kvStore = createKVStore(getIDBDatabasesDBName());\n }\n\n putDatabase(db: IndexedDBDatabase): Promise<IndexedDBDatabaseRecord> {\n return this.#putDatabase(db);\n }\n\n putDatabaseForTesting(\n db: IndexedDBDatabase,\n ): Promise<IndexedDBDatabaseRecord> {\n return this.#putDatabase(db);\n }\n\n #putDatabase(db: IndexedDBDatabase): Promise<IndexedDBDatabaseRecord> {\n return withWrite(this.#kvStore, async write => {\n const oldDbRecord = await getDatabases(write);\n const dbRecord = {\n ...oldDbRecord,\n [db.name]: db,\n };\n await write.put(DBS_KEY, dbRecord);\n return dbRecord;\n });\n }\n\n clearDatabases(): Promise<void> {\n return withWrite(this.#kvStore, write => write.del(DBS_KEY));\n }\n\n deleteDatabases(names: Iterable<IndexedDBName>): Promise<void> {\n return withWrite(this.#kvStore, async write => {\n const oldDbRecord = await getDatabases(write);\n const dbRecord = {\n ...oldDbRecord,\n };\n for (const name of names) {\n delete dbRecord[name];\n }\n await write.put(DBS_KEY, dbRecord);\n });\n }\n\n getDatabases(): Promise<IndexedDBDatabaseRecord> {\n return withRead(this.#kvStore, getDatabases);\n }\n\n close(): Promise<void> {\n return this.#kvStore.close();\n }\n\n getProfileID(): Promise<string> {\n return withWrite(this.#kvStore, async write => {\n let profileId = await write.get(PROFILE_ID_KEY);\n if (profileId === undefined) {\n // Not in the kv store. Try localStorage in case we are using a non persistent kv store.\n const maybeLocalStorage = getBrowserGlobal('localStorage');\n if (maybeLocalStorage) {\n profileId = maybeLocalStorage.getItem(PROFILE_ID_KEY) ?? undefined;\n }\n\n if (profileId === undefined) {\n // Profile id is 'p' followed by a random number.\n profileId = `p${makeClientID()}`;\n }\n\n await write.put(PROFILE_ID_KEY, profileId);\n if (maybeLocalStorage) {\n maybeLocalStorage.setItem(PROFILE_ID_KEY, profileId);\n }\n }\n assertString(profileId);\n return profileId;\n });\n }\n}\n\nasync function getDatabases(read: Read): Promise<IndexedDBDatabaseRecord> {\n let dbRecord = await read.get(DBS_KEY);\n if (!dbRecord) {\n dbRecord = deepFreeze({});\n }\n assertIndexedDBDatabaseRecord(dbRecord);\n return dbRecord;\n}\n"],"mappings":";;;;;;;AAaA,IAAM,UAAU;AAChB,IAAa,iBAAiB;AAkB9B,SAAS,8BACP,OAC0C;CAC1C,aAAa,KAAK;CAClB,KAAK,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,KAAK,GAAG;EAC9C,aAAa,IAAI;EACjB,wBAAwB,EAAE;EAC1B,OACE,SAAS,GAAG,YACN,wBAAwB,KAAK,sBAAsB,GAAG,KAAK,EACnE;CACF;AACF;AAEA,SAAS,wBACP,OACoC;CACpC,aAAa,KAAK;CAClB,aAAa,MAAM,IAAI;CACvB,aAAa,MAAM,cAAc;CACjC,aAAa,MAAM,uBAAuB;CAC1C,aAAa,MAAM,aAAa;CAChC,IAAI,MAAM,0BAA0B,KAAA,GAClC,aAAa,MAAM,qBAAqB;AAE5C;AAEA,IAAa,oBAAb,MAA+B;CAC7B;CAEA,YAAY,eAA4B;EACtC,KAAKA,WAAW,cAAc,sBAAsB,CAAC;CACvD;CAEA,YAAY,IAAyD;EACnE,OAAO,KAAKC,aAAa,EAAE;CAC7B;CAEA,sBACE,IACkC;EAClC,OAAO,KAAKA,aAAa,EAAE;CAC7B;CAEA,aAAa,IAAyD;EACpE,OAAO,UAAU,KAAKD,UAAU,OAAM,UAAS;GAE7C,MAAM,WAAW;IACf,GAAG,MAFqB,aAAa,KAAK;KAGzC,GAAG,OAAO;GACb;GACA,MAAM,MAAM,IAAI,SAAS,QAAQ;GACjC,OAAO;EACT,CAAC;CACH;CAEA,iBAAgC;EAC9B,OAAO,UAAU,KAAKA,WAAU,UAAS,MAAM,IAAI,OAAO,CAAC;CAC7D;CAEA,gBAAgB,OAA+C;EAC7D,OAAO,UAAU,KAAKA,UAAU,OAAM,UAAS;GAE7C,MAAM,WAAW,EACf,GAAG,MAFqB,aAAa,KAAK,EAG5C;GACA,KAAK,MAAM,QAAQ,OACjB,OAAO,SAAS;GAElB,MAAM,MAAM,IAAI,SAAS,QAAQ;EACnC,CAAC;CACH;CAEA,eAAiD;EAC/C,OAAO,SAAS,KAAKA,UAAU,YAAY;CAC7C;CAEA,QAAuB;EACrB,OAAO,KAAKA,SAAS,MAAM;CAC7B;CAEA,eAAgC;EAC9B,OAAO,UAAU,KAAKA,UAAU,OAAM,UAAS;GAC7C,IAAI,YAAY,MAAM,MAAM,IAAI,cAAc;GAC9C,IAAI,cAAc,KAAA,GAAW;IAE3B,MAAM,oBAAoB,iBAAiB,cAAc;IACzD,IAAI,mBACF,YAAY,kBAAkB,QAAA,WAAsB,KAAK,KAAA;IAG3D,IAAI,cAAc,KAAA,GAEhB,YAAY,IAAI,aAAa;IAG/B,MAAM,MAAM,IAAI,gBAAgB,SAAS;IACzC,IAAI,mBACF,kBAAkB,QAAQ,gBAAgB,SAAS;GAEvD;GACA,aAAa,SAAS;GACtB,OAAO;EACT,CAAC;CACH;AACF;AAEA,eAAe,aAAa,MAA8C;CACxE,IAAI,WAAW,MAAM,KAAK,IAAI,OAAO;CACrC,IAAI,CAAC,UACH,WAAW,WAAW,CAAC,CAAC;CAE1B,8BAA8B,QAAQ;CACtC,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"make-client-id.js","names":[],"sources":["../../../../../replicache/src/persist/make-client-id.ts"],"sourcesContent":["import {randomUint64} from '../../../shared/src/random-uint64.ts';\n\n/**\n * Returns a random 18 character string encoded in base32 suitable as a client\n * ID.\n */\nexport function makeClientID(): string {\n const length = 18;\n const high = randomUint64();\n const low = randomUint64();\n const combined = (high << 64n) | low;\n return combined.toString(32).slice(-length).padStart(length, '0');\n}\n"],"mappings":";;;;;;AAMA,SAAgB,eAAuB;CACrC,MAAM,SAAS;CACf,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"make-client-id.js","names":[],"sources":["../../../../../replicache/src/persist/make-client-id.ts"],"sourcesContent":["import {randomUint64} from '../../../shared/src/random-uint64.ts';\n\n/**\n * Returns a random 18 character string encoded in base32 suitable as a client\n * ID.\n */\nexport function makeClientID(): string {\n const length = 18;\n const high = randomUint64();\n const low = randomUint64();\n const combined = (high << 64n) | low;\n return combined.toString(32).slice(-length).padStart(length, '0');\n}\n"],"mappings":";;;;;;AAMA,SAAgB,eAAuB;CACrC,MAAM,SAAS;CACf,MAAM,OAAO,aAAa;CAC1B,MAAM,MAAM,aAAa;CAEzB,QADkB,QAAQ,MAAO,KACjB,SAAS,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,QAAQ,GAAG;AAClE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/persist/persist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/persist/persist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AAGtD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAO,KAAK,EAAQ,MAAM,iBAAiB,CAAC;AAexD,OAAO,KAAK,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAEhE,OAAO,KAAK,EAAC,UAAU,EAAa,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAgB,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAgB7C,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,KAAK,EACb,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,OAAO,EACrB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS,EAChD,4BAA4B,sBAA0B,GACrD,OAAO,CAAC,IAAI,CAAC,CA2Lf"}
|
|
@@ -4,6 +4,7 @@ import { withRead, withWrite } from "../with-transactions.js";
|
|
|
4
4
|
import { getClientGroup, setClientGroup } from "./client-groups.js";
|
|
5
5
|
import { assertClientV6, assertHasClientState, getClientGroupIDForClient, mustGetClient, setClient } from "./clients.js";
|
|
6
6
|
import { rebaseMutationAndPutCommit } from "../db/rebase.js";
|
|
7
|
+
import { getOrInsert, getOrInsertComputed } from "../../../shared/src/map.js";
|
|
7
8
|
import { GatherMemoryOnlyVisitor } from "./gather-mem-only-visitor.js";
|
|
8
9
|
//#region ../replicache/src/persist/persist.ts
|
|
9
10
|
/**
|
|
@@ -105,11 +106,12 @@ async function getClientGroupInfo(perdagRead, clientGroupID) {
|
|
|
105
106
|
return [clientGroup, await commitFromHash(clientGroup.headHash, perdagRead)];
|
|
106
107
|
}
|
|
107
108
|
async function rebase(mutations, basis, write, mutators, mutationIDs, lc, formatVersion, zeroData) {
|
|
109
|
+
const basisMutationIDs = /* @__PURE__ */ new Map();
|
|
110
|
+
const getBasisMutationID = (basisHash, clientID) => getOrInsertComputed(getOrInsert(basisMutationIDs, basisHash, /* @__PURE__ */ new Map()), clientID, () => commitFromHash(basisHash, write).then((commit) => commit.getMutationID(clientID, write)));
|
|
108
111
|
for (let i = mutations.length - 1; i >= 0; i--) {
|
|
109
112
|
const mutationCommit = mutations[i];
|
|
110
113
|
const { meta } = mutationCommit;
|
|
111
|
-
|
|
112
|
-
if (await mutationCommit.getMutationID(meta.clientID, write) > await newMainHead.getMutationID(meta.clientID, write)) {
|
|
114
|
+
if (meta.mutationID > await getBasisMutationID(basis, meta.clientID)) {
|
|
113
115
|
mutationIDs[meta.clientID] = meta.mutationID;
|
|
114
116
|
basis = (await rebaseMutationAndPutCommit(mutationCommit, write, basis, mutators, lc, meta.clientID, formatVersion, zeroData)).chunk.hash;
|
|
115
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persist.js","names":[],"sources":["../../../../../replicache/src/persist/persist.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 {Chunk} from '../dag/chunk.ts';\nimport type {LazyStore} from '../dag/lazy-store.ts';\nimport type {Read, Store, Write} from '../dag/store.ts';\nimport type {Commit} from '../db/commit.ts';\nimport {\n DEFAULT_HEAD_NAME,\n type LocalMetaDD31,\n type Meta,\n assertSnapshotCommitDD31,\n baseSnapshotFromCommit,\n commitFromHash,\n commitFromHead,\n compareCookiesForSnapshots,\n localMutationsDD31,\n localMutationsGreaterThan,\n} from '../db/commit.ts';\nimport {rebaseMutationAndPutCommit} from '../db/rebase.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroOption, ZeroTxData} from '../replicache-options.ts';\nimport type {ClientGroupID, ClientID} from '../sync/ids.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n type ClientGroup,\n getClientGroup,\n setClientGroup,\n} from './client-groups.ts';\nimport {\n assertClientV6,\n assertHasClientState,\n getClientGroupIDForClient,\n mustGetClient,\n setClient,\n} from './clients.ts';\nimport {GatherMemoryOnlyVisitor} from './gather-mem-only-visitor.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\n/**\n * Persists the client's memdag state to the client's perdag client group.\n *\n * Persists the base snapshot from memdag to the client's perdag client group,\n * but only if it’s newer than the client's perdag client group’s base snapshot.\n * The base snapshot is persisted by gathering all memory-only chunks in the dag\n * subgraph rooted at the base snapshot's commit and writing them to the perdag.\n * Once the base snapshot is persisted, rebases onto this new base snapshot all\n * local commits from the client's perdag client group that are not already\n * reflected in the base snapshot.\n *\n * Whether or not the base snapshot is persisted, rebases onto the client's\n * perdag client group all memdag local commits not already in the client's\n * perdag client group's history.\n *\n * Also updates the `lastMutationIDs` and `lastServerAckdMutationIDs` properties\n * of the client's client group's entry in the `ClientGroupMap`.\n */\nexport async function persistDD31(\n lc: LogContext,\n clientID: ClientID,\n memdag: LazyStore,\n perdag: Store,\n mutators: MutatorDefs,\n closed: () => boolean,\n formatVersion: FormatVersion,\n getZeroData: ZeroOption['getTxData'] | undefined,\n onGatherMemOnlyChunksForTest = () => Promise.resolve(),\n): Promise<void> {\n if (closed()) {\n return;\n }\n\n const [perdagLMID, perdagBaseSnapshot, mainClientGroupID] = await withRead(\n perdag,\n async perdagRead => {\n await assertHasClientState(clientID, perdagRead);\n const mainClientGroupID = await getClientGroupIDForClient(\n clientID,\n perdagRead,\n );\n assert(\n mainClientGroupID,\n `No main client group for clientID: ${clientID}`,\n );\n const [, perdagMainClientGroupHeadCommit] = await getClientGroupInfo(\n perdagRead,\n mainClientGroupID,\n );\n const perdagLMID = await perdagMainClientGroupHeadCommit.getMutationID(\n clientID,\n perdagRead,\n );\n const perdagBaseSnapshot = await baseSnapshotFromCommit(\n perdagMainClientGroupHeadCommit,\n perdagRead,\n );\n assertSnapshotCommitDD31(perdagBaseSnapshot);\n return [perdagLMID, perdagBaseSnapshot, mainClientGroupID];\n },\n );\n\n if (closed()) {\n return;\n }\n const [newMemdagMutations, memdagBaseSnapshot, gatheredChunks] =\n await withRead(memdag, async memdagRead => {\n const memdagHeadCommit = await commitFromHead(\n DEFAULT_HEAD_NAME,\n memdagRead,\n );\n const newMutations = await localMutationsGreaterThan(\n memdagHeadCommit,\n {[clientID]: perdagLMID || 0},\n memdagRead,\n );\n const memdagBaseSnapshot = await baseSnapshotFromCommit(\n memdagHeadCommit,\n memdagRead,\n );\n assertSnapshotCommitDD31(memdagBaseSnapshot);\n\n let gatheredChunks: ReadonlyMap<Hash, Chunk> | undefined;\n if (\n compareCookiesForSnapshots(memdagBaseSnapshot, perdagBaseSnapshot) > 0\n ) {\n await onGatherMemOnlyChunksForTest();\n // Might need to persist snapshot, we will have to double check\n // after gathering the snapshot chunks from memdag\n const memdagBaseSnapshotHash = memdagBaseSnapshot.chunk.hash;\n // Gather all memory only chunks from base snapshot on the memdag.\n const visitor = new GatherMemoryOnlyVisitor(memdagRead);\n await visitor.visit(memdagBaseSnapshotHash);\n gatheredChunks = visitor.gatheredChunks;\n }\n\n return [newMutations, memdagBaseSnapshot, gatheredChunks];\n });\n\n if (closed()) {\n return;\n }\n\n let memdagBaseSnapshotPersisted = false;\n const zeroDataForMemdagBaseSnapshot =\n getZeroData && (await getZeroData(memdagBaseSnapshot.chunk.hash));\n\n await withWrite(perdag, async perdagWrite => {\n const [mainClientGroup, latestPerdagMainClientGroupHeadCommit] =\n await getClientGroupInfo(perdagWrite, mainClientGroupID);\n\n // These initial values for newMainClientGroupHeadHash, mutationIDs,\n // lastServerAckdMutationIDs are correct for the case where the memdag\n // snapshot is *not* persisted. If the memdag snapshot is persisted\n // these values are overwritten appropriately.\n let newMainClientGroupHeadHash: Hash =\n latestPerdagMainClientGroupHeadCommit.chunk.hash;\n let mutationIDs: Record<ClientID, number> = {\n ...mainClientGroup.mutationIDs,\n };\n let {lastServerAckdMutationIDs} = mainClientGroup;\n\n if (gatheredChunks) {\n // check if memdag snapshot still newer than perdag snapshot\n\n const client = await mustGetClient(clientID, perdagWrite);\n assertClientV6(client);\n\n const latestPerdagBaseSnapshot = await baseSnapshotFromCommit(\n latestPerdagMainClientGroupHeadCommit,\n perdagWrite,\n );\n assertSnapshotCommitDD31(latestPerdagBaseSnapshot);\n\n // check if memdag snapshot still newer than perdag snapshot\n if (\n compareCookiesForSnapshots(\n memdagBaseSnapshot,\n latestPerdagBaseSnapshot,\n ) > 0\n ) {\n // still newer, persist memdag snapshot by writing chunks\n memdagBaseSnapshotPersisted = true;\n await Promise.all(\n Array.from(gatheredChunks.values(), c => perdagWrite.putChunk(c)),\n );\n\n await setClient(\n clientID,\n {\n ...client,\n persistHash: memdagBaseSnapshot.chunk.hash,\n },\n perdagWrite,\n );\n // Rebase local mutations from perdag main client group onto new\n // snapshot\n newMainClientGroupHeadHash = memdagBaseSnapshot.chunk.hash;\n const mainClientGroupLocalMutations = await localMutationsDD31(\n mainClientGroup.headHash,\n perdagWrite,\n );\n\n lastServerAckdMutationIDs = memdagBaseSnapshot.meta.lastMutationIDs;\n mutationIDs = {...lastServerAckdMutationIDs};\n\n newMainClientGroupHeadHash = await rebase(\n mainClientGroupLocalMutations,\n newMainClientGroupHeadHash,\n perdagWrite,\n mutators,\n mutationIDs,\n lc,\n formatVersion,\n zeroDataForMemdagBaseSnapshot,\n );\n }\n }\n\n let zeroDataForPerdagHeadCommit: ZeroTxData | undefined;\n if (!memdagBaseSnapshotPersisted) {\n zeroDataForPerdagHeadCommit =\n getZeroData &&\n (await getZeroData(newMainClientGroupHeadHash, {\n openLazySourceRead: perdagWrite,\n }));\n }\n\n // rebase new memdag mutations onto perdag\n newMainClientGroupHeadHash = await rebase(\n newMemdagMutations,\n newMainClientGroupHeadHash,\n perdagWrite,\n mutators,\n mutationIDs,\n lc,\n formatVersion,\n zeroDataForPerdagHeadCommit ?? zeroDataForMemdagBaseSnapshot,\n );\n\n const newMainClientGroup = {\n ...mainClientGroup,\n headHash: newMainClientGroupHeadHash,\n mutationIDs,\n lastServerAckdMutationIDs,\n };\n\n await setClientGroup(mainClientGroupID, newMainClientGroup, perdagWrite);\n });\n\n if (gatheredChunks && memdagBaseSnapshotPersisted) {\n await withWrite(memdag, memdagWrite =>\n memdagWrite.chunksPersisted([...gatheredChunks.keys()]),\n );\n }\n}\n\nasync function getClientGroupInfo(\n perdagRead: Read,\n clientGroupID: ClientGroupID,\n): Promise<[ClientGroup, Commit<Meta>]> {\n const clientGroup = await getClientGroup(clientGroupID, perdagRead);\n assert(clientGroup, `No client group for clientGroupID: ${clientGroupID}`);\n return [clientGroup, await commitFromHash(clientGroup.headHash, perdagRead)];\n}\n\nasync function rebase(\n mutations: Commit<LocalMetaDD31>[],\n basis: Hash,\n write: Write,\n mutators: MutatorDefs,\n mutationIDs: Record<ClientID, number>,\n lc: LogContext,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Hash> {\n for (let i = mutations.length - 1; i >= 0; i--) {\n const mutationCommit = mutations[i];\n const {meta} = mutationCommit;\n const newMainHead = await commitFromHash(basis, write);\n if (\n (await mutationCommit.getMutationID(meta.clientID, write)) >\n (await newMainHead.getMutationID(meta.clientID, write))\n ) {\n mutationIDs[meta.clientID] = meta.mutationID;\n basis = (\n await rebaseMutationAndPutCommit(\n mutationCommit,\n write,\n basis,\n mutators,\n lc,\n meta.clientID,\n formatVersion,\n zeroData,\n )\n ).chunk.hash;\n }\n }\n return basis;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persist.js","names":[],"sources":["../../../../../replicache/src/persist/persist.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {getOrInsert, getOrInsertComputed} from '../../../shared/src/map.ts';\nimport type {Chunk} from '../dag/chunk.ts';\nimport type {LazyStore} from '../dag/lazy-store.ts';\nimport type {Read, Store, Write} from '../dag/store.ts';\nimport type {Commit} from '../db/commit.ts';\nimport {\n DEFAULT_HEAD_NAME,\n type LocalMetaDD31,\n type Meta,\n assertSnapshotCommitDD31,\n baseSnapshotFromCommit,\n commitFromHash,\n commitFromHead,\n compareCookiesForSnapshots,\n localMutationsDD31,\n localMutationsGreaterThan,\n} from '../db/commit.ts';\nimport {rebaseMutationAndPutCommit} from '../db/rebase.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroOption, ZeroTxData} from '../replicache-options.ts';\nimport type {ClientGroupID, ClientID} from '../sync/ids.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n type ClientGroup,\n getClientGroup,\n setClientGroup,\n} from './client-groups.ts';\nimport {\n assertClientV6,\n assertHasClientState,\n getClientGroupIDForClient,\n mustGetClient,\n setClient,\n} from './clients.ts';\nimport {GatherMemoryOnlyVisitor} from './gather-mem-only-visitor.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\n/**\n * Persists the client's memdag state to the client's perdag client group.\n *\n * Persists the base snapshot from memdag to the client's perdag client group,\n * but only if it’s newer than the client's perdag client group’s base snapshot.\n * The base snapshot is persisted by gathering all memory-only chunks in the dag\n * subgraph rooted at the base snapshot's commit and writing them to the perdag.\n * Once the base snapshot is persisted, rebases onto this new base snapshot all\n * local commits from the client's perdag client group that are not already\n * reflected in the base snapshot.\n *\n * Whether or not the base snapshot is persisted, rebases onto the client's\n * perdag client group all memdag local commits not already in the client's\n * perdag client group's history.\n *\n * Also updates the `lastMutationIDs` and `lastServerAckdMutationIDs` properties\n * of the client's client group's entry in the `ClientGroupMap`.\n */\nexport async function persistDD31(\n lc: LogContext,\n clientID: ClientID,\n memdag: LazyStore,\n perdag: Store,\n mutators: MutatorDefs,\n closed: () => boolean,\n formatVersion: FormatVersion,\n getZeroData: ZeroOption['getTxData'] | undefined,\n onGatherMemOnlyChunksForTest = () => Promise.resolve(),\n): Promise<void> {\n if (closed()) {\n return;\n }\n\n const [perdagLMID, perdagBaseSnapshot, mainClientGroupID] = await withRead(\n perdag,\n async perdagRead => {\n await assertHasClientState(clientID, perdagRead);\n const mainClientGroupID = await getClientGroupIDForClient(\n clientID,\n perdagRead,\n );\n assert(\n mainClientGroupID,\n `No main client group for clientID: ${clientID}`,\n );\n const [, perdagMainClientGroupHeadCommit] = await getClientGroupInfo(\n perdagRead,\n mainClientGroupID,\n );\n const perdagLMID = await perdagMainClientGroupHeadCommit.getMutationID(\n clientID,\n perdagRead,\n );\n const perdagBaseSnapshot = await baseSnapshotFromCommit(\n perdagMainClientGroupHeadCommit,\n perdagRead,\n );\n assertSnapshotCommitDD31(perdagBaseSnapshot);\n return [perdagLMID, perdagBaseSnapshot, mainClientGroupID];\n },\n );\n\n if (closed()) {\n return;\n }\n const [newMemdagMutations, memdagBaseSnapshot, gatheredChunks] =\n await withRead(memdag, async memdagRead => {\n const memdagHeadCommit = await commitFromHead(\n DEFAULT_HEAD_NAME,\n memdagRead,\n );\n const newMutations = await localMutationsGreaterThan(\n memdagHeadCommit,\n {[clientID]: perdagLMID || 0},\n memdagRead,\n );\n const memdagBaseSnapshot = await baseSnapshotFromCommit(\n memdagHeadCommit,\n memdagRead,\n );\n assertSnapshotCommitDD31(memdagBaseSnapshot);\n\n let gatheredChunks: ReadonlyMap<Hash, Chunk> | undefined;\n if (\n compareCookiesForSnapshots(memdagBaseSnapshot, perdagBaseSnapshot) > 0\n ) {\n await onGatherMemOnlyChunksForTest();\n // Might need to persist snapshot, we will have to double check\n // after gathering the snapshot chunks from memdag\n const memdagBaseSnapshotHash = memdagBaseSnapshot.chunk.hash;\n // Gather all memory only chunks from base snapshot on the memdag.\n const visitor = new GatherMemoryOnlyVisitor(memdagRead);\n await visitor.visit(memdagBaseSnapshotHash);\n gatheredChunks = visitor.gatheredChunks;\n }\n\n return [newMutations, memdagBaseSnapshot, gatheredChunks];\n });\n\n if (closed()) {\n return;\n }\n\n let memdagBaseSnapshotPersisted = false;\n const zeroDataForMemdagBaseSnapshot =\n getZeroData && (await getZeroData(memdagBaseSnapshot.chunk.hash));\n\n await withWrite(perdag, async perdagWrite => {\n const [mainClientGroup, latestPerdagMainClientGroupHeadCommit] =\n await getClientGroupInfo(perdagWrite, mainClientGroupID);\n\n // These initial values for newMainClientGroupHeadHash, mutationIDs,\n // lastServerAckdMutationIDs are correct for the case where the memdag\n // snapshot is *not* persisted. If the memdag snapshot is persisted\n // these values are overwritten appropriately.\n let newMainClientGroupHeadHash: Hash =\n latestPerdagMainClientGroupHeadCommit.chunk.hash;\n let mutationIDs: Record<ClientID, number> = {\n ...mainClientGroup.mutationIDs,\n };\n let {lastServerAckdMutationIDs} = mainClientGroup;\n\n if (gatheredChunks) {\n // check if memdag snapshot still newer than perdag snapshot\n\n const client = await mustGetClient(clientID, perdagWrite);\n assertClientV6(client);\n\n const latestPerdagBaseSnapshot = await baseSnapshotFromCommit(\n latestPerdagMainClientGroupHeadCommit,\n perdagWrite,\n );\n assertSnapshotCommitDD31(latestPerdagBaseSnapshot);\n\n // check if memdag snapshot still newer than perdag snapshot\n if (\n compareCookiesForSnapshots(\n memdagBaseSnapshot,\n latestPerdagBaseSnapshot,\n ) > 0\n ) {\n // still newer, persist memdag snapshot by writing chunks\n memdagBaseSnapshotPersisted = true;\n await Promise.all(\n Array.from(gatheredChunks.values(), c => perdagWrite.putChunk(c)),\n );\n\n await setClient(\n clientID,\n {\n ...client,\n persistHash: memdagBaseSnapshot.chunk.hash,\n },\n perdagWrite,\n );\n // Rebase local mutations from perdag main client group onto new\n // snapshot\n newMainClientGroupHeadHash = memdagBaseSnapshot.chunk.hash;\n const mainClientGroupLocalMutations = await localMutationsDD31(\n mainClientGroup.headHash,\n perdagWrite,\n );\n\n lastServerAckdMutationIDs = memdagBaseSnapshot.meta.lastMutationIDs;\n mutationIDs = {...lastServerAckdMutationIDs};\n\n newMainClientGroupHeadHash = await rebase(\n mainClientGroupLocalMutations,\n newMainClientGroupHeadHash,\n perdagWrite,\n mutators,\n mutationIDs,\n lc,\n formatVersion,\n zeroDataForMemdagBaseSnapshot,\n );\n }\n }\n\n let zeroDataForPerdagHeadCommit: ZeroTxData | undefined;\n if (!memdagBaseSnapshotPersisted) {\n zeroDataForPerdagHeadCommit =\n getZeroData &&\n (await getZeroData(newMainClientGroupHeadHash, {\n openLazySourceRead: perdagWrite,\n }));\n }\n\n // rebase new memdag mutations onto perdag\n newMainClientGroupHeadHash = await rebase(\n newMemdagMutations,\n newMainClientGroupHeadHash,\n perdagWrite,\n mutators,\n mutationIDs,\n lc,\n formatVersion,\n zeroDataForPerdagHeadCommit ?? zeroDataForMemdagBaseSnapshot,\n );\n\n const newMainClientGroup = {\n ...mainClientGroup,\n headHash: newMainClientGroupHeadHash,\n mutationIDs,\n lastServerAckdMutationIDs,\n };\n\n await setClientGroup(mainClientGroupID, newMainClientGroup, perdagWrite);\n });\n\n if (gatheredChunks && memdagBaseSnapshotPersisted) {\n await withWrite(memdag, memdagWrite =>\n memdagWrite.chunksPersisted([...gatheredChunks.keys()]),\n );\n }\n}\n\nasync function getClientGroupInfo(\n perdagRead: Read,\n clientGroupID: ClientGroupID,\n): Promise<[ClientGroup, Commit<Meta>]> {\n const clientGroup = await getClientGroup(clientGroupID, perdagRead);\n assert(clientGroup, `No client group for clientGroupID: ${clientGroupID}`);\n return [clientGroup, await commitFromHash(clientGroup.headHash, perdagRead)];\n}\n\nasync function rebase(\n mutations: Commit<LocalMetaDD31>[],\n basis: Hash,\n write: Write,\n mutators: MutatorDefs,\n mutationIDs: Record<ClientID, number>,\n lc: LogContext,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Hash> {\n const basisMutationIDs = new Map<Hash, Map<ClientID, Promise<number>>>();\n const getBasisMutationID = (\n basisHash: Hash,\n clientID: ClientID,\n ): Promise<number> =>\n getOrInsertComputed(\n getOrInsert(basisMutationIDs, basisHash, new Map()),\n clientID,\n () =>\n commitFromHash(basisHash, write).then(commit =>\n commit.getMutationID(clientID, write),\n ),\n );\n for (let i = mutations.length - 1; i >= 0; i--) {\n const mutationCommit = mutations[i];\n const {meta} = mutationCommit;\n if (meta.mutationID > (await getBasisMutationID(basis, meta.clientID))) {\n mutationIDs[meta.clientID] = meta.mutationID;\n basis = (\n await rebaseMutationAndPutCommit(\n mutationCommit,\n write,\n basis,\n mutators,\n lc,\n meta.clientID,\n formatVersion,\n zeroData,\n )\n ).chunk.hash;\n }\n }\n return basis;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh.js","names":[],"sources":["../../../../../replicache/src/persist/refresh.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {LazyStore} from '../dag/lazy-store.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {Commit} from '../db/commit.ts';\nimport {\n DEFAULT_HEAD_NAME,\n type SnapshotMetaDD31,\n assertSnapshotCommitDD31,\n baseSnapshotFromCommit,\n baseSnapshotFromHash,\n baseSnapshotFromHead,\n commitFromHash,\n commitFromHead,\n compareCookiesForSnapshots,\n localMutationsGreaterThan,\n} from '../db/commit.ts';\nimport {rebaseMutationAndPutCommit} from '../db/rebase.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroOption} from '../replicache-options.ts';\nimport type {DiffsMap} from '../sync/diff.ts';\nimport {type DiffComputationConfig, diffCommits} from '../sync/diff.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n ClientStateNotFoundError,\n type ClientV6,\n assertClientV6,\n getClientGroupForClient,\n mustGetClient,\n setClient,\n} from './clients.ts';\nimport {\n type ChunkWithSize,\n GatherNotCachedVisitor,\n} from './gather-not-cached-visitor.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nconst GATHER_SIZE_LIMIT = 5 * 2 ** 20; // 5 MB\n\ntype RefreshResult =\n | {\n type: 'aborted';\n refreshHashesForRevert?: readonly Hash[] | undefined;\n }\n | {\n type: 'complete';\n diffs: DiffsMap;\n newPerdagClientHeadHash: Hash;\n oldHead: Hash;\n newHead: Hash;\n };\n\n/**\n * This returns the diff between the state of the btree before and after\n * refresh. It returns `undefined` if the refresh was aborted.\n */\nexport async function refresh(\n lc: LogContext,\n memdag: LazyStore,\n perdag: Store,\n clientID: ClientID,\n mutators: MutatorDefs,\n diffConfig: DiffComputationConfig,\n closed: () => boolean,\n formatVersion: FormatVersion,\n zero: ZeroOption | undefined,\n): Promise<{oldHead: Hash; newHead: Hash; diffs: DiffsMap} | undefined> {\n if (closed()) {\n return;\n }\n const memdagBaseSnapshot = await withRead(memdag, memdagRead =>\n baseSnapshotFromHead(DEFAULT_HEAD_NAME, memdagRead),\n );\n assertSnapshotCommitDD31(memdagBaseSnapshot);\n\n type PerdagWriteResult = [\n perdagClientGroupHeadHash: Hash,\n perdagClientGroupBaseSnapshot: Commit<SnapshotMetaDD31>,\n perdagLmid: number,\n gatheredChunks: ReadonlyMap<Hash, ChunkWithSize>,\n refreshHashesForRevert: readonly Hash[],\n ];\n\n // Suspend eviction and deletion of chunks cached by the lazy store\n // to prevent cache misses. If eviction and deletion are not suspended\n // some chunks that are not gathered due to already being cached, may be\n // evicted or deleted by the time the write lock is acquired on the memdag,\n // which can lead to cache misses when performing the rebase and diff.\n // It is important to avoid these cache misses because they often create jank\n // because they block local mutations, pulls and queries on reading from idb.\n // Cache misses can still happen during the rebase and diff, but only\n // if the gather step hits its size limit.\n const result: RefreshResult =\n await memdag.withSuspendedSourceCacheEvictsAndDeletes(async () => {\n const perdagWriteResult: PerdagWriteResult | undefined = await withWrite(\n perdag,\n async perdagWrite => {\n const clientGroup = await getClientGroupForClient(\n clientID,\n perdagWrite,\n );\n if (!clientGroup) {\n throw new ClientStateNotFoundError(clientID);\n }\n\n const perdagClientGroupHeadHash = clientGroup.headHash;\n const perdagClientGroupHeadCommit = await commitFromHash(\n perdagClientGroupHeadHash,\n perdagWrite,\n );\n const perdagLmid = await perdagClientGroupHeadCommit.getMutationID(\n clientID,\n perdagWrite,\n );\n\n // Need to pull this head into memdag, but can't have it disappear if\n // perdag moves forward while we're rebasing in memdag. Can't change\n // client headHash until our rebase in memdag is complete, because if\n // rebase fails, then nothing is keeping client's chunks alive in\n // perdag.\n const client = await mustGetClient(clientID, perdagWrite);\n assertClientV6(client);\n const perdagClientGroupBaseSnapshot = await baseSnapshotFromHash(\n perdagClientGroupHeadHash,\n perdagWrite,\n );\n assertSnapshotCommitDD31(perdagClientGroupBaseSnapshot);\n if (\n shouldAbortRefresh(\n memdagBaseSnapshot,\n perdagClientGroupBaseSnapshot,\n perdagClientGroupHeadHash,\n )\n ) {\n return undefined;\n }\n\n // To avoid pulling the entire perdag graph into the memdag\n // the amount of chunk data gathered is limited by size.\n const visitor = new GatherNotCachedVisitor(\n perdagWrite,\n memdag,\n GATHER_SIZE_LIMIT,\n );\n await visitor.visit(perdagClientGroupHeadHash);\n const {gatheredChunks} = visitor;\n\n const refreshHashesSet = new Set(client.refreshHashes);\n refreshHashesSet.add(perdagClientGroupHeadHash);\n\n const newClient: ClientV6 = {\n ...client,\n refreshHashes: [...refreshHashesSet],\n };\n\n await setClient(clientID, newClient, perdagWrite);\n return [\n perdagClientGroupHeadHash,\n perdagClientGroupBaseSnapshot,\n perdagLmid,\n gatheredChunks,\n client.refreshHashes,\n ];\n },\n );\n\n if (closed() || !perdagWriteResult) {\n return {\n type: 'aborted',\n } as const;\n }\n\n const [\n perdagClientGroupHeadHash,\n perdagClientGroupBaseSnapshot,\n perdagLmid,\n gatheredChunks,\n refreshHashesForRevert,\n ] = perdagWriteResult;\n return withWrite(memdag, async memdagWrite => {\n const memdagHeadCommit = await commitFromHead(\n DEFAULT_HEAD_NAME,\n memdagWrite,\n );\n const memdagBaseSnapshot = await baseSnapshotFromCommit(\n memdagHeadCommit,\n memdagWrite,\n );\n assertSnapshotCommitDD31(memdagBaseSnapshot);\n if (\n shouldAbortRefresh(\n memdagBaseSnapshot,\n perdagClientGroupBaseSnapshot,\n perdagClientGroupHeadHash,\n )\n ) {\n return {\n type: 'aborted',\n refreshHashesForRevert,\n };\n }\n\n const newMemdagMutations = await localMutationsGreaterThan(\n memdagHeadCommit,\n {[clientID]: perdagLmid},\n memdagWrite,\n );\n const ps = [];\n for (const {chunk, size} of gatheredChunks.values()) {\n ps.push(memdagWrite.putChunk(chunk, size));\n }\n await Promise.all(ps);\n\n let newMemdagHeadHash = perdagClientGroupHeadHash;\n if (newMemdagMutations.length > 0) {\n const zeroData = await zero?.getTxData?.(newMemdagHeadHash, {\n openLazyRead: memdagWrite,\n });\n for (let i = newMemdagMutations.length - 1; i >= 0; i--) {\n newMemdagHeadHash = (\n await rebaseMutationAndPutCommit(\n newMemdagMutations[i],\n memdagWrite,\n newMemdagHeadHash,\n mutators,\n lc,\n newMemdagMutations[i].meta.clientID,\n formatVersion,\n zeroData,\n )\n ).chunk.hash;\n }\n }\n\n const newMemdagHeadCommit = await commitFromHash(\n newMemdagHeadHash,\n memdagWrite,\n );\n const diffs = await diffCommits(\n memdagHeadCommit,\n newMemdagHeadCommit,\n memdagWrite,\n diffConfig,\n formatVersion,\n );\n\n await memdagWrite.setHead(DEFAULT_HEAD_NAME, newMemdagHeadHash);\n return {\n type: 'complete',\n diffs,\n oldHead: memdagHeadCommit.chunk.hash,\n newHead: newMemdagHeadHash,\n newPerdagClientHeadHash: perdagClientGroupHeadHash,\n } as const;\n });\n });\n\n if (closed()) {\n return;\n }\n\n const setRefreshHashes = (refreshHashes: readonly Hash[]) =>\n withWrite(perdag, async perdagWrite => {\n const client = await mustGetClient(clientID, perdagWrite);\n const newClient = {\n ...client,\n refreshHashes,\n };\n\n // If this cleanup never happens, it's no big deal, some data will stay\n // alive longer but next refresh will fix it.\n await setClient(clientID, newClient, perdagWrite);\n });\n\n if (result.type === 'aborted') {\n if (result.refreshHashesForRevert) {\n await setRefreshHashes(result.refreshHashesForRevert);\n }\n return undefined;\n }\n\n // Advance zero here before setting refresh hashes\n // since we must advance before delegating control of the microtask\n // loop.\n zero?.advance(result.oldHead, result.newHead, result.diffs.get('') ?? []);\n await setRefreshHashes([result.newPerdagClientHeadHash]);\n return {\n oldHead: result.oldHead,\n newHead: result.newHead,\n diffs: result.diffs,\n };\n}\n\nfunction shouldAbortRefresh(\n memdagBaseSnapshot: Commit<SnapshotMetaDD31>,\n perdagClientGroupBaseSnapshot: Commit<SnapshotMetaDD31>,\n perdagClientGroupHeadHash: Hash,\n): boolean {\n const baseSnapshotCookieCompareResult = compareCookiesForSnapshots(\n memdagBaseSnapshot,\n perdagClientGroupBaseSnapshot,\n );\n return (\n baseSnapshotCookieCompareResult > 0 ||\n (baseSnapshotCookieCompareResult === 0 &&\n perdagClientGroupHeadHash === perdagClientGroupBaseSnapshot.chunk.hash)\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"refresh.js","names":[],"sources":["../../../../../replicache/src/persist/refresh.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {LazyStore} from '../dag/lazy-store.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {Commit} from '../db/commit.ts';\nimport {\n DEFAULT_HEAD_NAME,\n type SnapshotMetaDD31,\n assertSnapshotCommitDD31,\n baseSnapshotFromCommit,\n baseSnapshotFromHash,\n baseSnapshotFromHead,\n commitFromHash,\n commitFromHead,\n compareCookiesForSnapshots,\n localMutationsGreaterThan,\n} from '../db/commit.ts';\nimport {rebaseMutationAndPutCommit} from '../db/rebase.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroOption} from '../replicache-options.ts';\nimport type {DiffsMap} from '../sync/diff.ts';\nimport {type DiffComputationConfig, diffCommits} from '../sync/diff.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n ClientStateNotFoundError,\n type ClientV6,\n assertClientV6,\n getClientGroupForClient,\n mustGetClient,\n setClient,\n} from './clients.ts';\nimport {\n type ChunkWithSize,\n GatherNotCachedVisitor,\n} from './gather-not-cached-visitor.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nconst GATHER_SIZE_LIMIT = 5 * 2 ** 20; // 5 MB\n\ntype RefreshResult =\n | {\n type: 'aborted';\n refreshHashesForRevert?: readonly Hash[] | undefined;\n }\n | {\n type: 'complete';\n diffs: DiffsMap;\n newPerdagClientHeadHash: Hash;\n oldHead: Hash;\n newHead: Hash;\n };\n\n/**\n * This returns the diff between the state of the btree before and after\n * refresh. It returns `undefined` if the refresh was aborted.\n */\nexport async function refresh(\n lc: LogContext,\n memdag: LazyStore,\n perdag: Store,\n clientID: ClientID,\n mutators: MutatorDefs,\n diffConfig: DiffComputationConfig,\n closed: () => boolean,\n formatVersion: FormatVersion,\n zero: ZeroOption | undefined,\n): Promise<{oldHead: Hash; newHead: Hash; diffs: DiffsMap} | undefined> {\n if (closed()) {\n return;\n }\n const memdagBaseSnapshot = await withRead(memdag, memdagRead =>\n baseSnapshotFromHead(DEFAULT_HEAD_NAME, memdagRead),\n );\n assertSnapshotCommitDD31(memdagBaseSnapshot);\n\n type PerdagWriteResult = [\n perdagClientGroupHeadHash: Hash,\n perdagClientGroupBaseSnapshot: Commit<SnapshotMetaDD31>,\n perdagLmid: number,\n gatheredChunks: ReadonlyMap<Hash, ChunkWithSize>,\n refreshHashesForRevert: readonly Hash[],\n ];\n\n // Suspend eviction and deletion of chunks cached by the lazy store\n // to prevent cache misses. If eviction and deletion are not suspended\n // some chunks that are not gathered due to already being cached, may be\n // evicted or deleted by the time the write lock is acquired on the memdag,\n // which can lead to cache misses when performing the rebase and diff.\n // It is important to avoid these cache misses because they often create jank\n // because they block local mutations, pulls and queries on reading from idb.\n // Cache misses can still happen during the rebase and diff, but only\n // if the gather step hits its size limit.\n const result: RefreshResult =\n await memdag.withSuspendedSourceCacheEvictsAndDeletes(async () => {\n const perdagWriteResult: PerdagWriteResult | undefined = await withWrite(\n perdag,\n async perdagWrite => {\n const clientGroup = await getClientGroupForClient(\n clientID,\n perdagWrite,\n );\n if (!clientGroup) {\n throw new ClientStateNotFoundError(clientID);\n }\n\n const perdagClientGroupHeadHash = clientGroup.headHash;\n const perdagClientGroupHeadCommit = await commitFromHash(\n perdagClientGroupHeadHash,\n perdagWrite,\n );\n const perdagLmid = await perdagClientGroupHeadCommit.getMutationID(\n clientID,\n perdagWrite,\n );\n\n // Need to pull this head into memdag, but can't have it disappear if\n // perdag moves forward while we're rebasing in memdag. Can't change\n // client headHash until our rebase in memdag is complete, because if\n // rebase fails, then nothing is keeping client's chunks alive in\n // perdag.\n const client = await mustGetClient(clientID, perdagWrite);\n assertClientV6(client);\n const perdagClientGroupBaseSnapshot = await baseSnapshotFromHash(\n perdagClientGroupHeadHash,\n perdagWrite,\n );\n assertSnapshotCommitDD31(perdagClientGroupBaseSnapshot);\n if (\n shouldAbortRefresh(\n memdagBaseSnapshot,\n perdagClientGroupBaseSnapshot,\n perdagClientGroupHeadHash,\n )\n ) {\n return undefined;\n }\n\n // To avoid pulling the entire perdag graph into the memdag\n // the amount of chunk data gathered is limited by size.\n const visitor = new GatherNotCachedVisitor(\n perdagWrite,\n memdag,\n GATHER_SIZE_LIMIT,\n );\n await visitor.visit(perdagClientGroupHeadHash);\n const {gatheredChunks} = visitor;\n\n const refreshHashesSet = new Set(client.refreshHashes);\n refreshHashesSet.add(perdagClientGroupHeadHash);\n\n const newClient: ClientV6 = {\n ...client,\n refreshHashes: [...refreshHashesSet],\n };\n\n await setClient(clientID, newClient, perdagWrite);\n return [\n perdagClientGroupHeadHash,\n perdagClientGroupBaseSnapshot,\n perdagLmid,\n gatheredChunks,\n client.refreshHashes,\n ];\n },\n );\n\n if (closed() || !perdagWriteResult) {\n return {\n type: 'aborted',\n } as const;\n }\n\n const [\n perdagClientGroupHeadHash,\n perdagClientGroupBaseSnapshot,\n perdagLmid,\n gatheredChunks,\n refreshHashesForRevert,\n ] = perdagWriteResult;\n return withWrite(memdag, async memdagWrite => {\n const memdagHeadCommit = await commitFromHead(\n DEFAULT_HEAD_NAME,\n memdagWrite,\n );\n const memdagBaseSnapshot = await baseSnapshotFromCommit(\n memdagHeadCommit,\n memdagWrite,\n );\n assertSnapshotCommitDD31(memdagBaseSnapshot);\n if (\n shouldAbortRefresh(\n memdagBaseSnapshot,\n perdagClientGroupBaseSnapshot,\n perdagClientGroupHeadHash,\n )\n ) {\n return {\n type: 'aborted',\n refreshHashesForRevert,\n };\n }\n\n const newMemdagMutations = await localMutationsGreaterThan(\n memdagHeadCommit,\n {[clientID]: perdagLmid},\n memdagWrite,\n );\n const ps = [];\n for (const {chunk, size} of gatheredChunks.values()) {\n ps.push(memdagWrite.putChunk(chunk, size));\n }\n await Promise.all(ps);\n\n let newMemdagHeadHash = perdagClientGroupHeadHash;\n if (newMemdagMutations.length > 0) {\n const zeroData = await zero?.getTxData?.(newMemdagHeadHash, {\n openLazyRead: memdagWrite,\n });\n for (let i = newMemdagMutations.length - 1; i >= 0; i--) {\n newMemdagHeadHash = (\n await rebaseMutationAndPutCommit(\n newMemdagMutations[i],\n memdagWrite,\n newMemdagHeadHash,\n mutators,\n lc,\n newMemdagMutations[i].meta.clientID,\n formatVersion,\n zeroData,\n )\n ).chunk.hash;\n }\n }\n\n const newMemdagHeadCommit = await commitFromHash(\n newMemdagHeadHash,\n memdagWrite,\n );\n const diffs = await diffCommits(\n memdagHeadCommit,\n newMemdagHeadCommit,\n memdagWrite,\n diffConfig,\n formatVersion,\n );\n\n await memdagWrite.setHead(DEFAULT_HEAD_NAME, newMemdagHeadHash);\n return {\n type: 'complete',\n diffs,\n oldHead: memdagHeadCommit.chunk.hash,\n newHead: newMemdagHeadHash,\n newPerdagClientHeadHash: perdagClientGroupHeadHash,\n } as const;\n });\n });\n\n if (closed()) {\n return;\n }\n\n const setRefreshHashes = (refreshHashes: readonly Hash[]) =>\n withWrite(perdag, async perdagWrite => {\n const client = await mustGetClient(clientID, perdagWrite);\n const newClient = {\n ...client,\n refreshHashes,\n };\n\n // If this cleanup never happens, it's no big deal, some data will stay\n // alive longer but next refresh will fix it.\n await setClient(clientID, newClient, perdagWrite);\n });\n\n if (result.type === 'aborted') {\n if (result.refreshHashesForRevert) {\n await setRefreshHashes(result.refreshHashesForRevert);\n }\n return undefined;\n }\n\n // Advance zero here before setting refresh hashes\n // since we must advance before delegating control of the microtask\n // loop.\n zero?.advance(result.oldHead, result.newHead, result.diffs.get('') ?? []);\n await setRefreshHashes([result.newPerdagClientHeadHash]);\n return {\n oldHead: result.oldHead,\n newHead: result.newHead,\n diffs: result.diffs,\n };\n}\n\nfunction shouldAbortRefresh(\n memdagBaseSnapshot: Commit<SnapshotMetaDD31>,\n perdagClientGroupBaseSnapshot: Commit<SnapshotMetaDD31>,\n perdagClientGroupHeadHash: Hash,\n): boolean {\n const baseSnapshotCookieCompareResult = compareCookiesForSnapshots(\n memdagBaseSnapshot,\n perdagClientGroupBaseSnapshot,\n );\n return (\n baseSnapshotCookieCompareResult > 0 ||\n (baseSnapshotCookieCompareResult === 0 &&\n perdagClientGroupHeadHash === perdagClientGroupBaseSnapshot.chunk.hash)\n );\n}\n"],"mappings":"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"}
|