@livestore/adapter-node 0.3.0-dev.18 → 0.3.0-dev.21
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/index.d.ts +10 -1
- package/dist/client-session/index.d.ts.map +1 -1
- package/dist/client-session/index.js +54 -14
- package/dist/client-session/index.js.map +1 -1
- package/dist/devtools/devtools-server.d.ts +2 -1
- package/dist/devtools/devtools-server.d.ts.map +1 -1
- package/dist/devtools/devtools-server.js +6 -5
- package/dist/devtools/devtools-server.js.map +1 -1
- package/dist/in-memory/index.d.ts +5 -1
- package/dist/in-memory/index.d.ts.map +1 -1
- package/dist/in-memory/index.js +2 -3
- package/dist/in-memory/index.js.map +1 -1
- package/dist/make-leader-worker.d.ts +1 -1
- package/dist/make-leader-worker.js +10 -10
- package/dist/make-leader-worker.js.map +1 -1
- package/dist/worker-schema.d.ts +15 -8
- package/dist/worker-schema.d.ts.map +1 -1
- package/dist/worker-schema.js +1 -0
- package/dist/worker-schema.js.map +1 -1
- package/package.json +12 -12
- package/src/client-session/index.ts +75 -14
- package/src/devtools/devtools-server.ts +7 -4
- package/src/in-memory/index.ts +6 -4
- package/src/make-leader-worker.ts +12 -9
- package/src/worker-schema.ts +1 -0
- package/tmp/test-effect.ts +30 -0
|
@@ -3,6 +3,7 @@ import * as WT from 'node:worker_threads'
|
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
5
|
Adapter,
|
|
6
|
+
BootStatus,
|
|
6
7
|
ClientSession,
|
|
7
8
|
ClientSessionLeaderThreadProxy,
|
|
8
9
|
IntentionalShutdownCause,
|
|
@@ -10,7 +11,7 @@ import type {
|
|
|
10
11
|
NetworkStatus,
|
|
11
12
|
} from '@livestore/common'
|
|
12
13
|
import { Devtools, UnexpectedError } from '@livestore/common'
|
|
13
|
-
import
|
|
14
|
+
import * as DevtoolsNode from '@livestore/devtools-node-common/web-channel'
|
|
14
15
|
import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
|
|
15
16
|
import { sqliteDbFactory } from '@livestore/sqlite-wasm/node'
|
|
16
17
|
import {
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
Effect,
|
|
19
20
|
Fiber,
|
|
20
21
|
ParseResult,
|
|
22
|
+
Queue,
|
|
21
23
|
Schema,
|
|
22
24
|
Stream,
|
|
23
25
|
SubscriptionRef,
|
|
@@ -40,6 +42,8 @@ export interface NodeAdapterOptions {
|
|
|
40
42
|
baseDirectory?: string
|
|
41
43
|
/** The default is the hostname of the current machine */
|
|
42
44
|
clientId?: string
|
|
45
|
+
/** @default 'static' */
|
|
46
|
+
sessionId?: string
|
|
43
47
|
devtools?: {
|
|
44
48
|
/**
|
|
45
49
|
* Where to run the devtools server (via Vite)
|
|
@@ -47,20 +51,28 @@ export interface NodeAdapterOptions {
|
|
|
47
51
|
* @default 4242
|
|
48
52
|
*/
|
|
49
53
|
port: number
|
|
54
|
+
/**
|
|
55
|
+
* @default 'localhost'
|
|
56
|
+
*/
|
|
57
|
+
host: string
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Warning: This adapter doesn't currently support multiple client sessions for the same client (i.e. same storeId + clientId)
|
|
63
|
+
*/
|
|
53
64
|
export const makeNodeAdapter = ({
|
|
54
65
|
workerUrl,
|
|
55
66
|
schemaPath,
|
|
56
67
|
baseDirectory,
|
|
57
|
-
devtools: devtoolsOptions = { port: 4242 },
|
|
68
|
+
devtools: devtoolsOptions = { port: 4242, host: 'localhost' },
|
|
58
69
|
clientId = hostname(),
|
|
70
|
+
// TODO make this dynamic and actually support multiple sessions
|
|
71
|
+
sessionId = 'static',
|
|
59
72
|
}: NodeAdapterOptions): Adapter =>
|
|
60
|
-
(({ storeId, devtoolsEnabled, shutdown, connectDevtoolsToStore }) =>
|
|
73
|
+
(({ storeId, devtoolsEnabled, shutdown, connectDevtoolsToStore, bootStatusQueue }) =>
|
|
61
74
|
Effect.gen(function* () {
|
|
62
|
-
|
|
63
|
-
const sessionId = 'static'
|
|
75
|
+
yield* Queue.offer(bootStatusQueue, { stage: 'loading' })
|
|
64
76
|
|
|
65
77
|
const sqlite3 = yield* Effect.promise(() => loadSqlite3Wasm())
|
|
66
78
|
const makeSqliteDb = yield* sqliteDbFactory({ sqlite3 })
|
|
@@ -99,20 +111,34 @@ export const makeNodeAdapter = ({
|
|
|
99
111
|
devtoolsEnabled,
|
|
100
112
|
devtoolsOptions,
|
|
101
113
|
schemaPath,
|
|
114
|
+
bootStatusQueue,
|
|
102
115
|
})
|
|
103
116
|
|
|
104
117
|
syncInMemoryDb.import(initialSnapshot)
|
|
105
118
|
|
|
106
119
|
if (devtoolsEnabled) {
|
|
107
120
|
yield* Effect.gen(function* () {
|
|
108
|
-
const
|
|
121
|
+
const webmeshNode = yield* DevtoolsNode.makeNodeDevtoolsConnectedMeshNode({
|
|
122
|
+
url: `ws://${devtoolsOptions.host}:${devtoolsOptions.port}`,
|
|
109
123
|
nodeName: `client-session-${storeId}-${clientId}-${sessionId}`,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const sessionsChannel = yield* webmeshNode.makeBroadcastChannel({
|
|
127
|
+
channelName: 'session-info',
|
|
128
|
+
schema: Devtools.SessionInfo.Message,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
yield* Devtools.SessionInfo.provideSessionInfo({
|
|
132
|
+
webChannel: sessionsChannel,
|
|
133
|
+
sessionInfo: Devtools.SessionInfo.SessionInfo.make({ storeId, clientId, sessionId }),
|
|
134
|
+
}).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
|
|
135
|
+
|
|
136
|
+
webmeshNode.debug.print()
|
|
137
|
+
|
|
138
|
+
const storeDevtoolsChannel = yield* DevtoolsNode.makeChannelForConnectedMeshNode({
|
|
139
|
+
node: webmeshNode,
|
|
140
|
+
target: `devtools-${storeId}-${clientId}-${sessionId}`,
|
|
141
|
+
schema: { listen: Devtools.ClientSession.MessageToApp, send: Devtools.ClientSession.MessageFromApp },
|
|
116
142
|
})
|
|
117
143
|
|
|
118
144
|
yield* connectDevtoolsToStore(storeDevtoolsChannel)
|
|
@@ -150,6 +176,7 @@ const makeLeaderThread = ({
|
|
|
150
176
|
devtoolsEnabled,
|
|
151
177
|
devtoolsOptions,
|
|
152
178
|
schemaPath,
|
|
179
|
+
// bootStatusQueue,
|
|
153
180
|
}: {
|
|
154
181
|
shutdown: (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) => void
|
|
155
182
|
storeId: string
|
|
@@ -158,8 +185,9 @@ const makeLeaderThread = ({
|
|
|
158
185
|
workerUrl: URL
|
|
159
186
|
baseDirectory: string | undefined
|
|
160
187
|
devtoolsEnabled: boolean
|
|
161
|
-
devtoolsOptions: { port: number }
|
|
188
|
+
devtoolsOptions: { port: number; host: string }
|
|
162
189
|
schemaPath: string
|
|
190
|
+
bootStatusQueue: Queue.Queue<BootStatus>
|
|
163
191
|
}) =>
|
|
164
192
|
Effect.gen(function* () {
|
|
165
193
|
const nodeWorker = new WT.Worker(workerUrl, {
|
|
@@ -175,7 +203,7 @@ const makeLeaderThread = ({
|
|
|
175
203
|
storeId,
|
|
176
204
|
clientId,
|
|
177
205
|
baseDirectory,
|
|
178
|
-
devtools: { enabled: devtoolsEnabled, port: devtoolsOptions.port },
|
|
206
|
+
devtools: { enabled: devtoolsEnabled, port: devtoolsOptions.port, host: devtoolsOptions.host },
|
|
179
207
|
schemaPath,
|
|
180
208
|
}),
|
|
181
209
|
}).pipe(
|
|
@@ -242,6 +270,39 @@ const makeLeaderThread = ({
|
|
|
242
270
|
)
|
|
243
271
|
}).pipe(Stream.unwrap) as any
|
|
244
272
|
|
|
273
|
+
const _bootStatusFiber = yield* runInWorkerStream(new WorkerSchema.LeaderWorkerInner.BootStatusStream()).pipe(
|
|
274
|
+
// TODO bring back when fixed https://github.com/Effect-TS/effect/issues/4576
|
|
275
|
+
// Stream.tap((bootStatus) => Queue.offer(bootStatusQueue, bootStatus)),
|
|
276
|
+
// TODO remove when fixed https://github.com/Effect-TS/effect/issues/4576
|
|
277
|
+
// Stream.tap(
|
|
278
|
+
// Effect.fn(function* (_) {
|
|
279
|
+
// if (yield* Queue.isShutdown(bootStatusQueue)) {
|
|
280
|
+
// return
|
|
281
|
+
// } else {
|
|
282
|
+
// console.log('offering boot status', _)
|
|
283
|
+
// // yield* Queue.offer(bootStatusQueue, _).pipe(
|
|
284
|
+
// // Effect.onInterrupt(() => Effect.log('boot status stream interrupted')),
|
|
285
|
+
// // // Effect.tapErrorCause((cause) => Effect.logError('error offering boot status', cause)),
|
|
286
|
+
// // )
|
|
287
|
+
// }
|
|
288
|
+
// }),
|
|
289
|
+
// ),
|
|
290
|
+
Stream.runDrain,
|
|
291
|
+
Effect.tapErrorCause((cause) =>
|
|
292
|
+
Cause.isInterruptedOnly(cause) ? Effect.void : Effect.sync(() => shutdown(cause)),
|
|
293
|
+
),
|
|
294
|
+
Effect.interruptible,
|
|
295
|
+
Effect.tapCauseLogPretty,
|
|
296
|
+
Effect.forkScoped,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
// TODO bring back when fixed https://github.com/Effect-TS/effect/issues/4576
|
|
300
|
+
// yield* Queue.awaitShutdown(bootStatusQueue).pipe(
|
|
301
|
+
// Effect.andThen(Fiber.interrupt(bootStatusFiber)),
|
|
302
|
+
// Effect.tapCauseLogPretty,
|
|
303
|
+
// Effect.forkScoped,
|
|
304
|
+
// )
|
|
305
|
+
|
|
245
306
|
const initialLeaderHead = yield* runInWorker(new WorkerSchema.LeaderWorkerInner.GetLeaderHead())
|
|
246
307
|
|
|
247
308
|
const networkStatus = yield* SubscriptionRef.make<NetworkStatus>({
|
|
@@ -20,11 +20,13 @@ export const startDevtoolsServer = ({
|
|
|
20
20
|
clientId,
|
|
21
21
|
sessionId,
|
|
22
22
|
port,
|
|
23
|
+
host,
|
|
23
24
|
}: {
|
|
24
25
|
schemaPath: string
|
|
25
26
|
storeId: string
|
|
26
27
|
clientId: string
|
|
27
28
|
sessionId: string
|
|
29
|
+
host: string
|
|
28
30
|
port: number
|
|
29
31
|
}): Effect.Effect<void, UnexpectedError, Scope.Scope> =>
|
|
30
32
|
Effect.gen(function* () {
|
|
@@ -59,7 +61,7 @@ export const startDevtoolsServer = ({
|
|
|
59
61
|
cb(UnexpectedError.make({ cause: err }))
|
|
60
62
|
})
|
|
61
63
|
|
|
62
|
-
httpServer.listen(port,
|
|
64
|
+
httpServer.listen(port, host, () => {
|
|
63
65
|
cb(Effect.succeed(undefined))
|
|
64
66
|
})
|
|
65
67
|
})
|
|
@@ -67,11 +69,12 @@ export const startDevtoolsServer = ({
|
|
|
67
69
|
yield* startServer(port)
|
|
68
70
|
|
|
69
71
|
yield* Effect.logDebug(
|
|
70
|
-
`[@livestore/adapter-node:devtools] LiveStore devtools are available at http
|
|
72
|
+
`[@livestore/adapter-node:devtools] LiveStore devtools are available at http://${host}:${port}/_livestore/node/${storeId}/${clientId}/${sessionId}`,
|
|
71
73
|
)
|
|
72
74
|
|
|
75
|
+
const clientSessionInfo = { storeId, clientId, sessionId }
|
|
73
76
|
const viteServer = yield* makeViteServer({
|
|
74
|
-
mode: { _tag: 'node',
|
|
77
|
+
mode: { _tag: 'node', clientSessionInfo, url: `ws://localhost:${port}` },
|
|
75
78
|
schemaPath: path.resolve(process.cwd(), schemaPath),
|
|
76
79
|
viteConfig: (viteConfig) => {
|
|
77
80
|
if (LS_DEV) {
|
|
@@ -91,7 +94,7 @@ export const startDevtoolsServer = ({
|
|
|
91
94
|
|
|
92
95
|
httpServer.on('request', (req, res) => {
|
|
93
96
|
if (req.url === '/' || req.url === '') {
|
|
94
|
-
res.writeHead(302, { Location: '/_livestore' })
|
|
97
|
+
res.writeHead(302, { Location: '/_livestore/node' })
|
|
95
98
|
res.end()
|
|
96
99
|
} else if (req.url?.startsWith('/_livestore')) {
|
|
97
100
|
return viteServer.middlewares(req, res as any)
|
package/src/in-memory/index.ts
CHANGED
|
@@ -25,11 +25,15 @@ export interface InMemoryAdapterOptions {
|
|
|
25
25
|
* @default 'in-memory'
|
|
26
26
|
*/
|
|
27
27
|
clientId?: string
|
|
28
|
+
/**
|
|
29
|
+
* @default nanoid(6)
|
|
30
|
+
*/
|
|
31
|
+
sessionId?: string
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/** NOTE: This adapter is currently only used for testing */
|
|
31
35
|
export const makeInMemoryAdapter =
|
|
32
|
-
({ sync: syncOptions, clientId = 'in-memory' }: InMemoryAdapterOptions): Adapter =>
|
|
36
|
+
({ sync: syncOptions, clientId = 'in-memory', sessionId = nanoid(6) }: InMemoryAdapterOptions): Adapter =>
|
|
33
37
|
({
|
|
34
38
|
schema,
|
|
35
39
|
storeId,
|
|
@@ -42,9 +46,7 @@ export const makeInMemoryAdapter =
|
|
|
42
46
|
const makeSqliteDb = sqliteDbFactory({ sqlite3 })
|
|
43
47
|
const sqliteDb = yield* makeSqliteDb({ _tag: 'in-memory' })
|
|
44
48
|
|
|
45
|
-
const lockStatus = SubscriptionRef.make<LockStatus>('has-lock')
|
|
46
|
-
|
|
47
|
-
const sessionId = nanoid(6)
|
|
49
|
+
const lockStatus = yield* SubscriptionRef.make<LockStatus>('has-lock')
|
|
48
50
|
|
|
49
51
|
const shutdownChannel = yield* makeShutdownChannel(storeId)
|
|
50
52
|
|
|
@@ -207,6 +207,7 @@ const makeLeaderThread = ({
|
|
|
207
207
|
const devtoolsOptions = yield* makeDevtoolsOptions({
|
|
208
208
|
devtoolsEnabled: devtools.enabled,
|
|
209
209
|
devtoolsPort: devtools.port,
|
|
210
|
+
devtoolsHost: devtools.host,
|
|
210
211
|
dbReadModel,
|
|
211
212
|
dbMutationLog,
|
|
212
213
|
storeId,
|
|
@@ -243,6 +244,7 @@ const makeDevtoolsOptions = ({
|
|
|
243
244
|
storeId,
|
|
244
245
|
clientId,
|
|
245
246
|
devtoolsPort,
|
|
247
|
+
devtoolsHost,
|
|
246
248
|
schemaPath,
|
|
247
249
|
}: {
|
|
248
250
|
devtoolsEnabled: boolean
|
|
@@ -251,6 +253,7 @@ const makeDevtoolsOptions = ({
|
|
|
251
253
|
storeId: string
|
|
252
254
|
clientId: string
|
|
253
255
|
devtoolsPort: number
|
|
256
|
+
devtoolsHost: string
|
|
254
257
|
schemaPath: string
|
|
255
258
|
}): Effect.Effect<DevtoolsOptions, UnexpectedError, Scope.Scope> =>
|
|
256
259
|
Effect.gen(function* () {
|
|
@@ -270,18 +273,18 @@ const makeDevtoolsOptions = ({
|
|
|
270
273
|
clientId,
|
|
271
274
|
sessionId: 'static', // TODO make this dynamic
|
|
272
275
|
port: devtoolsPort,
|
|
276
|
+
host: devtoolsHost,
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
const devtoolsWebChannel = yield* makeNodeDevtoolsChannel({
|
|
280
|
+
nodeName: `leader-${storeId}-${clientId}`,
|
|
281
|
+
target: `devtools-${storeId}-${clientId}-static`,
|
|
282
|
+
url: `ws://localhost:${devtoolsPort}`,
|
|
283
|
+
schema: { listen: Devtools.Leader.MessageToApp, send: Devtools.Leader.MessageFromApp },
|
|
273
284
|
})
|
|
274
285
|
|
|
275
286
|
return {
|
|
276
|
-
devtoolsWebChannel
|
|
277
|
-
nodeName: `leader-${storeId}-${clientId}`,
|
|
278
|
-
target: `devtools`,
|
|
279
|
-
url: `ws://localhost:${devtoolsPort}`,
|
|
280
|
-
schema: {
|
|
281
|
-
listen: Devtools.Leader.MessageToApp,
|
|
282
|
-
send: Devtools.Leader.MessageFromApp,
|
|
283
|
-
},
|
|
284
|
-
}),
|
|
287
|
+
devtoolsWebChannel,
|
|
285
288
|
persistenceInfo: {
|
|
286
289
|
readModel: dbReadModel.metadata.persistenceInfo,
|
|
287
290
|
mutationLog: dbMutationLog.metadata.persistenceInfo,
|
package/src/worker-schema.ts
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Effect, Queue, Stream } from '@livestore/utils/effect'
|
|
2
|
+
import { PlatformNode } from '@livestore/utils/node'
|
|
3
|
+
|
|
4
|
+
const main = Effect.gen(function* () {
|
|
5
|
+
const queue = yield* Queue.unbounded<number>()
|
|
6
|
+
|
|
7
|
+
yield* Queue.shutdown(queue)
|
|
8
|
+
|
|
9
|
+
// yield* Effect.gen(function* () {
|
|
10
|
+
// yield* Queue.offer(queue, 1)
|
|
11
|
+
// yield* Queue.shutdown(queue)
|
|
12
|
+
// }).pipe(Effect.delay(200), Effect.forkScoped)
|
|
13
|
+
|
|
14
|
+
yield* Effect.addFinalizer((exit) => Effect.log('finalizer', exit))
|
|
15
|
+
|
|
16
|
+
// const exit = yield* Stream.fromQueue(queue).pipe(
|
|
17
|
+
// Stream.tap((n) => Effect.log(n)),
|
|
18
|
+
// Stream.runDrain,
|
|
19
|
+
// Effect.exit,
|
|
20
|
+
// )
|
|
21
|
+
|
|
22
|
+
const exit = yield* Effect.andThen(Effect.void, () => Stream.fromQueue(queue)).pipe(
|
|
23
|
+
Stream.unwrap,
|
|
24
|
+
Stream.tap((n) => Effect.log(n)),
|
|
25
|
+
Stream.runDrain,
|
|
26
|
+
Effect.exit,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
console.log('exit', exit)
|
|
30
|
+
}).pipe(Effect.scoped, PlatformNode.NodeRuntime.runMain)
|