@livestore/common 0.0.0-snapshot-2ef046b02334f52613d31dbe06af53487685edc0 → 0.0.0-snapshot-8115ad48d5a57244358c943ecc92bb0a30274b87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/fixture.d.ts +83 -221
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +33 -11
- package/dist/__tests__/fixture.js.map +1 -1
- package/dist/adapter-types.d.ts +34 -13
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +20 -2
- package/dist/adapter-types.js.map +1 -1
- package/dist/bounded-collections.d.ts +1 -1
- package/dist/bounded-collections.d.ts.map +1 -1
- package/dist/debug-info.d.ts.map +1 -1
- package/dist/debug-info.js +1 -0
- package/dist/debug-info.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
- package/dist/devtools/devtools-messages-common.d.ts +6 -6
- package/dist/devtools/devtools-messages-leader.d.ts +45 -45
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +11 -11
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +25 -12
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +146 -98
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/{apply-mutation.d.ts → apply-event.d.ts} +7 -7
- package/dist/leader-thread/apply-event.d.ts.map +1 -0
- package/dist/leader-thread/{apply-mutation.js → apply-event.js} +45 -45
- package/dist/leader-thread/apply-event.js.map +1 -0
- package/dist/leader-thread/eventlog.d.ts +27 -0
- package/dist/leader-thread/eventlog.d.ts.map +1 -0
- package/dist/leader-thread/eventlog.js +123 -0
- package/dist/leader-thread/eventlog.js.map +1 -0
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +21 -19
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +16 -4
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +23 -16
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/mod.d.ts +1 -1
- package/dist/leader-thread/mod.d.ts.map +1 -1
- package/dist/leader-thread/mod.js +1 -1
- package/dist/leader-thread/mod.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +6 -7
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/types.d.ts +14 -15
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/materializer-helper.d.ts +23 -0
- package/dist/materializer-helper.d.ts.map +1 -0
- package/dist/materializer-helper.js +70 -0
- package/dist/materializer-helper.js.map +1 -0
- package/dist/query-builder/api.d.ts +56 -51
- package/dist/query-builder/api.d.ts.map +1 -1
- package/dist/query-builder/api.js +3 -5
- package/dist/query-builder/api.js.map +1 -1
- package/dist/query-builder/astToSql.d.ts.map +1 -1
- package/dist/query-builder/astToSql.js +59 -37
- package/dist/query-builder/astToSql.js.map +1 -1
- package/dist/query-builder/impl.d.ts +2 -3
- package/dist/query-builder/impl.d.ts.map +1 -1
- package/dist/query-builder/impl.js +48 -46
- package/dist/query-builder/impl.js.map +1 -1
- package/dist/query-builder/impl.test.d.ts +86 -1
- package/dist/query-builder/impl.test.d.ts.map +1 -1
- package/dist/query-builder/impl.test.js +223 -36
- package/dist/query-builder/impl.test.js.map +1 -1
- package/dist/rehydrate-from-eventlog.d.ts +15 -0
- package/dist/rehydrate-from-eventlog.d.ts.map +1 -0
- package/dist/{rehydrate-from-mutationlog.js → rehydrate-from-eventlog.js} +26 -25
- package/dist/rehydrate-from-eventlog.js.map +1 -0
- package/dist/schema/EventDef.d.ts +136 -0
- package/dist/schema/EventDef.d.ts.map +1 -0
- package/dist/schema/EventDef.js +58 -0
- package/dist/schema/EventDef.js.map +1 -0
- package/dist/schema/EventId.d.ts +7 -2
- package/dist/schema/EventId.d.ts.map +1 -1
- package/dist/schema/EventId.js +18 -3
- package/dist/schema/EventId.js.map +1 -1
- package/dist/schema/{MutationEvent.d.ts → LiveStoreEvent.d.ts} +56 -56
- package/dist/schema/LiveStoreEvent.d.ts.map +1 -0
- package/dist/schema/{MutationEvent.js → LiveStoreEvent.js} +25 -25
- package/dist/schema/LiveStoreEvent.js.map +1 -0
- package/dist/schema/client-document-def.d.ts +223 -0
- package/dist/schema/client-document-def.d.ts.map +1 -0
- package/dist/schema/client-document-def.js +170 -0
- package/dist/schema/client-document-def.js.map +1 -0
- package/dist/schema/client-document-def.test.d.ts +2 -0
- package/dist/schema/client-document-def.test.d.ts.map +1 -0
- package/dist/schema/client-document-def.test.js +201 -0
- package/dist/schema/client-document-def.test.js.map +1 -0
- package/dist/schema/db-schema/dsl/mod.d.ts.map +1 -1
- package/dist/schema/events.d.ts +2 -0
- package/dist/schema/events.d.ts.map +1 -0
- package/dist/schema/events.js +2 -0
- package/dist/schema/events.js.map +1 -0
- package/dist/schema/mod.d.ts +4 -3
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +4 -3
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +26 -22
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +45 -43
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/sqlite-state.d.ts +12 -0
- package/dist/schema/sqlite-state.d.ts.map +1 -0
- package/dist/schema/sqlite-state.js +36 -0
- package/dist/schema/sqlite-state.js.map +1 -0
- package/dist/schema/system-tables.d.ts +67 -98
- package/dist/schema/system-tables.d.ts.map +1 -1
- package/dist/schema/system-tables.js +62 -48
- package/dist/schema/system-tables.js.map +1 -1
- package/dist/schema/table-def.d.ts +26 -96
- package/dist/schema/table-def.d.ts.map +1 -1
- package/dist/schema/table-def.js +14 -64
- package/dist/schema/table-def.js.map +1 -1
- package/dist/schema/view.d.ts +3 -0
- package/dist/schema/view.d.ts.map +1 -0
- package/dist/schema/view.js +3 -0
- package/dist/schema/view.js.map +1 -0
- package/dist/schema-management/common.d.ts +4 -4
- package/dist/schema-management/common.d.ts.map +1 -1
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +6 -6
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/schema-management/validate-mutation-defs.d.ts +3 -3
- package/dist/schema-management/validate-mutation-defs.d.ts.map +1 -1
- package/dist/schema-management/validate-mutation-defs.js +17 -17
- package/dist/schema-management/validate-mutation-defs.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +7 -7
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +33 -30
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/next/facts.d.ts +19 -19
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +2 -2
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +3 -3
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +1 -1
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.js +1 -1
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.d.ts +7 -7
- package/dist/sync/next/rebase-events.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.js +5 -5
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/compact-events.calculator.test.js +38 -33
- package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
- package/dist/sync/next/test/compact-events.test.js +71 -71
- package/dist/sync/next/test/compact-events.test.js.map +1 -1
- package/dist/sync/next/test/{mutation-fixtures.d.ts → event-fixtures.d.ts} +25 -25
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -0
- package/dist/sync/next/test/{mutation-fixtures.js → event-fixtures.js} +60 -25
- package/dist/sync/next/test/event-fixtures.js.map +1 -0
- package/dist/sync/next/test/mod.d.ts +1 -1
- package/dist/sync/next/test/mod.d.ts.map +1 -1
- package/dist/sync/next/test/mod.js +1 -1
- package/dist/sync/next/test/mod.js.map +1 -1
- package/dist/sync/sync.d.ts +3 -3
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/syncstate.d.ts +30 -30
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +73 -40
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +175 -184
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/validate-push-payload.d.ts +2 -2
- package/dist/sync/validate-push-payload.d.ts.map +1 -1
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/__tests__/fixture.ts +36 -15
- package/src/adapter-types.ts +33 -13
- package/src/debug-info.ts +1 -0
- package/src/devtools/devtools-messages-leader.ts +13 -13
- package/src/index.ts +2 -5
- package/src/leader-thread/LeaderSyncProcessor.ts +210 -138
- package/src/leader-thread/{apply-mutation.ts → apply-event.ts} +61 -61
- package/src/leader-thread/eventlog.ts +199 -0
- package/src/leader-thread/leader-worker-devtools.ts +22 -19
- package/src/leader-thread/make-leader-thread-layer.ts +51 -29
- package/src/leader-thread/mod.ts +1 -1
- package/src/leader-thread/recreate-db.ts +6 -8
- package/src/leader-thread/types.ts +15 -16
- package/src/materializer-helper.ts +110 -0
- package/src/query-builder/api.ts +77 -103
- package/src/query-builder/astToSql.ts +68 -39
- package/src/query-builder/impl.test.ts +239 -42
- package/src/query-builder/impl.ts +72 -56
- package/src/{rehydrate-from-mutationlog.ts → rehydrate-from-eventlog.ts} +35 -38
- package/src/schema/EventDef.ts +216 -0
- package/src/schema/EventId.ts +23 -4
- package/src/schema/{MutationEvent.ts → LiveStoreEvent.ts} +68 -69
- package/src/schema/client-document-def.test.ts +239 -0
- package/src/schema/client-document-def.ts +444 -0
- package/src/schema/db-schema/dsl/mod.ts +0 -1
- package/src/schema/events.ts +1 -0
- package/src/schema/mod.ts +4 -3
- package/src/schema/schema.ts +78 -68
- package/src/schema/sqlite-state.ts +62 -0
- package/src/schema/system-tables.ts +42 -53
- package/src/schema/table-def.ts +51 -209
- package/src/schema/view.ts +2 -0
- package/src/schema-management/common.ts +4 -4
- package/src/schema-management/migrations.ts +8 -9
- package/src/schema-management/validate-mutation-defs.ts +22 -24
- package/src/sync/ClientSessionSyncProcessor.ts +41 -36
- package/src/sync/next/facts.ts +31 -32
- package/src/sync/next/history-dag-common.ts +4 -4
- package/src/sync/next/history-dag.ts +1 -1
- package/src/sync/next/rebase-events.ts +13 -13
- package/src/sync/next/test/compact-events.calculator.test.ts +45 -45
- package/src/sync/next/test/compact-events.test.ts +73 -73
- package/src/sync/next/test/event-fixtures.ts +219 -0
- package/src/sync/next/test/mod.ts +1 -1
- package/src/sync/sync.ts +3 -3
- package/src/sync/syncstate.test.ts +180 -189
- package/src/sync/syncstate.ts +162 -100
- package/src/sync/validate-push-payload.ts +2 -2
- package/src/version.ts +1 -1
- package/tsconfig.json +1 -0
- package/dist/derived-mutations.d.ts +0 -109
- package/dist/derived-mutations.d.ts.map +0 -1
- package/dist/derived-mutations.js +0 -54
- package/dist/derived-mutations.js.map +0 -1
- package/dist/derived-mutations.test.d.ts +0 -2
- package/dist/derived-mutations.test.d.ts.map +0 -1
- package/dist/derived-mutations.test.js +0 -93
- package/dist/derived-mutations.test.js.map +0 -1
- package/dist/init-singleton-tables.d.ts +0 -4
- package/dist/init-singleton-tables.d.ts.map +0 -1
- package/dist/init-singleton-tables.js +0 -16
- package/dist/init-singleton-tables.js.map +0 -1
- package/dist/leader-thread/apply-mutation.d.ts.map +0 -1
- package/dist/leader-thread/apply-mutation.js.map +0 -1
- package/dist/leader-thread/mutationlog.d.ts +0 -27
- package/dist/leader-thread/mutationlog.d.ts.map +0 -1
- package/dist/leader-thread/mutationlog.js +0 -124
- package/dist/leader-thread/mutationlog.js.map +0 -1
- package/dist/mutation.d.ts +0 -20
- package/dist/mutation.d.ts.map +0 -1
- package/dist/mutation.js +0 -68
- package/dist/mutation.js.map +0 -1
- package/dist/query-info.d.ts +0 -41
- package/dist/query-info.d.ts.map +0 -1
- package/dist/query-info.js +0 -7
- package/dist/query-info.js.map +0 -1
- package/dist/rehydrate-from-mutationlog.d.ts +0 -15
- package/dist/rehydrate-from-mutationlog.d.ts.map +0 -1
- package/dist/rehydrate-from-mutationlog.js.map +0 -1
- package/dist/schema/MutationEvent.d.ts.map +0 -1
- package/dist/schema/MutationEvent.js.map +0 -1
- package/dist/schema/mutations.d.ts +0 -115
- package/dist/schema/mutations.d.ts.map +0 -1
- package/dist/schema/mutations.js +0 -42
- package/dist/schema/mutations.js.map +0 -1
- package/dist/sync/next/test/mutation-fixtures.d.ts.map +0 -1
- package/dist/sync/next/test/mutation-fixtures.js.map +0 -1
- package/src/derived-mutations.test.ts +0 -101
- package/src/derived-mutations.ts +0 -170
- package/src/init-singleton-tables.ts +0 -24
- package/src/leader-thread/mutationlog.ts +0 -202
- package/src/mutation.ts +0 -108
- package/src/query-info.ts +0 -83
- package/src/schema/mutations.ts +0 -193
- package/src/sync/next/test/mutation-fixtures.ts +0 -228
|
@@ -2,67 +2,67 @@ import { LS_DEV, memoizeByRef, shouldNeverHappen } from '@livestore/utils'
|
|
|
2
2
|
import { Effect, ReadonlyArray, Schema } from '@livestore/utils/effect'
|
|
3
3
|
|
|
4
4
|
import type { SqliteDb } from '../adapter-types.js'
|
|
5
|
-
import {
|
|
6
|
-
import type {
|
|
5
|
+
import { getExecArgsFromEvent } from '../materializer-helper.js'
|
|
6
|
+
import type { LiveStoreEvent, LiveStoreSchema, SessionChangesetMetaRow } from '../schema/mod.js'
|
|
7
7
|
import {
|
|
8
8
|
EventId,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
EVENTLOG_META_TABLE,
|
|
10
|
+
getEventDef,
|
|
11
11
|
SESSION_CHANGESET_META_TABLE,
|
|
12
12
|
sessionChangesetMetaTable,
|
|
13
13
|
} from '../schema/mod.js'
|
|
14
14
|
import { insertRow } from '../sql-queries/index.js'
|
|
15
15
|
import { sql } from '../util.js'
|
|
16
16
|
import { execSql, execSqlPrepared } from './connection.js'
|
|
17
|
-
import * as
|
|
18
|
-
import type {
|
|
17
|
+
import * as Eventlog from './eventlog.js'
|
|
18
|
+
import type { ApplyEvent } from './types.js'
|
|
19
19
|
|
|
20
|
-
export const
|
|
20
|
+
export const makeApplyEvent = ({
|
|
21
21
|
schema,
|
|
22
22
|
dbReadModel: db,
|
|
23
|
-
|
|
23
|
+
dbEventlog,
|
|
24
24
|
}: {
|
|
25
25
|
schema: LiveStoreSchema
|
|
26
26
|
dbReadModel: SqliteDb
|
|
27
|
-
|
|
28
|
-
}): Effect.Effect<
|
|
27
|
+
dbEventlog: SqliteDb
|
|
28
|
+
}): Effect.Effect<ApplyEvent, never> =>
|
|
29
29
|
Effect.gen(function* () {
|
|
30
|
-
const
|
|
30
|
+
const shouldExcludeEventFromLog = makeShouldExcludeEventFromLog(schema)
|
|
31
31
|
|
|
32
|
-
const
|
|
32
|
+
const eventDefSchemaHashMap = new Map(
|
|
33
33
|
// TODO Running `Schema.hash` can be a bottleneck for larger schemas. There is an opportunity to run this
|
|
34
34
|
// at build time and lookup the pre-computed hash at runtime.
|
|
35
35
|
// Also see https://github.com/Effect-TS/effect/issues/2719
|
|
36
|
-
[...schema.
|
|
36
|
+
[...schema.eventsDefsMap.entries()].map(([k, v]) => [k, Schema.hash(v.schema)] as const),
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
-
return (
|
|
39
|
+
return (eventEncoded, options) =>
|
|
40
40
|
Effect.gen(function* () {
|
|
41
|
-
const
|
|
41
|
+
const skipEventlog = options?.skipEventlog ?? false
|
|
42
42
|
|
|
43
|
-
const
|
|
44
|
-
const
|
|
43
|
+
const eventName = eventEncoded.name
|
|
44
|
+
const eventDef = getEventDef(schema, eventName)
|
|
45
45
|
|
|
46
|
-
const execArgsArr =
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const execArgsArr = getExecArgsFromEvent({
|
|
47
|
+
eventDef,
|
|
48
|
+
event: { decoded: undefined, encoded: eventEncoded },
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
// NOTE we might want to bring this back if we want to debug no-op
|
|
51
|
+
// NOTE we might want to bring this back if we want to debug no-op events
|
|
52
52
|
// const makeExecuteOptions = (statementSql: string, bindValues: any) => ({
|
|
53
53
|
// onRowsChanged: (rowsChanged: number) => {
|
|
54
54
|
// if (rowsChanged === 0) {
|
|
55
|
-
// console.warn(`
|
|
55
|
+
// console.warn(`Event "${eventDef.name}" did not affect any rows:`, statementSql, bindValues)
|
|
56
56
|
// }
|
|
57
57
|
// },
|
|
58
58
|
// })
|
|
59
59
|
|
|
60
|
-
// console.group('[@livestore/common:leader-thread:
|
|
60
|
+
// console.group('[@livestore/common:leader-thread:applyEvent]', { eventName })
|
|
61
61
|
|
|
62
62
|
const session = db.session()
|
|
63
63
|
|
|
64
64
|
for (const { statementSql, bindValues } of execArgsArr) {
|
|
65
|
-
// console.debug(
|
|
65
|
+
// console.debug(eventName, statementSql, bindValues)
|
|
66
66
|
// TODO use cached prepared statements instead of exec
|
|
67
67
|
yield* execSqlPrepared(db, statementSql, bindValues)
|
|
68
68
|
}
|
|
@@ -77,9 +77,9 @@ export const makeApplyMutation = ({
|
|
|
77
77
|
tableName: SESSION_CHANGESET_META_TABLE,
|
|
78
78
|
columns: sessionChangesetMetaTable.sqliteDef.columns,
|
|
79
79
|
values: {
|
|
80
|
-
idGlobal:
|
|
81
|
-
idClient:
|
|
82
|
-
// NOTE the changeset will be empty (i.e. null) for no-op
|
|
80
|
+
idGlobal: eventEncoded.id.global,
|
|
81
|
+
idClient: eventEncoded.id.client,
|
|
82
|
+
// NOTE the changeset will be empty (i.e. null) for no-op events
|
|
83
83
|
changeset: changeset ?? null,
|
|
84
84
|
debug: LS_DEV ? execArgsArr : null,
|
|
85
85
|
},
|
|
@@ -88,22 +88,22 @@ export const makeApplyMutation = ({
|
|
|
88
88
|
|
|
89
89
|
// console.groupEnd()
|
|
90
90
|
|
|
91
|
-
// write to
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
yield*
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
91
|
+
// write to eventlog
|
|
92
|
+
const excludeFromEventlog = shouldExcludeEventFromLog(eventName, eventEncoded)
|
|
93
|
+
if (skipEventlog === false && excludeFromEventlog === false) {
|
|
94
|
+
const eventName = eventEncoded.name
|
|
95
|
+
const eventDefSchemaHash =
|
|
96
|
+
eventDefSchemaHashMap.get(eventName) ?? shouldNeverHappen(`Unknown event definition: ${eventName}`)
|
|
97
|
+
|
|
98
|
+
yield* Eventlog.insertIntoEventlog(
|
|
99
|
+
eventEncoded,
|
|
100
|
+
dbEventlog,
|
|
101
|
+
eventDefSchemaHash,
|
|
102
|
+
eventEncoded.clientId,
|
|
103
|
+
eventEncoded.sessionId,
|
|
104
104
|
)
|
|
105
105
|
} else {
|
|
106
|
-
// console.debug('[@livestore/common:leader-thread] skipping
|
|
106
|
+
// console.debug('[@livestore/common:leader-thread] skipping eventlog write', mutation, statementSql, bindValues)
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
return {
|
|
@@ -116,24 +116,24 @@ export const makeApplyMutation = ({
|
|
|
116
116
|
: { _tag: 'no-op' as const },
|
|
117
117
|
}
|
|
118
118
|
}).pipe(
|
|
119
|
-
Effect.withSpan(`@livestore/common:leader-thread:
|
|
119
|
+
Effect.withSpan(`@livestore/common:leader-thread:applyEvent`, {
|
|
120
120
|
attributes: {
|
|
121
|
-
|
|
122
|
-
mutationId:
|
|
123
|
-
'span.label': `${EventId.toString(
|
|
121
|
+
eventName: eventEncoded.name,
|
|
122
|
+
mutationId: eventEncoded.id,
|
|
123
|
+
'span.label': `${EventId.toString(eventEncoded.id)} ${eventEncoded.name}`,
|
|
124
124
|
},
|
|
125
125
|
}),
|
|
126
|
-
// Effect.logDuration('@livestore/common:leader-thread:
|
|
126
|
+
// Effect.logDuration('@livestore/common:leader-thread:applyEvent'),
|
|
127
127
|
)
|
|
128
128
|
})
|
|
129
129
|
|
|
130
130
|
export const rollback = ({
|
|
131
131
|
db,
|
|
132
|
-
|
|
132
|
+
dbEventlog,
|
|
133
133
|
eventIdsToRollback,
|
|
134
134
|
}: {
|
|
135
135
|
db: SqliteDb
|
|
136
|
-
|
|
136
|
+
dbEventlog: SqliteDb
|
|
137
137
|
eventIdsToRollback: EventId.EventId[]
|
|
138
138
|
}) =>
|
|
139
139
|
Effect.gen(function* () {
|
|
@@ -163,10 +163,10 @@ export const rollback = ({
|
|
|
163
163
|
)
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
// Delete the
|
|
166
|
+
// Delete the eventlog rows
|
|
167
167
|
for (const eventIdPairChunk of eventIdPairChunks) {
|
|
168
|
-
|
|
169
|
-
sql`DELETE FROM ${
|
|
168
|
+
dbEventlog.execute(
|
|
169
|
+
sql`DELETE FROM ${EVENTLOG_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`,
|
|
170
170
|
)
|
|
171
171
|
}
|
|
172
172
|
}).pipe(
|
|
@@ -176,20 +176,20 @@ export const rollback = ({
|
|
|
176
176
|
)
|
|
177
177
|
|
|
178
178
|
// TODO let's consider removing this "should exclude" mechanism in favour of log compaction etc
|
|
179
|
-
const
|
|
179
|
+
const makeShouldExcludeEventFromLog = memoizeByRef((schema: LiveStoreSchema) => {
|
|
180
180
|
const migrationOptions = schema.migrationOptions
|
|
181
|
-
const
|
|
182
|
-
migrationOptions.strategy === 'from-
|
|
183
|
-
? (migrationOptions.
|
|
181
|
+
const eventlogExclude =
|
|
182
|
+
migrationOptions.strategy === 'from-eventlog'
|
|
183
|
+
? (migrationOptions.excludeEvents ?? new Set(['livestore.RawSql']))
|
|
184
184
|
: new Set(['livestore.RawSql'])
|
|
185
185
|
|
|
186
|
-
return (
|
|
187
|
-
if (
|
|
186
|
+
return (eventName: string, eventEncoded: LiveStoreEvent.AnyEncoded): boolean => {
|
|
187
|
+
if (eventlogExclude.has(eventName)) return true
|
|
188
188
|
|
|
189
|
-
const
|
|
190
|
-
const execArgsArr =
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
const eventDef = getEventDef(schema, eventName)
|
|
190
|
+
const execArgsArr = getExecArgsFromEvent({
|
|
191
|
+
eventDef,
|
|
192
|
+
event: { decoded: undefined, encoded: eventEncoded },
|
|
193
193
|
})
|
|
194
194
|
|
|
195
195
|
return execArgsArr.some((_) => _.statementSql.includes('__livestore'))
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { LS_DEV, shouldNeverHappen } from '@livestore/utils'
|
|
2
|
+
import { Effect, Option, Schema } from '@livestore/utils/effect'
|
|
3
|
+
|
|
4
|
+
import type { SqliteDb } from '../adapter-types.js'
|
|
5
|
+
import * as EventId from '../schema/EventId.js'
|
|
6
|
+
import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
|
|
7
|
+
import {
|
|
8
|
+
EVENTLOG_META_TABLE,
|
|
9
|
+
eventlogMetaTable,
|
|
10
|
+
sessionChangesetMetaTable,
|
|
11
|
+
SYNC_STATUS_TABLE,
|
|
12
|
+
syncStatusTable,
|
|
13
|
+
} from '../schema/system-tables.js'
|
|
14
|
+
import { migrateTable } from '../schema-management/migrations.js'
|
|
15
|
+
import { insertRow, updateRows } from '../sql-queries/sql-queries.js'
|
|
16
|
+
import type { PreparedBindValues } from '../util.js'
|
|
17
|
+
import { prepareBindValues, sql } from '../util.js'
|
|
18
|
+
import { execSql } from './connection.js'
|
|
19
|
+
import type { InitialSyncInfo } from './types.js'
|
|
20
|
+
import { LeaderThreadCtx } from './types.js'
|
|
21
|
+
|
|
22
|
+
export const initEventlogDb = (dbEventlog: SqliteDb) =>
|
|
23
|
+
Effect.gen(function* () {
|
|
24
|
+
yield* migrateTable({
|
|
25
|
+
db: dbEventlog,
|
|
26
|
+
behaviour: 'create-if-not-exists',
|
|
27
|
+
tableAst: eventlogMetaTable.sqliteDef.ast,
|
|
28
|
+
skipMetaTable: true,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
yield* migrateTable({
|
|
32
|
+
db: dbEventlog,
|
|
33
|
+
behaviour: 'create-if-not-exists',
|
|
34
|
+
tableAst: syncStatusTable.sqliteDef.ast,
|
|
35
|
+
skipMetaTable: true,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Create sync status row if it doesn't exist
|
|
39
|
+
yield* execSql(
|
|
40
|
+
dbEventlog,
|
|
41
|
+
sql`INSERT INTO ${SYNC_STATUS_TABLE} (head)
|
|
42
|
+
SELECT ${EventId.ROOT.global}
|
|
43
|
+
WHERE NOT EXISTS (SELECT 1 FROM ${SYNC_STATUS_TABLE})`,
|
|
44
|
+
{},
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
/** Exclusive of the "since event" */
|
|
49
|
+
export const getEventsSince = (
|
|
50
|
+
since: EventId.EventId,
|
|
51
|
+
): Effect.Effect<ReadonlyArray<LiveStoreEvent.EncodedWithMeta>, never, LeaderThreadCtx> =>
|
|
52
|
+
Effect.gen(function* () {
|
|
53
|
+
const { dbEventlog, dbReadModel } = yield* LeaderThreadCtx
|
|
54
|
+
|
|
55
|
+
const query = eventlogMetaTable.where('idGlobal', '>=', since.global).asSql()
|
|
56
|
+
const pendingEventsRaw = dbEventlog.select(query.query, prepareBindValues(query.bindValues, query.query))
|
|
57
|
+
const pendingEvents = Schema.decodeUnknownSync(eventlogMetaTable.rowSchema.pipe(Schema.Array))(pendingEventsRaw)
|
|
58
|
+
|
|
59
|
+
const sessionChangesetRows = sessionChangesetMetaTable.where('idGlobal', '>=', since.global).asSql()
|
|
60
|
+
const sessionChangesetRowsRaw = dbReadModel.select(
|
|
61
|
+
sessionChangesetRows.query,
|
|
62
|
+
prepareBindValues(sessionChangesetRows.bindValues, sessionChangesetRows.query),
|
|
63
|
+
)
|
|
64
|
+
const sessionChangesetRowsDecoded = Schema.decodeUnknownSync(
|
|
65
|
+
sessionChangesetMetaTable.rowSchema.pipe(Schema.Array),
|
|
66
|
+
)(sessionChangesetRowsRaw)
|
|
67
|
+
|
|
68
|
+
return pendingEvents
|
|
69
|
+
.map((eventlogEvent) => {
|
|
70
|
+
const sessionChangeset = sessionChangesetRowsDecoded.find(
|
|
71
|
+
(readModelEvent) =>
|
|
72
|
+
readModelEvent.idGlobal === eventlogEvent.idGlobal && readModelEvent.idClient === eventlogEvent.idClient,
|
|
73
|
+
)
|
|
74
|
+
return LiveStoreEvent.EncodedWithMeta.make({
|
|
75
|
+
name: eventlogEvent.name,
|
|
76
|
+
args: eventlogEvent.argsJson,
|
|
77
|
+
id: { global: eventlogEvent.idGlobal, client: eventlogEvent.idClient },
|
|
78
|
+
parentId: { global: eventlogEvent.parentIdGlobal, client: eventlogEvent.parentIdClient },
|
|
79
|
+
clientId: eventlogEvent.clientId,
|
|
80
|
+
sessionId: eventlogEvent.sessionId,
|
|
81
|
+
meta: {
|
|
82
|
+
sessionChangeset:
|
|
83
|
+
sessionChangeset && sessionChangeset.changeset !== null
|
|
84
|
+
? {
|
|
85
|
+
_tag: 'sessionChangeset' as const,
|
|
86
|
+
data: sessionChangeset.changeset,
|
|
87
|
+
debug: sessionChangeset.debug,
|
|
88
|
+
}
|
|
89
|
+
: { _tag: 'unset' as const },
|
|
90
|
+
syncMetadata: eventlogEvent.syncMetadataJson,
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
.filter((_) => EventId.compare(_.id, since) > 0)
|
|
95
|
+
.sort((a, b) => EventId.compare(a.id, b.id))
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
export const getClientHeadFromDb = (dbEventlog: SqliteDb): EventId.EventId => {
|
|
99
|
+
const res = dbEventlog.select<{ idGlobal: EventId.GlobalEventId; idClient: EventId.ClientEventId }>(
|
|
100
|
+
sql`select idGlobal, idClient from ${EVENTLOG_META_TABLE} order by idGlobal DESC, idClient DESC limit 1`,
|
|
101
|
+
)[0]
|
|
102
|
+
|
|
103
|
+
return res ? { global: res.idGlobal, client: res.idClient } : EventId.ROOT
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const getBackendHeadFromDb = (dbEventlog: SqliteDb): EventId.GlobalEventId =>
|
|
107
|
+
dbEventlog.select<{ head: EventId.GlobalEventId }>(sql`select head from ${SYNC_STATUS_TABLE}`)[0]?.head ??
|
|
108
|
+
EventId.ROOT.global
|
|
109
|
+
|
|
110
|
+
// TODO use prepared statements
|
|
111
|
+
export const updateBackendHead = (dbEventlog: SqliteDb, head: EventId.EventId) =>
|
|
112
|
+
dbEventlog.execute(sql`UPDATE ${SYNC_STATUS_TABLE} SET head = ${head.global}`)
|
|
113
|
+
|
|
114
|
+
export const insertIntoEventlog = (
|
|
115
|
+
eventEncoded: LiveStoreEvent.EncodedWithMeta,
|
|
116
|
+
dbEventlog: SqliteDb,
|
|
117
|
+
eventDefSchemaHash: number,
|
|
118
|
+
clientId: string,
|
|
119
|
+
sessionId: string,
|
|
120
|
+
) =>
|
|
121
|
+
Effect.gen(function* () {
|
|
122
|
+
// Check history consistency during LS_DEV
|
|
123
|
+
if (LS_DEV && eventEncoded.parentId.global !== EventId.ROOT.global) {
|
|
124
|
+
const parentEventExists =
|
|
125
|
+
dbEventlog.select<{ count: number }>(
|
|
126
|
+
`SELECT COUNT(*) as count FROM ${EVENTLOG_META_TABLE} WHERE idGlobal = ? AND idClient = ?`,
|
|
127
|
+
[eventEncoded.parentId.global, eventEncoded.parentId.client] as any as PreparedBindValues,
|
|
128
|
+
)[0]!.count === 1
|
|
129
|
+
|
|
130
|
+
if (parentEventExists === false) {
|
|
131
|
+
shouldNeverHappen(
|
|
132
|
+
`Parent mutation ${eventEncoded.parentId.global},${eventEncoded.parentId.client} does not exist`,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// TODO use prepared statements
|
|
138
|
+
yield* execSql(
|
|
139
|
+
dbEventlog,
|
|
140
|
+
...insertRow({
|
|
141
|
+
tableName: EVENTLOG_META_TABLE,
|
|
142
|
+
columns: eventlogMetaTable.sqliteDef.columns,
|
|
143
|
+
values: {
|
|
144
|
+
idGlobal: eventEncoded.id.global,
|
|
145
|
+
idClient: eventEncoded.id.client,
|
|
146
|
+
parentIdGlobal: eventEncoded.parentId.global,
|
|
147
|
+
parentIdClient: eventEncoded.parentId.client,
|
|
148
|
+
name: eventEncoded.name,
|
|
149
|
+
argsJson: eventEncoded.args ?? {},
|
|
150
|
+
clientId,
|
|
151
|
+
sessionId,
|
|
152
|
+
schemaHash: eventDefSchemaHash,
|
|
153
|
+
syncMetadataJson: eventEncoded.meta.syncMetadata,
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
export const updateSyncMetadata = (items: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>) =>
|
|
160
|
+
Effect.gen(function* () {
|
|
161
|
+
const { dbEventlog } = yield* LeaderThreadCtx
|
|
162
|
+
|
|
163
|
+
// TODO try to do this in a single query
|
|
164
|
+
for (let i = 0; i < items.length; i++) {
|
|
165
|
+
const event = items[i]!
|
|
166
|
+
|
|
167
|
+
yield* execSql(
|
|
168
|
+
dbEventlog,
|
|
169
|
+
...updateRows({
|
|
170
|
+
tableName: EVENTLOG_META_TABLE,
|
|
171
|
+
columns: eventlogMetaTable.sqliteDef.columns,
|
|
172
|
+
where: { idGlobal: event.id.global, idClient: event.id.client },
|
|
173
|
+
updateValues: { syncMetadataJson: event.meta.syncMetadata },
|
|
174
|
+
}),
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
export const getSyncBackendCursorInfo = (remoteHead: EventId.GlobalEventId) =>
|
|
180
|
+
Effect.gen(function* () {
|
|
181
|
+
const { dbEventlog } = yield* LeaderThreadCtx
|
|
182
|
+
|
|
183
|
+
if (remoteHead === EventId.ROOT.global) return Option.none()
|
|
184
|
+
|
|
185
|
+
const EventlogQuerySchema = Schema.Struct({
|
|
186
|
+
syncMetadataJson: Schema.parseJson(Schema.Option(Schema.JsonValue)),
|
|
187
|
+
}).pipe(Schema.pluck('syncMetadataJson'), Schema.Array, Schema.head)
|
|
188
|
+
|
|
189
|
+
const syncMetadataOption = yield* Effect.sync(() =>
|
|
190
|
+
dbEventlog.select<{ syncMetadataJson: string }>(
|
|
191
|
+
sql`SELECT syncMetadataJson FROM ${EVENTLOG_META_TABLE} WHERE idGlobal = ${remoteHead} ORDER BY idClient ASC LIMIT 1`,
|
|
192
|
+
),
|
|
193
|
+
).pipe(Effect.andThen(Schema.decode(EventlogQuerySchema)), Effect.map(Option.flatten), Effect.orDie)
|
|
194
|
+
|
|
195
|
+
return Option.some({
|
|
196
|
+
cursor: { global: remoteHead, client: EventId.clientDefault },
|
|
197
|
+
metadata: syncMetadataOption,
|
|
198
|
+
}) satisfies InitialSyncInfo
|
|
199
|
+
}).pipe(Effect.withSpan('@livestore/common:eventlog:getSyncBackendCursorInfo', { attributes: { remoteHead } }))
|
|
@@ -2,7 +2,7 @@ import { Effect, FiberMap, Option, Stream, SubscriptionRef } from '@livestore/ut
|
|
|
2
2
|
import { nanoid } from '@livestore/utils/nanoid'
|
|
3
3
|
|
|
4
4
|
import { Devtools, IntentionalShutdownCause, liveStoreVersion, UnexpectedError } from '../index.js'
|
|
5
|
-
import {
|
|
5
|
+
import { EVENTLOG_META_TABLE, SCHEMA_EVENT_DEFS_META_TABLE, SCHEMA_META_TABLE } from '../schema/mod.js'
|
|
6
6
|
import type { DevtoolsOptions, PersistenceInfoPair } from './types.js'
|
|
7
7
|
import { LeaderThreadCtx } from './types.js'
|
|
8
8
|
|
|
@@ -33,7 +33,10 @@ export const bootDevtools = (options: DevtoolsOptions) =>
|
|
|
33
33
|
Effect.ignoreLogged,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
yield* syncProcessor.
|
|
36
|
+
const syncState = yield* syncProcessor.syncState
|
|
37
|
+
const mergeCounter = syncProcessor.getMergeCounter()
|
|
38
|
+
|
|
39
|
+
yield* syncProcessor.pull({ cursor: { mergeCounter, eventId: syncState.localHead } }).pipe(
|
|
37
40
|
Stream.tap(({ payload }) => sendMessage(Devtools.Leader.SyncPull.make({ payload, liveStoreVersion }))),
|
|
38
41
|
Stream.runDrain,
|
|
39
42
|
Effect.forkScoped,
|
|
@@ -60,7 +63,7 @@ const listenToDevtools = ({
|
|
|
60
63
|
syncBackend,
|
|
61
64
|
makeSqliteDb,
|
|
62
65
|
dbReadModel,
|
|
63
|
-
|
|
66
|
+
dbEventlog,
|
|
64
67
|
shutdownStateSubRef,
|
|
65
68
|
shutdownChannel,
|
|
66
69
|
syncProcessor,
|
|
@@ -140,20 +143,20 @@ const listenToDevtools = ({
|
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
try {
|
|
143
|
-
if (tableNames.has(
|
|
144
|
-
// Is
|
|
146
|
+
if (tableNames.has(EVENTLOG_META_TABLE)) {
|
|
147
|
+
// Is eventlog
|
|
145
148
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
146
149
|
|
|
147
|
-
|
|
150
|
+
dbEventlog.import(data)
|
|
148
151
|
|
|
149
152
|
dbReadModel.destroy()
|
|
150
|
-
} else if (tableNames.has(SCHEMA_META_TABLE) && tableNames.has(
|
|
153
|
+
} else if (tableNames.has(SCHEMA_META_TABLE) && tableNames.has(SCHEMA_EVENT_DEFS_META_TABLE)) {
|
|
151
154
|
// Is read model
|
|
152
155
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
153
156
|
|
|
154
157
|
dbReadModel.import(data)
|
|
155
158
|
|
|
156
|
-
|
|
159
|
+
dbEventlog.destroy()
|
|
157
160
|
} else {
|
|
158
161
|
yield* sendMessage(
|
|
159
162
|
Devtools.Leader.LoadDatabaseFile.Error.make({
|
|
@@ -187,7 +190,7 @@ const listenToDevtools = ({
|
|
|
187
190
|
dbReadModel.destroy()
|
|
188
191
|
|
|
189
192
|
if (mode === 'all-data') {
|
|
190
|
-
|
|
193
|
+
dbEventlog.destroy()
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
yield* sendMessage(Devtools.Leader.ResetAllData.Success.make({ ...reqPayload }))
|
|
@@ -204,33 +207,33 @@ const listenToDevtools = ({
|
|
|
204
207
|
|
|
205
208
|
const dbSizeQuery = `SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();`
|
|
206
209
|
const dbFileSize = dbReadModel.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
|
207
|
-
const
|
|
210
|
+
const eventlogFileSize = dbEventlog.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
|
208
211
|
|
|
209
212
|
yield* sendMessage(
|
|
210
213
|
Devtools.Leader.DatabaseFileInfoRes.make({
|
|
211
214
|
readModel: { fileSize: dbFileSize, persistenceInfo: persistenceInfo.readModel },
|
|
212
|
-
|
|
215
|
+
eventlog: { fileSize: eventlogFileSize, persistenceInfo: persistenceInfo.eventlog },
|
|
213
216
|
...reqPayload,
|
|
214
217
|
}),
|
|
215
218
|
)
|
|
216
219
|
|
|
217
220
|
return
|
|
218
221
|
}
|
|
219
|
-
case 'LSD.Leader.
|
|
220
|
-
const
|
|
222
|
+
case 'LSD.Leader.EventlogReq': {
|
|
223
|
+
const eventlog = dbEventlog.export()
|
|
221
224
|
|
|
222
|
-
yield* sendMessage(Devtools.Leader.
|
|
225
|
+
yield* sendMessage(Devtools.Leader.EventlogRes.make({ eventlog, ...reqPayload }))
|
|
223
226
|
|
|
224
227
|
return
|
|
225
228
|
}
|
|
226
|
-
case 'LSD.Leader.
|
|
229
|
+
case 'LSD.Leader.CommitEventReq': {
|
|
227
230
|
yield* syncProcessor.pushPartial({
|
|
228
|
-
|
|
231
|
+
event: decodedEvent.eventEncoded,
|
|
229
232
|
clientId: `devtools-${clientId}`,
|
|
230
233
|
sessionId: `devtools-${clientId}`,
|
|
231
234
|
})
|
|
232
235
|
|
|
233
|
-
yield* sendMessage(Devtools.Leader.
|
|
236
|
+
yield* sendMessage(Devtools.Leader.CommitEventRes.make({ ...reqPayload }))
|
|
234
237
|
|
|
235
238
|
return
|
|
236
239
|
}
|
|
@@ -242,10 +245,10 @@ const listenToDevtools = ({
|
|
|
242
245
|
yield* syncBackend.pull(Option.none()).pipe(
|
|
243
246
|
Stream.map((_) => _.batch),
|
|
244
247
|
Stream.flattenIterables,
|
|
245
|
-
Stream.tap(({
|
|
248
|
+
Stream.tap(({ eventEncoded, metadata }) =>
|
|
246
249
|
sendMessage(
|
|
247
250
|
Devtools.Leader.SyncHistoryRes.make({
|
|
248
|
-
|
|
251
|
+
eventEncoded,
|
|
249
252
|
metadata,
|
|
250
253
|
subscriptionId,
|
|
251
254
|
...reqPayload,
|