@livestore/adapter-node 0.3.0-dev.19 → 0.3.0-dev.22
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 +4 -0
- package/dist/client-session/index.d.ts.map +1 -1
- package/dist/client-session/index.js +50 -13
- 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/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 +69 -12
- package/src/devtools/devtools-server.ts +7 -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,
|
|
@@ -49,6 +51,10 @@ export interface NodeAdapterOptions {
|
|
|
49
51
|
* @default 4242
|
|
50
52
|
*/
|
|
51
53
|
port: number
|
|
54
|
+
/**
|
|
55
|
+
* @default 'localhost'
|
|
56
|
+
*/
|
|
57
|
+
host: string
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
|
|
@@ -59,13 +65,15 @@ export const makeNodeAdapter = ({
|
|
|
59
65
|
workerUrl,
|
|
60
66
|
schemaPath,
|
|
61
67
|
baseDirectory,
|
|
62
|
-
devtools: devtoolsOptions = { port: 4242 },
|
|
68
|
+
devtools: devtoolsOptions = { port: 4242, host: 'localhost' },
|
|
63
69
|
clientId = hostname(),
|
|
64
70
|
// TODO make this dynamic and actually support multiple sessions
|
|
65
71
|
sessionId = 'static',
|
|
66
72
|
}: NodeAdapterOptions): Adapter =>
|
|
67
|
-
(({ storeId, devtoolsEnabled, shutdown, connectDevtoolsToStore }) =>
|
|
73
|
+
(({ storeId, devtoolsEnabled, shutdown, connectDevtoolsToStore, bootStatusQueue }) =>
|
|
68
74
|
Effect.gen(function* () {
|
|
75
|
+
yield* Queue.offer(bootStatusQueue, { stage: 'loading' })
|
|
76
|
+
|
|
69
77
|
const sqlite3 = yield* Effect.promise(() => loadSqlite3Wasm())
|
|
70
78
|
const makeSqliteDb = yield* sqliteDbFactory({ sqlite3 })
|
|
71
79
|
|
|
@@ -103,20 +111,34 @@ export const makeNodeAdapter = ({
|
|
|
103
111
|
devtoolsEnabled,
|
|
104
112
|
devtoolsOptions,
|
|
105
113
|
schemaPath,
|
|
114
|
+
bootStatusQueue,
|
|
106
115
|
})
|
|
107
116
|
|
|
108
117
|
syncInMemoryDb.import(initialSnapshot)
|
|
109
118
|
|
|
110
119
|
if (devtoolsEnabled) {
|
|
111
120
|
yield* Effect.gen(function* () {
|
|
112
|
-
const
|
|
121
|
+
const webmeshNode = yield* DevtoolsNode.makeNodeDevtoolsConnectedMeshNode({
|
|
122
|
+
url: `ws://${devtoolsOptions.host}:${devtoolsOptions.port}`,
|
|
113
123
|
nodeName: `client-session-${storeId}-${clientId}-${sessionId}`,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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 },
|
|
120
142
|
})
|
|
121
143
|
|
|
122
144
|
yield* connectDevtoolsToStore(storeDevtoolsChannel)
|
|
@@ -154,6 +176,7 @@ const makeLeaderThread = ({
|
|
|
154
176
|
devtoolsEnabled,
|
|
155
177
|
devtoolsOptions,
|
|
156
178
|
schemaPath,
|
|
179
|
+
// bootStatusQueue,
|
|
157
180
|
}: {
|
|
158
181
|
shutdown: (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) => void
|
|
159
182
|
storeId: string
|
|
@@ -162,8 +185,9 @@ const makeLeaderThread = ({
|
|
|
162
185
|
workerUrl: URL
|
|
163
186
|
baseDirectory: string | undefined
|
|
164
187
|
devtoolsEnabled: boolean
|
|
165
|
-
devtoolsOptions: { port: number }
|
|
188
|
+
devtoolsOptions: { port: number; host: string }
|
|
166
189
|
schemaPath: string
|
|
190
|
+
bootStatusQueue: Queue.Queue<BootStatus>
|
|
167
191
|
}) =>
|
|
168
192
|
Effect.gen(function* () {
|
|
169
193
|
const nodeWorker = new WT.Worker(workerUrl, {
|
|
@@ -179,7 +203,7 @@ const makeLeaderThread = ({
|
|
|
179
203
|
storeId,
|
|
180
204
|
clientId,
|
|
181
205
|
baseDirectory,
|
|
182
|
-
devtools: { enabled: devtoolsEnabled, port: devtoolsOptions.port },
|
|
206
|
+
devtools: { enabled: devtoolsEnabled, port: devtoolsOptions.port, host: devtoolsOptions.host },
|
|
183
207
|
schemaPath,
|
|
184
208
|
}),
|
|
185
209
|
}).pipe(
|
|
@@ -246,6 +270,39 @@ const makeLeaderThread = ({
|
|
|
246
270
|
)
|
|
247
271
|
}).pipe(Stream.unwrap) as any
|
|
248
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
|
+
|
|
249
306
|
const initialLeaderHead = yield* runInWorker(new WorkerSchema.LeaderWorkerInner.GetLeaderHead())
|
|
250
307
|
|
|
251
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)
|
|
@@ -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)
|