@rocicorp/zero 0.5.2024103100 → 0.5.2024110200
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/{chunk-TBA526RR.js → chunk-INJ4WJHS.js} +87 -67
- package/out/chunk-INJ4WJHS.js.map +7 -0
- package/out/{chunk-RCVGGCMG.js → chunk-YCMA66NH.js} +2 -2
- package/out/{chunk-RCVGGCMG.js.map → chunk-YCMA66NH.js.map} +2 -2
- package/out/internal.js +1 -1
- package/out/react.js.map +2 -2
- package/out/shared/src/valita.d.ts +7 -0
- package/out/shared/src/valita.d.ts.map +1 -1
- package/out/shared/src/valita.js +28 -0
- package/out/shared/src/valita.js.map +1 -1
- package/out/solid.js +2 -2
- package/out/solid.js.map +2 -2
- package/out/zero-advanced/src/mod.d.ts +7 -7
- package/out/zero-advanced/src/mod.d.ts.map +1 -1
- package/out/zero-cache/src/config/config-query.d.ts +6 -6
- package/out/zero-cache/src/config/config-query.d.ts.map +1 -1
- package/out/zero-cache/src/config/config-query.js +1 -1
- package/out/zero-cache/src/config/config-query.js.map +1 -1
- package/out/zero-cache/src/config/define-config.d.ts +3 -3
- package/out/zero-cache/src/config/define-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/define-config.js +1 -0
- package/out/zero-cache/src/config/define-config.js.map +1 -1
- package/out/zero-cache/src/config/refs.js +1 -1
- package/out/zero-cache/src/config/refs.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/write-authorizer.js +2 -2
- package/out/zero-cache/src/services/mutagen/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/database-storage.d.ts +1 -1
- package/out/zero-cache/src/services/view-syncer/database-storage.d.ts.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/types/lite.d.ts +1 -1
- package/out/zero-cache/src/types/lite.d.ts.map +1 -1
- package/out/zero-cache/src/types/lite.js +0 -1
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-client/src/client/context.d.ts +4 -4
- package/out/zero-client/src/client/context.d.ts.map +1 -1
- package/out/zero-client/src/client/crud.d.ts +4 -4
- package/out/zero-client/src/client/crud.d.ts.map +1 -1
- package/out/zero-client/src/client/keys.d.ts +1 -1
- package/out/zero-client/src/client/keys.d.ts.map +1 -1
- package/out/zero-client/src/client/normalized-schema.d.ts +2 -2
- package/out/zero-client/src/client/normalized-schema.d.ts.map +1 -1
- package/out/zero-client/src/client/normalized-schema.js +1 -1
- package/out/zero-client/src/client/normalized-schema.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +1 -1
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/query-manager.d.ts +1 -1
- package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.d.ts +2 -9
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/mod.d.ts +6 -7
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-react/src/use-query.d.ts +2 -1
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-zero.d.ts +2 -1
- package/out/zero-react/src/use-zero.d.ts.map +1 -1
- package/out/zero-schema/src/mod.d.ts +4 -0
- package/out/zero-schema/src/mod.d.ts.map +1 -0
- package/out/zero-schema/src/mod.js +4 -0
- package/out/zero-schema/src/mod.js.map +1 -0
- package/out/{zql/src/zql/query → zero-schema/src}/normalize-table-schema.d.ts +2 -3
- package/out/zero-schema/src/normalize-table-schema.d.ts.map +1 -0
- package/out/{zql/src/zql/query → zero-schema/src}/normalize-table-schema.js +3 -3
- package/out/zero-schema/src/normalize-table-schema.js.map +1 -0
- package/out/zero-schema/src/schema.d.ts +9 -0
- package/out/zero-schema/src/schema.d.ts.map +1 -0
- package/out/zero-schema/src/schema.js +4 -0
- package/out/zero-schema/src/schema.js.map +1 -0
- package/out/{zql/src/zql/query/schema.d.ts → zero-schema/src/table-schema.d.ts} +39 -3
- package/out/zero-schema/src/table-schema.d.ts.map +1 -0
- package/out/{zql/src/zql/query/schema.js → zero-schema/src/table-schema.js} +1 -1
- package/out/zero-schema/src/table-schema.js.map +1 -0
- package/out/zero-solid/src/create-zero.d.ts +2 -1
- package/out/zero-solid/src/create-zero.d.ts.map +1 -1
- package/out/zero.js +2 -6
- package/out/zql/src/{zql/builder → builder}/builder.d.ts +4 -4
- package/out/zql/src/builder/builder.d.ts.map +1 -0
- package/out/zql/src/{zql/builder → builder}/builder.js +7 -3
- package/out/zql/src/builder/builder.js.map +1 -0
- package/out/zql/src/builder/error.d.ts.map +1 -0
- package/out/zql/src/builder/error.js.map +1 -0
- package/out/zql/src/{zql/builder → builder}/filter.d.ts +2 -2
- package/out/zql/src/builder/filter.d.ts.map +1 -0
- package/out/zql/src/{zql/builder → builder}/filter.js +1 -1
- package/out/zql/src/builder/filter.js.map +1 -0
- package/out/zql/src/builder/like.d.ts.map +1 -0
- package/out/zql/src/{zql/builder → builder}/like.js +1 -1
- package/out/zql/src/builder/like.js.map +1 -0
- package/out/zql/src/ivm/array-view.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/array-view.js +1 -1
- package/out/zql/src/ivm/array-view.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/change.d.ts +1 -1
- package/out/zql/src/ivm/change.d.ts.map +1 -0
- package/out/zql/src/ivm/change.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/data.d.ts +2 -2
- package/out/zql/src/ivm/data.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/data.js +1 -1
- package/out/zql/src/ivm/data.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/fan-in.d.ts +2 -2
- package/out/zql/src/ivm/fan-in.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/fan-in.js +5 -8
- package/out/zql/src/ivm/fan-in.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/fan-out.d.ts +1 -1
- package/out/zql/src/ivm/fan-out.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/fan-out.js +4 -0
- package/out/zql/src/ivm/fan-out.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/filter.d.ts +3 -3
- package/out/zql/src/ivm/filter.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/filter.js +1 -1
- package/out/zql/src/ivm/filter.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/join.d.ts +2 -2
- package/out/zql/src/ivm/join.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/join.js +2 -2
- package/out/zql/src/ivm/join.js.map +1 -0
- package/out/zql/src/ivm/lookahead-iterator.d.ts.map +1 -0
- package/out/zql/src/ivm/lookahead-iterator.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/maybe-split-and-push-edit-change.d.ts +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -0
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/memory-source.d.ts +6 -6
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/memory-source.js +12 -17
- package/out/zql/src/ivm/memory-source.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/memory-storage.d.ts +1 -1
- package/out/zql/src/ivm/memory-storage.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/operator.d.ts +5 -5
- package/out/zql/src/ivm/operator.d.ts.map +1 -0
- package/out/zql/src/ivm/operator.js.map +1 -0
- package/out/zql/src/ivm/schema.d.ts +15 -0
- package/out/zql/src/ivm/schema.d.ts.map +1 -0
- package/out/zql/src/ivm/schema.js +2 -0
- package/out/zql/src/ivm/schema.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/skip.d.ts +3 -3
- package/out/zql/src/ivm/skip.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/skip.js +1 -1
- package/out/zql/src/ivm/skip.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/source.d.ts +2 -2
- package/out/zql/src/ivm/source.d.ts.map +1 -0
- package/out/zql/src/ivm/source.js.map +1 -0
- package/out/zql/src/ivm/stream.d.ts.map +1 -0
- package/out/zql/src/ivm/stream.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/take.d.ts +2 -2
- package/out/zql/src/ivm/take.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/take.js +2 -2
- package/out/zql/src/ivm/take.js.map +1 -0
- package/out/zql/src/ivm/view-apply-change.d.ts +5 -0
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/view-apply-change.js +2 -2
- package/out/zql/src/ivm/view-apply-change.js.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/view.d.ts +2 -2
- package/out/zql/src/ivm/view.d.ts.map +1 -0
- package/out/zql/src/{zql/ivm → ivm}/view.js.map +1 -1
- package/out/zql/src/query/auth-query.d.ts +20 -0
- package/out/zql/src/query/auth-query.d.ts.map +1 -0
- package/out/zql/src/query/auth-query.js +26 -0
- package/out/zql/src/query/auth-query.js.map +1 -0
- package/out/zql/src/query/escape-like.d.ts.map +1 -0
- package/out/zql/src/query/expression.d.ts +24 -0
- package/out/zql/src/query/expression.d.ts.map +1 -0
- package/out/zql/src/{zql/query → query}/expression.js +7 -1
- package/out/zql/src/query/expression.js.map +1 -0
- package/out/zql/src/{zql/query → query}/query-impl.d.ts +5 -5
- package/out/zql/src/query/query-impl.d.ts.map +1 -0
- package/out/zql/src/{zql/query → query}/query-impl.js +6 -4
- package/out/zql/src/query/query-impl.js.map +1 -0
- package/out/zql/src/{zql/query → query}/query-internal.d.ts +1 -1
- package/out/zql/src/query/query-internal.d.ts.map +1 -0
- package/out/zql/src/query/query-internal.js.map +1 -0
- package/out/zql/src/{zql/query → query}/query.d.ts +11 -26
- package/out/zql/src/query/query.d.ts.map +1 -0
- package/out/zql/src/query/query.js.map +1 -0
- package/out/zql/src/{zql/query → query}/typed-view.d.ts +1 -1
- package/out/zql/src/query/typed-view.d.ts.map +1 -0
- package/out/zql/src/query/typed-view.js.map +1 -0
- package/out/zqlite/src/table-source.d.ts +14 -2
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +38 -18
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +10 -1
- package/out/chunk-TBA526RR.js.map +0 -7
- package/out/replicache/src/async-iterable-to-array.js +0 -8
- package/out/replicache/src/async-iterable-to-array.js.map +0 -1
- package/out/replicache/src/bg-interval.js +0 -38
- package/out/replicache/src/bg-interval.js.map +0 -1
- package/out/replicache/src/binary-search.js +0 -31
- package/out/replicache/src/binary-search.js.map +0 -1
- package/out/replicache/src/broadcast-channel.js +0 -29
- package/out/replicache/src/broadcast-channel.js.map +0 -1
- package/out/replicache/src/btree/diff.js +0 -6
- package/out/replicache/src/btree/diff.js.map +0 -1
- package/out/replicache/src/btree/node.js +0 -392
- package/out/replicache/src/btree/node.js.map +0 -1
- package/out/replicache/src/btree/read.js +0 -227
- package/out/replicache/src/btree/read.js.map +0 -1
- package/out/replicache/src/btree/splice.js +0 -79
- package/out/replicache/src/btree/splice.js.map +0 -1
- package/out/replicache/src/btree/write.js +0 -154
- package/out/replicache/src/btree/write.js.map +0 -1
- package/out/replicache/src/call-default-fetch.js +0 -37
- package/out/replicache/src/call-default-fetch.js.map +0 -1
- package/out/replicache/src/config.js +0 -13
- package/out/replicache/src/config.js.map +0 -1
- package/out/replicache/src/connection-loop-delegates.js +0 -30
- package/out/replicache/src/connection-loop-delegates.js.map +0 -1
- package/out/replicache/src/connection-loop.js +0 -268
- package/out/replicache/src/connection-loop.js.map +0 -1
- package/out/replicache/src/cookies.js +0 -41
- package/out/replicache/src/cookies.js.map +0 -1
- package/out/replicache/src/dag/chunk.js +0 -60
- package/out/replicache/src/dag/chunk.js.map +0 -1
- package/out/replicache/src/dag/gc.js +0 -126
- package/out/replicache/src/dag/gc.js.map +0 -1
- package/out/replicache/src/dag/key-type-enum.js +0 -6
- package/out/replicache/src/dag/key-type-enum.js.map +0 -1
- package/out/replicache/src/dag/key.js +0 -54
- package/out/replicache/src/dag/key.js.map +0 -1
- package/out/replicache/src/dag/lazy-store.js +0 -532
- package/out/replicache/src/dag/lazy-store.js.map +0 -1
- package/out/replicache/src/dag/store-impl.js +0 -175
- package/out/replicache/src/dag/store-impl.js.map +0 -1
- package/out/replicache/src/dag/store.js +0 -22
- package/out/replicache/src/dag/store.js.map +0 -1
- package/out/replicache/src/dag/visitor.js +0 -22
- package/out/replicache/src/dag/visitor.js.map +0 -1
- package/out/replicache/src/db/commit.js +0 -443
- package/out/replicache/src/db/commit.js.map +0 -1
- package/out/replicache/src/db/index-operation-enum.js +0 -4
- package/out/replicache/src/db/index-operation-enum.js.map +0 -1
- package/out/replicache/src/db/index.js +0 -170
- package/out/replicache/src/db/index.js.map +0 -1
- package/out/replicache/src/db/meta-type-enum.js +0 -7
- package/out/replicache/src/db/meta-type-enum.js.map +0 -1
- package/out/replicache/src/db/read.js +0 -59
- package/out/replicache/src/db/read.js.map +0 -1
- package/out/replicache/src/db/rebase.js +0 -56
- package/out/replicache/src/db/rebase.js.map +0 -1
- package/out/replicache/src/db/scan.js +0 -2
- package/out/replicache/src/db/scan.js.map +0 -1
- package/out/replicache/src/db/write.js +0 -260
- package/out/replicache/src/db/write.js.map +0 -1
- package/out/replicache/src/error-responses.js +0 -30
- package/out/replicache/src/error-responses.js.map +0 -1
- package/out/replicache/src/filter-async-iterable.js +0 -15
- package/out/replicache/src/filter-async-iterable.js.map +0 -1
- package/out/replicache/src/format-version-enum.js +0 -9
- package/out/replicache/src/format-version-enum.js.map +0 -1
- package/out/replicache/src/format-version.js +0 -8
- package/out/replicache/src/format-version.js.map +0 -1
- package/out/replicache/src/frozen-json.js +0 -151
- package/out/replicache/src/frozen-json.js.map +0 -1
- package/out/replicache/src/get-default-puller.js +0 -74
- package/out/replicache/src/get-default-puller.js.map +0 -1
- package/out/replicache/src/get-default-pusher.js +0 -36
- package/out/replicache/src/get-default-pusher.js.map +0 -1
- package/out/replicache/src/hash.js +0 -73
- package/out/replicache/src/hash.js.map +0 -1
- package/out/replicache/src/http-request-info.js +0 -7
- package/out/replicache/src/http-request-info.js.map +0 -1
- package/out/replicache/src/impl.js +0 -2
- package/out/replicache/src/impl.js.map +0 -1
- package/out/replicache/src/index-defs.js +0 -28
- package/out/replicache/src/index-defs.js.map +0 -1
- package/out/replicache/src/invoke-kind-enum.js +0 -4
- package/out/replicache/src/invoke-kind-enum.js.map +0 -1
- package/out/replicache/src/iterable-union.js +0 -5
- package/out/replicache/src/iterable-union.js.map +0 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js +0 -93
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +0 -1
- package/out/replicache/src/kv/idb-store.js +0 -179
- package/out/replicache/src/kv/idb-store.js.map +0 -1
- package/out/replicache/src/kv/mem-store.js +0 -61
- package/out/replicache/src/kv/mem-store.js.map +0 -1
- package/out/replicache/src/kv/read-impl.js +0 -23
- package/out/replicache/src/kv/read-impl.js.map +0 -1
- package/out/replicache/src/kv/store.js +0 -2
- package/out/replicache/src/kv/store.js.map +0 -1
- package/out/replicache/src/kv/write-impl-base.js +0 -48
- package/out/replicache/src/kv/write-impl-base.js.map +0 -1
- package/out/replicache/src/kv/write-impl.js +0 -25
- package/out/replicache/src/kv/write-impl.js.map +0 -1
- package/out/replicache/src/lazy.js +0 -10
- package/out/replicache/src/lazy.js.map +0 -1
- package/out/replicache/src/log-options.js +0 -13
- package/out/replicache/src/log-options.js.map +0 -1
- package/out/replicache/src/merge-async-iterables.js +0 -61
- package/out/replicache/src/merge-async-iterables.js.map +0 -1
- package/out/replicache/src/mod.js +0 -14
- package/out/replicache/src/mod.js.map +0 -1
- package/out/replicache/src/mutation-recovery.js +0 -439
- package/out/replicache/src/mutation-recovery.js.map +0 -1
- package/out/replicache/src/new-client-channel.js +0 -77
- package/out/replicache/src/new-client-channel.js.map +0 -1
- package/out/replicache/src/on-persist-channel.js +0 -33
- package/out/replicache/src/on-persist-channel.js.map +0 -1
- package/out/replicache/src/patch-operation.js +0 -37
- package/out/replicache/src/patch-operation.js.map +0 -1
- package/out/replicache/src/pending-mutations.js +0 -18
- package/out/replicache/src/pending-mutations.js.map +0 -1
- package/out/replicache/src/persist/client-gc.js +0 -38
- package/out/replicache/src/persist/client-gc.js.map +0 -1
- package/out/replicache/src/persist/client-group-gc.js +0 -38
- package/out/replicache/src/persist/client-group-gc.js.map +0 -1
- package/out/replicache/src/persist/client-groups.js +0 -180
- package/out/replicache/src/persist/client-groups.js.map +0 -1
- package/out/replicache/src/persist/clients.js +0 -390
- package/out/replicache/src/persist/clients.js.map +0 -1
- package/out/replicache/src/persist/collect-idb-databases.js +0 -174
- package/out/replicache/src/persist/collect-idb-databases.js.map +0 -1
- package/out/replicache/src/persist/gather-mem-only-visitor.js +0 -25
- package/out/replicache/src/persist/gather-mem-only-visitor.js.map +0 -1
- package/out/replicache/src/persist/gather-not-cached-visitor.js +0 -35
- package/out/replicache/src/persist/gather-not-cached-visitor.js.map +0 -1
- package/out/replicache/src/persist/heartbeat.js +0 -37
- package/out/replicache/src/persist/heartbeat.js.map +0 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js +0 -18
- package/out/replicache/src/persist/idb-databases-store-db-name.js.map +0 -1
- package/out/replicache/src/persist/idb-databases-store.js +0 -90
- package/out/replicache/src/persist/idb-databases-store.js.map +0 -1
- package/out/replicache/src/persist/make-client-id.js +0 -13
- package/out/replicache/src/persist/make-client-id.js.map +0 -1
- package/out/replicache/src/persist/persist.js +0 -132
- package/out/replicache/src/persist/persist.js.map +0 -1
- package/out/replicache/src/persist/refresh.js +0 -147
- package/out/replicache/src/persist/refresh.js.map +0 -1
- package/out/replicache/src/process-scheduler.js +0 -93
- package/out/replicache/src/process-scheduler.js.map +0 -1
- package/out/replicache/src/puller.js +0 -2
- package/out/replicache/src/puller.js.map +0 -1
- package/out/replicache/src/pusher.js +0 -32
- package/out/replicache/src/pusher.js.map +0 -1
- package/out/replicache/src/replicache-impl.js +0 -1007
- package/out/replicache/src/replicache-impl.js.map +0 -1
- package/out/replicache/src/replicache-options.js +0 -2
- package/out/replicache/src/replicache-options.js.map +0 -1
- package/out/replicache/src/replicache.js +0 -387
- package/out/replicache/src/replicache.js.map +0 -1
- package/out/replicache/src/request-idle.js +0 -15
- package/out/replicache/src/request-idle.js.map +0 -1
- package/out/replicache/src/scan-iterator.js +0 -202
- package/out/replicache/src/scan-iterator.js.map +0 -1
- package/out/replicache/src/scan-options.js +0 -45
- package/out/replicache/src/scan-options.js.map +0 -1
- package/out/replicache/src/set-interval-with-signal.js +0 -7
- package/out/replicache/src/set-interval-with-signal.js.map +0 -1
- package/out/replicache/src/size-of-value.js +0 -77
- package/out/replicache/src/size-of-value.js.map +0 -1
- package/out/replicache/src/subscriptions.js +0 -357
- package/out/replicache/src/subscriptions.js.map +0 -1
- package/out/replicache/src/sync/diff.js +0 -75
- package/out/replicache/src/sync/diff.js.map +0 -1
- package/out/replicache/src/sync/handle-pull-response-result-type-enum.js +0 -5
- package/out/replicache/src/sync/handle-pull-response-result-type-enum.js.map +0 -1
- package/out/replicache/src/sync/ids.js +0 -4
- package/out/replicache/src/sync/ids.js.map +0 -1
- package/out/replicache/src/sync/patch.js +0 -41
- package/out/replicache/src/sync/patch.js.map +0 -1
- package/out/replicache/src/sync/pull-error.js +0 -16
- package/out/replicache/src/sync/pull-error.js.map +0 -1
- package/out/replicache/src/sync/pull.js +0 -375
- package/out/replicache/src/sync/pull.js.map +0 -1
- package/out/replicache/src/sync/push.js +0 -141
- package/out/replicache/src/sync/push.js.map +0 -1
- package/out/replicache/src/sync/request-id.js +0 -31
- package/out/replicache/src/sync/request-id.js.map +0 -1
- package/out/replicache/src/sync/sync-head-name.js +0 -2
- package/out/replicache/src/sync/sync-head-name.js.map +0 -1
- package/out/replicache/src/test-license-key.js +0 -3
- package/out/replicache/src/test-license-key.js.map +0 -1
- package/out/replicache/src/to-error.js +0 -7
- package/out/replicache/src/to-error.js.map +0 -1
- package/out/replicache/src/transaction-closed-error.js +0 -17
- package/out/replicache/src/transaction-closed-error.js.map +0 -1
- package/out/replicache/src/transactions.js +0 -144
- package/out/replicache/src/transactions.js.map +0 -1
- package/out/replicache/src/types.js +0 -2
- package/out/replicache/src/types.js.map +0 -1
- package/out/replicache/src/version.js +0 -6
- package/out/replicache/src/version.js.map +0 -1
- package/out/replicache/src/with-transactions.js +0 -28
- package/out/replicache/src/with-transactions.js.map +0 -1
- package/out/shared/src/browser-env.js +0 -14
- package/out/shared/src/browser-env.js.map +0 -1
- package/out/shared/src/document-visible.js +0 -76
- package/out/shared/src/document-visible.js.map +0 -1
- package/out/shared/src/navigator.js +0 -3
- package/out/shared/src/navigator.js.map +0 -1
- package/out/shared/src/random-uint64.js +0 -8
- package/out/shared/src/random-uint64.js.map +0 -1
- package/out/shared/src/types.js +0 -2
- package/out/shared/src/types.js.map +0 -1
- package/out/zero-client/src/client/context.js +0 -113
- package/out/zero-client/src/client/context.js.map +0 -1
- package/out/zero-client/src/client/crud.js +0 -181
- package/out/zero-client/src/client/crud.js.map +0 -1
- package/out/zero-client/src/client/enable-analytics.js +0 -21
- package/out/zero-client/src/client/enable-analytics.js.map +0 -1
- package/out/zero-client/src/client/http-string.js +0 -14
- package/out/zero-client/src/client/http-string.js.map +0 -1
- package/out/zero-client/src/client/keys.js +0 -32
- package/out/zero-client/src/client/keys.js.map +0 -1
- package/out/zero-client/src/client/log-options.js +0 -57
- package/out/zero-client/src/client/log-options.js.map +0 -1
- package/out/zero-client/src/client/metrics.js +0 -268
- package/out/zero-client/src/client/metrics.js.map +0 -1
- package/out/zero-client/src/client/options.js +0 -2
- package/out/zero-client/src/client/options.js.map +0 -1
- package/out/zero-client/src/client/query-manager.js +0 -146
- package/out/zero-client/src/client/query-manager.js.map +0 -1
- package/out/zero-client/src/client/reload-error-handler.js +0 -23
- package/out/zero-client/src/client/reload-error-handler.js.map +0 -1
- package/out/zero-client/src/client/replicache-types.js +0 -2
- package/out/zero-client/src/client/replicache-types.js.map +0 -1
- package/out/zero-client/src/client/server-error.js +0 -22
- package/out/zero-client/src/client/server-error.js.map +0 -1
- package/out/zero-client/src/client/server-option.js +0 -37
- package/out/zero-client/src/client/server-option.js.map +0 -1
- package/out/zero-client/src/client/version.js +0 -6
- package/out/zero-client/src/client/version.js.map +0 -1
- package/out/zero-client/src/client/zero-poke-handler.js +0 -240
- package/out/zero-client/src/client/zero-poke-handler.js.map +0 -1
- package/out/zero-client/src/client/zero.js +0 -1233
- package/out/zero-client/src/client/zero.js.map +0 -1
- package/out/zero-client/src/mod.js +0 -7
- package/out/zero-client/src/mod.js.map +0 -1
- package/out/zero-client/src/util/nanoid.js +0 -34
- package/out/zero-client/src/util/nanoid.js.map +0 -1
- package/out/zero-client/src/util/socket.js +0 -4
- package/out/zero-client/src/util/socket.js.map +0 -1
- package/out/zql/src/zql/builder/builder.d.ts.map +0 -1
- package/out/zql/src/zql/builder/builder.js.map +0 -1
- package/out/zql/src/zql/builder/error.d.ts.map +0 -1
- package/out/zql/src/zql/builder/error.js.map +0 -1
- package/out/zql/src/zql/builder/filter.d.ts.map +0 -1
- package/out/zql/src/zql/builder/filter.js.map +0 -1
- package/out/zql/src/zql/builder/like.d.ts.map +0 -1
- package/out/zql/src/zql/builder/like.js.map +0 -1
- package/out/zql/src/zql/ivm/array-view.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/array-view.js.map +0 -1
- package/out/zql/src/zql/ivm/change.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/change.js.map +0 -1
- package/out/zql/src/zql/ivm/data.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/data.js.map +0 -1
- package/out/zql/src/zql/ivm/fan-in.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/fan-in.js.map +0 -1
- package/out/zql/src/zql/ivm/fan-out.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/fan-out.js.map +0 -1
- package/out/zql/src/zql/ivm/filter.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/filter.js.map +0 -1
- package/out/zql/src/zql/ivm/join.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/join.js.map +0 -1
- package/out/zql/src/zql/ivm/lookahead-iterator.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/lookahead-iterator.js.map +0 -1
- package/out/zql/src/zql/ivm/maybe-split-and-push-edit-change.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/maybe-split-and-push-edit-change.js.map +0 -1
- package/out/zql/src/zql/ivm/memory-source.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/memory-source.js.map +0 -1
- package/out/zql/src/zql/ivm/memory-storage.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/memory-storage.js +0 -33
- package/out/zql/src/zql/ivm/memory-storage.js.map +0 -1
- package/out/zql/src/zql/ivm/operator.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/operator.js.map +0 -1
- package/out/zql/src/zql/ivm/schema.d.ts +0 -29
- package/out/zql/src/zql/ivm/schema.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/schema.js +0 -3
- package/out/zql/src/zql/ivm/schema.js.map +0 -1
- package/out/zql/src/zql/ivm/skip.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/skip.js.map +0 -1
- package/out/zql/src/zql/ivm/source.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/source.js.map +0 -1
- package/out/zql/src/zql/ivm/stream.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/stream.js.map +0 -1
- package/out/zql/src/zql/ivm/take.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/take.js.map +0 -1
- package/out/zql/src/zql/ivm/view-apply-change.d.ts +0 -5
- package/out/zql/src/zql/ivm/view-apply-change.d.ts.map +0 -1
- package/out/zql/src/zql/ivm/view-apply-change.js.map +0 -1
- package/out/zql/src/zql/ivm/view.d.ts.map +0 -1
- package/out/zql/src/zql/query/escape-like.d.ts.map +0 -1
- package/out/zql/src/zql/query/escape-like.js +0 -4
- package/out/zql/src/zql/query/escape-like.js.map +0 -1
- package/out/zql/src/zql/query/expression.d.ts +0 -25
- package/out/zql/src/zql/query/expression.d.ts.map +0 -1
- package/out/zql/src/zql/query/expression.js.map +0 -1
- package/out/zql/src/zql/query/normalize-table-schema.d.ts.map +0 -1
- package/out/zql/src/zql/query/normalize-table-schema.js.map +0 -1
- package/out/zql/src/zql/query/query-impl.d.ts.map +0 -1
- package/out/zql/src/zql/query/query-impl.js.map +0 -1
- package/out/zql/src/zql/query/query-internal.d.ts.map +0 -1
- package/out/zql/src/zql/query/query-internal.js.map +0 -1
- package/out/zql/src/zql/query/query.d.ts.map +0 -1
- package/out/zql/src/zql/query/query.js.map +0 -1
- package/out/zql/src/zql/query/schema.d.ts.map +0 -1
- package/out/zql/src/zql/query/schema.js.map +0 -1
- package/out/zql/src/zql/query/typed-view.d.ts.map +0 -1
- package/out/zql/src/zql/query/typed-view.js.map +0 -1
- /package/out/zql/src/{zql/builder → builder}/error.d.ts +0 -0
- /package/out/zql/src/{zql/builder → builder}/error.js +0 -0
- /package/out/zql/src/{zql/builder → builder}/like.d.ts +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/array-view.d.ts +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/change.js +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/lookahead-iterator.d.ts +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/lookahead-iterator.js +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/maybe-split-and-push-edit-change.js +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/operator.js +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/source.js +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/stream.d.ts +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/stream.js +0 -0
- /package/out/zql/src/{zql/ivm → ivm}/view.js +0 -0
- /package/out/zql/src/{zql/query → query}/escape-like.d.ts +0 -0
- /package/out/zql/src/{zql/query → query}/query-internal.js +0 -0
- /package/out/zql/src/{zql/query → query}/query.js +0 -0
- /package/out/zql/src/{zql/query → query}/typed-view.js +0 -0
|
@@ -1,1233 +0,0 @@
|
|
|
1
|
-
import { LogContext } from '@rocicorp/logger';
|
|
2
|
-
import { resolver } from '@rocicorp/resolver';
|
|
3
|
-
import { ReplicacheImpl, } from '../../../replicache/src/impl.js';
|
|
4
|
-
import { dropDatabase, } from '../../../replicache/src/mod.js';
|
|
5
|
-
import { assert, unreachable } from '../../../shared/src/asserts.js';
|
|
6
|
-
import { getBrowserGlobal, mustGetBrowserGlobal, } from '../../../shared/src/browser-env.js';
|
|
7
|
-
import { getDocumentVisibilityWatcher } from '../../../shared/src/document-visible.js';
|
|
8
|
-
import { must } from '../../../shared/src/must.js';
|
|
9
|
-
import { navigator } from '../../../shared/src/navigator.js';
|
|
10
|
-
import { sleep, sleepWithAbort } from '../../../shared/src/sleep.js';
|
|
11
|
-
import * as valita from '../../../shared/src/valita.js';
|
|
12
|
-
import { CRUD_MUTATION_NAME, ErrorKind, MutationType, downstreamSchema, encodeSecProtocols, nullableVersionSchema, } from '../../../zero-protocol/src/mod.js';
|
|
13
|
-
import { newQuery } from '../../../zql/src/zql/query/query-impl.js';
|
|
14
|
-
import { nanoid } from '../util/nanoid.js';
|
|
15
|
-
import { send } from '../util/socket.js';
|
|
16
|
-
import { ZeroContext } from './context.js';
|
|
17
|
-
import { makeCRUDMutate, makeCRUDMutator, } from './crud.js';
|
|
18
|
-
import { shouldEnableAnalytics } from './enable-analytics.js';
|
|
19
|
-
import { toWSString } from './http-string.js';
|
|
20
|
-
import { ENTITIES_KEY_PREFIX } from './keys.js';
|
|
21
|
-
import { createLogOptions } from './log-options.js';
|
|
22
|
-
import { DID_NOT_CONNECT_VALUE, MetricManager, REPORT_INTERVAL_MS, getLastConnectErrorValue, } from './metrics.js';
|
|
23
|
-
import { normalizeSchema } from './normalized-schema.js';
|
|
24
|
-
import { QueryManager } from './query-manager.js';
|
|
25
|
-
import { reloadWithReason, reportReloadReason } from './reload-error-handler.js';
|
|
26
|
-
import { ServerError, isAuthError, isServerError } from './server-error.js';
|
|
27
|
-
import { getServer } from './server-option.js';
|
|
28
|
-
import { version } from './version.js';
|
|
29
|
-
import { PokeHandler } from './zero-poke-handler.js';
|
|
30
|
-
export function createSchema(schema) {
|
|
31
|
-
return schema;
|
|
32
|
-
}
|
|
33
|
-
export const onSetConnectionStateSymbol = Symbol();
|
|
34
|
-
export const exposedToTestingSymbol = Symbol();
|
|
35
|
-
export const createLogOptionsSymbol = Symbol();
|
|
36
|
-
function asTestZero(z) {
|
|
37
|
-
return z;
|
|
38
|
-
}
|
|
39
|
-
export var ConnectionState;
|
|
40
|
-
(function (ConnectionState) {
|
|
41
|
-
ConnectionState[ConnectionState["Disconnected"] = 0] = "Disconnected";
|
|
42
|
-
ConnectionState[ConnectionState["Connecting"] = 1] = "Connecting";
|
|
43
|
-
ConnectionState[ConnectionState["Connected"] = 2] = "Connected";
|
|
44
|
-
})(ConnectionState || (ConnectionState = {}));
|
|
45
|
-
export const RUN_LOOP_INTERVAL_MS = 5_000;
|
|
46
|
-
/**
|
|
47
|
-
* How frequently we should ping the server to keep the connection alive.
|
|
48
|
-
*/
|
|
49
|
-
export const PING_INTERVAL_MS = 5_000;
|
|
50
|
-
/**
|
|
51
|
-
* The amount of time we wait for a pong before we consider the ping timed out.
|
|
52
|
-
*/
|
|
53
|
-
export const PING_TIMEOUT_MS = 5_000;
|
|
54
|
-
/**
|
|
55
|
-
* The amount of time we wait for a pull response before we consider a pull
|
|
56
|
-
* request timed out.
|
|
57
|
-
*/
|
|
58
|
-
export const PULL_TIMEOUT_MS = 5_000;
|
|
59
|
-
export const DEFAULT_DISCONNECT_HIDDEN_DELAY_MS = 5_000;
|
|
60
|
-
/**
|
|
61
|
-
* The amount of time we wait for a connection to be established before we
|
|
62
|
-
* consider it timed out.
|
|
63
|
-
*/
|
|
64
|
-
export const CONNECT_TIMEOUT_MS = 10_000;
|
|
65
|
-
const CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY = 6;
|
|
66
|
-
const NULL_LAST_MUTATION_ID_SENT = { clientID: '', id: -1 };
|
|
67
|
-
// When the protocol changes (pull, push, poke,...) we need to bump this.
|
|
68
|
-
const REFLECT_VERSION = 1;
|
|
69
|
-
function convertOnUpdateNeededReason(reason) {
|
|
70
|
-
return { type: reason.type };
|
|
71
|
-
}
|
|
72
|
-
function updateNeededReloadReason(reason, serverErrMsg) {
|
|
73
|
-
const { type } = reason;
|
|
74
|
-
let reasonMsg = '';
|
|
75
|
-
switch (type) {
|
|
76
|
-
case 'NewClientGroup':
|
|
77
|
-
reasonMsg =
|
|
78
|
-
"This client could not sync with a newer client. This is probably due to another tab loading a newer incompatible version of the app's code.";
|
|
79
|
-
break;
|
|
80
|
-
case 'VersionNotSupported':
|
|
81
|
-
reasonMsg =
|
|
82
|
-
"The server no longer supports this client's protocol version.";
|
|
83
|
-
break;
|
|
84
|
-
case 'SchemaVersionNotSupported':
|
|
85
|
-
reasonMsg = "The server no longer supports this client's schema version.";
|
|
86
|
-
break;
|
|
87
|
-
default:
|
|
88
|
-
unreachable(type);
|
|
89
|
-
}
|
|
90
|
-
if (serverErrMsg) {
|
|
91
|
-
reasonMsg += ' ' + serverErrMsg;
|
|
92
|
-
}
|
|
93
|
-
return reasonMsg;
|
|
94
|
-
}
|
|
95
|
-
function serverAheadReloadReason(kind) {
|
|
96
|
-
return `Server reported that client is ahead of server (${kind}). This probably happened because the server is in development mode and restarted. Currently when this happens, the dev server loses its state and on reconnect sees the client as ahead. If you see this in other cases, it may be a bug in Zero.`;
|
|
97
|
-
}
|
|
98
|
-
function onClientStateNotFoundServerReason(serverErrMsg) {
|
|
99
|
-
return `Server could not find state needed to synchronize this client. ${serverErrMsg}`;
|
|
100
|
-
}
|
|
101
|
-
const ON_CLIENT_STATE_NOT_FOUND_REASON_CLIENT = 'The local persistent state needed to synchronize this client has been garbage collected.';
|
|
102
|
-
var PingResult;
|
|
103
|
-
(function (PingResult) {
|
|
104
|
-
PingResult[PingResult["TimedOut"] = 0] = "TimedOut";
|
|
105
|
-
PingResult[PingResult["Success"] = 1] = "Success";
|
|
106
|
-
})(PingResult || (PingResult = {}));
|
|
107
|
-
const internalReplicacheImplMap = new WeakMap();
|
|
108
|
-
export function getInternalReplicacheImplForTesting(z) {
|
|
109
|
-
return must(internalReplicacheImplMap.get(z));
|
|
110
|
-
}
|
|
111
|
-
export class Zero {
|
|
112
|
-
version = version;
|
|
113
|
-
#rep;
|
|
114
|
-
#server;
|
|
115
|
-
userID;
|
|
116
|
-
#lc;
|
|
117
|
-
#logOptions;
|
|
118
|
-
#enableAnalytics;
|
|
119
|
-
#pokeHandler;
|
|
120
|
-
#queryManager;
|
|
121
|
-
/**
|
|
122
|
-
* The queries we sent when inside the sec-protocol header when establishing a connection.
|
|
123
|
-
* More queries could be registered while we're waiting for the 'connected' message
|
|
124
|
-
* to come back from the server. To understand what queries we need to send
|
|
125
|
-
* to the server, we diff the `initConnectionQueries` with the current set of desired queries.
|
|
126
|
-
*
|
|
127
|
-
* If this is set to `undefined` that means no queries were sent inside the `sec-protocol` header
|
|
128
|
-
* and an `initConnection` message must be sent to the server after receiving the `connected` message.
|
|
129
|
-
*/
|
|
130
|
-
#initConnectionQueries;
|
|
131
|
-
#lastMutationIDSent = NULL_LAST_MUTATION_ID_SENT;
|
|
132
|
-
#onPong = () => undefined;
|
|
133
|
-
#online = false;
|
|
134
|
-
/**
|
|
135
|
-
* `onOnlineChange` is called when the Zero instance's online status
|
|
136
|
-
* changes.
|
|
137
|
-
*/
|
|
138
|
-
onOnlineChange = null;
|
|
139
|
-
#onUpdateNeeded = null;
|
|
140
|
-
#onClientStateNotFound = null;
|
|
141
|
-
#jurisdiction;
|
|
142
|
-
// Last cookie used to initiate a connection
|
|
143
|
-
#connectCookie = null;
|
|
144
|
-
// Total number of sockets successfully connected by this client
|
|
145
|
-
#connectedCount = 0;
|
|
146
|
-
// Number of messages received over currently connected socket. Reset
|
|
147
|
-
// on disconnect.
|
|
148
|
-
#messageCount = 0;
|
|
149
|
-
#connectedAt = 0;
|
|
150
|
-
// Reset on successful connection.
|
|
151
|
-
#connectErrorCount = 0;
|
|
152
|
-
#abortPingTimeout = () => {
|
|
153
|
-
// intentionally empty
|
|
154
|
-
};
|
|
155
|
-
#zeroContext;
|
|
156
|
-
/**
|
|
157
|
-
* `onUpdateNeeded` is called when a code update is needed.
|
|
158
|
-
*
|
|
159
|
-
* A code update can be needed because:
|
|
160
|
-
* - the server no longer supports the protocol version of the current code,
|
|
161
|
-
* - a new Zero client has created a new client group, because its code
|
|
162
|
-
* has different mutators, indexes, schema version and/or format version
|
|
163
|
-
* from this Zero client. This is likely due to the new client having
|
|
164
|
-
* newer code. A code update is needed to be able to locally sync with this
|
|
165
|
-
* new Zero client (i.e. to sync while offline, the clients can can
|
|
166
|
-
* still sync with each other via the server).
|
|
167
|
-
*
|
|
168
|
-
* The default behavior is to reload the page (using `location.reload()`). Set
|
|
169
|
-
* this to `null` or provide your own function to prevent the page from
|
|
170
|
-
* reloading automatically. You may want to provide your own function to
|
|
171
|
-
* display a toast to inform the end user there is a new version of your app
|
|
172
|
-
* available and prompting them to refresh.
|
|
173
|
-
*/
|
|
174
|
-
get onUpdateNeeded() {
|
|
175
|
-
return this.#onUpdateNeeded;
|
|
176
|
-
}
|
|
177
|
-
set onUpdateNeeded(callback) {
|
|
178
|
-
this.#onUpdateNeeded = callback;
|
|
179
|
-
this.#rep.onUpdateNeeded =
|
|
180
|
-
callback &&
|
|
181
|
-
(reason => {
|
|
182
|
-
callback(convertOnUpdateNeededReason(reason));
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* `onClientStateNotFound` is called when this client will no longer be able
|
|
187
|
-
* to sync due to missing synchronization state. This can be because:
|
|
188
|
-
* - the local persistent synchronization state has been garbage collected.
|
|
189
|
-
* This can happen if the client has no pending mutations and has not been
|
|
190
|
-
* used for a while.
|
|
191
|
-
* - the zero-cache fails to find the synchronization state of this client.
|
|
192
|
-
*
|
|
193
|
-
* The default behavior is to reload the page (using `location.reload()`). Set
|
|
194
|
-
* this to `null` or provide your own function to prevent the page from
|
|
195
|
-
* reloading automatically.
|
|
196
|
-
*/
|
|
197
|
-
get onClientStateNotFound() {
|
|
198
|
-
return this.#onClientStateNotFound;
|
|
199
|
-
}
|
|
200
|
-
set onClientStateNotFound(value) {
|
|
201
|
-
this.#onClientStateNotFound = value;
|
|
202
|
-
this.#rep.onClientStateNotFound = value;
|
|
203
|
-
}
|
|
204
|
-
#connectResolver = resolver();
|
|
205
|
-
#pendingPullsByRequestID = new Map();
|
|
206
|
-
#lastMutationIDReceived = 0;
|
|
207
|
-
#socket = undefined;
|
|
208
|
-
#socketResolver = resolver();
|
|
209
|
-
#connectionStateChangeResolver = resolver();
|
|
210
|
-
/**
|
|
211
|
-
* This resolver is only used for rejections. It is awaited in the connected
|
|
212
|
-
* state (including when waiting for a pong). It is rejected when we get an
|
|
213
|
-
* invalid message or an 'error' message.
|
|
214
|
-
*/
|
|
215
|
-
#rejectMessageError = undefined;
|
|
216
|
-
#closeAbortController = new AbortController();
|
|
217
|
-
#visibilityWatcher;
|
|
218
|
-
// We use an accessor pair to allow the subclass to override the setter.
|
|
219
|
-
#connectionState = ConnectionState.Disconnected;
|
|
220
|
-
#setConnectionState(state) {
|
|
221
|
-
if (state === this.#connectionState) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
this.#connectionState = state;
|
|
225
|
-
this.#connectionStateChangeResolver.resolve(state);
|
|
226
|
-
this.#connectionStateChangeResolver = resolver();
|
|
227
|
-
if (TESTING) {
|
|
228
|
-
asTestZero(this)[onSetConnectionStateSymbol]?.(state);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
#connectStart = undefined;
|
|
232
|
-
// Set on connect attempt if currently undefined.
|
|
233
|
-
// Reset to undefined when
|
|
234
|
-
// 1. client stops trying to connect because it is hidden
|
|
235
|
-
// 2. client encounters a connect error and canary request indicates
|
|
236
|
-
// the client is offline
|
|
237
|
-
// 2. client successfully connects
|
|
238
|
-
#totalToConnectStart = undefined;
|
|
239
|
-
#options;
|
|
240
|
-
query;
|
|
241
|
-
// TODO: Metrics needs to be rethought entirely as we're not going to
|
|
242
|
-
// send metrics to customer server.
|
|
243
|
-
#metrics;
|
|
244
|
-
// Store as field to allow test subclass to override. Web API doesn't allow
|
|
245
|
-
// overwriting location fields for security reasons.
|
|
246
|
-
#reload = () => getBrowserGlobal('location')?.reload();
|
|
247
|
-
/**
|
|
248
|
-
* Constructs a new Zero client.
|
|
249
|
-
*/
|
|
250
|
-
constructor(options) {
|
|
251
|
-
const { userID, onOnlineChange, jurisdiction, hiddenTabDisconnectDelay = DEFAULT_DISCONNECT_HIDDEN_DELAY_MS, kvStore = 'idb', schema, batchViewUpdates = applyViewUpdates => applyViewUpdates(), } = options;
|
|
252
|
-
if (!userID) {
|
|
253
|
-
throw new Error('ZeroOptions.userID must not be empty.');
|
|
254
|
-
}
|
|
255
|
-
const server = getServer(options.server);
|
|
256
|
-
this.#enableAnalytics = shouldEnableAnalytics(server, options.enableAnalytics);
|
|
257
|
-
if (jurisdiction !== undefined && jurisdiction !== 'eu') {
|
|
258
|
-
throw new Error('ZeroOptions.jurisdiction must be "eu" if present.');
|
|
259
|
-
}
|
|
260
|
-
if (hiddenTabDisconnectDelay < 0) {
|
|
261
|
-
throw new Error('ZeroOptions.hiddenTabDisconnectDelay must not be negative.');
|
|
262
|
-
}
|
|
263
|
-
this.onOnlineChange = onOnlineChange;
|
|
264
|
-
this.#options = options;
|
|
265
|
-
this.#logOptions = this.#createLogOptions({
|
|
266
|
-
consoleLogLevel: options.logLevel ?? 'error',
|
|
267
|
-
server: null, //server, // Reenable remote logging
|
|
268
|
-
enableAnalytics: this.#enableAnalytics,
|
|
269
|
-
});
|
|
270
|
-
const logOptions = this.#logOptions;
|
|
271
|
-
const normalizedSchema = normalizeSchema(schema);
|
|
272
|
-
const replicacheMutators = {
|
|
273
|
-
['_zero_crud']: makeCRUDMutator(normalizedSchema),
|
|
274
|
-
};
|
|
275
|
-
const replicacheOptions = {
|
|
276
|
-
schemaVersion: normalizedSchema.version.toString(),
|
|
277
|
-
logLevel: logOptions.logLevel,
|
|
278
|
-
logSinks: [logOptions.logSink],
|
|
279
|
-
mutators: replicacheMutators,
|
|
280
|
-
name: `zero-${userID}`,
|
|
281
|
-
pusher: (req, reqID) => this.#pusher(req, reqID),
|
|
282
|
-
puller: (req, reqID) => this.#puller(req, reqID),
|
|
283
|
-
pushDelay: 0,
|
|
284
|
-
requestOptions: {
|
|
285
|
-
maxDelayMs: 0,
|
|
286
|
-
minDelayMs: 0,
|
|
287
|
-
},
|
|
288
|
-
licenseKey: 'zero-client-static-key',
|
|
289
|
-
kvStore,
|
|
290
|
-
};
|
|
291
|
-
const replicacheImplOptions = {
|
|
292
|
-
enableClientGroupForking: false,
|
|
293
|
-
};
|
|
294
|
-
const rep = new ReplicacheImpl(replicacheOptions, replicacheImplOptions);
|
|
295
|
-
this.#rep = rep;
|
|
296
|
-
if (TESTING) {
|
|
297
|
-
internalReplicacheImplMap.set(this, rep);
|
|
298
|
-
}
|
|
299
|
-
rep.getAuth = this.#getAuthToken;
|
|
300
|
-
this.#server = server;
|
|
301
|
-
this.userID = userID;
|
|
302
|
-
this.#jurisdiction = jurisdiction;
|
|
303
|
-
this.#lc = new LogContext(logOptions.logLevel, { clientID: rep.clientID }, logOptions.logSink);
|
|
304
|
-
this.onUpdateNeeded = (reason, serverErrorMsg) => {
|
|
305
|
-
reloadWithReason(this.#lc, this.#reload, updateNeededReloadReason(reason, serverErrorMsg));
|
|
306
|
-
};
|
|
307
|
-
this.onClientStateNotFound = (reason) => {
|
|
308
|
-
reloadWithReason(this.#lc, this.#reload, reason ?? ON_CLIENT_STATE_NOT_FOUND_REASON_CLIENT);
|
|
309
|
-
};
|
|
310
|
-
this.mutate = makeCRUDMutate(normalizedSchema, rep.mutate);
|
|
311
|
-
this.#queryManager = new QueryManager(rep.clientID, msg => this.#sendChangeDesiredQueries(msg), rep.experimentalWatch.bind(rep));
|
|
312
|
-
this.#zeroContext = new ZeroContext(normalizedSchema.tables, (ast, gotCallback) => this.#queryManager.add(ast, gotCallback), batchViewUpdates);
|
|
313
|
-
rep.experimentalWatch(diff => this.#zeroContext.processChanges(diff), {
|
|
314
|
-
prefix: ENTITIES_KEY_PREFIX,
|
|
315
|
-
initialValuesInFirstDiff: true,
|
|
316
|
-
});
|
|
317
|
-
this.query = this.#registerQueries(normalizedSchema);
|
|
318
|
-
reportReloadReason(this.#lc);
|
|
319
|
-
this.#metrics = new MetricManager({
|
|
320
|
-
reportIntervalMs: REPORT_INTERVAL_MS,
|
|
321
|
-
host: getBrowserGlobal('location')?.host ?? '',
|
|
322
|
-
source: 'client',
|
|
323
|
-
reporter: this.#enableAnalytics
|
|
324
|
-
? allSeries => this.#reportMetrics(allSeries)
|
|
325
|
-
: () => Promise.resolve(),
|
|
326
|
-
lc: this.#lc,
|
|
327
|
-
});
|
|
328
|
-
this.#metrics.tags.push(`version:${this.version}`);
|
|
329
|
-
this.#pokeHandler = new PokeHandler(poke => this.#rep.poke(poke), () => this.#onPokeError(), rep.clientID, normalizedSchema, this.#lc);
|
|
330
|
-
this.#visibilityWatcher = getDocumentVisibilityWatcher(getBrowserGlobal('document'), hiddenTabDisconnectDelay, this.#closeAbortController.signal);
|
|
331
|
-
void this.#runLoop();
|
|
332
|
-
if (TESTING) {
|
|
333
|
-
asTestZero(this)[exposedToTestingSymbol] = {
|
|
334
|
-
puller: this.#puller,
|
|
335
|
-
pusher: this.#pusher,
|
|
336
|
-
setReload: (r) => {
|
|
337
|
-
this.#reload = r;
|
|
338
|
-
},
|
|
339
|
-
logOptions: this.#logOptions,
|
|
340
|
-
connectStart: () => this.#connectStart,
|
|
341
|
-
socketResolver: () => this.#socketResolver,
|
|
342
|
-
connectionState: () => this.#connectionState,
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
#sendChangeDesiredQueries(msg) {
|
|
347
|
-
if (this.#socket && this.#connectionState === ConnectionState.Connected) {
|
|
348
|
-
send(this.#socket, msg);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
#createLogOptions(options) {
|
|
352
|
-
if (TESTING) {
|
|
353
|
-
const testZero = asTestZero(this);
|
|
354
|
-
if (testZero[createLogOptionsSymbol]) {
|
|
355
|
-
return testZero[createLogOptionsSymbol](options);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return createLogOptions(options);
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* The name of the IndexedDB database in which the data of this
|
|
362
|
-
* instance of Zero is stored.
|
|
363
|
-
*/
|
|
364
|
-
get idbName() {
|
|
365
|
-
return this.#rep.idbName;
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* The schema version of the data understood by this application.
|
|
369
|
-
* See [[ZeroOptions.schemaVersion]].
|
|
370
|
-
*/
|
|
371
|
-
get schemaVersion() {
|
|
372
|
-
return this.#rep.schemaVersion;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* The client ID for this instance of Zero. Each instance
|
|
376
|
-
* gets a unique client ID.
|
|
377
|
-
*/
|
|
378
|
-
get clientID() {
|
|
379
|
-
return this.#rep.clientID;
|
|
380
|
-
}
|
|
381
|
-
get clientGroupID() {
|
|
382
|
-
return this.#rep.clientGroupID;
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Provides facilities to write data to Zero.
|
|
386
|
-
*
|
|
387
|
-
* `mutate` is a function as well as a "namespace" object for doing CRUD style
|
|
388
|
-
* mutations. When used as a function it is used to batch multiple mutations.
|
|
389
|
-
*
|
|
390
|
-
* ```ts
|
|
391
|
-
* await zero.mutate.issue.create({id: '1', title: 'First issue'});
|
|
392
|
-
* await zero.mutate.comment.create({id: '1', text: 'First comment', issueID: '1'});
|
|
393
|
-
*
|
|
394
|
-
* // or as a function:
|
|
395
|
-
* await zero.mutate(m => {
|
|
396
|
-
* await m.issue.create({id: '1', title: 'First issue'});
|
|
397
|
-
* await m.comment.create({id: '1', text: 'First comment', issueID: '1'});
|
|
398
|
-
* });
|
|
399
|
-
* ```
|
|
400
|
-
*
|
|
401
|
-
* The benefit of using the function form is that it allows you to batch
|
|
402
|
-
* multiple mutations together. This can be more efficient than making
|
|
403
|
-
* individual calls to `create`, `update`, `set`, and `delete`.
|
|
404
|
-
*
|
|
405
|
-
* The function form of `mutate` is not allowed to be called inside another
|
|
406
|
-
* `mutate` function. Doing so will throw an error.
|
|
407
|
-
*/
|
|
408
|
-
mutate;
|
|
409
|
-
/**
|
|
410
|
-
* Whether this Zero instance has been closed. Once a Zero instance has
|
|
411
|
-
* been closed it no longer syncs and you can no longer read or write data out
|
|
412
|
-
* of it. After it has been closed it is pretty much useless and should not be
|
|
413
|
-
* used any more.
|
|
414
|
-
*/
|
|
415
|
-
get closed() {
|
|
416
|
-
return this.#rep.closed;
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Closes this Zero instance.
|
|
420
|
-
*
|
|
421
|
-
* When closed all subscriptions end and no more read or writes are allowed.
|
|
422
|
-
*/
|
|
423
|
-
close() {
|
|
424
|
-
const lc = this.#lc.withContext('close');
|
|
425
|
-
if (this.#connectionState !== ConnectionState.Disconnected) {
|
|
426
|
-
this.#disconnect(lc, {
|
|
427
|
-
client: 'ClientClosed',
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
lc.debug?.('Aborting closeAbortController due to close()');
|
|
431
|
-
this.#closeAbortController.abort();
|
|
432
|
-
this.#metrics.stop();
|
|
433
|
-
return this.#rep.close();
|
|
434
|
-
}
|
|
435
|
-
#onMessage = (e) => {
|
|
436
|
-
const lc = this.#lc;
|
|
437
|
-
lc.debug?.('received message', e.data);
|
|
438
|
-
if (this.closed) {
|
|
439
|
-
lc.debug?.('ignoring message because already closed');
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
const rejectInvalidMessage = (e) => this.#rejectMessageError?.reject(new Error(`Invalid message received from server: ${e instanceof Error ? e.message + '. ' : ''}${data}`));
|
|
443
|
-
let downMessage;
|
|
444
|
-
const { data } = e;
|
|
445
|
-
try {
|
|
446
|
-
downMessage = valita.parse(JSON.parse(data), downstreamSchema);
|
|
447
|
-
}
|
|
448
|
-
catch (e) {
|
|
449
|
-
rejectInvalidMessage(e);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
this.#messageCount++;
|
|
453
|
-
const msgType = downMessage[0];
|
|
454
|
-
switch (msgType) {
|
|
455
|
-
case 'connected':
|
|
456
|
-
return this.#handleConnectedMessage(lc, downMessage);
|
|
457
|
-
case 'error':
|
|
458
|
-
return this.#handleErrorMessage(lc, downMessage);
|
|
459
|
-
case 'pong':
|
|
460
|
-
return this.#onPong();
|
|
461
|
-
case 'pokeStart':
|
|
462
|
-
return this.#handlePokeStart(lc, downMessage);
|
|
463
|
-
case 'pokePart':
|
|
464
|
-
return this.#handlePokePart(lc, downMessage);
|
|
465
|
-
case 'pokeEnd':
|
|
466
|
-
return this.#handlePokeEnd(lc, downMessage);
|
|
467
|
-
case 'pull':
|
|
468
|
-
return this.#handlePullResponse(lc, downMessage);
|
|
469
|
-
case 'warm':
|
|
470
|
-
// we ignore warming messages
|
|
471
|
-
break;
|
|
472
|
-
default:
|
|
473
|
-
msgType;
|
|
474
|
-
rejectInvalidMessage();
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
#onOpen = (e) => {
|
|
478
|
-
const l = addWebSocketIDFromSocketToLogContext(e.target, this.#lc);
|
|
479
|
-
if (this.#connectStart === undefined) {
|
|
480
|
-
l.error?.('Got open event but connect start time is undefined. This should not happen.');
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
const now = Date.now();
|
|
484
|
-
const timeToOpenMs = now - this.#connectStart;
|
|
485
|
-
l.info?.('Got socket open event', {
|
|
486
|
-
navigatorOnline: navigator?.onLine,
|
|
487
|
-
timeToOpenMs,
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
|
-
#onClose = (e) => {
|
|
492
|
-
const l = addWebSocketIDFromSocketToLogContext(e.target, this.#lc);
|
|
493
|
-
const { code, reason, wasClean } = e;
|
|
494
|
-
l.info?.('Got socket close event', { code, reason, wasClean });
|
|
495
|
-
const closeKind = wasClean ? 'CleanClose' : 'AbruptClose';
|
|
496
|
-
this.#connectResolver.reject(new CloseError(closeKind));
|
|
497
|
-
this.#disconnect(l, { client: closeKind });
|
|
498
|
-
};
|
|
499
|
-
// An error on the connection is fatal for the connection.
|
|
500
|
-
async #handleErrorMessage(lc, downMessage) {
|
|
501
|
-
const [, kind, message] = downMessage;
|
|
502
|
-
// Rate limit errors are not fatal to the connection.
|
|
503
|
-
// We really don't want to disconnect and reconnect a rate limited user as
|
|
504
|
-
// it'll use more resources on the server
|
|
505
|
-
if (kind === ErrorKind.MutationRateLimited) {
|
|
506
|
-
this.#lastMutationIDSent = NULL_LAST_MUTATION_ID_SENT;
|
|
507
|
-
lc.error?.('Mutation rate limited', { message });
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
lc.info?.(`${kind}: ${message}}`);
|
|
511
|
-
const error = new ServerError(kind, message);
|
|
512
|
-
this.#rejectMessageError?.reject(error);
|
|
513
|
-
lc.debug?.('Rejecting connect resolver due to error', error);
|
|
514
|
-
this.#connectResolver.reject(error);
|
|
515
|
-
this.#disconnect(lc, { server: kind });
|
|
516
|
-
if (kind === ErrorKind.VersionNotSupported) {
|
|
517
|
-
this.#onUpdateNeeded?.({ type: kind }, message);
|
|
518
|
-
}
|
|
519
|
-
else if (kind === ErrorKind.SchemaVersionNotSupported) {
|
|
520
|
-
await this.#rep.disableClientGroup();
|
|
521
|
-
this.#onUpdateNeeded?.({ type: 'SchemaVersionNotSupported' }, message);
|
|
522
|
-
}
|
|
523
|
-
else if (kind === ErrorKind.ClientNotFound) {
|
|
524
|
-
await this.#rep.disableClientGroup();
|
|
525
|
-
this.#onClientStateNotFound?.(onClientStateNotFoundServerReason(message));
|
|
526
|
-
}
|
|
527
|
-
else if (kind === ErrorKind.InvalidConnectionRequestLastMutationID ||
|
|
528
|
-
kind === ErrorKind.InvalidConnectionRequestBaseCookie) {
|
|
529
|
-
await dropDatabase(this.#rep.idbName);
|
|
530
|
-
reloadWithReason(lc, this.#reload, serverAheadReloadReason(kind));
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
async #handleConnectedMessage(lc, connectedMessage) {
|
|
534
|
-
const now = Date.now();
|
|
535
|
-
const [, connectBody] = connectedMessage;
|
|
536
|
-
lc = addWebSocketIDToLogContext(connectBody.wsid, lc);
|
|
537
|
-
if (this.#connectedCount === 0) {
|
|
538
|
-
this.#checkConnectivity('firstConnect');
|
|
539
|
-
}
|
|
540
|
-
else if (this.#connectErrorCount > 0) {
|
|
541
|
-
this.#checkConnectivity('connectAfterError');
|
|
542
|
-
}
|
|
543
|
-
this.#connectedCount++;
|
|
544
|
-
this.#connectedAt = now;
|
|
545
|
-
this.#metrics.lastConnectError.clear();
|
|
546
|
-
const proceedingConnectErrorCount = this.#connectErrorCount;
|
|
547
|
-
this.#connectErrorCount = 0;
|
|
548
|
-
let timeToConnectMs = undefined;
|
|
549
|
-
let connectMsgLatencyMs = undefined;
|
|
550
|
-
if (this.#connectStart === undefined) {
|
|
551
|
-
lc.error?.('Got connected message but connect start time is undefined. This should not happen.');
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
timeToConnectMs = now - this.#connectStart;
|
|
555
|
-
this.#metrics.timeToConnectMs.set(timeToConnectMs);
|
|
556
|
-
connectMsgLatencyMs =
|
|
557
|
-
connectBody.timestamp !== undefined
|
|
558
|
-
? now - connectBody.timestamp
|
|
559
|
-
: undefined;
|
|
560
|
-
this.#connectStart = undefined;
|
|
561
|
-
}
|
|
562
|
-
let totalTimeToConnectMs = undefined;
|
|
563
|
-
if (this.#totalToConnectStart === undefined) {
|
|
564
|
-
lc.error?.('Got connected message but total to connect start time is undefined. This should not happen.');
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
totalTimeToConnectMs = now - this.#totalToConnectStart;
|
|
568
|
-
this.#totalToConnectStart = undefined;
|
|
569
|
-
}
|
|
570
|
-
this.#metrics.setConnected(timeToConnectMs ?? 0, totalTimeToConnectMs ?? 0);
|
|
571
|
-
lc.info?.('Connected', {
|
|
572
|
-
navigatorOnline: navigator?.onLine,
|
|
573
|
-
timeToConnectMs,
|
|
574
|
-
totalTimeToConnectMs,
|
|
575
|
-
connectMsgLatencyMs,
|
|
576
|
-
connectedCount: this.#connectedCount,
|
|
577
|
-
proceedingConnectErrorCount,
|
|
578
|
-
});
|
|
579
|
-
this.#lastMutationIDSent = NULL_LAST_MUTATION_ID_SENT;
|
|
580
|
-
lc.debug?.('Resolving connect resolver');
|
|
581
|
-
assert(this.#socket);
|
|
582
|
-
const queriesPatch = await this.#rep.query(tx => this.#queryManager.getQueriesPatch(tx, this.#initConnectionQueries));
|
|
583
|
-
if (queriesPatch.size > 0 && this.#initConnectionQueries !== undefined) {
|
|
584
|
-
send(this.#socket, [
|
|
585
|
-
'changeDesiredQueries',
|
|
586
|
-
{
|
|
587
|
-
desiredQueriesPatch: [...queriesPatch.values()],
|
|
588
|
-
},
|
|
589
|
-
]);
|
|
590
|
-
}
|
|
591
|
-
else if (this.#initConnectionQueries === undefined) {
|
|
592
|
-
// if #initConnectionQueries was undefined that means we never
|
|
593
|
-
// sent `initConnection` to the server inside the sec-protocol header.
|
|
594
|
-
send(this.#socket, [
|
|
595
|
-
'initConnection',
|
|
596
|
-
{
|
|
597
|
-
desiredQueriesPatch: [...queriesPatch.values()],
|
|
598
|
-
},
|
|
599
|
-
]);
|
|
600
|
-
}
|
|
601
|
-
this.#initConnectionQueries = undefined;
|
|
602
|
-
this.#setConnectionState(ConnectionState.Connected);
|
|
603
|
-
this.#connectResolver.resolve();
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Starts a new connection. This will create the WebSocket that does the HTTP
|
|
607
|
-
* request to the server.
|
|
608
|
-
*
|
|
609
|
-
* {@link #connect} will throw an assertion error if the
|
|
610
|
-
* {@link #connectionState} is not {@link ConnectionState.Disconnected}.
|
|
611
|
-
* Callers MUST check the connection state before calling this method and log
|
|
612
|
-
* an error as needed.
|
|
613
|
-
*
|
|
614
|
-
* The function will resolve once the socket is connected. If you need to know
|
|
615
|
-
* when a connection has been established, as in we have received the
|
|
616
|
-
* {@link ConnectedMessage}, you should await the {@link #connectResolver}
|
|
617
|
-
* promise. The {@link #connectResolver} promise rejects if an error message
|
|
618
|
-
* is received before the connected message is received or if the connection
|
|
619
|
-
* attempt times out.
|
|
620
|
-
*/
|
|
621
|
-
async #connect(l) {
|
|
622
|
-
assert(this.#server);
|
|
623
|
-
// All the callers check this state already.
|
|
624
|
-
assert(this.#connectionState === ConnectionState.Disconnected);
|
|
625
|
-
const wsid = nanoid();
|
|
626
|
-
l = addWebSocketIDToLogContext(wsid, l);
|
|
627
|
-
l.info?.('Connecting...', { navigatorOnline: navigator?.onLine });
|
|
628
|
-
this.#setConnectionState(ConnectionState.Connecting);
|
|
629
|
-
// connect() called but connect start time is defined. This should not
|
|
630
|
-
// happen.
|
|
631
|
-
assert(this.#connectStart === undefined);
|
|
632
|
-
const now = Date.now();
|
|
633
|
-
this.#connectStart = now;
|
|
634
|
-
if (this.#totalToConnectStart === undefined) {
|
|
635
|
-
this.#totalToConnectStart = now;
|
|
636
|
-
}
|
|
637
|
-
if (this.closed) {
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
this.#connectCookie = valita.parse(await this.#rep.cookie, nullableVersionSchema);
|
|
641
|
-
if (this.closed) {
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
// Reject connect after a timeout.
|
|
645
|
-
const timeoutID = setTimeout(() => {
|
|
646
|
-
l.debug?.('Rejecting connect resolver due to timeout');
|
|
647
|
-
this.#connectResolver.reject(new TimedOutError('Connect'));
|
|
648
|
-
this.#disconnect(l, {
|
|
649
|
-
client: 'ConnectTimeout',
|
|
650
|
-
});
|
|
651
|
-
}, CONNECT_TIMEOUT_MS);
|
|
652
|
-
const abortHandler = () => {
|
|
653
|
-
clearTimeout(timeoutID);
|
|
654
|
-
};
|
|
655
|
-
this.#closeAbortController.signal.addEventListener('abort', abortHandler);
|
|
656
|
-
const [ws, initConnectionQueries] = await createSocket(this.#rep, this.#queryManager, toWSString(this.#server), this.#connectCookie, this.clientID, await this.clientGroupID, this.#options.schema.version, this.userID, this.#rep.auth, this.#jurisdiction, this.#lastMutationIDReceived, wsid, this.#options.logLevel === 'debug', l, this.#options.maxHeaderLength);
|
|
657
|
-
if (this.closed) {
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
this.#initConnectionQueries = initConnectionQueries;
|
|
661
|
-
ws.addEventListener('message', this.#onMessage);
|
|
662
|
-
ws.addEventListener('open', this.#onOpen);
|
|
663
|
-
ws.addEventListener('close', this.#onClose);
|
|
664
|
-
this.#socket = ws;
|
|
665
|
-
this.#socketResolver.resolve(ws);
|
|
666
|
-
try {
|
|
667
|
-
l.debug?.('Waiting for connection to be acknowledged');
|
|
668
|
-
await this.#connectResolver.promise;
|
|
669
|
-
}
|
|
670
|
-
finally {
|
|
671
|
-
clearTimeout(timeoutID);
|
|
672
|
-
this.#closeAbortController.signal.removeEventListener('abort', abortHandler);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
#disconnect(l, reason) {
|
|
676
|
-
if (this.#connectionState === ConnectionState.Connecting) {
|
|
677
|
-
this.#connectErrorCount++;
|
|
678
|
-
}
|
|
679
|
-
l.info?.('disconnecting', {
|
|
680
|
-
navigatorOnline: navigator?.onLine,
|
|
681
|
-
reason,
|
|
682
|
-
connectStart: this.#connectStart,
|
|
683
|
-
totalToConnectStart: this.#totalToConnectStart,
|
|
684
|
-
connectedAt: this.#connectedAt,
|
|
685
|
-
connectionDuration: this.#connectedAt
|
|
686
|
-
? Date.now() - this.#connectedAt
|
|
687
|
-
: 0,
|
|
688
|
-
messageCount: this.#messageCount,
|
|
689
|
-
connectionState: this.#connectionState,
|
|
690
|
-
connectErrorCount: this.#connectErrorCount,
|
|
691
|
-
});
|
|
692
|
-
switch (this.#connectionState) {
|
|
693
|
-
case ConnectionState.Connected: {
|
|
694
|
-
if (this.#connectStart !== undefined) {
|
|
695
|
-
l.error?.('disconnect() called while connected but connect start time is defined. This should not happen.');
|
|
696
|
-
// this._connectStart reset below.
|
|
697
|
-
}
|
|
698
|
-
break;
|
|
699
|
-
}
|
|
700
|
-
case ConnectionState.Connecting: {
|
|
701
|
-
this.#metrics.lastConnectError.set(getLastConnectErrorValue(reason));
|
|
702
|
-
this.#metrics.timeToConnectMs.set(DID_NOT_CONNECT_VALUE);
|
|
703
|
-
this.#metrics.setConnectError(reason);
|
|
704
|
-
if (this.#connectErrorCount % CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY ===
|
|
705
|
-
1) {
|
|
706
|
-
this.#checkConnectivity(`connectErrorCount=${this.#connectErrorCount}`);
|
|
707
|
-
}
|
|
708
|
-
// this._connectStart reset below.
|
|
709
|
-
if (this.#connectStart === undefined) {
|
|
710
|
-
l.error?.('disconnect() called while connecting but connect start time is undefined. This should not happen.');
|
|
711
|
-
}
|
|
712
|
-
break;
|
|
713
|
-
}
|
|
714
|
-
case ConnectionState.Disconnected:
|
|
715
|
-
l.error?.('disconnect() called while disconnected');
|
|
716
|
-
break;
|
|
717
|
-
}
|
|
718
|
-
this.#socketResolver = resolver();
|
|
719
|
-
l.debug?.('Creating new connect resolver');
|
|
720
|
-
this.#connectResolver = resolver();
|
|
721
|
-
this.#setConnectionState(ConnectionState.Disconnected);
|
|
722
|
-
this.#messageCount = 0;
|
|
723
|
-
this.#connectStart = undefined; // don't reset this._totalToConnectStart
|
|
724
|
-
this.#connectedAt = 0;
|
|
725
|
-
this.#socket?.removeEventListener('message', this.#onMessage);
|
|
726
|
-
this.#socket?.removeEventListener('open', this.#onOpen);
|
|
727
|
-
this.#socket?.removeEventListener('close', this.#onClose);
|
|
728
|
-
this.#socket?.close();
|
|
729
|
-
this.#socket = undefined;
|
|
730
|
-
this.#lastMutationIDSent = NULL_LAST_MUTATION_ID_SENT;
|
|
731
|
-
this.#pokeHandler.handleDisconnect();
|
|
732
|
-
}
|
|
733
|
-
async #handlePokeStart(_lc, pokeMessage) {
|
|
734
|
-
this.#abortPingTimeout();
|
|
735
|
-
await this.#pokeHandler.handlePokeStart(pokeMessage[1]);
|
|
736
|
-
}
|
|
737
|
-
async #handlePokePart(_lc, pokeMessage) {
|
|
738
|
-
this.#abortPingTimeout();
|
|
739
|
-
const lastMutationIDChangeForSelf = await this.#pokeHandler.handlePokePart(pokeMessage[1]);
|
|
740
|
-
if (lastMutationIDChangeForSelf !== undefined) {
|
|
741
|
-
this.#lastMutationIDReceived = lastMutationIDChangeForSelf;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
async #handlePokeEnd(_lc, pokeMessage) {
|
|
745
|
-
this.#abortPingTimeout();
|
|
746
|
-
await this.#pokeHandler.handlePokeEnd(pokeMessage[1]);
|
|
747
|
-
}
|
|
748
|
-
#onPokeError() {
|
|
749
|
-
const lc = this.#lc;
|
|
750
|
-
lc.info?.('poke error, disconnecting?', this.#connectionState !== ConnectionState.Disconnected);
|
|
751
|
-
// It is theoretically possible that we get disconnected during the
|
|
752
|
-
// async poke above. Only disconnect if we are not already
|
|
753
|
-
// disconnected.
|
|
754
|
-
if (this.#connectionState !== ConnectionState.Disconnected) {
|
|
755
|
-
this.#disconnect(lc, {
|
|
756
|
-
client: 'UnexpectedBaseCookie',
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
#handlePullResponse(lc, pullResponseMessage) {
|
|
761
|
-
this.#abortPingTimeout();
|
|
762
|
-
const body = pullResponseMessage[1];
|
|
763
|
-
lc = lc.withContext('requestID', body.requestID);
|
|
764
|
-
lc.debug?.('Handling pull response', body);
|
|
765
|
-
const resolver = this.#pendingPullsByRequestID.get(body.requestID);
|
|
766
|
-
if (!resolver) {
|
|
767
|
-
// This can happen because resolvers are deleted
|
|
768
|
-
// from this._pendingPullsByRequestID when pulls timeout.
|
|
769
|
-
lc.debug?.('No resolver found');
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
resolver.resolve(pullResponseMessage[1]);
|
|
773
|
-
}
|
|
774
|
-
async #pusher(req, requestID) {
|
|
775
|
-
// The deprecation of pushVersion 0 predates zero-client
|
|
776
|
-
assert(req.pushVersion === 1);
|
|
777
|
-
// If we are connecting we wait until we are connected.
|
|
778
|
-
await this.#connectResolver.promise;
|
|
779
|
-
const lc = this.#lc.withContext('requestID', requestID);
|
|
780
|
-
lc.debug?.(`pushing ${req.mutations.length} mutations`);
|
|
781
|
-
const socket = this.#socket;
|
|
782
|
-
assert(socket);
|
|
783
|
-
const isMutationRecoveryPush = req.clientGroupID !== (await this.clientGroupID);
|
|
784
|
-
const start = isMutationRecoveryPush
|
|
785
|
-
? 0
|
|
786
|
-
: req.mutations.findIndex(m => m.clientID === this.#lastMutationIDSent.clientID &&
|
|
787
|
-
m.id === this.#lastMutationIDSent.id) + 1;
|
|
788
|
-
lc.debug?.(isMutationRecoveryPush ? 'pushing for recovery' : 'pushing', req.mutations.length - start, 'mutations of', req.mutations.length, 'mutations.');
|
|
789
|
-
const now = Date.now();
|
|
790
|
-
for (let i = start; i < req.mutations.length; i++) {
|
|
791
|
-
const m = req.mutations[i];
|
|
792
|
-
const timestamp = now - Math.round(performance.now() - m.timestamp);
|
|
793
|
-
const zeroM = m.name === CRUD_MUTATION_NAME
|
|
794
|
-
? {
|
|
795
|
-
type: MutationType.CRUD,
|
|
796
|
-
timestamp,
|
|
797
|
-
id: m.id,
|
|
798
|
-
clientID: m.clientID,
|
|
799
|
-
name: m.name,
|
|
800
|
-
args: [m.args],
|
|
801
|
-
}
|
|
802
|
-
: {
|
|
803
|
-
type: MutationType.Custom,
|
|
804
|
-
timestamp,
|
|
805
|
-
id: m.id,
|
|
806
|
-
clientID: m.clientID,
|
|
807
|
-
name: m.name,
|
|
808
|
-
args: [m.args],
|
|
809
|
-
};
|
|
810
|
-
const msg = [
|
|
811
|
-
'push',
|
|
812
|
-
{
|
|
813
|
-
timestamp: now,
|
|
814
|
-
clientGroupID: req.clientGroupID,
|
|
815
|
-
mutations: [zeroM],
|
|
816
|
-
pushVersion: req.pushVersion,
|
|
817
|
-
// Zero schema versions are always numbers.
|
|
818
|
-
schemaVersion: parseInt(req.schemaVersion),
|
|
819
|
-
requestID,
|
|
820
|
-
},
|
|
821
|
-
];
|
|
822
|
-
send(socket, msg);
|
|
823
|
-
if (!isMutationRecoveryPush) {
|
|
824
|
-
this.#lastMutationIDSent = { clientID: m.clientID, id: m.id };
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
return {
|
|
828
|
-
httpRequestInfo: {
|
|
829
|
-
errorMessage: '',
|
|
830
|
-
httpStatusCode: 200,
|
|
831
|
-
},
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
#getAuthToken = () => {
|
|
835
|
-
const { auth } = this.#options;
|
|
836
|
-
return typeof auth === 'function' ? auth() : auth;
|
|
837
|
-
};
|
|
838
|
-
async #updateAuthToken(lc) {
|
|
839
|
-
const auth = await this.#getAuthToken();
|
|
840
|
-
if (auth) {
|
|
841
|
-
lc.debug?.('Got auth token');
|
|
842
|
-
this.#rep.auth = auth;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
async #runLoop() {
|
|
846
|
-
this.#lc.info?.(`Starting Zero version: ${this.version}`);
|
|
847
|
-
if (this.#server === null) {
|
|
848
|
-
this.#lc.info?.('No socket origin provided, not starting connect loop.');
|
|
849
|
-
return;
|
|
850
|
-
}
|
|
851
|
-
let runLoopCounter = 0;
|
|
852
|
-
const bareLogContext = this.#lc;
|
|
853
|
-
const getLogContext = () => {
|
|
854
|
-
let lc = bareLogContext;
|
|
855
|
-
if (this.#socket) {
|
|
856
|
-
lc = addWebSocketIDFromSocketToLogContext(this.#socket, lc);
|
|
857
|
-
}
|
|
858
|
-
return lc.withContext('runLoopCounter', runLoopCounter);
|
|
859
|
-
};
|
|
860
|
-
await this.#updateAuthToken(bareLogContext);
|
|
861
|
-
let needsReauth = false;
|
|
862
|
-
let gotError = false;
|
|
863
|
-
while (!this.closed) {
|
|
864
|
-
runLoopCounter++;
|
|
865
|
-
let lc = getLogContext();
|
|
866
|
-
try {
|
|
867
|
-
switch (this.#connectionState) {
|
|
868
|
-
case ConnectionState.Disconnected: {
|
|
869
|
-
if (this.#visibilityWatcher.visibilityState === 'hidden') {
|
|
870
|
-
this.#metrics.setDisconnectedWaitingForVisible();
|
|
871
|
-
// reset this._totalToConnectStart since this client
|
|
872
|
-
// is no longer trying to connect due to being hidden.
|
|
873
|
-
this.#totalToConnectStart = undefined;
|
|
874
|
-
}
|
|
875
|
-
// If hidden, we wait for the tab to become visible before trying again.
|
|
876
|
-
await this.#visibilityWatcher.waitForVisible();
|
|
877
|
-
// If we got an auth error we try to get a new auth token before reconnecting.
|
|
878
|
-
if (needsReauth) {
|
|
879
|
-
await this.#updateAuthToken(lc);
|
|
880
|
-
}
|
|
881
|
-
await this.#connect(lc);
|
|
882
|
-
if (this.closed) {
|
|
883
|
-
break;
|
|
884
|
-
}
|
|
885
|
-
// Now we have a new socket, update lc with the new wsid.
|
|
886
|
-
assert(this.#socket);
|
|
887
|
-
lc = getLogContext();
|
|
888
|
-
lc.debug?.('Connected successfully');
|
|
889
|
-
gotError = false;
|
|
890
|
-
needsReauth = false;
|
|
891
|
-
this.#setOnline(true);
|
|
892
|
-
break;
|
|
893
|
-
}
|
|
894
|
-
case ConnectionState.Connecting:
|
|
895
|
-
// Can't get here because Disconnected waits for Connected or
|
|
896
|
-
// rejection.
|
|
897
|
-
lc.error?.('unreachable');
|
|
898
|
-
gotError = true;
|
|
899
|
-
break;
|
|
900
|
-
case ConnectionState.Connected: {
|
|
901
|
-
// When connected we wait for whatever happens first out of:
|
|
902
|
-
// - After PING_INTERVAL_MS we send a ping
|
|
903
|
-
// - We get disconnected
|
|
904
|
-
// - We get a message
|
|
905
|
-
// - We get an error (rejectMessageError rejects)
|
|
906
|
-
// - The tab becomes hidden (with a delay)
|
|
907
|
-
const controller = new AbortController();
|
|
908
|
-
this.#abortPingTimeout = () => controller.abort();
|
|
909
|
-
const [pingTimeoutPromise, pingTimeoutAborted] = sleepWithAbort(PING_INTERVAL_MS, controller.signal);
|
|
910
|
-
this.#rejectMessageError = resolver();
|
|
911
|
-
let RaceCases;
|
|
912
|
-
(function (RaceCases) {
|
|
913
|
-
RaceCases[RaceCases["Ping"] = 0] = "Ping";
|
|
914
|
-
RaceCases[RaceCases["Hidden"] = 2] = "Hidden";
|
|
915
|
-
})(RaceCases || (RaceCases = {}));
|
|
916
|
-
const raceResult = await promiseRace([
|
|
917
|
-
pingTimeoutPromise,
|
|
918
|
-
pingTimeoutAborted,
|
|
919
|
-
this.#visibilityWatcher.waitForHidden(),
|
|
920
|
-
this.#connectionStateChangeResolver.promise,
|
|
921
|
-
this.#rejectMessageError.promise,
|
|
922
|
-
]);
|
|
923
|
-
if (this.closed) {
|
|
924
|
-
this.#rejectMessageError = undefined;
|
|
925
|
-
break;
|
|
926
|
-
}
|
|
927
|
-
switch (raceResult) {
|
|
928
|
-
case RaceCases.Ping: {
|
|
929
|
-
const pingResult = await this.#ping(lc, this.#rejectMessageError.promise);
|
|
930
|
-
if (pingResult === PingResult.TimedOut) {
|
|
931
|
-
gotError = true;
|
|
932
|
-
}
|
|
933
|
-
break;
|
|
934
|
-
}
|
|
935
|
-
case RaceCases.Hidden:
|
|
936
|
-
this.#disconnect(lc, {
|
|
937
|
-
client: 'Hidden',
|
|
938
|
-
});
|
|
939
|
-
this.#setOnline(false);
|
|
940
|
-
break;
|
|
941
|
-
}
|
|
942
|
-
this.#rejectMessageError = undefined;
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
catch (ex) {
|
|
947
|
-
if (this.#connectionState !== ConnectionState.Connected) {
|
|
948
|
-
lc.error?.('Failed to connect', ex, {
|
|
949
|
-
lmid: this.#lastMutationIDReceived,
|
|
950
|
-
baseCookie: this.#connectCookie,
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
lc.debug?.('Got an exception in the run loop', 'state:', this.#connectionState, 'exception:', ex);
|
|
954
|
-
if (isAuthError(ex)) {
|
|
955
|
-
if (!needsReauth) {
|
|
956
|
-
needsReauth = true;
|
|
957
|
-
// First auth error, try right away without waiting.
|
|
958
|
-
continue;
|
|
959
|
-
}
|
|
960
|
-
needsReauth = true;
|
|
961
|
-
}
|
|
962
|
-
if (isServerError(ex) ||
|
|
963
|
-
ex instanceof TimedOutError ||
|
|
964
|
-
ex instanceof CloseError) {
|
|
965
|
-
gotError = true;
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
// Only authentication errors are retried immediately the first time they
|
|
969
|
-
// occur. All other errors wait a few seconds before retrying the first
|
|
970
|
-
// time. We specifically do not use a backoff for consecutive errors
|
|
971
|
-
// because it's a bad experience to wait many seconds for reconnection.
|
|
972
|
-
if (gotError) {
|
|
973
|
-
this.#setOnline(false);
|
|
974
|
-
let cfGetCheckSucceeded = false;
|
|
975
|
-
const cfGetCheckURL = new URL(this.#server);
|
|
976
|
-
cfGetCheckURL.pathname = '/api/canary/v0/get';
|
|
977
|
-
cfGetCheckURL.searchParams.set('id', nanoid());
|
|
978
|
-
const cfGetCheckController = new AbortController();
|
|
979
|
-
fetch(cfGetCheckURL, { signal: cfGetCheckController.signal })
|
|
980
|
-
.then(_ => {
|
|
981
|
-
cfGetCheckSucceeded = true;
|
|
982
|
-
})
|
|
983
|
-
.catch(_ => {
|
|
984
|
-
cfGetCheckSucceeded = false;
|
|
985
|
-
});
|
|
986
|
-
lc.debug?.('Sleeping', RUN_LOOP_INTERVAL_MS, 'ms before reconnecting due to error, state:', this.#connectionState);
|
|
987
|
-
await sleep(RUN_LOOP_INTERVAL_MS);
|
|
988
|
-
cfGetCheckController.abort();
|
|
989
|
-
if (!cfGetCheckSucceeded) {
|
|
990
|
-
lc.info?.('Canary request failed, resetting total time to connect start time.');
|
|
991
|
-
this.#totalToConnectStart = undefined;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
async #puller(req, requestID) {
|
|
997
|
-
// The deprecation of pushVersion 0 predates zero-client
|
|
998
|
-
assert(req.pullVersion === 1);
|
|
999
|
-
const lc = this.#lc.withContext('requestID', requestID);
|
|
1000
|
-
lc.debug?.('Pull', req);
|
|
1001
|
-
// Pull request for this instance's client group. A no-op response is
|
|
1002
|
-
// returned as pulls for this client group are handled via poke over the
|
|
1003
|
-
// socket.
|
|
1004
|
-
if (req.clientGroupID === (await this.clientGroupID)) {
|
|
1005
|
-
return {
|
|
1006
|
-
httpRequestInfo: {
|
|
1007
|
-
errorMessage: '',
|
|
1008
|
-
httpStatusCode: 200,
|
|
1009
|
-
},
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
// If we are connecting we wait until we are connected.
|
|
1013
|
-
await this.#connectResolver.promise;
|
|
1014
|
-
const socket = this.#socket;
|
|
1015
|
-
assert(socket);
|
|
1016
|
-
// Mutation recovery pull.
|
|
1017
|
-
lc.debug?.('Pull is for mutation recovery');
|
|
1018
|
-
const cookie = valita.parse(req.cookie, nullableVersionSchema);
|
|
1019
|
-
const pullRequestMessage = [
|
|
1020
|
-
'pull',
|
|
1021
|
-
{
|
|
1022
|
-
clientGroupID: req.clientGroupID,
|
|
1023
|
-
cookie,
|
|
1024
|
-
requestID,
|
|
1025
|
-
},
|
|
1026
|
-
];
|
|
1027
|
-
send(socket, pullRequestMessage);
|
|
1028
|
-
const pullResponseResolver = resolver();
|
|
1029
|
-
this.#pendingPullsByRequestID.set(requestID, pullResponseResolver);
|
|
1030
|
-
try {
|
|
1031
|
-
let RaceCases;
|
|
1032
|
-
(function (RaceCases) {
|
|
1033
|
-
RaceCases[RaceCases["Timeout"] = 0] = "Timeout";
|
|
1034
|
-
RaceCases[RaceCases["Response"] = 1] = "Response";
|
|
1035
|
-
})(RaceCases || (RaceCases = {}));
|
|
1036
|
-
const raceResult = await promiseRace([
|
|
1037
|
-
sleep(PULL_TIMEOUT_MS),
|
|
1038
|
-
pullResponseResolver.promise,
|
|
1039
|
-
]);
|
|
1040
|
-
switch (raceResult) {
|
|
1041
|
-
case RaceCases.Timeout:
|
|
1042
|
-
lc.debug?.('Mutation recovery pull timed out');
|
|
1043
|
-
throw new Error('Pull timed out');
|
|
1044
|
-
case RaceCases.Response: {
|
|
1045
|
-
lc.debug?.('Returning mutation recovery pull response');
|
|
1046
|
-
const response = await pullResponseResolver.promise;
|
|
1047
|
-
return {
|
|
1048
|
-
response: {
|
|
1049
|
-
cookie: response.cookie,
|
|
1050
|
-
lastMutationIDChanges: response.lastMutationIDChanges,
|
|
1051
|
-
patch: [],
|
|
1052
|
-
},
|
|
1053
|
-
httpRequestInfo: {
|
|
1054
|
-
errorMessage: '',
|
|
1055
|
-
httpStatusCode: 200,
|
|
1056
|
-
},
|
|
1057
|
-
};
|
|
1058
|
-
}
|
|
1059
|
-
default:
|
|
1060
|
-
assert(false, 'unreachable');
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
finally {
|
|
1064
|
-
pullResponseResolver.reject('timed out');
|
|
1065
|
-
this.#pendingPullsByRequestID.delete(requestID);
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
#setOnline(online) {
|
|
1069
|
-
if (this.#online === online) {
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
this.#online = online;
|
|
1073
|
-
this.onOnlineChange?.(online);
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* A rough heuristic for whether the client is currently online and
|
|
1077
|
-
* authenticated.
|
|
1078
|
-
*/
|
|
1079
|
-
get online() {
|
|
1080
|
-
return this.#online;
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Starts a a ping and waits for a pong.
|
|
1084
|
-
*
|
|
1085
|
-
* If it takes too long to get a pong we disconnect and this returns
|
|
1086
|
-
* {@code PingResult.TimedOut}.
|
|
1087
|
-
*/
|
|
1088
|
-
async #ping(l, messageErrorRejectionPromise) {
|
|
1089
|
-
l.debug?.('pinging');
|
|
1090
|
-
const { promise, resolve } = resolver();
|
|
1091
|
-
this.#onPong = resolve;
|
|
1092
|
-
const pingMessage = ['ping', {}];
|
|
1093
|
-
const t0 = performance.now();
|
|
1094
|
-
assert(this.#socket);
|
|
1095
|
-
send(this.#socket, pingMessage);
|
|
1096
|
-
const connected = (await promiseRace([
|
|
1097
|
-
promise,
|
|
1098
|
-
sleep(PING_TIMEOUT_MS),
|
|
1099
|
-
messageErrorRejectionPromise,
|
|
1100
|
-
])) === 0;
|
|
1101
|
-
const delta = performance.now() - t0;
|
|
1102
|
-
if (!connected) {
|
|
1103
|
-
l.info?.('ping failed in', delta, 'ms - disconnecting');
|
|
1104
|
-
this.#disconnect(l, {
|
|
1105
|
-
client: 'PingTimeout',
|
|
1106
|
-
});
|
|
1107
|
-
return PingResult.TimedOut;
|
|
1108
|
-
}
|
|
1109
|
-
l.debug?.('ping succeeded in', delta, 'ms');
|
|
1110
|
-
return PingResult.Success;
|
|
1111
|
-
}
|
|
1112
|
-
// Sends a set of metrics to the server. Throws unless the server
|
|
1113
|
-
// returns 200.
|
|
1114
|
-
// TODO: Reenable metrics reporting
|
|
1115
|
-
async #reportMetrics(_allSeries) {
|
|
1116
|
-
// if (this.#server === null) {
|
|
1117
|
-
// this.#lc.info?.('Skipping metrics report, socketOrigin is null');
|
|
1118
|
-
// return;
|
|
1119
|
-
// }
|
|
1120
|
-
// const body = JSON.stringify({series: allSeries});
|
|
1121
|
-
// const url = new URL('/api/metrics/v0/report', this.#server);
|
|
1122
|
-
// url.searchParams.set('clientID', this.clientID);
|
|
1123
|
-
// url.searchParams.set('clientGroupID', await this.clientGroupID);
|
|
1124
|
-
// url.searchParams.set('userID', this.userID);
|
|
1125
|
-
// url.searchParams.set('requestID', nanoid());
|
|
1126
|
-
// const res = await fetch(url.toString(), {
|
|
1127
|
-
// method: 'POST',
|
|
1128
|
-
// body,
|
|
1129
|
-
// keepalive: true,
|
|
1130
|
-
// });
|
|
1131
|
-
// if (!res.ok) {
|
|
1132
|
-
// const maybeBody = await res.text();
|
|
1133
|
-
// throw new Error(
|
|
1134
|
-
// `unexpected response: ${res.status} ${res.statusText} body: ${maybeBody}`,
|
|
1135
|
-
// );
|
|
1136
|
-
// }
|
|
1137
|
-
}
|
|
1138
|
-
#checkConnectivity(reason) {
|
|
1139
|
-
void this.#checkConnectivityAsync(reason);
|
|
1140
|
-
}
|
|
1141
|
-
#checkConnectivityAsync(_reason) {
|
|
1142
|
-
// skipping connectivity checks for now - the server doesn't respond to
|
|
1143
|
-
// them so it just creates noise.
|
|
1144
|
-
// assert(this.#server);
|
|
1145
|
-
// if (this.closed) {
|
|
1146
|
-
// return;
|
|
1147
|
-
// }
|
|
1148
|
-
// try {
|
|
1149
|
-
// await checkConnectivity(
|
|
1150
|
-
// reason,
|
|
1151
|
-
// this.#server,
|
|
1152
|
-
// this.#lc,
|
|
1153
|
-
// this.#closeAbortController.signal,
|
|
1154
|
-
// this.#enableAnalytics,
|
|
1155
|
-
// );
|
|
1156
|
-
// } catch (e) {
|
|
1157
|
-
// this.#lc.info?.('Error checking connectivity for', reason, e);
|
|
1158
|
-
// }
|
|
1159
|
-
}
|
|
1160
|
-
#registerQueries(schema) {
|
|
1161
|
-
const rv = {};
|
|
1162
|
-
const context = this.#zeroContext;
|
|
1163
|
-
// Not using parse yet
|
|
1164
|
-
for (const [name, table] of Object.entries(schema.tables)) {
|
|
1165
|
-
rv[name] = newQuery(context, table);
|
|
1166
|
-
}
|
|
1167
|
-
return rv;
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
export async function createSocket(rep, queryManager, socketOrigin, baseCookie, clientID, clientGroupID, schemaVersion, userID, auth, jurisdiction, lmid, wsid, debugPerf, lc, maxHeaderLength = 1024 * 8) {
|
|
1171
|
-
const url = new URL(socketOrigin);
|
|
1172
|
-
// Keep this in sync with the server.
|
|
1173
|
-
url.pathname = `/api/sync/v${REFLECT_VERSION}/connect`;
|
|
1174
|
-
const { searchParams } = url;
|
|
1175
|
-
searchParams.set('clientID', clientID);
|
|
1176
|
-
searchParams.set('clientGroupID', clientGroupID);
|
|
1177
|
-
searchParams.set('schemaVersion', schemaVersion.toString());
|
|
1178
|
-
searchParams.set('userID', userID);
|
|
1179
|
-
if (jurisdiction !== undefined) {
|
|
1180
|
-
searchParams.set('jurisdiction', jurisdiction);
|
|
1181
|
-
}
|
|
1182
|
-
searchParams.set('baseCookie', baseCookie === null ? '' : String(baseCookie));
|
|
1183
|
-
searchParams.set('ts', String(performance.now()));
|
|
1184
|
-
searchParams.set('lmid', String(lmid));
|
|
1185
|
-
searchParams.set('wsid', wsid);
|
|
1186
|
-
if (debugPerf) {
|
|
1187
|
-
searchParams.set('debugPerf', true.toString());
|
|
1188
|
-
}
|
|
1189
|
-
lc.info?.('Connecting to', url.toString());
|
|
1190
|
-
// Pass auth to the server via the `Sec-WebSocket-Protocol` header by passing
|
|
1191
|
-
// it as a `protocol` to the `WebSocket` constructor. The empty string is an
|
|
1192
|
-
// invalid `protocol`, and will result in an exception, so pass undefined
|
|
1193
|
-
// instead. encodeURIComponent to ensure it only contains chars allowed
|
|
1194
|
-
// for a `protocol`.
|
|
1195
|
-
const WS = mustGetBrowserGlobal('WebSocket');
|
|
1196
|
-
let queriesPatch = await rep.query(tx => queryManager.getQueriesPatch(tx));
|
|
1197
|
-
let secProtocol = encodeSecProtocols(['initConnection', { desiredQueriesPatch: [...queriesPatch.values()] }], auth);
|
|
1198
|
-
if (secProtocol.length > maxHeaderLength) {
|
|
1199
|
-
secProtocol = encodeSecProtocols(undefined, auth);
|
|
1200
|
-
queriesPatch = undefined;
|
|
1201
|
-
}
|
|
1202
|
-
return [
|
|
1203
|
-
new WS(
|
|
1204
|
-
// toString() required for RN URL polyfill.
|
|
1205
|
-
url.toString(), secProtocol),
|
|
1206
|
-
queriesPatch,
|
|
1207
|
-
];
|
|
1208
|
-
}
|
|
1209
|
-
/**
|
|
1210
|
-
* Adds the wsid query parameter to the log context. If the URL does not
|
|
1211
|
-
* have a wsid we use a randomID instead.
|
|
1212
|
-
*/
|
|
1213
|
-
function addWebSocketIDFromSocketToLogContext({ url }, lc) {
|
|
1214
|
-
const wsid = new URL(url).searchParams.get('wsid') ?? nanoid();
|
|
1215
|
-
return addWebSocketIDToLogContext(wsid, lc);
|
|
1216
|
-
}
|
|
1217
|
-
function addWebSocketIDToLogContext(wsid, lc) {
|
|
1218
|
-
return lc.withContext('wsid', wsid);
|
|
1219
|
-
}
|
|
1220
|
-
/**
|
|
1221
|
-
* Like Promise.race but returns the index of the first promise that resolved.
|
|
1222
|
-
*/
|
|
1223
|
-
function promiseRace(ps) {
|
|
1224
|
-
return Promise.race(ps.map((p, i) => p.then(() => i)));
|
|
1225
|
-
}
|
|
1226
|
-
class TimedOutError extends Error {
|
|
1227
|
-
constructor(m) {
|
|
1228
|
-
super(`${m} timed out`);
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
class CloseError extends Error {
|
|
1232
|
-
}
|
|
1233
|
-
//# sourceMappingURL=zero.js.map
|