@livestore/common 0.3.0-dev.8 → 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 +45 -30
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +484 -321
- 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 +85 -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 +150 -72
- 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 +213 -82
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +337 -139
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +309 -286
- 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 +738 -477
- 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 +94 -48
- 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 +220 -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 +346 -320
- package/src/sync/syncstate.ts +422 -230
- 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 -166
- 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 -181
- 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,221 +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
|
-
export
|
43
|
-
pending:
|
44
|
-
|
45
|
-
upstreamHead:
|
46
|
-
|
45
|
+
export class SyncState extends Schema.Class<SyncState>('SyncState')({
|
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,
|
51
|
+
}) {
|
52
|
+
toJSON = (): any => ({
|
53
|
+
pending: this.pending.map((e) => e.toJSON()),
|
54
|
+
upstreamHead: EventSequenceNumber.toString(this.upstreamHead),
|
55
|
+
localHead: EventSequenceNumber.toString(this.localHead),
|
56
|
+
})
|
47
57
|
}
|
48
58
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
upstreamHead: EventId.EventId,
|
53
|
-
localHead: EventId.EventId,
|
54
|
-
}).annotations({ title: 'SyncState' })
|
55
|
-
|
59
|
+
/**
|
60
|
+
* This payload propagates a rebase from the upstream node
|
61
|
+
*/
|
56
62
|
export class PayloadUpstreamRebase extends Schema.TaggedStruct('upstream-rebase', {
|
57
|
-
/**
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
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),
|
62
67
|
}) {}
|
63
68
|
|
64
69
|
export class PayloadUpstreamAdvance extends Schema.TaggedStruct('upstream-advance', {
|
65
|
-
newEvents: Schema.Array(
|
66
|
-
/** Trim rollback tail up to this event (inclusive). */
|
67
|
-
trimRollbackUntil: Schema.optional(EventId.EventId),
|
70
|
+
newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
68
71
|
}) {}
|
69
72
|
|
70
73
|
export class PayloadLocalPush extends Schema.TaggedStruct('local-push', {
|
71
|
-
newEvents: Schema.Array(
|
74
|
+
newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
|
72
75
|
}) {}
|
73
76
|
|
74
77
|
export class Payload extends Schema.Union(PayloadUpstreamRebase, PayloadUpstreamAdvance, PayloadLocalPush) {}
|
75
78
|
|
76
|
-
export
|
77
|
-
|
78
|
-
|
79
|
+
export class PayloadUpstream extends Schema.Union(PayloadUpstreamRebase, PayloadUpstreamAdvance) {}
|
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
|
+
}
|
79
109
|
|
80
|
-
export
|
81
|
-
_tag: 'advance'
|
82
|
-
newSyncState: SyncState
|
83
|
-
|
84
|
-
/** Events which
|
85
|
-
|
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
|
+
}
|
86
127
|
}
|
87
128
|
|
88
|
-
export
|
89
|
-
_tag: 'rebase'
|
90
|
-
newSyncState: SyncState
|
91
|
-
|
92
|
-
/** Events which
|
93
|
-
|
94
|
-
|
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
|
+
}
|
95
146
|
}
|
96
147
|
|
97
|
-
export
|
98
|
-
_tag: 'reject'
|
99
|
-
previousSyncState: SyncState
|
148
|
+
export class MergeResultReject extends Schema.Class<MergeResultReject>('MergeResultReject')({
|
149
|
+
_tag: Schema.Literal('reject'),
|
100
150
|
/** The minimum id that the new events must have */
|
101
|
-
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
|
+
}
|
161
|
+
}
|
162
|
+
|
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
|
+
})
|
102
184
|
}
|
103
185
|
|
104
|
-
|
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
|
105
190
|
|
106
|
-
|
191
|
+
TODO: possibly even keep the client events in a separate table in the client leader
|
192
|
+
*/
|
193
|
+
export const merge = ({
|
107
194
|
syncState,
|
108
195
|
payload,
|
109
|
-
|
196
|
+
isClientEvent,
|
110
197
|
isEqualEvent,
|
111
|
-
|
198
|
+
ignoreClientEvents = false,
|
112
199
|
}: {
|
113
200
|
syncState: SyncState
|
114
201
|
payload: typeof Payload.Type
|
115
|
-
|
116
|
-
isEqualEvent: (a:
|
117
|
-
/** This is used in the leader which should ignore
|
118
|
-
|
119
|
-
}):
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
if (trimRollbackUntil === undefined) return rollbackTail
|
125
|
-
const index = rollbackTail.findIndex((event) => EventId.isEqual(event.id, trimRollbackUntil))
|
126
|
-
if (index === -1) return []
|
127
|
-
return rollbackTail.slice(index + 1)
|
128
|
-
}
|
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 })
|
129
211
|
|
130
212
|
switch (payload._tag) {
|
131
213
|
case 'upstream-rebase': {
|
132
|
-
|
133
|
-
const rollbackIndex = syncState.rollbackTail.findIndex((event) =>
|
134
|
-
EventId.isEqual(event.id, payload.rollbackUntil),
|
135
|
-
)
|
136
|
-
if (rollbackIndex === -1) {
|
137
|
-
return shouldNeverHappen(
|
138
|
-
`Rollback event not found in rollback tail. Rollback until: [${payload.rollbackUntil.global},${payload.rollbackUntil.local}]. Rollback tail: [${syncState.rollbackTail.map((e) => e.toString()).join(', ')}]`,
|
139
|
-
)
|
140
|
-
}
|
141
|
-
|
142
|
-
const eventsToRollback = [...syncState.rollbackTail.slice(rollbackIndex), ...syncState.pending]
|
214
|
+
const rollbackEvents = [...payload.rollbackEvents, ...syncState.pending]
|
143
215
|
|
144
216
|
// Get the last new event's ID as the new upstream head
|
145
|
-
const newUpstreamHead = payload.newEvents.at(-1)?.
|
217
|
+
const newUpstreamHead = payload.newEvents.at(-1)?.seqNum ?? syncState.upstreamHead
|
146
218
|
|
147
219
|
// Rebase pending events on top of the new events
|
148
220
|
const rebasedPending = rebaseEvents({
|
149
221
|
events: syncState.pending,
|
150
|
-
|
151
|
-
|
222
|
+
baseEventSequenceNumber: newUpstreamHead,
|
223
|
+
isClientEvent,
|
152
224
|
})
|
153
225
|
|
154
|
-
return
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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,
|
237
|
+
}),
|
238
|
+
)
|
166
239
|
}
|
167
240
|
|
241
|
+
// #region upstream-advance
|
168
242
|
case 'upstream-advance': {
|
169
243
|
if (payload.newEvents.length === 0) {
|
170
|
-
return
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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,
|
255
|
+
}),
|
256
|
+
)
|
181
257
|
}
|
182
258
|
|
183
|
-
// Validate that newEvents are sorted in ascending order by
|
259
|
+
// Validate that newEvents are sorted in ascending order by eventNum
|
184
260
|
for (let i = 1; i < payload.newEvents.length; i++) {
|
185
|
-
if (
|
186
|
-
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
|
+
)
|
187
265
|
}
|
188
266
|
}
|
189
267
|
|
190
|
-
|
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
|
191
279
|
|
192
280
|
const divergentPendingIndex = findDivergencePoint({
|
193
281
|
existingEvents: syncState.pending,
|
194
282
|
incomingEvents: payload.newEvents,
|
195
283
|
isEqualEvent,
|
196
|
-
|
197
|
-
|
284
|
+
isClientEvent,
|
285
|
+
ignoreClientEvents,
|
198
286
|
})
|
199
287
|
|
288
|
+
// No divergent pending events, thus we can just advance (some of) the pending events
|
200
289
|
if (divergentPendingIndex === -1) {
|
201
|
-
const
|
202
|
-
|
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
|
+
)
|
203
296
|
|
204
297
|
// In the case where the incoming events are a subset of the pending events,
|
205
298
|
// we need to split the pending events into two groups:
|
206
299
|
// - pendingMatching: The pending events up to point where they match the incoming events
|
207
300
|
// - pendingRemaining: The pending events after the point where they match the incoming events
|
208
|
-
// The `
|
209
|
-
let
|
301
|
+
// The `clientIndexOffset` is used to account for the client events that are being ignored
|
302
|
+
let clientIndexOffset = 0
|
210
303
|
const [pendingMatching, pendingRemaining] = ReadonlyArray.splitWhere(
|
211
304
|
syncState.pending,
|
212
305
|
(pendingEvent, index) => {
|
213
|
-
if (
|
214
|
-
|
306
|
+
if (ignoreClientEvents && isClientEvent(pendingEvent)) {
|
307
|
+
clientIndexOffset++
|
215
308
|
return false
|
216
309
|
}
|
217
310
|
|
218
|
-
const newEvent = payload.newEvents.at(index -
|
311
|
+
const newEvent = payload.newEvents.at(index - clientIndexOffset)
|
219
312
|
if (!newEvent) {
|
220
313
|
return true
|
221
314
|
}
|
@@ -223,105 +316,100 @@ export const updateSyncState = ({
|
|
223
316
|
},
|
224
317
|
)
|
225
318
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
rollbackTail: trimRollbackTail([...syncState.rollbackTail, ...pendingAndNewEvents]),
|
241
|
-
upstreamHead: newUpstreamHead,
|
242
|
-
localHead: pendingRemaining.at(-1)?.id ?? newUpstreamHead,
|
243
|
-
},
|
244
|
-
previousSyncState: syncState,
|
245
|
-
newEvents,
|
246
|
-
}
|
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,
|
331
|
+
}),
|
332
|
+
)
|
247
333
|
} else {
|
248
334
|
const divergentPending = syncState.pending.slice(divergentPendingIndex)
|
249
335
|
const rebasedPending = rebaseEvents({
|
250
336
|
events: divergentPending,
|
251
|
-
|
252
|
-
|
337
|
+
baseEventSequenceNumber: newUpstreamHead,
|
338
|
+
isClientEvent,
|
253
339
|
})
|
254
340
|
|
255
341
|
const divergentNewEventsIndex = findDivergencePoint({
|
256
342
|
existingEvents: payload.newEvents,
|
257
343
|
incomingEvents: syncState.pending,
|
258
344
|
isEqualEvent,
|
259
|
-
|
260
|
-
|
345
|
+
isClientEvent,
|
346
|
+
ignoreClientEvents,
|
261
347
|
})
|
262
348
|
|
263
|
-
return
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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,
|
360
|
+
}),
|
361
|
+
)
|
275
362
|
}
|
276
363
|
}
|
364
|
+
// #endregion
|
277
365
|
|
366
|
+
// This is the same as what's running in the sync backend
|
278
367
|
case 'local-push': {
|
279
368
|
if (payload.newEvents.length === 0) {
|
280
|
-
return
|
369
|
+
return validateMergeResult(
|
370
|
+
MergeResultAdvance.make({
|
371
|
+
_tag: 'advance',
|
372
|
+
newSyncState: syncState,
|
373
|
+
newEvents: [],
|
374
|
+
confirmedEvents: [],
|
375
|
+
mergeContext: mergeContext,
|
376
|
+
}),
|
377
|
+
)
|
281
378
|
}
|
282
379
|
|
283
380
|
const newEventsFirst = payload.newEvents.at(0)!
|
284
|
-
const
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
+
)
|
289
393
|
} else {
|
290
|
-
return
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
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,
|
405
|
+
}),
|
406
|
+
)
|
301
407
|
}
|
302
408
|
}
|
303
409
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
// if (startIndex === -1) {
|
308
|
-
// return shouldNeverHappen('New rollback start event not found in rollback tail')
|
309
|
-
// }
|
310
|
-
|
311
|
-
// // Keep only the events from the start index onwards
|
312
|
-
// const newRollbackTail = syncState.rollbackTail.slice(startIndex)
|
313
|
-
|
314
|
-
// return {
|
315
|
-
// _tag: 'advance',
|
316
|
-
// syncState: {
|
317
|
-
// pending: syncState.pending,
|
318
|
-
// rollbackTail: newRollbackTail,
|
319
|
-
// upstreamHead: syncState.upstreamHead,
|
320
|
-
// localHead: syncState.localHead,
|
321
|
-
// },
|
322
|
-
// newEvents: [],
|
323
|
-
// }
|
324
|
-
// }
|
410
|
+
default: {
|
411
|
+
casesHandled(payload)
|
412
|
+
}
|
325
413
|
}
|
326
414
|
}
|
327
415
|
|
@@ -329,34 +417,36 @@ export const updateSyncState = ({
|
|
329
417
|
* Gets the index relative to `existingEvents` where the divergence point is
|
330
418
|
* by comparing each event in `existingEvents` to the corresponding event in `incomingEvents`
|
331
419
|
*/
|
332
|
-
const findDivergencePoint = ({
|
420
|
+
export const findDivergencePoint = ({
|
333
421
|
existingEvents,
|
334
422
|
incomingEvents,
|
335
423
|
isEqualEvent,
|
336
|
-
|
337
|
-
|
424
|
+
isClientEvent,
|
425
|
+
ignoreClientEvents,
|
338
426
|
}: {
|
339
|
-
existingEvents: ReadonlyArray<
|
340
|
-
incomingEvents: ReadonlyArray<
|
341
|
-
isEqualEvent: (a:
|
342
|
-
|
343
|
-
|
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
|
344
432
|
}): number => {
|
345
|
-
if (
|
346
|
-
const filteredExistingEvents = existingEvents.filter((event) => !
|
347
|
-
const
|
433
|
+
if (ignoreClientEvents) {
|
434
|
+
const filteredExistingEvents = existingEvents.filter((event) => !isClientEvent(event))
|
435
|
+
const divergencePointWithoutClientEvents = findDivergencePoint({
|
348
436
|
existingEvents: filteredExistingEvents,
|
349
437
|
incomingEvents,
|
350
438
|
isEqualEvent,
|
351
|
-
|
352
|
-
|
439
|
+
isClientEvent,
|
440
|
+
ignoreClientEvents: false,
|
353
441
|
})
|
354
442
|
|
355
|
-
if (
|
443
|
+
if (divergencePointWithoutClientEvents === -1) return -1
|
356
444
|
|
357
|
-
const
|
445
|
+
const divergencePointEventSequenceNumber = existingEvents[divergencePointWithoutClientEvents]!.seqNum
|
358
446
|
// Now find the divergence point in the original array
|
359
|
-
return existingEvents.findIndex((event) =>
|
447
|
+
return existingEvents.findIndex((event) =>
|
448
|
+
EventSequenceNumber.isEqual(event.seqNum, divergencePointEventSequenceNumber),
|
449
|
+
)
|
360
450
|
}
|
361
451
|
|
362
452
|
return existingEvents.findIndex((existingEvent, index) => {
|
@@ -368,18 +458,120 @@ const findDivergencePoint = ({
|
|
368
458
|
|
369
459
|
const rebaseEvents = ({
|
370
460
|
events,
|
371
|
-
|
372
|
-
|
461
|
+
baseEventSequenceNumber,
|
462
|
+
isClientEvent,
|
373
463
|
}: {
|
374
|
-
events: ReadonlyArray<
|
375
|
-
|
376
|
-
|
377
|
-
}): ReadonlyArray<
|
378
|
-
let
|
464
|
+
events: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
|
465
|
+
baseEventSequenceNumber: EventSequenceNumber.EventSequenceNumber
|
466
|
+
isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
|
467
|
+
}): ReadonlyArray<LiveStoreEvent.EncodedWithMeta> => {
|
468
|
+
let prevEventSequenceNumber = baseEventSequenceNumber
|
379
469
|
return events.map((event) => {
|
380
|
-
const isLocal =
|
381
|
-
const newEvent = event.rebase(
|
382
|
-
|
470
|
+
const isLocal = isClientEvent(event)
|
471
|
+
const newEvent = event.rebase(prevEventSequenceNumber, isLocal)
|
472
|
+
prevEventSequenceNumber = newEvent.seqNum
|
383
473
|
return newEvent
|
384
474
|
})
|
385
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
|
+
}
|