@livestore/common 0.4.0-dev.21 → 0.4.0-dev.23
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 +16 -9
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
- package/dist/WorkerTransportError.d.ts +11 -0
- package/dist/WorkerTransportError.d.ts.map +1 -0
- package/dist/WorkerTransportError.js +11 -0
- package/dist/WorkerTransportError.js.map +1 -0
- package/dist/adapter-types.d.ts +26 -3
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +27 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/bounded-collections.d.ts.map +1 -1
- package/dist/bounded-collections.js +6 -4
- package/dist/bounded-collections.js.map +1 -1
- package/dist/debug-info.js +4 -4
- package/dist/debug-info.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +42 -22
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.js +12 -1
- package/dist/devtools/devtools-messages-client-session.js.map +1 -1
- package/dist/devtools/devtools-messages-common.d.ts +12 -6
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-common.js +8 -3
- package/dist/devtools/devtools-messages-common.js.map +1 -1
- package/dist/devtools/devtools-messages-leader.d.ts +45 -25
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +12 -1
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/devtools/mod.js +1 -1
- package/dist/devtools/mod.js.map +1 -1
- package/dist/errors.d.ts +15 -15
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -11
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +20 -6
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +283 -253
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/RejectedPushError.d.ts +107 -0
- package/dist/leader-thread/RejectedPushError.d.ts.map +1 -0
- package/dist/leader-thread/RejectedPushError.js +78 -0
- package/dist/leader-thread/RejectedPushError.js.map +1 -0
- package/dist/leader-thread/connection.js +1 -1
- package/dist/leader-thread/connection.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts.map +1 -1
- package/dist/leader-thread/eventlog.js +12 -11
- package/dist/leader-thread/eventlog.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +1 -2
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +34 -14
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +12 -5
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +12 -11
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.js +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -1
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +7 -4
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/leader-thread/recreate-db.js +1 -1
- 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.map +1 -1
- package/dist/leader-thread/stream-events.js +4 -3
- package/dist/leader-thread/stream-events.js.map +1 -1
- package/dist/leader-thread/types.d.ts +7 -6
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/logging.js +4 -4
- package/dist/logging.js.map +1 -1
- package/dist/make-client-session.js +2 -2
- package/dist/make-client-session.js.map +1 -1
- package/dist/materializer-helper.js +6 -6
- package/dist/materializer-helper.js.map +1 -1
- package/dist/otel.d.ts +1 -1
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +2 -2
- package/dist/otel.js.map +1 -1
- package/dist/rematerialize-from-eventlog.d.ts +1 -1
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
- package/dist/rematerialize-from-eventlog.js +11 -9
- package/dist/rematerialize-from-eventlog.js.map +1 -1
- package/dist/schema/EventDef/define.d.ts +16 -2
- package/dist/schema/EventDef/define.d.ts.map +1 -1
- package/dist/schema/EventDef/define.js +5 -4
- package/dist/schema/EventDef/define.js.map +1 -1
- package/dist/schema/EventDef/deprecated.d.ts +99 -0
- package/dist/schema/EventDef/deprecated.d.ts.map +1 -0
- package/dist/schema/EventDef/deprecated.js +144 -0
- package/dist/schema/EventDef/deprecated.js.map +1 -0
- package/dist/schema/EventDef/deprecated.test.d.ts +2 -0
- package/dist/schema/EventDef/deprecated.test.d.ts.map +1 -0
- package/dist/schema/EventDef/deprecated.test.js +95 -0
- package/dist/schema/EventDef/deprecated.test.js.map +1 -0
- package/dist/schema/EventDef/event-def.d.ts +4 -0
- package/dist/schema/EventDef/event-def.d.ts.map +1 -1
- package/dist/schema/EventDef/mod.d.ts +1 -0
- package/dist/schema/EventDef/mod.d.ts.map +1 -1
- package/dist/schema/EventDef/mod.js +1 -0
- package/dist/schema/EventDef/mod.js.map +1 -1
- package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -1
- package/dist/schema/EventSequenceNumber/client.js +11 -11
- package/dist/schema/EventSequenceNumber/client.js.map +1 -1
- package/dist/schema/EventSequenceNumber.test.js +1 -1
- package/dist/schema/EventSequenceNumber.test.js.map +1 -1
- package/dist/schema/LiveStoreEvent/client.d.ts +6 -6
- package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -1
- package/dist/schema/LiveStoreEvent/client.js +6 -3
- package/dist/schema/LiveStoreEvent/client.js.map +1 -1
- package/dist/schema/LiveStoreEvent/client.test.d.ts +2 -0
- package/dist/schema/LiveStoreEvent/client.test.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/client.test.js +83 -0
- package/dist/schema/LiveStoreEvent/client.test.js.map +1 -0
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +7 -4
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +1 -0
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +34 -13
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +121 -2
- 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 +1 -1
- 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.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-def.js +36 -34
- package/dist/schema/state/sqlite/column-def.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.test.js +7 -6
- 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 +8 -8
- package/dist/schema/state/sqlite/column-spec.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.test.js +1 -1
- package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +2 -2
- 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 +2 -2
- 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 +11 -2
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +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 +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +3 -5
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +37 -13
- 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 +77 -7
- package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.js +28 -14
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.test.js +112 -3
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.js +2 -2
- package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.d.ts +5 -3
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.js +1 -1
- package/dist/schema/state/sqlite/table-def.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.test.js +57 -4
- package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
- package/dist/schema/unknown-events.d.ts +1 -1
- package/dist/schema/unknown-events.d.ts.map +1 -1
- package/dist/schema/unknown-events.js +1 -1
- package/dist/schema/unknown-events.js.map +1 -1
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +1 -1
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -1
- package/dist/schema-management/common.js +2 -2
- package/dist/schema-management/common.js.map +1 -1
- package/dist/schema-management/migrations.js +1 -1
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/sql-queries/sql-queries.js +8 -6
- package/dist/sql-queries/sql-queries.js.map +1 -1
- package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
- package/dist/sql-queries/sql-query-builder.js.map +1 -1
- package/dist/sqlite-db-helper.js +3 -3
- package/dist/sqlite-db-helper.js.map +1 -1
- package/dist/sqlite-types.d.ts +2 -2
- package/dist/sqlite-types.d.ts.map +1 -1
- package/dist/sqlite-types.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +8 -9
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +93 -107
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/errors.d.ts +0 -38
- package/dist/sync/errors.d.ts.map +1 -1
- package/dist/sync/errors.js +3 -20
- package/dist/sync/errors.js.map +1 -1
- package/dist/sync/mock-sync-backend.d.ts +5 -3
- package/dist/sync/mock-sync-backend.d.ts.map +1 -1
- package/dist/sync/mock-sync-backend.js +70 -68
- package/dist/sync/mock-sync-backend.js.map +1 -1
- package/dist/sync/next/compact-events.js +6 -6
- package/dist/sync/next/compact-events.js.map +1 -1
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +6 -6
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +6 -6
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.js +3 -3
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.js +1 -1
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/compact-events.calculator.test.js +2 -2
- package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
- package/dist/sync/next/test/compact-events.test.d.ts.map +1 -1
- package/dist/sync/next/test/compact-events.test.js +2 -2
- package/dist/sync/next/test/compact-events.test.js.map +1 -1
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
- package/dist/sync/next/test/event-fixtures.js +2 -2
- package/dist/sync/next/test/event-fixtures.js.map +1 -1
- package/dist/sync/sync-backend-kv.d.ts.map +1 -1
- package/dist/sync/sync-backend-kv.js.map +1 -1
- package/dist/sync/sync-backend.d.ts +3 -3
- package/dist/sync/sync-backend.d.ts.map +1 -1
- package/dist/sync/sync-backend.js +1 -1
- package/dist/sync/sync-backend.js.map +1 -1
- package/dist/sync/sync.d.ts +20 -0
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/syncstate.d.ts +4 -17
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +51 -74
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +112 -96
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/transport-chunking.js +3 -3
- package/dist/sync/transport-chunking.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 -6
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/util.js +2 -2
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +7 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +8 -4
- package/dist/version.js.map +1 -1
- package/package.json +66 -12
- package/src/ClientSessionLeaderThreadProxy.ts +16 -9
- package/src/WorkerTransportError.ts +12 -0
- package/src/adapter-types.ts +39 -3
- package/src/bounded-collections.ts +6 -5
- package/src/debug-info.ts +4 -4
- package/src/devtools/devtools-messages-client-session.ts +12 -0
- package/src/devtools/devtools-messages-common.ts +8 -4
- package/src/devtools/devtools-messages-leader.ts +12 -0
- package/src/devtools/mod.ts +1 -1
- package/src/errors.ts +18 -17
- package/src/index.ts +2 -0
- package/src/leader-thread/LeaderSyncProcessor.ts +417 -347
- package/src/leader-thread/RejectedPushError.ts +106 -0
- package/src/leader-thread/connection.ts +1 -1
- package/src/leader-thread/eventlog.ts +16 -14
- package/src/leader-thread/leader-worker-devtools.ts +107 -66
- package/src/leader-thread/make-leader-thread-layer.test.ts +1 -1
- package/src/leader-thread/make-leader-thread-layer.ts +41 -31
- package/src/leader-thread/materialize-event.ts +8 -4
- package/src/leader-thread/recreate-db.ts +1 -1
- package/src/leader-thread/shutdown-channel.ts +2 -6
- package/src/leader-thread/stream-events.ts +10 -5
- package/src/leader-thread/types.ts +7 -6
- package/src/logging.ts +4 -4
- package/src/make-client-session.ts +2 -2
- package/src/materializer-helper.ts +9 -9
- package/src/otel.ts +3 -2
- package/src/rematerialize-from-eventlog.ts +60 -60
- package/src/schema/EventDef/define.ts +22 -6
- package/src/schema/EventDef/deprecated.test.ts +129 -0
- package/src/schema/EventDef/deprecated.ts +175 -0
- package/src/schema/EventDef/event-def.ts +5 -0
- package/src/schema/EventDef/mod.ts +1 -0
- package/src/schema/EventSequenceNumber/client.ts +11 -11
- package/src/schema/EventSequenceNumber.test.ts +2 -1
- package/src/schema/LiveStoreEvent/client.test.ts +97 -0
- package/src/schema/LiveStoreEvent/client.ts +6 -3
- package/src/schema/schema.ts +9 -4
- package/src/schema/state/sqlite/client-document-def.test.ts +142 -3
- package/src/schema/state/sqlite/client-document-def.ts +37 -14
- package/src/schema/state/sqlite/column-annotations.test.ts +2 -1
- package/src/schema/state/sqlite/column-annotations.ts +2 -1
- package/src/schema/state/sqlite/column-def.test.ts +8 -6
- package/src/schema/state/sqlite/column-def.ts +41 -36
- package/src/schema/state/sqlite/column-spec.test.ts +3 -1
- package/src/schema/state/sqlite/column-spec.ts +9 -8
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +2 -2
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +2 -1
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +13 -4
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +3 -3
- package/src/schema/state/sqlite/mod.ts +4 -5
- package/src/schema/state/sqlite/query-builder/api.ts +37 -8
- package/src/schema/state/sqlite/query-builder/astToSql.ts +87 -7
- package/src/schema/state/sqlite/query-builder/impl.test.ts +145 -3
- package/src/schema/state/sqlite/query-builder/impl.ts +26 -12
- package/src/schema/state/sqlite/schema-helpers.ts +2 -2
- package/src/schema/state/sqlite/table-def.test.ts +67 -4
- package/src/schema/state/sqlite/table-def.ts +8 -15
- package/src/schema/unknown-events.ts +2 -2
- package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +3 -1
- package/src/schema-management/common.ts +2 -2
- package/src/schema-management/migrations.ts +1 -1
- package/src/sql-queries/sql-queries.ts +10 -6
- package/src/sql-queries/sql-query-builder.ts +1 -0
- package/src/sqlite-db-helper.ts +3 -3
- package/src/sqlite-types.ts +3 -2
- package/src/sync/ClientSessionSyncProcessor.ts +142 -133
- package/src/sync/errors.ts +10 -22
- package/src/sync/mock-sync-backend.ts +139 -97
- package/src/sync/next/compact-events.ts +5 -5
- package/src/sync/next/facts.ts +7 -6
- package/src/sync/next/history-dag-common.ts +9 -6
- package/src/sync/next/history-dag.ts +3 -3
- package/src/sync/next/rebase-events.ts +1 -1
- package/src/sync/next/test/compact-events.calculator.test.ts +3 -2
- package/src/sync/next/test/compact-events.test.ts +4 -3
- package/src/sync/next/test/event-fixtures.ts +2 -2
- package/src/sync/sync-backend-kv.ts +1 -0
- package/src/sync/sync-backend.ts +5 -4
- package/src/sync/sync.ts +21 -0
- package/src/sync/syncstate.test.ts +513 -435
- package/src/sync/syncstate.ts +80 -86
- package/src/sync/transport-chunking.ts +3 -3
- package/src/sync/validate-push-payload.ts +4 -6
- package/src/util.ts +2 -2
- package/src/version.ts +8 -4
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push validation errors returned by {@link LeaderSyncProcessor.push}.
|
|
3
|
+
*
|
|
4
|
+
* All three errors share a common {@link RejectedPushErrorTypeId} so consumers can catch the
|
|
5
|
+
* family as a group via {@link isRejectedPushError} instead of matching individual tags.
|
|
6
|
+
* Recovery is the same in every case: the client should rebase and retry.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { Predicate, Schema } from '@livestore/utils/effect'
|
|
11
|
+
|
|
12
|
+
import { EventSequenceNumber } from '../schema/mod.ts'
|
|
13
|
+
|
|
14
|
+
export const RejectedPushErrorTypeId = '~@livestore/common/RejectedPushError' as const
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A pushed batch of events failed validation because its sequence numbers are not strictly increasing.
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
*
|
|
21
|
+
* This is a defensive check — callers are expected to construct monotonic event batches.
|
|
22
|
+
* The client should rebase and retry.
|
|
23
|
+
*/
|
|
24
|
+
export class NonMonotonicBatchError extends Schema.TaggedError<NonMonotonicBatchError>(
|
|
25
|
+
`${RejectedPushErrorTypeId}/NonMonotonicBatchError`,
|
|
26
|
+
)(
|
|
27
|
+
'NonMonotonicBatchError',
|
|
28
|
+
{
|
|
29
|
+
/** The sequence number that broke the monotonic invariant (i.e. the one that is >= the next). */
|
|
30
|
+
precedingSeqNum: EventSequenceNumber.Client.Composite,
|
|
31
|
+
/** The sequence number that was expected to be greater than `precedingSeqNum`. */
|
|
32
|
+
violatingSeqNum: EventSequenceNumber.Client.Composite,
|
|
33
|
+
/** The index in the batch where the violation occurred. */
|
|
34
|
+
violationIndex: Schema.Number,
|
|
35
|
+
/** The session that produced the malformed batch. */
|
|
36
|
+
sessionId: Schema.String,
|
|
37
|
+
},
|
|
38
|
+
) {
|
|
39
|
+
readonly [RejectedPushErrorTypeId] = RejectedPushErrorTypeId
|
|
40
|
+
|
|
41
|
+
override get message(): string {
|
|
42
|
+
return `Pushed events' sequence numbers are not strictly increasing at index ${this.violationIndex} (session ${this.sessionId}): ${EventSequenceNumber.Client.toString(this.precedingSeqNum)} >= ${EventSequenceNumber.Client.toString(this.violatingSeqNum)}`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A pushed batch of events failed validation because its rebase generation is older than the leader's current rebase generation.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
*
|
|
51
|
+
* This happens when events were enqueued before a backend-pull-triggered rebase incremented the generation.
|
|
52
|
+
*/
|
|
53
|
+
export class StaleRebaseGenerationError extends Schema.TaggedError<StaleRebaseGenerationError>(
|
|
54
|
+
`${RejectedPushErrorTypeId}/StaleRebaseGenerationError`,
|
|
55
|
+
)(
|
|
56
|
+
'StaleRebaseGenerationError',
|
|
57
|
+
{
|
|
58
|
+
/** The leader's current rebase generation. */
|
|
59
|
+
currentRebaseGeneration: Schema.Number,
|
|
60
|
+
/** The rebase generation carried by the dropped events. */
|
|
61
|
+
providedRebaseGeneration: Schema.Number,
|
|
62
|
+
/** The session that produced the stale batch. */
|
|
63
|
+
sessionId: Schema.String,
|
|
64
|
+
},
|
|
65
|
+
) {
|
|
66
|
+
readonly [RejectedPushErrorTypeId] = RejectedPushErrorTypeId
|
|
67
|
+
|
|
68
|
+
override get message(): string {
|
|
69
|
+
return `Pushed events have stale rebase generation (session ${this.sessionId}): expected >= ${this.currentRebaseGeneration}, got ${this.providedRebaseGeneration}`
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A pushed batch of events was rejected because the leader's push head has already advanced
|
|
75
|
+
* past the batch's first event.
|
|
76
|
+
*
|
|
77
|
+
* @remarks
|
|
78
|
+
*
|
|
79
|
+
* This occurs when another client session (or a backend pull) has pushed events that the current
|
|
80
|
+
* session hasn't seen yet.
|
|
81
|
+
*/
|
|
82
|
+
export class LeaderAheadError extends Schema.TaggedError<LeaderAheadError>(
|
|
83
|
+
`${RejectedPushErrorTypeId}/LeaderAheadError`,
|
|
84
|
+
)('LeaderAheadError', {
|
|
85
|
+
minimumExpectedNum: EventSequenceNumber.Client.Composite,
|
|
86
|
+
providedNum: EventSequenceNumber.Client.Composite,
|
|
87
|
+
/** The session that produced the stale batch. */
|
|
88
|
+
sessionId: Schema.String,
|
|
89
|
+
}) {
|
|
90
|
+
readonly [RejectedPushErrorTypeId] = RejectedPushErrorTypeId
|
|
91
|
+
|
|
92
|
+
override get message(): string {
|
|
93
|
+
return `Leader push head is ahead of batch (session ${this.sessionId}): expected > ${EventSequenceNumber.Client.toString(this.minimumExpectedNum)}, got ${EventSequenceNumber.Client.toString(this.providedNum)}`
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const RejectedPushError = Schema.Union(
|
|
98
|
+
LeaderAheadError,
|
|
99
|
+
NonMonotonicBatchError,
|
|
100
|
+
StaleRebaseGenerationError,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
export type RejectedPushError = typeof RejectedPushError.Type
|
|
104
|
+
|
|
105
|
+
export const isRejectedPushError = (u: unknown): u is RejectedPushError =>
|
|
106
|
+
Predicate.hasProperty(u, RejectedPushErrorTypeId)
|
|
@@ -62,7 +62,7 @@ export const configureConnection = (sqliteDb: SqliteDb, { foreignKeys, lockingMo
|
|
|
62
62
|
-- disable WAL until we have it working properly
|
|
63
63
|
-- PRAGMA journal_mode=WAL;
|
|
64
64
|
PRAGMA page_size=8192;
|
|
65
|
-
PRAGMA foreign_keys=${foreignKeys ? 'ON' : 'OFF'};
|
|
65
|
+
PRAGMA foreign_keys=${foreignKeys === true ? 'ON' : 'OFF'};
|
|
66
66
|
${lockingMode === undefined ? '' : sql`PRAGMA locking_mode=${lockingMode};`}
|
|
67
67
|
`,
|
|
68
68
|
{},
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { LS_DEV, shouldNeverHappen } from '@livestore/utils'
|
|
2
2
|
import { Chunk, Effect, Option, Schema } from '@livestore/utils/effect'
|
|
3
|
+
|
|
3
4
|
import type { SqliteDb } from '../adapter-types.ts'
|
|
5
|
+
import { migrateTable } from '../schema-management/migrations.ts'
|
|
4
6
|
import * as EventSequenceNumber from '../schema/EventSequenceNumber/mod.ts'
|
|
5
7
|
import * as LiveStoreEvent from '../schema/LiveStoreEvent/mod.ts'
|
|
6
8
|
import {
|
|
@@ -10,7 +12,6 @@ import {
|
|
|
10
12
|
SYNC_STATUS_TABLE,
|
|
11
13
|
} from '../schema/state/sqlite/system-tables/eventlog-tables.ts'
|
|
12
14
|
import { sessionChangesetMetaTable } from '../schema/state/sqlite/system-tables/state-tables.ts'
|
|
13
|
-
import { migrateTable } from '../schema-management/migrations.ts'
|
|
14
15
|
import { insertRow, updateRows } from '../sql-queries/sql-queries.ts'
|
|
15
16
|
import type { PreparedBindValues } from '../util.ts'
|
|
16
17
|
import { sql } from '../util.ts'
|
|
@@ -58,13 +59,14 @@ export const getEventsSince = ({
|
|
|
58
59
|
sessionChangesetMetaTable.where('seqNumGlobal', '>=', since.global),
|
|
59
60
|
)
|
|
60
61
|
|
|
62
|
+
// Create a Map for O(1) lookup instead of O(n) find
|
|
63
|
+
const sessionChangesetMap = new Map(
|
|
64
|
+
sessionChangesetRowsDecoded.map((row) => [`${row.seqNumGlobal}:${row.seqNumClient}`, row]),
|
|
65
|
+
)
|
|
66
|
+
|
|
61
67
|
return pendingEvents
|
|
62
68
|
.map((eventlogEvent) => {
|
|
63
|
-
const sessionChangeset =
|
|
64
|
-
(readModelEvent) =>
|
|
65
|
-
readModelEvent.seqNumGlobal === eventlogEvent.seqNumGlobal &&
|
|
66
|
-
readModelEvent.seqNumClient === eventlogEvent.seqNumClient,
|
|
67
|
-
)
|
|
69
|
+
const sessionChangeset = sessionChangesetMap.get(`${eventlogEvent.seqNumGlobal}:${eventlogEvent.seqNumClient}`)
|
|
68
70
|
return LiveStoreEvent.Client.EncodedWithMeta.make({
|
|
69
71
|
name: eventlogEvent.name,
|
|
70
72
|
args: eventlogEvent.argsJson,
|
|
@@ -82,7 +84,7 @@ export const getEventsSince = ({
|
|
|
82
84
|
sessionId: eventlogEvent.sessionId,
|
|
83
85
|
meta: {
|
|
84
86
|
sessionChangeset:
|
|
85
|
-
sessionChangeset && sessionChangeset.changeset !== null
|
|
87
|
+
sessionChangeset !== undefined && sessionChangeset.changeset !== null
|
|
86
88
|
? {
|
|
87
89
|
_tag: 'sessionChangeset' as const,
|
|
88
90
|
data: sessionChangeset.changeset,
|
|
@@ -96,7 +98,7 @@ export const getEventsSince = ({
|
|
|
96
98
|
})
|
|
97
99
|
})
|
|
98
100
|
.filter((_) => EventSequenceNumber.Client.compare(_.seqNum, since) > 0)
|
|
99
|
-
.
|
|
101
|
+
.toSorted((a, b) => EventSequenceNumber.Client.compare(a.seqNum, b.seqNum))
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
export const getEventsFromEventlog = ({
|
|
@@ -113,19 +115,19 @@ export const getEventsFromEventlog = ({
|
|
|
113
115
|
const makeQuery = () => {
|
|
114
116
|
let query = eventlogMetaTable.where('seqNumGlobal', '>', since.global)
|
|
115
117
|
|
|
116
|
-
if (options.until) {
|
|
118
|
+
if (options.until !== undefined) {
|
|
117
119
|
query = query.where('seqNumGlobal', '<=', options.until.global)
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
if (options.filter && options.filter.length > 0) {
|
|
122
|
+
if (options.filter !== undefined && options.filter.length > 0) {
|
|
121
123
|
query = query.where({ name: { op: 'IN', value: options.filter } })
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
if (options.clientIds && options.clientIds.length > 0) {
|
|
126
|
+
if (options.clientIds !== undefined && options.clientIds.length > 0) {
|
|
125
127
|
query = query.where({ clientId: { op: 'IN', value: options.clientIds } })
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
if (options.sessionIds && options.sessionIds.length > 0) {
|
|
130
|
+
if (options.sessionIds !== undefined && options.sessionIds.length > 0) {
|
|
129
131
|
query = query.where({ sessionId: { op: 'IN', value: options.sessionIds } })
|
|
130
132
|
}
|
|
131
133
|
|
|
@@ -185,7 +187,7 @@ export const getClientHeadFromDb = (dbEventlog: SqliteDb): EventSequenceNumber.C
|
|
|
185
187
|
sql`select seqNumGlobal, seqNumClient, seqNumRebaseGeneration from ${EVENTLOG_META_TABLE} order by seqNumGlobal DESC, seqNumClient DESC limit 1`,
|
|
186
188
|
)[0]
|
|
187
189
|
|
|
188
|
-
return res
|
|
190
|
+
return res !== undefined
|
|
189
191
|
? { global: res.seqNumGlobal, client: res.seqNumClient, rebaseGeneration: res.seqNumRebaseGeneration }
|
|
190
192
|
: EventSequenceNumber.Client.ROOT
|
|
191
193
|
}
|
|
@@ -215,7 +217,7 @@ export const insertIntoEventlog = (
|
|
|
215
217
|
) =>
|
|
216
218
|
Effect.gen(function* () {
|
|
217
219
|
// Check history consistency during LS_DEV
|
|
218
|
-
if (LS_DEV && eventEncoded.parentSeqNum.global !== EventSequenceNumber.Client.ROOT.global) {
|
|
220
|
+
if (LS_DEV === true && eventEncoded.parentSeqNum.global !== EventSequenceNumber.Client.ROOT.global) {
|
|
219
221
|
const parentEventExists =
|
|
220
222
|
dbEventlog.select<{ count: number }>(
|
|
221
223
|
`SELECT COUNT(*) as count FROM ${EVENTLOG_META_TABLE} WHERE seqNumGlobal = ? AND seqNumClient = ?`,
|
|
@@ -8,63 +8,91 @@ import { LeaderThreadCtx } from './types.ts'
|
|
|
8
8
|
|
|
9
9
|
type SendMessageToDevtools = (message: Devtools.Leader.MessageFromApp) => Effect.Effect<void>
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
yield* listenToDevtools({
|
|
21
|
-
incomingMessages: Stream.fromQueue(extraIncomingMessagesQueue),
|
|
22
|
-
sendMessage: () => Effect.void,
|
|
23
|
-
}).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
|
|
11
|
+
/**
|
|
12
|
+
* Type guard for DevtoolsViteNotInstalledError.
|
|
13
|
+
* This error is defined in @livestore/adapter-node but we need to handle it here.
|
|
14
|
+
*/
|
|
15
|
+
const isDevtoolsViteNotInstalledError = (
|
|
16
|
+
error: unknown,
|
|
17
|
+
): error is { _tag: 'DevtoolsViteNotInstalledError'; message: string } =>
|
|
18
|
+
typeof error === 'object' && error !== null && '_tag' in error && error._tag === 'DevtoolsViteNotInstalledError'
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Effect.withSpan('@livestore/common:leader-thread:devtools:sendToDevtools'),
|
|
46
|
-
Effect.interruptible,
|
|
47
|
-
Effect.ignoreLogged,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
const syncState = yield* syncProcessor.syncState
|
|
51
|
-
|
|
52
|
-
yield* syncProcessor.pull({ cursor: syncState.localHead }).pipe(
|
|
53
|
-
Stream.tap(({ payload }) => sendMessage(Devtools.Leader.SyncPull.make({ payload, liveStoreVersion }))),
|
|
54
|
-
Stream.runDrain,
|
|
55
|
-
Effect.forkScoped,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
yield* listenToDevtools({
|
|
59
|
-
incomingMessages: channel.listen.pipe(Stream.flatten(), Stream.orDie),
|
|
60
|
-
sendMessage,
|
|
61
|
-
persistenceInfo,
|
|
62
|
-
})
|
|
63
|
-
}).pipe(Effect.tapCauseLogPretty, Effect.forkScoped),
|
|
20
|
+
// TODO bind scope to the webchannel lifetime
|
|
21
|
+
export const bootDevtools = Effect.fn('@livestore/common:leader-thread:devtools:boot')(function* (
|
|
22
|
+
options: DevtoolsOptions,
|
|
23
|
+
) {
|
|
24
|
+
if (options.enabled === false) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { syncProcessor, extraIncomingMessagesQueue, clientId, storeId } = yield* LeaderThreadCtx
|
|
29
|
+
|
|
30
|
+
yield* listenToDevtools({
|
|
31
|
+
incomingMessages: Stream.fromQueue(extraIncomingMessagesQueue),
|
|
32
|
+
sendMessage: () => Effect.void,
|
|
33
|
+
}).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
|
|
34
|
+
|
|
35
|
+
const bootResult = yield* options.boot.pipe(
|
|
36
|
+
Effect.map(Option.some),
|
|
37
|
+
Effect.catchIf(isDevtoolsViteNotInstalledError, (error) =>
|
|
38
|
+
Effect.logWarning(`[@livestore/devtools] ${error.message} Devtools will be disabled.`).pipe(
|
|
39
|
+
Effect.as(Option.none()),
|
|
64
40
|
),
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
|
|
41
|
+
),
|
|
42
|
+
Effect.catchAllCause((cause) =>
|
|
43
|
+
Effect.logWarning(
|
|
44
|
+
`[@livestore/devtools] Failed to start devtools server. Devtools will be disabled.`,
|
|
45
|
+
cause,
|
|
46
|
+
).pipe(Effect.as(Option.none())),
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if (Option.isNone(bootResult) === true) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { node, persistenceInfo, mode } = bootResult.value
|
|
55
|
+
|
|
56
|
+
yield* node.listenForChannel.pipe(
|
|
57
|
+
Stream.filter(
|
|
58
|
+
(res) => Devtools.isChannelName.devtoolsClientLeader(res.channelName, { storeId, clientId }) && res.mode === mode,
|
|
59
|
+
),
|
|
60
|
+
Stream.tap(({ channelName, source }) =>
|
|
61
|
+
Effect.gen(function* () {
|
|
62
|
+
const channel = yield* node.makeChannel({
|
|
63
|
+
target: source,
|
|
64
|
+
channelName,
|
|
65
|
+
schema: { listen: Devtools.Leader.MessageToApp, send: Devtools.Leader.MessageFromApp },
|
|
66
|
+
mode,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const sendMessage: SendMessageToDevtools = (message) =>
|
|
70
|
+
channel
|
|
71
|
+
.send(message)
|
|
72
|
+
.pipe(
|
|
73
|
+
Effect.withSpan('@livestore/common:leader-thread:devtools:sendToDevtools'),
|
|
74
|
+
Effect.interruptible,
|
|
75
|
+
Effect.ignoreLogged,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
const syncState = yield* syncProcessor.syncState
|
|
79
|
+
|
|
80
|
+
yield* syncProcessor.pull({ cursor: syncState.localHead }).pipe(
|
|
81
|
+
Stream.tap(({ payload }) => sendMessage(Devtools.Leader.SyncPull.make({ payload, liveStoreVersion }))),
|
|
82
|
+
Stream.runDrain,
|
|
83
|
+
Effect.forkScoped,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
yield* listenToDevtools({
|
|
87
|
+
incomingMessages: channel.listen.pipe(Stream.flatten(), Stream.orDie),
|
|
88
|
+
sendMessage,
|
|
89
|
+
persistenceInfo,
|
|
90
|
+
})
|
|
91
|
+
}).pipe(Effect.tapCauseLogPretty, Effect.forkScoped),
|
|
92
|
+
),
|
|
93
|
+
Stream.runDrain,
|
|
94
|
+
)
|
|
95
|
+
})
|
|
68
96
|
|
|
69
97
|
const listenToDevtools = ({
|
|
70
98
|
incomingMessages,
|
|
@@ -103,7 +131,7 @@ const listenToDevtools = ({
|
|
|
103
131
|
loadDatabaseBatchTracker.set(batchId, entry)
|
|
104
132
|
const finished = entry.has('state') && entry.has('eventlog')
|
|
105
133
|
|
|
106
|
-
if (finished) {
|
|
134
|
+
if (finished === true) {
|
|
107
135
|
loadDatabaseBatchTracker.delete(batchId)
|
|
108
136
|
}
|
|
109
137
|
|
|
@@ -129,7 +157,7 @@ const listenToDevtools = ({
|
|
|
129
157
|
// So far I could only observe this problem with webmesh proxy channels (e.g. for Expo)
|
|
130
158
|
// Proof: https://share.cleanshot.com/V9G87B0B
|
|
131
159
|
// Also see `store/devtools.ts` for same problem
|
|
132
|
-
if (handledRequestIds.has(requestId)) {
|
|
160
|
+
if (handledRequestIds.has(requestId) === true) {
|
|
133
161
|
// yield* Effect.logWarning(`Duplicate message`, decodedEvent)
|
|
134
162
|
return
|
|
135
163
|
}
|
|
@@ -138,6 +166,17 @@ const listenToDevtools = ({
|
|
|
138
166
|
|
|
139
167
|
switch (decodedEvent._tag) {
|
|
140
168
|
case 'LSD.Leader.Ping': {
|
|
169
|
+
// Check version mismatch and respond with VersionMismatch if versions don't match
|
|
170
|
+
if (decodedEvent.liveStoreVersion !== liveStoreVersion) {
|
|
171
|
+
yield* sendMessage(
|
|
172
|
+
Devtools.Leader.VersionMismatch.make({
|
|
173
|
+
...reqPayload,
|
|
174
|
+
appVersion: liveStoreVersion,
|
|
175
|
+
receivedVersion: decodedEvent.liveStoreVersion,
|
|
176
|
+
}),
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
141
180
|
yield* sendMessage(Devtools.Leader.Pong.make({ ...reqPayload }))
|
|
142
181
|
return
|
|
143
182
|
}
|
|
@@ -166,24 +205,24 @@ const listenToDevtools = ({
|
|
|
166
205
|
|
|
167
206
|
let databaseKind: LoadDatabaseKind | undefined
|
|
168
207
|
|
|
169
|
-
if (tableNames.has(SystemTables.EVENTLOG_META_TABLE)) {
|
|
208
|
+
if (tableNames.has(SystemTables.EVENTLOG_META_TABLE) === true) {
|
|
170
209
|
databaseKind = 'eventlog'
|
|
171
210
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
172
|
-
yield* Effect.try(() =>
|
|
211
|
+
yield* Effect.try(() => dbEventlog.import(data))
|
|
173
212
|
|
|
174
213
|
if (batchId === undefined) {
|
|
175
|
-
yield* Effect.try(() =>
|
|
214
|
+
yield* Effect.try(() => dbState.destroy())
|
|
176
215
|
}
|
|
177
216
|
} else if (
|
|
178
|
-
tableNames.has(SystemTables.SCHEMA_META_TABLE) &&
|
|
179
|
-
tableNames.has(SystemTables.SCHEMA_EVENT_DEFS_META_TABLE)
|
|
217
|
+
tableNames.has(SystemTables.SCHEMA_META_TABLE) === true &&
|
|
218
|
+
tableNames.has(SystemTables.SCHEMA_EVENT_DEFS_META_TABLE) === true
|
|
180
219
|
) {
|
|
181
220
|
databaseKind = 'state'
|
|
182
221
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
183
|
-
yield* Effect.try(() =>
|
|
222
|
+
yield* Effect.try(() => dbState.import(data))
|
|
184
223
|
|
|
185
224
|
if (batchId === undefined) {
|
|
186
|
-
yield* Effect.try(() =>
|
|
225
|
+
yield* Effect.try(() => dbEventlog.destroy())
|
|
187
226
|
}
|
|
188
227
|
} else {
|
|
189
228
|
return yield* Effect.fail({ _tag: 'unsupported-database' } as const)
|
|
@@ -199,7 +238,7 @@ const listenToDevtools = ({
|
|
|
199
238
|
|
|
200
239
|
yield* sendMessage(Devtools.Leader.LoadDatabaseFile.Success.make({ ...reqPayload }))
|
|
201
240
|
|
|
202
|
-
if (shouldShutdown) {
|
|
241
|
+
if (shouldShutdown === true) {
|
|
203
242
|
yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-import' }))
|
|
204
243
|
}
|
|
205
244
|
})
|
|
@@ -341,7 +380,9 @@ const listenToDevtools = ({
|
|
|
341
380
|
|
|
342
381
|
yield* Stream.zipLatest(
|
|
343
382
|
syncBackend.isConnected.changes,
|
|
344
|
-
devtools.enabled
|
|
383
|
+
devtools.enabled === true
|
|
384
|
+
? devtools.syncBackendLatchState.changes
|
|
385
|
+
: Stream.make({ latchClosed: false }),
|
|
345
386
|
).pipe(
|
|
346
387
|
Stream.tap(([isConnected, { latchClosed }]) =>
|
|
347
388
|
sendMessage(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Effect, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
|
2
1
|
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
2
|
+
import { Effect, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
|
3
3
|
|
|
4
4
|
import { makeMockSyncBackend } from '../sync/mock-sync-backend.ts'
|
|
5
5
|
import type { SyncBackend } from '../sync/sync.ts'
|
|
@@ -12,19 +12,19 @@ import {
|
|
|
12
12
|
Subscribable,
|
|
13
13
|
SubscriptionRef,
|
|
14
14
|
} from '@livestore/utils/effect'
|
|
15
|
+
|
|
15
16
|
import {
|
|
16
17
|
type BootStatus,
|
|
17
18
|
type MakeSqliteDb,
|
|
18
19
|
type MaterializerHashMismatchError,
|
|
19
20
|
type SqliteDb,
|
|
20
|
-
type SqliteError,
|
|
21
21
|
UnknownError,
|
|
22
22
|
} from '../adapter-types.ts'
|
|
23
23
|
import type { MigrationsReport } from '../defs.ts'
|
|
24
24
|
import type * as Devtools from '../devtools/mod.ts'
|
|
25
25
|
import type { LiveStoreSchema } from '../schema/mod.ts'
|
|
26
26
|
import { EventSequenceNumber, LiveStoreEvent, SystemTables } from '../schema/mod.ts'
|
|
27
|
-
import type {
|
|
27
|
+
import type { SyncBackend, SyncOptions } from '../sync/sync.ts'
|
|
28
28
|
import { SyncState } from '../sync/syncstate.ts'
|
|
29
29
|
import { sql } from '../util.ts'
|
|
30
30
|
import * as Eventlog from './eventlog.ts'
|
|
@@ -55,6 +55,8 @@ export interface MakeLeaderThreadLayerParams {
|
|
|
55
55
|
dbEventlog: LeaderSqliteDb
|
|
56
56
|
devtoolsOptions: DevtoolsOptions
|
|
57
57
|
shutdownChannel: ShutdownChannel
|
|
58
|
+
/** Boot warning to emit (e.g., OPFS unavailable in private browsing) */
|
|
59
|
+
bootWarning?: BootStatus
|
|
58
60
|
params?: {
|
|
59
61
|
localPushBatchSize?: number
|
|
60
62
|
backendPushBatchSize?: number
|
|
@@ -80,6 +82,7 @@ export const makeLeaderThreadLayer = ({
|
|
|
80
82
|
dbEventlog,
|
|
81
83
|
devtoolsOptions,
|
|
82
84
|
shutdownChannel,
|
|
85
|
+
bootWarning,
|
|
83
86
|
params,
|
|
84
87
|
testing,
|
|
85
88
|
}: MakeLeaderThreadLayerParams): Layer.Layer<LeaderThreadCtx, UnknownError, Scope.Scope | HttpClient.HttpClient> =>
|
|
@@ -89,6 +92,11 @@ export const makeLeaderThreadLayer = ({
|
|
|
89
92
|
|
|
90
93
|
const bootStatusQueue = yield* Queue.unbounded<BootStatus>().pipe(Effect.acquireRelease(Queue.shutdown))
|
|
91
94
|
|
|
95
|
+
// Emit boot warning if present (e.g., OPFS unavailable in private browsing)
|
|
96
|
+
if (bootWarning !== undefined) {
|
|
97
|
+
yield* Queue.offer(bootStatusQueue, bootWarning)
|
|
98
|
+
}
|
|
99
|
+
|
|
92
100
|
const dbEventlogMissing = !hasEventlogTables(dbEventlog)
|
|
93
101
|
|
|
94
102
|
// Either happens on initial boot or if schema changes
|
|
@@ -148,9 +156,10 @@ export const makeLeaderThreadLayer = ({
|
|
|
148
156
|
|
|
149
157
|
// Recreate state database if needed BEFORE creating sync processor
|
|
150
158
|
// This ensures all system tables exist before any queries are made
|
|
151
|
-
const { migrationsReport } =
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
const { migrationsReport } =
|
|
160
|
+
dbStateMissing === true
|
|
161
|
+
? yield* recreateDb({ dbState, dbEventlog, schema, bootStatusQueue, materializeEvent })
|
|
162
|
+
: { migrationsReport: { migrations: [] } }
|
|
154
163
|
|
|
155
164
|
const syncProcessor = yield* makeLeaderSyncProcessor({
|
|
156
165
|
schema,
|
|
@@ -158,6 +167,7 @@ export const makeLeaderThreadLayer = ({
|
|
|
158
167
|
initialSyncState: getInitialSyncState({ dbEventlog, dbState, dbEventlogMissing }),
|
|
159
168
|
initialBlockingSyncContext,
|
|
160
169
|
onError: syncOptions?.onSyncError ?? 'ignore',
|
|
170
|
+
onBackendIdMismatch: syncOptions?.onBackendIdMismatch ?? 'reset',
|
|
161
171
|
livePull: syncOptions?.livePull ?? true,
|
|
162
172
|
params: {
|
|
163
173
|
...omitUndefineds({
|
|
@@ -174,13 +184,14 @@ export const makeLeaderThreadLayer = ({
|
|
|
174
184
|
Effect.acquireRelease(Queue.shutdown),
|
|
175
185
|
)
|
|
176
186
|
|
|
177
|
-
const devtoolsContext =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
187
|
+
const devtoolsContext =
|
|
188
|
+
devtoolsOptions.enabled === true
|
|
189
|
+
? {
|
|
190
|
+
enabled: true as const,
|
|
191
|
+
syncBackendLatch: yield* Effect.makeLatch(true),
|
|
192
|
+
syncBackendLatchState: yield* SubscriptionRef.make<{ latchClosed: boolean }>({ latchClosed: false }),
|
|
193
|
+
}
|
|
194
|
+
: { enabled: false as const }
|
|
184
195
|
|
|
185
196
|
const networkStatus = yield* makeNetworkStatusSubscribable({ syncBackend, devtoolsContext })
|
|
186
197
|
|
|
@@ -239,7 +250,7 @@ const hasStateTables = (db: SqliteDb) => {
|
|
|
239
250
|
|
|
240
251
|
const isSubsetOf = (a: Set<string>, b: Set<string>): boolean => {
|
|
241
252
|
for (const item of a) {
|
|
242
|
-
if (
|
|
253
|
+
if (b.has(item) === false) {
|
|
243
254
|
return false
|
|
244
255
|
}
|
|
245
256
|
}
|
|
@@ -256,13 +267,11 @@ const getInitialSyncState = ({
|
|
|
256
267
|
dbState: SqliteDb
|
|
257
268
|
dbEventlogMissing: boolean
|
|
258
269
|
}) => {
|
|
259
|
-
const initialBackendHead =
|
|
260
|
-
? EventSequenceNumber.Client.ROOT.global
|
|
261
|
-
: Eventlog.getBackendHeadFromDb(dbEventlog)
|
|
270
|
+
const initialBackendHead =
|
|
271
|
+
dbEventlogMissing === true ? EventSequenceNumber.Client.ROOT.global : Eventlog.getBackendHeadFromDb(dbEventlog)
|
|
262
272
|
|
|
263
|
-
const initialLocalHead =
|
|
264
|
-
? EventSequenceNumber.Client.ROOT
|
|
265
|
-
: Eventlog.getClientHeadFromDb(dbEventlog)
|
|
273
|
+
const initialLocalHead =
|
|
274
|
+
dbEventlogMissing === true ? EventSequenceNumber.Client.ROOT : Eventlog.getClientHeadFromDb(dbEventlog)
|
|
266
275
|
|
|
267
276
|
if (initialBackendHead > initialLocalHead.global) {
|
|
268
277
|
return shouldNeverHappen(
|
|
@@ -277,17 +286,18 @@ const getInitialSyncState = ({
|
|
|
277
286
|
client: EventSequenceNumber.Client.DEFAULT,
|
|
278
287
|
rebaseGeneration: EventSequenceNumber.Client.REBASE_GENERATION_DEFAULT,
|
|
279
288
|
},
|
|
280
|
-
pending:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
pending:
|
|
290
|
+
dbEventlogMissing === true
|
|
291
|
+
? []
|
|
292
|
+
: Eventlog.getEventsSince({
|
|
293
|
+
dbEventlog,
|
|
294
|
+
dbState,
|
|
295
|
+
since: {
|
|
296
|
+
global: initialBackendHead,
|
|
297
|
+
client: EventSequenceNumber.Client.DEFAULT,
|
|
298
|
+
rebaseGeneration: initialLocalHead.rebaseGeneration,
|
|
299
|
+
},
|
|
300
|
+
}),
|
|
291
301
|
})
|
|
292
302
|
}
|
|
293
303
|
|
|
@@ -352,7 +362,7 @@ const bootLeaderThread = ({
|
|
|
352
362
|
devtoolsOptions: DevtoolsOptions
|
|
353
363
|
}): Effect.Effect<
|
|
354
364
|
LeaderThreadCtx['Type']['initialState'],
|
|
355
|
-
UnknownError |
|
|
365
|
+
UnknownError | MaterializerHashMismatchError,
|
|
356
366
|
LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
|
|
357
367
|
> =>
|
|
358
368
|
Effect.gen(function* () {
|
|
@@ -3,6 +3,7 @@ import { Effect, Option, ReadonlyArray, Schema } from '@livestore/utils/effect'
|
|
|
3
3
|
|
|
4
4
|
import { MaterializeError, MaterializerHashMismatchError, type SqliteDb } from '../adapter-types.ts'
|
|
5
5
|
import { getExecStatementsFromMaterializer, hashMaterializerResults } from '../materializer-helper.ts'
|
|
6
|
+
import { logDeprecationWarnings } from '../schema/EventDef/deprecated.ts'
|
|
6
7
|
import type { LiveStoreSchema } from '../schema/mod.ts'
|
|
7
8
|
import { EventSequenceNumber, resolveEventDef, SystemTables, UNKNOWN_EVENT_SCHEMA_HASH } from '../schema/mod.ts'
|
|
8
9
|
import { insertRow } from '../sql-queries/index.ts'
|
|
@@ -62,6 +63,9 @@ export const makeMaterializeEvent = ({
|
|
|
62
63
|
|
|
63
64
|
const { eventDef, materializer } = resolution
|
|
64
65
|
|
|
66
|
+
// Log deprecation warnings for deprecated events/fields
|
|
67
|
+
yield* logDeprecationWarnings(eventDef, eventEncoded.args as Record<string, unknown>)
|
|
68
|
+
|
|
65
69
|
const execArgsArr = getExecStatementsFromMaterializer({
|
|
66
70
|
eventDef,
|
|
67
71
|
materializer,
|
|
@@ -69,7 +73,7 @@ export const makeMaterializeEvent = ({
|
|
|
69
73
|
event: { decoded: undefined, encoded: eventEncoded },
|
|
70
74
|
})
|
|
71
75
|
|
|
72
|
-
const materializerHash = isDevEnv() ? Option.some(hashMaterializerResults(execArgsArr)) : Option.none()
|
|
76
|
+
const materializerHash = isDevEnv() === true ? Option.some(hashMaterializerResults(execArgsArr)) : Option.none()
|
|
73
77
|
|
|
74
78
|
if (
|
|
75
79
|
materializerHash._tag === 'Some' &&
|
|
@@ -115,7 +119,7 @@ export const makeMaterializeEvent = ({
|
|
|
115
119
|
seqNumRebaseGeneration: eventEncoded.seqNum.rebaseGeneration,
|
|
116
120
|
// NOTE the changeset will be empty (i.e. null) for no-op events
|
|
117
121
|
changeset: changeset ?? null,
|
|
118
|
-
debug: LS_DEV ? execArgsArr : null,
|
|
122
|
+
debug: LS_DEV === true ? execArgsArr : null,
|
|
119
123
|
},
|
|
120
124
|
}),
|
|
121
125
|
)
|
|
@@ -140,11 +144,11 @@ export const makeMaterializeEvent = ({
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
return {
|
|
143
|
-
sessionChangeset: changeset
|
|
147
|
+
sessionChangeset: changeset !== undefined
|
|
144
148
|
? {
|
|
145
149
|
_tag: 'sessionChangeset' as const,
|
|
146
150
|
data: changeset,
|
|
147
|
-
debug: LS_DEV ? execArgsArr : null,
|
|
151
|
+
debug: LS_DEV === true ? execArgsArr : null,
|
|
148
152
|
}
|
|
149
153
|
: { _tag: 'no-op' as const },
|
|
150
154
|
hash: materializerHash,
|
|
@@ -31,7 +31,7 @@ export const recreateDb = ({
|
|
|
31
31
|
}): Effect.Effect<{ migrationsReport: MigrationsReport }, UnknownError | MaterializeError | SqliteError> =>
|
|
32
32
|
Effect.gen(function* () {
|
|
33
33
|
const migrationOptions = schema.state.sqlite.migrations
|
|
34
|
-
let migrationsReport: MigrationsReport
|
|
34
|
+
let migrationsReport: MigrationsReport = { migrations: [] }
|
|
35
35
|
|
|
36
36
|
yield* Effect.addFinalizer(
|
|
37
37
|
Effect.fn('recreateDb:finalizer')(function* (ex) {
|