@livestore/common 0.4.0-dev.2 → 0.4.0-dev.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/ClientSessionLeaderThreadProxy.d.ts +20 -12
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
- package/dist/adapter-types.d.ts +14 -6
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/debug-info.d.ts.map +1 -1
- package/dist/debug-info.js +33 -6
- package/dist/debug-info.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +28 -23
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.js +2 -2
- package/dist/devtools/devtools-messages-client-session.js.map +1 -1
- package/dist/devtools/devtools-messages-common.d.ts +7 -14
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-common.js +1 -6
- package/dist/devtools/devtools-messages-common.js.map +1 -1
- package/dist/devtools/devtools-messages-leader.d.ts +38 -29
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +9 -8
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/devtools/devtools-sessioninfo.d.ts +14 -2
- package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -1
- package/dist/devtools/devtools-sessioninfo.js +7 -4
- package/dist/devtools/devtools-sessioninfo.js.map +1 -1
- package/dist/devtools/mod.d.ts +13 -2
- package/dist/devtools/mod.d.ts.map +1 -1
- package/dist/devtools/mod.js +10 -3
- package/dist/devtools/mod.js.map +1 -1
- package/dist/errors.d.ts +52 -10
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +25 -6
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +41 -4
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +158 -75
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts +21 -22
- package/dist/leader-thread/eventlog.d.ts.map +1 -1
- package/dist/leader-thread/eventlog.js +77 -20
- package/dist/leader-thread/eventlog.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +2 -2
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +56 -45
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +6 -6
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +79 -27
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts +2 -0
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts.map +1 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js +32 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -0
- package/dist/leader-thread/materialize-event.d.ts +3 -3
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +25 -11
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/leader-thread/mod.d.ts +1 -0
- package/dist/leader-thread/mod.d.ts.map +1 -1
- package/dist/leader-thread/mod.js +1 -0
- package/dist/leader-thread/mod.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts +2 -3
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +5 -5
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/shutdown-channel.d.ts +2 -2
- package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
- package/dist/leader-thread/shutdown-channel.js +2 -2
- package/dist/leader-thread/shutdown-channel.js.map +1 -1
- package/dist/leader-thread/stream-events.d.ts +56 -0
- package/dist/leader-thread/stream-events.d.ts.map +1 -0
- package/dist/leader-thread/stream-events.js +166 -0
- package/dist/leader-thread/stream-events.js.map +1 -0
- package/dist/leader-thread/types.d.ts +98 -20
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js +13 -0
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/logging.d.ts +40 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +33 -0
- package/dist/logging.js.map +1 -0
- package/dist/make-client-session.d.ts +5 -3
- package/dist/make-client-session.d.ts.map +1 -1
- package/dist/make-client-session.js +5 -2
- package/dist/make-client-session.js.map +1 -1
- package/dist/materializer-helper.d.ts +6 -6
- package/dist/materializer-helper.d.ts.map +1 -1
- package/dist/materializer-helper.js +20 -4
- package/dist/materializer-helper.js.map +1 -1
- package/dist/otel.d.ts +2 -1
- 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 +2 -2
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
- package/dist/rematerialize-from-eventlog.js +29 -20
- package/dist/rematerialize-from-eventlog.js.map +1 -1
- package/dist/schema/EventDef/define.d.ts +147 -0
- package/dist/schema/EventDef/define.d.ts.map +1 -0
- package/dist/schema/EventDef/define.js +139 -0
- package/dist/schema/EventDef/define.js.map +1 -0
- package/dist/schema/EventDef/event-def.d.ts +106 -0
- package/dist/schema/EventDef/event-def.d.ts.map +1 -0
- package/dist/schema/EventDef/event-def.js +2 -0
- package/dist/schema/EventDef/event-def.js.map +1 -0
- package/dist/schema/EventDef/facts.d.ts +118 -0
- package/dist/schema/EventDef/facts.d.ts.map +1 -0
- package/dist/schema/EventDef/facts.js +53 -0
- package/dist/schema/EventDef/facts.js.map +1 -0
- package/dist/schema/EventDef/materializer.d.ts +155 -0
- package/dist/schema/EventDef/materializer.d.ts.map +1 -0
- package/dist/schema/EventDef/materializer.js +83 -0
- package/dist/schema/EventDef/materializer.js.map +1 -0
- package/dist/schema/EventDef/mod.d.ts +5 -0
- package/dist/schema/EventDef/mod.d.ts.map +1 -0
- package/dist/schema/EventDef/mod.js +5 -0
- package/dist/schema/EventDef/mod.js.map +1 -0
- package/dist/schema/EventSequenceNumber/client.d.ts +136 -0
- package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/client.js +193 -0
- package/dist/schema/EventSequenceNumber/client.js.map +1 -0
- package/dist/schema/EventSequenceNumber/global.d.ts +15 -0
- package/dist/schema/EventSequenceNumber/global.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/global.js +14 -0
- package/dist/schema/EventSequenceNumber/global.js.map +1 -0
- package/dist/schema/EventSequenceNumber/mod.d.ts +37 -0
- package/dist/schema/EventSequenceNumber/mod.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/mod.js +37 -0
- package/dist/schema/EventSequenceNumber/mod.js.map +1 -0
- package/dist/schema/EventSequenceNumber.test.js +43 -43
- package/dist/schema/EventSequenceNumber.test.js.map +1 -1
- package/dist/schema/{LiveStoreEvent.d.ts → LiveStoreEvent/client.d.ts} +89 -106
- package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -0
- package/dist/schema/{LiveStoreEvent.js → LiveStoreEvent/client.js} +74 -58
- package/dist/schema/LiveStoreEvent/client.js.map +1 -0
- package/dist/schema/LiveStoreEvent/for-event-def.d.ts +52 -0
- package/dist/schema/LiveStoreEvent/for-event-def.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/for-event-def.js +2 -0
- package/dist/schema/LiveStoreEvent/for-event-def.js.map +1 -0
- package/dist/schema/LiveStoreEvent/global.d.ts +36 -0
- package/dist/schema/LiveStoreEvent/global.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/global.js +31 -0
- package/dist/schema/LiveStoreEvent/global.js.map +1 -0
- package/dist/schema/LiveStoreEvent/input.d.ts +46 -0
- package/dist/schema/LiveStoreEvent/input.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/input.js +26 -0
- package/dist/schema/LiveStoreEvent/input.js.map +1 -0
- package/dist/schema/LiveStoreEvent/mod.d.ts +5 -0
- package/dist/schema/LiveStoreEvent/mod.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/mod.js +5 -0
- package/dist/schema/LiveStoreEvent/mod.js.map +1 -0
- package/dist/schema/events.d.ts +1 -1
- package/dist/schema/events.d.ts.map +1 -1
- package/dist/schema/events.js +1 -1
- package/dist/schema/events.js.map +1 -1
- package/dist/schema/mod.d.ts +6 -4
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +5 -4
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +16 -1
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +27 -2
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +36 -6
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +97 -6
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +16 -0
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.js +14 -6
- package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.test.js +1 -1
- package/dist/schema/state/sqlite/column-annotations.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.js +69 -22
- package/dist/schema/state/sqlite/column-def.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.test.js +48 -10
- package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-spec.js +30 -12
- package/dist/schema/state/sqlite/column-spec.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.test.js +23 -14
- package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +2 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +23 -6
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +14 -8
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +5 -3
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js +2 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts +3 -3
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +3 -3
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +19 -11
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.js +22 -15
- package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.js +6 -3
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.test.js +252 -88
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.d.ts +2 -2
- package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.js +22 -12
- package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.test.d.ts +2 -0
- package/dist/schema/state/sqlite/schema-helpers.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/schema-helpers.test.js +36 -0
- package/dist/schema/state/sqlite/schema-helpers.test.js.map +1 -0
- package/dist/schema/state/sqlite/{system-tables.d.ts → system-tables/eventlog-tables.d.ts} +63 -456
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js +54 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables/mod.d.ts +3 -0
- package/dist/schema/state/sqlite/system-tables/mod.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/mod.js +3 -0
- package/dist/schema/state/sqlite/system-tables/mod.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.d.ts +456 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.js +55 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.js.map +1 -0
- package/dist/schema/state/sqlite/table-def.d.ts +4 -4
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.js +2 -2
- package/dist/schema/state/sqlite/table-def.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.test.js +80 -0
- package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
- package/dist/schema/unknown-events.d.ts +47 -0
- package/dist/schema/unknown-events.d.ts.map +1 -0
- package/dist/schema/unknown-events.js +69 -0
- package/dist/schema/unknown-events.js.map +1 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts +2 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts.map +1 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +73 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -0
- package/dist/schema-management/migrations.d.ts +32 -2
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +37 -5
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/schema-management/validate-schema.d.ts +3 -3
- package/dist/schema-management/validate-schema.d.ts.map +1 -1
- package/dist/schema-management/validate-schema.js +2 -2
- package/dist/schema-management/validate-schema.js.map +1 -1
- package/dist/sql-queries/sql-queries.d.ts.map +1 -1
- package/dist/sql-queries/sql-queries.js +11 -1
- package/dist/sql-queries/sql-queries.js.map +1 -1
- package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
- package/dist/sql-queries/sql-query-builder.js +2 -1
- package/dist/sql-queries/sql-query-builder.js.map +1 -1
- package/dist/sqlite-types.d.ts +3 -3
- package/dist/sqlite-types.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +11 -13
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +45 -42
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/errors.d.ts +66 -0
- package/dist/sync/errors.d.ts.map +1 -0
- package/dist/sync/errors.js +36 -0
- package/dist/sync/errors.js.map +1 -0
- package/dist/sync/index.d.ts +3 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +3 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/mock-sync-backend.d.ts +23 -0
- package/dist/sync/mock-sync-backend.d.ts.map +1 -0
- package/dist/sync/mock-sync-backend.js +114 -0
- package/dist/sync/mock-sync-backend.js.map +1 -0
- package/dist/sync/next/compact-events.d.ts.map +1 -1
- package/dist/sync/next/compact-events.js +6 -7
- package/dist/sync/next/compact-events.js.map +1 -1
- package/dist/sync/next/facts.d.ts +5 -5
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +1 -2
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +54 -15
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +198 -9
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.d.ts.map +1 -1
- package/dist/sync/next/history-dag.js +10 -8
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.d.ts +5 -5
- package/dist/sync/next/rebase-events.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.js +5 -5
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/event-fixtures.d.ts +2 -2
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
- package/dist/sync/next/test/event-fixtures.js +9 -9
- package/dist/sync/next/test/event-fixtures.js.map +1 -1
- package/dist/sync/sync-backend-kv.d.ts +7 -0
- package/dist/sync/sync-backend-kv.d.ts.map +1 -0
- package/dist/sync/sync-backend-kv.js +18 -0
- package/dist/sync/sync-backend-kv.js.map +1 -0
- package/dist/sync/sync-backend.d.ts +105 -0
- package/dist/sync/sync-backend.d.ts.map +1 -0
- package/dist/sync/sync-backend.js +61 -0
- package/dist/sync/sync-backend.js.map +1 -0
- package/dist/sync/sync.d.ts +9 -86
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js +2 -27
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/syncstate.d.ts +57 -44
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +50 -45
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +83 -46
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/transport-chunking.d.ts +36 -0
- package/dist/sync/transport-chunking.d.ts.map +1 -0
- package/dist/sync/transport-chunking.js +56 -0
- package/dist/sync/transport-chunking.js.map +1 -0
- package/dist/sync/validate-push-payload.d.ts +2 -2
- package/dist/sync/validate-push-payload.d.ts.map +1 -1
- package/dist/sync/validate-push-payload.js +6 -6
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/testing/event-factory.d.ts +68 -0
- package/dist/testing/event-factory.d.ts.map +1 -0
- package/dist/testing/event-factory.js +78 -0
- package/dist/testing/event-factory.js.map +1 -0
- package/dist/testing/mod.d.ts +2 -0
- package/dist/testing/mod.d.ts.map +1 -0
- package/dist/testing/mod.js +2 -0
- package/dist/testing/mod.js.map +1 -0
- package/dist/version.d.ts +16 -6
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +16 -6
- package/dist/version.js.map +1 -1
- package/package.json +7 -8
- package/src/ClientSessionLeaderThreadProxy.ts +20 -12
- package/src/adapter-types.ts +18 -6
- package/src/debug-info.ts +37 -6
- package/src/devtools/devtools-messages-client-session.ts +2 -2
- package/src/devtools/devtools-messages-common.ts +1 -8
- package/src/devtools/devtools-messages-leader.ts +9 -8
- package/src/devtools/devtools-sessioninfo.ts +8 -5
- package/src/devtools/mod.ts +11 -2
- package/src/errors.ts +38 -11
- package/src/index.ts +2 -1
- package/src/leader-thread/LeaderSyncProcessor.ts +277 -105
- package/src/leader-thread/eventlog.ts +113 -38
- package/src/leader-thread/leader-worker-devtools.ts +86 -55
- package/src/leader-thread/make-leader-thread-layer.test.ts +44 -0
- package/src/leader-thread/make-leader-thread-layer.ts +156 -37
- package/src/leader-thread/materialize-event.ts +37 -12
- package/src/leader-thread/mod.ts +1 -0
- package/src/leader-thread/recreate-db.ts +15 -7
- package/src/leader-thread/shutdown-channel.ts +16 -2
- package/src/leader-thread/stream-events.ts +201 -0
- package/src/leader-thread/types.ts +70 -20
- package/src/logging.ts +62 -0
- package/src/make-client-session.ts +9 -3
- package/src/materializer-helper.ts +27 -10
- package/src/otel.ts +10 -0
- package/src/rematerialize-from-eventlog.ts +37 -27
- package/src/schema/EventDef/define.ts +201 -0
- package/src/schema/EventDef/event-def.ts +120 -0
- package/src/schema/EventDef/facts.ts +135 -0
- package/src/schema/EventDef/materializer.ts +172 -0
- package/src/schema/EventDef/mod.ts +4 -0
- package/src/schema/EventSequenceNumber/client.ts +257 -0
- package/src/schema/EventSequenceNumber/global.ts +19 -0
- package/src/schema/EventSequenceNumber/mod.ts +37 -0
- package/src/schema/EventSequenceNumber.test.ts +70 -52
- package/src/schema/LiveStoreEvent/client.ts +221 -0
- package/src/schema/LiveStoreEvent/for-event-def.ts +60 -0
- package/src/schema/LiveStoreEvent/global.ts +45 -0
- package/src/schema/LiveStoreEvent/input.ts +63 -0
- package/src/schema/LiveStoreEvent/mod.ts +4 -0
- package/src/schema/events.ts +1 -1
- package/src/schema/mod.ts +6 -4
- package/src/schema/schema.ts +39 -3
- package/src/schema/state/sqlite/client-document-def.test.ts +19 -2
- package/src/schema/state/sqlite/client-document-def.ts +127 -25
- package/src/schema/state/sqlite/column-annotations.test.ts +1 -1
- package/src/schema/state/sqlite/column-annotations.ts +16 -6
- package/src/schema/state/sqlite/column-def.test.ts +62 -10
- package/src/schema/state/sqlite/column-def.ts +88 -21
- package/src/schema/state/sqlite/column-spec.test.ts +29 -16
- package/src/schema/state/sqlite/column-spec.ts +36 -11
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +26 -6
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +29 -12
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +12 -17
- package/src/schema/state/sqlite/mod.ts +4 -3
- package/src/schema/state/sqlite/query-builder/api.ts +25 -11
- package/src/schema/state/sqlite/query-builder/astToSql.ts +23 -14
- package/src/schema/state/sqlite/query-builder/impl.test.ts +305 -92
- package/src/schema/state/sqlite/query-builder/impl.ts +8 -3
- package/src/schema/state/sqlite/schema-helpers.test.ts +44 -0
- package/src/schema/state/sqlite/schema-helpers.ts +28 -20
- package/src/schema/state/sqlite/system-tables/eventlog-tables.ts +64 -0
- package/src/schema/state/sqlite/system-tables/mod.ts +2 -0
- package/src/schema/state/sqlite/system-tables/state-tables.ts +69 -0
- package/src/schema/state/sqlite/table-def.test.ts +101 -0
- package/src/schema/state/sqlite/table-def.ts +8 -6
- package/src/schema/unknown-events.ts +131 -0
- package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +86 -0
- package/src/schema-management/migrations.ts +41 -8
- package/src/schema-management/validate-schema.ts +3 -3
- package/src/sql-queries/sql-queries.ts +9 -1
- package/src/sql-queries/sql-query-builder.ts +2 -1
- package/src/sqlite-types.ts +3 -3
- package/src/sync/ClientSessionSyncProcessor.ts +69 -62
- package/src/sync/errors.ts +38 -0
- package/src/sync/index.ts +3 -0
- package/src/sync/mock-sync-backend.ts +184 -0
- package/src/sync/next/compact-events.ts +6 -7
- package/src/sync/next/facts.ts +7 -9
- package/src/sync/next/history-dag-common.ts +277 -26
- package/src/sync/next/history-dag.ts +16 -10
- package/src/sync/next/rebase-events.ts +11 -11
- package/src/sync/next/test/event-fixtures.ts +11 -11
- package/src/sync/sync-backend-kv.ts +22 -0
- package/src/sync/sync-backend.ts +185 -0
- package/src/sync/sync.ts +9 -91
- package/src/sync/syncstate.test.ts +96 -52
- package/src/sync/syncstate.ts +69 -58
- package/src/sync/transport-chunking.ts +90 -0
- package/src/sync/validate-push-payload.ts +8 -9
- package/src/testing/event-factory.ts +131 -0
- package/src/testing/mod.ts +1 -0
- package/src/version.ts +16 -6
- package/dist/schema/EventDef.d.ts +0 -123
- package/dist/schema/EventDef.d.ts.map +0 -1
- package/dist/schema/EventDef.js +0 -46
- package/dist/schema/EventDef.js.map +0 -1
- package/dist/schema/EventSequenceNumber.d.ts +0 -80
- package/dist/schema/EventSequenceNumber.d.ts.map +0 -1
- package/dist/schema/EventSequenceNumber.js +0 -139
- package/dist/schema/EventSequenceNumber.js.map +0 -1
- package/dist/schema/LiveStoreEvent.d.ts.map +0 -1
- package/dist/schema/LiveStoreEvent.js.map +0 -1
- package/dist/schema/state/sqlite/system-tables.d.ts.map +0 -1
- package/dist/schema/state/sqlite/system-tables.js +0 -79
- package/dist/schema/state/sqlite/system-tables.js.map +0 -1
- package/dist/schema-management/migrations.test.d.ts +0 -2
- package/dist/schema-management/migrations.test.d.ts.map +0 -1
- package/dist/schema-management/migrations.test.js +0 -52
- package/dist/schema-management/migrations.test.js.map +0 -1
- package/dist/sync/next/graphology.d.ts +0 -8
- package/dist/sync/next/graphology.d.ts.map +0 -1
- package/dist/sync/next/graphology.js +0 -30
- package/dist/sync/next/graphology.js.map +0 -1
- package/dist/sync/next/graphology_.d.ts +0 -3
- package/dist/sync/next/graphology_.d.ts.map +0 -1
- package/dist/sync/next/graphology_.js +0 -3
- package/dist/sync/next/graphology_.js.map +0 -1
- package/src/schema/EventDef.ts +0 -219
- package/src/schema/EventSequenceNumber.ts +0 -199
- package/src/schema/LiveStoreEvent.ts +0 -287
- package/src/schema/state/sqlite/system-tables.ts +0 -104
- package/src/sync/next/ambient.d.ts +0 -3
- package/src/sync/next/graphology.ts +0 -41
- package/src/sync/next/graphology_.ts +0 -2
|
@@ -1,20 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTOMATIC HASH-BASED SCHEMA MIGRATIONS
|
|
3
|
+
*
|
|
4
|
+
* This module implements automatic schema versioning using hash-based change detection.
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ CRITICAL DISTINCTION:
|
|
7
|
+
* - STATE TABLES (safe to modify): Changes trigger rematerialization from eventlog
|
|
8
|
+
* - EVENTLOG TABLES (NEVER modify): Changes cause data loss - need manual versioning!
|
|
9
|
+
*
|
|
10
|
+
* How it works:
|
|
11
|
+
* 1. Each table's schema is hashed using SqliteAst.hash()
|
|
12
|
+
* 2. Hashes are stored in SCHEMA_META_TABLE after successful migrations
|
|
13
|
+
* 3. On app start, current schema hashes are compared with stored hashes
|
|
14
|
+
* 4. Mismatches trigger migrations:
|
|
15
|
+
* - State tables: Recreated and repopulated from eventlog (safe, no data loss)
|
|
16
|
+
* - Eventlog tables: Uses 'create-if-not-exists' (UNSAFE - causes data loss!)
|
|
17
|
+
*
|
|
18
|
+
* State Table Changes (SAFE):
|
|
19
|
+
* - User-defined tables are rebuilt from eventlog
|
|
20
|
+
* - System tables (schemaMetaTable, etc.) are recreated
|
|
21
|
+
* - Data preserved through rematerializeFromEventlog()
|
|
22
|
+
*
|
|
23
|
+
* Eventlog Table Changes (UNSAFE):
|
|
24
|
+
* - eventlogMetaTable, syncStatusTable changes cause "soft reset"
|
|
25
|
+
* - Old table becomes inaccessible (but remains in DB)
|
|
26
|
+
* - No automatic migration - effectively data loss
|
|
27
|
+
* - TODO: Implement proper EVENTLOG_PERSISTENCE_FORMAT_VERSION system
|
|
28
|
+
*
|
|
29
|
+
* See system-tables/state-tables.ts and system-tables/eventlog-tables.ts for detailed documentation on each table type.
|
|
30
|
+
*/
|
|
31
|
+
|
|
1
32
|
import { memoizeByStringifyArgs } from '@livestore/utils'
|
|
2
33
|
import { Effect } from '@livestore/utils/effect'
|
|
3
34
|
|
|
4
35
|
import type { SqliteDb } from '../adapter-types.ts'
|
|
5
36
|
import type { MigrationsReport, MigrationsReportEntry } from '../defs.ts'
|
|
6
|
-
import type {
|
|
37
|
+
import type { UnknownError } from '../errors.ts'
|
|
7
38
|
import type { LiveStoreSchema } from '../schema/mod.ts'
|
|
8
39
|
import { makeColumnSpec } from '../schema/state/sqlite/column-spec.ts'
|
|
9
40
|
import { SqliteAst } from '../schema/state/sqlite/db-schema/mod.ts'
|
|
10
|
-
import type { SchemaEventDefsMetaRow, SchemaMetaRow } from '../schema/state/sqlite/system-tables.ts'
|
|
41
|
+
import type { SchemaEventDefsMetaRow, SchemaMetaRow } from '../schema/state/sqlite/system-tables/state-tables.ts'
|
|
11
42
|
import {
|
|
12
43
|
isStateSystemTable,
|
|
13
44
|
SCHEMA_EVENT_DEFS_META_TABLE,
|
|
14
45
|
SCHEMA_META_TABLE,
|
|
15
46
|
schemaEventDefsMetaTable,
|
|
16
47
|
stateSystemTables,
|
|
17
|
-
} from '../schema/state/sqlite/system-tables.ts'
|
|
48
|
+
} from '../schema/state/sqlite/system-tables/state-tables.ts'
|
|
18
49
|
import { sql } from '../util.ts'
|
|
19
50
|
import type { SchemaManager } from './common.ts'
|
|
20
51
|
import { dbExecute, dbSelect } from './common.ts'
|
|
@@ -56,7 +87,7 @@ export const migrateDb = ({
|
|
|
56
87
|
db: SqliteDb
|
|
57
88
|
schema: LiveStoreSchema
|
|
58
89
|
onProgress?: (opts: { done: number; total: number }) => Effect.Effect<void>
|
|
59
|
-
}): Effect.Effect<MigrationsReport,
|
|
90
|
+
}): Effect.Effect<MigrationsReport, UnknownError> =>
|
|
60
91
|
Effect.gen(function* () {
|
|
61
92
|
for (const tableDef of stateSystemTables) {
|
|
62
93
|
yield* migrateTable({
|
|
@@ -137,10 +168,10 @@ export const migrateTable = ({
|
|
|
137
168
|
|
|
138
169
|
if (behaviour === 'drop-and-recreate') {
|
|
139
170
|
// TODO need to possibly handle cascading deletes due to foreign keys
|
|
140
|
-
dbExecute(db, sql`drop table if exists
|
|
141
|
-
dbExecute(db, sql`create table if not exists
|
|
171
|
+
dbExecute(db, sql`drop table if exists "${tableName}"`)
|
|
172
|
+
dbExecute(db, sql`create table if not exists "${tableName}" (${columnSpec}) strict`)
|
|
142
173
|
} else if (behaviour === 'create-if-not-exists') {
|
|
143
|
-
dbExecute(db, sql`create table if not exists
|
|
174
|
+
dbExecute(db, sql`create table if not exists "${tableName}" (${columnSpec}) strict`)
|
|
144
175
|
}
|
|
145
176
|
|
|
146
177
|
for (const index of tableAst.indexes) {
|
|
@@ -170,5 +201,7 @@ export const migrateTable = ({
|
|
|
170
201
|
|
|
171
202
|
const createIndexFromDefinition = (tableName: string, index: SqliteAst.Index) => {
|
|
172
203
|
const uniqueStr = index.unique ? 'UNIQUE' : ''
|
|
173
|
-
return sql`create ${uniqueStr} index if not exists
|
|
204
|
+
return sql`create ${uniqueStr} index if not exists "${index.name}" on "${tableName}" (${index.columns
|
|
205
|
+
.map((col) => `"${col}"`)
|
|
206
|
+
.join(', ')})`
|
|
174
207
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, Schema } from '@livestore/utils/effect'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import type { EventDef } from '../schema/EventDef.ts'
|
|
3
|
+
import { UnknownError } from '../adapter-types.ts'
|
|
4
|
+
import type { EventDef } from '../schema/EventDef/mod.ts'
|
|
5
5
|
import type { LiveStoreSchema } from '../schema/mod.ts'
|
|
6
6
|
import type { EventDefInfo, SchemaManager } from './common.ts'
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ export const validateSchema = (schema: LiveStoreSchema, schemaManager: SchemaMan
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
if (missingEventDefs.length > 0) {
|
|
18
|
-
return yield* new
|
|
18
|
+
return yield* new UnknownError({
|
|
19
19
|
cause: `Missing mutation definitions: ${missingEventDefs.map((info) => info.eventName).join(', ')}`,
|
|
20
20
|
})
|
|
21
21
|
}
|
|
@@ -247,7 +247,15 @@ export const createTable = ({
|
|
|
247
247
|
.map(([columnName, _]) => columnName)
|
|
248
248
|
const columnDefStrs = Object.entries(table.columns).map(([columnName, columnDef]) => {
|
|
249
249
|
const nullModifier = columnDef.nullable === true ? '' : 'NOT NULL'
|
|
250
|
-
const defaultModifier =
|
|
250
|
+
const defaultModifier = (() => {
|
|
251
|
+
if (columnDef.default._tag === 'None') return ''
|
|
252
|
+
const defaultValue = columnDef.default.value
|
|
253
|
+
if (typeof defaultValue === 'function') return ''
|
|
254
|
+
if (defaultValue && typeof defaultValue === 'object' && 'sql' in defaultValue) {
|
|
255
|
+
return `DEFAULT ${defaultValue.sql}`
|
|
256
|
+
}
|
|
257
|
+
return `DEFAULT ${defaultValue}`
|
|
258
|
+
})()
|
|
251
259
|
return sql`${columnName} ${columnDef.columnType} ${nullModifier} ${defaultModifier}`
|
|
252
260
|
})
|
|
253
261
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { omitUndefineds } from '@livestore/utils'
|
|
1
2
|
import type { SqliteDsl } from '../schema/state/sqlite/db-schema/mod.ts'
|
|
2
3
|
import type { BindValues } from './sql-queries.ts'
|
|
3
4
|
import * as SqlQueries from './sql-queries.ts'
|
|
@@ -16,7 +17,7 @@ export const makeSqlQueryBuilder = <TSchema extends SqliteDsl.DbSchema>(schema:
|
|
|
16
17
|
limit?: number
|
|
17
18
|
}): [string, BindValues, TTableName] => {
|
|
18
19
|
const columns = schema[tableName]!.columns
|
|
19
|
-
const [stmt, bindValues] = SqlQueries.findManyRows({ columns, tableName, where, limit })
|
|
20
|
+
const [stmt, bindValues] = SqlQueries.findManyRows({ columns, tableName, where, ...omitUndefineds({ limit }) })
|
|
20
21
|
return [stmt, bindValues, tableName]
|
|
21
22
|
}
|
|
22
23
|
|
package/src/sqlite-types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Effect, Schema } from '@livestore/utils/effect'
|
|
2
|
-
import type { SqliteError,
|
|
2
|
+
import type { SqliteError, UnknownError } from './errors.ts'
|
|
3
3
|
import type { EventSequenceNumber } from './schema/mod.ts'
|
|
4
4
|
import type { QueryBuilder } from './schema/state/sqlite/query-builder/api.ts'
|
|
5
5
|
import type { PreparedBindValues } from './util.ts'
|
|
@@ -33,7 +33,7 @@ export interface SqliteDb<TReq = any, TMetadata extends TReq = TReq> {
|
|
|
33
33
|
makeChangeset: (data: Uint8Array<ArrayBuffer>) => SqliteDbChangeset
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export type SqliteDebugInfo = { head: EventSequenceNumber.
|
|
36
|
+
export type SqliteDebugInfo = { head: EventSequenceNumber.Client.Composite }
|
|
37
37
|
|
|
38
38
|
// TODO refactor this helper type. It's quite cumbersome to use and should be revisited.
|
|
39
39
|
export type MakeSqliteDb<
|
|
@@ -46,7 +46,7 @@ export type MakeSqliteDb<
|
|
|
46
46
|
TMetadata extends TMetadata_ & { _tag: TInput['_tag'] } = TMetadata_ & { _tag: TInput['_tag'] },
|
|
47
47
|
>(
|
|
48
48
|
input: TInput,
|
|
49
|
-
) => Effect.Effect<SqliteDb<TReq, Extract<TMetadata, { _tag: TInput['_tag'] }>>, SqliteError |
|
|
49
|
+
) => Effect.Effect<SqliteDb<TReq, Extract<TMetadata, { _tag: TInput['_tag'] }>>, SqliteError | UnknownError, R>
|
|
50
50
|
|
|
51
51
|
export interface PreparedStatement {
|
|
52
52
|
execute(bindValues: PreparedBindValues | undefined, options?: { onRowsChanged?: (rowsChanged: number) => void }): void
|
|
@@ -13,12 +13,13 @@ import {
|
|
|
13
13
|
Stream,
|
|
14
14
|
Subscribable,
|
|
15
15
|
} from '@livestore/utils/effect'
|
|
16
|
-
import * as otel from '@opentelemetry/api'
|
|
16
|
+
import type * as otel from '@opentelemetry/api'
|
|
17
17
|
|
|
18
|
-
import { type ClientSession,
|
|
19
|
-
import
|
|
20
|
-
import * as
|
|
21
|
-
import
|
|
18
|
+
import { type ClientSession, UnknownError } from '../adapter-types.ts'
|
|
19
|
+
import type { MaterializeError } from '../errors.ts'
|
|
20
|
+
import * as EventSequenceNumber from '../schema/EventSequenceNumber/mod.ts'
|
|
21
|
+
import * as LiveStoreEvent from '../schema/LiveStoreEvent/mod.ts'
|
|
22
|
+
import type { LiveStoreSchema } from '../schema/mod.ts'
|
|
22
23
|
import * as SyncState from './syncstate.ts'
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -51,16 +52,19 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
51
52
|
clientSession: ClientSession
|
|
52
53
|
runtime: Runtime.Runtime<Scope.Scope>
|
|
53
54
|
materializeEvent: (
|
|
54
|
-
|
|
55
|
-
options: {
|
|
56
|
-
) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
eventEncoded: LiveStoreEvent.Client.EncodedWithMeta,
|
|
56
|
+
options: { withChangeset: boolean; materializerHashLeader: Option.Option<number> },
|
|
57
|
+
) => Effect.Effect<
|
|
58
|
+
{
|
|
59
|
+
writeTables: Set<string>
|
|
60
|
+
sessionChangeset:
|
|
61
|
+
| { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any }
|
|
62
|
+
| { _tag: 'no-op' }
|
|
63
|
+
| { _tag: 'unset' }
|
|
64
|
+
materializerHash: Option.Option<number>
|
|
65
|
+
},
|
|
66
|
+
MaterializeError
|
|
67
|
+
>
|
|
64
68
|
rollback: (changeset: Uint8Array<ArrayBuffer>) => void
|
|
65
69
|
refreshTables: (tables: Set<string>) => void
|
|
66
70
|
span: otel.Span
|
|
@@ -74,7 +78,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
74
78
|
*/
|
|
75
79
|
confirmUnsavedChanges: boolean
|
|
76
80
|
}): ClientSessionSyncProcessor => {
|
|
77
|
-
const eventSchema = LiveStoreEvent.
|
|
81
|
+
const eventSchema = LiveStoreEvent.Client.makeSchemaMemo(schema)
|
|
78
82
|
|
|
79
83
|
const simSleep = <TKey extends keyof ClientSessionSyncProcessorSimulationParams>(
|
|
80
84
|
key: TKey,
|
|
@@ -91,26 +95,30 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
91
95
|
}),
|
|
92
96
|
}
|
|
93
97
|
|
|
94
|
-
/** Only used for debugging / observability, it's not relied upon for correctness of the sync processor. */
|
|
98
|
+
/** Only used for debugging / observability / testing, it's not relied upon for correctness of the sync processor. */
|
|
95
99
|
const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>().pipe(Effect.runSync)
|
|
96
|
-
const isClientEvent = (eventEncoded: LiveStoreEvent.EncodedWithMeta) =>
|
|
97
|
-
|
|
100
|
+
const isClientEvent = (eventEncoded: LiveStoreEvent.Client.EncodedWithMeta) =>
|
|
101
|
+
schema.eventsDefsMap.get(eventEncoded.name)?.options.clientOnly ?? false
|
|
98
102
|
|
|
99
103
|
/** We're queuing push requests to reduce the number of messages sent to the leader by batching them */
|
|
100
|
-
const leaderPushQueue = BucketQueue.make<LiveStoreEvent.EncodedWithMeta>().pipe(Effect.runSync)
|
|
104
|
+
const leaderPushQueue = BucketQueue.make<LiveStoreEvent.Client.EncodedWithMeta>().pipe(Effect.runSync)
|
|
101
105
|
|
|
102
|
-
const push: ClientSessionSyncProcessor['push'] = (batch
|
|
106
|
+
const push: ClientSessionSyncProcessor['push'] = Effect.fn('client-session-sync-processor:push')(function* (batch) {
|
|
103
107
|
// TODO validate batch
|
|
104
108
|
|
|
105
109
|
let baseEventSequenceNumber = syncStateRef.current.localHead
|
|
106
110
|
const encodedEventDefs = batch.map(({ name, args }) => {
|
|
107
|
-
const eventDef =
|
|
108
|
-
|
|
111
|
+
const eventDef = schema.eventsDefsMap.get(name)
|
|
112
|
+
if (eventDef === undefined) {
|
|
113
|
+
return shouldNeverHappen(`No event definition found for \`${name}\`.`)
|
|
114
|
+
}
|
|
115
|
+
const nextNumPair = EventSequenceNumber.Client.nextPair({
|
|
109
116
|
seqNum: baseEventSequenceNumber,
|
|
110
|
-
isClient: eventDef.
|
|
117
|
+
isClient: eventDef.options.clientOnly,
|
|
118
|
+
rebaseGeneration: baseEventSequenceNumber.rebaseGeneration,
|
|
111
119
|
})
|
|
112
120
|
baseEventSequenceNumber = nextNumPair.seqNum
|
|
113
|
-
return new LiveStoreEvent.EncodedWithMeta(
|
|
121
|
+
return new LiveStoreEvent.Client.EncodedWithMeta(
|
|
114
122
|
Schema.encodeUnknownSync(eventSchema)({
|
|
115
123
|
name,
|
|
116
124
|
args,
|
|
@@ -125,36 +133,38 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
125
133
|
syncState: syncStateRef.current,
|
|
126
134
|
payload: { _tag: 'local-push', newEvents: encodedEventDefs },
|
|
127
135
|
isClientEvent,
|
|
128
|
-
isEqualEvent: LiveStoreEvent.isEqualEncoded,
|
|
136
|
+
isEqualEvent: LiveStoreEvent.Client.isEqualEncoded,
|
|
129
137
|
})
|
|
130
138
|
|
|
131
|
-
|
|
132
|
-
return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.message)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
span.addEvent('local-push', {
|
|
139
|
+
yield* Effect.annotateCurrentSpan({
|
|
136
140
|
batchSize: encodedEventDefs.length,
|
|
137
|
-
|
|
141
|
+
mergeResultTag: mergeResult._tag,
|
|
142
|
+
eventCounts: encodedEventDefs.reduce<Record<string, number>>((acc, event) => {
|
|
143
|
+
acc[event.name] = (acc[event.name] ?? 0) + 1
|
|
144
|
+
return acc
|
|
145
|
+
}, {}),
|
|
146
|
+
...(TRACE_VERBOSE && { mergeResult: JSON.stringify(mergeResult) }),
|
|
138
147
|
})
|
|
139
148
|
|
|
149
|
+
if (mergeResult._tag === 'unknown-error') {
|
|
150
|
+
return shouldNeverHappen('Unknown error in client-session-sync-processor', mergeResult.message)
|
|
151
|
+
}
|
|
152
|
+
|
|
140
153
|
if (mergeResult._tag !== 'advance') {
|
|
141
154
|
return shouldNeverHappen(`Expected advance, got ${mergeResult._tag}`)
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
syncStateRef.current = mergeResult.newSyncState
|
|
145
|
-
syncStateUpdateQueue.offer(mergeResult.newSyncState)
|
|
158
|
+
yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
|
|
146
159
|
|
|
147
160
|
// Materialize events to state
|
|
148
161
|
const writeTables = new Set<string>()
|
|
149
162
|
for (const event of mergeResult.newEvents) {
|
|
150
|
-
// TODO avoid encoding and decoding here again
|
|
151
|
-
const decodedEventDef = Schema.decodeSync(eventSchema)(event)
|
|
152
163
|
const {
|
|
153
164
|
writeTables: newWriteTables,
|
|
154
165
|
sessionChangeset,
|
|
155
166
|
materializerHash,
|
|
156
|
-
} = materializeEvent(
|
|
157
|
-
otelContext,
|
|
167
|
+
} = yield* materializeEvent(event, {
|
|
158
168
|
withChangeset: true,
|
|
159
169
|
materializerHashLeader: Option.none(),
|
|
160
170
|
})
|
|
@@ -167,10 +177,10 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
167
177
|
|
|
168
178
|
// Trigger push to leader
|
|
169
179
|
// console.debug('pushToLeader', encodedEventDefs.length, ...encodedEventDefs.map((_) => _.toJSON()))
|
|
170
|
-
BucketQueue.offerAll(leaderPushQueue, encodedEventDefs)
|
|
180
|
+
yield* BucketQueue.offerAll(leaderPushQueue, encodedEventDefs)
|
|
171
181
|
|
|
172
182
|
return { writeTables }
|
|
173
|
-
}
|
|
183
|
+
})
|
|
174
184
|
|
|
175
185
|
const debugInfo = {
|
|
176
186
|
rebaseCount: 0,
|
|
@@ -178,8 +188,6 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
178
188
|
rejectCount: 0,
|
|
179
189
|
}
|
|
180
190
|
|
|
181
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
182
|
-
|
|
183
191
|
const boot: ClientSessionSyncProcessor['boot'] = Effect.gen(function* () {
|
|
184
192
|
if (confirmUnsavedChanges && typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
|
|
185
193
|
const onBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
@@ -225,17 +233,16 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
225
233
|
syncState: syncStateRef.current,
|
|
226
234
|
payload,
|
|
227
235
|
isClientEvent,
|
|
228
|
-
isEqualEvent: LiveStoreEvent.isEqualEncoded,
|
|
236
|
+
isEqualEvent: LiveStoreEvent.Client.isEqualEncoded,
|
|
229
237
|
})
|
|
230
238
|
|
|
231
|
-
if (mergeResult._tag === '
|
|
232
|
-
return yield* new
|
|
239
|
+
if (mergeResult._tag === 'unknown-error') {
|
|
240
|
+
return yield* new UnknownError({ cause: mergeResult.message })
|
|
233
241
|
} else if (mergeResult._tag === 'reject') {
|
|
234
242
|
return shouldNeverHappen('Unexpected reject in client-session-sync-processor', mergeResult)
|
|
235
243
|
}
|
|
236
244
|
|
|
237
245
|
syncStateRef.current = mergeResult.newSyncState
|
|
238
|
-
yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
|
|
239
246
|
|
|
240
247
|
if (mergeResult._tag === 'rebase') {
|
|
241
248
|
span.addEvent('merge:pull:rebase', {
|
|
@@ -244,7 +251,6 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
244
251
|
newEventsCount: mergeResult.newEvents.length,
|
|
245
252
|
rollbackCount: mergeResult.rollbackEvents.length,
|
|
246
253
|
res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
|
|
247
|
-
rebaseGeneration: mergeResult.newSyncState.localHead.rebaseGeneration,
|
|
248
254
|
})
|
|
249
255
|
|
|
250
256
|
debugInfo.rebaseCount++
|
|
@@ -294,18 +300,19 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
294
300
|
debugInfo.advanceCount++
|
|
295
301
|
}
|
|
296
302
|
|
|
297
|
-
if (mergeResult.newEvents.length === 0)
|
|
303
|
+
if (mergeResult.newEvents.length === 0) {
|
|
304
|
+
// If there are no new events, we need to update the sync state as well
|
|
305
|
+
yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
|
|
306
|
+
return
|
|
307
|
+
}
|
|
298
308
|
|
|
299
309
|
const writeTables = new Set<string>()
|
|
300
310
|
for (const event of mergeResult.newEvents) {
|
|
301
|
-
// TODO apply changeset if available (will require tracking of write tables as well)
|
|
302
|
-
const decodedEventDef = Schema.decodeSync(eventSchema)(event)
|
|
303
311
|
const {
|
|
304
312
|
writeTables: newWriteTables,
|
|
305
313
|
sessionChangeset,
|
|
306
314
|
materializerHash,
|
|
307
|
-
} = materializeEvent(
|
|
308
|
-
otelContext,
|
|
315
|
+
} = yield* materializeEvent(event, {
|
|
309
316
|
withChangeset: true,
|
|
310
317
|
materializerHashLeader: event.meta.materializerHashLeader,
|
|
311
318
|
})
|
|
@@ -318,6 +325,9 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
318
325
|
}
|
|
319
326
|
|
|
320
327
|
refreshTables(writeTables)
|
|
328
|
+
|
|
329
|
+
// We're only triggering the sync state update after all events have been materialized
|
|
330
|
+
yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
|
|
321
331
|
}).pipe(
|
|
322
332
|
Effect.tapCauseLogPretty,
|
|
323
333
|
Effect.catchAllCause((cause) => clientSession.shutdown(Exit.failCause(cause))),
|
|
@@ -363,12 +373,9 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
363
373
|
|
|
364
374
|
export interface ClientSessionSyncProcessor {
|
|
365
375
|
push: (
|
|
366
|
-
batch: ReadonlyArray<LiveStoreEvent.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
writeTables: Set<string>
|
|
370
|
-
}
|
|
371
|
-
boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
|
|
376
|
+
batch: ReadonlyArray<LiveStoreEvent.Input.Decoded>,
|
|
377
|
+
) => Effect.Effect<{ writeTables: Set<string> }, MaterializeError>
|
|
378
|
+
boot: Effect.Effect<void, UnknownError, Scope.Scope>
|
|
372
379
|
/**
|
|
373
380
|
* Only used for debugging / observability.
|
|
374
381
|
*/
|
|
@@ -388,11 +395,11 @@ const SIMULATION_ENABLED = true
|
|
|
388
395
|
// Warning: High values for the simulation params can lead to very long test runs since those get multiplied with the number of events
|
|
389
396
|
export const ClientSessionSyncProcessorSimulationParams = Schema.Struct({
|
|
390
397
|
pull: Schema.Struct({
|
|
391
|
-
'1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0,
|
|
392
|
-
'2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0,
|
|
393
|
-
'3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0,
|
|
394
|
-
'4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0,
|
|
395
|
-
'5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0,
|
|
398
|
+
'1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0, 15)),
|
|
399
|
+
'2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0, 15)),
|
|
400
|
+
'3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0, 15)),
|
|
401
|
+
'4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0, 15)),
|
|
402
|
+
'5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0, 15)),
|
|
396
403
|
}),
|
|
397
404
|
})
|
|
398
405
|
type ClientSessionSyncProcessorSimulationParams = typeof ClientSessionSyncProcessorSimulationParams.Type
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Schema } from '@livestore/utils/effect'
|
|
2
|
+
import { UnknownError } from '../errors.ts'
|
|
3
|
+
import { EventSequenceNumber } from '../schema/mod.ts'
|
|
4
|
+
|
|
5
|
+
export class IsOfflineError extends Schema.TaggedError<IsOfflineError>()('IsOfflineError', {
|
|
6
|
+
cause: Schema.Defect,
|
|
7
|
+
}) {}
|
|
8
|
+
|
|
9
|
+
/** Unique ID generated by the backend when its created. Used to check whether the backend identity has changed. */
|
|
10
|
+
export const BackendId = Schema.String.annotations({ title: '@livestore/sync-cf:BackendId' })
|
|
11
|
+
|
|
12
|
+
export class BackendIdMismatchError extends Schema.TaggedError<BackendIdMismatchError>()('BackendIdMismatchError', {
|
|
13
|
+
expected: BackendId,
|
|
14
|
+
received: BackendId,
|
|
15
|
+
}) {}
|
|
16
|
+
|
|
17
|
+
export class ServerAheadError extends Schema.TaggedError<ServerAheadError>()('ServerAheadError', {
|
|
18
|
+
minimumExpectedNum: EventSequenceNumber.Global.Schema,
|
|
19
|
+
providedNum: EventSequenceNumber.Global.Schema,
|
|
20
|
+
}) {}
|
|
21
|
+
|
|
22
|
+
export class InvalidPushError extends Schema.TaggedError<InvalidPushError>()('InvalidPushError', {
|
|
23
|
+
cause: Schema.Union(UnknownError, ServerAheadError, BackendIdMismatchError),
|
|
24
|
+
}) {}
|
|
25
|
+
|
|
26
|
+
export class InvalidPullError extends Schema.TaggedError<InvalidPullError>()('InvalidPullError', {
|
|
27
|
+
cause: Schema.Defect,
|
|
28
|
+
}) {}
|
|
29
|
+
|
|
30
|
+
export class LeaderAheadError extends Schema.TaggedError<LeaderAheadError>()('LeaderAheadError', {
|
|
31
|
+
minimumExpectedNum: EventSequenceNumber.Client.Composite,
|
|
32
|
+
providedNum: EventSequenceNumber.Client.Composite,
|
|
33
|
+
/** Generation number the client session should use for subsequent pushes */
|
|
34
|
+
// nextGeneration: Schema.Number,
|
|
35
|
+
}) {}
|
|
36
|
+
|
|
37
|
+
export const SyncError = Schema.Union(InvalidPushError, InvalidPullError)
|
|
38
|
+
export type SyncError = typeof SyncError.Type
|
package/src/sync/index.ts
CHANGED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { Schema, Scope } from '@livestore/utils/effect'
|
|
2
|
+
import { Effect, Mailbox, Option, Queue, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
|
3
|
+
import { UnknownError } from '../errors.ts'
|
|
4
|
+
import { EventSequenceNumber, type LiveStoreEvent } from '../schema/mod.ts'
|
|
5
|
+
import { InvalidPushError } from './errors.ts'
|
|
6
|
+
import * as SyncBackend from './sync-backend.ts'
|
|
7
|
+
import { validatePushPayload } from './validate-push-payload.ts'
|
|
8
|
+
|
|
9
|
+
export interface MockSyncBackend {
|
|
10
|
+
pushedEvents: Stream.Stream<LiveStoreEvent.Global.Encoded>
|
|
11
|
+
connect: Effect.Effect<void>
|
|
12
|
+
disconnect: Effect.Effect<void>
|
|
13
|
+
makeSyncBackend: Effect.Effect<SyncBackend.SyncBackend, UnknownError, Scope.Scope>
|
|
14
|
+
advance: (...batch: LiveStoreEvent.Global.Encoded[]) => Effect.Effect<void>
|
|
15
|
+
/** Fail the next N push calls with an InvalidPushError (or custom error) */
|
|
16
|
+
failNextPushes: (
|
|
17
|
+
count: number,
|
|
18
|
+
error?: (batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>,
|
|
19
|
+
) => Effect.Effect<void>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MockSyncBackendOptions {
|
|
23
|
+
/** Chunk size for non-live pulls; defaults to 100 */
|
|
24
|
+
nonLiveChunkSize?: number
|
|
25
|
+
/** Initial connected state; defaults to false */
|
|
26
|
+
startConnected?: boolean
|
|
27
|
+
// TODO add a "flaky" mode to simulate transient network / server failures for pull/push
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const makeMockSyncBackend = (
|
|
31
|
+
options?: MockSyncBackendOptions,
|
|
32
|
+
): Effect.Effect<MockSyncBackend, UnknownError, Scope.Scope> =>
|
|
33
|
+
Effect.gen(function* () {
|
|
34
|
+
const syncEventSequenceNumberRef = { current: EventSequenceNumber.Client.ROOT.global }
|
|
35
|
+
const syncPullQueue = yield* Queue.unbounded<LiveStoreEvent.Global.Encoded>()
|
|
36
|
+
const pushedEventsQueue = yield* Mailbox.make<LiveStoreEvent.Global.Encoded>()
|
|
37
|
+
const syncIsConnectedRef = yield* SubscriptionRef.make(options?.startConnected ?? false)
|
|
38
|
+
const allEventsRef: { current: LiveStoreEvent.Global.Encoded[] } = { current: [] }
|
|
39
|
+
|
|
40
|
+
const span = yield* Effect.currentSpan.pipe(Effect.orDie)
|
|
41
|
+
|
|
42
|
+
const semaphore = yield* Effect.makeSemaphore(1)
|
|
43
|
+
|
|
44
|
+
// TODO improve the API and implementation of simulating errors
|
|
45
|
+
const failCounterRef = yield* SubscriptionRef.make(0)
|
|
46
|
+
const failEffectRef = yield* SubscriptionRef.make<
|
|
47
|
+
((batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>) | undefined
|
|
48
|
+
>(undefined)
|
|
49
|
+
|
|
50
|
+
const makeSyncBackend = Effect.gen(function* () {
|
|
51
|
+
const nonLiveChunkSize = Math.max(1, options?.nonLiveChunkSize ?? 100)
|
|
52
|
+
|
|
53
|
+
// TODO consider making offline state actively error pull/push.
|
|
54
|
+
// Currently, offline only reflects in `isConnected`, while operations still succeed,
|
|
55
|
+
// mirroring how some real providers behave during transient disconnects.
|
|
56
|
+
return SyncBackend.of<Schema.JsonValue>({
|
|
57
|
+
isConnected: syncIsConnectedRef,
|
|
58
|
+
connect: SubscriptionRef.set(syncIsConnectedRef, true),
|
|
59
|
+
ping: Effect.void,
|
|
60
|
+
pull: (cursor, options) =>
|
|
61
|
+
(options?.live
|
|
62
|
+
? Stream.concat(
|
|
63
|
+
Stream.make(SyncBackend.pullResItemEmpty()),
|
|
64
|
+
Stream.fromQueue(syncPullQueue).pipe(
|
|
65
|
+
Stream.chunks,
|
|
66
|
+
Stream.map((chunk) => ({
|
|
67
|
+
batch: [...chunk].map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
|
|
68
|
+
pageInfo: SyncBackend.pageInfoNoMore,
|
|
69
|
+
})),
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
: Stream.fromEffect(
|
|
73
|
+
Effect.sync(() => {
|
|
74
|
+
const lastSeen = cursor.pipe(
|
|
75
|
+
Option.match({
|
|
76
|
+
onNone: () => EventSequenceNumber.Client.ROOT.global,
|
|
77
|
+
onSome: (_) => _.eventSequenceNumber,
|
|
78
|
+
}),
|
|
79
|
+
)
|
|
80
|
+
// All events with seqNum greater than lastSeen
|
|
81
|
+
const slice = allEventsRef.current.filter((e) => e.seqNum > lastSeen)
|
|
82
|
+
// Split into configured chunk size
|
|
83
|
+
const chunks: { events: LiveStoreEvent.Global.Encoded[]; remaining: number }[] = []
|
|
84
|
+
for (let i = 0; i < slice.length; i += nonLiveChunkSize) {
|
|
85
|
+
const end = Math.min(i + nonLiveChunkSize, slice.length)
|
|
86
|
+
const remaining = Math.max(slice.length - end, 0)
|
|
87
|
+
chunks.push({ events: slice.slice(i, end), remaining })
|
|
88
|
+
}
|
|
89
|
+
if (chunks.length === 0) {
|
|
90
|
+
chunks.push({ events: [], remaining: 0 })
|
|
91
|
+
}
|
|
92
|
+
return chunks
|
|
93
|
+
}),
|
|
94
|
+
).pipe(
|
|
95
|
+
Stream.flatMap((chunks) =>
|
|
96
|
+
Stream.fromIterable(chunks).pipe(
|
|
97
|
+
Stream.map(({ events, remaining }) => ({
|
|
98
|
+
batch: events.map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
|
|
99
|
+
pageInfo: remaining > 0 ? SyncBackend.pageInfoMoreKnown(remaining) : SyncBackend.pageInfoNoMore,
|
|
100
|
+
})),
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
).pipe(Stream.withSpan('MockSyncBackend:pull', { parent: span })),
|
|
105
|
+
push: (batch) =>
|
|
106
|
+
Effect.gen(function* () {
|
|
107
|
+
yield* validatePushPayload(batch, syncEventSequenceNumberRef.current)
|
|
108
|
+
|
|
109
|
+
const remaining = yield* SubscriptionRef.get(failCounterRef)
|
|
110
|
+
if (remaining > 0) {
|
|
111
|
+
const maybeFail = yield* SubscriptionRef.get(failEffectRef)
|
|
112
|
+
// decrement counter first
|
|
113
|
+
yield* SubscriptionRef.set(failCounterRef, remaining - 1)
|
|
114
|
+
if (maybeFail) {
|
|
115
|
+
return yield* maybeFail(batch)
|
|
116
|
+
}
|
|
117
|
+
return yield* new InvalidPushError({
|
|
118
|
+
cause: new UnknownError({ cause: new Error('MockSyncBackend: simulated push failure') }),
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
yield* Effect.sleep(10).pipe(Effect.withSpan('MockSyncBackend:push:sleep')) // Simulate network latency
|
|
123
|
+
|
|
124
|
+
yield* pushedEventsQueue.offerAll(batch)
|
|
125
|
+
yield* syncPullQueue.offerAll(batch)
|
|
126
|
+
allEventsRef.current = allEventsRef.current.concat(batch)
|
|
127
|
+
|
|
128
|
+
syncEventSequenceNumberRef.current = batch.at(-1)!.seqNum
|
|
129
|
+
}).pipe(
|
|
130
|
+
Effect.withSpan('MockSyncBackend:push', {
|
|
131
|
+
parent: span,
|
|
132
|
+
attributes: {
|
|
133
|
+
nums: batch.map((_) => _.seqNum),
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
semaphore.withPermits(1),
|
|
137
|
+
),
|
|
138
|
+
metadata: {
|
|
139
|
+
name: '@livestore/mock-sync',
|
|
140
|
+
description: 'Just a mock sync backend',
|
|
141
|
+
},
|
|
142
|
+
supports: {
|
|
143
|
+
pullPageInfoKnown: true,
|
|
144
|
+
pullLive: true,
|
|
145
|
+
},
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const advance = (...batch: LiveStoreEvent.Global.Encoded[]) =>
|
|
150
|
+
Effect.gen(function* () {
|
|
151
|
+
syncEventSequenceNumberRef.current = batch.at(-1)!.seqNum
|
|
152
|
+
allEventsRef.current = allEventsRef.current.concat(batch)
|
|
153
|
+
yield* syncPullQueue.offerAll(batch)
|
|
154
|
+
}).pipe(
|
|
155
|
+
Effect.withSpan('MockSyncBackend:advance', {
|
|
156
|
+
parent: span,
|
|
157
|
+
attributes: { nums: batch.map((_) => _.seqNum) },
|
|
158
|
+
}),
|
|
159
|
+
semaphore.withPermits(1),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
const connect = SubscriptionRef.set(syncIsConnectedRef, true)
|
|
163
|
+
const disconnect = SubscriptionRef.set(syncIsConnectedRef, false)
|
|
164
|
+
|
|
165
|
+
const failNextPushes = (
|
|
166
|
+
count: number,
|
|
167
|
+
error?: (batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>,
|
|
168
|
+
) =>
|
|
169
|
+
Effect.gen(function* () {
|
|
170
|
+
yield* SubscriptionRef.set(failCounterRef, count)
|
|
171
|
+
yield* SubscriptionRef.set(failEffectRef, error)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
syncEventSequenceNumberRef,
|
|
176
|
+
syncPullQueue,
|
|
177
|
+
pushedEvents: Mailbox.toStream(pushedEventsQueue),
|
|
178
|
+
connect,
|
|
179
|
+
disconnect,
|
|
180
|
+
makeSyncBackend,
|
|
181
|
+
advance,
|
|
182
|
+
failNextPushes,
|
|
183
|
+
}
|
|
184
|
+
}).pipe(Effect.withSpanScoped('MockSyncBackend'))
|