@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Subscribable } from '@livestore/utils/effect'
|
|
2
2
|
import { Chunk, Effect, Option, Queue, Stream } from '@livestore/utils/effect'
|
|
3
|
+
|
|
3
4
|
import { EventSequenceNumber, type LiveStoreEvent } from '../schema/mod.ts'
|
|
4
5
|
import type * as SyncState from '../sync/syncstate.ts'
|
|
5
6
|
import * as Eventlog from './eventlog.ts'
|
|
@@ -109,7 +110,10 @@ export const streamEventsWithSyncState = ({
|
|
|
109
110
|
* since === until : Prevent empty query
|
|
110
111
|
* since > until : Incorrectly inverted interval
|
|
111
112
|
*/
|
|
112
|
-
if (
|
|
113
|
+
if (
|
|
114
|
+
options.until !== undefined &&
|
|
115
|
+
EventSequenceNumber.Client.isGreaterThanOrEqual(cursor, options.until) === true
|
|
116
|
+
) {
|
|
113
117
|
return [Chunk.empty(), Option.none()]
|
|
114
118
|
}
|
|
115
119
|
|
|
@@ -144,9 +148,10 @@ export const streamEventsWithSyncState = ({
|
|
|
144
148
|
* nextHead: The latest head from headQueue
|
|
145
149
|
*/
|
|
146
150
|
const waitForHead = EventSequenceNumber.Client.isGreaterThanOrEqual(cursor, head)
|
|
147
|
-
const maybeHead =
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
const maybeHead =
|
|
152
|
+
waitForHead === true
|
|
153
|
+
? yield* Queue.take(headQueue).pipe(Effect.map(Option.some))
|
|
154
|
+
: yield* Queue.poll(headQueue)
|
|
150
155
|
const nextHead = Option.getOrElse(maybeHead, () => head)
|
|
151
156
|
const hardStop = options.until?.global ?? Number.POSITIVE_INFINITY
|
|
152
157
|
const target = EventSequenceNumber.Client.Composite.make({
|
|
@@ -181,7 +186,7 @@ export const streamEventsWithSyncState = ({
|
|
|
181
186
|
const nextState: Option.Option<{
|
|
182
187
|
cursor: EventSequenceNumber.Client.Composite
|
|
183
188
|
head: EventSequenceNumber.Client.Composite
|
|
184
|
-
}> = reachedUntil ? Option.none() : Option.some({ cursor: target, head: nextHead })
|
|
189
|
+
}> = reachedUntil === true ? Option.none() : Option.some({ cursor: target, head: nextHead })
|
|
185
190
|
|
|
186
191
|
const spanAttributes = {
|
|
187
192
|
'livestore.streamEvents.cursor.global': cursor.global,
|
|
@@ -17,13 +17,14 @@ import type { MaterializeError } from '../errors.ts'
|
|
|
17
17
|
import type {
|
|
18
18
|
BootStatus,
|
|
19
19
|
Devtools,
|
|
20
|
-
LeaderAheadError,
|
|
21
20
|
MakeSqliteDb,
|
|
22
21
|
PersistenceInfo,
|
|
23
22
|
SqliteDb,
|
|
24
23
|
SyncBackend,
|
|
25
24
|
UnknownError,
|
|
25
|
+
UnknownEventError,
|
|
26
26
|
} from '../index.ts'
|
|
27
|
+
import type { RejectedPushError } from './RejectedPushError.ts'
|
|
27
28
|
import { EventSequenceNumber, type LiveStoreEvent, type LiveStoreSchema } from '../schema/mod.ts'
|
|
28
29
|
import type * as SyncState from '../sync/syncstate.ts'
|
|
29
30
|
import type { ShutdownChannel } from './shutdown-channel.ts'
|
|
@@ -186,11 +187,11 @@ export interface LeaderSyncProcessor {
|
|
|
186
187
|
/** Used by client sessions to subscribe to upstream sync state changes */
|
|
187
188
|
pull: (args: {
|
|
188
189
|
cursor: EventSequenceNumber.Client.Composite
|
|
189
|
-
}) => Stream.Stream<{ payload: typeof SyncState.PayloadUpstream.Type }
|
|
190
|
+
}) => Stream.Stream<{ payload: typeof SyncState.PayloadUpstream.Type }>
|
|
190
191
|
/** The `pullQueue` API can be used instead of `pull` when more convenient */
|
|
191
192
|
pullQueue: (args: {
|
|
192
193
|
cursor: EventSequenceNumber.Client.Composite
|
|
193
|
-
}) => Effect.Effect<Queue.Queue<{ payload: typeof SyncState.PayloadUpstream.Type }>,
|
|
194
|
+
}) => Effect.Effect<Queue.Queue<{ payload: typeof SyncState.PayloadUpstream.Type }>, never, Scope.Scope>
|
|
194
195
|
|
|
195
196
|
/** Used by client sessions to push events to the leader thread */
|
|
196
197
|
push: (
|
|
@@ -204,18 +205,18 @@ export interface LeaderSyncProcessor {
|
|
|
204
205
|
*/
|
|
205
206
|
waitForProcessing?: boolean
|
|
206
207
|
},
|
|
207
|
-
) => Effect.Effect<void,
|
|
208
|
+
) => Effect.Effect<void, RejectedPushError>
|
|
208
209
|
|
|
209
210
|
/** Currently only used by devtools which don't provide their own event numbers */
|
|
210
211
|
pushPartial: (args: {
|
|
211
212
|
event: LiveStoreEvent.Input.Encoded
|
|
212
213
|
clientId: string
|
|
213
214
|
sessionId: string
|
|
214
|
-
}) => Effect.Effect<void,
|
|
215
|
+
}) => Effect.Effect<void, UnknownEventError>
|
|
215
216
|
|
|
216
217
|
boot: Effect.Effect<
|
|
217
218
|
{ initialLeaderHead: EventSequenceNumber.Client.Composite },
|
|
218
|
-
|
|
219
|
+
never,
|
|
219
220
|
LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
|
|
220
221
|
>
|
|
221
222
|
syncState: Subscribable.Subscribable<SyncState.SyncState>
|
package/src/logging.ts
CHANGED
|
@@ -33,11 +33,11 @@ export type LoggerDefaults = {
|
|
|
33
33
|
* Resolve the logger layer to provide to the Effect runtime.
|
|
34
34
|
*/
|
|
35
35
|
export const resolveLoggerLayer = (config?: WithLoggerOptions, defaults?: LoggerDefaults): Layer.Layer<never> => {
|
|
36
|
-
if (config?.logger) return config.logger
|
|
37
|
-
if (defaults?.defaultLogger) return defaults.defaultLogger
|
|
36
|
+
if (config?.logger !== undefined) return config.logger
|
|
37
|
+
if (defaults?.defaultLogger !== undefined) return defaults.defaultLogger
|
|
38
38
|
const threadName = defaults?.threadName ?? 'livestore'
|
|
39
39
|
const mode = defaults?.mode
|
|
40
|
-
return Logger.prettyWithThread(threadName, mode ? { mode } : {})
|
|
40
|
+
return Logger.prettyWithThread(threadName, mode !== undefined ? { mode } : {})
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -46,7 +46,7 @@ export const resolveLoggerLayer = (config?: WithLoggerOptions, defaults?: Logger
|
|
|
46
46
|
export const resolveLogLevel = (config?: WithLoggerOptions, defaults?: LoggerDefaults): LogLevel.LogLevel => {
|
|
47
47
|
if (config?.logLevel !== undefined) return config.logLevel
|
|
48
48
|
if (defaults?.defaultLogLevel !== undefined) return defaults.defaultLogLevel
|
|
49
|
-
return isDevEnv() ? LogLevel.Debug : LogLevel.Info
|
|
49
|
+
return isDevEnv() === true ? LogLevel.Debug : LogLevel.Info
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -51,11 +51,11 @@ export const makeClientSession = <R>({
|
|
|
51
51
|
origin: string | undefined
|
|
52
52
|
}): Effect.Effect<ClientSession, never, Scope.Scope | R> =>
|
|
53
53
|
Effect.gen(function* () {
|
|
54
|
-
const devtools: ClientSession['devtools'] = devtoolsEnabled
|
|
54
|
+
const devtools: ClientSession['devtools'] = devtoolsEnabled === true
|
|
55
55
|
? { enabled: true, pullLatch: yield* Effect.makeLatch(true), pushLatch: yield* Effect.makeLatch(true) }
|
|
56
56
|
: { enabled: false }
|
|
57
57
|
|
|
58
|
-
if (devtoolsEnabled) {
|
|
58
|
+
if (devtoolsEnabled === true) {
|
|
59
59
|
yield* Effect.gen(function* () {
|
|
60
60
|
const webmeshNode = yield* Webmesh.makeMeshNode(
|
|
61
61
|
Devtools.makeNodeName.client.session({ storeId, clientId, sessionId }),
|
|
@@ -34,14 +34,14 @@ export const getExecStatementsFromMaterializer = ({
|
|
|
34
34
|
const eventDecoded =
|
|
35
35
|
event.decoded === undefined
|
|
36
36
|
? {
|
|
37
|
-
...event.encoded
|
|
38
|
-
args: Schema.decodeUnknownSync(eventDef.schema)(event.encoded
|
|
37
|
+
...event.encoded,
|
|
38
|
+
args: Schema.decodeUnknownSync(eventDef.schema)(event.encoded.args),
|
|
39
39
|
}
|
|
40
40
|
: event.decoded
|
|
41
41
|
|
|
42
|
-
const eventArgsEncoded = isNil(event.decoded?.args)
|
|
42
|
+
const eventArgsEncoded = isNil(event.decoded?.args) === true
|
|
43
43
|
? undefined
|
|
44
|
-
: Schema.encodeUnknownSync(eventDef.schema)(event.decoded
|
|
44
|
+
: Schema.encodeUnknownSync(eventDef.schema)(event.decoded.args)
|
|
45
45
|
|
|
46
46
|
const query: MaterializerContextQuery = (
|
|
47
47
|
rawQueryOrQueryBuilder:
|
|
@@ -51,7 +51,7 @@ export const getExecStatementsFromMaterializer = ({
|
|
|
51
51
|
}
|
|
52
52
|
| QueryBuilder.Any,
|
|
53
53
|
) => {
|
|
54
|
-
if (isQueryBuilder(rawQueryOrQueryBuilder)) {
|
|
54
|
+
if (isQueryBuilder(rawQueryOrQueryBuilder) === true) {
|
|
55
55
|
const { query, bindValues } = rawQueryOrQueryBuilder.asSql()
|
|
56
56
|
const rawResults = dbState.select(query, prepareBindValues(bindValues, query))
|
|
57
57
|
const resultSchema = getResultSchema(rawQueryOrQueryBuilder)
|
|
@@ -86,7 +86,7 @@ export const getExecStatementsFromMaterializer = ({
|
|
|
86
86
|
export const makeMaterializerHash =
|
|
87
87
|
({ schema, dbState }: { schema: LiveStoreSchema; dbState: SqliteDb }) =>
|
|
88
88
|
(event: LiveStoreEvent.Client.Encoded): Option.Option<number> => {
|
|
89
|
-
if (isDevEnv()) {
|
|
89
|
+
if (isDevEnv() === true) {
|
|
90
90
|
// Hashing is only needed during dev-mode diagnostics. Skip work entirely for
|
|
91
91
|
// unknown events (no definition/materializer) so we do not introduce noisy
|
|
92
92
|
// warnings while still returning `Option.none()` to disable hash checks.
|
|
@@ -126,10 +126,10 @@ const fromMaterializerResult = (
|
|
|
126
126
|
bindValues: BindValues
|
|
127
127
|
writeTables: ReadonlySet<string> | undefined
|
|
128
128
|
}> => {
|
|
129
|
-
if (isReadonlyArray(materializerResult)) {
|
|
129
|
+
if (isReadonlyArray(materializerResult) === true) {
|
|
130
130
|
return materializerResult.flatMap(fromMaterializerResult)
|
|
131
131
|
}
|
|
132
|
-
if (isQueryBuilder(materializerResult)) {
|
|
132
|
+
if (isQueryBuilder(materializerResult) === true) {
|
|
133
133
|
const { query, bindValues, usedTables } = materializerResult.asSql()
|
|
134
134
|
return [{ sql: query, bindValues: bindValues as BindValues, writeTables: usedTables }]
|
|
135
135
|
} else if (typeof materializerResult === 'string') {
|
|
@@ -155,7 +155,7 @@ export const replaceSessionIdSymbol = (
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
const deepReplaceValue = <S, R>(input: any, searchValue: S, replaceValue: R): void => {
|
|
158
|
-
if (Array.isArray(input)) {
|
|
158
|
+
if (Array.isArray(input) === true) {
|
|
159
159
|
for (const i in input) {
|
|
160
160
|
if (input[i] === searchValue) {
|
|
161
161
|
input[i] = replaceValue
|
package/src/otel.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import * as otel from '@opentelemetry/api'
|
|
2
|
+
|
|
1
3
|
import { makeNoopTracer } from '@livestore/utils'
|
|
2
4
|
import { Effect, identity, Layer, OtelTracer } from '@livestore/utils/effect'
|
|
3
|
-
import * as otel from '@opentelemetry/api'
|
|
4
5
|
|
|
5
6
|
export const OtelLiveDummy: Layer.Layer<OtelTracer.OtelTracer> = Layer.suspend(() => {
|
|
6
7
|
const OtelTracerLive = Layer.succeed(OtelTracer.OtelTracer, makeNoopTracer())
|
|
@@ -22,7 +23,7 @@ export const provideOtel =
|
|
|
22
23
|
) as any as Layer.Layer<OtelTracer.OtelTracer>
|
|
23
24
|
|
|
24
25
|
return effect.pipe(
|
|
25
|
-
parentSpanContext
|
|
26
|
+
parentSpanContext !== undefined
|
|
26
27
|
? Effect.withParentSpan(OtelTracer.makeExternalSpan(otel.trace.getSpanContext(parentSpanContext)!))
|
|
27
28
|
: identity,
|
|
28
29
|
Effect.provide(TracingLive),
|
|
@@ -8,7 +8,10 @@ import { EventSequenceNumber, LiveStoreEvent, resolveEventDef, SystemTables } fr
|
|
|
8
8
|
import type { PreparedBindValues } from './util.ts'
|
|
9
9
|
import { sql } from './util.ts'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/** Parse JSON string to unknown value */
|
|
12
|
+
const jsonParse = Schema.decodeUnknownSync(Schema.parseJson())
|
|
13
|
+
|
|
14
|
+
export const rematerializeFromEventlog = Effect.fn('@livestore/common:rematerializeFromEventlog')(function* ({
|
|
12
15
|
dbEventlog,
|
|
13
16
|
// TODO re-use this db when bringing back the boot in-memory db implementation
|
|
14
17
|
// db,
|
|
@@ -21,69 +24,69 @@ export const rematerializeFromEventlog = ({
|
|
|
21
24
|
schema: LiveStoreSchema
|
|
22
25
|
onProgress: (_: { done: number; total: number }) => Effect.Effect<void>
|
|
23
26
|
materializeEvent: MaterializeEvent
|
|
24
|
-
})
|
|
25
|
-
Effect.gen(function* () {
|
|
27
|
+
}) {
|
|
26
28
|
const eventsCount = dbEventlog.select<{ count: number }>(
|
|
27
29
|
`SELECT COUNT(*) AS count FROM ${SystemTables.EVENTLOG_META_TABLE}`,
|
|
28
30
|
)[0]!.count
|
|
29
31
|
|
|
30
32
|
const hashEventDef = memoizeByRef((event: EventDef.AnyWithoutFn) => Schema.hash(event.schema))
|
|
31
33
|
|
|
32
|
-
const processEvent = (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
34
|
+
const processEvent = Effect.fn(`@livestore/common:rematerializeFromEventlog:processEvent`)(function* (
|
|
35
|
+
row: SystemTables.EventlogMetaRow,
|
|
36
|
+
) {
|
|
37
|
+
const args = jsonParse(row.argsJson)
|
|
38
|
+
const eventEncoded = LiveStoreEvent.Client.EncodedWithMeta.make({
|
|
39
|
+
name: row.name,
|
|
40
|
+
args,
|
|
41
|
+
seqNum: {
|
|
42
|
+
global: row.seqNumGlobal,
|
|
43
|
+
client: row.seqNumClient,
|
|
44
|
+
rebaseGeneration: row.seqNumRebaseGeneration,
|
|
45
|
+
},
|
|
46
|
+
parentSeqNum: {
|
|
47
|
+
global: row.parentSeqNumGlobal,
|
|
48
|
+
client: row.parentSeqNumClient,
|
|
49
|
+
rebaseGeneration: row.parentSeqNumRebaseGeneration,
|
|
50
|
+
},
|
|
51
|
+
clientId: row.clientId,
|
|
52
|
+
sessionId: row.sessionId,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const resolution = yield* resolveEventDef(schema, {
|
|
56
|
+
operation: '@livestore/common:rematerializeFromEventlog:processEvent',
|
|
57
|
+
event: eventEncoded,
|
|
58
|
+
}).pipe(UnknownError.mapToUnknownError)
|
|
59
|
+
|
|
60
|
+
if (resolution._tag === 'unknown') {
|
|
61
|
+
// Old snapshots can contain newer events. Skip until the runtime has
|
|
62
|
+
// been updated; the event stays in the log for future replays.
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { eventDef } = resolution
|
|
67
|
+
|
|
68
|
+
if (hashEventDef(eventDef) !== row.schemaHash) {
|
|
69
|
+
yield* Effect.logWarning(
|
|
70
|
+
`Schema hash mismatch for event definition ${row.name}. Trying to materialize event anyway.`,
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Checking whether the schema has changed in an incompatible way
|
|
75
|
+
yield* Schema.decodeUnknown(eventDef.schema)(args).pipe(
|
|
76
|
+
Effect.mapError((cause) =>
|
|
77
|
+
UnknownError.make({
|
|
78
|
+
cause,
|
|
79
|
+
note: `\
|
|
77
80
|
There was an error during rematerializing from the eventlog while decoding
|
|
78
81
|
the persisted event args for event definition "${row.name}".
|
|
79
82
|
This likely means the schema has changed in an incompatible way.
|
|
80
83
|
`,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
}),
|
|
85
|
+
),
|
|
86
|
+
)
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
yield* materializeEvent(eventEncoded, { skipEventlog: true })
|
|
89
|
+
})
|
|
87
90
|
|
|
88
91
|
const CHUNK_SIZE = 100
|
|
89
92
|
|
|
@@ -101,9 +104,9 @@ LIMIT ${CHUNK_SIZE}
|
|
|
101
104
|
SystemTables.EventlogMetaRow
|
|
102
105
|
>({ _tag: 'Initial' }, (item) => {
|
|
103
106
|
// End stream if no more rows
|
|
104
|
-
if (Chunk.isChunk(item) && item.length === 0) return Option.none()
|
|
107
|
+
if (Chunk.isChunk(item) === true && item.length === 0) return Option.none()
|
|
105
108
|
|
|
106
|
-
const lastId = Chunk.isChunk(item)
|
|
109
|
+
const lastId = Chunk.isChunk(item) === true
|
|
107
110
|
? Chunk.last(item).pipe(
|
|
108
111
|
Option.map((_) => ({ global: _.seqNumGlobal, client: _.seqNumClient })),
|
|
109
112
|
Option.getOrElse(() => EventSequenceNumber.Client.ROOT),
|
|
@@ -115,7 +118,7 @@ LIMIT ${CHUNK_SIZE}
|
|
|
115
118
|
$seqNumClient: lastId?.client,
|
|
116
119
|
} as any as PreparedBindValues),
|
|
117
120
|
)
|
|
118
|
-
const prevItem = Chunk.isChunk(item) ? item : Chunk.empty()
|
|
121
|
+
const prevItem = Chunk.isChunk(item) === true ? item : Chunk.empty()
|
|
119
122
|
return Option.some([prevItem, nextItem])
|
|
120
123
|
}).pipe(
|
|
121
124
|
Stream.bufferChunks({ capacity: 2 }),
|
|
@@ -129,7 +132,4 @@ LIMIT ${CHUNK_SIZE}
|
|
|
129
132
|
),
|
|
130
133
|
Stream.runDrain,
|
|
131
134
|
)
|
|
132
|
-
|
|
133
|
-
Effect.withPerformanceMeasure('@livestore/common:rematerializeFromEventlog'),
|
|
134
|
-
Effect.withSpan('@livestore/common:rematerializeFromEventlog'),
|
|
135
|
-
)
|
|
135
|
+
}, Effect.withPerformanceMeasure('@livestore/common:rematerializeFromEventlog'))
|
|
@@ -127,15 +127,15 @@ export const defineEvent = <TName extends string, TType, TEncoded = TType, TDeri
|
|
|
127
127
|
Object.defineProperty(makePartialEvent, 'options', {
|
|
128
128
|
value: {
|
|
129
129
|
clientOnly: options?.clientOnly ?? false,
|
|
130
|
-
facts: options?.facts
|
|
130
|
+
facts: options?.facts !== undefined
|
|
131
131
|
? (args, currentFacts) => {
|
|
132
132
|
const res = options.facts!(args, currentFacts)
|
|
133
133
|
return {
|
|
134
134
|
modify: {
|
|
135
|
-
set: res.modify?.set ? new Set(res.modify.set) : new Set(),
|
|
136
|
-
unset: res.modify?.unset ? new Set(res.modify.unset) : new Set(),
|
|
135
|
+
set: res.modify?.set !== undefined ? new Set(res.modify.set) : new Set(),
|
|
136
|
+
unset: res.modify?.unset !== undefined ? new Set(res.modify.unset) : new Set(),
|
|
137
137
|
},
|
|
138
|
-
require: res.require ? new Set(res.require) : new Set(),
|
|
138
|
+
require: res.require !== undefined ? new Set(res.require) : new Set(),
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
: undefined,
|
|
@@ -179,7 +179,7 @@ export const synced = <TName extends string, TType, TEncoded = TType>(
|
|
|
179
179
|
args: {
|
|
180
180
|
name: TName
|
|
181
181
|
schema: Schema.Schema<TType, TEncoded>
|
|
182
|
-
} & Omit<DefineEventOptions<TType
|
|
182
|
+
} & Omit<DefineEventOptions<TType>, 'derived' | 'clientOnly'>,
|
|
183
183
|
): EventDef<TName, TType, TEncoded> => defineEvent({ ...args, clientOnly: false })
|
|
184
184
|
|
|
185
185
|
/**
|
|
@@ -213,5 +213,5 @@ export const clientOnly = <TName extends string, TType, TEncoded = TType>(
|
|
|
213
213
|
args: {
|
|
214
214
|
name: TName
|
|
215
215
|
schema: Schema.Schema<TType, TEncoded>
|
|
216
|
-
} & Omit<DefineEventOptions<TType
|
|
216
|
+
} & Omit<DefineEventOptions<TType>, 'derived' | 'clientOnly'>,
|
|
217
217
|
): EventDef<TName, TType, TEncoded> => defineEvent({ ...args, clientOnly: true })
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Effect, Logger, Schema } from '@livestore/utils/effect'
|
|
2
1
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest'
|
|
3
2
|
|
|
3
|
+
import { Effect, Logger, Schema } from '@livestore/utils/effect'
|
|
4
|
+
|
|
4
5
|
import { synced } from './define.ts'
|
|
5
6
|
import {
|
|
6
7
|
deprecated,
|
|
@@ -109,7 +109,7 @@ export const findDeprecatedFieldsWithValues = (
|
|
|
109
109
|
// Also check deprecation on the type (for direct field deprecation)
|
|
110
110
|
const typeDeprecation = SchemaAST.getAnnotation<string>(DeprecatedId)(prop.type)
|
|
111
111
|
|
|
112
|
-
const reason = deprecationReason ?? (Option.isSome(typeDeprecation) ? typeDeprecation.value : undefined)
|
|
112
|
+
const reason = deprecationReason ?? (Option.isSome(typeDeprecation) === true ? typeDeprecation.value : undefined)
|
|
113
113
|
if (reason !== undefined) {
|
|
114
114
|
result.push({ field: fieldName, reason })
|
|
115
115
|
}
|
|
@@ -143,7 +143,7 @@ export const logDeprecationWarnings = (
|
|
|
143
143
|
|
|
144
144
|
// Check for event-level deprecation
|
|
145
145
|
const eventDeprecation = eventDef.options.deprecated
|
|
146
|
-
if (eventDeprecation !== undefined &&
|
|
146
|
+
if (eventDeprecation !== undefined && warnedDeprecatedEvents.has(eventName) === false) {
|
|
147
147
|
warnedDeprecatedEvents.add(eventName)
|
|
148
148
|
yield* Effect.logWarning('@livestore/schema:deprecated-event', {
|
|
149
149
|
event: eventName,
|
|
@@ -155,7 +155,7 @@ export const logDeprecationWarnings = (
|
|
|
155
155
|
const deprecatedFields = findDeprecatedFieldsWithValues(eventDef.schema, args)
|
|
156
156
|
for (const { field, reason } of deprecatedFields) {
|
|
157
157
|
const key = `${eventName}:${field}`
|
|
158
|
-
if (
|
|
158
|
+
if (warnedDeprecatedFields.has(key) === false) {
|
|
159
159
|
warnedDeprecatedFields.add(key)
|
|
160
160
|
yield* Effect.logWarning('@livestore/schema:deprecated-field', {
|
|
161
161
|
event: eventName,
|
|
@@ -28,7 +28,7 @@ export const make = ClientBrand
|
|
|
28
28
|
* const defaultSeq = EventSequenceNumber.Client.DEFAULT // 0
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
export const DEFAULT = 0
|
|
31
|
+
export const DEFAULT = make(0)
|
|
32
32
|
|
|
33
33
|
/** Default rebase generation (0). Increments each time the client rebases unconfirmed events. */
|
|
34
34
|
export const REBASE_GENERATION_DEFAULT = 0
|
|
@@ -91,7 +91,7 @@ export const toString = (seqNum: Composite) => {
|
|
|
91
91
|
* For full notation documentation, see: contributor-docs/events-notation.md
|
|
92
92
|
*/
|
|
93
93
|
export const fromString = (str: string): Composite => {
|
|
94
|
-
if (
|
|
94
|
+
if (str.startsWith('e') === false) {
|
|
95
95
|
throw new Error('Invalid event sequence number string: must start with "e"')
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -111,24 +111,24 @@ export const fromString = (str: string): Composite => {
|
|
|
111
111
|
const parts = withoutRebase.split('.')
|
|
112
112
|
|
|
113
113
|
// Validate that parts contain only digits (and possibly empty for client)
|
|
114
|
-
if (parts[0] === '' ||
|
|
114
|
+
if (parts[0] === '' || /^\d+$/.test(parts[0]!) === false) {
|
|
115
115
|
throw new Error('Invalid event sequence number string: invalid number format')
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
if (parts.length > 1 && parts[1] !== undefined && (parts[1] === '' ||
|
|
118
|
+
if (parts.length > 1 && parts[1] !== undefined && (parts[1] === '' || /^\d+$/.test(parts[1]) === false)) {
|
|
119
119
|
throw new Error('Invalid event sequence number string: invalid number format')
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const global = Number.parseInt(parts[0]!, 10)
|
|
123
123
|
const client = parts.length > 1 && parts[1] !== undefined ? Number.parseInt(parts[1], 10) : 0
|
|
124
124
|
|
|
125
|
-
if (Number.isNaN(global) || Number.isNaN(client) || Number.isNaN(rebaseGeneration)) {
|
|
125
|
+
if (Number.isNaN(global) === true || Number.isNaN(client) === true || Number.isNaN(rebaseGeneration) === true) {
|
|
126
126
|
throw new TypeError('Invalid event sequence number string: invalid number format')
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
return {
|
|
130
|
-
global: global
|
|
131
|
-
client: client
|
|
130
|
+
global: makeGlobal(global),
|
|
131
|
+
client: make(client),
|
|
132
132
|
rebaseGeneration,
|
|
133
133
|
}
|
|
134
134
|
}
|
|
@@ -187,7 +187,7 @@ const CompositeSchema = S.Struct({
|
|
|
187
187
|
* If rebaseGeneration is omitted, defaults to REBASE_GENERATION_DEFAULT (0).
|
|
188
188
|
*/
|
|
189
189
|
const makeComposite = (seqNum: CompositeInput): Composite => {
|
|
190
|
-
return S.is(CompositeSchema)(seqNum)
|
|
190
|
+
return S.is(CompositeSchema)(seqNum) === true
|
|
191
191
|
? seqNum
|
|
192
192
|
: S.decodeSync(CompositeSchema)({
|
|
193
193
|
...seqNum,
|
|
@@ -234,11 +234,11 @@ export const nextPair = ({
|
|
|
234
234
|
isClient: boolean
|
|
235
235
|
rebaseGeneration?: number
|
|
236
236
|
}): CompositePair => {
|
|
237
|
-
if (isClient) {
|
|
237
|
+
if (isClient === true) {
|
|
238
238
|
return {
|
|
239
239
|
seqNum: {
|
|
240
240
|
global: seqNum.global,
|
|
241
|
-
client: (seqNum.client + 1)
|
|
241
|
+
client: make(seqNum.client + 1),
|
|
242
242
|
rebaseGeneration: rebaseGeneration ?? seqNum.rebaseGeneration,
|
|
243
243
|
},
|
|
244
244
|
parentSeqNum: seqNum,
|
|
@@ -247,7 +247,7 @@ export const nextPair = ({
|
|
|
247
247
|
|
|
248
248
|
return {
|
|
249
249
|
seqNum: {
|
|
250
|
-
global: (seqNum.global + 1)
|
|
250
|
+
global: makeGlobal(seqNum.global + 1),
|
|
251
251
|
client: DEFAULT,
|
|
252
252
|
rebaseGeneration: rebaseGeneration ?? seqNum.rebaseGeneration,
|
|
253
253
|
},
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { expect } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { Option } from '@livestore/utils/effect'
|
|
4
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
5
|
+
|
|
6
|
+
import * as EventSequenceNumber from '../EventSequenceNumber/mod.ts'
|
|
7
|
+
import { EncodedWithMeta, isEqualEncoded, type Encoded } from './client.ts'
|
|
8
|
+
|
|
9
|
+
Vitest.describe('EncodedWithMeta', () => {
|
|
10
|
+
Vitest.test('toGlobal() produces numeric seqNums through JSON.stringify', () => {
|
|
11
|
+
const event = new EncodedWithMeta({
|
|
12
|
+
name: 'test-v1',
|
|
13
|
+
args: { id: '1' },
|
|
14
|
+
seqNum: EventSequenceNumber.Client.Composite.make({ global: 5, client: 0 }),
|
|
15
|
+
parentSeqNum: EventSequenceNumber.Client.Composite.make({ global: 4, client: 0 }),
|
|
16
|
+
clientId: 'client-1',
|
|
17
|
+
sessionId: 'session-1',
|
|
18
|
+
meta: {
|
|
19
|
+
sessionChangeset: { _tag: 'unset' },
|
|
20
|
+
syncMetadata: Option.none(),
|
|
21
|
+
materializerHashLeader: Option.none(),
|
|
22
|
+
materializerHashSession: Option.none(),
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const global = event.toGlobal()
|
|
27
|
+
const parsed = JSON.parse(JSON.stringify(global))
|
|
28
|
+
|
|
29
|
+
expect(parsed.seqNum).toBe(5)
|
|
30
|
+
expect(parsed.parentSeqNum).toBe(4)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
Vitest.describe('isEqualEncoded', () => {
|
|
35
|
+
const makeEncodedEvent = (args: unknown): Encoded => ({
|
|
36
|
+
name: 'testEvent-v1',
|
|
37
|
+
args,
|
|
38
|
+
seqNum: EventSequenceNumber.Client.Composite.make({ global: 1, client: 0 }),
|
|
39
|
+
parentSeqNum: EventSequenceNumber.Client.Composite.make(EventSequenceNumber.Client.ROOT),
|
|
40
|
+
clientId: 'client-1',
|
|
41
|
+
sessionId: 'session-1',
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
Vitest.it('should consider events with identical args as equal', () => {
|
|
45
|
+
const a = makeEncodedEvent({ id: 'abc', text: 'hello' })
|
|
46
|
+
const b = makeEncodedEvent({ id: 'abc', text: 'hello' })
|
|
47
|
+
expect(isEqualEncoded(a, b)).toBe(true)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
Vitest.it('should consider events with different key order in args as equal', () => {
|
|
51
|
+
const a = makeEncodedEvent({ b: 2, a: 1 })
|
|
52
|
+
const b = makeEncodedEvent({ a: 1, b: 2 })
|
|
53
|
+
expect(isEqualEncoded(a, b)).toBe(true)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
Vitest.it('should consider events with different key order in nested args as equal', () => {
|
|
57
|
+
const a = makeEncodedEvent({ outer: { b: 2, a: 1 }, x: 'y' })
|
|
58
|
+
const b = makeEncodedEvent({ x: 'y', outer: { a: 1, b: 2 } })
|
|
59
|
+
expect(isEqualEncoded(a, b)).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
Vitest.it('should consider events with different args values as not equal', () => {
|
|
63
|
+
const a = makeEncodedEvent({ id: 'abc' })
|
|
64
|
+
const b = makeEncodedEvent({ id: 'def' })
|
|
65
|
+
expect(isEqualEncoded(a, b)).toBe(false)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
Vitest.it('should consider events with different args keys as not equal', () => {
|
|
69
|
+
const a = makeEncodedEvent({ a: 1 })
|
|
70
|
+
const b = makeEncodedEvent({ b: 1 })
|
|
71
|
+
expect(isEqualEncoded(a, b)).toBe(false)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
Vitest.it('should handle null args', () => {
|
|
75
|
+
const a = makeEncodedEvent(null)
|
|
76
|
+
const b = makeEncodedEvent(null)
|
|
77
|
+
expect(isEqualEncoded(a, b)).toBe(true)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
Vitest.it('should handle array args', () => {
|
|
81
|
+
const a = makeEncodedEvent([1, 2, 3])
|
|
82
|
+
const b = makeEncodedEvent([1, 2, 3])
|
|
83
|
+
expect(isEqualEncoded(a, b)).toBe(true)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
Vitest.it('should handle empty object args', () => {
|
|
87
|
+
const a = makeEncodedEvent({})
|
|
88
|
+
const b = makeEncodedEvent({})
|
|
89
|
+
expect(isEqualEncoded(a, b)).toBe(true)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
Vitest.it('should consider events with different names as not equal', () => {
|
|
93
|
+
const a = { ...makeEncodedEvent({ id: 'abc' }), name: 'eventA' }
|
|
94
|
+
const b = { ...makeEncodedEvent({ id: 'abc' }), name: 'eventB' }
|
|
95
|
+
expect(isEqualEncoded(a, b)).toBe(false)
|
|
96
|
+
})
|
|
97
|
+
})
|