@livestore/common 0.4.0-dev.22 → 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 +9 -9
- package/dist/ClientSessionLeaderThreadProxy.d.ts.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 +3 -3
- package/dist/adapter-types.d.ts.map +1 -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-common.js +1 -1
- package/dist/devtools/devtools-messages-common.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 +287 -257
- 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 +25 -14
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +8 -3
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +7 -10
- 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.js +4 -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 +2 -2
- package/dist/schema/EventDef/define.d.ts.map +1 -1
- package/dist/schema/EventDef/define.js +4 -4
- package/dist/schema/EventDef/define.js.map +1 -1
- package/dist/schema/EventDef/deprecated.js +3 -3
- package/dist/schema/EventDef/deprecated.js.map +1 -1
- package/dist/schema/EventDef/deprecated.test.js +1 -1
- package/dist/schema/EventDef/deprecated.test.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.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.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +18 -6
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +1 -1
- 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 +10 -2
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.js +11 -11
- 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 +3 -2
- 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 +95 -113
- 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.map +1 -1
- package/dist/version.js +2 -5
- package/dist/version.js.map +1 -1
- package/package.json +66 -12
- package/src/ClientSessionLeaderThreadProxy.ts +9 -9
- package/src/WorkerTransportError.ts +12 -0
- package/src/adapter-types.ts +9 -3
- package/src/bounded-collections.ts +6 -5
- package/src/debug-info.ts +4 -4
- package/src/devtools/devtools-messages-common.ts +1 -1
- 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 +421 -392
- 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 +96 -66
- package/src/leader-thread/make-leader-thread-layer.test.ts +1 -1
- package/src/leader-thread/make-leader-thread-layer.ts +33 -31
- package/src/leader-thread/materialize-event.ts +4 -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 +6 -6
- package/src/schema/EventDef/deprecated.test.ts +2 -1
- package/src/schema/EventDef/deprecated.ts +3 -3
- 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 +2 -1
- package/src/schema/state/sqlite/client-document-def.ts +20 -6
- 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 +12 -5
- package/src/schema/state/sqlite/query-builder/astToSql.ts +11 -11
- package/src/schema/state/sqlite/query-builder/impl.test.ts +4 -2
- 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 +148 -152
- 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 +2 -6
|
@@ -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)
|
|
24
|
-
|
|
25
|
-
const { node, persistenceInfo, mode } = yield* options.boot
|
|
26
|
-
|
|
27
|
-
yield* node.listenForChannel.pipe(
|
|
28
|
-
Stream.filter(
|
|
29
|
-
(res) =>
|
|
30
|
-
Devtools.isChannelName.devtoolsClientLeader(res.channelName, { storeId, clientId }) && res.mode === mode,
|
|
31
|
-
),
|
|
32
|
-
Stream.tap(({ channelName, source }) =>
|
|
33
|
-
Effect.gen(function* () {
|
|
34
|
-
const channel = yield* node.makeChannel({
|
|
35
|
-
target: source,
|
|
36
|
-
channelName,
|
|
37
|
-
schema: { listen: Devtools.Leader.MessageToApp, send: Devtools.Leader.MessageFromApp },
|
|
38
|
-
mode,
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
const sendMessage: SendMessageToDevtools = (message) =>
|
|
42
|
-
channel
|
|
43
|
-
.send(message)
|
|
44
|
-
.pipe(
|
|
45
|
-
Effect.withSpan('@livestore/common:leader-thread:devtools:sendToDevtools'),
|
|
46
|
-
Effect.interruptible,
|
|
47
|
-
Effect.ignoreLogged,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
const syncState = yield* syncProcessor.syncState
|
|
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'
|
|
51
19
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
}
|
|
@@ -177,24 +205,24 @@ const listenToDevtools = ({
|
|
|
177
205
|
|
|
178
206
|
let databaseKind: LoadDatabaseKind | undefined
|
|
179
207
|
|
|
180
|
-
if (tableNames.has(SystemTables.EVENTLOG_META_TABLE)) {
|
|
208
|
+
if (tableNames.has(SystemTables.EVENTLOG_META_TABLE) === true) {
|
|
181
209
|
databaseKind = 'eventlog'
|
|
182
210
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
183
|
-
yield* Effect.try(() =>
|
|
211
|
+
yield* Effect.try(() => dbEventlog.import(data))
|
|
184
212
|
|
|
185
213
|
if (batchId === undefined) {
|
|
186
|
-
yield* Effect.try(() =>
|
|
214
|
+
yield* Effect.try(() => dbState.destroy())
|
|
187
215
|
}
|
|
188
216
|
} else if (
|
|
189
|
-
tableNames.has(SystemTables.SCHEMA_META_TABLE) &&
|
|
190
|
-
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
|
|
191
219
|
) {
|
|
192
220
|
databaseKind = 'state'
|
|
193
221
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
194
|
-
yield* Effect.try(() =>
|
|
222
|
+
yield* Effect.try(() => dbState.import(data))
|
|
195
223
|
|
|
196
224
|
if (batchId === undefined) {
|
|
197
|
-
yield* Effect.try(() =>
|
|
225
|
+
yield* Effect.try(() => dbEventlog.destroy())
|
|
198
226
|
}
|
|
199
227
|
} else {
|
|
200
228
|
return yield* Effect.fail({ _tag: 'unsupported-database' } as const)
|
|
@@ -210,7 +238,7 @@ const listenToDevtools = ({
|
|
|
210
238
|
|
|
211
239
|
yield* sendMessage(Devtools.Leader.LoadDatabaseFile.Success.make({ ...reqPayload }))
|
|
212
240
|
|
|
213
|
-
if (shouldShutdown) {
|
|
241
|
+
if (shouldShutdown === true) {
|
|
214
242
|
yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-import' }))
|
|
215
243
|
}
|
|
216
244
|
})
|
|
@@ -352,7 +380,9 @@ const listenToDevtools = ({
|
|
|
352
380
|
|
|
353
381
|
yield* Stream.zipLatest(
|
|
354
382
|
syncBackend.isConnected.changes,
|
|
355
|
-
devtools.enabled
|
|
383
|
+
devtools.enabled === true
|
|
384
|
+
? devtools.syncBackendLatchState.changes
|
|
385
|
+
: Stream.make({ latchClosed: false }),
|
|
356
386
|
).pipe(
|
|
357
387
|
Stream.tap(([isConnected, { latchClosed }]) =>
|
|
358
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'
|
|
@@ -156,9 +156,10 @@ export const makeLeaderThreadLayer = ({
|
|
|
156
156
|
|
|
157
157
|
// Recreate state database if needed BEFORE creating sync processor
|
|
158
158
|
// This ensures all system tables exist before any queries are made
|
|
159
|
-
const { migrationsReport } =
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
const { migrationsReport } =
|
|
160
|
+
dbStateMissing === true
|
|
161
|
+
? yield* recreateDb({ dbState, dbEventlog, schema, bootStatusQueue, materializeEvent })
|
|
162
|
+
: { migrationsReport: { migrations: [] } }
|
|
162
163
|
|
|
163
164
|
const syncProcessor = yield* makeLeaderSyncProcessor({
|
|
164
165
|
schema,
|
|
@@ -166,6 +167,7 @@ export const makeLeaderThreadLayer = ({
|
|
|
166
167
|
initialSyncState: getInitialSyncState({ dbEventlog, dbState, dbEventlogMissing }),
|
|
167
168
|
initialBlockingSyncContext,
|
|
168
169
|
onError: syncOptions?.onSyncError ?? 'ignore',
|
|
170
|
+
onBackendIdMismatch: syncOptions?.onBackendIdMismatch ?? 'reset',
|
|
169
171
|
livePull: syncOptions?.livePull ?? true,
|
|
170
172
|
params: {
|
|
171
173
|
...omitUndefineds({
|
|
@@ -182,13 +184,14 @@ export const makeLeaderThreadLayer = ({
|
|
|
182
184
|
Effect.acquireRelease(Queue.shutdown),
|
|
183
185
|
)
|
|
184
186
|
|
|
185
|
-
const devtoolsContext =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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 }
|
|
192
195
|
|
|
193
196
|
const networkStatus = yield* makeNetworkStatusSubscribable({ syncBackend, devtoolsContext })
|
|
194
197
|
|
|
@@ -247,7 +250,7 @@ const hasStateTables = (db: SqliteDb) => {
|
|
|
247
250
|
|
|
248
251
|
const isSubsetOf = (a: Set<string>, b: Set<string>): boolean => {
|
|
249
252
|
for (const item of a) {
|
|
250
|
-
if (
|
|
253
|
+
if (b.has(item) === false) {
|
|
251
254
|
return false
|
|
252
255
|
}
|
|
253
256
|
}
|
|
@@ -264,13 +267,11 @@ const getInitialSyncState = ({
|
|
|
264
267
|
dbState: SqliteDb
|
|
265
268
|
dbEventlogMissing: boolean
|
|
266
269
|
}) => {
|
|
267
|
-
const initialBackendHead =
|
|
268
|
-
? EventSequenceNumber.Client.ROOT.global
|
|
269
|
-
: Eventlog.getBackendHeadFromDb(dbEventlog)
|
|
270
|
+
const initialBackendHead =
|
|
271
|
+
dbEventlogMissing === true ? EventSequenceNumber.Client.ROOT.global : Eventlog.getBackendHeadFromDb(dbEventlog)
|
|
270
272
|
|
|
271
|
-
const initialLocalHead =
|
|
272
|
-
? EventSequenceNumber.Client.ROOT
|
|
273
|
-
: Eventlog.getClientHeadFromDb(dbEventlog)
|
|
273
|
+
const initialLocalHead =
|
|
274
|
+
dbEventlogMissing === true ? EventSequenceNumber.Client.ROOT : Eventlog.getClientHeadFromDb(dbEventlog)
|
|
274
275
|
|
|
275
276
|
if (initialBackendHead > initialLocalHead.global) {
|
|
276
277
|
return shouldNeverHappen(
|
|
@@ -285,17 +286,18 @@ const getInitialSyncState = ({
|
|
|
285
286
|
client: EventSequenceNumber.Client.DEFAULT,
|
|
286
287
|
rebaseGeneration: EventSequenceNumber.Client.REBASE_GENERATION_DEFAULT,
|
|
287
288
|
},
|
|
288
|
-
pending:
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
+
}),
|
|
299
301
|
})
|
|
300
302
|
}
|
|
301
303
|
|
|
@@ -360,7 +362,7 @@ const bootLeaderThread = ({
|
|
|
360
362
|
devtoolsOptions: DevtoolsOptions
|
|
361
363
|
}): Effect.Effect<
|
|
362
364
|
LeaderThreadCtx['Type']['initialState'],
|
|
363
|
-
UnknownError |
|
|
365
|
+
UnknownError | MaterializerHashMismatchError,
|
|
364
366
|
LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
|
|
365
367
|
> =>
|
|
366
368
|
Effect.gen(function* () {
|
|
@@ -73,7 +73,7 @@ export const makeMaterializeEvent = ({
|
|
|
73
73
|
event: { decoded: undefined, encoded: eventEncoded },
|
|
74
74
|
})
|
|
75
75
|
|
|
76
|
-
const materializerHash = isDevEnv() ? Option.some(hashMaterializerResults(execArgsArr)) : Option.none()
|
|
76
|
+
const materializerHash = isDevEnv() === true ? Option.some(hashMaterializerResults(execArgsArr)) : Option.none()
|
|
77
77
|
|
|
78
78
|
if (
|
|
79
79
|
materializerHash._tag === 'Some' &&
|
|
@@ -119,7 +119,7 @@ export const makeMaterializeEvent = ({
|
|
|
119
119
|
seqNumRebaseGeneration: eventEncoded.seqNum.rebaseGeneration,
|
|
120
120
|
// NOTE the changeset will be empty (i.e. null) for no-op events
|
|
121
121
|
changeset: changeset ?? null,
|
|
122
|
-
debug: LS_DEV ? execArgsArr : null,
|
|
122
|
+
debug: LS_DEV === true ? execArgsArr : null,
|
|
123
123
|
},
|
|
124
124
|
}),
|
|
125
125
|
)
|
|
@@ -144,11 +144,11 @@ export const makeMaterializeEvent = ({
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
return {
|
|
147
|
-
sessionChangeset: changeset
|
|
147
|
+
sessionChangeset: changeset !== undefined
|
|
148
148
|
? {
|
|
149
149
|
_tag: 'sessionChangeset' as const,
|
|
150
150
|
data: changeset,
|
|
151
|
-
debug: LS_DEV ? execArgsArr : null,
|
|
151
|
+
debug: LS_DEV === true ? execArgsArr : null,
|
|
152
152
|
}
|
|
153
153
|
: { _tag: 'no-op' as const },
|
|
154
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) {
|
|
@@ -2,10 +2,8 @@ import type { WebChannel } from '@livestore/utils/effect'
|
|
|
2
2
|
import { Schema } from '@livestore/utils/effect'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
BackendIdMismatchError,
|
|
5
6
|
IntentionalShutdownCause,
|
|
6
|
-
InvalidPullError,
|
|
7
|
-
InvalidPushError,
|
|
8
|
-
IsOfflineError,
|
|
9
7
|
MaterializeError,
|
|
10
8
|
UnknownError,
|
|
11
9
|
} from '../index.ts'
|
|
@@ -13,9 +11,7 @@ import {
|
|
|
13
11
|
export class All extends Schema.Union(
|
|
14
12
|
IntentionalShutdownCause,
|
|
15
13
|
UnknownError,
|
|
16
|
-
|
|
17
|
-
InvalidPushError,
|
|
18
|
-
InvalidPullError,
|
|
14
|
+
BackendIdMismatchError,
|
|
19
15
|
MaterializeError,
|
|
20
16
|
) {}
|
|
21
17
|
|