@livestore/common 0.3.0-dev.11 → 0.3.0-dev.2
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/adapter-types.d.ts +35 -47
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/derived-mutations.d.ts +4 -4
- package/dist/derived-mutations.d.ts.map +1 -1
- package/dist/derived-mutations.test.js.map +1 -1
- package/dist/devtools/devtools-bridge.d.ts +1 -2
- package/dist/devtools/devtools-bridge.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.d.ts +592 -3
- package/dist/devtools/devtools-messages.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.js +171 -3
- package/dist/devtools/devtools-messages.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/init-singleton-tables.d.ts +2 -2
- package/dist/init-singleton-tables.d.ts.map +1 -1
- package/dist/init-singleton-tables.js.map +1 -1
- package/dist/leader-thread/apply-mutation.d.ts +2 -5
- package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
- package/dist/leader-thread/apply-mutation.js +29 -41
- package/dist/leader-thread/apply-mutation.js.map +1 -1
- package/dist/leader-thread/connection.d.ts +4 -4
- package/dist/leader-thread/connection.d.ts.map +1 -1
- package/dist/leader-thread/connection.js +5 -5
- package/dist/leader-thread/connection.js.map +1 -1
- package/dist/leader-thread/leader-sync-processor.d.ts +2 -2
- package/dist/leader-thread/leader-sync-processor.d.ts.map +1 -1
- package/dist/leader-thread/leader-sync-processor.js +12 -20
- package/dist/leader-thread/leader-sync-processor.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +81 -37
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +11 -12
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +14 -33
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/mutationlog.d.ts +19 -6
- package/dist/leader-thread/mutationlog.d.ts.map +1 -1
- package/dist/leader-thread/mutationlog.js +6 -7
- package/dist/leader-thread/mutationlog.js.map +1 -1
- package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +18 -24
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/types.d.ts +16 -36
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/mutation.d.ts +2 -9
- package/dist/mutation.d.ts.map +1 -1
- package/dist/mutation.js +5 -5
- package/dist/mutation.js.map +1 -1
- package/dist/query-builder/impl.d.ts +1 -1
- package/dist/rehydrate-from-mutationlog.d.ts +5 -5
- package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
- package/dist/rehydrate-from-mutationlog.js +19 -13
- package/dist/rehydrate-from-mutationlog.js.map +1 -1
- package/dist/schema/EventId.d.ts +14 -16
- package/dist/schema/EventId.d.ts.map +1 -1
- package/dist/schema/EventId.js +7 -15
- package/dist/schema/EventId.js.map +1 -1
- package/dist/schema/MutationEvent.d.ts +80 -49
- package/dist/schema/MutationEvent.d.ts.map +1 -1
- package/dist/schema/MutationEvent.js +15 -32
- package/dist/schema/MutationEvent.js.map +1 -1
- package/dist/schema/system-tables.d.ts +26 -26
- package/dist/schema/system-tables.d.ts.map +1 -1
- package/dist/schema/system-tables.js +11 -19
- package/dist/schema/system-tables.js.map +1 -1
- package/dist/schema-management/common.d.ts +3 -3
- package/dist/schema-management/common.d.ts.map +1 -1
- package/dist/schema-management/common.js.map +1 -1
- package/dist/schema-management/migrations.d.ts +4 -4
- 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/sync/client-session-sync-processor.d.ts +4 -4
- package/dist/sync/client-session-sync-processor.d.ts.map +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +4 -1
- 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/rebase-events.d.ts +3 -3
- package/dist/sync/next/rebase-events.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.js +2 -3
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/mutation-fixtures.d.ts +7 -7
- package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -1
- package/dist/sync/next/test/mutation-fixtures.js +9 -3
- package/dist/sync/next/test/mutation-fixtures.js.map +1 -1
- package/dist/sync/sync.d.ts +11 -21
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/syncstate.d.ts +23 -45
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +12 -56
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +69 -125
- 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 +2 -2
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +5 -6
- package/src/adapter-types.ts +40 -39
- package/src/derived-mutations.test.ts +1 -1
- package/src/derived-mutations.ts +5 -9
- package/src/devtools/devtools-bridge.ts +1 -2
- package/src/devtools/devtools-messages.ts +243 -3
- package/src/index.ts +6 -0
- package/src/init-singleton-tables.ts +2 -2
- package/src/leader-thread/apply-mutation.ts +35 -53
- package/src/leader-thread/connection.ts +7 -7
- package/src/leader-thread/{LeaderSyncProcessor.ts → leader-sync-processor.ts} +268 -306
- package/src/leader-thread/leader-worker-devtools.ts +124 -52
- package/src/leader-thread/make-leader-thread-layer.ts +30 -62
- package/src/leader-thread/mutationlog.ts +10 -14
- package/src/leader-thread/recreate-db.ts +20 -24
- package/src/leader-thread/types.ts +20 -41
- package/src/mutation.ts +7 -17
- package/src/rehydrate-from-mutationlog.ts +26 -18
- package/src/schema/EventId.ts +9 -23
- package/src/schema/MutationEvent.ts +24 -46
- package/src/schema/system-tables.ts +11 -19
- package/src/schema-management/common.ts +3 -3
- package/src/schema-management/migrations.ts +10 -10
- package/src/sync/{ClientSessionSyncProcessor.ts → client-session-sync-processor.ts} +19 -26
- package/src/sync/index.ts +1 -1
- package/src/sync/next/history-dag-common.ts +1 -1
- package/src/sync/next/rebase-events.ts +7 -7
- package/src/sync/next/test/mutation-fixtures.ts +10 -3
- package/src/sync/sync.ts +6 -19
- package/src/sync/syncstate.test.ts +67 -127
- package/src/sync/syncstate.ts +19 -21
- package/src/sync/validate-push-payload.ts +4 -7
- package/src/version.ts +1 -1
- package/dist/devtools/devtool-message-leader.d.ts +0 -2
- package/dist/devtools/devtool-message-leader.d.ts.map +0 -1
- package/dist/devtools/devtool-message-leader.js +0 -2
- package/dist/devtools/devtool-message-leader.js.map +0 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +0 -297
- package/dist/devtools/devtools-messages-client-session.d.ts.map +0 -1
- package/dist/devtools/devtools-messages-client-session.js +0 -61
- package/dist/devtools/devtools-messages-client-session.js.map +0 -1
- package/dist/devtools/devtools-messages-common.d.ts +0 -65
- package/dist/devtools/devtools-messages-common.d.ts.map +0 -1
- package/dist/devtools/devtools-messages-common.js +0 -35
- package/dist/devtools/devtools-messages-common.js.map +0 -1
- package/dist/devtools/devtools-messages-leader.d.ts +0 -261
- package/dist/devtools/devtools-messages-leader.d.ts.map +0 -1
- package/dist/devtools/devtools-messages-leader.js +0 -85
- package/dist/devtools/devtools-messages-leader.js.map +0 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +0 -37
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +0 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +0 -432
- package/dist/leader-thread/LeaderSyncProcessor.js.map +0 -1
- package/dist/schema/EventId.test.d.ts +0 -2
- package/dist/schema/EventId.test.d.ts.map +0 -1
- package/dist/schema/EventId.test.js +0 -11
- package/dist/schema/EventId.test.js.map +0 -1
- package/dist/schema/MutationEvent.test.d.ts +0 -2
- package/dist/schema/MutationEvent.test.d.ts.map +0 -1
- package/dist/schema/MutationEvent.test.js +0 -2
- package/dist/schema/MutationEvent.test.js.map +0 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +0 -43
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +0 -1
- package/dist/sync/ClientSessionSyncProcessor.js +0 -141
- package/dist/sync/ClientSessionSyncProcessor.js.map +0 -1
- package/src/devtools/devtools-messages-client-session.ts +0 -109
- package/src/devtools/devtools-messages-common.ts +0 -52
- package/src/devtools/devtools-messages-leader.ts +0 -115
- package/src/schema/EventId.test.ts +0 -12
@@ -1,11 +1,18 @@
|
|
1
|
-
import { Effect, FiberMap, Option, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
1
|
+
import { Effect, FiberMap, Option, PubSub, Queue, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
2
2
|
|
3
3
|
import { Devtools, IntentionalShutdownCause, liveStoreVersion, UnexpectedError } from '../index.js'
|
4
4
|
import { MUTATION_LOG_META_TABLE, SCHEMA_META_TABLE, SCHEMA_MUTATIONS_META_TABLE } from '../schema/mod.js'
|
5
|
+
import type { ShutdownChannel } from './shutdown-channel.js'
|
5
6
|
import type { DevtoolsOptions, PersistenceInfoPair } from './types.js'
|
6
7
|
import { LeaderThreadCtx } from './types.js'
|
7
8
|
|
8
|
-
type SendMessageToDevtools = (
|
9
|
+
type SendMessageToDevtools = (
|
10
|
+
message: Devtools.MessageFromAppLeader,
|
11
|
+
options?: {
|
12
|
+
/** Send message even if not connected (e.g. for initial broadcast messages) */
|
13
|
+
force: boolean
|
14
|
+
},
|
15
|
+
) => Effect.Effect<void>
|
9
16
|
|
10
17
|
// TODO bind scope to the webchannel lifetime
|
11
18
|
export const bootDevtools = (options: DevtoolsOptions) =>
|
@@ -14,24 +21,44 @@ export const bootDevtools = (options: DevtoolsOptions) =>
|
|
14
21
|
return
|
15
22
|
}
|
16
23
|
|
17
|
-
const {
|
24
|
+
const { persistenceInfo, shutdownChannel, devtoolsWebChannel } = yield* options.makeContext
|
18
25
|
|
19
|
-
yield*
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
const isConnected = yield* SubscriptionRef.make(true)
|
27
|
+
|
28
|
+
const incomingMessagesPubSub = yield* PubSub.unbounded<Devtools.MessageToAppLeader>().pipe(
|
29
|
+
Effect.acquireRelease(PubSub.shutdown),
|
30
|
+
)
|
23
31
|
|
24
|
-
const
|
32
|
+
const incomingMessages = Stream.fromPubSub(incomingMessagesPubSub)
|
25
33
|
|
26
|
-
const
|
27
|
-
|
28
|
-
|
29
|
-
.pipe(
|
30
|
-
Effect.withSpan('@livestore/common:leader-thread:devtools:sendToDevtools'),
|
31
|
-
Effect.interruptible,
|
32
|
-
Effect.ignoreLogged,
|
33
|
-
)
|
34
|
+
const outgoingMessagesQueue = yield* Queue.unbounded<Devtools.MessageFromAppLeader>().pipe(
|
35
|
+
Effect.acquireRelease(Queue.shutdown),
|
36
|
+
)
|
34
37
|
|
38
|
+
const devtoolsCoordinatorChannel = devtoolsWebChannel
|
39
|
+
// coordinatorMessagePortOrChannel instanceof MessagePort
|
40
|
+
// ? yield* WebChannel.messagePortChannel({
|
41
|
+
// port: coordinatorMessagePortOrChannel,
|
42
|
+
// schema: { send: Devtools.MessageFromAppLeader, listen: Devtools.MessageToAppLeader },
|
43
|
+
// })
|
44
|
+
// : coordinatorMessagePortOrChannel
|
45
|
+
|
46
|
+
const sendMessage: SendMessageToDevtools = (message, options) =>
|
47
|
+
Effect.gen(function* () {
|
48
|
+
if (options?.force === true || (yield* isConnected)) {
|
49
|
+
yield* devtoolsCoordinatorChannel.send(message)
|
50
|
+
} else {
|
51
|
+
yield* Queue.offer(outgoingMessagesQueue, message)
|
52
|
+
}
|
53
|
+
}).pipe(
|
54
|
+
Effect.withSpan('@livestore/common:leader-thread:devtools:sendToDevtools'),
|
55
|
+
Effect.interruptible,
|
56
|
+
Effect.ignoreLogged,
|
57
|
+
)
|
58
|
+
|
59
|
+
// broadcastCallbacks.add((message) => sendMessage(message))
|
60
|
+
|
61
|
+
const { connectedClientSessionPullQueues, syncProcessor } = yield* LeaderThreadCtx
|
35
62
|
const { localHead } = yield* syncProcessor.syncState
|
36
63
|
|
37
64
|
// TODO close queue when devtools disconnects
|
@@ -42,8 +69,13 @@ export const bootDevtools = (options: DevtoolsOptions) =>
|
|
42
69
|
Effect.gen(function* () {
|
43
70
|
if (msg.payload._tag === 'upstream-advance') {
|
44
71
|
for (const mutationEventEncoded of msg.payload.newEvents) {
|
45
|
-
|
46
|
-
|
72
|
+
yield* sendMessage(
|
73
|
+
Devtools.MutationBroadcast.make({
|
74
|
+
mutationEventEncoded,
|
75
|
+
|
76
|
+
liveStoreVersion,
|
77
|
+
}),
|
78
|
+
)
|
47
79
|
}
|
48
80
|
} else {
|
49
81
|
yield* Effect.logWarning('TODO implement rebases in devtools')
|
@@ -54,32 +86,68 @@ export const bootDevtools = (options: DevtoolsOptions) =>
|
|
54
86
|
Effect.forkScoped,
|
55
87
|
)
|
56
88
|
|
89
|
+
yield* devtoolsCoordinatorChannel.listen.pipe(
|
90
|
+
Stream.flatten(),
|
91
|
+
// Stream.tapLogWithLabel('@livestore/common:leader-thread:devtools:onPortMessage'),
|
92
|
+
Stream.tap((msg) =>
|
93
|
+
Effect.gen(function* () {
|
94
|
+
// yield* Effect.logDebug(`[@livestore/common:leader-thread:devtools] message from port: ${msg._tag}`, msg)
|
95
|
+
// if (msg._tag === 'LSD.MessagePortForStoreRes') {
|
96
|
+
// yield* Deferred.succeed(storeMessagePortDeferred, msg.port)
|
97
|
+
// } else {
|
98
|
+
yield* PubSub.publish(incomingMessagesPubSub, msg)
|
99
|
+
// }
|
100
|
+
}),
|
101
|
+
),
|
102
|
+
Stream.runDrain,
|
103
|
+
Effect.withSpan(`@livestore/common:leader-thread:devtools:onPortMessage`),
|
104
|
+
Effect.ignoreLogged,
|
105
|
+
Effect.forkScoped,
|
106
|
+
)
|
107
|
+
|
108
|
+
// yield* sendMessage(Devtools.AppHostReady.make({ appHostId, liveStoreVersion, isLeader }), { force: true })
|
109
|
+
|
110
|
+
// yield* sendMessage(Devtools.MessagePortForStoreReq.make({ appHostId, liveStoreVersion, requestId: nanoid() }), {
|
111
|
+
// force: true,
|
112
|
+
// })
|
113
|
+
|
57
114
|
yield* listenToDevtools({
|
58
|
-
incomingMessages
|
115
|
+
incomingMessages,
|
59
116
|
sendMessage,
|
117
|
+
// isConnected,
|
118
|
+
// disconnect,
|
119
|
+
// storeId,
|
120
|
+
// appHostId,
|
121
|
+
// isLeader,
|
60
122
|
persistenceInfo,
|
61
|
-
|
123
|
+
shutdownChannel,
|
124
|
+
})
|
62
125
|
}).pipe(Effect.withSpan('@livestore/common:leader-thread:devtools:boot'))
|
63
126
|
|
64
127
|
const listenToDevtools = ({
|
65
128
|
incomingMessages,
|
66
129
|
sendMessage,
|
130
|
+
// isConnected,
|
131
|
+
// disconnect,
|
132
|
+
// appHostId,
|
133
|
+
// storeId,
|
134
|
+
// isLeader,
|
67
135
|
persistenceInfo,
|
136
|
+
shutdownChannel,
|
68
137
|
}: {
|
69
138
|
incomingMessages: Stream.Stream<Devtools.MessageToAppLeader>
|
70
139
|
sendMessage: SendMessageToDevtools
|
71
|
-
|
140
|
+
// isConnected: SubscriptionRef.SubscriptionRef<boolean>
|
141
|
+
// disconnect: Effect.Effect<void>
|
142
|
+
// appHostId: string
|
143
|
+
// storeId: string
|
144
|
+
// isLeader: boolean
|
145
|
+
persistenceInfo: PersistenceInfoPair
|
146
|
+
shutdownChannel: ShutdownChannel
|
72
147
|
}) =>
|
73
148
|
Effect.gen(function* () {
|
74
|
-
const
|
75
|
-
|
76
|
-
makeSqliteDb,
|
77
|
-
dbReadModel,
|
78
|
-
dbMutationLog,
|
79
|
-
shutdownStateSubRef,
|
80
|
-
shutdownChannel,
|
81
|
-
syncProcessor,
|
82
|
-
} = yield* LeaderThreadCtx
|
149
|
+
const innerWorkerCtx = yield* LeaderThreadCtx
|
150
|
+
const { syncBackend, makeSyncDb, db, dbLog, shutdownStateSubRef, syncProcessor } = innerWorkerCtx
|
83
151
|
|
84
152
|
type RequestId = string
|
85
153
|
const subscriptionFiberMap = yield* FiberMap.make<RequestId>()
|
@@ -90,6 +158,15 @@ const listenToDevtools = ({
|
|
90
158
|
// yield* Effect.logDebug('[@livestore/common:leader-thread:devtools] incomingMessage', decodedEvent)
|
91
159
|
|
92
160
|
if (decodedEvent._tag === 'LSD.Disconnect') {
|
161
|
+
// yield* SubscriptionRef.set(isConnected, false)
|
162
|
+
|
163
|
+
// yield* disconnect
|
164
|
+
|
165
|
+
// TODO is there a better place for this?
|
166
|
+
// yield* sendMessage(Devtools.AppHostReady.make({ appHostId, liveStoreVersion, isLeader }), {
|
167
|
+
// force: true,
|
168
|
+
// })
|
169
|
+
|
93
170
|
return
|
94
171
|
}
|
95
172
|
|
@@ -102,7 +179,7 @@ const listenToDevtools = ({
|
|
102
179
|
return
|
103
180
|
}
|
104
181
|
case 'LSD.Leader.SnapshotReq': {
|
105
|
-
const snapshot =
|
182
|
+
const snapshot = db.export()
|
106
183
|
|
107
184
|
yield* sendMessage(Devtools.SnapshotRes.make({ snapshot, ...reqPayload }))
|
108
185
|
|
@@ -114,15 +191,15 @@ const listenToDevtools = ({
|
|
114
191
|
let tableNames: Set<string>
|
115
192
|
|
116
193
|
try {
|
117
|
-
const
|
118
|
-
|
119
|
-
const tableNameResults =
|
194
|
+
const tmpSyncDb = yield* makeSyncDb({ _tag: 'in-memory' })
|
195
|
+
tmpSyncDb.import(data)
|
196
|
+
const tableNameResults = tmpSyncDb.select<{ name: string }>(
|
120
197
|
`select name from sqlite_master where type = 'table'`,
|
121
198
|
)
|
122
199
|
|
123
200
|
tableNames = new Set(tableNameResults.map((_) => _.name))
|
124
201
|
|
125
|
-
|
202
|
+
tmpSyncDb.close()
|
126
203
|
} catch (e) {
|
127
204
|
yield* Effect.logError(`Error importing database file`, e)
|
128
205
|
yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-file' }))
|
@@ -133,15 +210,15 @@ const listenToDevtools = ({
|
|
133
210
|
if (tableNames.has(MUTATION_LOG_META_TABLE)) {
|
134
211
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
135
212
|
|
136
|
-
|
213
|
+
dbLog.import(data)
|
137
214
|
|
138
|
-
|
215
|
+
db.destroy()
|
139
216
|
} else if (tableNames.has(SCHEMA_META_TABLE) && tableNames.has(SCHEMA_MUTATIONS_META_TABLE)) {
|
140
217
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
141
218
|
|
142
|
-
|
219
|
+
db.import(data)
|
143
220
|
|
144
|
-
|
221
|
+
dbLog.destroy()
|
145
222
|
} else {
|
146
223
|
yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-database' }))
|
147
224
|
return
|
@@ -149,7 +226,7 @@ const listenToDevtools = ({
|
|
149
226
|
|
150
227
|
yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'ok' }))
|
151
228
|
|
152
|
-
yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-import' }))
|
229
|
+
yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-import' }))
|
153
230
|
|
154
231
|
return
|
155
232
|
}
|
@@ -158,31 +235,26 @@ const listenToDevtools = ({
|
|
158
235
|
|
159
236
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
160
237
|
|
161
|
-
|
238
|
+
db.destroy()
|
162
239
|
|
163
240
|
if (mode === 'all-data') {
|
164
|
-
|
241
|
+
dbLog.destroy()
|
165
242
|
}
|
166
243
|
|
167
244
|
yield* sendMessage(Devtools.ResetAllDataRes.make({ ...reqPayload }))
|
168
245
|
|
169
|
-
yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-reset' }))
|
246
|
+
yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-reset' }))
|
170
247
|
|
171
248
|
return
|
172
249
|
}
|
173
250
|
case 'LSD.Leader.DatabaseFileInfoReq': {
|
174
|
-
if (persistenceInfo === undefined) {
|
175
|
-
console.log('[@livestore/common:leader-thread:devtools] persistenceInfo is required for this request')
|
176
|
-
return
|
177
|
-
}
|
178
|
-
|
179
251
|
const dbSizeQuery = `SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();`
|
180
|
-
const dbFileSize =
|
181
|
-
const mutationLogFileSize =
|
252
|
+
const dbFileSize = db.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
253
|
+
const mutationLogFileSize = dbLog.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
182
254
|
|
183
255
|
yield* sendMessage(
|
184
256
|
Devtools.DatabaseFileInfoRes.make({
|
185
|
-
|
257
|
+
db: { fileSize: dbFileSize, persistenceInfo: persistenceInfo.db },
|
186
258
|
mutationLog: { fileSize: mutationLogFileSize, persistenceInfo: persistenceInfo.mutationLog },
|
187
259
|
...reqPayload,
|
188
260
|
}),
|
@@ -191,7 +263,7 @@ const listenToDevtools = ({
|
|
191
263
|
return
|
192
264
|
}
|
193
265
|
case 'LSD.Leader.MutationLogReq': {
|
194
|
-
const mutationLog =
|
266
|
+
const mutationLog = dbLog.export()
|
195
267
|
|
196
268
|
yield* sendMessage(Devtools.MutationLogRes.make({ mutationLog, ...reqPayload }))
|
197
269
|
|
@@ -1,100 +1,68 @@
|
|
1
|
-
import type { HttpClient, Scope } from '@livestore/utils/effect'
|
2
|
-
import { Deferred, Effect, Layer, Queue, SubscriptionRef } from '@livestore/utils/effect'
|
1
|
+
import type { HttpClient, Scope, WebChannel } from '@livestore/utils/effect'
|
2
|
+
import { Deferred, Effect, FiberSet, Layer, Queue, SubscriptionRef } from '@livestore/utils/effect'
|
3
3
|
|
4
|
-
import type { BootStatus,
|
4
|
+
import type { BootStatus, MakeSynchronousDatabase, SqliteError, SynchronousDatabase } from '../adapter-types.js'
|
5
5
|
import { UnexpectedError } from '../adapter-types.js'
|
6
|
-
import type * as Devtools from '../devtools/index.js'
|
7
6
|
import type { LiveStoreSchema } from '../schema/mod.js'
|
8
7
|
import { EventId, MutationEvent, mutationLogMetaTable, SYNC_STATUS_TABLE, syncStatusTable } from '../schema/mod.js'
|
9
8
|
import { migrateTable } from '../schema-management/migrations.js'
|
10
|
-
import type { InvalidPullError, IsOfflineError,
|
9
|
+
import type { InvalidPullError, IsOfflineError, SyncBackend } from '../sync/sync.js'
|
11
10
|
import { sql } from '../util.js'
|
12
11
|
import { execSql } from './connection.js'
|
12
|
+
import { makeLeaderSyncProcessor } from './leader-sync-processor.js'
|
13
13
|
import { bootDevtools } from './leader-worker-devtools.js'
|
14
|
-
import { makeLeaderSyncProcessor } from './LeaderSyncProcessor.js'
|
15
14
|
import { makePullQueueSet } from './pull-queue-set.js'
|
16
15
|
import { recreateDb } from './recreate-db.js'
|
17
|
-
import type {
|
18
|
-
import type {
|
19
|
-
DevtoolsOptions,
|
20
|
-
InitialBlockingSyncContext,
|
21
|
-
InitialSyncOptions,
|
22
|
-
LeaderSqliteDb,
|
23
|
-
ShutdownState,
|
24
|
-
} from './types.js'
|
16
|
+
import type { DevtoolsOptions, InitialBlockingSyncContext, InitialSyncOptions, ShutdownState } from './types.js'
|
25
17
|
import { LeaderThreadCtx } from './types.js'
|
26
18
|
|
27
19
|
export const makeLeaderThreadLayer = ({
|
28
20
|
schema,
|
29
21
|
storeId,
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
originId,
|
23
|
+
makeSyncDb,
|
24
|
+
makeSyncBackend,
|
25
|
+
db,
|
26
|
+
dbLog,
|
35
27
|
devtoolsOptions,
|
36
|
-
|
28
|
+
initialSyncOptions = { _tag: 'Skip' },
|
37
29
|
}: {
|
38
30
|
storeId: string
|
39
|
-
|
31
|
+
originId: string
|
40
32
|
schema: LiveStoreSchema
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
33
|
+
makeSyncDb: MakeSynchronousDatabase
|
34
|
+
makeSyncBackend: Effect.Effect<SyncBackend, UnexpectedError, Scope.Scope> | undefined
|
35
|
+
db: SynchronousDatabase
|
36
|
+
dbLog: SynchronousDatabase
|
45
37
|
devtoolsOptions: DevtoolsOptions
|
46
|
-
|
38
|
+
initialSyncOptions: InitialSyncOptions | undefined
|
47
39
|
}): Layer.Layer<LeaderThreadCtx, UnexpectedError, Scope.Scope | HttpClient.HttpClient> =>
|
48
40
|
Effect.gen(function* () {
|
49
41
|
const bootStatusQueue = yield* Queue.unbounded<BootStatus>().pipe(Effect.acquireRelease(Queue.shutdown))
|
50
42
|
|
51
43
|
// TODO do more validation here than just checking the count of tables
|
52
44
|
// Either happens on initial boot or if schema changes
|
53
|
-
const dbMissing =
|
54
|
-
dbReadModel.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
|
45
|
+
const dbMissing = db.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
|
55
46
|
|
56
|
-
const syncBackend =
|
47
|
+
const syncBackend = makeSyncBackend === undefined ? undefined : yield* makeSyncBackend
|
57
48
|
|
58
|
-
const initialBlockingSyncContext = yield* makeInitialBlockingSyncContext({
|
59
|
-
initialSyncOptions: syncOptions?.initialSyncOptions ?? { _tag: 'Skip' },
|
60
|
-
bootStatusQueue,
|
61
|
-
})
|
62
|
-
|
63
|
-
const syncProcessor = yield* makeLeaderSyncProcessor({
|
64
|
-
schema,
|
65
|
-
dbMissing,
|
66
|
-
dbMutationLog,
|
67
|
-
initialBlockingSyncContext,
|
68
|
-
})
|
69
|
-
|
70
|
-
const extraIncomingMessagesQueue = yield* Queue.unbounded<Devtools.MessageToAppLeader>().pipe(
|
71
|
-
Effect.acquireRelease(Queue.shutdown),
|
72
|
-
)
|
49
|
+
const initialBlockingSyncContext = yield* makeInitialBlockingSyncContext({ initialSyncOptions, bootStatusQueue })
|
73
50
|
|
74
|
-
const
|
75
|
-
? {
|
76
|
-
enabled: true as const,
|
77
|
-
syncBackendPullLatch: yield* Effect.makeLatch(true),
|
78
|
-
syncBackendPushLatch: yield* Effect.makeLatch(true),
|
79
|
-
}
|
80
|
-
: { enabled: false as const }
|
51
|
+
const syncProcessor = yield* makeLeaderSyncProcessor({ schema, dbMissing, dbLog, initialBlockingSyncContext })
|
81
52
|
|
82
53
|
const ctx = {
|
83
54
|
schema,
|
84
55
|
bootStatusQueue,
|
85
56
|
storeId,
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
57
|
+
originId,
|
58
|
+
db,
|
59
|
+
dbLog,
|
60
|
+
makeSyncDb,
|
90
61
|
mutationEventSchema: MutationEvent.makeMutationEventSchema(schema),
|
91
62
|
shutdownStateSubRef: yield* SubscriptionRef.make<ShutdownState>('running'),
|
92
|
-
shutdownChannel,
|
93
63
|
syncBackend,
|
94
64
|
syncProcessor,
|
95
65
|
connectedClientSessionPullQueues: yield* makePullQueueSet,
|
96
|
-
extraIncomingMessagesQueue,
|
97
|
-
devtools: devtoolsContext,
|
98
66
|
} satisfies typeof LeaderThreadCtx.Service
|
99
67
|
|
100
68
|
// @ts-expect-error For debugging purposes
|
@@ -177,17 +145,17 @@ const bootLeaderThread = ({
|
|
177
145
|
LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
|
178
146
|
> =>
|
179
147
|
Effect.gen(function* () {
|
180
|
-
const {
|
148
|
+
const { dbLog, bootStatusQueue, syncProcessor } = yield* LeaderThreadCtx
|
181
149
|
|
182
150
|
yield* migrateTable({
|
183
|
-
db:
|
151
|
+
db: dbLog,
|
184
152
|
behaviour: 'create-if-not-exists',
|
185
153
|
tableAst: mutationLogMetaTable.sqliteDef.ast,
|
186
154
|
skipMetaTable: true,
|
187
155
|
})
|
188
156
|
|
189
157
|
yield* migrateTable({
|
190
|
-
db:
|
158
|
+
db: dbLog,
|
191
159
|
behaviour: 'create-if-not-exists',
|
192
160
|
tableAst: syncStatusTable.sqliteDef.ast,
|
193
161
|
skipMetaTable: true,
|
@@ -195,7 +163,7 @@ const bootLeaderThread = ({
|
|
195
163
|
|
196
164
|
// Create sync status row if it doesn't exist
|
197
165
|
yield* execSql(
|
198
|
-
|
166
|
+
dbLog,
|
199
167
|
sql`INSERT INTO ${SYNC_STATUS_TABLE} (head)
|
200
168
|
SELECT ${EventId.ROOT.global}
|
201
169
|
WHERE NOT EXISTS (SELECT 1 FROM ${SYNC_STATUS_TABLE})`,
|
@@ -1,20 +1,17 @@
|
|
1
1
|
import { Effect, Schema } from '@livestore/utils/effect'
|
2
2
|
|
3
|
-
import type {
|
3
|
+
import type { SynchronousDatabase } from '../adapter-types.js'
|
4
4
|
import * as EventId from '../schema/EventId.js'
|
5
|
-
import type * as MutationEvent from '../schema/MutationEvent.js'
|
6
5
|
import { MUTATION_LOG_META_TABLE, mutationLogMetaTable, SYNC_STATUS_TABLE } from '../schema/system-tables.js'
|
7
6
|
import { prepareBindValues, sql } from '../util.js'
|
8
7
|
import { LeaderThreadCtx } from './types.js'
|
9
8
|
|
10
|
-
export const getMutationEventsSince = (
|
11
|
-
since: EventId.EventId,
|
12
|
-
): Effect.Effect<ReadonlyArray<MutationEvent.AnyEncoded>, never, LeaderThreadCtx> =>
|
9
|
+
export const getMutationEventsSince = (since: EventId.EventId) =>
|
13
10
|
Effect.gen(function* () {
|
14
|
-
const {
|
11
|
+
const { dbLog } = yield* LeaderThreadCtx
|
15
12
|
|
16
13
|
const query = mutationLogMetaTable.query.where('idGlobal', '>=', since.global).asSql()
|
17
|
-
const pendingMutationEventsRaw =
|
14
|
+
const pendingMutationEventsRaw = dbLog.select(query.query, prepareBindValues(query.bindValues, query.query))
|
18
15
|
const pendingMutationEvents = Schema.decodeUnknownSync(mutationLogMetaTable.schema.pipe(Schema.Array))(
|
19
16
|
pendingMutationEventsRaw,
|
20
17
|
)
|
@@ -29,18 +26,17 @@ export const getMutationEventsSince = (
|
|
29
26
|
.filter((_) => EventId.compare(_.id, since) > 0)
|
30
27
|
})
|
31
28
|
|
32
|
-
export const getLocalHeadFromDb = (
|
33
|
-
const res =
|
29
|
+
export const getLocalHeadFromDb = (dbLog: SynchronousDatabase) => {
|
30
|
+
const res = dbLog.select<{ idGlobal: number; idLocal: number }>(
|
34
31
|
sql`select idGlobal, idLocal from ${MUTATION_LOG_META_TABLE} order by idGlobal DESC, idLocal DESC limit 1`,
|
35
32
|
)[0]
|
36
33
|
|
37
34
|
return res ? { global: res.idGlobal, local: res.idLocal } : EventId.ROOT
|
38
35
|
}
|
39
36
|
|
40
|
-
export const getBackendHeadFromDb = (
|
41
|
-
|
42
|
-
EventId.ROOT.global
|
37
|
+
export const getBackendHeadFromDb = (dbLog: SynchronousDatabase) =>
|
38
|
+
dbLog.select<{ head: number }>(sql`select head from ${SYNC_STATUS_TABLE}`)[0]?.head ?? EventId.ROOT.global
|
43
39
|
|
44
40
|
// TODO use prepared statements
|
45
|
-
export const updateBackendHead = (
|
46
|
-
|
41
|
+
export const updateBackendHead = (dbLog: SynchronousDatabase, head: EventId.EventId) =>
|
42
|
+
dbLog.execute(sql`UPDATE ${SYNC_STATUS_TABLE} SET head = ${head.global}`)
|
@@ -12,56 +12,54 @@ export const recreateDb: Effect.Effect<
|
|
12
12
|
UnexpectedError | SqliteError | IsOfflineError | InvalidPullError,
|
13
13
|
LeaderThreadCtx | HttpClient.HttpClient
|
14
14
|
> = Effect.gen(function* () {
|
15
|
-
const {
|
15
|
+
const { db, dbLog, makeSyncDb, schema, bootStatusQueue } = yield* LeaderThreadCtx
|
16
16
|
|
17
17
|
const migrationOptions = schema.migrationOptions
|
18
18
|
|
19
19
|
yield* Effect.addFinalizer(
|
20
20
|
Effect.fn('recreateDb:finalizer')(function* (ex) {
|
21
|
-
if (ex._tag === 'Failure')
|
21
|
+
if (ex._tag === 'Failure') db.destroy()
|
22
22
|
}),
|
23
23
|
)
|
24
24
|
|
25
25
|
// NOTE to speed up the operations below, we're creating a temporary in-memory database
|
26
26
|
// and later we'll overwrite the persisted database with the new data
|
27
|
-
|
28
|
-
|
29
|
-
const tmpDb = dbReadModel
|
30
|
-
yield* configureConnection(tmpDb, { fkEnabled: true })
|
27
|
+
const tmpSyncDb = yield* makeSyncDb({ _tag: 'in-memory' })
|
28
|
+
yield* configureConnection(tmpSyncDb, { fkEnabled: true })
|
31
29
|
|
32
30
|
const initDb = (hooks: Partial<MigrationHooks> | undefined) =>
|
33
31
|
Effect.gen(function* () {
|
34
|
-
yield* Effect.tryAll(() => hooks?.init?.(
|
32
|
+
yield* Effect.tryAll(() => hooks?.init?.(tmpSyncDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
35
33
|
|
36
34
|
yield* migrateDb({
|
37
|
-
db:
|
35
|
+
db: tmpSyncDb,
|
38
36
|
schema,
|
39
37
|
onProgress: ({ done, total }) =>
|
40
38
|
Queue.offer(bootStatusQueue, { stage: 'migrating', progress: { done, total } }),
|
41
39
|
})
|
42
40
|
|
43
|
-
initializeSingletonTables(schema,
|
41
|
+
initializeSingletonTables(schema, tmpSyncDb)
|
44
42
|
|
45
|
-
yield* Effect.tryAll(() => hooks?.pre?.(
|
43
|
+
yield* Effect.tryAll(() => hooks?.pre?.(tmpSyncDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
46
44
|
|
47
|
-
return
|
45
|
+
return tmpSyncDb
|
48
46
|
})
|
49
47
|
|
50
48
|
switch (migrationOptions.strategy) {
|
51
49
|
case 'from-mutation-log': {
|
52
50
|
const hooks = migrationOptions.hooks
|
53
|
-
const
|
51
|
+
const tmpSyncDb = yield* initDb(hooks)
|
54
52
|
|
55
53
|
yield* rehydrateFromMutationLog({
|
56
|
-
db:
|
57
|
-
logDb:
|
54
|
+
db: tmpSyncDb,
|
55
|
+
logDb: dbLog,
|
58
56
|
schema,
|
59
57
|
migrationOptions,
|
60
58
|
onProgress: ({ done, total }) =>
|
61
59
|
Queue.offer(bootStatusQueue, { stage: 'rehydrating', progress: { done, total } }),
|
62
60
|
})
|
63
61
|
|
64
|
-
yield* Effect.tryAll(() => hooks?.post?.(
|
62
|
+
yield* Effect.tryAll(() => hooks?.post?.(tmpSyncDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
65
63
|
|
66
64
|
break
|
67
65
|
}
|
@@ -76,13 +74,13 @@ export const recreateDb: Effect.Effect<
|
|
76
74
|
break
|
77
75
|
}
|
78
76
|
case 'manual': {
|
79
|
-
const oldDbData =
|
77
|
+
const oldDbData = db.export()
|
80
78
|
|
81
79
|
const newDbData = yield* Effect.tryAll(() => migrationOptions.migrate(oldDbData)).pipe(
|
82
80
|
UnexpectedError.mapToUnexpectedError,
|
83
81
|
)
|
84
82
|
|
85
|
-
|
83
|
+
tmpSyncDb.import(newDbData)
|
86
84
|
|
87
85
|
// TODO validate schema
|
88
86
|
|
@@ -93,19 +91,17 @@ export const recreateDb: Effect.Effect<
|
|
93
91
|
}
|
94
92
|
}
|
95
93
|
|
96
|
-
// TODO bring back
|
97
94
|
// Import the temporary in-memory database into the persistent database
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
yield* Effect.sync(() => db.import(tmpSyncDb)).pipe(
|
96
|
+
Effect.withSpan('@livestore/common:leader-thread:recreateDb:import'),
|
97
|
+
)
|
101
98
|
|
102
99
|
// TODO maybe bring back re-using this initial snapshot to avoid calling `.export()` again
|
103
100
|
// We've disabled this for now as it made the code too complex, as we often run syncing right after
|
104
101
|
// so the snapshot is no longer up to date
|
105
|
-
// const snapshotFromTmpDb =
|
102
|
+
// const snapshotFromTmpDb = tmpSyncDb.export()
|
106
103
|
|
107
|
-
|
108
|
-
// tmpDb.close()
|
104
|
+
tmpSyncDb.close()
|
109
105
|
}).pipe(
|
110
106
|
Effect.scoped, // NOTE we're closing the scope here so finalizers are called when the effect is done
|
111
107
|
Effect.withSpan('@livestore/common:leader-thread:recreateDb'),
|