@livestore/adapter-node 0.0.0-snapshot-76bdaead452769e22fce7f5ec10482474416e04d → 0.0.0-snapshot-5349579816dd765d89c87e6aa7b82030b1c019c1

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.
@@ -1,13 +1,22 @@
1
1
  import http from 'node:http'
2
2
  import path from 'node:path'
3
3
 
4
- import { UnexpectedError } from '@livestore/common'
5
4
  import { LS_DEV } from '@livestore/utils'
6
- import type { HttpClient, Scope } from '@livestore/utils/effect'
7
- import { Effect } from '@livestore/utils/effect'
8
- import { makeWebSocketServer } from '@livestore/webmesh/websocket-server'
5
+ import {
6
+ Deferred,
7
+ Effect,
8
+ Exit,
9
+ Headers,
10
+ HttpMiddleware,
11
+ HttpServer,
12
+ HttpServerRequest,
13
+ HttpServerResponse,
14
+ Layer,
15
+ } from '@livestore/utils/effect'
16
+ import { PlatformNode } from '@livestore/utils/node'
17
+ import { makeMeshNode, makeWebSocketEdge } from '@livestore/webmesh'
9
18
 
10
- import { makeViteServer } from './vite-dev-server.js'
19
+ import { makeViteMiddleware } from './vite-dev-server.js'
11
20
 
12
21
  /**
13
22
  * Starts a devtools HTTP/WS server which serves ...
@@ -28,52 +37,10 @@ export const startDevtoolsServer = ({
28
37
  sessionId: string
29
38
  host: string
30
39
  port: number
31
- }): Effect.Effect<void, UnexpectedError, Scope.Scope | HttpClient.HttpClient> =>
40
+ }) =>
32
41
  Effect.gen(function* () {
33
- const httpServer = yield* Effect.sync(() => http.createServer()).pipe(
34
- Effect.acquireRelease((httpServer) =>
35
- Effect.async<void, UnexpectedError>((cb) => {
36
- httpServer.removeAllListeners()
37
- httpServer.closeAllConnections()
38
- httpServer.close((err) => {
39
- if (err) {
40
- cb(Effect.fail(UnexpectedError.make({ cause: err })))
41
- } else {
42
- cb(Effect.succeed(undefined))
43
- }
44
- })
45
- }).pipe(Effect.orDie),
46
- ),
47
- )
48
-
49
- const webSocketServer = yield* makeWebSocketServer({ relayNodeName: 'ws' })
50
-
51
- // Handle upgrade manually
52
- httpServer.on('upgrade', (request, socket, head) => {
53
- webSocketServer.handleUpgrade(request, socket, head, (ws) => {
54
- webSocketServer.emit('connection', ws, request)
55
- })
56
- })
57
-
58
- const startServer = (port: number) =>
59
- Effect.async<void, UnexpectedError>((cb) => {
60
- httpServer.on('error', (err: any) => {
61
- cb(UnexpectedError.make({ cause: err }))
62
- })
63
-
64
- httpServer.listen(port, host, () => {
65
- cb(Effect.succeed(undefined))
66
- })
67
- })
68
-
69
- yield* startServer(port)
70
-
71
- yield* Effect.logDebug(
72
- `[@livestore/adapter-node:devtools] LiveStore devtools are available at http://${host}:${port}/_livestore/node/${storeId}/${clientId}/${sessionId}`,
73
- )
74
-
75
42
  const clientSessionInfo = { storeId, clientId, sessionId }
76
- const viteServer = yield* makeViteServer({
43
+ const viteMiddleware = yield* makeViteMiddleware({
77
44
  mode: { _tag: 'node', clientSessionInfo, url: `ws://localhost:${port}` },
78
45
  schemaPath: path.resolve(process.cwd(), schemaPath),
79
46
  viteConfig: (viteConfig) => {
@@ -88,16 +55,68 @@ export const startDevtoolsServer = ({
88
55
 
89
56
  return viteConfig
90
57
  },
91
- })
58
+ }).pipe(Effect.acquireRelease((viteMiddleware) => Effect.promise(() => viteMiddleware.close())))
59
+
60
+ const relayNodeName = 'ws'
61
+
62
+ const node = yield* makeMeshNode(relayNodeName)
63
+
64
+ const handler = Effect.gen(function* () {
65
+ const req = yield* HttpServerRequest.HttpServerRequest
92
66
 
93
- yield* Effect.addFinalizer(() => Effect.promise(() => viteServer.close()))
67
+ if (Headers.has(req.headers, 'upgrade')) {
68
+ yield* Effect.log(`WS Relay ${relayNodeName}: request ${req.url}`)
94
69
 
95
- httpServer.on('request', (req, res) => {
96
- if (req.url === '/' || req.url === '') {
97
- res.writeHead(302, { Location: '/_livestore/node' })
98
- res.end()
99
- } else if (req.url?.startsWith('/_livestore')) {
100
- return viteServer.middlewares(req, res as any)
70
+ const socket = yield* req.upgrade
71
+ const { webChannel, from } = yield* makeWebSocketEdge({ socket, socketType: { _tag: 'relay' } })
72
+
73
+ yield* node.addEdge({ target: from, edgeChannel: webChannel, replaceIfExists: true })
74
+ if (LS_DEV) {
75
+ yield* Effect.log(`WS Relay ${relayNodeName}: added edge from '${from}'`)
76
+ }
77
+
78
+ yield* Effect.addFinalizer(
79
+ Effect.fn(function* () {
80
+ if (LS_DEV) {
81
+ yield* Effect.log(`WS Relay ${relayNodeName}: removed edge from '${from}'`)
82
+ }
83
+ yield* node.removeEdge(from).pipe(Effect.orDie)
84
+ }),
85
+ )
86
+
87
+ yield* Effect.never
88
+
89
+ return HttpServerResponse.empty({ status: 101 })
90
+ } else {
91
+ if (req.url === '/' || req.url === '') {
92
+ return HttpServerResponse.redirect('/_livestore/node')
93
+ } else if (req.url.startsWith('/_livestore')) {
94
+ // Here we're delegating to the Vite middleware
95
+
96
+ // TODO replace this once @effect/platform-node supports Node HTTP middlewares
97
+ const nodeReq = PlatformNode.NodeHttpServerRequest.toIncomingMessage(req)
98
+ const nodeRes = PlatformNode.NodeHttpServerRequest.toServerResponse(req)
99
+ const deferred = yield* Deferred.make()
100
+ viteMiddleware.middlewares(nodeReq, nodeRes, () => Deferred.unsafeDone(deferred, Exit.void))
101
+ yield* deferred
102
+
103
+ // The response is already sent, so we need to return an empty response (which won't be sent)
104
+ return HttpServerResponse.empty()
105
+ }
101
106
  }
107
+
108
+ return HttpServerResponse.text('Not found')
102
109
  })
103
- }).pipe(Effect.withSpan('@livestore/adapter-node:devtools:startDevtoolsServer'))
110
+
111
+ yield* Effect.logDebug(
112
+ `[@livestore/adapter-node:devtools] LiveStore devtools are available at http://${host}:${port}/_livestore/node/${storeId}/${clientId}/${sessionId}`,
113
+ )
114
+
115
+ return HttpServer.serve(handler, HttpMiddleware.logger)
116
+ }).pipe(
117
+ Layer.unwrapScoped,
118
+ // HttpServer.withLogAddress,
119
+ Layer.provide(PlatformNode.NodeHttpServer.layer(() => http.createServer(), { port, host })),
120
+ Layer.launch,
121
+ Effect.orDie,
122
+ )
@@ -1,2 +1,2 @@
1
- export { makeViteServer } from './vite-dev-server.js'
1
+ export { makeViteMiddleware } from './vite-dev-server.js'
2
2
  export type { ViteDevtoolsOptions } from './vite-dev-server.js'
@@ -28,7 +28,7 @@ export type ViteDevtoolsOptions = {
28
28
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
29
29
 
30
30
  // NOTE this is currently also used in @livestore/devtools-expo
31
- export const makeViteServer = (options: ViteDevtoolsOptions): Effect.Effect<Vite.ViteDevServer, UnexpectedError> =>
31
+ export const makeViteMiddleware = (options: ViteDevtoolsOptions): Effect.Effect<Vite.ViteDevServer, UnexpectedError> =>
32
32
  Effect.gen(function* () {
33
33
  const hmrPort = yield* getFreePort
34
34
 
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { makeInMemoryAdapter } from './in-memory/index.js'
2
- export { makeNodeAdapter } from './client-session/index.js'
2
+ export { makePersistedAdapter } from './client-session/index.js'
@@ -276,7 +276,7 @@ const makeDevtoolsOptions = ({
276
276
  sessionId: 'static', // TODO make this dynamic
277
277
  port: devtoolsPort,
278
278
  host: devtoolsHost,
279
- })
279
+ }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
280
280
 
281
281
  const devtoolsWebChannel = yield* makeNodeDevtoolsChannel({
282
282
  nodeName: `leader-${storeId}-${clientId}`,