@livestore/adapter-node 0.4.0-dev.0 → 0.4.0-dev.10
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/client-session/adapter.d.ts +13 -1
- package/dist/client-session/adapter.d.ts.map +1 -1
- package/dist/client-session/adapter.js +44 -14
- package/dist/client-session/adapter.js.map +1 -1
- package/dist/make-leader-worker.d.ts +2 -1
- package/dist/make-leader-worker.d.ts.map +1 -1
- package/dist/make-leader-worker.js +8 -0
- package/dist/make-leader-worker.js.map +1 -1
- package/dist/worker-schema.d.ts +27 -4
- package/dist/worker-schema.d.ts.map +1 -1
- package/dist/worker-schema.js +15 -2
- package/dist/worker-schema.js.map +1 -1
- package/package.json +8 -8
- package/src/client-session/adapter.ts +79 -12
- package/src/make-leader-worker.ts +10 -0
- package/src/worker-schema.ts +30 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { hostname } from 'node:os'
|
|
2
|
+
import path from 'node:path'
|
|
2
3
|
import * as WT from 'node:worker_threads'
|
|
3
4
|
import {
|
|
4
5
|
type Adapter,
|
|
5
6
|
type BootStatus,
|
|
6
7
|
ClientSessionLeaderThreadProxy,
|
|
7
|
-
|
|
8
|
+
IntentionalShutdownCause,
|
|
8
9
|
type LockStatus,
|
|
9
10
|
type MakeSqliteDb,
|
|
10
11
|
makeClientSession,
|
|
@@ -17,17 +18,21 @@ import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
|
17
18
|
import { LiveStoreEvent } from '@livestore/common/schema'
|
|
18
19
|
import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
|
|
19
20
|
import { sqliteDbFactory } from '@livestore/sqlite-wasm/node'
|
|
21
|
+
import { omitUndefineds } from '@livestore/utils'
|
|
20
22
|
import {
|
|
21
23
|
Cause,
|
|
22
24
|
Effect,
|
|
23
25
|
Exit,
|
|
24
26
|
FetchHttpClient,
|
|
25
27
|
Fiber,
|
|
28
|
+
FileSystem,
|
|
26
29
|
Layer,
|
|
27
30
|
ParseResult,
|
|
28
31
|
Queue,
|
|
32
|
+
Schedule,
|
|
29
33
|
Schema,
|
|
30
34
|
Stream,
|
|
35
|
+
Subscribable,
|
|
31
36
|
SubscriptionRef,
|
|
32
37
|
Worker,
|
|
33
38
|
WorkerError,
|
|
@@ -50,6 +55,13 @@ export interface NodeAdapterOptions {
|
|
|
50
55
|
*/
|
|
51
56
|
sessionId?: string
|
|
52
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Warning: This will reset both the app and eventlog database. This should only be used during development.
|
|
60
|
+
*
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
resetPersistence?: boolean
|
|
64
|
+
|
|
53
65
|
devtools?: {
|
|
54
66
|
schemaPath: string | URL
|
|
55
67
|
/**
|
|
@@ -89,13 +101,19 @@ export const makeAdapter = ({
|
|
|
89
101
|
*/
|
|
90
102
|
export const makeWorkerAdapter = ({
|
|
91
103
|
workerUrl,
|
|
104
|
+
workerExtraArgs,
|
|
92
105
|
...options
|
|
93
106
|
}: NodeAdapterOptions & {
|
|
94
107
|
/**
|
|
95
108
|
* Example: `new URL('./livestore.worker.ts', import.meta.url)`
|
|
96
109
|
*/
|
|
97
110
|
workerUrl: URL
|
|
98
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Extra arguments to pass to the worker which can be accessed in the worker
|
|
113
|
+
* via `getWorkerArgs()`
|
|
114
|
+
*/
|
|
115
|
+
workerExtraArgs?: Schema.JsonValue
|
|
116
|
+
}): Adapter => makeAdapterImpl({ ...options, leaderThread: { _tag: 'multi-threaded', workerUrl, workerExtraArgs } })
|
|
99
117
|
|
|
100
118
|
const makeAdapterImpl = ({
|
|
101
119
|
storage,
|
|
@@ -104,6 +122,7 @@ const makeAdapterImpl = ({
|
|
|
104
122
|
// TODO make this dynamic and actually support multiple sessions
|
|
105
123
|
sessionId = 'static',
|
|
106
124
|
testing,
|
|
125
|
+
resetPersistence = false,
|
|
107
126
|
leaderThread: leaderThreadInput,
|
|
108
127
|
}: NodeAdapterOptions & {
|
|
109
128
|
leaderThread:
|
|
@@ -114,6 +133,7 @@ const makeAdapterImpl = ({
|
|
|
114
133
|
| {
|
|
115
134
|
_tag: 'multi-threaded'
|
|
116
135
|
workerUrl: URL
|
|
136
|
+
workerExtraArgs: Schema.JsonValue | undefined
|
|
117
137
|
}
|
|
118
138
|
}): Adapter =>
|
|
119
139
|
((adapterArgs) =>
|
|
@@ -135,6 +155,14 @@ const makeAdapterImpl = ({
|
|
|
135
155
|
|
|
136
156
|
const shutdownChannel = yield* makeShutdownChannel(storeId)
|
|
137
157
|
|
|
158
|
+
if (resetPersistence === true) {
|
|
159
|
+
yield* shutdownChannel
|
|
160
|
+
.send(IntentionalShutdownCause.make({ reason: 'adapter-reset' }))
|
|
161
|
+
.pipe(UnexpectedError.mapToUnexpectedError)
|
|
162
|
+
|
|
163
|
+
yield* resetNodePersistence({ storage, storeId })
|
|
164
|
+
}
|
|
165
|
+
|
|
138
166
|
yield* shutdownChannel.listen.pipe(
|
|
139
167
|
Stream.flatten(),
|
|
140
168
|
Stream.tap((cause) =>
|
|
@@ -173,11 +201,13 @@ const makeAdapterImpl = ({
|
|
|
173
201
|
clientId,
|
|
174
202
|
schema,
|
|
175
203
|
makeSqliteDb,
|
|
176
|
-
syncOptions: leaderThreadInput.sync,
|
|
177
|
-
syncPayload,
|
|
178
204
|
devtools: devtoolsOptions,
|
|
179
205
|
storage,
|
|
180
|
-
|
|
206
|
+
...omitUndefineds({
|
|
207
|
+
syncOptions: leaderThreadInput.sync,
|
|
208
|
+
syncPayload,
|
|
209
|
+
testing,
|
|
210
|
+
}),
|
|
181
211
|
}).pipe(UnexpectedError.mapToUnexpectedError)
|
|
182
212
|
: yield* makeWorkerLeaderThread({
|
|
183
213
|
shutdown,
|
|
@@ -185,6 +215,7 @@ const makeAdapterImpl = ({
|
|
|
185
215
|
clientId,
|
|
186
216
|
sessionId,
|
|
187
217
|
workerUrl: leaderThreadInput.workerUrl,
|
|
218
|
+
workerExtraArgs: leaderThreadInput.workerExtraArgs,
|
|
188
219
|
storage,
|
|
189
220
|
devtools: devtoolsOptions,
|
|
190
221
|
bootStatusQueue,
|
|
@@ -219,10 +250,38 @@ const makeAdapterImpl = ({
|
|
|
219
250
|
return clientSession
|
|
220
251
|
}).pipe(
|
|
221
252
|
Effect.withSpan('@livestore/adapter-node:adapter'),
|
|
222
|
-
Effect.provide(PlatformNode.NodeFileSystem.layer),
|
|
223
|
-
Effect.provide(FetchHttpClient.layer),
|
|
253
|
+
Effect.provide(Layer.mergeAll(PlatformNode.NodeFileSystem.layer, FetchHttpClient.layer)),
|
|
224
254
|
)) satisfies Adapter
|
|
225
255
|
|
|
256
|
+
const resetNodePersistence = ({
|
|
257
|
+
storage,
|
|
258
|
+
storeId,
|
|
259
|
+
}: {
|
|
260
|
+
storage: WorkerSchema.StorageType
|
|
261
|
+
storeId: string
|
|
262
|
+
}): Effect.Effect<void, UnexpectedError, FileSystem.FileSystem> => {
|
|
263
|
+
if (storage.type !== 'fs') {
|
|
264
|
+
return Effect.void
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const directory = path.join(storage.baseDirectory ?? '', storeId)
|
|
268
|
+
|
|
269
|
+
return Effect.gen(function* () {
|
|
270
|
+
const fs = yield* FileSystem.FileSystem
|
|
271
|
+
|
|
272
|
+
const directoryExists = yield* fs.exists(directory).pipe(UnexpectedError.mapToUnexpectedError)
|
|
273
|
+
|
|
274
|
+
if (directoryExists === false) {
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
yield* fs.remove(directory, { recursive: true }).pipe(UnexpectedError.mapToUnexpectedError)
|
|
279
|
+
}).pipe(
|
|
280
|
+
Effect.retry({ schedule: Schedule.exponentialBackoff10Sec }),
|
|
281
|
+
Effect.withSpan('@livestore/adapter-node:resetPersistence', { attributes: { directory } }),
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
|
|
226
285
|
const makeLocalLeaderThread = ({
|
|
227
286
|
storeId,
|
|
228
287
|
clientId,
|
|
@@ -257,12 +316,13 @@ const makeLocalLeaderThread = ({
|
|
|
257
316
|
syncPayload,
|
|
258
317
|
devtools,
|
|
259
318
|
makeSqliteDb,
|
|
260
|
-
testing: testing?.overrides,
|
|
319
|
+
...omitUndefineds({ testing: testing?.overrides }),
|
|
261
320
|
}).pipe(Layer.unwrapScoped),
|
|
262
321
|
)
|
|
263
322
|
|
|
264
323
|
return yield* Effect.gen(function* () {
|
|
265
|
-
const { dbState, dbEventlog, syncProcessor, extraIncomingMessagesQueue, initialState } =
|
|
324
|
+
const { dbState, dbEventlog, syncProcessor, extraIncomingMessagesQueue, initialState, networkStatus } =
|
|
325
|
+
yield* LeaderThreadCtx
|
|
266
326
|
|
|
267
327
|
const initialLeaderHead = Eventlog.getClientHeadFromDb(dbEventlog)
|
|
268
328
|
|
|
@@ -281,8 +341,9 @@ const makeLocalLeaderThread = ({
|
|
|
281
341
|
getEventlogData: Effect.sync(() => dbEventlog.export()),
|
|
282
342
|
getSyncState: syncProcessor.syncState,
|
|
283
343
|
sendDevtoolsMessage: (message) => extraIncomingMessagesQueue.offer(message),
|
|
344
|
+
networkStatus,
|
|
284
345
|
},
|
|
285
|
-
{ overrides: testing?.overrides?.clientSession?.leaderThreadProxy },
|
|
346
|
+
{ ...omitUndefineds({ overrides: testing?.overrides?.clientSession?.leaderThreadProxy }) },
|
|
286
347
|
)
|
|
287
348
|
|
|
288
349
|
const initialSnapshot = dbState.export()
|
|
@@ -297,6 +358,7 @@ const makeWorkerLeaderThread = ({
|
|
|
297
358
|
clientId,
|
|
298
359
|
sessionId,
|
|
299
360
|
workerUrl,
|
|
361
|
+
workerExtraArgs,
|
|
300
362
|
storage,
|
|
301
363
|
devtools,
|
|
302
364
|
bootStatusQueue,
|
|
@@ -308,6 +370,7 @@ const makeWorkerLeaderThread = ({
|
|
|
308
370
|
clientId: string
|
|
309
371
|
sessionId: string
|
|
310
372
|
workerUrl: URL
|
|
373
|
+
workerExtraArgs: Schema.JsonValue | undefined
|
|
311
374
|
storage: WorkerSchema.StorageType
|
|
312
375
|
devtools: WorkerSchema.LeaderWorkerInnerInitialMessage['devtools']
|
|
313
376
|
bootStatusQueue: Queue.Queue<BootStatus>
|
|
@@ -319,7 +382,7 @@ const makeWorkerLeaderThread = ({
|
|
|
319
382
|
Effect.gen(function* () {
|
|
320
383
|
const nodeWorker = new WT.Worker(workerUrl, {
|
|
321
384
|
execArgv: process.env.DEBUG_WORKER ? ['--inspect --enable-source-maps'] : ['--enable-source-maps'],
|
|
322
|
-
argv: [Schema.encodeSync(WorkerSchema.WorkerArgv)({ storeId, clientId, sessionId })],
|
|
385
|
+
argv: [Schema.encodeSync(WorkerSchema.WorkerArgv)({ storeId, clientId, sessionId, extraArgs: workerExtraArgs })],
|
|
323
386
|
})
|
|
324
387
|
const nodeWorkerLayer = yield* Layer.build(PlatformNode.NodeWorker.layer(() => nodeWorker))
|
|
325
388
|
|
|
@@ -432,9 +495,13 @@ const makeWorkerLeaderThread = ({
|
|
|
432
495
|
UnexpectedError.mapToUnexpectedError,
|
|
433
496
|
Effect.withSpan('@livestore/adapter-node:client-session:devtoolsMessageForLeader'),
|
|
434
497
|
),
|
|
498
|
+
networkStatus: Subscribable.make({
|
|
499
|
+
get: runInWorker(new WorkerSchema.LeaderWorkerInnerGetNetworkStatus()).pipe(Effect.orDie),
|
|
500
|
+
changes: runInWorkerStream(new WorkerSchema.LeaderWorkerInnerNetworkStatusStream()).pipe(Stream.orDie),
|
|
501
|
+
}),
|
|
435
502
|
},
|
|
436
503
|
{
|
|
437
|
-
overrides: testing?.overrides?.clientSession?.leaderThreadProxy,
|
|
504
|
+
...omitUndefineds({ overrides: testing?.overrides?.clientSession?.leaderThreadProxy }),
|
|
438
505
|
},
|
|
439
506
|
)
|
|
440
507
|
|
|
@@ -113,6 +113,16 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
|
|
|
113
113
|
UnexpectedError.mapToUnexpectedError,
|
|
114
114
|
Effect.withSpan('@livestore/adapter-node:worker:GetLeaderSyncState'),
|
|
115
115
|
),
|
|
116
|
+
GetNetworkStatus: () =>
|
|
117
|
+
Effect.gen(function* () {
|
|
118
|
+
const workerCtx = yield* LeaderThreadCtx
|
|
119
|
+
return yield* workerCtx.networkStatus
|
|
120
|
+
}).pipe(UnexpectedError.mapToUnexpectedError, Effect.withSpan('@livestore/adapter-node:worker:GetNetworkStatus')),
|
|
121
|
+
NetworkStatusStream: () =>
|
|
122
|
+
Effect.gen(function* () {
|
|
123
|
+
const workerCtx = yield* LeaderThreadCtx
|
|
124
|
+
return workerCtx.networkStatus.changes
|
|
125
|
+
}).pipe(Stream.unwrapScoped),
|
|
116
126
|
GetRecreateSnapshot: () =>
|
|
117
127
|
Effect.gen(function* () {
|
|
118
128
|
const workerCtx = yield* LeaderThreadCtx
|
package/src/worker-schema.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BootStatus,
|
|
3
|
+
Devtools,
|
|
4
|
+
LeaderAheadError,
|
|
5
|
+
MigrationsReport,
|
|
6
|
+
SyncBackend,
|
|
7
|
+
SyncState,
|
|
8
|
+
UnexpectedError,
|
|
9
|
+
} from '@livestore/common'
|
|
2
10
|
import { EventSequenceNumber, LiveStoreEvent } from '@livestore/common/schema'
|
|
3
11
|
import { Schema, Transferable } from '@livestore/utils/effect'
|
|
4
12
|
|
|
@@ -7,6 +15,7 @@ export const WorkerArgv = Schema.parseJson(
|
|
|
7
15
|
clientId: Schema.String,
|
|
8
16
|
storeId: Schema.String,
|
|
9
17
|
sessionId: Schema.String,
|
|
18
|
+
extraArgs: Schema.UndefinedOr(Schema.JsonValue),
|
|
10
19
|
}),
|
|
11
20
|
)
|
|
12
21
|
|
|
@@ -158,6 +167,24 @@ export class LeaderWorkerInnerGetLeaderSyncState extends Schema.TaggedRequest<Le
|
|
|
158
167
|
},
|
|
159
168
|
) {}
|
|
160
169
|
|
|
170
|
+
export class LeaderWorkerInnerGetNetworkStatus extends Schema.TaggedRequest<LeaderWorkerInnerGetNetworkStatus>()(
|
|
171
|
+
'GetNetworkStatus',
|
|
172
|
+
{
|
|
173
|
+
payload: {},
|
|
174
|
+
success: SyncBackend.NetworkStatus,
|
|
175
|
+
failure: UnexpectedError,
|
|
176
|
+
},
|
|
177
|
+
) {}
|
|
178
|
+
|
|
179
|
+
export class LeaderWorkerInnerNetworkStatusStream extends Schema.TaggedRequest<LeaderWorkerInnerNetworkStatusStream>()(
|
|
180
|
+
'NetworkStatusStream',
|
|
181
|
+
{
|
|
182
|
+
payload: {},
|
|
183
|
+
success: SyncBackend.NetworkStatus,
|
|
184
|
+
failure: UnexpectedError,
|
|
185
|
+
},
|
|
186
|
+
) {}
|
|
187
|
+
|
|
161
188
|
export class LeaderWorkerInnerShutdown extends Schema.TaggedRequest<LeaderWorkerInnerShutdown>()('Shutdown', {
|
|
162
189
|
payload: {},
|
|
163
190
|
success: Schema.Void,
|
|
@@ -185,6 +212,8 @@ export const LeaderWorkerInnerRequest = Schema.Union(
|
|
|
185
212
|
LeaderWorkerInnerExportEventlog,
|
|
186
213
|
LeaderWorkerInnerGetLeaderHead,
|
|
187
214
|
LeaderWorkerInnerGetLeaderSyncState,
|
|
215
|
+
LeaderWorkerInnerGetNetworkStatus,
|
|
216
|
+
LeaderWorkerInnerNetworkStatusStream,
|
|
188
217
|
LeaderWorkerInnerShutdown,
|
|
189
218
|
LeaderWorkerInnerExtraDevtoolsMessage,
|
|
190
219
|
)
|