@livestore/common 0.4.0-dev.9 → 0.5.0-dev.0
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/dist/.tsbuildinfo +1 -1
- package/dist/ClientSessionLeaderThreadProxy.d.ts +27 -12
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
- package/dist/MaterializationJournal.d.ts +44 -0
- package/dist/MaterializationJournal.d.ts.map +1 -0
- package/dist/MaterializationJournal.js +112 -0
- package/dist/MaterializationJournal.js.map +1 -0
- package/dist/StateHead.d.ts +26 -0
- package/dist/StateHead.d.ts.map +1 -0
- package/dist/StateHead.js +50 -0
- package/dist/StateHead.js.map +1 -0
- package/dist/WorkerTransportError.d.ts +11 -0
- package/dist/WorkerTransportError.d.ts.map +1 -0
- package/dist/WorkerTransportError.js +11 -0
- package/dist/WorkerTransportError.js.map +1 -0
- package/dist/__tests__/fixture.d.ts +1 -1
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +3 -3
- package/dist/__tests__/fixture.js.map +1 -1
- package/dist/adapter-types.d.ts +59 -57
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +31 -22
- package/dist/adapter-types.js.map +1 -1
- package/dist/bounded-collections.d.ts.map +1 -1
- package/dist/bounded-collections.js +6 -4
- package/dist/bounded-collections.js.map +1 -1
- package/dist/debug-info.d.ts +56 -56
- package/dist/debug-info.d.ts.map +1 -1
- package/dist/debug-info.js +46 -32
- package/dist/debug-info.js.map +1 -1
- package/dist/defs.d.ts +9 -9
- package/dist/defs.js +2 -2
- package/dist/devtools/devtools-compatibility.test.d.ts +2 -0
- package/dist/devtools/devtools-compatibility.test.d.ts.map +1 -0
- package/dist/devtools/devtools-compatibility.test.js +16 -0
- package/dist/devtools/devtools-compatibility.test.js.map +1 -0
- package/dist/devtools/devtools-messages-client-session.d.ts +531 -312
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.js +86 -67
- package/dist/devtools/devtools-messages-client-session.js.map +1 -1
- package/dist/devtools/devtools-messages-common.d.ts +32 -35
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-common.js +12 -14
- package/dist/devtools/devtools-messages-common.js.map +1 -1
- package/dist/devtools/devtools-messages-leader.d.ts +417 -320
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +117 -87
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/devtools/devtools-sessioninfo.d.ts +28 -17
- package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -1
- package/dist/devtools/devtools-sessioninfo.js +12 -6
- package/dist/devtools/devtools-sessioninfo.js.map +1 -1
- package/dist/devtools/mod.d.ts +17 -7
- package/dist/devtools/mod.d.ts.map +1 -1
- package/dist/devtools/mod.js +19 -8
- package/dist/devtools/mod.js.map +1 -1
- package/dist/errors.d.ts +52 -46
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +31 -21
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +116 -21
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +596 -470
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/RejectedPushError.d.ts +138 -0
- package/dist/leader-thread/RejectedPushError.d.ts.map +1 -0
- package/dist/leader-thread/RejectedPushError.js +97 -0
- package/dist/leader-thread/RejectedPushError.js.map +1 -0
- package/dist/leader-thread/connection.d.ts +1 -0
- package/dist/leader-thread/connection.d.ts.map +1 -1
- package/dist/leader-thread/connection.js +10 -9
- package/dist/leader-thread/connection.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts +20 -15
- package/dist/leader-thread/eventlog.d.ts.map +1 -1
- package/dist/leader-thread/eventlog.js +80 -20
- package/dist/leader-thread/eventlog.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +1 -2
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +110 -62
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +19 -7
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +67 -45
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts +2 -0
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts.map +1 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js +32 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -0
- package/dist/leader-thread/materialize-event.d.ts +3 -2
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +32 -9
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/leader-thread/mod.d.ts +1 -0
- package/dist/leader-thread/mod.d.ts.map +1 -1
- package/dist/leader-thread/mod.js +1 -0
- package/dist/leader-thread/mod.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts +2 -2
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +19 -40
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/shutdown-channel.d.ts +3 -7
- package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
- package/dist/leader-thread/shutdown-channel.js +2 -3
- package/dist/leader-thread/shutdown-channel.js.map +1 -1
- package/dist/leader-thread/stream-events.d.ts +53 -0
- package/dist/leader-thread/stream-events.d.ts.map +1 -0
- package/dist/leader-thread/stream-events.js +164 -0
- package/dist/leader-thread/stream-events.js.map +1 -0
- package/dist/leader-thread/types.d.ts +92 -51
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js +17 -4
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/logging.d.ts +15 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +2 -0
- package/dist/logging.js.map +1 -0
- package/dist/make-client-session.d.ts +6 -5
- package/dist/make-client-session.d.ts.map +1 -1
- package/dist/make-client-session.js +10 -7
- package/dist/make-client-session.js.map +1 -1
- package/dist/materializer-helper.d.ts +6 -7
- package/dist/materializer-helper.d.ts.map +1 -1
- package/dist/materializer-helper.js +22 -40
- package/dist/materializer-helper.js.map +1 -1
- package/dist/otel.d.ts +4 -4
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +8 -8
- package/dist/otel.js.map +1 -1
- package/dist/rematerialize-from-eventlog.d.ts +3 -3
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
- package/dist/rematerialize-from-eventlog.js +52 -39
- package/dist/rematerialize-from-eventlog.js.map +1 -1
- package/dist/schema/EventDef/define.d.ts +161 -0
- package/dist/schema/EventDef/define.d.ts.map +1 -0
- package/dist/schema/EventDef/define.js +140 -0
- package/dist/schema/EventDef/define.js.map +1 -0
- package/dist/schema/EventDef/define.test.d.ts +2 -0
- package/dist/schema/EventDef/define.test.d.ts.map +1 -0
- package/dist/schema/EventDef/define.test.js +23 -0
- package/dist/schema/EventDef/define.test.js.map +1 -0
- package/dist/schema/EventDef/deprecated.d.ts +94 -0
- package/dist/schema/EventDef/deprecated.d.ts.map +1 -0
- package/dist/schema/EventDef/deprecated.js +143 -0
- package/dist/schema/EventDef/deprecated.js.map +1 -0
- package/dist/schema/EventDef/deprecated.test.d.ts +2 -0
- package/dist/schema/EventDef/deprecated.test.d.ts.map +1 -0
- package/dist/schema/EventDef/deprecated.test.js +95 -0
- package/dist/schema/EventDef/deprecated.test.js.map +1 -0
- package/dist/schema/EventDef/event-def.d.ts +110 -0
- package/dist/schema/EventDef/event-def.d.ts.map +1 -0
- package/dist/schema/EventDef/event-def.js +2 -0
- package/dist/schema/EventDef/event-def.js.map +1 -0
- package/dist/schema/EventDef/facts.d.ts +118 -0
- package/dist/schema/EventDef/facts.d.ts.map +1 -0
- package/dist/schema/EventDef/facts.js +53 -0
- package/dist/schema/EventDef/facts.js.map +1 -0
- package/dist/schema/EventDef/materializer.d.ts +155 -0
- package/dist/schema/EventDef/materializer.d.ts.map +1 -0
- package/dist/schema/EventDef/materializer.js +83 -0
- package/dist/schema/EventDef/materializer.js.map +1 -0
- package/dist/schema/EventDef/mod.d.ts +6 -0
- package/dist/schema/EventDef/mod.d.ts.map +1 -0
- package/dist/schema/EventDef/mod.js +6 -0
- package/dist/schema/EventDef/mod.js.map +1 -0
- package/dist/schema/EventSequenceNumber/client.d.ts +136 -0
- package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/client.js +192 -0
- package/dist/schema/EventSequenceNumber/client.js.map +1 -0
- package/dist/schema/EventSequenceNumber/global.d.ts +15 -0
- package/dist/schema/EventSequenceNumber/global.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/global.js +14 -0
- package/dist/schema/EventSequenceNumber/global.js.map +1 -0
- package/dist/schema/EventSequenceNumber/mod.d.ts +37 -0
- package/dist/schema/EventSequenceNumber/mod.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/mod.js +37 -0
- package/dist/schema/EventSequenceNumber/mod.js.map +1 -0
- package/dist/schema/EventSequenceNumber.test.js +44 -44
- package/dist/schema/EventSequenceNumber.test.js.map +1 -1
- package/dist/schema/LiveStoreEvent/client.d.ts +168 -0
- package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/client.js +179 -0
- package/dist/schema/LiveStoreEvent/client.js.map +1 -0
- package/dist/schema/LiveStoreEvent/client.test.d.ts +2 -0
- package/dist/schema/LiveStoreEvent/client.test.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/client.test.js +120 -0
- package/dist/schema/LiveStoreEvent/client.test.js.map +1 -0
- package/dist/schema/LiveStoreEvent/for-event-def.d.ts +52 -0
- package/dist/schema/LiveStoreEvent/for-event-def.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/for-event-def.js +2 -0
- package/dist/schema/LiveStoreEvent/for-event-def.js.map +1 -0
- package/dist/schema/LiveStoreEvent/global.d.ts +36 -0
- package/dist/schema/LiveStoreEvent/global.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/global.js +31 -0
- package/dist/schema/LiveStoreEvent/global.js.map +1 -0
- package/dist/schema/LiveStoreEvent/input.d.ts +46 -0
- package/dist/schema/LiveStoreEvent/input.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/input.js +26 -0
- package/dist/schema/LiveStoreEvent/input.js.map +1 -0
- package/dist/schema/LiveStoreEvent/mod.d.ts +5 -0
- package/dist/schema/LiveStoreEvent/mod.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/mod.js +5 -0
- package/dist/schema/LiveStoreEvent/mod.js.map +1 -0
- package/dist/schema/events.d.ts +1 -1
- package/dist/schema/events.d.ts.map +1 -1
- package/dist/schema/events.js +1 -1
- package/dist/schema/events.js.map +1 -1
- package/dist/schema/mod.d.ts +7 -4
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +6 -4
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +16 -1
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +34 -5
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +14 -18
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +54 -30
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +146 -6
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.d.ts +13 -13
- package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.js +13 -15
- package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.test.js +19 -43
- package/dist/schema/state/sqlite/column-annotations.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.d.ts +1 -1
- package/dist/schema/state/sqlite/column-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-def.js +132 -57
- package/dist/schema/state/sqlite/column-def.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.test.js +155 -70
- package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-spec.js +30 -12
- package/dist/schema/state/sqlite/column-spec.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.test.js +26 -17
- package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +2 -2
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +15 -6
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +25 -19
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +22 -16
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +28 -17
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +14 -12
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js +6 -4
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts +3 -5
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +6 -8
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/persistence.d.ts +11 -0
- package/dist/schema/state/sqlite/persistence.d.ts.map +1 -0
- package/dist/schema/state/sqlite/persistence.js +10 -0
- package/dist/schema/state/sqlite/persistence.js.map +1 -0
- package/dist/schema/state/sqlite/query-builder/api.d.ts +58 -29
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.js +105 -24
- package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts +4 -2
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.js +42 -27
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.test.js +402 -498
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.d.ts +2 -2
- package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.js +24 -14
- package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.test.d.ts +2 -0
- package/dist/schema/state/sqlite/schema-helpers.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/schema-helpers.test.js +36 -0
- package/dist/schema/state/sqlite/schema-helpers.test.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.d.ts +515 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js +54 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables/mod.d.ts +3 -0
- package/dist/schema/state/sqlite/system-tables/mod.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/mod.js +3 -0
- package/dist/schema/state/sqlite/system-tables/mod.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.d.ts +678 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.js +87 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.js.map +1 -0
- package/dist/schema/state/sqlite/table-def.d.ts +19 -20
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.js +54 -4
- package/dist/schema/state/sqlite/table-def.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.test.js +226 -20
- package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
- package/dist/schema/unknown-events.d.ts +47 -0
- package/dist/schema/unknown-events.d.ts.map +1 -0
- package/dist/schema/unknown-events.js +70 -0
- package/dist/schema/unknown-events.js.map +1 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts +2 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts.map +1 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +73 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -0
- package/dist/schema-management/common.js +2 -2
- package/dist/schema-management/common.js.map +1 -1
- package/dist/schema-management/migrations.d.ts +32 -2
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +38 -6
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/schema-management/validate-schema.d.ts +3 -3
- package/dist/schema-management/validate-schema.d.ts.map +1 -1
- package/dist/schema-management/validate-schema.js +2 -2
- package/dist/schema-management/validate-schema.js.map +1 -1
- package/dist/session-id-symbol.d.ts +29 -0
- package/dist/session-id-symbol.d.ts.map +1 -0
- package/dist/session-id-symbol.js +44 -0
- package/dist/session-id-symbol.js.map +1 -0
- package/dist/sql-queries/sql-queries.d.ts.map +1 -1
- package/dist/sql-queries/sql-queries.js +26 -16
- package/dist/sql-queries/sql-queries.js.map +1 -1
- package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
- package/dist/sql-queries/sql-query-builder.js.map +1 -1
- package/dist/sql-queries/types.d.ts +6 -7
- package/dist/sql-queries/types.d.ts.map +1 -1
- package/dist/sql-queries/types.js.map +1 -1
- package/dist/sqlite-db-helper.d.ts +21 -1
- package/dist/sqlite-db-helper.d.ts.map +1 -1
- package/dist/sqlite-db-helper.js +40 -5
- package/dist/sqlite-db-helper.js.map +1 -1
- package/dist/sqlite-types.d.ts +8 -11
- package/dist/sqlite-types.d.ts.map +1 -1
- package/dist/sqlite-types.js +3 -3
- package/dist/sqlite-types.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +47 -25
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +269 -154
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/errors.d.ts +12 -51
- package/dist/sync/errors.d.ts.map +1 -1
- package/dist/sync/errors.js +7 -24
- package/dist/sync/errors.js.map +1 -1
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +2 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/mock-sync-backend.d.ts +11 -10
- package/dist/sync/mock-sync-backend.d.ts.map +1 -1
- package/dist/sync/mock-sync-backend.js +97 -74
- package/dist/sync/mock-sync-backend.js.map +1 -1
- package/dist/sync/mock-sync-backend.test.d.ts +2 -0
- package/dist/sync/mock-sync-backend.test.d.ts.map +1 -0
- package/dist/sync/mock-sync-backend.test.js +49 -0
- package/dist/sync/mock-sync-backend.test.js.map +1 -0
- package/dist/sync/next/compact-events.d.ts.map +1 -1
- package/dist/sync/next/compact-events.js +11 -12
- package/dist/sync/next/compact-events.js.map +1 -1
- package/dist/sync/next/facts.d.ts +5 -5
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +7 -8
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +58 -15
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +219 -9
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.d.ts.map +1 -1
- package/dist/sync/next/history-dag.js +12 -11
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.d.ts +5 -5
- package/dist/sync/next/rebase-events.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.js +6 -6
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/compact-events.calculator.test.js +4 -4
- package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
- package/dist/sync/next/test/compact-events.test.d.ts.map +1 -1
- package/dist/sync/next/test/compact-events.test.js +2 -2
- package/dist/sync/next/test/compact-events.test.js.map +1 -1
- package/dist/sync/next/test/event-fixtures.d.ts +9 -9
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
- package/dist/sync/next/test/event-fixtures.js +12 -12
- package/dist/sync/next/test/event-fixtures.js.map +1 -1
- package/dist/sync/next/test/history-dag-common.test.d.ts +2 -0
- package/dist/sync/next/test/history-dag-common.test.d.ts.map +1 -0
- package/dist/sync/next/test/history-dag-common.test.js +73 -0
- package/dist/sync/next/test/history-dag-common.test.js.map +1 -0
- package/dist/sync/sync-backend-kv.d.ts +3 -3
- package/dist/sync/sync-backend-kv.d.ts.map +1 -1
- package/dist/sync/sync-backend-kv.js +6 -4
- package/dist/sync/sync-backend-kv.js.map +1 -1
- package/dist/sync/sync-backend.d.ts +41 -21
- package/dist/sync/sync-backend.d.ts.map +1 -1
- package/dist/sync/sync-backend.js +45 -4
- package/dist/sync/sync-backend.js.map +1 -1
- package/dist/sync/sync.d.ts +23 -2
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/syncstate.d.ts +115 -191
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +108 -129
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +289 -161
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/transport-chunking.d.ts +35 -0
- package/dist/sync/transport-chunking.d.ts.map +1 -0
- package/dist/sync/transport-chunking.js +47 -0
- package/dist/sync/transport-chunking.js.map +1 -0
- package/dist/sync/validate-push-payload.d.ts +2 -2
- package/dist/sync/validate-push-payload.d.ts.map +1 -1
- package/dist/sync/validate-push-payload.js +4 -6
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/testing/event-factory.d.ts +3 -3
- package/dist/testing/event-factory.d.ts.map +1 -1
- package/dist/testing/event-factory.js +5 -7
- package/dist/testing/event-factory.js.map +1 -1
- package/dist/util.d.ts +11 -4
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +21 -4
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +24 -5
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +25 -8
- package/dist/version.js.map +1 -1
- package/package.json +50 -16
- package/src/ClientSessionLeaderThreadProxy.ts +27 -12
- package/src/MaterializationJournal.ts +184 -0
- package/src/StateHead.ts +80 -0
- package/src/WorkerTransportError.ts +12 -0
- package/src/__tests__/fixture.ts +3 -3
- package/src/adapter-types.ts +62 -56
- package/src/bounded-collections.ts +6 -5
- package/src/debug-info.ts +54 -43
- package/src/defs.ts +2 -2
- package/src/devtools/devtools-compatibility.test.ts +19 -0
- package/src/devtools/devtools-messages-client-session.ts +72 -63
- package/src/devtools/devtools-messages-common.ts +18 -22
- package/src/devtools/devtools-messages-leader.ts +89 -68
- package/src/devtools/devtools-sessioninfo.ts +17 -12
- package/src/devtools/mod.ts +15 -7
- package/src/errors.ts +58 -38
- package/src/index.ts +11 -1
- package/src/leader-thread/LeaderSyncProcessor.ts +927 -674
- package/src/leader-thread/RejectedPushError.ts +119 -0
- package/src/leader-thread/connection.ts +13 -9
- package/src/leader-thread/eventlog.ts +114 -41
- package/src/leader-thread/leader-worker-devtools.ts +223 -124
- package/src/leader-thread/make-leader-thread-layer.test.ts +44 -0
- package/src/leader-thread/make-leader-thread-layer.ts +170 -82
- package/src/leader-thread/materialize-event.ts +51 -16
- package/src/leader-thread/mod.ts +1 -0
- package/src/leader-thread/recreate-db.ts +26 -61
- package/src/leader-thread/shutdown-channel.ts +3 -18
- package/src/leader-thread/stream-events.ts +209 -0
- package/src/leader-thread/types.ts +77 -65
- package/src/logging.ts +15 -0
- package/src/make-client-session.ts +16 -10
- package/src/materializer-helper.ts +31 -49
- package/src/otel.ts +15 -13
- package/src/rematerialize-from-eventlog.ts +106 -93
- package/src/schema/EventDef/define.test.ts +28 -0
- package/src/schema/EventDef/define.ts +218 -0
- package/src/schema/EventDef/deprecated.test.ts +122 -0
- package/src/schema/EventDef/deprecated.ts +172 -0
- package/src/schema/EventDef/event-def.ts +123 -0
- package/src/schema/EventDef/facts.ts +135 -0
- package/src/schema/EventDef/materializer.ts +172 -0
- package/src/schema/EventDef/mod.ts +5 -0
- package/src/schema/EventSequenceNumber/client.ts +256 -0
- package/src/schema/EventSequenceNumber/global.ts +19 -0
- package/src/schema/EventSequenceNumber/mod.ts +37 -0
- package/src/schema/EventSequenceNumber.test.ts +72 -53
- package/src/schema/LiveStoreEvent/client.test.ts +142 -0
- package/src/schema/LiveStoreEvent/client.ts +236 -0
- package/src/schema/LiveStoreEvent/for-event-def.ts +60 -0
- package/src/schema/LiveStoreEvent/global.ts +45 -0
- package/src/schema/LiveStoreEvent/input.ts +63 -0
- package/src/schema/LiveStoreEvent/mod.ts +4 -0
- package/src/schema/events.ts +1 -1
- package/src/schema/mod.ts +7 -4
- package/src/schema/schema.ts +48 -6
- package/src/schema/state/sqlite/client-document-def.test.ts +172 -14
- package/src/schema/state/sqlite/client-document-def.ts +126 -98
- package/src/schema/state/sqlite/column-annotations.test.ts +27 -45
- package/src/schema/state/sqlite/column-annotations.ts +27 -30
- package/src/schema/state/sqlite/column-def.test.ts +186 -77
- package/src/schema/state/sqlite/column-def.ts +169 -62
- package/src/schema/state/sqlite/column-spec.test.ts +34 -19
- package/src/schema/state/sqlite/column-spec.ts +38 -11
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +17 -7
- package/src/schema/state/sqlite/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +123 -63
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +37 -17
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +65 -45
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +48 -40
- package/src/schema/state/sqlite/mod.ts +9 -12
- package/src/schema/state/sqlite/persistence.ts +11 -0
- package/src/schema/state/sqlite/query-builder/api.ts +61 -23
- package/src/schema/state/sqlite/query-builder/astToSql.ts +117 -23
- package/src/schema/state/sqlite/query-builder/impl.test.ts +489 -551
- package/src/schema/state/sqlite/query-builder/impl.ts +43 -26
- package/src/schema/state/sqlite/schema-helpers.test.ts +44 -0
- package/src/schema/state/sqlite/schema-helpers.ts +30 -22
- package/src/schema/state/sqlite/system-tables/eventlog-tables.ts +64 -0
- package/src/schema/state/sqlite/system-tables/mod.ts +2 -0
- package/src/schema/state/sqlite/system-tables/state-tables.ts +109 -0
- package/src/schema/state/sqlite/table-def.test.ts +295 -46
- package/src/schema/state/sqlite/table-def.ts +109 -64
- package/src/schema/unknown-events.ts +132 -0
- package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +88 -0
- package/src/schema-management/common.ts +2 -2
- package/src/schema-management/migrations.ts +42 -9
- package/src/schema-management/validate-schema.ts +3 -3
- package/src/session-id-symbol.ts +59 -0
- package/src/sql-queries/sql-queries.ts +26 -18
- package/src/sql-queries/sql-query-builder.ts +1 -0
- package/src/sql-queries/types.ts +6 -7
- package/src/sqlite-db-helper.ts +67 -5
- package/src/sqlite-types.ts +9 -11
- package/src/sync/ClientSessionSyncProcessor.ts +388 -194
- package/src/sync/errors.ts +18 -28
- package/src/sync/index.ts +2 -0
- package/src/sync/mock-sync-backend.test.ts +78 -0
- package/src/sync/mock-sync-backend.ts +196 -108
- package/src/sync/next/compact-events.ts +10 -11
- package/src/sync/next/facts.ts +13 -14
- package/src/sync/next/history-dag-common.ts +306 -26
- package/src/sync/next/history-dag.ts +20 -13
- package/src/sync/next/rebase-events.ts +12 -12
- package/src/sync/next/test/compact-events.calculator.test.ts +5 -4
- package/src/sync/next/test/compact-events.test.ts +4 -3
- package/src/sync/next/test/event-fixtures.ts +14 -14
- package/src/sync/next/test/history-dag-common.test.ts +87 -0
- package/src/sync/sync-backend-kv.ts +7 -4
- package/src/sync/sync-backend.ts +75 -26
- package/src/sync/sync.ts +24 -2
- package/src/sync/syncstate.test.ts +657 -442
- package/src/sync/syncstate.ts +175 -160
- package/src/sync/transport-chunking.ts +85 -0
- package/src/sync/validate-push-payload.ts +6 -8
- package/src/testing/event-factory.ts +10 -12
- package/src/util.ts +20 -11
- package/src/version.ts +32 -8
- package/dist/schema/EventDef.d.ts +0 -126
- package/dist/schema/EventDef.d.ts.map +0 -1
- package/dist/schema/EventDef.js +0 -46
- package/dist/schema/EventDef.js.map +0 -1
- package/dist/schema/EventSequenceNumber.d.ts +0 -80
- package/dist/schema/EventSequenceNumber.d.ts.map +0 -1
- package/dist/schema/EventSequenceNumber.js +0 -139
- package/dist/schema/EventSequenceNumber.js.map +0 -1
- package/dist/schema/LiveStoreEvent.d.ts +0 -306
- package/dist/schema/LiveStoreEvent.d.ts.map +0 -1
- package/dist/schema/LiveStoreEvent.js +0 -147
- package/dist/schema/LiveStoreEvent.js.map +0 -1
- package/dist/schema/state/sqlite/system-tables.d.ts +0 -976
- package/dist/schema/state/sqlite/system-tables.d.ts.map +0 -1
- package/dist/schema/state/sqlite/system-tables.js +0 -81
- package/dist/schema/state/sqlite/system-tables.js.map +0 -1
- package/dist/sql-queries/misc.d.ts +0 -2
- package/dist/sql-queries/misc.d.ts.map +0 -1
- package/dist/sql-queries/misc.js +0 -2
- package/dist/sql-queries/misc.js.map +0 -1
- package/dist/sync/next/graphology.d.ts +0 -8
- package/dist/sync/next/graphology.d.ts.map +0 -1
- package/dist/sync/next/graphology.js +0 -30
- package/dist/sync/next/graphology.js.map +0 -1
- package/dist/sync/next/graphology_.d.ts +0 -3
- package/dist/sync/next/graphology_.d.ts.map +0 -1
- package/dist/sync/next/graphology_.js +0 -3
- package/dist/sync/next/graphology_.js.map +0 -1
- package/src/schema/EventDef.ts +0 -222
- package/src/schema/EventSequenceNumber.ts +0 -199
- package/src/schema/LiveStoreEvent.ts +0 -286
- package/src/schema/state/sqlite/system-tables.ts +0 -106
- package/src/sql-queries/misc.ts +0 -2
- package/src/sync/next/ambient.d.ts +0 -3
- package/src/sync/next/graphology.ts +0 -41
- package/src/sync/next/graphology_.ts +0 -2
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import { LS_DEV,
|
|
2
|
+
import { LS_DEV, TRACE_VERBOSE } from '@livestore/utils'
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
Cause,
|
|
5
|
+
Deferred,
|
|
5
6
|
Effect,
|
|
6
7
|
Exit,
|
|
8
|
+
Filter,
|
|
7
9
|
FiberHandle,
|
|
8
10
|
Option,
|
|
9
11
|
Queue,
|
|
10
|
-
type Runtime,
|
|
11
12
|
Schema,
|
|
13
|
+
Semaphore,
|
|
12
14
|
type Scope,
|
|
13
15
|
Stream,
|
|
14
16
|
Subscribable,
|
|
17
|
+
TxQueue,
|
|
15
18
|
} from '@livestore/utils/effect'
|
|
16
|
-
import type * as otel from '@opentelemetry/api'
|
|
17
19
|
|
|
18
|
-
import {
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
20
|
+
import type { ClientSession } from '../adapter-types.ts'
|
|
21
|
+
import { MaterializeError } from '../errors.ts'
|
|
22
|
+
import { isRejectedPushError } from '../leader-thread/RejectedPushError.ts'
|
|
23
|
+
import * as EventSequenceNumber from '../schema/EventSequenceNumber/mod.ts'
|
|
24
|
+
import * as LiveStoreEvent from '../schema/LiveStoreEvent/mod.ts'
|
|
25
|
+
import type { LiveStoreSchema } from '../schema/mod.ts'
|
|
26
|
+
import { resolveSessionIdSymbolInEventArgs } from '../session-id-symbol.ts'
|
|
27
|
+
import * as StateHead from '../StateHead.ts'
|
|
22
28
|
import * as SyncState from './syncstate.ts'
|
|
23
29
|
|
|
30
|
+
/** Serialize value to JSON string for trace attributes */
|
|
31
|
+
const jsonStringify = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown))
|
|
32
|
+
|
|
24
33
|
/**
|
|
25
34
|
* Rebase behaviour:
|
|
26
35
|
* - We continously pull events from the leader and apply them to the local store.
|
|
@@ -36,23 +45,20 @@ import * as SyncState from './syncstate.ts'
|
|
|
36
45
|
* - The leader sync processor pulls regular LiveStore events, while the session sync processor pulls SyncState.PayloadUpstream items
|
|
37
46
|
* - The session sync processor has no downstream nodes.
|
|
38
47
|
*/
|
|
39
|
-
export const makeClientSessionSyncProcessor = ({
|
|
48
|
+
export const makeClientSessionSyncProcessor = Effect.fn('makeClientSessionSyncProcessor')(function* ({
|
|
40
49
|
schema,
|
|
41
50
|
clientSession,
|
|
42
|
-
runtime,
|
|
43
51
|
materializeEvent,
|
|
44
52
|
rollback,
|
|
45
53
|
refreshTables,
|
|
46
|
-
span,
|
|
47
54
|
params,
|
|
48
55
|
confirmUnsavedChanges,
|
|
49
56
|
}: {
|
|
50
57
|
schema: LiveStoreSchema
|
|
51
58
|
clientSession: ClientSession
|
|
52
|
-
runtime: Runtime.Runtime<Scope.Scope>
|
|
53
59
|
materializeEvent: (
|
|
54
|
-
|
|
55
|
-
options: {
|
|
60
|
+
eventEncoded: LiveStoreEvent.Client.EncodedWithMeta,
|
|
61
|
+
options: { materializerHashLeader: Option.Option<number> },
|
|
56
62
|
) => Effect.Effect<
|
|
57
63
|
{
|
|
58
64
|
writeTables: Set<string>
|
|
@@ -66,23 +72,27 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
66
72
|
>
|
|
67
73
|
rollback: (changeset: Uint8Array<ArrayBuffer>) => void
|
|
68
74
|
refreshTables: (tables: Set<string>) => void
|
|
69
|
-
span: otel.Span
|
|
70
75
|
params: {
|
|
71
76
|
leaderPushBatchSize: number
|
|
72
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Test-only deterministic barriers, awaited at fixed points of the rebase critical section.
|
|
79
|
+
* A test can park the pull fiber at a chosen point, inject a concurrent operation
|
|
80
|
+
* (a synchronous `push` or a `shutdown`), then release the barrier — without relying on
|
|
81
|
+
* virtual-time scheduling. Unset in production, where each lookup resolves to `Effect.void`.
|
|
82
|
+
*/
|
|
83
|
+
rebaseBarriers?: Partial<Record<RebaseBarrierPoint, Effect.Effect<void>>>
|
|
73
84
|
}
|
|
74
85
|
/**
|
|
75
86
|
* Currently only used in the web adapter:
|
|
76
87
|
* If true, registers a beforeunload event listener to confirm unsaved changes.
|
|
77
88
|
*/
|
|
78
89
|
confirmUnsavedChanges: boolean
|
|
79
|
-
})
|
|
80
|
-
const
|
|
90
|
+
}) {
|
|
91
|
+
const stateHead = yield* StateHead.StateHead
|
|
92
|
+
const eventSchema = LiveStoreEvent.Client.makeSchemaMemo(schema)
|
|
81
93
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
key2: keyof ClientSessionSyncProcessorSimulationParams[TKey],
|
|
85
|
-
) => Effect.sleep((params.simulation?.[key]?.[key2] ?? 0) as number)
|
|
94
|
+
const rebaseBarrier = (point: RebaseBarrierPoint): Effect.Effect<void> =>
|
|
95
|
+
params.rebaseBarriers?.[point] ?? Effect.void
|
|
86
96
|
|
|
87
97
|
const syncStateRef = {
|
|
88
98
|
// The initial state is identical to the leader's initial state
|
|
@@ -95,98 +105,50 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
/** Only used for debugging / observability / testing, it's not relied upon for correctness of the sync processor. */
|
|
98
|
-
const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>()
|
|
99
|
-
const
|
|
100
|
-
|
|
108
|
+
const syncStateUpdateQueue = yield* Queue.unbounded<SyncState.SyncState>()
|
|
109
|
+
const isClientOnlyEvent = (eventEncoded: LiveStoreEvent.Client.EncodedWithMeta) =>
|
|
110
|
+
schema.eventsDefsMap.get(eventEncoded.name)?.options.clientOnly ?? false
|
|
101
111
|
|
|
102
112
|
/** We're queuing push requests to reduce the number of messages sent to the leader by batching them */
|
|
103
|
-
const leaderPushQueue =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
sessionId: clientSession.sessionId,
|
|
123
|
-
}),
|
|
124
|
-
)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
const mergeResult = SyncState.merge({
|
|
128
|
-
syncState: syncStateRef.current,
|
|
129
|
-
payload: { _tag: 'local-push', newEvents: encodedEventDefs },
|
|
130
|
-
isClientEvent,
|
|
131
|
-
isEqualEvent: LiveStoreEvent.isEqualEncoded,
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
yield* Effect.annotateCurrentSpan({
|
|
135
|
-
batchSize: encodedEventDefs.length,
|
|
136
|
-
mergeResultTag: mergeResult._tag,
|
|
137
|
-
eventCounts: encodedEventDefs.reduce<Record<string, number>>((acc, event) => {
|
|
138
|
-
acc[event.name] = (acc[event.name] ?? 0) + 1
|
|
139
|
-
return acc
|
|
140
|
-
}, {}),
|
|
141
|
-
...(TRACE_VERBOSE && { mergeResult: JSON.stringify(mergeResult) }),
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
if (mergeResult._tag === 'unexpected-error') {
|
|
145
|
-
return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.message)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (mergeResult._tag !== 'advance') {
|
|
149
|
-
return shouldNeverHappen(`Expected advance, got ${mergeResult._tag}`)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
syncStateRef.current = mergeResult.newSyncState
|
|
153
|
-
yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
|
|
154
|
-
|
|
155
|
-
// Materialize events to state
|
|
156
|
-
const writeTables = new Set<string>()
|
|
157
|
-
for (const event of mergeResult.newEvents) {
|
|
158
|
-
// TODO avoid encoding and decoding here again
|
|
159
|
-
const decodedEventDef = Schema.decodeSync(eventSchema)(event)
|
|
160
|
-
const {
|
|
161
|
-
writeTables: newWriteTables,
|
|
162
|
-
sessionChangeset,
|
|
163
|
-
materializerHash,
|
|
164
|
-
} = yield* materializeEvent(decodedEventDef, {
|
|
165
|
-
withChangeset: true,
|
|
166
|
-
materializerHashLeader: Option.none(),
|
|
167
|
-
})
|
|
168
|
-
for (const table of newWriteTables) {
|
|
169
|
-
writeTables.add(table)
|
|
113
|
+
const leaderPushQueue = yield* TxQueue.unbounded<LiveStoreEvent.Client.EncodedWithMeta, Cause.Done>()
|
|
114
|
+
/**
|
|
115
|
+
* Prevents pull reconciliation, push-rejection handling, and shutdown from running concurrently.
|
|
116
|
+
* These transitions inspect or update the pending events, leader push queue, and rejection state,
|
|
117
|
+
* so each must observe the others either fully before or fully after—not midway through bookkeeping.
|
|
118
|
+
*
|
|
119
|
+
* Regular local commits do not acquire this mutex; they remain synchronous and are incorporated
|
|
120
|
+
* when `reconcileLeaderPushQueue` reads the current pending events transactionally.
|
|
121
|
+
*/
|
|
122
|
+
const pullReconciliationMutex = yield* Semaphore.make(1)
|
|
123
|
+
const shutdownDone = yield* Deferred.make<void>()
|
|
124
|
+
const drainStartedSignal = yield* Deferred.make<void>()
|
|
125
|
+
const rejectionObserved = yield* Deferred.make<void>()
|
|
126
|
+
let shutdownStarted = false
|
|
127
|
+
let terminalPushCause: Cause.Cause<never> | undefined
|
|
128
|
+
let unresolvedRejection:
|
|
129
|
+
| {
|
|
130
|
+
readonly error: Error
|
|
131
|
+
readonly events: ReadonlyArray<LiveStoreEvent.Client.EncodedWithMeta>
|
|
170
132
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
rebaseCount: 0,
|
|
184
|
-
advanceCount: 0,
|
|
185
|
-
rejectCount: 0,
|
|
186
|
-
}
|
|
133
|
+
| undefined
|
|
134
|
+
let leaderPushingFiberHandle: FiberHandle.FiberHandle<void, never> | undefined
|
|
135
|
+
let pullingFiberHandle: FiberHandle.FiberHandle<void, never> | undefined
|
|
136
|
+
|
|
137
|
+
/** Rebuild the leader push queue from the current pending events without racing synchronous commits. */
|
|
138
|
+
const reconcileLeaderPushQueue = Effect.tx(
|
|
139
|
+
Effect.gen(function* () {
|
|
140
|
+
const livePending = syncStateRef.current.pending
|
|
141
|
+
yield* TxQueue.clear(leaderPushQueue)
|
|
142
|
+
yield* TxQueue.offerAll(leaderPushQueue, livePending)
|
|
143
|
+
}),
|
|
144
|
+
)
|
|
187
145
|
|
|
188
146
|
const boot: ClientSessionSyncProcessor['boot'] = Effect.gen(function* () {
|
|
189
|
-
if (
|
|
147
|
+
if (
|
|
148
|
+
confirmUnsavedChanges === true &&
|
|
149
|
+
typeof window !== 'undefined' &&
|
|
150
|
+
typeof window.addEventListener === 'function'
|
|
151
|
+
) {
|
|
190
152
|
const onBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
191
153
|
if (syncStateRef.current.pending.length > 0) {
|
|
192
154
|
// Trigger the default browser dialog
|
|
@@ -200,70 +162,117 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
200
162
|
)
|
|
201
163
|
}
|
|
202
164
|
|
|
203
|
-
const
|
|
165
|
+
const leaderPushingHandle = yield* FiberHandle.make<void, never>()
|
|
166
|
+
const pullingHandle = yield* FiberHandle.make<void, never>()
|
|
167
|
+
leaderPushingFiberHandle = leaderPushingHandle
|
|
168
|
+
pullingFiberHandle = pullingHandle
|
|
169
|
+
|
|
170
|
+
const backgroundLeaderPushing: Effect.Effect<void> = Effect.gen(function* () {
|
|
171
|
+
while (true) {
|
|
172
|
+
const batch = yield* TxQueue.takeBetween(leaderPushQueue, 1, params.leaderPushBatchSize).pipe(
|
|
173
|
+
Effect.catchIf(Cause.isDone, () => Effect.void),
|
|
174
|
+
)
|
|
175
|
+
if (batch === undefined) return
|
|
176
|
+
|
|
177
|
+
yield* clientSession.leaderThread.events.push(batch).pipe(
|
|
178
|
+
Effect.catchIf(isRejectedPushError, (error) =>
|
|
179
|
+
// A pull carrying the leader's corrective history can complete before or after this
|
|
180
|
+
// rejection response. Use the same mutex as pull reconciliation so this decision uses
|
|
181
|
+
// one stable view of pending events and cannot install a stale rejection fence after recovery.
|
|
182
|
+
pullReconciliationMutex
|
|
183
|
+
.withPermits(1)(
|
|
184
|
+
Effect.gen(function* () {
|
|
185
|
+
debugInfo.rejectCount++
|
|
186
|
+
if (shutdownStarted === true) return yield* Effect.die(error)
|
|
187
|
+
|
|
188
|
+
// A concurrent pull may have already confirmed or rebased this batch while the
|
|
189
|
+
// push response was in flight. In that case, rebuild the queue from the reconciled
|
|
190
|
+
// pending events instead of creating a fence that no future pull could release.
|
|
191
|
+
if (isRejectedBatchRecovered(batch, syncStateRef.current.pending) === true) {
|
|
192
|
+
yield* reconcileLeaderPushQueue
|
|
193
|
+
yield* Deferred.succeed(rejectionObserved, undefined)
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
unresolvedRejection = { error, events: batch }
|
|
198
|
+
yield* TxQueue.clear(leaderPushQueue)
|
|
199
|
+
yield* Deferred.succeed(rejectionObserved, undefined)
|
|
200
|
+
return true
|
|
201
|
+
}),
|
|
202
|
+
)
|
|
203
|
+
.pipe(
|
|
204
|
+
Effect.flatMap((shouldFence) =>
|
|
205
|
+
// Local commits remain synchronous and continue accumulating in pending/the FIFO,
|
|
206
|
+
// but this sole drain worker stays parked until pull reconciliation reseeds it.
|
|
207
|
+
shouldFence === true ? Effect.never : Effect.void,
|
|
208
|
+
),
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
}).pipe(
|
|
214
|
+
Effect.interruptible,
|
|
215
|
+
Effect.tapCauseLogPretty,
|
|
216
|
+
Effect.catchCause((cause) => {
|
|
217
|
+
if (Cause.hasInterruptsOnly(cause) === true) return Effect.void
|
|
204
218
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}),
|
|
212
|
-
)
|
|
213
|
-
}).pipe(Effect.forever, Effect.interruptible, Effect.tapCauseLogPretty)
|
|
219
|
+
terminalPushCause ??= cause
|
|
220
|
+
return shutdownStarted === true
|
|
221
|
+
? Effect.void
|
|
222
|
+
: clientSession.shutdown(Exit.failCause(cause)).pipe(Effect.forkDetach, Effect.asVoid)
|
|
223
|
+
}),
|
|
224
|
+
)
|
|
214
225
|
|
|
215
|
-
yield* FiberHandle.run(
|
|
226
|
+
yield* FiberHandle.run(leaderPushingHandle, backgroundLeaderPushing)
|
|
216
227
|
|
|
217
228
|
// NOTE We need to lazily call `.pull` as we want the cursor to be updated
|
|
218
|
-
|
|
229
|
+
const backgroundPulling = Stream.suspend(() =>
|
|
219
230
|
clientSession.leaderThread.events.pull({ cursor: syncStateRef.current.upstreamHead }),
|
|
220
231
|
).pipe(
|
|
232
|
+
Stream.tap(() =>
|
|
233
|
+
clientSession.devtools.enabled === true ? clientSession.devtools.pullLatch.await : Effect.void,
|
|
234
|
+
),
|
|
221
235
|
Stream.tap(({ payload }) =>
|
|
222
236
|
Effect.gen(function* () {
|
|
223
237
|
// yield* Effect.logDebug('ClientSessionSyncProcessor:pull', payload)
|
|
224
238
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const mergeResult = SyncState.merge({
|
|
239
|
+
const rejectionAtPullStart = unresolvedRejection
|
|
240
|
+
const mergeResult = yield* SyncState.merge({
|
|
230
241
|
syncState: syncStateRef.current,
|
|
231
242
|
payload,
|
|
232
|
-
|
|
233
|
-
isEqualEvent: LiveStoreEvent.isEqualEncoded,
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
243
|
+
isClientOnlyEvent,
|
|
244
|
+
isEqualEvent: LiveStoreEvent.Client.isEqualEncoded,
|
|
245
|
+
}).pipe(
|
|
246
|
+
Effect.filterOrElse(
|
|
247
|
+
(r) => r._tag !== 'reject',
|
|
248
|
+
() => Effect.die(new Error('Unexpected reject in client-session-sync-processor')),
|
|
249
|
+
),
|
|
250
|
+
)
|
|
241
251
|
|
|
242
252
|
syncStateRef.current = mergeResult.newSyncState
|
|
243
253
|
|
|
254
|
+
const recoveredRejection =
|
|
255
|
+
rejectionAtPullStart !== undefined &&
|
|
256
|
+
unresolvedRejection === rejectionAtPullStart &&
|
|
257
|
+
isRejectedBatchRecovered(rejectionAtPullStart.events, mergeResult.newSyncState.pending) === true
|
|
258
|
+
|
|
244
259
|
if (mergeResult._tag === 'rebase') {
|
|
245
|
-
|
|
260
|
+
yield* Effect.spanEvent('merge:pull:rebase', {
|
|
246
261
|
payloadTag: payload._tag,
|
|
247
|
-
|
|
262
|
+
...(TRACE_VERBOSE === true ? { payload: jsonStringify(payload) } : {}),
|
|
248
263
|
newEventsCount: mergeResult.newEvents.length,
|
|
249
264
|
rollbackCount: mergeResult.rollbackEvents.length,
|
|
250
|
-
|
|
265
|
+
...(TRACE_VERBOSE === true ? { res: jsonStringify(mergeResult) } : {}),
|
|
251
266
|
})
|
|
252
267
|
|
|
253
268
|
debugInfo.rebaseCount++
|
|
254
269
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
yield* FiberHandle.clear(leaderPushingFiberHandle)
|
|
258
|
-
|
|
259
|
-
if (SIMULATION_ENABLED) yield* simSleep('pull', '2_before_leader_push_queue_clear')
|
|
270
|
+
// Barrier: before we interrupt the in-flight leader-push worker.
|
|
271
|
+
yield* rebaseBarrier('before_leader_push_fiber_interrupt')
|
|
260
272
|
|
|
261
|
-
|
|
262
|
-
yield* BucketQueue.clear(leaderPushQueue)
|
|
273
|
+
yield* FiberHandle.clear(leaderPushingHandle)
|
|
263
274
|
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
if (LS_DEV) {
|
|
275
|
+
if (LS_DEV === true) {
|
|
267
276
|
yield* Effect.logDebug(
|
|
268
277
|
'merge:pull:rebase: rollback',
|
|
269
278
|
mergeResult.rollbackEvents.length,
|
|
@@ -271,6 +280,8 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
271
280
|
)
|
|
272
281
|
}
|
|
273
282
|
|
|
283
|
+
// Roll back the optimistic session changesets for the events this rebase discards.
|
|
284
|
+
// (Independent of the push queue below; order relative to the queue reconcile is irrelevant.)
|
|
274
285
|
for (let i = mergeResult.rollbackEvents.length - 1; i >= 0; i--) {
|
|
275
286
|
const event = mergeResult.rollbackEvents[i]!
|
|
276
287
|
if (event.meta.sessionChangeset._tag !== 'no-op' && event.meta.sessionChangeset._tag !== 'unset') {
|
|
@@ -278,41 +289,68 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
278
289
|
event.meta.sessionChangeset = { _tag: 'unset' }
|
|
279
290
|
}
|
|
280
291
|
}
|
|
292
|
+
if (mergeResult.rollbackEvents.length > 0) {
|
|
293
|
+
yield* stateHead
|
|
294
|
+
.set(mergeResult.rollbackEvents[0]!.parentSeqNum)
|
|
295
|
+
.pipe(Effect.mapError((cause) => MaterializeError.make({ cause })))
|
|
296
|
+
}
|
|
281
297
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
298
|
+
// Barrier: before the atomic queue reconciliation (the "discard + re-offer" step).
|
|
299
|
+
// A `push` admitted here appends its event to `syncStateRef.current.pending` AND to
|
|
300
|
+
// `leaderPushQueue`; the reconciliation below re-reads the current pending events, so
|
|
301
|
+
// that event is preserved rather than removed by the queue clear.
|
|
302
|
+
yield* rebaseBarrier('before_queue_reconcile')
|
|
303
|
+
|
|
304
|
+
// Atomic queue reconciliation. `push` runs via `Effect.runSyncWith` (a synchronous run,
|
|
305
|
+
// per the store's fully-synchronous commit contract), so it executes as an indivisible
|
|
306
|
+
// unit that can only interleave in THIS fiber's async gaps — never inside a synchronous
|
|
307
|
+
// stretch. By reading the current pending events, clearing, and re-offering with no async
|
|
308
|
+
// park between them, this block is atomic w.r.t. `push`. This replaces the blocking
|
|
309
|
+
// `pullReconciliationMutex` permit that previously forced `push` to wait: queue rebuilding
|
|
310
|
+
// and synchronous commits cannot interleave, without suspending the commit path.
|
|
311
|
+
//
|
|
312
|
+
// We re-read the current `syncStateRef.current.pending` instead of the stale
|
|
313
|
+
// `mergeResult.newSyncState.pending` snapshot captured at merge time, so any event a
|
|
314
|
+
// concurrent push appended during the async steps above (fiber interrupt / rollback /
|
|
315
|
+
// barriers) is included. `Effect.tx` commits the clear+offer as one transaction.
|
|
316
|
+
yield* reconcileLeaderPushQueue
|
|
317
|
+
|
|
318
|
+
// Barrier: before restarting the leader-push worker.
|
|
319
|
+
yield* rebaseBarrier('before_leader_push_fiber_run')
|
|
320
|
+
|
|
321
|
+
if (recoveredRejection === true) unresolvedRejection = undefined
|
|
322
|
+
yield* FiberHandle.run(leaderPushingHandle, backgroundLeaderPushing)
|
|
289
323
|
} else {
|
|
290
|
-
|
|
324
|
+
yield* Effect.spanEvent('merge:pull:advance', {
|
|
291
325
|
payloadTag: payload._tag,
|
|
292
|
-
|
|
326
|
+
...(TRACE_VERBOSE === true ? { payload: jsonStringify(payload) } : {}),
|
|
293
327
|
newEventsCount: mergeResult.newEvents.length,
|
|
294
|
-
|
|
328
|
+
...(TRACE_VERBOSE === true ? { res: jsonStringify(mergeResult) } : {}),
|
|
295
329
|
})
|
|
296
330
|
|
|
297
331
|
debugInfo.advanceCount++
|
|
332
|
+
|
|
333
|
+
if (recoveredRejection === true) {
|
|
334
|
+
yield* FiberHandle.clear(leaderPushingHandle)
|
|
335
|
+
yield* reconcileLeaderPushQueue
|
|
336
|
+
unresolvedRejection = undefined
|
|
337
|
+
yield* FiberHandle.run(leaderPushingHandle, backgroundLeaderPushing)
|
|
338
|
+
}
|
|
298
339
|
}
|
|
299
340
|
|
|
300
341
|
if (mergeResult.newEvents.length === 0) {
|
|
301
342
|
// If there are no new events, we need to update the sync state as well
|
|
302
|
-
yield*
|
|
343
|
+
yield* Queue.offer(syncStateUpdateQueue, mergeResult.newSyncState)
|
|
303
344
|
return
|
|
304
345
|
}
|
|
305
346
|
|
|
306
347
|
const writeTables = new Set<string>()
|
|
307
348
|
for (const event of mergeResult.newEvents) {
|
|
308
|
-
// TODO apply changeset if available (will require tracking of write tables as well)
|
|
309
|
-
const decodedEventDef = Schema.decodeSync(eventSchema)(event)
|
|
310
349
|
const {
|
|
311
350
|
writeTables: newWriteTables,
|
|
312
351
|
sessionChangeset,
|
|
313
352
|
materializerHash,
|
|
314
|
-
} = yield* materializeEvent(
|
|
315
|
-
withChangeset: true,
|
|
353
|
+
} = yield* materializeEvent(event, {
|
|
316
354
|
materializerHashLeader: event.meta.materializerHashLeader,
|
|
317
355
|
})
|
|
318
356
|
for (const table of newWriteTables) {
|
|
@@ -326,10 +364,11 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
326
364
|
refreshTables(writeTables)
|
|
327
365
|
|
|
328
366
|
// We're only triggering the sync state update after all events have been materialized
|
|
329
|
-
yield*
|
|
367
|
+
yield* Queue.offer(syncStateUpdateQueue, mergeResult.newSyncState)
|
|
330
368
|
}).pipe(
|
|
369
|
+
pullReconciliationMutex.withPermits(1),
|
|
331
370
|
Effect.tapCauseLogPretty,
|
|
332
|
-
Effect.
|
|
371
|
+
Effect.catchCause((cause) => clientSession.shutdown(Exit.failCause(cause))),
|
|
333
372
|
),
|
|
334
373
|
),
|
|
335
374
|
Stream.runDrain,
|
|
@@ -337,49 +376,210 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
337
376
|
Effect.interruptible,
|
|
338
377
|
Effect.withSpan('client-session-sync-processor:pull'),
|
|
339
378
|
Effect.tapCauseLogPretty,
|
|
340
|
-
Effect.forkScoped,
|
|
341
379
|
)
|
|
380
|
+
yield* FiberHandle.run(pullingHandle, backgroundPulling)
|
|
381
|
+
}).pipe(Effect.withSpan('client-session-sync-processor:boot'))
|
|
382
|
+
|
|
383
|
+
const runShutdown = Effect.fn('client-session-sync-processor:shutdown')(function* (
|
|
384
|
+
exit: Exit.Exit<unknown, unknown>,
|
|
385
|
+
) {
|
|
386
|
+
if (Exit.isFailure(exit) === true) {
|
|
387
|
+
if (pullingFiberHandle !== undefined) yield* FiberHandle.clear(pullingFiberHandle)
|
|
388
|
+
yield* pullReconciliationMutex.withPermits(1)(TxQueue.end(leaderPushQueue))
|
|
389
|
+
if (leaderPushingFiberHandle !== undefined) yield* FiberHandle.clear(leaderPushingFiberHandle)
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
yield* pullReconciliationMutex.withPermits(1)(
|
|
394
|
+
Effect.gen(function* () {
|
|
395
|
+
if (pullingFiberHandle !== undefined) yield* FiberHandle.clear(pullingFiberHandle)
|
|
396
|
+
yield* TxQueue.end(leaderPushQueue)
|
|
397
|
+
yield* Deferred.succeed(drainStartedSignal, undefined)
|
|
398
|
+
}),
|
|
399
|
+
)
|
|
400
|
+
if (leaderPushingFiberHandle !== undefined) {
|
|
401
|
+
if (unresolvedRejection === undefined) {
|
|
402
|
+
yield* FiberHandle.awaitEmpty(leaderPushingFiberHandle)
|
|
403
|
+
} else {
|
|
404
|
+
yield* FiberHandle.clear(leaderPushingFiberHandle)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (terminalPushCause !== undefined) return yield* Effect.failCause(terminalPushCause)
|
|
408
|
+
if (unresolvedRejection !== undefined) return yield* Effect.die(unresolvedRejection.error)
|
|
342
409
|
})
|
|
343
410
|
|
|
411
|
+
const shutdown: ClientSessionSyncProcessor['shutdown'] = (exit) =>
|
|
412
|
+
Effect.suspend(() => {
|
|
413
|
+
if (shutdownStarted === true) return Deferred.await(shutdownDone)
|
|
414
|
+
shutdownStarted = true
|
|
415
|
+
return runShutdown(exit).pipe(
|
|
416
|
+
Effect.exit,
|
|
417
|
+
Effect.tap((shutdownExit) => Deferred.done(shutdownDone, shutdownExit)),
|
|
418
|
+
Effect.forkDetach,
|
|
419
|
+
Effect.andThen(Deferred.await(shutdownDone)),
|
|
420
|
+
)
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
const encodeEvents: ClientSessionSyncProcessor['encodeEvents'] = Effect.fn(
|
|
424
|
+
'client-session-sync-processor:encode-events',
|
|
425
|
+
)(function* (events) {
|
|
426
|
+
let baseEventSequenceNumber = syncStateRef.current.localHead
|
|
427
|
+
return yield* Effect.forEach(events, ({ name, args }) =>
|
|
428
|
+
Effect.gen(function* () {
|
|
429
|
+
const eventDef = yield* Effect.fromNullishOr(schema.eventsDefsMap.get(name)).pipe(Effect.orDieDebugger)
|
|
430
|
+
const nextNumPair = EventSequenceNumber.Client.nextPair({
|
|
431
|
+
seqNum: baseEventSequenceNumber,
|
|
432
|
+
isClientOnly: eventDef.options.clientOnly,
|
|
433
|
+
rebaseGeneration: baseEventSequenceNumber.rebaseGeneration,
|
|
434
|
+
})
|
|
435
|
+
baseEventSequenceNumber = nextNumPair.seqNum
|
|
436
|
+
// Encoding known-valid domain data: an encode failure is an invariant violation (a defect),
|
|
437
|
+
// so `Effect.orDie` is the correct modeling — it keeps the typed error channel narrow.
|
|
438
|
+
const encoded = yield* Schema.encodeUnknownEffect(eventSchema)({
|
|
439
|
+
name,
|
|
440
|
+
// Client-document events expose SessionIdSymbol as an input placeholder, but encoded events are persisted
|
|
441
|
+
// and replayed by concrete id. Resolve during schema encoding so commit never mutates the caller's event.
|
|
442
|
+
args: resolveSessionIdSymbolInEventArgs(args, clientSession.sessionId),
|
|
443
|
+
...nextNumPair,
|
|
444
|
+
clientId: clientSession.clientId,
|
|
445
|
+
sessionId: clientSession.sessionId,
|
|
446
|
+
}).pipe(Effect.orDie)
|
|
447
|
+
return new LiveStoreEvent.Client.EncodedWithMeta(encoded)
|
|
448
|
+
}),
|
|
449
|
+
)
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
const materializeEvents: ClientSessionSyncProcessor['materializeEvents'] = Effect.fn(
|
|
453
|
+
'client-session-sync-processor:materialize-events',
|
|
454
|
+
)(function* (events) {
|
|
455
|
+
const writeTables = new Set<string>()
|
|
456
|
+
for (const event of events) {
|
|
457
|
+
const {
|
|
458
|
+
writeTables: newWriteTables,
|
|
459
|
+
sessionChangeset,
|
|
460
|
+
materializerHash,
|
|
461
|
+
} = yield* materializeEvent(event, {
|
|
462
|
+
materializerHashLeader: Option.none(),
|
|
463
|
+
})
|
|
464
|
+
for (const table of newWriteTables) {
|
|
465
|
+
writeTables.add(table)
|
|
466
|
+
}
|
|
467
|
+
event.meta.sessionChangeset = sessionChangeset
|
|
468
|
+
event.meta.materializerHashSession = materializerHash
|
|
469
|
+
}
|
|
470
|
+
return { writeTables }
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
const push: ClientSessionSyncProcessor['push'] = Effect.fn('client-session-sync-processor:push')(
|
|
474
|
+
function* (encodedEvents) {
|
|
475
|
+
if (shutdownStarted === true) {
|
|
476
|
+
return yield* Effect.die(
|
|
477
|
+
new Error('Cannot push events after the client session sync processor starts shutting down'),
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const mergeResult = yield* SyncState.merge({
|
|
482
|
+
syncState: syncStateRef.current,
|
|
483
|
+
payload: { _tag: 'local-push', newEvents: encodedEvents },
|
|
484
|
+
isClientOnlyEvent,
|
|
485
|
+
isEqualEvent: LiveStoreEvent.Client.isEqualEncoded,
|
|
486
|
+
}).pipe(
|
|
487
|
+
Effect.filterMapOrElse(Filter.tagged<typeof SyncState.MergeResult.Type>()('advance'), () =>
|
|
488
|
+
Effect.die(new Error('Expected advance from local-push merge')),
|
|
489
|
+
),
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
yield* Effect.annotateCurrentSpan({
|
|
493
|
+
batchSize: encodedEvents.length,
|
|
494
|
+
mergeResultTag: mergeResult._tag,
|
|
495
|
+
eventCounts: encodedEvents.reduce<Record<string, number>>((acc, event) => {
|
|
496
|
+
acc[event.name] = (acc[event.name] ?? 0) + 1
|
|
497
|
+
return acc
|
|
498
|
+
}, {}),
|
|
499
|
+
...(TRACE_VERBOSE === true ? { mergeResult: jsonStringify(mergeResult) } : {}),
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
syncStateRef.current = mergeResult.newSyncState
|
|
503
|
+
yield* Queue.offer(syncStateUpdateQueue, mergeResult.newSyncState)
|
|
504
|
+
const rejectedEvents = yield* TxQueue.offerAll(leaderPushQueue, mergeResult.newEvents)
|
|
505
|
+
if (rejectedEvents.length > 0) {
|
|
506
|
+
return yield* Effect.die(new Error('Leader push queue closed while accepting events'))
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
const debugInfo = {
|
|
512
|
+
rebaseCount: 0,
|
|
513
|
+
advanceCount: 0,
|
|
514
|
+
rejectCount: 0,
|
|
515
|
+
}
|
|
516
|
+
|
|
344
517
|
return {
|
|
345
|
-
push,
|
|
346
518
|
boot,
|
|
519
|
+
shutdown,
|
|
520
|
+
encodeEvents,
|
|
521
|
+
materializeEvents,
|
|
522
|
+
push,
|
|
347
523
|
syncState: Subscribable.make({
|
|
348
|
-
get: Effect.
|
|
349
|
-
const syncState = syncStateRef.current
|
|
350
|
-
if (syncStateRef === undefined) return shouldNeverHappen('Not initialized')
|
|
351
|
-
return syncState
|
|
352
|
-
}),
|
|
524
|
+
get: Effect.sync(() => syncStateRef.current),
|
|
353
525
|
changes: Stream.fromQueue(syncStateUpdateQueue),
|
|
354
526
|
}),
|
|
355
527
|
debug: {
|
|
528
|
+
awaitDrainStarted: Deferred.await(drainStartedSignal),
|
|
529
|
+
awaitRejection: Deferred.await(rejectionObserved),
|
|
356
530
|
print: () =>
|
|
357
531
|
Effect.gen(function* () {
|
|
358
532
|
console.log('debugInfo', debugInfo)
|
|
359
533
|
console.log('syncState', syncStateRef.current)
|
|
360
|
-
const
|
|
361
|
-
console.log('pushQueueSize',
|
|
362
|
-
const pushQueueItems = yield* BucketQueue.peekAll(leaderPushQueue)
|
|
534
|
+
const pushQueueItems = yield* snapshotTxQueue(leaderPushQueue)
|
|
535
|
+
console.log('pushQueueSize', pushQueueItems.length)
|
|
363
536
|
console.log(
|
|
364
537
|
'pushQueueItems',
|
|
365
538
|
pushQueueItems.map((_) => _.toJSON()),
|
|
366
539
|
)
|
|
367
|
-
}).pipe(Effect.
|
|
540
|
+
}).pipe(Effect.runSync),
|
|
368
541
|
debugInfo: () => debugInfo,
|
|
369
542
|
},
|
|
370
543
|
} satisfies ClientSessionSyncProcessor
|
|
371
|
-
}
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
const snapshotTxQueue = <A, E>(queue: TxQueue.TxQueue<A, E>): Effect.Effect<ReadonlyArray<A>, E> =>
|
|
547
|
+
Effect.tx(
|
|
548
|
+
Effect.gen(function* () {
|
|
549
|
+
if ((yield* TxQueue.isOpen(queue)) === false) return []
|
|
550
|
+
|
|
551
|
+
const items = yield* TxQueue.clear(queue)
|
|
552
|
+
yield* TxQueue.offerAll(queue, items)
|
|
553
|
+
return items
|
|
554
|
+
}),
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
const isRejectedBatchRecovered = (
|
|
558
|
+
rejectedEvents: ReadonlyArray<LiveStoreEvent.Client.EncodedWithMeta>,
|
|
559
|
+
pendingEvents: ReadonlyArray<LiveStoreEvent.Client.EncodedWithMeta>,
|
|
560
|
+
): boolean =>
|
|
561
|
+
rejectedEvents.every(
|
|
562
|
+
(rejectedEvent) =>
|
|
563
|
+
pendingEvents.some((pendingEvent) => LiveStoreEvent.Client.isEqualEncoded(pendingEvent, rejectedEvent)) === false,
|
|
564
|
+
)
|
|
372
565
|
|
|
373
566
|
export interface ClientSessionSyncProcessor {
|
|
374
|
-
|
|
375
|
-
|
|
567
|
+
boot: Effect.Effect<void, never, Scope.Scope>
|
|
568
|
+
shutdown: (exit: Exit.Exit<unknown, unknown>) => Effect.Effect<void>
|
|
569
|
+
encodeEvents: (
|
|
570
|
+
events: ReadonlyArray<LiveStoreEvent.Input.Decoded>,
|
|
571
|
+
) => Effect.Effect<ReadonlyArray<LiveStoreEvent.Client.EncodedWithMeta>>
|
|
572
|
+
push: (events: ReadonlyArray<LiveStoreEvent.Client.EncodedWithMeta>) => Effect.Effect<void>
|
|
573
|
+
materializeEvents: (
|
|
574
|
+
events: ReadonlyArray<LiveStoreEvent.Client.EncodedWithMeta>,
|
|
376
575
|
) => Effect.Effect<{ writeTables: Set<string> }, MaterializeError>
|
|
377
|
-
boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
|
|
378
576
|
/**
|
|
379
577
|
* Only used for debugging / observability.
|
|
380
578
|
*/
|
|
381
579
|
syncState: Subscribable.Subscribable<SyncState.SyncState>
|
|
382
580
|
debug: {
|
|
581
|
+
awaitDrainStarted: Effect.Effect<void>
|
|
582
|
+
awaitRejection: Effect.Effect<void>
|
|
383
583
|
print: () => void
|
|
384
584
|
debugInfo: () => {
|
|
385
585
|
rebaseCount: number
|
|
@@ -388,17 +588,11 @@ export interface ClientSessionSyncProcessor {
|
|
|
388
588
|
}
|
|
389
589
|
}
|
|
390
590
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
export
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
'3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0, 15)),
|
|
400
|
-
'4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0, 15)),
|
|
401
|
-
'5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0, 15)),
|
|
402
|
-
}),
|
|
403
|
-
})
|
|
404
|
-
type ClientSessionSyncProcessorSimulationParams = typeof ClientSessionSyncProcessorSimulationParams.Type
|
|
591
|
+
/**
|
|
592
|
+
* Injection points inside the rebase critical section where a test-only barrier may be awaited.
|
|
593
|
+
* Named for the step they precede so the deterministic no-loss tests can target the exact window.
|
|
594
|
+
*/
|
|
595
|
+
export type RebaseBarrierPoint =
|
|
596
|
+
| 'before_leader_push_fiber_interrupt'
|
|
597
|
+
| 'before_queue_reconcile'
|
|
598
|
+
| 'before_leader_push_fiber_run'
|