@rocicorp/zero 1.12.0-canary.7 → 1.12.0-canary.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/replicache/src/btree/node.d.ts +0 -2
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/connection-loop.d.ts.map +1 -1
- package/out/replicache/src/connection-loop.js +1 -1
- package/out/replicache/src/connection-loop.js.map +1 -1
- package/out/replicache/src/db/commit.d.ts +0 -1
- package/out/replicache/src/db/commit.d.ts.map +1 -1
- package/out/replicache/src/db/commit.js +1 -1
- package/out/replicache/src/db/commit.js.map +1 -1
- package/out/replicache/src/db/write.d.ts +0 -2
- package/out/replicache/src/db/write.d.ts.map +1 -1
- package/out/replicache/src/db/write.js +1 -1
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/deleted-clients.d.ts +0 -2
- package/out/replicache/src/deleted-clients.d.ts.map +1 -1
- package/out/replicache/src/deleted-clients.js +1 -1
- package/out/replicache/src/deleted-clients.js.map +1 -1
- package/out/replicache/src/frozen-json.d.ts +0 -1
- package/out/replicache/src/frozen-json.d.ts.map +1 -1
- package/out/replicache/src/frozen-json.js.map +1 -1
- package/out/replicache/src/index-defs.d.ts +0 -2
- package/out/replicache/src/index-defs.d.ts.map +1 -1
- package/out/replicache/src/index-defs.js +1 -1
- package/out/replicache/src/index-defs.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts +0 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store.js +1 -1
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/throw-if-closed.d.ts +0 -3
- package/out/replicache/src/kv/throw-if-closed.d.ts.map +1 -1
- package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
- package/out/replicache/src/persist/client-group-gc.d.ts +0 -7
- package/out/replicache/src/persist/client-group-gc.d.ts.map +1 -1
- package/out/replicache/src/persist/client-group-gc.js +1 -1
- package/out/replicache/src/persist/client-group-gc.js.map +1 -1
- package/out/replicache/src/process-scheduler.js +1 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +2 -7
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +1 -1
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/sync/patch.d.ts +0 -5
- package/out/replicache/src/sync/patch.d.ts.map +1 -1
- package/out/replicache/src/sync/patch.js +1 -1
- package/out/replicache/src/sync/patch.js.map +1 -1
- package/out/replicache/src/sync/pull.d.ts +0 -1
- package/out/replicache/src/sync/pull.d.ts.map +1 -1
- package/out/replicache/src/sync/pull.js.map +1 -1
- package/out/replicache/src/sync/push.d.ts +2 -1
- package/out/replicache/src/sync/push.d.ts.map +1 -1
- package/out/replicache/src/sync/push.js.map +1 -1
- package/out/replicache/src/types.d.ts +2 -1
- package/out/replicache/src/types.d.ts.map +1 -1
- package/out/shared/src/asserts.d.ts +0 -5
- package/out/shared/src/asserts.d.ts.map +1 -1
- package/out/shared/src/asserts.js +1 -1
- package/out/shared/src/asserts.js.map +1 -1
- package/out/shared/src/browser-env.d.ts +0 -17
- package/out/shared/src/browser-env.d.ts.map +1 -1
- package/out/shared/src/browser-env.js.map +1 -1
- package/out/shared/src/deep-clone.d.ts +0 -1
- package/out/shared/src/deep-clone.d.ts.map +1 -1
- package/out/shared/src/deep-clone.js +1 -1
- package/out/shared/src/deep-clone.js.map +1 -1
- package/out/shared/src/document-visible.js +1 -1
- package/out/shared/src/document-visible.js.map +1 -1
- package/out/shared/src/expand.d.ts +0 -3
- package/out/shared/src/expand.d.ts.map +1 -1
- package/out/shared/src/immutable.d.ts +1 -1
- package/out/shared/src/immutable.d.ts.map +1 -1
- package/out/shared/src/iterables.d.ts +0 -1
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/logging-test-utils.js +1 -1
- package/out/shared/src/logging-test-utils.js.map +1 -1
- package/out/shared/src/options-types.d.ts +1 -1
- package/out/shared/src/options-types.d.ts.map +1 -1
- package/out/shared/src/options.d.ts +2 -3
- package/out/shared/src/options.d.ts.map +1 -1
- package/out/shared/src/options.js +1 -1
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/promise-race.js +1 -1
- package/out/shared/src/promise-race.js.map +1 -1
- package/out/shared/src/queue.js +1 -1
- package/out/shared/src/queue.js.map +1 -1
- package/out/shared/src/resolved-promises.d.ts +0 -4
- package/out/shared/src/resolved-promises.d.ts.map +1 -1
- package/out/shared/src/resolved-promises.js +0 -1
- package/out/shared/src/resolved-promises.js.map +1 -1
- package/out/shared/src/resolver.d.ts +9 -0
- package/out/shared/src/resolver.d.ts.map +1 -0
- package/out/shared/src/resolver.js +6 -0
- package/out/shared/src/resolver.js.map +1 -0
- package/out/shared/src/sleep.js +1 -1
- package/out/shared/src/sleep.js.map +1 -1
- package/out/shared/src/tdigest.d.ts +0 -1
- package/out/shared/src/tdigest.d.ts.map +1 -1
- package/out/shared/src/tdigest.js.map +1 -1
- package/out/z2s/src/sql.d.ts +1 -5
- package/out/z2s/src/sql.d.ts.map +1 -1
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +2 -3
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/zero-cache-dev.js +1 -1
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +14 -11
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +23 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.js +1 -1
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.js +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
- package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
- package/out/zero-cache/src/db/specs.d.ts +3 -4
- package/out/zero-cache/src/db/specs.d.ts.map +1 -1
- package/out/zero-cache/src/db/specs.js +5 -7
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js +2 -2
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/observability/events.js +1 -1
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +1 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.js +1 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +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 +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-restore.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +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-stream.js +1 -1
- 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.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.d.ts +0 -2
- package/out/zero-cache/src/services/change-source/pg/lsn.d.ts.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.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +0 -12
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +5 -3
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts +0 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +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 +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 +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 +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 +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js +1 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +2 -2
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.js +2 -2
- package/out/zero-cache/src/services/change-streamer/restore-admission.js.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-message.js +2 -3
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +2 -2
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +1 -1
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +1 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +2 -2
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +57 -14
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.d.ts +4 -12
- package/out/zero-cache/src/services/plannerz.d.ts.map +1 -1
- package/out/zero-cache/src/services/plannerz.js +1 -3
- package/out/zero-cache/src/services/plannerz.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-db.js +1 -1
- package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
- package/out/zero-cache/src/services/replicator/drain-handler.js +1 -1
- package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +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.d.ts +2 -11
- package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js +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.d.ts +0 -10
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts +0 -7
- package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/running-state.js +1 -1
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.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-store.js +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 +2 -2
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +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/schema/cvr.js +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/types.js +3 -17
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +13 -10
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/lite.d.ts +0 -1
- package/out/zero-cache/src/types/lite.d.ts.map +1 -1
- package/out/zero-cache/src/types/lite.js +2 -2
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/pg-types.d.ts +0 -40
- package/out/zero-cache/src/types/pg-types.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg-types.js.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.js +1 -1
- package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
- package/out/zero-cache/src/types/row-key.d.ts +0 -5
- package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-cache/src/types/streams.js +1 -1
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/subscription.js +1 -1
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts +0 -6
- package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js +8 -2
- package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
- package/out/zero-cache/src/types/ws.d.ts +2 -1
- package/out/zero-cache/src/types/ws.d.ts.map +1 -1
- package/out/zero-cache/src/types/ws.js +1 -1
- package/out/zero-cache/src/types/ws.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +4 -1
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/mutator.js +1 -1
- package/out/zero-cache/src/workers/mutator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +45 -3
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.js +1 -1
- package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +1 -1
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/crud.d.ts +2 -8
- package/out/zero-client/src/client/crud.d.ts.map +1 -1
- package/out/zero-client/src/client/crud.js +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/custom.d.ts +0 -2
- package/out/zero-client/src/client/custom.d.ts.map +1 -1
- package/out/zero-client/src/client/custom.js +1 -1
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/enable-analytics.d.ts +0 -1
- package/out/zero-client/src/client/enable-analytics.d.ts.map +1 -1
- package/out/zero-client/src/client/enable-analytics.js +2 -1
- package/out/zero-client/src/client/enable-analytics.js.map +1 -1
- package/out/zero-client/src/client/error.d.ts +4 -5
- package/out/zero-client/src/client/error.d.ts.map +1 -1
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/keys.d.ts +0 -1
- package/out/zero-client/src/client/keys.d.ts.map +1 -1
- package/out/zero-client/src/client/keys.js +4 -2
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js +1 -1
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +0 -4
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/replicache-types.d.ts +3 -2
- package/out/zero-client/src/client/replicache-types.d.ts.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.d.ts +1 -2
- package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-client/src/client/zero.d.ts +1 -5
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +1 -1
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/types/client-state.d.ts +0 -6
- package/out/zero-client/src/types/client-state.d.ts.map +1 -1
- package/out/zero-permissions/src/permissions.d.ts +1 -12
- package/out/zero-permissions/src/permissions.d.ts.map +1 -1
- package/out/zero-permissions/src/permissions.js +1 -2
- package/out/zero-permissions/src/permissions.js.map +1 -1
- package/out/zero-protocol/src/ast.d.ts +0 -4
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js +1 -2
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.d.ts +0 -20
- package/out/zero-protocol/src/change-desired-queries.d.ts.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
- package/out/zero-protocol/src/client-schema.d.ts +2 -5
- package/out/zero-protocol/src/client-schema.d.ts.map +1 -1
- package/out/zero-protocol/src/client-schema.js +1 -1
- package/out/zero-protocol/src/client-schema.js.map +1 -1
- package/out/zero-protocol/src/close-connection.d.ts +0 -8
- package/out/zero-protocol/src/close-connection.d.ts.map +1 -1
- package/out/zero-protocol/src/close-connection.js +1 -1
- package/out/zero-protocol/src/close-connection.js.map +1 -1
- package/out/zero-protocol/src/connect.d.ts +0 -12
- package/out/zero-protocol/src/connect.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.js +1 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.d.ts +3 -17
- package/out/zero-protocol/src/custom-queries.d.ts.map +1 -1
- package/out/zero-protocol/src/custom-queries.js +1 -1
- package/out/zero-protocol/src/custom-queries.js.map +1 -1
- package/out/zero-protocol/src/data.d.ts +0 -1
- package/out/zero-protocol/src/data.d.ts.map +1 -1
- package/out/zero-protocol/src/data.js +1 -1
- package/out/zero-protocol/src/data.js.map +1 -1
- package/out/zero-protocol/src/down.d.ts +27 -27
- package/out/zero-protocol/src/inspect-down.d.ts +82 -84
- package/out/zero-protocol/src/inspect-down.d.ts.map +1 -1
- package/out/zero-protocol/src/inspect-down.js +1 -1
- package/out/zero-protocol/src/inspect-down.js.map +1 -1
- package/out/zero-protocol/src/inspect-up.d.ts +0 -28
- package/out/zero-protocol/src/inspect-up.d.ts.map +1 -1
- package/out/zero-protocol/src/inspect-up.js +1 -1
- package/out/zero-protocol/src/inspect-up.js.map +1 -1
- package/out/zero-protocol/src/mutate-server.d.ts +0 -21
- package/out/zero-protocol/src/mutate-server.d.ts.map +1 -1
- package/out/zero-protocol/src/mutate-server.js +1 -1
- package/out/zero-protocol/src/mutate-server.js.map +1 -1
- package/out/zero-protocol/src/mutation-type-enum.d.ts +0 -1
- package/out/zero-protocol/src/mutation-type-enum.d.ts.map +1 -1
- package/out/zero-protocol/src/mutation-type-enum.js.map +1 -1
- package/out/zero-protocol/src/mutation.d.ts +2 -4
- package/out/zero-protocol/src/mutation.d.ts.map +1 -1
- package/out/zero-protocol/src/mutation.js +1 -1
- package/out/zero-protocol/src/mutation.js.map +1 -1
- package/out/zero-protocol/src/mutations-patch.d.ts +0 -33
- package/out/zero-protocol/src/mutations-patch.d.ts.map +1 -1
- package/out/zero-protocol/src/mutations-patch.js +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/ping.d.ts +0 -2
- package/out/zero-protocol/src/ping.d.ts.map +1 -1
- package/out/zero-protocol/src/ping.js +1 -1
- package/out/zero-protocol/src/ping.js.map +1 -1
- package/out/zero-protocol/src/poke.d.ts +3 -2
- package/out/zero-protocol/src/poke.d.ts.map +1 -1
- package/out/zero-protocol/src/poke.js +1 -1
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/pong.d.ts +0 -2
- package/out/zero-protocol/src/pong.d.ts.map +1 -1
- package/out/zero-protocol/src/pong.js +1 -1
- package/out/zero-protocol/src/pong.js.map +1 -1
- package/out/zero-protocol/src/pull.d.ts +2 -7
- package/out/zero-protocol/src/pull.d.ts.map +1 -1
- package/out/zero-protocol/src/pull.js +1 -1
- package/out/zero-protocol/src/pull.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +1 -3
- package/out/zero-protocol/src/push.d.ts.map +1 -1
- package/out/zero-protocol/src/push.js +1 -1
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/queries-patch.d.ts +0 -16
- package/out/zero-protocol/src/queries-patch.d.ts.map +1 -1
- package/out/zero-protocol/src/queries-patch.js +1 -1
- package/out/zero-protocol/src/queries-patch.js.map +1 -1
- package/out/zero-protocol/src/query-server.d.ts +2 -41
- package/out/zero-protocol/src/query-server.d.ts.map +1 -1
- package/out/zero-protocol/src/query-server.js +1 -1
- package/out/zero-protocol/src/query-server.js.map +1 -1
- package/out/zero-protocol/src/up.d.ts +42 -42
- package/out/zero-protocol/src/version.d.ts +0 -1
- package/out/zero-protocol/src/version.d.ts.map +1 -1
- package/out/zero-protocol/src/version.js.map +1 -1
- package/out/zero-react/src/use-query.js +1 -1
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-schema/src/builder/relationship-builder.d.ts +0 -2
- package/out/zero-schema/src/builder/relationship-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
- package/out/zero-schema/src/builder/table-builder.d.ts +1 -1
- package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/table-builder.js +1 -1
- package/out/zero-schema/src/builder/table-builder.js.map +1 -1
- package/out/zero-schema/src/schema-config.d.ts +0 -1
- package/out/zero-schema/src/schema-config.d.ts.map +1 -1
- package/out/zero-schema/src/schema-config.js +1 -1
- package/out/zero-schema/src/schema-config.js.map +1 -1
- package/out/zero-schema/src/table-schema.d.ts +3 -11
- package/out/zero-schema/src/table-schema.d.ts.map +1 -1
- package/out/zero-server/src/queries/process-queries.d.ts +2 -4
- package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-solid/src/solid-view.d.ts +0 -2
- package/out/zero-solid/src/solid-view.d.ts.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-types/src/name-mapper.d.ts +2 -1
- package/out/zero-types/src/name-mapper.d.ts.map +1 -1
- package/out/zero-types/src/name-mapper.js.map +1 -1
- package/out/zero-types/src/schema-value.d.ts +3 -2
- package/out/zero-types/src/schema-value.d.ts.map +1 -1
- package/out/zero-types/src/schema.d.ts +1 -1
- package/out/zero-types/src/schema.d.ts.map +1 -1
- package/out/zql/src/builder/builder.d.ts +1 -11
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +1 -1
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/filter.d.ts +0 -1
- package/out/zql/src/builder/filter.d.ts.map +1 -1
- package/out/zql/src/builder/filter.js.map +1 -1
- package/out/zql/src/error.d.ts +0 -5
- package/out/zql/src/error.d.ts.map +1 -1
- package/out/zql/src/error.js.map +1 -1
- package/out/zql/src/ivm/constraint.d.ts +0 -7
- package/out/zql/src/ivm/constraint.d.ts.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.d.ts +0 -1
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +1 -2
- package/out/zql/src/ivm/memory-source.d.ts.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/stream.d.ts +0 -1
- package/out/zql/src/ivm/stream.d.ts.map +1 -1
- package/out/zql/src/ivm/stream.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts +0 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +1 -1
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts +5 -35
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +1 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/crud.d.ts +1 -3
- package/out/zql/src/mutate/crud.d.ts.map +1 -1
- package/out/zql/src/mutate/crud.js +1 -1
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/mutate/mutator.d.ts +1 -1
- 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-debug.d.ts +10 -9
- package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-node.d.ts +0 -1
- package/out/zql/src/planner/planner-node.d.ts.map +1 -1
- package/out/zql/src/planner/planner-node.js.map +1 -1
- package/out/zql/src/planner/planner-warnings.d.ts +3 -3
- package/out/zql/src/planner/planner-warnings.d.ts.map +1 -1
- package/out/zql/src/planner/planner-warnings.js.map +1 -1
- package/out/zql/src/query/expression.d.ts +0 -22
- package/out/zql/src/query/expression.d.ts.map +1 -1
- package/out/zql/src/query/expression.js +1 -1
- package/out/zql/src/query/expression.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts +0 -7
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +2 -2
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-delegate.d.ts +1 -4
- package/out/zql/src/query/query-delegate.d.ts.map +1 -1
- package/out/zql/src/query/query-internals.d.ts +0 -3
- package/out/zql/src/query/query-internals.d.ts.map +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts +5 -5
- 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/query.d.ts +2 -13
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.d.ts +1 -19
- package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +1 -1
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/ttl.d.ts +2 -3
- package/out/zql/src/query/ttl.d.ts.map +1 -1
- package/out/zql/src/query/ttl.js.map +1 -1
- package/out/zqlite/src/query-builder.d.ts +0 -2
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js +1 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts +0 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +1 -1
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +2 -3
|
@@ -29,7 +29,6 @@ export declare function liteRow(row: RowValue, table: LiteTableSpec, jsonFormat:
|
|
|
29
29
|
*/
|
|
30
30
|
export declare function liteValue(val: PostgresValueType, pgType: string, jsonFormat: JSONFormat): LiteValueType;
|
|
31
31
|
export declare function mapLiteDataTypeToZqlSchemaValue(liteDataType: LiteTypeString): SchemaValue;
|
|
32
|
-
export declare const TEXT_ARRAY_ATTRIBUTE = "|TEXT_ARRAY";
|
|
33
32
|
/**
|
|
34
33
|
* The `LiteTypeString` utilizes SQLite's loose type system to encode
|
|
35
34
|
* auxiliary information about the upstream column (e.g. type and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lite.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/lite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACV,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AAE3C,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,UAAU,CAAC;AAEzE,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC;AAQjC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,MAAM,MAAM,UAAU,GAAG,OAAO,gBAAgB,GAAG,OAAO,WAAW,CAAC;AAEtE;;;GAGG;AACH,wBAAgB,OAAO,CACrB,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,UAAU,GACrB;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAwBjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,iBAAiB,EACtB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GACrB,aAAa,CAef;AAwBD,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,cAAc,GAC3B,WAAW,CAKb;
|
|
1
|
+
{"version":3,"file":"lite.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/lite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACV,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AAE3C,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,UAAU,CAAC;AAEzE,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC;AAQjC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,MAAM,MAAM,UAAU,GAAG,OAAO,gBAAgB,GAAG,OAAO,WAAW,CAAC;AAEtE;;;GAGG;AACH,wBAAgB,OAAO,CACrB,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,UAAU,GACrB;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAwBjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,iBAAiB,EACtB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GACrB,aAAa,CAef;AAwBD,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,cAAc,GAC3B,WAAW,CAKb;AAeD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC;;GAEG;AACH,wBAAgB,cAAc,CAC5B,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,EACnC,QAAQ,EAAE,OAAO,EACjB,SAAS,EAAE,OAAO,GACjB,cAAc,CAahB;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,cAAc,UAG9D;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,cAAc,WAE9D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,cAAc,GAC7B,SAAS,GAAG,SAAS,CAMvB;AAED,wBAAgB,MAAM,CAAC,cAAc,EAAE,cAAc,WAEpD;AAED,wBAAgB,OAAO,CAAC,cAAc,EAAE,cAAc,WAMrD;AAID,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,UAAU,QAezD"}
|
|
@@ -113,7 +113,7 @@ function isEnum(liteTypeString) {
|
|
|
113
113
|
return liteTypeString.includes(TEXT_ENUM_ATTRIBUTE);
|
|
114
114
|
}
|
|
115
115
|
function isArray(liteTypeString) {
|
|
116
|
-
return liteTypeString.includes(
|
|
116
|
+
return liteTypeString.includes(TEXT_ARRAY_ATTRIBUTE) || liteTypeString.includes("[]");
|
|
117
117
|
}
|
|
118
118
|
var invalidDataTypeRe = /^.+\|.*\[\]/;
|
|
119
119
|
function assertValidLiteColumnSpec(spec) {
|
|
@@ -123,6 +123,6 @@ function assertValidLiteColumnSpec(spec) {
|
|
|
123
123
|
assert(!invalidDataTypeRe.test(dataType), `Invalid dataType ${dataType}`);
|
|
124
124
|
}
|
|
125
125
|
//#endregion
|
|
126
|
-
export {
|
|
126
|
+
export { assertValidLiteColumnSpec, isArray, isEnum, liteRow, liteTypeString, liteTypeToZqlValueType, liteValue, mapLiteDataTypeToZqlSchemaValue, nullableUpstream, upstreamDataType };
|
|
127
127
|
|
|
128
128
|
//# sourceMappingURL=lite.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lite.js","names":["upstreamDataTypeToZqlValueType"],"sources":["../../../../../zero-cache/src/types/lite.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport type {\n SchemaValue,\n ValueType,\n} from '../../../zero-schema/src/table-schema.ts';\nimport type {ColumnSpec, LiteTableSpec} from '../db/specs.ts';\nimport {dataTypeToZqlValueType as upstreamDataTypeToZqlValueType} from './pg-data-type.ts';\nimport type {PostgresValueType} from './pg.ts';\nimport type {RowValue} from './row-key.ts';\n\n/** Javascript value types supported by better-sqlite3. */\nexport type LiteValueType = number | bigint | string | null | Uint8Array;\n\nexport type LiteRow = Readonly<Record<string, LiteValueType>>;\nexport type LiteRowKey = LiteRow; // just for API readability\n\nfunction columnType(col: string, table: LiteTableSpec) {\n const spec = table.columns[col];\n assert(spec, `Unknown column ${col} in table ${table.name}`);\n return spec.dataType;\n}\n\nexport const JSON_STRINGIFIED = 's';\nexport const JSON_PARSED = 'p';\n\nexport type JSONFormat = typeof JSON_STRINGIFIED | typeof JSON_PARSED;\n\n/**\n * Creates a LiteRow from the supplied RowValue. A copy of the `row`\n * is made only if a value conversion is performed.\n */\nexport function liteRow(\n row: RowValue,\n table: LiteTableSpec,\n jsonFormat: JSONFormat,\n): {row: LiteRow; numCols: number} {\n let copyNeeded = false;\n let numCols = 0;\n\n for (const key in row) {\n numCols++;\n const val = row[key];\n const liteVal = liteValue(val, columnType(key, table), jsonFormat);\n if (val !== liteVal) {\n copyNeeded = true;\n break;\n }\n }\n if (!copyNeeded) {\n return {row: row as unknown as LiteRow, numCols};\n }\n // Slow path for when a conversion is needed.\n numCols = 0;\n const converted: Record<string, LiteValueType> = {};\n for (const key in row) {\n numCols++;\n converted[key] = liteValue(row[key], columnType(key, table), jsonFormat);\n }\n return {row: converted, numCols};\n}\n\n/**\n * Postgres values types that are supported by SQLite are stored as-is.\n * This includes Uint8Arrays for the `bytea` / `BLOB` type.\n * * `boolean` values are converted to `0` or `1` integers.\n * * `PreciseDate` values are converted to epoch microseconds.\n * * JSON and Array values are stored as `JSON.stringify()` strings.\n *\n * Note that this currently does not handle the `bytea[]` type, but that's\n * already a pretty questionable type.\n */\nexport function liteValue(\n val: PostgresValueType,\n pgType: string,\n jsonFormat: JSONFormat,\n): LiteValueType {\n if (val instanceof Uint8Array || val === null) {\n return val;\n }\n const valueType = liteTypeToZqlValueType(pgType);\n if (valueType === 'json') {\n if (jsonFormat === JSON_STRINGIFIED && typeof val === 'string') {\n // JSON and JSONB values are already strings if the JSON was not parsed.\n return val;\n }\n // Non-JSON/JSONB values will always appear as objects / arrays.\n return stringify(val);\n }\n const obj = toLiteValue(val);\n return obj && typeof obj === 'object' ? stringify(obj) : obj;\n}\n\nfunction toLiteValue(val: JSONValue): Exclude<JSONValue, boolean> {\n switch (typeof val) {\n case 'string':\n case 'number':\n case 'bigint':\n return val;\n case 'boolean':\n return val ? 1 : 0;\n }\n if (val === null) {\n return val;\n }\n if (Array.isArray(val)) {\n return val.map(v => toLiteValue(v));\n }\n assert(\n val.constructor?.name === 'Object',\n `Unhandled object type ${val.constructor?.name}`,\n );\n return val; // JSON\n}\n\nexport function mapLiteDataTypeToZqlSchemaValue(\n liteDataType: LiteTypeString,\n): SchemaValue {\n return {\n type: mapLiteDataTypeToZqlValueType(liteDataType),\n optional: nullableUpstream(liteDataType),\n };\n}\n\nfunction mapLiteDataTypeToZqlValueType(dataType: LiteTypeString): ValueType {\n const type = liteTypeToZqlValueType(dataType);\n if (type === undefined) {\n throw new Error(`Unsupported data type ${dataType}`);\n }\n return type;\n}\n\n// Note: Includes the \"TEXT\" substring for SQLite type affinity\nconst TEXT_ENUM_ATTRIBUTE = '|TEXT_ENUM';\nconst NOT_NULL_ATTRIBUTE = '|NOT_NULL';\nexport const TEXT_ARRAY_ATTRIBUTE = '|TEXT_ARRAY';\n\n/**\n * The `LiteTypeString` utilizes SQLite's loose type system to encode\n * auxiliary information about the upstream column (e.g. type and\n * constraints) that does not necessarily affect how SQLite handles the data,\n * but nonetheless determines how higher level logic handles the data.\n *\n * The format of the type string is the original upstream type, followed\n * by any number of attributes, each of which begins with the `|` character.\n * The current list of attributes are:\n * * `|NOT_NULL` to indicate that the upstream column does not allow nulls\n * * `|TEXT_ENUM` to indicate an enum that should be treated as a string\n * * `|TEXT_ARRAY` to indicate an array\n *\n * Examples:\n * * `int8`\n * * `int8|NOT_NULL`\n * * `timestamp with time zone`\n * * `timestamp with time zone|NOT_NULL`\n * * `nomz|TEXT_ENUM`\n * * `nomz|NOT_NULL|TEXT_ENUM`\n * * `int8[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY`\n * * `int8|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8|NOT_NULL[]` - Legacy (read support)\n * * `nomz[]|TEXT_ENUM|TEXT_ARRAY`\n * * `nomz|TEXT_ENUM[]` - Legacy (read support)\n * * `nomz|TEXT_ENUM|TEXT_ARRAY[]` - Legacy (read support)\n */\nexport type LiteTypeString = string;\n\n/**\n * Formats a {@link LiteTypeString}.\n */\nexport function liteTypeString(\n upstreamDataType: string,\n notNull: boolean | null | undefined,\n textEnum: boolean,\n textArray: boolean,\n): LiteTypeString {\n let typeString = upstreamDataType;\n assert(!typeString.includes('|'), 'Upstream type should not contain |');\n if (notNull) {\n typeString += NOT_NULL_ATTRIBUTE;\n }\n if (textEnum) {\n typeString += TEXT_ENUM_ATTRIBUTE;\n }\n if (textArray) {\n typeString += TEXT_ARRAY_ATTRIBUTE;\n }\n return typeString;\n}\n\nexport function upstreamDataType(liteTypeString: LiteTypeString) {\n const delim = liteTypeString.indexOf('|');\n return delim > 0 ? liteTypeString.substring(0, delim) : liteTypeString;\n}\n\nexport function nullableUpstream(liteTypeString: LiteTypeString) {\n return !liteTypeString.includes(NOT_NULL_ATTRIBUTE);\n}\n\n/**\n * Returns the value type for the `pgDataType` if it is supported by ZQL.\n * (Note that `pgDataType` values are stored as-is in the SQLite column defs).\n *\n * For types not supported by ZQL, returns `undefined`.\n */\nexport function liteTypeToZqlValueType(\n liteTypeString: LiteTypeString,\n): ValueType | undefined {\n return upstreamDataTypeToZqlValueType(\n upstreamDataType(liteTypeString).toLowerCase(),\n liteTypeString.includes(TEXT_ENUM_ATTRIBUTE),\n isArray(liteTypeString),\n );\n}\n\nexport function isEnum(liteTypeString: LiteTypeString) {\n return liteTypeString.includes(TEXT_ENUM_ATTRIBUTE);\n}\n\nexport function isArray(liteTypeString: LiteTypeString) {\n return (\n liteTypeString.includes(TEXT_ARRAY_ATTRIBUTE) ||\n // Before we added `|TEXT_ARRAY`, we just used `[]` suffix to indicate arrays.\n liteTypeString.includes('[]')\n );\n}\n\nconst invalidDataTypeRe = /^.+\\|.*\\[\\]/;\n\nexport function assertValidLiteColumnSpec(spec: ColumnSpec) {\n const {dataType} = spec;\n assert(\n dataType.includes(TEXT_ARRAY_ATTRIBUTE) === dataType.includes('[]'),\n () =>\n `TEXT_ARRAY_ATTRIBUTE and [] must be consistent in dataType: ${dataType}`,\n );\n assert(\n dataType.includes('[]') === (spec.elemPgTypeClass !== null),\n () =>\n `[] in dataType (${dataType}) must match elemPgTypeClass presence (${spec.elemPgTypeClass})`,\n );\n\n // and no [] after |\n assert(!invalidDataTypeRe.test(dataType), `Invalid dataType ${dataType}`);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lite.js","names":["upstreamDataTypeToZqlValueType"],"sources":["../../../../../zero-cache/src/types/lite.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport type {\n SchemaValue,\n ValueType,\n} from '../../../zero-schema/src/table-schema.ts';\nimport type {ColumnSpec, LiteTableSpec} from '../db/specs.ts';\nimport {dataTypeToZqlValueType as upstreamDataTypeToZqlValueType} from './pg-data-type.ts';\nimport type {PostgresValueType} from './pg.ts';\nimport type {RowValue} from './row-key.ts';\n\n/** Javascript value types supported by better-sqlite3. */\nexport type LiteValueType = number | bigint | string | null | Uint8Array;\n\nexport type LiteRow = Readonly<Record<string, LiteValueType>>;\nexport type LiteRowKey = LiteRow; // just for API readability\n\nfunction columnType(col: string, table: LiteTableSpec) {\n const spec = table.columns[col];\n assert(spec, `Unknown column ${col} in table ${table.name}`);\n return spec.dataType;\n}\n\nexport const JSON_STRINGIFIED = 's';\nexport const JSON_PARSED = 'p';\n\nexport type JSONFormat = typeof JSON_STRINGIFIED | typeof JSON_PARSED;\n\n/**\n * Creates a LiteRow from the supplied RowValue. A copy of the `row`\n * is made only if a value conversion is performed.\n */\nexport function liteRow(\n row: RowValue,\n table: LiteTableSpec,\n jsonFormat: JSONFormat,\n): {row: LiteRow; numCols: number} {\n let copyNeeded = false;\n let numCols = 0;\n\n for (const key in row) {\n numCols++;\n const val = row[key];\n const liteVal = liteValue(val, columnType(key, table), jsonFormat);\n if (val !== liteVal) {\n copyNeeded = true;\n break;\n }\n }\n if (!copyNeeded) {\n return {row: row as unknown as LiteRow, numCols};\n }\n // Slow path for when a conversion is needed.\n numCols = 0;\n const converted: Record<string, LiteValueType> = {};\n for (const key in row) {\n numCols++;\n converted[key] = liteValue(row[key], columnType(key, table), jsonFormat);\n }\n return {row: converted, numCols};\n}\n\n/**\n * Postgres values types that are supported by SQLite are stored as-is.\n * This includes Uint8Arrays for the `bytea` / `BLOB` type.\n * * `boolean` values are converted to `0` or `1` integers.\n * * `PreciseDate` values are converted to epoch microseconds.\n * * JSON and Array values are stored as `JSON.stringify()` strings.\n *\n * Note that this currently does not handle the `bytea[]` type, but that's\n * already a pretty questionable type.\n */\nexport function liteValue(\n val: PostgresValueType,\n pgType: string,\n jsonFormat: JSONFormat,\n): LiteValueType {\n if (val instanceof Uint8Array || val === null) {\n return val;\n }\n const valueType = liteTypeToZqlValueType(pgType);\n if (valueType === 'json') {\n if (jsonFormat === JSON_STRINGIFIED && typeof val === 'string') {\n // JSON and JSONB values are already strings if the JSON was not parsed.\n return val;\n }\n // Non-JSON/JSONB values will always appear as objects / arrays.\n return stringify(val);\n }\n const obj = toLiteValue(val);\n return obj && typeof obj === 'object' ? stringify(obj) : obj;\n}\n\nfunction toLiteValue(val: JSONValue): Exclude<JSONValue, boolean> {\n switch (typeof val) {\n case 'string':\n case 'number':\n case 'bigint':\n return val;\n case 'boolean':\n return val ? 1 : 0;\n }\n if (val === null) {\n return val;\n }\n if (Array.isArray(val)) {\n return val.map(v => toLiteValue(v));\n }\n assert(\n val.constructor?.name === 'Object',\n `Unhandled object type ${val.constructor?.name}`,\n );\n return val; // JSON\n}\n\nexport function mapLiteDataTypeToZqlSchemaValue(\n liteDataType: LiteTypeString,\n): SchemaValue {\n return {\n type: mapLiteDataTypeToZqlValueType(liteDataType),\n optional: nullableUpstream(liteDataType),\n };\n}\n\nfunction mapLiteDataTypeToZqlValueType(dataType: LiteTypeString): ValueType {\n const type = liteTypeToZqlValueType(dataType);\n if (type === undefined) {\n throw new Error(`Unsupported data type ${dataType}`);\n }\n return type;\n}\n\n// Note: Includes the \"TEXT\" substring for SQLite type affinity\nconst TEXT_ENUM_ATTRIBUTE = '|TEXT_ENUM';\nconst NOT_NULL_ATTRIBUTE = '|NOT_NULL';\nconst TEXT_ARRAY_ATTRIBUTE = '|TEXT_ARRAY';\n\n/**\n * The `LiteTypeString` utilizes SQLite's loose type system to encode\n * auxiliary information about the upstream column (e.g. type and\n * constraints) that does not necessarily affect how SQLite handles the data,\n * but nonetheless determines how higher level logic handles the data.\n *\n * The format of the type string is the original upstream type, followed\n * by any number of attributes, each of which begins with the `|` character.\n * The current list of attributes are:\n * * `|NOT_NULL` to indicate that the upstream column does not allow nulls\n * * `|TEXT_ENUM` to indicate an enum that should be treated as a string\n * * `|TEXT_ARRAY` to indicate an array\n *\n * Examples:\n * * `int8`\n * * `int8|NOT_NULL`\n * * `timestamp with time zone`\n * * `timestamp with time zone|NOT_NULL`\n * * `nomz|TEXT_ENUM`\n * * `nomz|NOT_NULL|TEXT_ENUM`\n * * `int8[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY`\n * * `int8|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8|NOT_NULL[]` - Legacy (read support)\n * * `nomz[]|TEXT_ENUM|TEXT_ARRAY`\n * * `nomz|TEXT_ENUM[]` - Legacy (read support)\n * * `nomz|TEXT_ENUM|TEXT_ARRAY[]` - Legacy (read support)\n */\nexport type LiteTypeString = string;\n\n/**\n * Formats a {@link LiteTypeString}.\n */\nexport function liteTypeString(\n upstreamDataType: string,\n notNull: boolean | null | undefined,\n textEnum: boolean,\n textArray: boolean,\n): LiteTypeString {\n let typeString = upstreamDataType;\n assert(!typeString.includes('|'), 'Upstream type should not contain |');\n if (notNull) {\n typeString += NOT_NULL_ATTRIBUTE;\n }\n if (textEnum) {\n typeString += TEXT_ENUM_ATTRIBUTE;\n }\n if (textArray) {\n typeString += TEXT_ARRAY_ATTRIBUTE;\n }\n return typeString;\n}\n\nexport function upstreamDataType(liteTypeString: LiteTypeString) {\n const delim = liteTypeString.indexOf('|');\n return delim > 0 ? liteTypeString.substring(0, delim) : liteTypeString;\n}\n\nexport function nullableUpstream(liteTypeString: LiteTypeString) {\n return !liteTypeString.includes(NOT_NULL_ATTRIBUTE);\n}\n\n/**\n * Returns the value type for the `pgDataType` if it is supported by ZQL.\n * (Note that `pgDataType` values are stored as-is in the SQLite column defs).\n *\n * For types not supported by ZQL, returns `undefined`.\n */\nexport function liteTypeToZqlValueType(\n liteTypeString: LiteTypeString,\n): ValueType | undefined {\n return upstreamDataTypeToZqlValueType(\n upstreamDataType(liteTypeString).toLowerCase(),\n liteTypeString.includes(TEXT_ENUM_ATTRIBUTE),\n isArray(liteTypeString),\n );\n}\n\nexport function isEnum(liteTypeString: LiteTypeString) {\n return liteTypeString.includes(TEXT_ENUM_ATTRIBUTE);\n}\n\nexport function isArray(liteTypeString: LiteTypeString) {\n return (\n liteTypeString.includes(TEXT_ARRAY_ATTRIBUTE) ||\n // Before we added `|TEXT_ARRAY`, we just used `[]` suffix to indicate arrays.\n liteTypeString.includes('[]')\n );\n}\n\nconst invalidDataTypeRe = /^.+\\|.*\\[\\]/;\n\nexport function assertValidLiteColumnSpec(spec: ColumnSpec) {\n const {dataType} = spec;\n assert(\n dataType.includes(TEXT_ARRAY_ATTRIBUTE) === dataType.includes('[]'),\n () =>\n `TEXT_ARRAY_ATTRIBUTE and [] must be consistent in dataType: ${dataType}`,\n );\n assert(\n dataType.includes('[]') === (spec.elemPgTypeClass !== null),\n () =>\n `[] in dataType (${dataType}) must match elemPgTypeClass presence (${spec.elemPgTypeClass})`,\n );\n\n // and no [] after |\n assert(!invalidDataTypeRe.test(dataType), `Invalid dataType ${dataType}`);\n}\n"],"mappings":"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"}
|
|
@@ -14,58 +14,18 @@ export declare const CHAR = 18;
|
|
|
14
14
|
export declare const INT8 = 20;
|
|
15
15
|
export declare const INT2 = 21;
|
|
16
16
|
export declare const INT4 = 23;
|
|
17
|
-
export declare const REGPROC = 24;
|
|
18
17
|
export declare const TEXT = 25;
|
|
19
|
-
export declare const OID = 26;
|
|
20
|
-
export declare const TID = 27;
|
|
21
|
-
export declare const XID = 28;
|
|
22
|
-
export declare const CID = 29;
|
|
23
18
|
export declare const JSON = 114;
|
|
24
|
-
export declare const XML = 142;
|
|
25
|
-
export declare const PG_NODE_TREE = 194;
|
|
26
|
-
export declare const SMGR = 210;
|
|
27
|
-
export declare const PATH = 602;
|
|
28
|
-
export declare const POLYGON = 604;
|
|
29
|
-
export declare const CIDR = 650;
|
|
30
19
|
export declare const FLOAT4 = 700;
|
|
31
20
|
export declare const FLOAT8 = 701;
|
|
32
|
-
export declare const ABSTIME = 702;
|
|
33
|
-
export declare const RELTIME = 703;
|
|
34
|
-
export declare const TINTERVAL = 704;
|
|
35
|
-
export declare const CIRCLE = 718;
|
|
36
|
-
export declare const MACADDR8 = 774;
|
|
37
|
-
export declare const MONEY = 790;
|
|
38
|
-
export declare const MACADDR = 829;
|
|
39
|
-
export declare const INET = 869;
|
|
40
|
-
export declare const ACLITEM = 1033;
|
|
41
21
|
export declare const BPCHAR = 1042;
|
|
42
22
|
export declare const VARCHAR = 1043;
|
|
43
23
|
export declare const DATE = 1082;
|
|
44
24
|
export declare const TIME = 1083;
|
|
45
25
|
export declare const TIMESTAMP = 1114;
|
|
46
26
|
export declare const TIMESTAMPTZ = 1184;
|
|
47
|
-
export declare const INTERVAL = 1186;
|
|
48
27
|
export declare const TIMETZ = 1266;
|
|
49
|
-
export declare const BIT = 1560;
|
|
50
|
-
export declare const VARBIT = 1562;
|
|
51
28
|
export declare const NUMERIC = 1700;
|
|
52
|
-
export declare const REFCURSOR = 1790;
|
|
53
|
-
export declare const REGPROCEDURE = 2202;
|
|
54
|
-
export declare const REGOPER = 2203;
|
|
55
|
-
export declare const REGOPERATOR = 2204;
|
|
56
|
-
export declare const REGCLASS = 2205;
|
|
57
|
-
export declare const REGTYPE = 2206;
|
|
58
29
|
export declare const UUID = 2950;
|
|
59
|
-
export declare const TXID_SNAPSHOT = 2970;
|
|
60
|
-
export declare const PG_LSN = 3220;
|
|
61
|
-
export declare const PG_NDISTINCT = 3361;
|
|
62
|
-
export declare const PG_DEPENDENCIES = 3402;
|
|
63
|
-
export declare const TSVECTOR = 3614;
|
|
64
|
-
export declare const TSQUERY = 3615;
|
|
65
|
-
export declare const GTSVECTOR = 3642;
|
|
66
|
-
export declare const REGCONFIG = 3734;
|
|
67
|
-
export declare const REGDICTIONARY = 3769;
|
|
68
30
|
export declare const JSONB = 3802;
|
|
69
|
-
export declare const REGNAMESPACE = 4089;
|
|
70
|
-
export declare const REGROLE = 4096;
|
|
71
31
|
//# sourceMappingURL=pg-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-types.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/pg-types.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,KAAK,KAAK,CAAC;AACxB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"pg-types.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/pg-types.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,KAAK,KAAK,CAAC;AACxB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,IAAI,MAAM,CAAC;AACxB,eAAO,MAAM,MAAM,MAAM,CAAC;AAC1B,eAAO,MAAM,MAAM,MAAM,CAAC;AAC1B,eAAO,MAAM,MAAM,OAAO,CAAC;AAC3B,eAAO,MAAM,OAAO,OAAO,CAAC;AAC5B,eAAO,MAAM,IAAI,OAAO,CAAC;AACzB,eAAO,MAAM,IAAI,OAAO,CAAC;AACzB,eAAO,MAAM,SAAS,OAAO,CAAC;AAC9B,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,eAAO,MAAM,MAAM,OAAO,CAAC;AAC3B,eAAO,MAAM,OAAO,OAAO,CAAC;AAC5B,eAAO,MAAM,IAAI,OAAO,CAAC;AACzB,eAAO,MAAM,KAAK,OAAO,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-types.js","names":[],"sources":["../../../../../zero-cache/src/types/pg-types.ts"],"sourcesContent":["// Forked from https=//github.com/brianc/node-pg-types/blob/5b26b826466cff4a9092b8c9e31960fe293ef3d9/lib/builtins.js\n\n/**\n * Following query was used to generate this file=\n\n SELECT json_object_agg(UPPER(PT.typname), PT.oid==int4 ORDER BY pt.oid)\n FROM pg_type PT\n WHERE typnamespace = (SELECT pgn.oid FROM pg_namespace pgn WHERE nspname = 'pg_catalog') -- Take only builting Postgres types with stable OID (extension types are not guaranted to be stable)\n AND typtype = 'b' -- Only basic types\n AND typelem = 0 -- Ignore aliases\n AND typisdefined -- Ignore undefined types\n */\n\nexport const BOOL = 16;\nexport const BYTEA = 17;\nexport const CHAR = 18;\nexport const INT8 = 20;\nexport const INT2 = 21;\nexport const INT4 = 23;\nexport const
|
|
1
|
+
{"version":3,"file":"pg-types.js","names":[],"sources":["../../../../../zero-cache/src/types/pg-types.ts"],"sourcesContent":["// Forked from https=//github.com/brianc/node-pg-types/blob/5b26b826466cff4a9092b8c9e31960fe293ef3d9/lib/builtins.js\n\n/**\n * Following query was used to generate this file=\n\n SELECT json_object_agg(UPPER(PT.typname), PT.oid==int4 ORDER BY pt.oid)\n FROM pg_type PT\n WHERE typnamespace = (SELECT pgn.oid FROM pg_namespace pgn WHERE nspname = 'pg_catalog') -- Take only builting Postgres types with stable OID (extension types are not guaranted to be stable)\n AND typtype = 'b' -- Only basic types\n AND typelem = 0 -- Ignore aliases\n AND typisdefined -- Ignore undefined types\n */\n\nexport const BOOL = 16;\nexport const BYTEA = 17;\nexport const CHAR = 18;\nexport const INT8 = 20;\nexport const INT2 = 21;\nexport const INT4 = 23;\nexport const TEXT = 25;\nexport const JSON = 114;\nexport const FLOAT4 = 700;\nexport const FLOAT8 = 701;\nexport const BPCHAR = 1042;\nexport const VARCHAR = 1043;\nexport const DATE = 1082;\nexport const TIME = 1083;\nexport const TIMESTAMP = 1114;\nexport const TIMESTAMPTZ = 1184;\nexport const TIMETZ = 1266;\nexport const NUMERIC = 1700;\nexport const UUID = 2950;\nexport const JSONB = 3802;\n"],"mappings":";AAuBA,IAAa,SAAS;AACtB,IAAa,UAAU;AACvB,IAAa,OAAO;AACpB,IAAa,OAAO;AACpB,IAAa,YAAY;AACzB,IAAa,cAAc;AAC3B,IAAa,SAAS;AACtB,IAAa,UAAU;AACvB,IAAa,OAAO;AACpB,IAAa,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poke-chunk.js","names":[],"sources":["../../../../../zero-cache/src/types/poke-chunk.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {\n POKE_CHUNK_MESSAGE_TYPE,\n type PokeChunk,\n} from '../../../zero-protocol/src/poke.ts';\n\
|
|
1
|
+
{"version":3,"file":"poke-chunk.js","names":[],"sources":["../../../../../zero-cache/src/types/poke-chunk.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {\n POKE_CHUNK_MESSAGE_TYPE,\n type PokeChunk,\n} from '../../../zero-protocol/src/poke.ts';\n\nconst POKE_CHUNK_BYTES = 1024 * 1024;\n\nexport type EmitPokeChunk = (chunk: PokeChunk) => Promise<void>;\n\n/**\n * Streams a JSON array into fixed-size UTF-8 chunks.\n *\n * The emitter must finish consuming a chunk before its promise resolves. This\n * lets the encoder reuse one fixed-size buffer for the entire poke. Memory is\n * therefore O(chunk size), independent of poke size.\n */\nexport class PokeChunkEncoder {\n readonly #encoder = new TextEncoder();\n readonly #buffer: Uint8Array;\n #bufferedBytes = 1;\n #patchCount = 0;\n #finished = false;\n\n constructor(chunkBytes = POKE_CHUNK_BYTES) {\n // One byte tags the binary message and four bytes are required to encode\n // the largest UTF-8 code point. A smaller buffer could prevent progress.\n assert(chunkBytes >= 5, 'poke chunk size must be at least five bytes');\n this.#buffer = new Uint8Array(chunkBytes);\n this.#buffer[0] = POKE_CHUNK_MESSAGE_TYPE;\n }\n\n async addPatch(serializedPatch: string, emit: EmitPokeChunk): Promise<void> {\n assert(!this.#finished, 'cannot add a patch to a finished poke');\n await this.#write(this.#patchCount++ === 0 ? '[' : ',', emit);\n await this.#write(serializedPatch, emit);\n }\n\n async finish(emit: EmitPokeChunk): Promise<void> {\n assert(!this.#finished, 'cannot finish a poke twice');\n this.#finished = true;\n await this.#write(this.#patchCount === 0 ? '[]' : ']', emit);\n await this.#flush(emit);\n }\n\n cancel(): void {\n this.#finished = true;\n this.#bufferedBytes = 1;\n }\n\n async #write(value: string, emit: EmitPokeChunk): Promise<void> {\n let read = 0;\n while (read < value.length) {\n const target = this.#buffer.subarray(this.#bufferedBytes);\n const result = this.#encoder.encodeInto(value.slice(read), target);\n read += result.read;\n this.#bufferedBytes += result.written;\n\n // A multi-byte code point may not fit in the remaining space.\n if (this.#bufferedBytes === this.#buffer.length || result.read === 0) {\n await this.#flush(emit);\n }\n }\n }\n\n async #flush(emit: EmitPokeChunk): Promise<void> {\n if (this.#bufferedBytes === 1) {\n return;\n }\n await emit(this.#buffer.subarray(0, this.#bufferedBytes));\n this.#bufferedBytes = 1;\n }\n}\n"],"mappings":";;;AAMA,IAAM,mBAAmB;;;;;;;;AAWzB,IAAa,mBAAb,MAA8B;CAC5B,WAAoB,IAAI,YAAY;CACpC;CACA,iBAAiB;CACjB,cAAc;CACd,YAAY;CAEZ,YAAY,aAAa,kBAAkB;EAGzC,OAAO,cAAc,GAAG,6CAA6C;EACrE,KAAK,UAAU,IAAI,WAAW,UAAU;EACxC,KAAK,QAAQ,KAAA;CACf;CAEA,MAAM,SAAS,iBAAyB,MAAoC;EAC1E,OAAO,CAAC,KAAK,WAAW,uCAAuC;EAC/D,MAAM,KAAK,OAAO,KAAK,kBAAkB,IAAI,MAAM,KAAK,IAAI;EAC5D,MAAM,KAAK,OAAO,iBAAiB,IAAI;CACzC;CAEA,MAAM,OAAO,MAAoC;EAC/C,OAAO,CAAC,KAAK,WAAW,4BAA4B;EACpD,KAAK,YAAY;EACjB,MAAM,KAAK,OAAO,KAAK,gBAAgB,IAAI,OAAO,KAAK,IAAI;EAC3D,MAAM,KAAK,OAAO,IAAI;CACxB;CAEA,SAAe;EACb,KAAK,YAAY;EACjB,KAAK,iBAAiB;CACxB;CAEA,MAAM,OAAO,OAAe,MAAoC;EAC9D,IAAI,OAAO;EACX,OAAO,OAAO,MAAM,QAAQ;GAC1B,MAAM,SAAS,KAAK,QAAQ,SAAS,KAAK,cAAc;GACxD,MAAM,SAAS,KAAK,SAAS,WAAW,MAAM,MAAM,IAAI,GAAG,MAAM;GACjE,QAAQ,OAAO;GACf,KAAK,kBAAkB,OAAO;GAG9B,IAAI,KAAK,mBAAmB,KAAK,QAAQ,UAAU,OAAO,SAAS,GACjE,MAAM,KAAK,OAAO,IAAI;EAE1B;CACF;CAEA,MAAM,OAAO,MAAoC;EAC/C,IAAI,KAAK,mBAAmB,GAC1B;EAEF,MAAM,KAAK,KAAK,QAAQ,SAAS,GAAG,KAAK,cAAc,CAAC;EACxD,KAAK,iBAAiB;CACxB;AACF"}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { type JSONValue } from '../../../shared/src/bigint-json.ts';
|
|
2
|
-
export type ColumnType = {
|
|
3
|
-
readonly typeOid: number;
|
|
4
|
-
};
|
|
5
|
-
export type RowKeyType = Readonly<Record<string, ColumnType>>;
|
|
6
2
|
export type RowKey = Readonly<Record<string, JSONValue>>;
|
|
7
3
|
export type RowID = Readonly<{
|
|
8
4
|
schema: string;
|
|
9
5
|
table: string;
|
|
10
6
|
rowKey: RowKey;
|
|
11
7
|
}>;
|
|
12
|
-
export type RowType = RowKeyType;
|
|
13
8
|
export type RowValue = RowKey;
|
|
14
9
|
/**
|
|
15
10
|
* Returns the `RowKey` such that key iteration produces a sorted sequence. If the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"row-key.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/row-key.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,SAAS,EAAC,MAAM,oCAAoC,CAAC;AAG7E,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"row-key.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/row-key.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,SAAS,EAAC,MAAM,oCAAoC,CAAC;AAG7E,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAEzD,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC,CAAC;AAE9E,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAClC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAkB7B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAQD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,CAE7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"row-key.js","names":[],"sources":["../../../../../zero-cache/src/types/row-key.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\n\nexport type
|
|
1
|
+
{"version":3,"file":"row-key.js","names":[],"sources":["../../../../../zero-cache/src/types/row-key.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\n\nexport type RowKey = Readonly<Record<string, JSONValue>>;\n\nexport type RowID = Readonly<{schema: string; table: string; rowKey: RowKey}>;\n\nexport type RowValue = RowKey;\n\n/**\n * Returns the `RowKey` such that key iteration produces a sorted sequence. If the\n * keys are already sorted, the input is returned as is.\n *\n * Note that the value type is parameterized as `V` so that this method can be used\n * for both (pg) RowKeys and LiteRowKeys.\n */\nexport function normalizedKeyOrder<V>(\n rowKey: Readonly<Record<string, V>>,\n): Readonly<Record<string, V>> {\n let last = '';\n let empty = true;\n for (const col in rowKey) {\n empty = false;\n if (last > col) {\n const entries = Object.entries(rowKey).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0,\n );\n assert(entries.length > 0, 'empty row key');\n return Object.fromEntries(entries);\n }\n last = col;\n }\n assert(!empty, 'empty row key');\n // This case iterates over columns and avoids object allocations, which is\n // expected to be the common case (e.g. single column key).\n return rowKey;\n}\n\n/**\n * Returns a normalized string suitable for representing a row key in a form\n * that can be used as a Map key.\n */\nexport function rowKeyString(key: RowKey): string {\n return stringify(tuples(key));\n}\n\nfunction tuples(key: RowKey) {\n return Object.entries(normalizedKeyOrder(key)).flat();\n}\n\nconst rowIDStrings = new WeakMap<RowID, string>();\n\n/**\n * A normalized string representation of a {@link RowID} suitable to use\n * as a Map key.\n */\nexport function rowIDString(id: RowID): string {\n return getOrInsertComputed(rowIDStrings, id, rowIDStringUncached);\n}\n\nfunction rowIDStringUncached(id: RowID): string {\n return stringify([id.schema, id.table, ...tuples(id.rowKey)]);\n}\n"],"mappings":";;;;;;;;;;;AAiBA,SAAgB,mBACd,QAC6B;CAC7B,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,KAAK,MAAM,OAAO,QAAQ;EACxB,QAAQ;EACR,IAAI,OAAO,KAAK;GACd,MAAM,UAAU,OAAO,QAAQ,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OACjD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAC3B;GACA,OAAO,QAAQ,SAAS,GAAG,eAAe;GAC1C,OAAO,OAAO,YAAY,OAAO;EACnC;EACA,OAAO;CACT;CACA,OAAO,CAAC,OAAO,eAAe;CAG9B,OAAO;AACT;AAUA,SAAS,OAAO,KAAa;CAC3B,OAAO,OAAO,QAAQ,mBAAmB,GAAG,CAAC,CAAC,CAAC,KAAK;AACtD;AAEA,IAAM,+BAAe,IAAI,QAAuB;;;;;AAMhD,SAAgB,YAAY,IAAmB;CAC7C,OAAO,oBAAoB,cAAc,IAAI,mBAAmB;AAClE;AAEA,SAAS,oBAAoB,IAAmB;CAC9C,OAAO,UAAU;EAAC,GAAG;EAAQ,GAAG;EAAO,GAAG,OAAO,GAAG,MAAM;CAAC,CAAC;AAC9D"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { assert } from "../../../shared/src/asserts.js";
|
|
2
|
+
import { resolver } from "../../../shared/src/resolver.js";
|
|
2
3
|
import { parse, test, valita_exports } from "../../../shared/src/valita.js";
|
|
3
4
|
import { BigIntJSON } from "../../../shared/src/bigint-json.js";
|
|
4
5
|
import { closeWithError, sendPingsForLiveness } from "./ws.js";
|
|
5
6
|
import { Queue } from "../../../shared/src/queue.js";
|
|
6
7
|
import { Subscription } from "./subscription.js";
|
|
7
|
-
import { resolver } from "@rocicorp/resolver";
|
|
8
8
|
import { createWebSocketStream } from "ws";
|
|
9
9
|
import { Readable, Transform, Writable, pipeline } from "node:stream";
|
|
10
10
|
//#region ../zero-cache/src/types/streams.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streams.js","names":["v.parse","v","v.test"],"sources":["../../../../../zero-cache/src/types/streams.ts"],"sourcesContent":["import {\n pipeline,\n Readable,\n Transform,\n Writable,\n type DuplexOptions,\n} from 'node:stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {\n createWebSocketStream,\n type CloseEvent,\n type ErrorEvent,\n type MessageEvent,\n type WebSocket,\n} from 'ws';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {Queue} from '../../../shared/src/queue.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Subscription, type Options} from './subscription.ts';\nimport {closeWithError, sendPingsForLiveness} from './ws.ts';\n\n// Consistent with Postgres keepalives, and shorter than the\n// commonly used default idle timeout of 1 minute.\nconst PING_INTERVAL_MS = 30_000;\n\nexport type Source<T> = AsyncIterable<T> & {\n /**\n * Immediately terminates all current iterations (i.e. {@link AsyncIterator.next next()})\n * will return `{value: undefined, done: true}`), and prevents any subsequent iterations\n * from yielding any values.\n *\n * @param err Terminate the iteration by throwing the `err` instead.\n */\n cancel: (err?: Error) => void;\n\n /**\n * An AbortSignal that can be used to listen for termination or\n * race a short-lived promise against the termination of the iterable\n * (via `promiseOrAbort()`)\n */\n readonly signal: AbortSignal;\n\n /**\n * The presence of a `pipeline` iterable allows the usual \"consumed-on-iterate\" semantics\n * to be overridden.\n *\n * This is suitable for transport layers that serialize messages across processes, such\n * as the {@link streamOut()} method; pipelining allows the transport to send messages\n * as they arrive without waiting for the previous message to be acked, streaming\n * them to the receiving process where they are presumably queued and processed without\n * a per-message ack delay. The receiving end of the transport then responds with acks\n * asynchronously as the receiving end processes the messages.\n */\n pipeline?: AsyncIterable<{value: T; consumed: () => void}> | undefined;\n\n /**\n * Pipelined batching support: eagerly drains available queued messages up to `maxBatch`.\n */\n pipelineBatched?:\n | ((\n maxBatch?: number,\n ) => AsyncIterable<{values: T[]; consumed: () => void}> | undefined)\n | undefined;\n};\n\nexport type Sink<T> = {\n push(message: T): void;\n};\n\n/**\n * Utility method for reading the first items from a {@link Source} and passing\n * the `rest` to a separate consumer. The caller should call `head.next()` to\n * pull items out of the Source, but should not call the\n * {@link AsyncIterator.return()} method, as that will cancel the Source.\n */\nexport function readHead<T>(source: Source<T>): {\n head: AsyncIterator<T, T, T>;\n rest: Source<T>;\n} {\n const iter = source[Symbol.asyncIterator]();\n const rest: Source<T> = {\n [Symbol.asyncIterator]: () => iter,\n cancel: err => source.cancel(err),\n signal: source.signal,\n };\n return {head: iter, rest};\n}\n\n/**\n * Back-pressure-aware transformation of a WebSocket into\n * upstream and downstream {@link Subscription} objects.\n *\n * This is used for connections with external servers\n * (i.e. custom change sources). For inter-zero-cache communication,\n * see {@link streamInternal}.\n */\nexport function stream<In extends JSONValue, Out extends JSONValue>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In>,\n outOptions: Options<Out> = {},\n inOptions: Options<In> = {},\n streamOptions: DuplexOptions = {},\n): {outstream: Sink<Out>; instream: Source<In>} {\n const endpoint = ws.url ?? 'client';\n function close(err?: unknown) {\n if (ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) {\n if (err) {\n closeWithError(lc, ws, err);\n } else {\n lc.info?.(`closing connection to ${endpoint}`);\n ws.close();\n }\n }\n }\n\n const instream = Subscription.create<In>({\n ...inOptions,\n cleanup: (unconsumed, err) => {\n inOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n const outstream = Subscription.create<Out>({\n ...outOptions,\n cleanup: (unconsumed, err) => {\n outOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n\n const duplex = createWebSocketStream(ws, {\n ...streamOptions,\n decodeStrings: false,\n });\n\n // Outgoing transform.\n function streamOut() {\n // Mainly used for verifying that back-pressure kicks in tests.\n duplex.on('drain', () => lc.debug?.(`drained messages to ${endpoint}`));\n\n pipeline(\n Readable.from(outstream),\n new Transform({\n objectMode: true,\n transform: (msg, _encoding, callback) =>\n callback(null, BigIntJSON.stringify(msg)),\n }),\n duplex,\n err => (err ? outstream.fail(err) : outstream.cancel()),\n );\n }\n\n if (ws.readyState === ws.CONNECTING) {\n ws.on('open', () => {\n lc.info?.(`connected to ${endpoint}`);\n streamOut();\n });\n } else {\n streamOut();\n }\n\n // Incoming transform.\n pipe({\n source: duplex,\n sink: instream,\n parse: chunk => {\n const json = BigIntJSON.parse(chunk.toString());\n return v.parse(json, inSchema, 'passthrough');\n },\n });\n\n sendPingsForLiveness(lc, ws, PING_INTERVAL_MS);\n\n return {outstream, instream};\n}\n\ntype PipeOptions<T> = {\n source: Readable;\n sink: Subscription<T>;\n parse: (buffer: Buffer) => T | null;\n bufferMessages?: number;\n};\n\nexport function pipe<T>({source, sink, parse, bufferMessages}: PipeOptions<T>) {\n bufferMessages ??= 0;\n assert(bufferMessages >= 0, 'bufferMessages must be non-negative');\n const pending: Promise<unknown>[] = [];\n\n pipeline(\n source,\n new Writable({\n decodeStrings: false,\n write: (chunk, _encoding, callback) => {\n let msg: T | null;\n try {\n if ((msg = parse(chunk)) === null) {\n callback();\n return;\n }\n } catch (err) {\n callback(ensureError(err));\n return;\n }\n // Inbound backpressure is exerted by unconsumed messages in the\n // subscription. A buffer can be used to allow messages to queue up in\n // in the Subscription object, which allows the consumer to \"peek\" at\n // whether there are more messages immediately available\n // (via {@link Subscription.queued}.\n const {result} = sink.push(msg);\n pending.push(result);\n void result.then(() => pending.shift());\n\n if (pending.length <= bufferMessages) {\n // immediately allow more messages\n callback();\n } else {\n // wait for the oldest result in the pending queue\n pending[0].then(\n () => callback(),\n err => callback(ensureError(err)),\n );\n }\n },\n destroy: (err, callback) => {\n if (err) {\n sink.fail(ensureError(err));\n }\n // Otherwise, final will handle the cancel.\n callback();\n },\n final: callback => {\n sink.cancel();\n callback();\n },\n }),\n err => (err ? sink.fail(err) : sink.cancel()),\n );\n}\n\nfunction ensureError(err: unknown) {\n return err instanceof Error ? err : new Error(String(err));\n}\n\nconst ackSchema = v.object({ack: v.number()});\n\ntype Ack = v.Infer<typeof ackSchema>;\n\n/** A parsed value paired with its approximate serialized transport size. */\nexport type Sized<T> = {\n data: T;\n size: number;\n};\n\nexport type StreamOutOptions = {\n batched?: boolean | undefined;\n maxBatchSize?: number | undefined;\n};\n\nexport type PreSerialized = {\n readonly payload: Buffer;\n readonly byteLength: number;\n};\n\nexport function isPreSerialized(val: unknown): val is PreSerialized {\n return (\n typeof val === 'object' &&\n val !== null &&\n 'payload' in val &&\n Buffer.isBuffer((val as PreSerialized).payload)\n );\n}\n\nfunction sendTextFrame(sink: WebSocket, data: Buffer | string) {\n if (typeof data === 'string') {\n sink.send(data);\n } else {\n (sink as unknown as {send: (data: unknown, opts?: unknown) => void}).send(\n data,\n {binary: false},\n );\n }\n}\n\n/**\n * Establishes a bidirectional stream over an internal zero-cache to zero-cache\n * WebSocket. Both peers call this: each may send an outbound {@link Source}\n * (`outSource`) and receives an inbound {@link Source} (the return value).\n * Transport-level `{id, msg}` / `{ack}` framing is multiplexed in both\n * directions over the single socket.\n *\n * `inSchema` validates inbound application frames; it may be omitted by a peer\n * that expects no inbound application messages (any that arrive are a protocol\n * error).\n */\nexport function streamInternal<\n In extends JSONValue,\n Out extends JSONValue = JSONValue,\n>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In> | undefined,\n outSource?: Source<Out>,\n options?: StreamOutOptions,\n): Promise<Source<In>> {\n return streamInternalCore(\n lc,\n ws,\n inSchema,\n outSource,\n BigIntJSON.stringify,\n data => data,\n options,\n );\n}\n\n/**\n * {@link streamInternal} whose outbound `Source` carries messages that are\n * already stringified JSON or pre-serialized Buffers.\n */\nexport function streamInternalStringified<In extends JSONValue>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In> | undefined,\n outSource?: Source<string | PreSerialized>,\n options?: StreamOutOptions,\n): Promise<Source<In>> {\n return streamInternalCore(\n lc,\n ws,\n inSchema,\n outSource,\n msg => (typeof msg === 'string' ? msg : msg.payload.toString('utf8')),\n data => data,\n options,\n );\n}\n\n/**\n * {@link streamInternal} that retains only the transport-frame size of each\n * inbound message. The size bounds downstream batching without keeping or\n * copying the JSON.\n */\nexport function streamInternalWithSize<\n In extends JSONValue,\n Out extends JSONValue = JSONValue,\n>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In>,\n outSource?: Source<Out>,\n options?: StreamOutOptions,\n): Promise<Source<Sized<In>>> {\n return streamInternalCore(\n lc,\n ws,\n inSchema,\n outSource,\n BigIntJSON.stringify,\n (data, _frame, _id, size) => ({data, size}),\n options,\n );\n}\n\n/**\n * Runs the outbound send loop of a {@link streamInternal} connection: reads\n * messages from `source`, frames them as `{id, msg}` / `{id, batch}` (or\n * pre-serialized Buffers), and awaits the corresponding `{ack}` frames — which\n * are demuxed off the socket by the shared message handler in\n * {@link streamInternalCore} and delivered via the `acks` queue.\n */\nasync function runOutbound<T extends JSONValue | PreSerialized>(\n lc: LogContext,\n source: Source<T>,\n sink: WebSocket,\n stringify: (payload: T) => string,\n acks: Queue<Ack>,\n closer: WebSocketCloser,\n options?: StreamOutOptions | undefined,\n): Promise<void> {\n try {\n let nextID = 0;\n const {pipeline} = source;\n const batched = options?.batched ?? false;\n const maxBatchSize = Math.max(1, Math.floor(options?.maxBatchSize ?? 64));\n\n if (batched && source.pipelineBatched) {\n const batchedIterable = source.pipelineBatched(maxBatchSize);\n if (batchedIterable) {\n lc.debug?.(\n `started batched outbound stream (maxBatchSize=${maxBatchSize})`,\n );\n for await (const {values, consumed} of batchedIterable) {\n if (values.length === 1 && isPreSerialized(values[0])) {\n const id = ++nextID;\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, values[0].payload]);\n sendTextFrame(sink, data);\n\n void (async () => {\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })().catch(e => closer.close(e));\n } else if (values.some(isPreSerialized)) {\n let remaining = values.length;\n const onConsumed = () => {\n if (--remaining === 0) {\n consumed();\n }\n };\n for (const val of values) {\n const id = ++nextID;\n if (isPreSerialized(val)) {\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, val.payload]);\n sendTextFrame(sink, data);\n } else {\n const data = `{\"id\":${id},\"msg\":${stringify(val)}}`;\n sink.send(data);\n }\n void (async () => {\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n onConsumed();\n })().catch(e => closer.close(e));\n }\n } else {\n const id = ++nextID;\n const data =\n values.length === 1\n ? `{\"id\":${id},\"msg\":${stringify(values[0])}}`\n : `{\"id\":${id},\"batch\":[${values.map(stringify).join(',')}]}`;\n sink.send(data);\n\n void (async () => {\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })().catch(e => closer.close(e));\n }\n }\n closer.close();\n return;\n }\n }\n\n if (pipeline) {\n lc.debug?.(`started pipelined outbound stream`);\n for await (const {value: msg, consumed} of pipeline) {\n const id = ++nextID;\n if (isPreSerialized(msg)) {\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, msg.payload]);\n sendTextFrame(sink, data);\n } else {\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`pipelining`, data);\n sink.send(data);\n }\n\n // The ack is awaited off the send loop so that the next message can be\n // sent without waiting for it. A bad ack is a protocol error like in\n // the synchronous path below: close the socket (which cancels the\n // source) rather than leaving the rejection unhandled.\n void (async () => {\n const {ack} = await acks.dequeue();\n // lc.debug?.(`received ack`, ack);\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })().catch(e => closer.close(e));\n }\n } else {\n lc.debug?.(`started synchronous outbound stream`);\n for await (const msg of source) {\n const id = ++nextID;\n if (isPreSerialized(msg)) {\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, msg.payload]);\n sendTextFrame(sink, data);\n } else {\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`sending`, data);\n sink.send(data);\n }\n\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n }\n }\n closer.close();\n } catch (e) {\n closer.close(e);\n }\n}\n\nasync function streamInternalCore<\n In extends JSONValue,\n Out extends JSONValue | PreSerialized,\n TIn,\n>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In> | undefined,\n outSource: Source<Out> | undefined,\n stringify: (payload: Out) => string,\n transform: (data: In, frame: string, id: number, size: number) => TIn,\n options: StreamOutOptions | undefined,\n): Promise<Source<TIn>> {\n sendPingsForLiveness(lc, ws, PING_INTERVAL_MS);\n\n // Acks received from the peer for the messages we send on `outSource`.\n const acks = new Queue<Ack>();\n\n const streamedSchema = inSchema\n ? v.object({\n id: v.number(),\n msg: inSchema.optional(),\n batch: v.array(inSchema).optional(),\n })\n : undefined;\n\n type SinkEntry = {\n consumed: () => void;\n data: TIn;\n };\n\n const sink: Subscription<TIn, SinkEntry> = new Subscription<TIn, SinkEntry>(\n {\n consumed: ({consumed}) => consumed(),\n cleanup: () => closer.close(),\n },\n ({data}) => data,\n );\n\n const closer = WebSocketCloser.forDuplex(\n lc,\n ws,\n outSource,\n sink,\n handleMessage,\n );\n\n function handleMessage(event: MessageEvent) {\n const data = event.data.toString();\n if (!sink.active) {\n lc.warn?.('dropping ws message received after close');\n return;\n }\n try {\n const value = BigIntJSON.parse(data);\n\n // Demux: an `{ack}` frame acknowledges a message we sent on `outSource`.\n const parsedAck = v.test(value, ackSchema);\n if (parsedAck.ok) {\n acks.enqueue(parsedAck.value);\n return;\n }\n\n // Otherwise it is an inbound application frame.\n if (!streamedSchema) {\n closer.close(new Error(`unexpected inbound message: ${data}`));\n return;\n }\n const parsed = v.parse(value, streamedSchema, 'passthrough');\n const {id, msg, batch} = parsed;\n\n const sendAck = () => {\n if (ws.readyState === ws.OPEN) {\n ws.send(JSON.stringify({ack: id} satisfies Ack));\n }\n };\n\n if (batch !== undefined && msg !== undefined) {\n throw new Error(`Message ${id} has both \"msg\" and \"batch\"`);\n }\n\n if (batch !== undefined) {\n let remaining = batch.length;\n if (remaining === 0) {\n sendAck();\n return;\n }\n const onConsumed = () => {\n if (--remaining === 0) {\n sendAck();\n }\n };\n const itemSize = Math.max(1, Math.round(data.length / batch.length));\n for (const item of batch) {\n sink.push({\n consumed: onConsumed,\n data: transform(item, data, id, itemSize),\n });\n }\n } else if (msg !== undefined) {\n sink.push({\n consumed: sendAck,\n data: transform(msg, data, id, data.length),\n });\n } else {\n throw new Error(`Message ${id} has neither \"msg\" nor \"batch\"`);\n }\n } catch (e) {\n closer.close(e);\n }\n }\n\n await closer.connected;\n\n // Start the outbound send loop only after the connection is open, so that a\n // client whose socket begins in CONNECTING can push before it connects.\n if (outSource) {\n void runOutbound(lc, outSource, ws, stringify, acks, closer, options);\n }\n\n return sink;\n}\n\nclass WebSocketCloser {\n readonly #lc: LogContext;\n readonly #ws: WebSocket;\n readonly #closeStream: (err?: unknown) => void;\n readonly #messageHandler: ((e: MessageEvent) => void | undefined) | null;\n readonly #connected = resolver();\n\n get connected(): Promise<void> {\n return this.#connected.promise;\n }\n\n /**\n * Closer for a {@link streamInternalCore} duplex. On socket close it cancels\n * the outbound `Source` (notifying it of unconsumed messages, as the old\n * `forSource` did) and fails/ends the inbound `Sink` (as the old `forSink`\n * did): fail() on error so consumers throw, end() otherwise so pending\n * messages finish. Either stream may be absent for a one-directional peer.\n */\n static forDuplex<In, InInput, Out>(\n lc: LogContext,\n ws: WebSocket,\n outSource: Source<Out> | undefined,\n inSink: Subscription<In, InInput>,\n messageHandler: (e: MessageEvent) => void | undefined,\n ) {\n return new WebSocketCloser(\n lc,\n ws,\n (err?: unknown) => {\n outSource?.cancel(err instanceof Error ? err : undefined);\n if (err) {\n inSink.fail(err instanceof Error ? err : new Error(String(err)));\n } else {\n inSink.end();\n }\n },\n messageHandler,\n );\n }\n\n private constructor(\n lc: LogContext,\n ws: WebSocket,\n closeStream: (err?: unknown) => void,\n messageHandler?: (e: MessageEvent) => void | undefined,\n ) {\n this.#lc = lc;\n this.#ws = ws;\n this.#closeStream = closeStream;\n this.#messageHandler = messageHandler ?? null;\n\n ws.addEventListener('open', this.#handleOpen);\n ws.addEventListener('close', this.#handleClose);\n ws.addEventListener('error', this.#handleError);\n if (this.#messageHandler) {\n ws.addEventListener('message', this.#messageHandler);\n }\n\n switch (ws.readyState) {\n case ws.CONNECTING:\n break; // expected for new connections. resolve or reject in handlers.\n case ws.OPEN:\n this.#connected.resolve();\n break;\n default:\n this.#connected.reject(\n new Error(`websocket already in state ${ws.readyState}`),\n );\n break;\n }\n }\n\n get #conn(): string {\n return 'connection' + (this.#ws.url ? ` to ${this.#ws.url}` : '');\n }\n\n #handleOpen = () => {\n this.#lc.info?.(`${this.#conn} established`);\n this.#connected.resolve();\n };\n\n #handleClose = (e: CloseEvent) => {\n const {code, reason, wasClean} = e;\n this.#lc.info?.(`${this.#conn} closed`, {\n code,\n reason,\n wasClean,\n });\n this.close();\n this.#connected.reject(`${this.#conn} closed with code ${code}`);\n };\n\n #handleError = ({message, error}: ErrorEvent) => {\n if (this.#ws.readyState === this.#ws.OPEN) {\n this.#lc.error?.(`error in ${this.#conn}`, message, error);\n }\n this.#connected.reject(error);\n };\n\n close(err?: unknown) {\n if (err) {\n this.#lc.error?.(`closing stream with error`, err);\n }\n this.#closeStream(err);\n if (!this.closed()) {\n this.#ws.close();\n }\n }\n\n closed() {\n return (\n this.#ws.readyState === this.#ws.CLOSED ||\n this.#ws.readyState === this.#ws.CLOSING\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"streams.js","names":["v.parse","v","v.test"],"sources":["../../../../../zero-cache/src/types/streams.ts"],"sourcesContent":["import {\n pipeline,\n Readable,\n Transform,\n Writable,\n type DuplexOptions,\n} from 'node:stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {\n createWebSocketStream,\n type CloseEvent,\n type ErrorEvent,\n type MessageEvent,\n type WebSocket,\n} from 'ws';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {Queue} from '../../../shared/src/queue.ts';\nimport {resolver} from '../../../shared/src/resolver.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Subscription, type Options} from './subscription.ts';\nimport {closeWithError, sendPingsForLiveness} from './ws.ts';\n\n// Consistent with Postgres keepalives, and shorter than the\n// commonly used default idle timeout of 1 minute.\nconst PING_INTERVAL_MS = 30_000;\n\nexport type Source<T> = AsyncIterable<T> & {\n /**\n * Immediately terminates all current iterations (i.e. {@link AsyncIterator.next next()})\n * will return `{value: undefined, done: true}`), and prevents any subsequent iterations\n * from yielding any values.\n *\n * @param err Terminate the iteration by throwing the `err` instead.\n */\n cancel: (err?: Error) => void;\n\n /**\n * An AbortSignal that can be used to listen for termination or\n * race a short-lived promise against the termination of the iterable\n * (via `promiseOrAbort()`)\n */\n readonly signal: AbortSignal;\n\n /**\n * The presence of a `pipeline` iterable allows the usual \"consumed-on-iterate\" semantics\n * to be overridden.\n *\n * This is suitable for transport layers that serialize messages across processes, such\n * as the {@link streamOut()} method; pipelining allows the transport to send messages\n * as they arrive without waiting for the previous message to be acked, streaming\n * them to the receiving process where they are presumably queued and processed without\n * a per-message ack delay. The receiving end of the transport then responds with acks\n * asynchronously as the receiving end processes the messages.\n */\n pipeline?: AsyncIterable<{value: T; consumed: () => void}> | undefined;\n\n /**\n * Pipelined batching support: eagerly drains available queued messages up to `maxBatch`.\n */\n pipelineBatched?:\n | ((\n maxBatch?: number,\n ) => AsyncIterable<{values: T[]; consumed: () => void}> | undefined)\n | undefined;\n};\n\nexport type Sink<T> = {\n push(message: T): void;\n};\n\n/**\n * Utility method for reading the first items from a {@link Source} and passing\n * the `rest` to a separate consumer. The caller should call `head.next()` to\n * pull items out of the Source, but should not call the\n * {@link AsyncIterator.return()} method, as that will cancel the Source.\n */\nexport function readHead<T>(source: Source<T>): {\n head: AsyncIterator<T, T, T>;\n rest: Source<T>;\n} {\n const iter = source[Symbol.asyncIterator]();\n const rest: Source<T> = {\n [Symbol.asyncIterator]: () => iter,\n cancel: err => source.cancel(err),\n signal: source.signal,\n };\n return {head: iter, rest};\n}\n\n/**\n * Back-pressure-aware transformation of a WebSocket into\n * upstream and downstream {@link Subscription} objects.\n *\n * This is used for connections with external servers\n * (i.e. custom change sources). For inter-zero-cache communication,\n * see {@link streamInternal}.\n */\nexport function stream<In extends JSONValue, Out extends JSONValue>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In>,\n outOptions: Options<Out> = {},\n inOptions: Options<In> = {},\n streamOptions: DuplexOptions = {},\n): {outstream: Sink<Out>; instream: Source<In>} {\n const endpoint = ws.url ?? 'client';\n function close(err?: unknown) {\n if (ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) {\n if (err) {\n closeWithError(lc, ws, err);\n } else {\n lc.info?.(`closing connection to ${endpoint}`);\n ws.close();\n }\n }\n }\n\n const instream = Subscription.create<In>({\n ...inOptions,\n cleanup: (unconsumed, err) => {\n inOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n const outstream = Subscription.create<Out>({\n ...outOptions,\n cleanup: (unconsumed, err) => {\n outOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n\n const duplex = createWebSocketStream(ws, {\n ...streamOptions,\n decodeStrings: false,\n });\n\n // Outgoing transform.\n function streamOut() {\n // Mainly used for verifying that back-pressure kicks in tests.\n duplex.on('drain', () => lc.debug?.(`drained messages to ${endpoint}`));\n\n pipeline(\n Readable.from(outstream),\n new Transform({\n objectMode: true,\n transform: (msg, _encoding, callback) =>\n callback(null, BigIntJSON.stringify(msg)),\n }),\n duplex,\n err => (err ? outstream.fail(err) : outstream.cancel()),\n );\n }\n\n if (ws.readyState === ws.CONNECTING) {\n ws.on('open', () => {\n lc.info?.(`connected to ${endpoint}`);\n streamOut();\n });\n } else {\n streamOut();\n }\n\n // Incoming transform.\n pipe({\n source: duplex,\n sink: instream,\n parse: chunk => {\n const json = BigIntJSON.parse(chunk.toString());\n return v.parse(json, inSchema, 'passthrough');\n },\n });\n\n sendPingsForLiveness(lc, ws, PING_INTERVAL_MS);\n\n return {outstream, instream};\n}\n\ntype PipeOptions<T> = {\n source: Readable;\n sink: Subscription<T>;\n parse: (buffer: Buffer) => T | null;\n bufferMessages?: number;\n};\n\nexport function pipe<T>({source, sink, parse, bufferMessages}: PipeOptions<T>) {\n bufferMessages ??= 0;\n assert(bufferMessages >= 0, 'bufferMessages must be non-negative');\n const pending: Promise<unknown>[] = [];\n\n pipeline(\n source,\n new Writable({\n decodeStrings: false,\n write: (chunk, _encoding, callback) => {\n let msg: T | null;\n try {\n if ((msg = parse(chunk)) === null) {\n callback();\n return;\n }\n } catch (err) {\n callback(ensureError(err));\n return;\n }\n // Inbound backpressure is exerted by unconsumed messages in the\n // subscription. A buffer can be used to allow messages to queue up in\n // in the Subscription object, which allows the consumer to \"peek\" at\n // whether there are more messages immediately available\n // (via {@link Subscription.queued}.\n const {result} = sink.push(msg);\n pending.push(result);\n void result.then(() => pending.shift());\n\n if (pending.length <= bufferMessages) {\n // immediately allow more messages\n callback();\n } else {\n // wait for the oldest result in the pending queue\n pending[0].then(\n () => callback(),\n err => callback(ensureError(err)),\n );\n }\n },\n destroy: (err, callback) => {\n if (err) {\n sink.fail(ensureError(err));\n }\n // Otherwise, final will handle the cancel.\n callback();\n },\n final: callback => {\n sink.cancel();\n callback();\n },\n }),\n err => (err ? sink.fail(err) : sink.cancel()),\n );\n}\n\nfunction ensureError(err: unknown) {\n return err instanceof Error ? err : new Error(String(err));\n}\n\nconst ackSchema = v.object({ack: v.number()});\n\ntype Ack = v.Infer<typeof ackSchema>;\n\n/** A parsed value paired with its approximate serialized transport size. */\nexport type Sized<T> = {\n data: T;\n size: number;\n};\n\nexport type StreamOutOptions = {\n batched?: boolean | undefined;\n maxBatchSize?: number | undefined;\n};\n\nexport type PreSerialized = {\n readonly payload: Buffer;\n readonly byteLength: number;\n};\n\nexport function isPreSerialized(val: unknown): val is PreSerialized {\n return (\n typeof val === 'object' &&\n val !== null &&\n 'payload' in val &&\n Buffer.isBuffer((val as PreSerialized).payload)\n );\n}\n\nfunction sendTextFrame(sink: WebSocket, data: Buffer | string) {\n if (typeof data === 'string') {\n sink.send(data);\n } else {\n (sink as unknown as {send: (data: unknown, opts?: unknown) => void}).send(\n data,\n {binary: false},\n );\n }\n}\n\n/**\n * Establishes a bidirectional stream over an internal zero-cache to zero-cache\n * WebSocket. Both peers call this: each may send an outbound {@link Source}\n * (`outSource`) and receives an inbound {@link Source} (the return value).\n * Transport-level `{id, msg}` / `{ack}` framing is multiplexed in both\n * directions over the single socket.\n *\n * `inSchema` validates inbound application frames; it may be omitted by a peer\n * that expects no inbound application messages (any that arrive are a protocol\n * error).\n */\nexport function streamInternal<\n In extends JSONValue,\n Out extends JSONValue = JSONValue,\n>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In> | undefined,\n outSource?: Source<Out>,\n options?: StreamOutOptions,\n): Promise<Source<In>> {\n return streamInternalCore(\n lc,\n ws,\n inSchema,\n outSource,\n BigIntJSON.stringify,\n data => data,\n options,\n );\n}\n\n/**\n * {@link streamInternal} whose outbound `Source` carries messages that are\n * already stringified JSON or pre-serialized Buffers.\n */\nexport function streamInternalStringified<In extends JSONValue>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In> | undefined,\n outSource?: Source<string | PreSerialized>,\n options?: StreamOutOptions,\n): Promise<Source<In>> {\n return streamInternalCore(\n lc,\n ws,\n inSchema,\n outSource,\n msg => (typeof msg === 'string' ? msg : msg.payload.toString('utf8')),\n data => data,\n options,\n );\n}\n\n/**\n * {@link streamInternal} that retains only the transport-frame size of each\n * inbound message. The size bounds downstream batching without keeping or\n * copying the JSON.\n */\nexport function streamInternalWithSize<\n In extends JSONValue,\n Out extends JSONValue = JSONValue,\n>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In>,\n outSource?: Source<Out>,\n options?: StreamOutOptions,\n): Promise<Source<Sized<In>>> {\n return streamInternalCore(\n lc,\n ws,\n inSchema,\n outSource,\n BigIntJSON.stringify,\n (data, _frame, _id, size) => ({data, size}),\n options,\n );\n}\n\n/**\n * Runs the outbound send loop of a {@link streamInternal} connection: reads\n * messages from `source`, frames them as `{id, msg}` / `{id, batch}` (or\n * pre-serialized Buffers), and awaits the corresponding `{ack}` frames — which\n * are demuxed off the socket by the shared message handler in\n * {@link streamInternalCore} and delivered via the `acks` queue.\n */\nasync function runOutbound<T extends JSONValue | PreSerialized>(\n lc: LogContext,\n source: Source<T>,\n sink: WebSocket,\n stringify: (payload: T) => string,\n acks: Queue<Ack>,\n closer: WebSocketCloser,\n options?: StreamOutOptions | undefined,\n): Promise<void> {\n try {\n let nextID = 0;\n const {pipeline} = source;\n const batched = options?.batched ?? false;\n const maxBatchSize = Math.max(1, Math.floor(options?.maxBatchSize ?? 64));\n\n if (batched && source.pipelineBatched) {\n const batchedIterable = source.pipelineBatched(maxBatchSize);\n if (batchedIterable) {\n lc.debug?.(\n `started batched outbound stream (maxBatchSize=${maxBatchSize})`,\n );\n for await (const {values, consumed} of batchedIterable) {\n if (values.length === 1 && isPreSerialized(values[0])) {\n const id = ++nextID;\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, values[0].payload]);\n sendTextFrame(sink, data);\n\n void (async () => {\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })().catch(e => closer.close(e));\n } else if (values.some(isPreSerialized)) {\n let remaining = values.length;\n const onConsumed = () => {\n if (--remaining === 0) {\n consumed();\n }\n };\n for (const val of values) {\n const id = ++nextID;\n if (isPreSerialized(val)) {\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, val.payload]);\n sendTextFrame(sink, data);\n } else {\n const data = `{\"id\":${id},\"msg\":${stringify(val)}}`;\n sink.send(data);\n }\n void (async () => {\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n onConsumed();\n })().catch(e => closer.close(e));\n }\n } else {\n const id = ++nextID;\n const data =\n values.length === 1\n ? `{\"id\":${id},\"msg\":${stringify(values[0])}}`\n : `{\"id\":${id},\"batch\":[${values.map(stringify).join(',')}]}`;\n sink.send(data);\n\n void (async () => {\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })().catch(e => closer.close(e));\n }\n }\n closer.close();\n return;\n }\n }\n\n if (pipeline) {\n lc.debug?.(`started pipelined outbound stream`);\n for await (const {value: msg, consumed} of pipeline) {\n const id = ++nextID;\n if (isPreSerialized(msg)) {\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, msg.payload]);\n sendTextFrame(sink, data);\n } else {\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`pipelining`, data);\n sink.send(data);\n }\n\n // The ack is awaited off the send loop so that the next message can be\n // sent without waiting for it. A bad ack is a protocol error like in\n // the synchronous path below: close the socket (which cancels the\n // source) rather than leaving the rejection unhandled.\n void (async () => {\n const {ack} = await acks.dequeue();\n // lc.debug?.(`received ack`, ack);\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })().catch(e => closer.close(e));\n }\n } else {\n lc.debug?.(`started synchronous outbound stream`);\n for await (const msg of source) {\n const id = ++nextID;\n if (isPreSerialized(msg)) {\n const prefix = Buffer.from(`{\"id\":${id}`);\n const data = Buffer.concat([prefix, msg.payload]);\n sendTextFrame(sink, data);\n } else {\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`sending`, data);\n sink.send(data);\n }\n\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n }\n }\n closer.close();\n } catch (e) {\n closer.close(e);\n }\n}\n\nasync function streamInternalCore<\n In extends JSONValue,\n Out extends JSONValue | PreSerialized,\n TIn,\n>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In> | undefined,\n outSource: Source<Out> | undefined,\n stringify: (payload: Out) => string,\n transform: (data: In, frame: string, id: number, size: number) => TIn,\n options: StreamOutOptions | undefined,\n): Promise<Source<TIn>> {\n sendPingsForLiveness(lc, ws, PING_INTERVAL_MS);\n\n // Acks received from the peer for the messages we send on `outSource`.\n const acks = new Queue<Ack>();\n\n const streamedSchema = inSchema\n ? v.object({\n id: v.number(),\n msg: inSchema.optional(),\n batch: v.array(inSchema).optional(),\n })\n : undefined;\n\n type SinkEntry = {\n consumed: () => void;\n data: TIn;\n };\n\n const sink: Subscription<TIn, SinkEntry> = new Subscription<TIn, SinkEntry>(\n {\n consumed: ({consumed}) => consumed(),\n cleanup: () => closer.close(),\n },\n ({data}) => data,\n );\n\n const closer = WebSocketCloser.forDuplex(\n lc,\n ws,\n outSource,\n sink,\n handleMessage,\n );\n\n function handleMessage(event: MessageEvent) {\n const data = event.data.toString();\n if (!sink.active) {\n lc.warn?.('dropping ws message received after close');\n return;\n }\n try {\n const value = BigIntJSON.parse(data);\n\n // Demux: an `{ack}` frame acknowledges a message we sent on `outSource`.\n const parsedAck = v.test(value, ackSchema);\n if (parsedAck.ok) {\n acks.enqueue(parsedAck.value);\n return;\n }\n\n // Otherwise it is an inbound application frame.\n if (!streamedSchema) {\n closer.close(new Error(`unexpected inbound message: ${data}`));\n return;\n }\n const parsed = v.parse(value, streamedSchema, 'passthrough');\n const {id, msg, batch} = parsed;\n\n const sendAck = () => {\n if (ws.readyState === ws.OPEN) {\n ws.send(JSON.stringify({ack: id} satisfies Ack));\n }\n };\n\n if (batch !== undefined && msg !== undefined) {\n throw new Error(`Message ${id} has both \"msg\" and \"batch\"`);\n }\n\n if (batch !== undefined) {\n let remaining = batch.length;\n if (remaining === 0) {\n sendAck();\n return;\n }\n const onConsumed = () => {\n if (--remaining === 0) {\n sendAck();\n }\n };\n const itemSize = Math.max(1, Math.round(data.length / batch.length));\n for (const item of batch) {\n sink.push({\n consumed: onConsumed,\n data: transform(item, data, id, itemSize),\n });\n }\n } else if (msg !== undefined) {\n sink.push({\n consumed: sendAck,\n data: transform(msg, data, id, data.length),\n });\n } else {\n throw new Error(`Message ${id} has neither \"msg\" nor \"batch\"`);\n }\n } catch (e) {\n closer.close(e);\n }\n }\n\n await closer.connected;\n\n // Start the outbound send loop only after the connection is open, so that a\n // client whose socket begins in CONNECTING can push before it connects.\n if (outSource) {\n void runOutbound(lc, outSource, ws, stringify, acks, closer, options);\n }\n\n return sink;\n}\n\nclass WebSocketCloser {\n readonly #lc: LogContext;\n readonly #ws: WebSocket;\n readonly #closeStream: (err?: unknown) => void;\n readonly #messageHandler: ((e: MessageEvent) => void | undefined) | null;\n readonly #connected = resolver();\n\n get connected(): Promise<void> {\n return this.#connected.promise;\n }\n\n /**\n * Closer for a {@link streamInternalCore} duplex. On socket close it cancels\n * the outbound `Source` (notifying it of unconsumed messages, as the old\n * `forSource` did) and fails/ends the inbound `Sink` (as the old `forSink`\n * did): fail() on error so consumers throw, end() otherwise so pending\n * messages finish. Either stream may be absent for a one-directional peer.\n */\n static forDuplex<In, InInput, Out>(\n lc: LogContext,\n ws: WebSocket,\n outSource: Source<Out> | undefined,\n inSink: Subscription<In, InInput>,\n messageHandler: (e: MessageEvent) => void | undefined,\n ) {\n return new WebSocketCloser(\n lc,\n ws,\n (err?: unknown) => {\n outSource?.cancel(err instanceof Error ? err : undefined);\n if (err) {\n inSink.fail(err instanceof Error ? err : new Error(String(err)));\n } else {\n inSink.end();\n }\n },\n messageHandler,\n );\n }\n\n private constructor(\n lc: LogContext,\n ws: WebSocket,\n closeStream: (err?: unknown) => void,\n messageHandler?: (e: MessageEvent) => void | undefined,\n ) {\n this.#lc = lc;\n this.#ws = ws;\n this.#closeStream = closeStream;\n this.#messageHandler = messageHandler ?? null;\n\n ws.addEventListener('open', this.#handleOpen);\n ws.addEventListener('close', this.#handleClose);\n ws.addEventListener('error', this.#handleError);\n if (this.#messageHandler) {\n ws.addEventListener('message', this.#messageHandler);\n }\n\n switch (ws.readyState) {\n case ws.CONNECTING:\n break; // expected for new connections. resolve or reject in handlers.\n case ws.OPEN:\n this.#connected.resolve();\n break;\n default:\n this.#connected.reject(\n new Error(`websocket already in state ${ws.readyState}`),\n );\n break;\n }\n }\n\n get #conn(): string {\n return 'connection' + (this.#ws.url ? ` to ${this.#ws.url}` : '');\n }\n\n #handleOpen = () => {\n this.#lc.info?.(`${this.#conn} established`);\n this.#connected.resolve();\n };\n\n #handleClose = (e: CloseEvent) => {\n const {code, reason, wasClean} = e;\n this.#lc.info?.(`${this.#conn} closed`, {\n code,\n reason,\n wasClean,\n });\n this.close();\n this.#connected.reject(`${this.#conn} closed with code ${code}`);\n };\n\n #handleError = ({message, error}: ErrorEvent) => {\n if (this.#ws.readyState === this.#ws.OPEN) {\n this.#lc.error?.(`error in ${this.#conn}`, message, error);\n }\n this.#connected.reject(error);\n };\n\n close(err?: unknown) {\n if (err) {\n this.#lc.error?.(`closing stream with error`, err);\n }\n this.#closeStream(err);\n if (!this.closed()) {\n this.#ws.close();\n }\n }\n\n closed() {\n return (\n this.#ws.readyState === this.#ws.CLOSED ||\n this.#ws.readyState === this.#ws.CLOSING\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assert } from "../../../shared/src/asserts.js";
|
|
2
|
+
import { resolver } from "../../../shared/src/resolver.js";
|
|
2
3
|
import { RingBuffer } from "../../../shared/src/ring-buffer.js";
|
|
3
|
-
import { resolver } from "@rocicorp/resolver";
|
|
4
4
|
//#region ../zero-cache/src/types/subscription.ts
|
|
5
5
|
/**
|
|
6
6
|
* A Subscription abstracts a continuous, logically infinite stream of messages intended
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.js","names":[],"sources":["../../../../../zero-cache/src/types/subscription.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {RingBuffer} from '../../../shared/src/ring-buffer.ts';\nimport type {Sink, Source} from './streams.ts';\n\n/**\n * A Subscription abstracts a continuous, logically infinite stream of messages intended\n * for serial processing. Unlike the more general Node `Stream` API, a Subscription has\n * a limited API with specific semantics:\n *\n * * **Serial processing**: Messages must be consumed via the {@link AsyncIterable}\n * interface, e.g.\n * ```ts\n * const subscription = server.subscribe(parameters);\n *\n * for await (const message of subscription) {\n * await process(message); // fully process the message before consuming the next\n * }\n * ```\n *\n * Moreover, the consumer is expected to completely process each message before\n * requesting the next. This is important for cleanup semantics (explained later).\n *\n * * **cancel()**, not close(): The underlying data in a subscription is logically infinite\n * and only terminated when the consumer is no longer interested in receiving the messages\n * (or requires a Subscription with a different configuration). As such, there is no API\n * for gracefully closing the subscription after pending messages are consumed; rather,\n * cancellation is immediate, and upon cancellation, pending messages are dropped. A\n * Subscription can also be terminated with exceptional (i.e. `Error`) circumstances,\n * for which the behavior is equivalent.\n *\n * * **Coalescing** (optional): A producer can configure pending messages in the Subscription\n * to be merged together with a {@link Options.coalesce coalesce} function. This is useful\n * for semantics in which the consumer is not necessarily interested in every incremental\n * change, but rather the cumulative change since the last processed message. A\n * Subscription with coalescing is guaranteed to have at most one outstanding message,\n * regardless of how quickly messages are produced and consumed. This effectively constrains\n * the amount of outstanding work in the system.\n *\n * ### Resource Tracking and Cleanup\n *\n * Because message consumption is constrained to the async iteration API, standard\n * control flow mechanisms allow the producer to perform bookkeeping without any\n * explicit cleanup actions on the part of the consumer. This includes:\n *\n * * **Per-message cleanup**: Each request for the {@link AsyncIterator.next next}\n * message, or the termination of the iteration, signals that the consumer has\n * finished processing the previous message. The producer of a Subscription can\n * supply a {@link Options.consumed consumed} callback to receive these processed\n * messages, allowing it to clean up attached resources (e.g. TransactionPools, etc.).\n *\n * * **Per-subscription cleanup**: The producer of a Subscription can supply a\n * {@link Options.cleanup cleanup} callback that is invoked when the Subscription\n * is terminated, either explicitly via {@link Subscription.cancel cancel()} /\n * {@link Subscription.fail fail()}, or implicitly when an iteration is exited via a\n * `break`, `return`, or `throw` statement. All unconsumed messages are passed to the\n * call back to facilitate bookkeeping.\n *\n * @param T The external message type, published to the AsyncIterable\n * @param M The internal message type used in the producer-side interfaces\n * (e.g. {@link push}, {@link Options.consumed}, {@link Options.coalesce},\n * and {@link Options.cleanup}). This is often the same as the external type\n * T, but may be diverged to facilitate internal bookkeeping.\n */\nexport class Subscription<T, M = T> implements Source<T>, Sink<M> {\n /**\n * Convenience factory method for creating a {@link Subscription} with internal message type\n * `M` as a subtype of `T`, defaulting to the same type. The default `publish` method publishes\n * the message of type `M` directly to the AsyncIterable.\n */\n static create<T, M extends T = T>(\n options: Options<M> = {},\n publish: (m: M) => T = m => m,\n ) {\n return new Subscription(options, publish);\n }\n\n readonly #abortController = new AbortController();\n // Consumers waiting to consume messages (i.e. an async iteration awaiting the next message).\n readonly #consumers = new RingBuffer<Resolver<Entry<M> | null>>();\n // Messages waiting to be dequeued.\n readonly #messages = new RingBuffer<Entry<M> | 'terminus'>();\n // Messages dequeued but not yet consumed.\n readonly #consuming = new Set<Entry<M>>();\n readonly #pipelineEnabled: boolean;\n // Sentinel value signaling that the subscription is \"done\" and no more\n // messages can be added.\n #sentinel: 'canceled' | Error | undefined = undefined;\n\n readonly #coalesceFunc: ((curr: M, prev: M) => M) | undefined;\n readonly #consumedFunc: (prev: M) => void;\n readonly #cleanupFunc: (unconsumed: M[], err?: Error) => void;\n\n #publish: (internal: M) => T;\n\n /**\n * @param publish function for converting the internally pushed / coalesced message\n * of type `M` to the external type `T` exposed via async iteration.\n */\n constructor(options: Options<M> = {}, publish: (m: M) => T) {\n const {\n coalesce,\n consumed = () => {},\n cleanup = () => {},\n pipeline = coalesce === undefined,\n } = options;\n\n this.#coalesceFunc = coalesce;\n this.#consumedFunc = consumed;\n this.#cleanupFunc = cleanup;\n this.#publish = publish;\n this.#pipelineEnabled = pipeline;\n }\n\n #coalesce(curr: Entry<M>, prev: Entry<M>): M {\n assert(this.#coalesceFunc, 'expected a coalesce function');\n try {\n return this.#coalesceFunc(curr.value, prev.value);\n } finally {\n prev.resolve('coalesced');\n }\n }\n\n #consumed(prev: Entry<M>): void {\n this.#consumedFunc(prev.value);\n this.#consuming.delete(prev);\n prev.resolve('consumed');\n }\n\n #cleanup(entries: Entry<M>[], err?: Error) {\n this.#cleanupFunc(\n entries.map(e => e.value),\n err,\n );\n entries.forEach(e => e.resolve('unconsumed'));\n }\n\n /**\n * Dynamically adds a function that is called when the Subscription\n * terminates. This will be called immediately if the Subscription is\n * already closed.\n *\n * Unlike the `cleanup` handler specified in the Subscription options,\n * this a close handler can be added to an existing Subscription object\n * thereby avoiding a cyclic dependency, and allowing Subscription\n * receivers to add their own cleanup logic.\n */\n addCloseHandler(onClose: (reason?: unknown) => void): this {\n if (this.signal.aborted) {\n onClose(this.signal.reason);\n } else {\n this.signal.addEventListener('abort', () => onClose(this.signal.reason));\n }\n return this;\n }\n\n /**\n * Pushes the next message to be consumed, and returns a `result` that resolves to the\n * eventual {@link Result} of the `value`.\n *\n * If there is an existing unconsumed message and the Subscription has a\n * {@link Options#coalesce coalesce} function, the specified `value` will be coalesced\n * with the pending message. In this case, the result of the pending message\n * is resolved to `coalesced`, regardless of the `coalesce` function implementation.\n *\n * If the subscription is in a terminal state, the message is dropped and the\n * result resolves to `unconsumed`.\n */\n push(value: M): PendingResult {\n const {promise: result, resolve} = resolver<Result>();\n const entry = {value, resolve};\n\n if (this.#sentinel) {\n entry.resolve('unconsumed');\n return {result};\n }\n const consumer = this.#consumers.shift();\n if (consumer) {\n consumer.resolve(entry);\n } else if (\n this.#coalesceFunc &&\n this.#messages.size &&\n this.#messages.last() !== 'terminus'\n ) {\n const prev = this.#messages.last();\n assert(prev !== undefined && prev !== 'terminus', 'expected an entry');\n this.#messages.replaceLast({\n value: this.#coalesce(entry, prev),\n resolve,\n });\n } else {\n this.#messages.push(entry);\n }\n return {result};\n }\n\n /** False if the subscription has been canceled or has failed. */\n get active(): boolean {\n return this.#sentinel === undefined;\n }\n\n /** The number of messages waiting to be dequeued. */\n get queued(): number {\n return this.#messages.size;\n }\n\n /** The number of messages dequeued but not yet \"consumed\" */\n get consuming(): number {\n return this.#consuming.size;\n }\n\n /** Satisfies the Source interface. */\n get signal() {\n return this.#abortController.signal;\n }\n\n /**\n * Cancels the subscription after any queued messages are consumed. This is\n * meant for the producer-side code.\n *\n * Any messages pushed after calling `end()` will be unconsumed as if\n * `cancel()` were called (once the first set of pending messages is\n * consumed). In particular, if a coalesce function is defined, the new\n * messages will not be coalesced with the messages enqueued before `end()`\n * was called. However, to effect the intent of memory efficiency, multiple\n * messages pushed after calling `end()` will be coalesced together.\n *\n */\n end() {\n if (this.#sentinel) {\n // already terminated\n } else if (this.#messages.size === 0) {\n this.cancel();\n } else {\n this.#messages.push('terminus');\n }\n }\n\n /**\n * Cancels the subscription immediately, cleans up, and terminates any iteration.\n * This is intended for the consumer to call when it is no longer interested\n * in the subscription.\n *\n * @param err If an `err` is specified, an iteration over the Subscription /\n * Sink will throw the `err` (equivalent to the producer calling\n * {@link fail()}). If undefined, the iteration will exit gracefully.\n */\n cancel(err?: Error) {\n this.#terminate(err ?? 'canceled');\n }\n\n /** Fails the subscription, cleans up, and throws from any iteration. */\n fail(err: Error) {\n this.#terminate(err);\n }\n\n #terminate(sentinel: 'canceled' | Error) {\n if (!this.#sentinel) {\n this.#sentinel = sentinel;\n this.#cleanup(\n [\n ...this.#consuming,\n ...this.#messages.toArray().filter(m => m !== 'terminus'),\n ],\n sentinel instanceof Error ? sentinel : undefined,\n );\n this.#messages.clear();\n\n for (\n let consumer = this.#consumers.shift();\n consumer;\n consumer = this.#consumers.shift()\n ) {\n if (sentinel === 'canceled') {\n consumer.resolve(null);\n } else {\n consumer.reject(sentinel);\n }\n }\n this.#abortController.abort(sentinel);\n }\n }\n\n get pipeline(): AsyncIterable<{value: T; consumed: () => void}> | undefined {\n return this.#pipelineEnabled\n ? {[Symbol.asyncIterator]: () => this.#pipeline()}\n : undefined;\n }\n\n pipelineBatched = (\n maxBatch = 64,\n ): AsyncIterable<{values: T[]; consumed: () => void}> | undefined => {\n assert(\n Number.isInteger(maxBatch) && maxBatch > 0,\n () => `maxBatch must be a positive integer, got: ${maxBatch}`,\n );\n return this.#pipelineEnabled\n ? {[Symbol.asyncIterator]: () => this.#pipelineBatched(maxBatch)}\n : undefined;\n };\n\n #pipelineBatched(\n maxBatch: number,\n ): AsyncIterator<{values: T[]; consumed: () => void}> {\n return {\n next: async () => {\n const entries: Entry<M>[] = [];\n\n while (this.#messages.size > 0 && entries.length < maxBatch) {\n const head = this.#messages.first();\n if (head === 'terminus') {\n if (entries.length > 0) {\n break;\n }\n this.#messages.shift();\n this.cancel();\n return {value: undefined, done: true};\n }\n const entry = this.#messages.shift() as Entry<M>;\n entries.push(entry);\n }\n\n if (entries.length > 0) {\n for (const e of entries) {\n this.#consuming.add(e);\n }\n return {\n value: {\n values: entries.map(e => this.#publish(e.value)),\n consumed: () => {\n for (const e of entries) {\n this.#consumed(e);\n }\n },\n },\n done: false,\n };\n }\n\n if (this.#sentinel === 'canceled') {\n return {value: undefined, done: true};\n }\n if (this.#sentinel) {\n return Promise.reject(this.#sentinel);\n }\n\n const consumer = resolver<Entry<M> | null>();\n this.#consumers.push(consumer);\n\n const result = await consumer.promise;\n if (result === null) {\n return {value: undefined, done: true};\n }\n\n entries.push(result);\n\n while (this.#messages.size > 0 && entries.length < maxBatch) {\n if (this.#messages.first() === 'terminus') {\n break;\n }\n const entry = this.#messages.shift() as Entry<M>;\n entries.push(entry);\n }\n\n for (const e of entries) {\n this.#consuming.add(e);\n }\n\n return {\n value: {\n values: entries.map(e => this.#publish(e.value)),\n consumed: () => {\n for (const e of entries) {\n this.#consumed(e);\n }\n },\n },\n done: false,\n };\n },\n\n return: value => {\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n\n #pipeline(): AsyncIterator<{value: T; consumed: () => void}> {\n return {\n next: async () => {\n const entry = this.#messages.shift();\n if (entry === 'terminus') {\n this.cancel();\n return {value: undefined, done: true};\n }\n if (entry !== undefined) {\n this.#consuming.add(entry);\n return {\n value: {\n value: this.#publish(entry.value),\n consumed: () => this.#consumed(entry),\n },\n };\n }\n if (this.#sentinel === 'canceled') {\n return {value: undefined, done: true};\n }\n if (this.#sentinel) {\n return Promise.reject(this.#sentinel);\n }\n const consumer = resolver<Entry<M> | null>();\n this.#consumers.push(consumer);\n\n // Wait for push() (or termination) to resolve the consumer.\n const result = await consumer.promise;\n if (result !== null) {\n this.#consuming.add(result);\n return {\n value: {\n value: this.#publish(result.value),\n consumed: () => this.#consumed(result),\n },\n };\n }\n return {value: undefined, done: true};\n },\n\n return: value => {\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const delegate = this.#pipeline();\n\n let prevConsumed = () => {};\n return {\n next: async () => {\n prevConsumed();\n\n const entry = await delegate.next();\n if (entry.done) {\n return entry;\n }\n\n const {value, consumed} = entry.value;\n prevConsumed = consumed;\n return {value};\n },\n\n return: value => {\n prevConsumed();\n\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n}\n\nexport type Options<M> = {\n /**\n * Coalesces messages waiting to be consumed. This is useful for \"watermark\" type\n * subscriptions in which the consumer is only interested in the cumulative state\n * change since the last processed message. When a `coalesce` function is specified,\n * there is guaranteed to be at most one message waiting to be consumed.\n *\n * Note that the `curr` argument comes before `prev`. This facilitates a common\n * scenario in which coalescing just means using the newest value; in such a case,\n * `coalesce` can simply be the identity function (e.g. `msg => msg`).\n */\n coalesce?: (curr: M, prev: M) => M;\n\n /**\n * Called on the previous message in an iteration (1) when the next message is requested,\n * or (2) when the iteration is terminated. This allows the producer to perform\n * per-message cleanup.\n *\n * Note that when a {@link Options.coalesce coalesce} function is defined,\n * `consumed` is _not_ called on the `prev` message; it is the responsibility of\n * producers requiring both coalescing and consumption notification to perform any\n * necessary cleanup of `prev` messages when coalescing.\n */\n consumed?: (prev: M) => void;\n\n /**\n * `cleanup` is called exactly once when the subscription is terminated via a failure or\n * cancelation (whichever happens first), which includes implicit cancelation when\n * the consumer exits an iteration via a `break`, `return`, or `throw` statement.\n *\n * Note that the `err` argument will only reflect an explicit cancelation via a call\n * to {@link Subscription.fail()}. On the other hand, if the iteration is canceled via\n * a `throw` statement, the thrown reason is not reflected in the `err` parameter, as that\n * information is not made available to the AsyncIterator implementation.\n */\n cleanup?: (unconsumed: M[], err?: Error) => void;\n\n /**\n * Enable or disable pipelining when streaming messages over a websocket.\n *\n * If unspecified, pipelining is enabled if there is no {@link Options.coalesce coalesce}\n * method, as pipelining is counter to the semantics of coalescing. However, the\n * application can explicitly enable pipelining even if there is a coalesce method\n * by specifying `true` for this option. This assumes that coalescing is either\n * not important for websocket semantics, or that the receiving end of the websocket\n * transport performs the desired coalescing.\n */\n pipeline?: boolean;\n};\n\n/** Post-queueing results of messages. */\nexport type Result = 'consumed' | 'coalesced' | 'unconsumed';\n\n/**\n * {@link Subscription.subscribe()} wraps the `Promise<Result>` in a `PendingResult`\n * object to avoid forcing all callers to handle the Promise, as most logic does not\n * need to.\n */\nexport type PendingResult = {result: Promise<Result>};\n\ntype Entry<M> = {\n readonly value: M;\n readonly resolve: (r: Result) => void;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"subscription.js","names":[],"sources":["../../../../../zero-cache/src/types/subscription.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {resolver, type Resolver} from '../../../shared/src/resolver.ts';\nimport {RingBuffer} from '../../../shared/src/ring-buffer.ts';\nimport type {Sink, Source} from './streams.ts';\n\n/**\n * A Subscription abstracts a continuous, logically infinite stream of messages intended\n * for serial processing. Unlike the more general Node `Stream` API, a Subscription has\n * a limited API with specific semantics:\n *\n * * **Serial processing**: Messages must be consumed via the {@link AsyncIterable}\n * interface, e.g.\n * ```ts\n * const subscription = server.subscribe(parameters);\n *\n * for await (const message of subscription) {\n * await process(message); // fully process the message before consuming the next\n * }\n * ```\n *\n * Moreover, the consumer is expected to completely process each message before\n * requesting the next. This is important for cleanup semantics (explained later).\n *\n * * **cancel()**, not close(): The underlying data in a subscription is logically infinite\n * and only terminated when the consumer is no longer interested in receiving the messages\n * (or requires a Subscription with a different configuration). As such, there is no API\n * for gracefully closing the subscription after pending messages are consumed; rather,\n * cancellation is immediate, and upon cancellation, pending messages are dropped. A\n * Subscription can also be terminated with exceptional (i.e. `Error`) circumstances,\n * for which the behavior is equivalent.\n *\n * * **Coalescing** (optional): A producer can configure pending messages in the Subscription\n * to be merged together with a {@link Options.coalesce coalesce} function. This is useful\n * for semantics in which the consumer is not necessarily interested in every incremental\n * change, but rather the cumulative change since the last processed message. A\n * Subscription with coalescing is guaranteed to have at most one outstanding message,\n * regardless of how quickly messages are produced and consumed. This effectively constrains\n * the amount of outstanding work in the system.\n *\n * ### Resource Tracking and Cleanup\n *\n * Because message consumption is constrained to the async iteration API, standard\n * control flow mechanisms allow the producer to perform bookkeeping without any\n * explicit cleanup actions on the part of the consumer. This includes:\n *\n * * **Per-message cleanup**: Each request for the {@link AsyncIterator.next next}\n * message, or the termination of the iteration, signals that the consumer has\n * finished processing the previous message. The producer of a Subscription can\n * supply a {@link Options.consumed consumed} callback to receive these processed\n * messages, allowing it to clean up attached resources (e.g. TransactionPools, etc.).\n *\n * * **Per-subscription cleanup**: The producer of a Subscription can supply a\n * {@link Options.cleanup cleanup} callback that is invoked when the Subscription\n * is terminated, either explicitly via {@link Subscription.cancel cancel()} /\n * {@link Subscription.fail fail()}, or implicitly when an iteration is exited via a\n * `break`, `return`, or `throw` statement. All unconsumed messages are passed to the\n * call back to facilitate bookkeeping.\n *\n * @param T The external message type, published to the AsyncIterable\n * @param M The internal message type used in the producer-side interfaces\n * (e.g. {@link push}, {@link Options.consumed}, {@link Options.coalesce},\n * and {@link Options.cleanup}). This is often the same as the external type\n * T, but may be diverged to facilitate internal bookkeeping.\n */\nexport class Subscription<T, M = T> implements Source<T>, Sink<M> {\n /**\n * Convenience factory method for creating a {@link Subscription} with internal message type\n * `M` as a subtype of `T`, defaulting to the same type. The default `publish` method publishes\n * the message of type `M` directly to the AsyncIterable.\n */\n static create<T, M extends T = T>(\n options: Options<M> = {},\n publish: (m: M) => T = m => m,\n ) {\n return new Subscription(options, publish);\n }\n\n readonly #abortController = new AbortController();\n // Consumers waiting to consume messages (i.e. an async iteration awaiting the next message).\n readonly #consumers = new RingBuffer<Resolver<Entry<M> | null>>();\n // Messages waiting to be dequeued.\n readonly #messages = new RingBuffer<Entry<M> | 'terminus'>();\n // Messages dequeued but not yet consumed.\n readonly #consuming = new Set<Entry<M>>();\n readonly #pipelineEnabled: boolean;\n // Sentinel value signaling that the subscription is \"done\" and no more\n // messages can be added.\n #sentinel: 'canceled' | Error | undefined = undefined;\n\n readonly #coalesceFunc: ((curr: M, prev: M) => M) | undefined;\n readonly #consumedFunc: (prev: M) => void;\n readonly #cleanupFunc: (unconsumed: M[], err?: Error) => void;\n\n #publish: (internal: M) => T;\n\n /**\n * @param publish function for converting the internally pushed / coalesced message\n * of type `M` to the external type `T` exposed via async iteration.\n */\n constructor(options: Options<M> = {}, publish: (m: M) => T) {\n const {\n coalesce,\n consumed = () => {},\n cleanup = () => {},\n pipeline = coalesce === undefined,\n } = options;\n\n this.#coalesceFunc = coalesce;\n this.#consumedFunc = consumed;\n this.#cleanupFunc = cleanup;\n this.#publish = publish;\n this.#pipelineEnabled = pipeline;\n }\n\n #coalesce(curr: Entry<M>, prev: Entry<M>): M {\n assert(this.#coalesceFunc, 'expected a coalesce function');\n try {\n return this.#coalesceFunc(curr.value, prev.value);\n } finally {\n prev.resolve('coalesced');\n }\n }\n\n #consumed(prev: Entry<M>): void {\n this.#consumedFunc(prev.value);\n this.#consuming.delete(prev);\n prev.resolve('consumed');\n }\n\n #cleanup(entries: Entry<M>[], err?: Error) {\n this.#cleanupFunc(\n entries.map(e => e.value),\n err,\n );\n entries.forEach(e => e.resolve('unconsumed'));\n }\n\n /**\n * Dynamically adds a function that is called when the Subscription\n * terminates. This will be called immediately if the Subscription is\n * already closed.\n *\n * Unlike the `cleanup` handler specified in the Subscription options,\n * this a close handler can be added to an existing Subscription object\n * thereby avoiding a cyclic dependency, and allowing Subscription\n * receivers to add their own cleanup logic.\n */\n addCloseHandler(onClose: (reason?: unknown) => void): this {\n if (this.signal.aborted) {\n onClose(this.signal.reason);\n } else {\n this.signal.addEventListener('abort', () => onClose(this.signal.reason));\n }\n return this;\n }\n\n /**\n * Pushes the next message to be consumed, and returns a `result` that resolves to the\n * eventual {@link Result} of the `value`.\n *\n * If there is an existing unconsumed message and the Subscription has a\n * {@link Options#coalesce coalesce} function, the specified `value` will be coalesced\n * with the pending message. In this case, the result of the pending message\n * is resolved to `coalesced`, regardless of the `coalesce` function implementation.\n *\n * If the subscription is in a terminal state, the message is dropped and the\n * result resolves to `unconsumed`.\n */\n push(value: M): PendingResult {\n const {promise: result, resolve} = resolver<Result>();\n const entry = {value, resolve};\n\n if (this.#sentinel) {\n entry.resolve('unconsumed');\n return {result};\n }\n const consumer = this.#consumers.shift();\n if (consumer) {\n consumer.resolve(entry);\n } else if (\n this.#coalesceFunc &&\n this.#messages.size &&\n this.#messages.last() !== 'terminus'\n ) {\n const prev = this.#messages.last();\n assert(prev !== undefined && prev !== 'terminus', 'expected an entry');\n this.#messages.replaceLast({\n value: this.#coalesce(entry, prev),\n resolve,\n });\n } else {\n this.#messages.push(entry);\n }\n return {result};\n }\n\n /** False if the subscription has been canceled or has failed. */\n get active(): boolean {\n return this.#sentinel === undefined;\n }\n\n /** The number of messages waiting to be dequeued. */\n get queued(): number {\n return this.#messages.size;\n }\n\n /** The number of messages dequeued but not yet \"consumed\" */\n get consuming(): number {\n return this.#consuming.size;\n }\n\n /** Satisfies the Source interface. */\n get signal() {\n return this.#abortController.signal;\n }\n\n /**\n * Cancels the subscription after any queued messages are consumed. This is\n * meant for the producer-side code.\n *\n * Any messages pushed after calling `end()` will be unconsumed as if\n * `cancel()` were called (once the first set of pending messages is\n * consumed). In particular, if a coalesce function is defined, the new\n * messages will not be coalesced with the messages enqueued before `end()`\n * was called. However, to effect the intent of memory efficiency, multiple\n * messages pushed after calling `end()` will be coalesced together.\n *\n */\n end() {\n if (this.#sentinel) {\n // already terminated\n } else if (this.#messages.size === 0) {\n this.cancel();\n } else {\n this.#messages.push('terminus');\n }\n }\n\n /**\n * Cancels the subscription immediately, cleans up, and terminates any iteration.\n * This is intended for the consumer to call when it is no longer interested\n * in the subscription.\n *\n * @param err If an `err` is specified, an iteration over the Subscription /\n * Sink will throw the `err` (equivalent to the producer calling\n * {@link fail()}). If undefined, the iteration will exit gracefully.\n */\n cancel(err?: Error) {\n this.#terminate(err ?? 'canceled');\n }\n\n /** Fails the subscription, cleans up, and throws from any iteration. */\n fail(err: Error) {\n this.#terminate(err);\n }\n\n #terminate(sentinel: 'canceled' | Error) {\n if (!this.#sentinel) {\n this.#sentinel = sentinel;\n this.#cleanup(\n [\n ...this.#consuming,\n ...this.#messages.toArray().filter(m => m !== 'terminus'),\n ],\n sentinel instanceof Error ? sentinel : undefined,\n );\n this.#messages.clear();\n\n for (\n let consumer = this.#consumers.shift();\n consumer;\n consumer = this.#consumers.shift()\n ) {\n if (sentinel === 'canceled') {\n consumer.resolve(null);\n } else {\n consumer.reject(sentinel);\n }\n }\n this.#abortController.abort(sentinel);\n }\n }\n\n get pipeline(): AsyncIterable<{value: T; consumed: () => void}> | undefined {\n return this.#pipelineEnabled\n ? {[Symbol.asyncIterator]: () => this.#pipeline()}\n : undefined;\n }\n\n pipelineBatched = (\n maxBatch = 64,\n ): AsyncIterable<{values: T[]; consumed: () => void}> | undefined => {\n assert(\n Number.isInteger(maxBatch) && maxBatch > 0,\n () => `maxBatch must be a positive integer, got: ${maxBatch}`,\n );\n return this.#pipelineEnabled\n ? {[Symbol.asyncIterator]: () => this.#pipelineBatched(maxBatch)}\n : undefined;\n };\n\n #pipelineBatched(\n maxBatch: number,\n ): AsyncIterator<{values: T[]; consumed: () => void}> {\n return {\n next: async () => {\n const entries: Entry<M>[] = [];\n\n while (this.#messages.size > 0 && entries.length < maxBatch) {\n const head = this.#messages.first();\n if (head === 'terminus') {\n if (entries.length > 0) {\n break;\n }\n this.#messages.shift();\n this.cancel();\n return {value: undefined, done: true};\n }\n const entry = this.#messages.shift() as Entry<M>;\n entries.push(entry);\n }\n\n if (entries.length > 0) {\n for (const e of entries) {\n this.#consuming.add(e);\n }\n return {\n value: {\n values: entries.map(e => this.#publish(e.value)),\n consumed: () => {\n for (const e of entries) {\n this.#consumed(e);\n }\n },\n },\n done: false,\n };\n }\n\n if (this.#sentinel === 'canceled') {\n return {value: undefined, done: true};\n }\n if (this.#sentinel) {\n return Promise.reject(this.#sentinel);\n }\n\n const consumer = resolver<Entry<M> | null>();\n this.#consumers.push(consumer);\n\n const result = await consumer.promise;\n if (result === null) {\n return {value: undefined, done: true};\n }\n\n entries.push(result);\n\n while (this.#messages.size > 0 && entries.length < maxBatch) {\n if (this.#messages.first() === 'terminus') {\n break;\n }\n const entry = this.#messages.shift() as Entry<M>;\n entries.push(entry);\n }\n\n for (const e of entries) {\n this.#consuming.add(e);\n }\n\n return {\n value: {\n values: entries.map(e => this.#publish(e.value)),\n consumed: () => {\n for (const e of entries) {\n this.#consumed(e);\n }\n },\n },\n done: false,\n };\n },\n\n return: value => {\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n\n #pipeline(): AsyncIterator<{value: T; consumed: () => void}> {\n return {\n next: async () => {\n const entry = this.#messages.shift();\n if (entry === 'terminus') {\n this.cancel();\n return {value: undefined, done: true};\n }\n if (entry !== undefined) {\n this.#consuming.add(entry);\n return {\n value: {\n value: this.#publish(entry.value),\n consumed: () => this.#consumed(entry),\n },\n };\n }\n if (this.#sentinel === 'canceled') {\n return {value: undefined, done: true};\n }\n if (this.#sentinel) {\n return Promise.reject(this.#sentinel);\n }\n const consumer = resolver<Entry<M> | null>();\n this.#consumers.push(consumer);\n\n // Wait for push() (or termination) to resolve the consumer.\n const result = await consumer.promise;\n if (result !== null) {\n this.#consuming.add(result);\n return {\n value: {\n value: this.#publish(result.value),\n consumed: () => this.#consumed(result),\n },\n };\n }\n return {value: undefined, done: true};\n },\n\n return: value => {\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const delegate = this.#pipeline();\n\n let prevConsumed = () => {};\n return {\n next: async () => {\n prevConsumed();\n\n const entry = await delegate.next();\n if (entry.done) {\n return entry;\n }\n\n const {value, consumed} = entry.value;\n prevConsumed = consumed;\n return {value};\n },\n\n return: value => {\n prevConsumed();\n\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n}\n\nexport type Options<M> = {\n /**\n * Coalesces messages waiting to be consumed. This is useful for \"watermark\" type\n * subscriptions in which the consumer is only interested in the cumulative state\n * change since the last processed message. When a `coalesce` function is specified,\n * there is guaranteed to be at most one message waiting to be consumed.\n *\n * Note that the `curr` argument comes before `prev`. This facilitates a common\n * scenario in which coalescing just means using the newest value; in such a case,\n * `coalesce` can simply be the identity function (e.g. `msg => msg`).\n */\n coalesce?: (curr: M, prev: M) => M;\n\n /**\n * Called on the previous message in an iteration (1) when the next message is requested,\n * or (2) when the iteration is terminated. This allows the producer to perform\n * per-message cleanup.\n *\n * Note that when a {@link Options.coalesce coalesce} function is defined,\n * `consumed` is _not_ called on the `prev` message; it is the responsibility of\n * producers requiring both coalescing and consumption notification to perform any\n * necessary cleanup of `prev` messages when coalescing.\n */\n consumed?: (prev: M) => void;\n\n /**\n * `cleanup` is called exactly once when the subscription is terminated via a failure or\n * cancelation (whichever happens first), which includes implicit cancelation when\n * the consumer exits an iteration via a `break`, `return`, or `throw` statement.\n *\n * Note that the `err` argument will only reflect an explicit cancelation via a call\n * to {@link Subscription.fail()}. On the other hand, if the iteration is canceled via\n * a `throw` statement, the thrown reason is not reflected in the `err` parameter, as that\n * information is not made available to the AsyncIterator implementation.\n */\n cleanup?: (unconsumed: M[], err?: Error) => void;\n\n /**\n * Enable or disable pipelining when streaming messages over a websocket.\n *\n * If unspecified, pipelining is enabled if there is no {@link Options.coalesce coalesce}\n * method, as pipelining is counter to the semantics of coalescing. However, the\n * application can explicitly enable pipelining even if there is a coalesce method\n * by specifying `true` for this option. This assumes that coalescing is either\n * not important for websocket semantics, or that the receiving end of the websocket\n * transport performs the desired coalescing.\n */\n pipeline?: boolean;\n};\n\n/** Post-queueing results of messages. */\nexport type Result = 'consumed' | 'coalesced' | 'unconsumed';\n\n/**\n * {@link Subscription.subscribe()} wraps the `Promise<Result>` in a `PendingResult`\n * object to avoid forcing all callers to handle the Promise, as most logic does not\n * need to.\n */\nexport type PendingResult = {result: Promise<Result>};\n\ntype Entry<M> = {\n readonly value: M;\n readonly resolve: (r: Result) => void;\n};\n"],"mappings":"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"}
|
|
@@ -20,12 +20,6 @@ export type HandoffSpec<P> = {
|
|
|
20
20
|
export type WebSocketHandoff<P> = (message: IncomingMessageSubset, callback: (h: HandoffSpec<P>) => void, onerror: (reason: unknown) => void) => HandoffSpec<P> | void;
|
|
21
21
|
export type WebSocketReceiver<P> = (ws: WebSocket, payload: P, msg: IncomingMessageSubset) => void;
|
|
22
22
|
export type WebSocketHandoffHandler = (message: IncomingMessageSubset, socket: Socket, head: ArrayBuffer) => void;
|
|
23
|
-
/**
|
|
24
|
-
* Installs websocket handoff logic from either an http.Server
|
|
25
|
-
* receiving requests, or a parent Worker process
|
|
26
|
-
* that is handing off requests to this process.
|
|
27
|
-
*/
|
|
28
|
-
export declare function createWebSocketHandoffHandler<P>(lc: LogContext, handoff: WebSocketHandoff<P>, serverOptions?: ServerOptions): WebSocketHandoffHandler;
|
|
29
23
|
/**
|
|
30
24
|
* Installs websocket handoff logic from either an http.Server
|
|
31
25
|
* receiving requests, or a parent Worker process
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-handoff.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/websocket-handoff.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAC,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAC,MAAM,IAAI,CAAC;AAEvE,OAAO,EAAqB,KAAK,qBAAqB,EAAC,MAAM,WAAW,CAAC;AACzE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAC,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAC,MAAM,gBAAgB,CAAC;AAGvE,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAChC,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,EACrC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,KAC/B,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AAE3B,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CACjC,EAAE,EAAE,SAAS,EACb,OAAO,EAAE,CAAC,EACV,GAAG,EAAE,qBAAqB,KACvB,IAAI,CAAC;AAEV,MAAM,MAAM,uBAAuB,GAAG,CACpC,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,KACd,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"websocket-handoff.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/websocket-handoff.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAC,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAC,MAAM,IAAI,CAAC;AAEvE,OAAO,EAAqB,KAAK,qBAAqB,EAAC,MAAM,WAAW,CAAC;AACzE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAC,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAC,MAAM,gBAAgB,CAAC;AAGvE,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAChC,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,EACrC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,KAC/B,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AAE3B,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CACjC,EAAE,EAAE,SAAS,EACb,OAAO,EAAE,CAAC,EACV,GAAG,EAAE,qBAAqB,KACvB,IAAI,CAAC;AAEV,MAAM,MAAM,uBAAuB,GAAG,CACpC,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,KACd,IAAI,CAAC;AAuEV;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC5B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,aAAa,CAAC,EAAE,aAAa,QAc9B;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC7B,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAC5C,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,QAsChD;AAED,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI;IACvB,OAAO,aAAa,CAAC,OAAO;IAC5B;QACE,OAAO,EAAE,qBAAqB,CAAC;QAC/B,IAAI,EAAE,WAAW,CAAC;QAClB,OAAO,EAAE,CAAC,CAAC;KACZ;CACF,CAAC"}
|
|
@@ -25,7 +25,13 @@ function createWebSocketHandoffHandler(lc, handoff, serverOptions) {
|
|
|
25
25
|
sender.send(data, socket);
|
|
26
26
|
}
|
|
27
27
|
function onError(error) {
|
|
28
|
-
wss.handleUpgrade(message, socket, Buffer.from(head), (ws) =>
|
|
28
|
+
wss.handleUpgrade(message, socket, Buffer.from(head), (ws) => {
|
|
29
|
+
ws.on("error", (error) => {
|
|
30
|
+
lc.warn?.("error while rejecting websocket handoff", error);
|
|
31
|
+
ws.terminate();
|
|
32
|
+
});
|
|
33
|
+
closeWithError(lc, ws, error, PROTOCOL_ERROR);
|
|
34
|
+
});
|
|
29
35
|
}
|
|
30
36
|
try {
|
|
31
37
|
const spec = handoff(message, send, onError);
|
|
@@ -74,6 +80,6 @@ function installWebSocketReceiver(lc, server, receive, receiver, onAbort, onRece
|
|
|
74
80
|
});
|
|
75
81
|
}
|
|
76
82
|
//#endregion
|
|
77
|
-
export {
|
|
83
|
+
export { installWebSocketHandoff, installWebSocketReceiver };
|
|
78
84
|
|
|
79
85
|
//# sourceMappingURL=websocket-handoff.js.map
|