@livestore/common 0.3.0-dev.9 → 0.3.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/__tests__/fixture.d.ts +83 -221
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +33 -11
- package/dist/__tests__/fixture.js.map +1 -1
- package/dist/adapter-types.d.ts +120 -64
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +39 -8
- package/dist/adapter-types.js.map +1 -1
- package/dist/bounded-collections.d.ts.map +1 -1
- package/dist/debug-info.d.ts +1 -1
- package/dist/debug-info.d.ts.map +1 -1
- package/dist/debug-info.js +1 -0
- package/dist/debug-info.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +390 -0
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -0
- package/dist/devtools/devtools-messages-client-session.js +97 -0
- package/dist/devtools/devtools-messages-client-session.js.map +1 -0
- package/dist/devtools/devtools-messages-common.d.ts +68 -0
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -0
- package/dist/devtools/devtools-messages-common.js +60 -0
- package/dist/devtools/devtools-messages-common.js.map +1 -0
- package/dist/devtools/devtools-messages-leader.d.ts +394 -0
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -0
- package/dist/devtools/devtools-messages-leader.js +147 -0
- package/dist/devtools/devtools-messages-leader.js.map +1 -0
- package/dist/devtools/devtools-messages.d.ts +3 -580
- package/dist/devtools/devtools-messages.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.js +3 -174
- package/dist/devtools/devtools-messages.js.map +1 -1
- package/dist/devtools/devtools-sessioninfo.d.ts +32 -0
- package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -0
- package/dist/devtools/devtools-sessioninfo.js +36 -0
- package/dist/devtools/devtools-sessioninfo.js.map +1 -0
- package/dist/devtools/mod.d.ts +55 -0
- package/dist/devtools/mod.d.ts.map +1 -0
- package/dist/devtools/mod.js +33 -0
- package/dist/devtools/mod.js.map +1 -0
- package/dist/index.d.ts +7 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -9
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +36 -11
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +426 -252
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/connection.d.ts +34 -6
- package/dist/leader-thread/connection.d.ts.map +1 -1
- package/dist/leader-thread/connection.js +22 -7
- package/dist/leader-thread/connection.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts +27 -0
- package/dist/leader-thread/eventlog.d.ts.map +1 -0
- package/dist/leader-thread/eventlog.js +119 -0
- package/dist/leader-thread/eventlog.js.map +1 -0
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +155 -80
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +22 -9
- 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/materialize-event.d.ts +16 -0
- package/dist/leader-thread/materialize-event.d.ts.map +1 -0
- package/dist/leader-thread/materialize-event.js +109 -0
- package/dist/leader-thread/materialize-event.js.map +1 -0
- package/dist/leader-thread/mod.d.ts +1 -1
- package/dist/leader-thread/mod.d.ts.map +1 -1
- package/dist/leader-thread/mod.js +1 -1
- package/dist/leader-thread/mod.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts +4 -2
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +28 -32
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/shutdown-channel.d.ts +2 -5
- package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
- package/dist/leader-thread/shutdown-channel.js +2 -4
- package/dist/leader-thread/shutdown-channel.js.map +1 -1
- package/dist/leader-thread/types.d.ts +79 -38
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js +1 -3
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/make-client-session.d.ts +23 -0
- package/dist/make-client-session.d.ts.map +1 -0
- package/dist/make-client-session.js +57 -0
- package/dist/make-client-session.js.map +1 -0
- package/dist/materializer-helper.d.ts +23 -0
- package/dist/materializer-helper.d.ts.map +1 -0
- package/dist/materializer-helper.js +86 -0
- package/dist/materializer-helper.js.map +1 -0
- package/dist/otel.d.ts +2 -0
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +5 -0
- package/dist/otel.js.map +1 -1
- package/dist/rematerialize-from-eventlog.d.ts +14 -0
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -0
- package/dist/rematerialize-from-eventlog.js +64 -0
- package/dist/rematerialize-from-eventlog.js.map +1 -0
- package/dist/schema/EventDef.d.ts +146 -0
- package/dist/schema/EventDef.d.ts.map +1 -0
- package/dist/schema/EventDef.js +58 -0
- package/dist/schema/EventDef.js.map +1 -0
- package/dist/schema/EventSequenceNumber.d.ts +57 -0
- package/dist/schema/EventSequenceNumber.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber.js +82 -0
- package/dist/schema/EventSequenceNumber.js.map +1 -0
- package/dist/schema/EventSequenceNumber.test.d.ts +2 -0
- package/dist/schema/EventSequenceNumber.test.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber.test.js +11 -0
- package/dist/schema/EventSequenceNumber.test.js.map +1 -0
- package/dist/schema/LiveStoreEvent.d.ts +257 -0
- package/dist/schema/LiveStoreEvent.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent.js +117 -0
- package/dist/schema/LiveStoreEvent.js.map +1 -0
- package/dist/schema/events.d.ts +2 -0
- package/dist/schema/events.d.ts.map +1 -0
- package/dist/schema/events.js +2 -0
- package/dist/schema/events.js.map +1 -0
- package/dist/schema/mod.d.ts +8 -6
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +8 -6
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +50 -32
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +36 -43
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/mod.d.ts +3 -0
- package/dist/schema/state/mod.d.ts.map +1 -0
- package/dist/schema/state/mod.js +3 -0
- package/dist/schema/state/mod.js.map +1 -0
- package/dist/schema/state/sqlite/client-document-def.d.ts +223 -0
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -0
- package/dist/schema/state/sqlite/client-document-def.js +170 -0
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -0
- package/dist/schema/state/sqlite/client-document-def.test.d.ts +2 -0
- package/dist/schema/state/sqlite/client-document-def.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/client-document-def.test.js +201 -0
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +69 -0
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -0
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +71 -0
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/ast/validate.d.ts +3 -0
- package/dist/schema/state/sqlite/db-schema/ast/validate.d.ts.map +1 -0
- package/dist/schema/state/sqlite/db-schema/ast/validate.js +12 -0
- package/dist/schema/state/sqlite/db-schema/ast/validate.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +90 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +87 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.d.ts +2 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +29 -0
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +90 -0
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -0
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js +41 -0
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/hash.d.ts +2 -0
- package/dist/schema/state/sqlite/db-schema/hash.d.ts.map +1 -0
- package/dist/schema/state/sqlite/db-schema/hash.js +14 -0
- package/dist/schema/state/sqlite/db-schema/hash.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/mod.d.ts +3 -0
- package/dist/schema/state/sqlite/db-schema/mod.d.ts.map +1 -0
- package/dist/schema/state/sqlite/db-schema/mod.js +3 -0
- package/dist/schema/state/sqlite/db-schema/mod.js.map +1 -0
- package/dist/schema/state/sqlite/mod.d.ts +17 -0
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -0
- package/dist/schema/state/sqlite/mod.js +41 -0
- package/dist/schema/state/sqlite/mod.js.map +1 -0
- package/dist/schema/state/sqlite/query-builder/api.d.ts +294 -0
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -0
- package/dist/schema/state/sqlite/query-builder/api.js +6 -0
- package/dist/schema/state/sqlite/query-builder/api.js.map +1 -0
- package/dist/schema/state/sqlite/query-builder/astToSql.d.ts +7 -0
- package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -0
- package/dist/schema/state/sqlite/query-builder/astToSql.js +190 -0
- package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -0
- package/dist/schema/state/sqlite/query-builder/impl.d.ts +7 -0
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -0
- package/dist/schema/state/sqlite/query-builder/impl.js +286 -0
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -0
- package/dist/schema/state/sqlite/query-builder/impl.test.d.ts +87 -0
- package/dist/schema/state/sqlite/query-builder/impl.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/query-builder/impl.test.js +563 -0
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -0
- package/dist/{query-builder → schema/state/sqlite/query-builder}/mod.d.ts +7 -0
- package/dist/schema/state/sqlite/query-builder/mod.d.ts.map +1 -0
- package/dist/{query-builder → schema/state/sqlite/query-builder}/mod.js +7 -0
- package/dist/schema/state/sqlite/query-builder/mod.js.map +1 -0
- package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -0
- package/dist/schema/{schema-helpers.js → state/sqlite/schema-helpers.js} +1 -1
- package/dist/schema/state/sqlite/schema-helpers.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables.d.ts +574 -0
- package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables.js +88 -0
- package/dist/schema/state/sqlite/system-tables.js.map +1 -0
- package/dist/schema/state/sqlite/table-def.d.ts +84 -0
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -0
- package/dist/schema/state/sqlite/table-def.js +36 -0
- package/dist/schema/state/sqlite/table-def.js.map +1 -0
- package/dist/schema-management/common.d.ts +7 -7
- package/dist/schema-management/common.d.ts.map +1 -1
- package/dist/schema-management/common.js.map +1 -1
- package/dist/schema-management/migrations.d.ts +6 -6
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +27 -18
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/schema-management/validate-schema.d.ts +8 -0
- package/dist/schema-management/validate-schema.d.ts.map +1 -0
- package/dist/schema-management/validate-schema.js +39 -0
- package/dist/schema-management/validate-schema.js.map +1 -0
- package/dist/sql-queries/misc.d.ts.map +1 -1
- package/dist/sql-queries/sql-queries.d.ts +1 -1
- package/dist/sql-queries/sql-queries.d.ts.map +1 -1
- package/dist/sql-queries/sql-queries.js.map +1 -1
- package/dist/sql-queries/sql-query-builder.d.ts +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 +2 -1
- package/dist/sql-queries/types.d.ts.map +1 -1
- package/dist/sql-queries/types.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +40 -19
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +149 -73
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/next/compact-events.d.ts.map +1 -1
- package/dist/sync/next/compact-events.js +38 -35
- package/dist/sync/next/compact-events.js.map +1 -1
- package/dist/sync/next/facts.d.ts +21 -21
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +11 -11
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +9 -7
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +10 -5
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.d.ts +0 -2
- package/dist/sync/next/history-dag.d.ts.map +1 -1
- package/dist/sync/next/history-dag.js +16 -14
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.d.ts +10 -8
- package/dist/sync/next/rebase-events.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.js +18 -10
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/compact-events.calculator.test.js +39 -34
- package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
- package/dist/sync/next/test/compact-events.test.js +77 -77
- package/dist/sync/next/test/compact-events.test.js.map +1 -1
- package/dist/sync/next/test/{mutation-fixtures.d.ts → event-fixtures.d.ts} +35 -25
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -0
- package/dist/sync/next/test/{mutation-fixtures.js → event-fixtures.js} +81 -38
- package/dist/sync/next/test/event-fixtures.js.map +1 -0
- package/dist/sync/next/test/mod.d.ts +1 -1
- package/dist/sync/next/test/mod.d.ts.map +1 -1
- package/dist/sync/next/test/mod.js +1 -1
- package/dist/sync/next/test/mod.js.map +1 -1
- package/dist/sync/sync.d.ts +46 -21
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js +10 -6
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/syncstate.d.ts +193 -84
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +305 -151
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +267 -303
- package/dist/sync/syncstate.test.js.map +1 -1
- 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 -4
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/util.d.ts +2 -2
- package/dist/util.d.ts.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +10 -4
- package/src/__tests__/fixture.ts +36 -15
- package/src/adapter-types.ts +107 -68
- package/src/debug-info.ts +1 -0
- package/src/devtools/devtools-messages-client-session.ts +142 -0
- package/src/devtools/devtools-messages-common.ts +115 -0
- package/src/devtools/devtools-messages-leader.ts +191 -0
- package/src/devtools/devtools-messages.ts +3 -246
- package/src/devtools/devtools-sessioninfo.ts +101 -0
- package/src/devtools/mod.ts +59 -0
- package/src/index.ts +7 -9
- package/src/leader-thread/LeaderSyncProcessor.ts +664 -394
- package/src/leader-thread/connection.ts +54 -9
- package/src/leader-thread/eventlog.ts +199 -0
- package/src/leader-thread/leader-worker-devtools.ts +227 -104
- package/src/leader-thread/make-leader-thread-layer.ts +121 -72
- package/src/leader-thread/materialize-event.ts +173 -0
- package/src/leader-thread/mod.ts +1 -1
- package/src/leader-thread/recreate-db.ts +33 -38
- package/src/leader-thread/shutdown-channel.ts +2 -4
- package/src/leader-thread/types.ts +84 -46
- package/src/make-client-session.ts +136 -0
- package/src/materializer-helper.ts +138 -0
- package/src/otel.ts +8 -0
- package/src/rematerialize-from-eventlog.ts +117 -0
- package/src/schema/EventDef.ts +227 -0
- package/src/schema/EventSequenceNumber.test.ts +12 -0
- package/src/schema/EventSequenceNumber.ts +121 -0
- package/src/schema/LiveStoreEvent.ts +240 -0
- package/src/schema/events.ts +1 -0
- package/src/schema/mod.ts +8 -6
- package/src/schema/schema.ts +88 -84
- package/src/schema/state/mod.ts +2 -0
- package/src/schema/state/sqlite/client-document-def.test.ts +238 -0
- package/src/schema/state/sqlite/client-document-def.ts +444 -0
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +142 -0
- package/src/schema/state/sqlite/db-schema/ast/validate.ts +13 -0
- package/src/schema/state/sqlite/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +206 -0
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +35 -0
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +242 -0
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +222 -0
- package/src/schema/state/sqlite/db-schema/hash.ts +14 -0
- package/src/schema/state/sqlite/db-schema/mod.ts +2 -0
- package/src/schema/state/sqlite/mod.ts +73 -0
- package/src/schema/state/sqlite/query-builder/api.ts +440 -0
- package/src/schema/state/sqlite/query-builder/astToSql.ts +232 -0
- package/src/schema/state/sqlite/query-builder/impl.test.ts +617 -0
- package/src/schema/state/sqlite/query-builder/impl.ts +351 -0
- package/src/{query-builder → schema/state/sqlite/query-builder}/mod.ts +7 -0
- package/src/schema/{schema-helpers.ts → state/sqlite/schema-helpers.ts} +1 -1
- package/src/schema/state/sqlite/system-tables.ts +117 -0
- package/src/schema/state/sqlite/table-def.ts +197 -0
- package/src/schema-management/common.ts +7 -7
- package/src/schema-management/migrations.ts +37 -31
- package/src/schema-management/validate-schema.ts +61 -0
- package/src/sql-queries/sql-queries.ts +1 -1
- package/src/sql-queries/sql-query-builder.ts +1 -2
- package/src/sql-queries/types.ts +3 -1
- package/src/sync/ClientSessionSyncProcessor.ts +218 -94
- package/src/sync/next/compact-events.ts +38 -35
- package/src/sync/next/facts.ts +43 -41
- package/src/sync/next/history-dag-common.ts +17 -10
- package/src/sync/next/history-dag.ts +16 -17
- package/src/sync/next/rebase-events.ts +29 -17
- package/src/sync/next/test/compact-events.calculator.test.ts +46 -46
- package/src/sync/next/test/compact-events.test.ts +79 -79
- package/src/sync/next/test/event-fixtures.ts +226 -0
- package/src/sync/next/test/mod.ts +1 -1
- package/src/sync/sync.ts +46 -21
- package/src/sync/syncstate.test.ts +312 -345
- package/src/sync/syncstate.ts +414 -224
- package/src/sync/validate-push-payload.ts +6 -6
- package/src/version.ts +2 -2
- package/dist/derived-mutations.d.ts +0 -109
- package/dist/derived-mutations.d.ts.map +0 -1
- package/dist/derived-mutations.js +0 -54
- package/dist/derived-mutations.js.map +0 -1
- package/dist/derived-mutations.test.d.ts +0 -2
- package/dist/derived-mutations.test.d.ts.map +0 -1
- package/dist/derived-mutations.test.js +0 -93
- package/dist/derived-mutations.test.js.map +0 -1
- package/dist/devtools/devtools-bridge.d.ts +0 -13
- package/dist/devtools/devtools-bridge.d.ts.map +0 -1
- package/dist/devtools/devtools-bridge.js +0 -2
- package/dist/devtools/devtools-bridge.js.map +0 -1
- package/dist/devtools/devtools-window-message.d.ts +0 -29
- package/dist/devtools/devtools-window-message.d.ts.map +0 -1
- package/dist/devtools/devtools-window-message.js +0 -33
- package/dist/devtools/devtools-window-message.js.map +0 -1
- package/dist/devtools/index.d.ts +0 -42
- package/dist/devtools/index.d.ts.map +0 -1
- package/dist/devtools/index.js +0 -48
- package/dist/devtools/index.js.map +0 -1
- package/dist/init-singleton-tables.d.ts +0 -4
- package/dist/init-singleton-tables.d.ts.map +0 -1
- package/dist/init-singleton-tables.js +0 -16
- package/dist/init-singleton-tables.js.map +0 -1
- package/dist/leader-thread/apply-mutation.d.ts +0 -11
- package/dist/leader-thread/apply-mutation.d.ts.map +0 -1
- package/dist/leader-thread/apply-mutation.js +0 -107
- package/dist/leader-thread/apply-mutation.js.map +0 -1
- package/dist/leader-thread/leader-sync-processor.d.ts +0 -47
- package/dist/leader-thread/leader-sync-processor.d.ts.map +0 -1
- package/dist/leader-thread/leader-sync-processor.js +0 -430
- package/dist/leader-thread/leader-sync-processor.js.map +0 -1
- package/dist/leader-thread/mutationlog.d.ts +0 -10
- package/dist/leader-thread/mutationlog.d.ts.map +0 -1
- package/dist/leader-thread/mutationlog.js +0 -28
- package/dist/leader-thread/mutationlog.js.map +0 -1
- package/dist/leader-thread/pull-queue-set.d.ts +0 -7
- package/dist/leader-thread/pull-queue-set.d.ts.map +0 -1
- package/dist/leader-thread/pull-queue-set.js +0 -39
- package/dist/leader-thread/pull-queue-set.js.map +0 -1
- package/dist/mutation.d.ts +0 -20
- package/dist/mutation.d.ts.map +0 -1
- package/dist/mutation.js +0 -57
- package/dist/mutation.js.map +0 -1
- package/dist/query-builder/api.d.ts +0 -190
- package/dist/query-builder/api.d.ts.map +0 -1
- package/dist/query-builder/api.js +0 -8
- package/dist/query-builder/api.js.map +0 -1
- package/dist/query-builder/impl.d.ts +0 -12
- package/dist/query-builder/impl.d.ts.map +0 -1
- package/dist/query-builder/impl.js +0 -244
- package/dist/query-builder/impl.js.map +0 -1
- package/dist/query-builder/impl.test.d.ts +0 -2
- package/dist/query-builder/impl.test.d.ts.map +0 -1
- package/dist/query-builder/impl.test.js +0 -212
- package/dist/query-builder/impl.test.js.map +0 -1
- package/dist/query-builder/mod.d.ts.map +0 -1
- package/dist/query-builder/mod.js.map +0 -1
- package/dist/query-info.d.ts +0 -38
- package/dist/query-info.d.ts.map +0 -1
- package/dist/query-info.js +0 -7
- package/dist/query-info.js.map +0 -1
- package/dist/rehydrate-from-mutationlog.d.ts +0 -14
- package/dist/rehydrate-from-mutationlog.d.ts.map +0 -1
- package/dist/rehydrate-from-mutationlog.js +0 -66
- package/dist/rehydrate-from-mutationlog.js.map +0 -1
- package/dist/schema/EventId.d.ts +0 -39
- package/dist/schema/EventId.d.ts.map +0 -1
- package/dist/schema/EventId.js +0 -38
- package/dist/schema/EventId.js.map +0 -1
- package/dist/schema/EventId.test.d.ts +0 -2
- package/dist/schema/EventId.test.d.ts.map +0 -1
- package/dist/schema/EventId.test.js +0 -11
- package/dist/schema/EventId.test.js.map +0 -1
- package/dist/schema/MutationEvent.d.ts +0 -167
- package/dist/schema/MutationEvent.d.ts.map +0 -1
- package/dist/schema/MutationEvent.js +0 -72
- package/dist/schema/MutationEvent.js.map +0 -1
- package/dist/schema/MutationEvent.test.d.ts +0 -2
- package/dist/schema/MutationEvent.test.d.ts.map +0 -1
- package/dist/schema/MutationEvent.test.js +0 -2
- package/dist/schema/MutationEvent.test.js.map +0 -1
- package/dist/schema/mutations.d.ts +0 -107
- package/dist/schema/mutations.d.ts.map +0 -1
- package/dist/schema/mutations.js +0 -42
- package/dist/schema/mutations.js.map +0 -1
- package/dist/schema/schema-helpers.d.ts.map +0 -1
- package/dist/schema/schema-helpers.js.map +0 -1
- package/dist/schema/system-tables.d.ts +0 -399
- package/dist/schema/system-tables.d.ts.map +0 -1
- package/dist/schema/system-tables.js +0 -59
- package/dist/schema/system-tables.js.map +0 -1
- package/dist/schema/table-def.d.ts +0 -156
- package/dist/schema/table-def.d.ts.map +0 -1
- package/dist/schema/table-def.js +0 -79
- package/dist/schema/table-def.js.map +0 -1
- package/dist/schema-management/validate-mutation-defs.d.ts +0 -8
- package/dist/schema-management/validate-mutation-defs.d.ts.map +0 -1
- package/dist/schema-management/validate-mutation-defs.js +0 -39
- package/dist/schema-management/validate-mutation-defs.js.map +0 -1
- package/dist/sync/client-session-sync-processor.d.ts +0 -45
- package/dist/sync/client-session-sync-processor.d.ts.map +0 -1
- package/dist/sync/client-session-sync-processor.js +0 -131
- package/dist/sync/client-session-sync-processor.js.map +0 -1
- package/dist/sync/next/test/mutation-fixtures.d.ts.map +0 -1
- package/dist/sync/next/test/mutation-fixtures.js.map +0 -1
- package/src/derived-mutations.test.ts +0 -101
- package/src/derived-mutations.ts +0 -170
- package/src/devtools/devtools-bridge.ts +0 -14
- package/src/devtools/devtools-window-message.ts +0 -27
- package/src/devtools/index.ts +0 -48
- package/src/init-singleton-tables.ts +0 -24
- package/src/leader-thread/apply-mutation.ts +0 -161
- package/src/leader-thread/mutationlog.ts +0 -46
- package/src/leader-thread/pull-queue-set.ts +0 -58
- package/src/mutation.ts +0 -91
- package/src/query-builder/api.ts +0 -289
- package/src/query-builder/impl.test.ts +0 -239
- package/src/query-builder/impl.ts +0 -285
- package/src/query-info.ts +0 -78
- package/src/rehydrate-from-mutationlog.ts +0 -119
- package/src/schema/EventId.test.ts +0 -12
- package/src/schema/EventId.ts +0 -60
- package/src/schema/MutationEvent.ts +0 -185
- package/src/schema/mutations.ts +0 -192
- package/src/schema/system-tables.ts +0 -105
- package/src/schema/table-def.ts +0 -343
- package/src/schema-management/validate-mutation-defs.ts +0 -63
- package/src/sync/next/test/mutation-fixtures.ts +0 -224
- package/tsconfig.json +0 -11
- /package/dist/schema/{schema-helpers.d.ts → state/sqlite/schema-helpers.d.ts} +0 -0
package/src/sync/syncstate.ts
CHANGED
@@ -1,223 +1,314 @@
|
|
1
|
-
import { shouldNeverHappen } from '@livestore/utils'
|
2
|
-
import { ReadonlyArray, Schema } from '@livestore/utils/effect'
|
1
|
+
import { casesHandled, LS_DEV, shouldNeverHappen } from '@livestore/utils'
|
2
|
+
import { Match, ReadonlyArray, Schema } from '@livestore/utils/effect'
|
3
3
|
|
4
|
-
import
|
5
|
-
import * as
|
4
|
+
import { UnexpectedError } from '../adapter-types.js'
|
5
|
+
import * as EventSequenceNumber from '../schema/EventSequenceNumber.js'
|
6
|
+
import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
|
6
7
|
|
7
8
|
/**
|
8
9
|
* SyncState represents the current sync state of a sync node relative to an upstream node.
|
9
10
|
* Events flow from local to upstream, with each state maintaining its own event head.
|
10
11
|
*
|
11
|
-
*
|
12
|
+
* Example:
|
12
13
|
* ```
|
13
|
-
*
|
14
|
-
* |
|
15
|
-
*
|
16
|
-
*
|
17
|
-
*
|
18
|
-
*
|
14
|
+
* +------------------------+
|
15
|
+
* | PENDING EVENTS |
|
16
|
+
* +------------------------+
|
17
|
+
* ▼ ▼
|
18
|
+
* Upstream Head Local Head
|
19
|
+
* (1,0) (1,1), (1,2), (2,0)
|
19
20
|
* ```
|
20
21
|
*
|
21
|
-
*
|
22
|
-
* -
|
23
|
-
*
|
24
|
-
* - Subject to rebase if rejected.
|
25
|
-
* - **Rollback Tail**: Events that are kept around temporarily for potential rollback until confirmed by upstream.
|
22
|
+
* **Pending Events**: Events awaiting acknowledgment from the upstream.
|
23
|
+
* - Can be confirmed or rejected by the upstream.
|
24
|
+
* - Subject to rebase if rejected.
|
26
25
|
*
|
27
26
|
* Payloads:
|
28
27
|
* - `PayloadUpstreamRebase`: Upstream has performed a rebase, so downstream must roll back to the specified event
|
29
28
|
* and rebase the pending events on top of the new events.
|
30
29
|
* - `PayloadUpstreamAdvance`: Upstream has advanced, so downstream must rebase the pending events on top of the new events.
|
31
|
-
* - `PayloadUpstreamTrimRollbackTail`: Upstream has advanced, so downstream can trim the rollback tail.
|
32
30
|
* - `PayloadLocalPush`: Local push payload
|
33
31
|
*
|
34
32
|
* Invariants:
|
35
33
|
* 1. **Chain Continuity**: Each event must reference its immediate parent.
|
36
34
|
* 2. **Head Ordering**: Upstream Head ≤ Local Head.
|
37
|
-
* 3. **
|
35
|
+
* 3. **Event number sequence**: Must follow the pattern (1,0)→(1,1)→(1,2)→(2,0).
|
38
36
|
*
|
39
|
-
*
|
40
|
-
*
|
37
|
+
* A few further notes to help form an intuition:
|
38
|
+
* - The goal is to keep the pending events as small as possible (i.e. to have synced with the next upstream node)
|
39
|
+
* - There are 2 cases for rebasing:
|
40
|
+
* - The conflicting event only conflicts with the pending events -> only (some of) the pending events need to be rolled back
|
41
|
+
*
|
42
|
+
* The `merge` function processes updates to the sync state based on incoming payloads,
|
43
|
+
* handling cases such as upstream rebase, advance and local push.
|
41
44
|
*/
|
42
45
|
export class SyncState extends Schema.Class<SyncState>('SyncState')({
|
43
|
-
pending: Schema.Array(
|
44
|
-
|
45
|
-
upstreamHead:
|
46
|
-
|
46
|
+
pending: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
47
|
+
/** What this node expects the next upstream node to have as its own local head */
|
48
|
+
upstreamHead: EventSequenceNumber.EventSequenceNumber,
|
49
|
+
/** Equivalent to `pending.at(-1)?.id` if there are pending events */
|
50
|
+
localHead: EventSequenceNumber.EventSequenceNumber,
|
47
51
|
}) {
|
48
|
-
toJSON = (): any => {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
localHead: `(${this.localHead.global},${this.localHead.local})`,
|
54
|
-
}
|
55
|
-
}
|
52
|
+
toJSON = (): any => ({
|
53
|
+
pending: this.pending.map((e) => e.toJSON()),
|
54
|
+
upstreamHead: EventSequenceNumber.toString(this.upstreamHead),
|
55
|
+
localHead: EventSequenceNumber.toString(this.localHead),
|
56
|
+
})
|
56
57
|
}
|
57
58
|
|
59
|
+
/**
|
60
|
+
* This payload propagates a rebase from the upstream node
|
61
|
+
*/
|
58
62
|
export class PayloadUpstreamRebase extends Schema.TaggedStruct('upstream-rebase', {
|
59
|
-
/**
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
trimRollbackUntil: Schema.optional(EventId.EventId),
|
63
|
+
/** Events which need to be rolled back */
|
64
|
+
rollbackEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
65
|
+
/** Events which need to be applied after the rollback (already rebased by the upstream node) */
|
66
|
+
newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
64
67
|
}) {}
|
65
68
|
|
66
69
|
export class PayloadUpstreamAdvance extends Schema.TaggedStruct('upstream-advance', {
|
67
|
-
newEvents: Schema.Array(
|
68
|
-
/** Trim rollback tail up to this event (inclusive). */
|
69
|
-
trimRollbackUntil: Schema.optional(EventId.EventId),
|
70
|
+
newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
70
71
|
}) {}
|
71
72
|
|
72
73
|
export class PayloadLocalPush extends Schema.TaggedStruct('local-push', {
|
73
|
-
newEvents: Schema.Array(
|
74
|
+
newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
74
75
|
}) {}
|
75
76
|
|
76
77
|
export class Payload extends Schema.Union(PayloadUpstreamRebase, PayloadUpstreamAdvance, PayloadLocalPush) {}
|
77
78
|
|
78
|
-
export
|
79
|
+
export class PayloadUpstream extends Schema.Union(PayloadUpstreamRebase, PayloadUpstreamAdvance) {}
|
79
80
|
|
80
|
-
|
81
|
+
/** Only used for debugging purposes */
|
82
|
+
export class MergeContext extends Schema.Class<MergeContext>('MergeContext')({
|
83
|
+
payload: Payload,
|
84
|
+
syncState: SyncState,
|
85
|
+
}) {
|
86
|
+
toJSON = (): any => {
|
87
|
+
const payload = Match.value(this.payload).pipe(
|
88
|
+
Match.tag('local-push', () => ({
|
89
|
+
_tag: 'local-push',
|
90
|
+
newEvents: this.payload.newEvents.map((e) => e.toJSON()),
|
91
|
+
})),
|
92
|
+
Match.tag('upstream-advance', () => ({
|
93
|
+
_tag: 'upstream-advance',
|
94
|
+
newEvents: this.payload.newEvents.map((e) => e.toJSON()),
|
95
|
+
})),
|
96
|
+
Match.tag('upstream-rebase', (payload) => ({
|
97
|
+
_tag: 'upstream-rebase',
|
98
|
+
newEvents: payload.newEvents.map((e) => e.toJSON()),
|
99
|
+
rollbackEvents: payload.rollbackEvents.map((e) => e.toJSON()),
|
100
|
+
})),
|
101
|
+
Match.exhaustive,
|
102
|
+
)
|
103
|
+
return {
|
104
|
+
payload,
|
105
|
+
syncState: this.syncState.toJSON(),
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
81
109
|
|
82
|
-
export
|
83
|
-
_tag: 'advance'
|
84
|
-
newSyncState: SyncState
|
85
|
-
|
86
|
-
/** Events which
|
87
|
-
|
110
|
+
export class MergeResultAdvance extends Schema.Class<MergeResultAdvance>('MergeResultAdvance')({
|
111
|
+
_tag: Schema.Literal('advance'),
|
112
|
+
newSyncState: SyncState,
|
113
|
+
newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
114
|
+
/** Events which were previously pending but are now confirmed */
|
115
|
+
confirmedEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
116
|
+
mergeContext: MergeContext,
|
117
|
+
}) {
|
118
|
+
toJSON = (): any => {
|
119
|
+
return {
|
120
|
+
_tag: this._tag,
|
121
|
+
newSyncState: this.newSyncState.toJSON(),
|
122
|
+
newEvents: this.newEvents.map((e) => e.toJSON()),
|
123
|
+
confirmedEvents: this.confirmedEvents.map((e) => e.toJSON()),
|
124
|
+
mergeContext: this.mergeContext.toJSON(),
|
125
|
+
}
|
126
|
+
}
|
88
127
|
}
|
89
128
|
|
90
|
-
export
|
91
|
-
_tag: 'rebase'
|
92
|
-
newSyncState: SyncState
|
93
|
-
|
94
|
-
/** Events which
|
95
|
-
|
96
|
-
|
129
|
+
export class MergeResultRebase extends Schema.Class<MergeResultRebase>('MergeResultRebase')({
|
130
|
+
_tag: Schema.Literal('rebase'),
|
131
|
+
newSyncState: SyncState,
|
132
|
+
newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
133
|
+
/** Events which need to be rolled back */
|
134
|
+
rollbackEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
135
|
+
mergeContext: MergeContext,
|
136
|
+
}) {
|
137
|
+
toJSON = (): any => {
|
138
|
+
return {
|
139
|
+
_tag: this._tag,
|
140
|
+
newSyncState: this.newSyncState.toJSON(),
|
141
|
+
newEvents: this.newEvents.map((e) => e.toJSON()),
|
142
|
+
rollbackEvents: this.rollbackEvents.map((e) => e.toJSON()),
|
143
|
+
mergeContext: this.mergeContext.toJSON(),
|
144
|
+
}
|
145
|
+
}
|
97
146
|
}
|
98
147
|
|
99
|
-
export
|
100
|
-
_tag: 'reject'
|
101
|
-
previousSyncState: SyncState
|
148
|
+
export class MergeResultReject extends Schema.Class<MergeResultReject>('MergeResultReject')({
|
149
|
+
_tag: Schema.Literal('reject'),
|
102
150
|
/** The minimum id that the new events must have */
|
103
|
-
expectedMinimumId:
|
151
|
+
expectedMinimumId: EventSequenceNumber.EventSequenceNumber,
|
152
|
+
mergeContext: MergeContext,
|
153
|
+
}) {
|
154
|
+
toJSON = (): any => {
|
155
|
+
return {
|
156
|
+
_tag: this._tag,
|
157
|
+
expectedMinimumId: EventSequenceNumber.toString(this.expectedMinimumId),
|
158
|
+
mergeContext: this.mergeContext.toJSON(),
|
159
|
+
}
|
160
|
+
}
|
104
161
|
}
|
105
162
|
|
106
|
-
export
|
163
|
+
export class MergeResultUnexpectedError extends Schema.Class<MergeResultUnexpectedError>('MergeResultUnexpectedError')({
|
164
|
+
_tag: Schema.Literal('unexpected-error'),
|
165
|
+
cause: UnexpectedError,
|
166
|
+
}) {}
|
167
|
+
|
168
|
+
export class MergeResult extends Schema.Union(
|
169
|
+
MergeResultAdvance,
|
170
|
+
MergeResultRebase,
|
171
|
+
MergeResultReject,
|
172
|
+
MergeResultUnexpectedError,
|
173
|
+
) {}
|
174
|
+
|
175
|
+
const unexpectedError = (cause: unknown): MergeResultUnexpectedError => {
|
176
|
+
if (LS_DEV) {
|
177
|
+
debugger
|
178
|
+
}
|
179
|
+
|
180
|
+
return MergeResultUnexpectedError.make({
|
181
|
+
_tag: 'unexpected-error',
|
182
|
+
cause: new UnexpectedError({ cause }),
|
183
|
+
})
|
184
|
+
}
|
107
185
|
|
108
|
-
|
186
|
+
// TODO Idea: call merge recursively through hierarchy levels
|
187
|
+
/*
|
188
|
+
Idea: have a map that maps from `globalEventSequenceNumber` to Array<ClientEvents>
|
189
|
+
The same applies to even further hierarchy levels
|
190
|
+
|
191
|
+
TODO: possibly even keep the client events in a separate table in the client leader
|
192
|
+
*/
|
193
|
+
export const merge = ({
|
109
194
|
syncState,
|
110
195
|
payload,
|
111
|
-
|
196
|
+
isClientEvent,
|
112
197
|
isEqualEvent,
|
113
|
-
|
198
|
+
ignoreClientEvents = false,
|
114
199
|
}: {
|
115
200
|
syncState: SyncState
|
116
201
|
payload: typeof Payload.Type
|
117
|
-
|
118
|
-
isEqualEvent: (a:
|
119
|
-
/** This is used in the leader which should ignore
|
120
|
-
|
121
|
-
}):
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
if (trimRollbackUntil === undefined) return rollbackTail
|
127
|
-
const index = rollbackTail.findIndex((event) => EventId.isEqual(event.id, trimRollbackUntil))
|
128
|
-
if (index === -1) return []
|
129
|
-
return rollbackTail.slice(index + 1)
|
130
|
-
}
|
202
|
+
isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
|
203
|
+
isEqualEvent: (a: LiveStoreEvent.EncodedWithMeta, b: LiveStoreEvent.EncodedWithMeta) => boolean
|
204
|
+
/** This is used in the leader which should ignore client events when receiving an upstream-advance payload */
|
205
|
+
ignoreClientEvents?: boolean
|
206
|
+
}): typeof MergeResult.Type => {
|
207
|
+
validateSyncState(syncState)
|
208
|
+
validatePayload(payload)
|
209
|
+
|
210
|
+
const mergeContext = MergeContext.make({ payload, syncState })
|
131
211
|
|
132
212
|
switch (payload._tag) {
|
133
213
|
case 'upstream-rebase': {
|
134
|
-
|
135
|
-
const rollbackIndex = syncState.rollbackTail.findIndex((event) =>
|
136
|
-
EventId.isEqual(event.id, payload.rollbackUntil),
|
137
|
-
)
|
138
|
-
if (rollbackIndex === -1) {
|
139
|
-
return shouldNeverHappen(
|
140
|
-
`Rollback event not found in rollback tail. Rollback until: [${payload.rollbackUntil.global},${payload.rollbackUntil.local}]. Rollback tail: [${syncState.rollbackTail.map((e) => e.toString()).join(', ')}]`,
|
141
|
-
)
|
142
|
-
}
|
143
|
-
|
144
|
-
const eventsToRollback = [...syncState.rollbackTail.slice(rollbackIndex), ...syncState.pending]
|
214
|
+
const rollbackEvents = [...payload.rollbackEvents, ...syncState.pending]
|
145
215
|
|
146
216
|
// Get the last new event's ID as the new upstream head
|
147
|
-
const newUpstreamHead = payload.newEvents.at(-1)?.
|
217
|
+
const newUpstreamHead = payload.newEvents.at(-1)?.seqNum ?? syncState.upstreamHead
|
148
218
|
|
149
219
|
// Rebase pending events on top of the new events
|
150
220
|
const rebasedPending = rebaseEvents({
|
151
221
|
events: syncState.pending,
|
152
|
-
|
153
|
-
|
222
|
+
baseEventSequenceNumber: newUpstreamHead,
|
223
|
+
isClientEvent,
|
154
224
|
})
|
155
225
|
|
156
|
-
return
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
226
|
+
return validateMergeResult(
|
227
|
+
MergeResultRebase.make({
|
228
|
+
_tag: 'rebase',
|
229
|
+
newSyncState: new SyncState({
|
230
|
+
pending: rebasedPending,
|
231
|
+
upstreamHead: newUpstreamHead,
|
232
|
+
localHead: rebasedPending.at(-1)?.seqNum ?? newUpstreamHead,
|
233
|
+
}),
|
234
|
+
newEvents: [...payload.newEvents, ...rebasedPending],
|
235
|
+
rollbackEvents,
|
236
|
+
mergeContext,
|
163
237
|
}),
|
164
|
-
|
165
|
-
newEvents: payload.newEvents,
|
166
|
-
eventsToRollback,
|
167
|
-
}
|
238
|
+
)
|
168
239
|
}
|
169
240
|
|
241
|
+
// #region upstream-advance
|
170
242
|
case 'upstream-advance': {
|
171
243
|
if (payload.newEvents.length === 0) {
|
172
|
-
return
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
244
|
+
return validateMergeResult(
|
245
|
+
MergeResultAdvance.make({
|
246
|
+
_tag: 'advance',
|
247
|
+
newSyncState: new SyncState({
|
248
|
+
pending: syncState.pending,
|
249
|
+
upstreamHead: syncState.upstreamHead,
|
250
|
+
localHead: syncState.localHead,
|
251
|
+
}),
|
252
|
+
newEvents: [],
|
253
|
+
confirmedEvents: [],
|
254
|
+
mergeContext: mergeContext,
|
179
255
|
}),
|
180
|
-
|
181
|
-
newEvents: [],
|
182
|
-
}
|
256
|
+
)
|
183
257
|
}
|
184
258
|
|
185
|
-
// Validate that newEvents are sorted in ascending order by
|
259
|
+
// Validate that newEvents are sorted in ascending order by eventNum
|
186
260
|
for (let i = 1; i < payload.newEvents.length; i++) {
|
187
|
-
if (
|
188
|
-
return
|
261
|
+
if (EventSequenceNumber.isGreaterThan(payload.newEvents[i - 1]!.seqNum, payload.newEvents[i]!.seqNum)) {
|
262
|
+
return unexpectedError(
|
263
|
+
`Events must be sorted in ascending order by event number. Received: [${payload.newEvents.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
|
264
|
+
)
|
189
265
|
}
|
190
266
|
}
|
191
267
|
|
192
|
-
|
268
|
+
// Validate that incoming events are larger than upstream head
|
269
|
+
if (
|
270
|
+
EventSequenceNumber.isGreaterThan(syncState.upstreamHead, payload.newEvents[0]!.seqNum) ||
|
271
|
+
EventSequenceNumber.isEqual(syncState.upstreamHead, payload.newEvents[0]!.seqNum)
|
272
|
+
) {
|
273
|
+
return unexpectedError(
|
274
|
+
`Incoming events must be greater than upstream head. Expected greater than: ${EventSequenceNumber.toString(syncState.upstreamHead)}. Received: [${payload.newEvents.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
|
275
|
+
)
|
276
|
+
}
|
277
|
+
|
278
|
+
const newUpstreamHead = payload.newEvents.at(-1)!.seqNum
|
193
279
|
|
194
280
|
const divergentPendingIndex = findDivergencePoint({
|
195
281
|
existingEvents: syncState.pending,
|
196
282
|
incomingEvents: payload.newEvents,
|
197
283
|
isEqualEvent,
|
198
|
-
|
199
|
-
|
284
|
+
isClientEvent,
|
285
|
+
ignoreClientEvents,
|
200
286
|
})
|
201
287
|
|
288
|
+
// No divergent pending events, thus we can just advance (some of) the pending events
|
202
289
|
if (divergentPendingIndex === -1) {
|
203
|
-
const
|
204
|
-
|
290
|
+
const pendingEventSequenceNumbers = new Set(
|
291
|
+
syncState.pending.map((e) => `${e.seqNum.global},${e.seqNum.client}`),
|
292
|
+
)
|
293
|
+
const newEvents = payload.newEvents.filter(
|
294
|
+
(e) => !pendingEventSequenceNumbers.has(`${e.seqNum.global},${e.seqNum.client}`),
|
295
|
+
)
|
205
296
|
|
206
297
|
// In the case where the incoming events are a subset of the pending events,
|
207
298
|
// we need to split the pending events into two groups:
|
208
299
|
// - pendingMatching: The pending events up to point where they match the incoming events
|
209
300
|
// - pendingRemaining: The pending events after the point where they match the incoming events
|
210
|
-
// The `
|
211
|
-
let
|
301
|
+
// The `clientIndexOffset` is used to account for the client events that are being ignored
|
302
|
+
let clientIndexOffset = 0
|
212
303
|
const [pendingMatching, pendingRemaining] = ReadonlyArray.splitWhere(
|
213
304
|
syncState.pending,
|
214
305
|
(pendingEvent, index) => {
|
215
|
-
if (
|
216
|
-
|
306
|
+
if (ignoreClientEvents && isClientEvent(pendingEvent)) {
|
307
|
+
clientIndexOffset++
|
217
308
|
return false
|
218
309
|
}
|
219
310
|
|
220
|
-
const newEvent = payload.newEvents.at(index -
|
311
|
+
const newEvent = payload.newEvents.at(index - clientIndexOffset)
|
221
312
|
if (!newEvent) {
|
222
313
|
return true
|
223
314
|
}
|
@@ -225,105 +316,100 @@ export const updateSyncState = ({
|
|
225
316
|
},
|
226
317
|
)
|
227
318
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
newSyncState: new SyncState({
|
241
|
-
pending: pendingRemaining,
|
242
|
-
rollbackTail: trimRollbackTail([...syncState.rollbackTail, ...pendingAndNewEvents]),
|
243
|
-
upstreamHead: newUpstreamHead,
|
244
|
-
localHead: pendingRemaining.at(-1)?.id ?? newUpstreamHead,
|
319
|
+
return validateMergeResult(
|
320
|
+
MergeResultAdvance.make({
|
321
|
+
_tag: 'advance',
|
322
|
+
newSyncState: new SyncState({
|
323
|
+
pending: pendingRemaining,
|
324
|
+
upstreamHead: newUpstreamHead,
|
325
|
+
localHead:
|
326
|
+
pendingRemaining.at(-1)?.seqNum ?? EventSequenceNumber.max(syncState.localHead, newUpstreamHead),
|
327
|
+
}),
|
328
|
+
newEvents,
|
329
|
+
confirmedEvents: pendingMatching,
|
330
|
+
mergeContext: mergeContext,
|
245
331
|
}),
|
246
|
-
|
247
|
-
newEvents,
|
248
|
-
}
|
332
|
+
)
|
249
333
|
} else {
|
250
334
|
const divergentPending = syncState.pending.slice(divergentPendingIndex)
|
251
335
|
const rebasedPending = rebaseEvents({
|
252
336
|
events: divergentPending,
|
253
|
-
|
254
|
-
|
337
|
+
baseEventSequenceNumber: newUpstreamHead,
|
338
|
+
isClientEvent,
|
255
339
|
})
|
256
340
|
|
257
341
|
const divergentNewEventsIndex = findDivergencePoint({
|
258
342
|
existingEvents: payload.newEvents,
|
259
343
|
incomingEvents: syncState.pending,
|
260
344
|
isEqualEvent,
|
261
|
-
|
262
|
-
|
345
|
+
isClientEvent,
|
346
|
+
ignoreClientEvents,
|
263
347
|
})
|
264
348
|
|
265
|
-
return
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
349
|
+
return validateMergeResult(
|
350
|
+
MergeResultRebase.make({
|
351
|
+
_tag: 'rebase',
|
352
|
+
newSyncState: new SyncState({
|
353
|
+
pending: rebasedPending,
|
354
|
+
upstreamHead: newUpstreamHead,
|
355
|
+
localHead: rebasedPending.at(-1)!.seqNum,
|
356
|
+
}),
|
357
|
+
newEvents: [...payload.newEvents.slice(divergentNewEventsIndex), ...rebasedPending],
|
358
|
+
rollbackEvents: divergentPending,
|
359
|
+
mergeContext,
|
272
360
|
}),
|
273
|
-
|
274
|
-
newEvents: [...payload.newEvents.slice(divergentNewEventsIndex), ...rebasedPending],
|
275
|
-
eventsToRollback: [...syncState.rollbackTail, ...divergentPending],
|
276
|
-
}
|
361
|
+
)
|
277
362
|
}
|
278
363
|
}
|
364
|
+
// #endregion
|
279
365
|
|
366
|
+
// This is the same as what's running in the sync backend
|
280
367
|
case 'local-push': {
|
281
368
|
if (payload.newEvents.length === 0) {
|
282
|
-
return
|
369
|
+
return validateMergeResult(
|
370
|
+
MergeResultAdvance.make({
|
371
|
+
_tag: 'advance',
|
372
|
+
newSyncState: syncState,
|
373
|
+
newEvents: [],
|
374
|
+
confirmedEvents: [],
|
375
|
+
mergeContext: mergeContext,
|
376
|
+
}),
|
377
|
+
)
|
283
378
|
}
|
284
379
|
|
285
380
|
const newEventsFirst = payload.newEvents.at(0)!
|
286
|
-
const
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
381
|
+
const invalidEventSequenceNumber =
|
382
|
+
EventSequenceNumber.isGreaterThan(newEventsFirst.seqNum, syncState.localHead) === false
|
383
|
+
|
384
|
+
if (invalidEventSequenceNumber) {
|
385
|
+
const expectedMinimumId = EventSequenceNumber.nextPair(syncState.localHead, true).seqNum
|
386
|
+
return validateMergeResult(
|
387
|
+
MergeResultReject.make({
|
388
|
+
_tag: 'reject',
|
389
|
+
expectedMinimumId,
|
390
|
+
mergeContext,
|
391
|
+
}),
|
392
|
+
)
|
291
393
|
} else {
|
292
|
-
return
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
394
|
+
return validateMergeResult(
|
395
|
+
MergeResultAdvance.make({
|
396
|
+
_tag: 'advance',
|
397
|
+
newSyncState: new SyncState({
|
398
|
+
pending: [...syncState.pending, ...payload.newEvents],
|
399
|
+
upstreamHead: syncState.upstreamHead,
|
400
|
+
localHead: payload.newEvents.at(-1)!.seqNum,
|
401
|
+
}),
|
402
|
+
newEvents: payload.newEvents,
|
403
|
+
confirmedEvents: [],
|
404
|
+
mergeContext: mergeContext,
|
299
405
|
}),
|
300
|
-
|
301
|
-
newEvents: payload.newEvents,
|
302
|
-
}
|
406
|
+
)
|
303
407
|
}
|
304
408
|
}
|
305
409
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
// if (startIndex === -1) {
|
310
|
-
// return shouldNeverHappen('New rollback start event not found in rollback tail')
|
311
|
-
// }
|
312
|
-
|
313
|
-
// // Keep only the events from the start index onwards
|
314
|
-
// const newRollbackTail = syncState.rollbackTail.slice(startIndex)
|
315
|
-
|
316
|
-
// return {
|
317
|
-
// _tag: 'advance',
|
318
|
-
// syncState: {
|
319
|
-
// pending: syncState.pending,
|
320
|
-
// rollbackTail: newRollbackTail,
|
321
|
-
// upstreamHead: syncState.upstreamHead,
|
322
|
-
// localHead: syncState.localHead,
|
323
|
-
// },
|
324
|
-
// newEvents: [],
|
325
|
-
// }
|
326
|
-
// }
|
410
|
+
default: {
|
411
|
+
casesHandled(payload)
|
412
|
+
}
|
327
413
|
}
|
328
414
|
}
|
329
415
|
|
@@ -331,34 +417,36 @@ export const updateSyncState = ({
|
|
331
417
|
* Gets the index relative to `existingEvents` where the divergence point is
|
332
418
|
* by comparing each event in `existingEvents` to the corresponding event in `incomingEvents`
|
333
419
|
*/
|
334
|
-
const findDivergencePoint = ({
|
420
|
+
export const findDivergencePoint = ({
|
335
421
|
existingEvents,
|
336
422
|
incomingEvents,
|
337
423
|
isEqualEvent,
|
338
|
-
|
339
|
-
|
424
|
+
isClientEvent,
|
425
|
+
ignoreClientEvents,
|
340
426
|
}: {
|
341
|
-
existingEvents: ReadonlyArray<
|
342
|
-
incomingEvents: ReadonlyArray<
|
343
|
-
isEqualEvent: (a:
|
344
|
-
|
345
|
-
|
427
|
+
existingEvents: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
|
428
|
+
incomingEvents: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
|
429
|
+
isEqualEvent: (a: LiveStoreEvent.EncodedWithMeta, b: LiveStoreEvent.EncodedWithMeta) => boolean
|
430
|
+
isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
|
431
|
+
ignoreClientEvents: boolean
|
346
432
|
}): number => {
|
347
|
-
if (
|
348
|
-
const filteredExistingEvents = existingEvents.filter((event) => !
|
349
|
-
const
|
433
|
+
if (ignoreClientEvents) {
|
434
|
+
const filteredExistingEvents = existingEvents.filter((event) => !isClientEvent(event))
|
435
|
+
const divergencePointWithoutClientEvents = findDivergencePoint({
|
350
436
|
existingEvents: filteredExistingEvents,
|
351
437
|
incomingEvents,
|
352
438
|
isEqualEvent,
|
353
|
-
|
354
|
-
|
439
|
+
isClientEvent,
|
440
|
+
ignoreClientEvents: false,
|
355
441
|
})
|
356
442
|
|
357
|
-
if (
|
443
|
+
if (divergencePointWithoutClientEvents === -1) return -1
|
358
444
|
|
359
|
-
const
|
445
|
+
const divergencePointEventSequenceNumber = existingEvents[divergencePointWithoutClientEvents]!.seqNum
|
360
446
|
// Now find the divergence point in the original array
|
361
|
-
return existingEvents.findIndex((event) =>
|
447
|
+
return existingEvents.findIndex((event) =>
|
448
|
+
EventSequenceNumber.isEqual(event.seqNum, divergencePointEventSequenceNumber),
|
449
|
+
)
|
362
450
|
}
|
363
451
|
|
364
452
|
return existingEvents.findIndex((existingEvent, index) => {
|
@@ -370,18 +458,120 @@ const findDivergencePoint = ({
|
|
370
458
|
|
371
459
|
const rebaseEvents = ({
|
372
460
|
events,
|
373
|
-
|
374
|
-
|
461
|
+
baseEventSequenceNumber,
|
462
|
+
isClientEvent,
|
375
463
|
}: {
|
376
|
-
events: ReadonlyArray<
|
377
|
-
|
378
|
-
|
379
|
-
}): ReadonlyArray<
|
380
|
-
let
|
464
|
+
events: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
|
465
|
+
baseEventSequenceNumber: EventSequenceNumber.EventSequenceNumber
|
466
|
+
isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
|
467
|
+
}): ReadonlyArray<LiveStoreEvent.EncodedWithMeta> => {
|
468
|
+
let prevEventSequenceNumber = baseEventSequenceNumber
|
381
469
|
return events.map((event) => {
|
382
|
-
const isLocal =
|
383
|
-
const newEvent = event.rebase(
|
384
|
-
|
470
|
+
const isLocal = isClientEvent(event)
|
471
|
+
const newEvent = event.rebase(prevEventSequenceNumber, isLocal)
|
472
|
+
prevEventSequenceNumber = newEvent.seqNum
|
385
473
|
return newEvent
|
386
474
|
})
|
387
475
|
}
|
476
|
+
|
477
|
+
/**
|
478
|
+
* TODO: Implement this
|
479
|
+
*
|
480
|
+
* In certain scenarios e.g. when the client session has a queue of upstream update results,
|
481
|
+
* it could make sense to "flatten" update results into a single update result which the client session
|
482
|
+
* can process more efficiently which avoids push-threshing
|
483
|
+
*/
|
484
|
+
const _flattenMergeResults = (_updateResults: ReadonlyArray<MergeResult>) => {}
|
485
|
+
|
486
|
+
const validatePayload = (payload: typeof Payload.Type) => {
|
487
|
+
for (let i = 1; i < payload.newEvents.length; i++) {
|
488
|
+
if (EventSequenceNumber.isGreaterThanOrEqual(payload.newEvents[i - 1]!.seqNum, payload.newEvents[i]!.seqNum)) {
|
489
|
+
return unexpectedError(
|
490
|
+
`Events must be ordered in monotonically ascending order by eventNum. Received: [${payload.newEvents.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
|
491
|
+
)
|
492
|
+
}
|
493
|
+
}
|
494
|
+
}
|
495
|
+
|
496
|
+
const validateSyncState = (syncState: SyncState) => {
|
497
|
+
for (let i = 0; i < syncState.pending.length; i++) {
|
498
|
+
const event = syncState.pending[i]!
|
499
|
+
const nextEvent = syncState.pending[i + 1]
|
500
|
+
if (nextEvent === undefined) break // Reached end of chain
|
501
|
+
|
502
|
+
if (EventSequenceNumber.isGreaterThanOrEqual(event.seqNum, nextEvent.seqNum)) {
|
503
|
+
shouldNeverHappen(
|
504
|
+
`Events must be ordered in monotonically ascending order by eventNum. Received: [${syncState.pending.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
|
505
|
+
{
|
506
|
+
event,
|
507
|
+
nextEvent,
|
508
|
+
},
|
509
|
+
)
|
510
|
+
}
|
511
|
+
|
512
|
+
// If the global id has increased, then the client id must be 0
|
513
|
+
const globalIdHasIncreased = nextEvent.seqNum.global > event.seqNum.global
|
514
|
+
if (globalIdHasIncreased) {
|
515
|
+
if (nextEvent.seqNum.client !== 0) {
|
516
|
+
shouldNeverHappen(
|
517
|
+
`New global events must point to clientId 0 in the parentSeqNum. Received: (${EventSequenceNumber.toString(nextEvent.seqNum)})`,
|
518
|
+
syncState.pending,
|
519
|
+
{
|
520
|
+
event,
|
521
|
+
nextEvent,
|
522
|
+
},
|
523
|
+
)
|
524
|
+
}
|
525
|
+
} else {
|
526
|
+
// Otherwise, the parentSeqNum must be the same as the previous event's id
|
527
|
+
if (EventSequenceNumber.isEqual(nextEvent.parentSeqNum, event.seqNum) === false) {
|
528
|
+
shouldNeverHappen('Events must be linked in a continuous chain via the parentSeqNum', syncState.pending, {
|
529
|
+
event,
|
530
|
+
nextEvent,
|
531
|
+
})
|
532
|
+
}
|
533
|
+
}
|
534
|
+
}
|
535
|
+
}
|
536
|
+
|
537
|
+
const validateMergeResult = (mergeResult: typeof MergeResult.Type) => {
|
538
|
+
if (mergeResult._tag === 'unexpected-error' || mergeResult._tag === 'reject') return mergeResult
|
539
|
+
|
540
|
+
validateSyncState(mergeResult.newSyncState)
|
541
|
+
|
542
|
+
// Ensure local head is always greater than or equal to upstream head
|
543
|
+
if (EventSequenceNumber.isGreaterThan(mergeResult.newSyncState.upstreamHead, mergeResult.newSyncState.localHead)) {
|
544
|
+
shouldNeverHappen('Local head must be greater than or equal to upstream head', {
|
545
|
+
localHead: mergeResult.newSyncState.localHead,
|
546
|
+
upstreamHead: mergeResult.newSyncState.upstreamHead,
|
547
|
+
})
|
548
|
+
}
|
549
|
+
|
550
|
+
// Ensure new local head is greater than or equal to the previous local head
|
551
|
+
if (
|
552
|
+
EventSequenceNumber.isGreaterThanOrEqual(
|
553
|
+
mergeResult.newSyncState.localHead,
|
554
|
+
mergeResult.mergeContext.syncState.localHead,
|
555
|
+
) === false
|
556
|
+
) {
|
557
|
+
shouldNeverHappen('New local head must be greater than or equal to the previous local head', {
|
558
|
+
localHead: mergeResult.newSyncState.localHead,
|
559
|
+
previousLocalHead: mergeResult.mergeContext.syncState.localHead,
|
560
|
+
})
|
561
|
+
}
|
562
|
+
|
563
|
+
// Ensure new upstream head is greater than or equal to the previous upstream head
|
564
|
+
if (
|
565
|
+
EventSequenceNumber.isGreaterThanOrEqual(
|
566
|
+
mergeResult.newSyncState.upstreamHead,
|
567
|
+
mergeResult.mergeContext.syncState.upstreamHead,
|
568
|
+
) === false
|
569
|
+
) {
|
570
|
+
shouldNeverHappen('New upstream head must be greater than or equal to the previous upstream head', {
|
571
|
+
upstreamHead: mergeResult.newSyncState.upstreamHead,
|
572
|
+
previousUpstreamHead: mergeResult.mergeContext.syncState.upstreamHead,
|
573
|
+
})
|
574
|
+
}
|
575
|
+
|
576
|
+
return mergeResult
|
577
|
+
}
|