@livestore/common-cf 0.4.0-dev.9 โ†’ 0.5.0-dev.0

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.
Files changed (58) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/cf-types.js +2 -0
  3. package/dist/cf-types.js.map +1 -1
  4. package/dist/declare/cf-declare.d.ts +32 -5
  5. package/dist/declare/cf-declare.d.ts.map +1 -1
  6. package/dist/declare/cf-declare.js +9 -10
  7. package/dist/declare/cf-declare.js.map +1 -1
  8. package/dist/do-rpc/client.d.ts +1 -1
  9. package/dist/do-rpc/client.d.ts.map +1 -1
  10. package/dist/do-rpc/client.js +23 -46
  11. package/dist/do-rpc/client.js.map +1 -1
  12. package/dist/do-rpc/client.test.d.ts +2 -0
  13. package/dist/do-rpc/client.test.d.ts.map +1 -0
  14. package/dist/do-rpc/client.test.js +99 -0
  15. package/dist/do-rpc/client.test.js.map +1 -0
  16. package/dist/do-rpc/rpc.test.js +27 -59
  17. package/dist/do-rpc/rpc.test.js.map +1 -1
  18. package/dist/do-rpc/server.d.ts +10 -5
  19. package/dist/do-rpc/server.d.ts.map +1 -1
  20. package/dist/do-rpc/server.js +104 -54
  21. package/dist/do-rpc/server.js.map +1 -1
  22. package/dist/do-rpc/test-fixtures/rpc-schema.d.ts +27 -27
  23. package/dist/do-rpc/test-fixtures/rpc-schema.js +11 -11
  24. package/dist/do-rpc/test-fixtures/worker.d.ts.map +1 -1
  25. package/dist/do-rpc/test-fixtures/worker.js +10 -10
  26. package/dist/do-rpc/test-fixtures/worker.js.map +1 -1
  27. package/dist/ws-rpc/test-fixtures/rpc-schema.d.ts +27 -27
  28. package/dist/ws-rpc/test-fixtures/rpc-schema.js +9 -9
  29. package/dist/ws-rpc/test-fixtures/worker.d.ts.map +1 -1
  30. package/dist/ws-rpc/test-fixtures/worker.js +2 -2
  31. package/dist/ws-rpc/test-fixtures/worker.js.map +1 -1
  32. package/dist/ws-rpc/ws-rpc-server.d.ts +47 -6
  33. package/dist/ws-rpc/ws-rpc-server.d.ts.map +1 -1
  34. package/dist/ws-rpc/ws-rpc-server.js +30 -20
  35. package/dist/ws-rpc/ws-rpc-server.js.map +1 -1
  36. package/dist/ws-rpc/ws-rpc.test.js +29 -61
  37. package/dist/ws-rpc/ws-rpc.test.js.map +1 -1
  38. package/package.json +40 -16
  39. package/src/cf-types.ts +2 -0
  40. package/src/declare/cf-declare.ts +18 -10
  41. package/src/do-rpc/README.md +1 -2
  42. package/src/do-rpc/client.test.ts +122 -0
  43. package/src/do-rpc/client.ts +30 -50
  44. package/src/do-rpc/rpc.test.ts +33 -61
  45. package/src/do-rpc/server.ts +157 -80
  46. package/src/do-rpc/test-fixtures/rpc-schema.ts +11 -11
  47. package/src/do-rpc/test-fixtures/worker.ts +13 -14
  48. package/src/ws-rpc/README.md +1 -1
  49. package/src/ws-rpc/test-fixtures/rpc-schema.ts +9 -9
  50. package/src/ws-rpc/test-fixtures/worker.ts +6 -4
  51. package/src/ws-rpc/ws-rpc-server.ts +69 -34
  52. package/src/ws-rpc/ws-rpc.test.ts +35 -63
  53. package/src/do-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite +0 -0
  54. package/src/do-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite-shm +0 -0
  55. package/src/do-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite-wal +0 -0
  56. package/src/ws-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite +0 -0
  57. package/src/ws-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite-shm +0 -0
  58. package/src/ws-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite-wal +0 -0
@@ -17,21 +17,30 @@
17
17
 
18
18
  import { notYetImplemented, omitUndefineds } from '@livestore/utils'
19
19
  import {
20
- constVoid,
20
+ Context,
21
21
  Effect,
22
+ Function,
22
23
  Exit,
23
24
  Layer,
24
25
  Logger,
25
- LogLevel,
26
- Mailbox,
26
+ Queue,
27
+ References,
27
28
  RpcMessage,
28
29
  RpcSerialization,
29
30
  RpcServer,
30
31
  Scope,
31
32
  Stream,
32
33
  } from '@livestore/utils/effect'
34
+
33
35
  import type * as CfTypes from '../cf-types.ts'
34
36
 
37
+ /**
38
+ * Context service providing access to the current WebSocket.
39
+ * This is useful for reading WebSocket attachment data (e.g., forwarded headers)
40
+ * inside RPC handlers.
41
+ */
42
+ export class WsContext extends Context.Service<WsContext, { readonly ws: CfTypes.WebSocket }>()('WsContext') {}
43
+
35
44
  /**
36
45
  * Configuration options for setting up WebSocket RPC on a Durable Object.
37
46
  */
@@ -44,11 +53,25 @@ export interface DurableObjectWebSocketRpcConfig {
44
53
  * - 'accept': Use traditional WebSocket handling (not yet implemented)
45
54
  */
46
55
  webSocketMode: 'hibernate' | 'accept'
47
- /** Effect RPC layer that defines the available RPC methods and handlers */
48
- rpcLayer: Layer.Layer<never, never, RpcServer.Protocol>
56
+ /**
57
+ * Effect RPC layer that requires `RpcServer.Protocol` (and `WsContext` if used)
58
+ * and provides the RPC server services.
59
+ *
60
+ * This is typically created by:
61
+ * ```typescript
62
+ * RpcServer.layer(MyRpcs).pipe(Layer.provide(handlersLayer))
63
+ * ```
64
+ *
65
+ * `WsContext` is provided by the WebSocket protocol layer, so handlers can access
66
+ * WebSocket attachment data (e.g., forwarded headers stored after WebSocket upgrade).
67
+ *
68
+ * The layer requirements (`RIn`) must be a subset of `RpcServer.Protocol | WsContext`,
69
+ * which are both provided by the WebSocket protocol layer.
70
+ */
71
+ rpcLayer: Layer.Layer<never, never, RpcServer.Protocol | WsContext>
49
72
  /** Function to get access to incoming requests */
50
73
  onMessage?: (msg: RpcMessage.FromClientEncoded, ws: CfTypes.WebSocket) => void
51
- mainLayer?: Layer.Layer<never, never, never>
74
+ mainLayer?: Layer.Layer<never>
52
75
  }
53
76
 
54
77
  /**
@@ -125,14 +148,14 @@ export const setupDurableObjectWebSocketRpc = ({
125
148
  const serverCtxMap = new Map<
126
149
  CfTypes.WebSocket,
127
150
  {
128
- scope: Scope.CloseableScope
151
+ scope: Scope.Closeable
129
152
  onMessage: (message: string | ArrayBuffer) => Promise<void>
130
153
  }
131
154
  >()
132
155
 
133
156
  const launchServer = (ws: CfTypes.WebSocket) =>
134
157
  Effect.gen(function* () {
135
- if (serverCtxMap.has(ws)) {
158
+ if (serverCtxMap.has(ws) === true) {
136
159
  return serverCtxMap.get(ws)!
137
160
  }
138
161
 
@@ -140,12 +163,13 @@ export const setupDurableObjectWebSocketRpc = ({
140
163
 
141
164
  const scope = yield* Scope.make()
142
165
 
143
- const incomingQueue = yield* Mailbox.make<Uint8Array<ArrayBufferLike> | string>()
166
+ const incomingQueue = yield* Queue.unbounded<Uint8Array | string>()
144
167
 
145
- yield* Scope.addFinalizer(scope, incomingQueue.shutdown)
168
+ yield* Scope.addFinalizer(scope, Queue.shutdown(incomingQueue).pipe(Effect.asVoid))
146
169
 
147
170
  const ProtocolLive = layerRpcServerWebsocket({
148
171
  ws,
172
+ scope,
149
173
  incomingQueue,
150
174
  ...omitUndefineds({ onMessage }),
151
175
  }).pipe(Layer.provide(RpcSerialization.layerJson))
@@ -154,19 +178,16 @@ export const setupDurableObjectWebSocketRpc = ({
154
178
 
155
179
  yield* Layer.launch(ServerLive).pipe(Effect.tapCauseLogPretty, Effect.forkIn(scope))
156
180
 
157
- const runtime = yield* Effect.runtime()
181
+ const services = yield* Effect.context()
158
182
 
159
183
  const ctx = {
160
184
  scope,
161
185
  onMessage: (message: string | ArrayBuffer) =>
162
- incomingQueue
163
- .offer(message as Uint8Array<ArrayBufferLike> | string)
164
- .pipe(
165
- Effect.asVoid,
166
- Effect.withSpan('ws-rpc-server/onMessage', { root: true }),
167
- Effect.provide(runtime),
168
- Effect.runPromise,
169
- ),
186
+ Queue.offer(incomingQueue, message as Uint8Array | string).pipe(
187
+ Effect.asVoid,
188
+ Effect.withSpan('ws-rpc-server/onMessage', { root: true }),
189
+ Effect.runPromiseWith(services),
190
+ ),
170
191
  }
171
192
 
172
193
  serverCtxMap.set(ws, ctx)
@@ -174,8 +195,14 @@ export const setupDurableObjectWebSocketRpc = ({
174
195
  return ctx
175
196
  }).pipe(
176
197
  Effect.tapCauseLogPretty,
177
- Logger.withMinimumLogLevel(LogLevel.Debug), // Useful for debugging
178
- Effect.provide(Layer.mergeAll(Logger.consoleWithThread('ws-rpc-server'), mainLayer ?? Layer.empty)),
198
+ Effect.annotateLogs({ thread: 'ws-rpc-server' }),
199
+ Effect.provide(
200
+ Layer.mergeAll(
201
+ Logger.layer([Logger.consoleStructured]),
202
+ Layer.succeed(References.MinimumLogLevel, 'Debug'), // Useful for debugging
203
+ mainLayer ?? Layer.empty,
204
+ ),
205
+ ),
179
206
  Effect.withSpan('effect-ws-rpc-server'),
180
207
  Effect.runPromise,
181
208
  )
@@ -190,7 +217,7 @@ export const setupDurableObjectWebSocketRpc = ({
190
217
  const webSocketClose: CfTypes.DurableObject['webSocketClose'] = async (ws, _code, _reason, _wasClean) => {
191
218
  const ctx = serverCtxMap.get(ws)
192
219
  // console.log('webSocketClose', ctx, ws)
193
- if (ctx) {
220
+ if (ctx !== undefined) {
194
221
  await Scope.close(ctx.scope, Exit.void).pipe(Effect.runPromise)
195
222
  serverCtxMap.delete(ws)
196
223
  }
@@ -210,9 +237,10 @@ export const setupDurableObjectWebSocketRpc = ({
210
237
  */
211
238
  export interface WsRpcServerArgs {
212
239
  ws: CfTypes.WebSocket
240
+ scope: Scope.Scope
213
241
  onMessage?: (message: RpcMessage.FromClientEncoded, ws: CfTypes.WebSocket) => void
214
- /** Mailbox queue for receiving incoming messages from the WebSocket */
215
- incomingQueue: Mailbox.Mailbox<Uint8Array<ArrayBufferLike> | string>
242
+ /** Queue for receiving incoming messages from the WebSocket */
243
+ incomingQueue: Queue.Queue<Uint8Array | string>
216
244
  }
217
245
 
218
246
  /**
@@ -221,13 +249,19 @@ export interface WsRpcServerArgs {
221
249
  * This layer handles the low-level WebSocket protocol details for RPC communication,
222
250
  * including message serialization, routing, and error handling.
223
251
  *
252
+ * Also provides `WsContext` with the current WebSocket so handlers can access
253
+ * WebSocket attachment data (e.g., forwarded headers).
254
+ *
224
255
  * @param args Configuration for WebSocket RPC protocol
225
- * @returns Effect layer that provides RPC server protocol functionality
256
+ * @returns Effect layer that provides RPC server protocol functionality and WsContext
226
257
  *
227
258
  * @internal This is typically used internally by `setupDurableObjectWebSocketRpc`
228
259
  */
229
260
  export const layerRpcServerWebsocket = (args: WsRpcServerArgs) =>
230
- Layer.scoped(RpcServer.Protocol, makeSocketProtocol(args))
261
+ Layer.mergeAll(
262
+ Layer.effect(RpcServer.Protocol, makeSocketProtocol(args)),
263
+ Layer.succeed(WsContext, WsContext.of({ ws: args.ws })),
264
+ )
231
265
 
232
266
  /**
233
267
  * Creates the low-level RPC protocol implementation for WebSocket communication.
@@ -240,16 +274,16 @@ export const layerRpcServerWebsocket = (args: WsRpcServerArgs) =>
240
274
  *
241
275
  * @internal Used internally by `layerRpcServerWebsocket`
242
276
  */
243
- const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =>
277
+ const makeSocketProtocol = ({ incomingQueue, scope, ws, onMessage }: WsRpcServerArgs) =>
244
278
  Effect.gen(function* () {
245
279
  const serialization = yield* RpcSerialization.RpcSerialization
246
- const disconnects = yield* Mailbox.make<number>()
280
+ const disconnects = yield* Queue.unbounded<number>()
247
281
 
248
- const writeRaw = (msg: Uint8Array<ArrayBufferLike> | string) => Effect.succeed(ws.send(msg))
282
+ const writeRaw = (msg: Uint8Array | string) => Effect.succeed(ws.send(msg))
249
283
 
250
284
  let writeRequest!: (clientId: number, message: RpcMessage.FromClientEncoded) => Effect.Effect<void>
251
285
 
252
- const parser = serialization.unsafeMake()
286
+ const parser = serialization.makeUnsafe()
253
287
  const id = 0
254
288
 
255
289
  const write = (response: RpcMessage.FromServerEncoded) => {
@@ -268,7 +302,7 @@ const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =
268
302
  writeRequest = writeRequest_
269
303
 
270
304
  // Start processing messages now that writeRequest is available
271
- const startProcessing = Mailbox.toStream(incomingQueue).pipe(
305
+ const startProcessing = Stream.fromQueue(incomingQueue).pipe(
272
306
  Stream.tap((data) => {
273
307
  try {
274
308
  const decoded = parser.decode(data) as ReadonlyArray<RpcMessage.FromClientEncoded>
@@ -278,12 +312,12 @@ const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =
278
312
  while: () => i < decoded.length,
279
313
  body: () => {
280
314
  const request = decoded[i++]!
281
- if (onMessage) {
315
+ if (onMessage !== undefined) {
282
316
  onMessage(request, ws)
283
317
  }
284
318
  return writeRequest(id, request)
285
319
  },
286
- step: constVoid,
320
+ step: Function.constVoid,
287
321
  })
288
322
  } catch (cause) {
289
323
  return Effect.orDie(writeRaw(parser.encode(RpcMessage.ResponseDefectEncoded(cause))!))
@@ -291,7 +325,7 @@ const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =
291
325
  }),
292
326
  Stream.runDrain,
293
327
  Effect.tapCauseLogPretty,
294
- Effect.fork,
328
+ Effect.forkIn(scope),
295
329
  )
296
330
 
297
331
  // Start the message processing
@@ -307,6 +341,7 @@ const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =
307
341
  supportsAck: true,
308
342
  supportsTransferables: false,
309
343
  supportsSpanPropagation: true,
344
+ supportsNotifications: true,
310
345
  }))
311
346
  })
312
347
 
@@ -1,20 +1,20 @@
1
+ import { expect } from 'vitest'
2
+
3
+ import { Vitest } from '@livestore/utils-dev/node-vitest'
4
+ import { WranglerDevServer } from '@livestore/utils-dev/wrangler'
1
5
  import {
2
- Chunk,
3
6
  Effect,
4
7
  FetchHttpClient,
5
8
  Layer,
6
- Logger,
7
- LogLevel,
8
9
  Option,
10
+ References,
9
11
  RpcClient,
10
12
  RpcSerialization,
11
13
  Socket,
12
14
  Stream,
13
15
  } from '@livestore/utils/effect'
14
16
  import { PlatformNode } from '@livestore/utils/node'
15
- import { Vitest } from '@livestore/utils-dev/node-vitest'
16
- import { WranglerDevServerService } from '@livestore/utils-dev/wrangler'
17
- import { expect } from 'vitest'
17
+
18
18
  import { TestRpcs } from './test-fixtures/rpc-schema.ts'
19
19
 
20
20
  const testTimeout = 60_000
@@ -22,29 +22,33 @@ const testTimeout = 60_000
22
22
  const withWranglerTest = Vitest.makeWithTestCtx({
23
23
  timeout: testTimeout,
24
24
  makeLayer: () =>
25
- WranglerDevServerService.Default({
25
+ WranglerDevServer.layer({
26
26
  cwd: `${import.meta.dirname}/test-fixtures`,
27
27
  }).pipe(
28
28
  Layer.provide(
29
- Layer.mergeAll(PlatformNode.NodeContext.layer, FetchHttpClient.layer, Logger.minimumLogLevel(LogLevel.Debug)),
29
+ Layer.mergeAll(
30
+ PlatformNode.NodeServices.layer,
31
+ FetchHttpClient.layer,
32
+ Layer.succeed(References.MinimumLogLevel, 'Debug'),
33
+ ),
30
34
  ),
31
35
  ),
32
36
  })
33
37
 
34
38
  const ProtocolLive = Layer.suspend(() =>
35
39
  Effect.gen(function* () {
36
- const server = yield* WranglerDevServerService
40
+ const server = yield* WranglerDevServer.WranglerDevServer
37
41
  return RpcClient.layerProtocolSocket().pipe(
38
42
  Layer.provide(Socket.layerWebSocket(`ws://localhost:${server.port}`)),
39
43
  Layer.provide(Socket.layerWebSocketConstructorGlobal),
40
44
  Layer.provide(RpcSerialization.layerJson),
41
45
  )
42
- }).pipe(Layer.unwrapEffect),
46
+ }).pipe(Layer.unwrap),
43
47
  )
44
48
 
45
49
  Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () => {
46
50
  // Direct HTTP RPC client tests
47
- Vitest.scopedLive('should call ping method', (test) =>
51
+ Vitest.live('should call ping method', (test) =>
48
52
  Effect.gen(function* () {
49
53
  const client = yield* RpcClient.make(TestRpcs)
50
54
  const result = yield* client.Ping({ message: 'Hello HTTP RPC' })
@@ -52,7 +56,7 @@ Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () =>
52
56
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
53
57
  )
54
58
 
55
- Vitest.scopedLive('should call echo method', (test) =>
59
+ Vitest.live('should call echo method', (test) =>
56
60
  Effect.gen(function* () {
57
61
  const client = yield* RpcClient.make(TestRpcs)
58
62
  const result = yield* client.Echo({ text: 'Echo' })
@@ -60,7 +64,7 @@ Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () =>
60
64
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
61
65
  )
62
66
 
63
- Vitest.scopedLive('should call add method', (test) =>
67
+ Vitest.live('should call add method', (test) =>
64
68
  Effect.gen(function* () {
65
69
  const client = yield* RpcClient.make(TestRpcs)
66
70
  const result = yield* client.Add({ a: 15, b: 25 })
@@ -68,104 +72,72 @@ Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () =>
68
72
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
69
73
  )
70
74
 
71
- Vitest.scopedLive('should handle RPC fail method', (test) =>
75
+ Vitest.live('should handle RPC fail method', (test) =>
72
76
  Effect.gen(function* () {
73
77
  const client = yield* RpcClient.make(TestRpcs)
74
78
  const error = yield* client.Fail({ message: 'test http failure' }).pipe(Effect.exit)
75
79
  expect(error.toString()).toMatchInlineSnapshot(`
76
- "{
77
- "_id": "Exit",
78
- "_tag": "Failure",
79
- "cause": {
80
- "_id": "Cause",
81
- "_tag": "Fail",
82
- "failure": "RPC failure: test http failure"
83
- }
84
- }"
80
+ "Failure(Cause([Fail("RPC failure: test http failure")]))"
85
81
  `)
86
82
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
87
83
  )
88
84
 
89
- Vitest.scopedLive('should handle defect method', (test) =>
85
+ Vitest.live('should handle defect method', (test) =>
90
86
  Effect.gen(function* () {
91
87
  const client = yield* RpcClient.make(TestRpcs)
92
88
  const error = yield* client.Defect({ message: 'test http defect' }).pipe(Effect.exit)
93
89
  expect(error.toString()).toMatchInlineSnapshot(`
94
- "{
95
- "_id": "Exit",
96
- "_tag": "Failure",
97
- "cause": {
98
- "_id": "Cause",
99
- "_tag": "Die",
100
- "defect": "some defect: test http defect"
101
- }
102
- }"
90
+ "Failure(Cause([Die("some defect: test http defect")]))"
103
91
  `)
104
92
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
105
93
  )
106
94
 
107
- Vitest.scopedLive('should handle streaming RPC via HTTP', (test) =>
95
+ Vitest.live('should handle streaming RPC via HTTP', (test) =>
108
96
  Effect.gen(function* () {
109
97
  const client = yield* RpcClient.make(TestRpcs)
110
98
  const stream = client.Stream({}).pipe(
111
99
  Stream.take(4),
112
100
  Stream.map((c) => c.maybeNumber.pipe(Option.getOrUndefined)),
113
101
  )
114
- const chunks = yield* Stream.runCollect(stream)
115
- expect(Chunk.toReadonlyArray(chunks)).toEqual([1, 4, 9, 16]) // squares of 1,2,3,4
102
+ const result = yield* Stream.runCollect(stream)
103
+ expect(result).toEqual([1, 4, 9, 16]) // squares of 1,2,3,4
116
104
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
117
105
  )
118
106
 
119
- Vitest.scopedLive('should handle streaming RPC with error via HTTP', (test) =>
107
+ Vitest.live('should handle streaming RPC with error via HTTP', (test) =>
120
108
  Effect.gen(function* () {
121
109
  const client = yield* RpcClient.make(TestRpcs)
122
110
  const stream = client.StreamError({ count: 5, errorAfter: 4 })
123
111
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
124
112
  expect(error.toString()).toMatchInlineSnapshot(`
125
- "{
126
- "_id": "Exit",
127
- "_tag": "Failure",
128
- "cause": {
129
- "_id": "Cause",
130
- "_tag": "Fail",
131
- "failure": "Stream error after 4: got 9"
132
- }
133
- }"
113
+ "Failure(Cause([Fail("Stream error after 4: got 9")]))"
134
114
  `)
135
115
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
136
116
  )
137
117
 
138
- Vitest.scopedLive('should handle streaming RPC with defect via HTTP', (test) =>
118
+ Vitest.live('should handle streaming RPC with defect via HTTP', (test) =>
139
119
  Effect.gen(function* () {
140
120
  const client = yield* RpcClient.make(TestRpcs)
141
121
  const stream = client.StreamDefect({ count: 4, defectAfter: 1 })
142
122
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
143
123
  expect(error.toString()).toMatchInlineSnapshot(`
144
- "{
145
- "_id": "Exit",
146
- "_tag": "Failure",
147
- "cause": {
148
- "_id": "Cause",
149
- "_tag": "Die",
150
- "defect": "Stream defect after 1: got 4"
151
- }
152
- }"
124
+ "Failure(Cause([Die("Stream defect after 1: got 4")]))"
153
125
  `)
154
126
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
155
127
  )
156
128
 
157
- Vitest.scopedLive('should handle stream interruption via HTTP', (test) =>
129
+ Vitest.live('should handle stream interruption via HTTP', (test) =>
158
130
  Effect.gen(function* () {
159
131
  const client = yield* RpcClient.make(TestRpcs)
160
132
  const stream = client.StreamInterruptible({ delay: 50, interruptAfterCount: 3 }).pipe(Stream.take(3))
161
- const chunks = yield* Stream.runCollect(stream)
162
- expect(Chunk.toReadonlyArray(chunks)).toEqual([1, 2, 3])
133
+ const result = yield* Stream.runCollect(stream)
134
+ expect(result).toEqual([1, 2, 3])
163
135
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
164
136
  )
165
137
  })
166
138
 
167
139
  Vitest.describe('Hibernation Tests', { timeout: 25000 }, () => {
168
- Vitest.scopedLive('should maintain RPC functionality after hibernation', (test) =>
140
+ Vitest.live('should maintain RPC functionality after hibernation', (test) =>
169
141
  Effect.gen(function* () {
170
142
  console.log('๐Ÿงช Testing RPC server persistence across hibernation...')
171
143
 
@@ -220,15 +192,15 @@ Vitest.describe('Hibernation Tests', { timeout: 25000 }, () => {
220
192
  Stream.take(3),
221
193
  Stream.map((c) => c.maybeNumber.pipe(Option.getOrUndefined)),
222
194
  )
223
- const chunks = yield* Stream.runCollect(stream)
224
- expect(Chunk.toReadonlyArray(chunks)).toEqual([1, 4, 9]) // squares of 1,2,3
195
+ const result = yield* Stream.runCollect(stream)
196
+ expect(result).toEqual([1, 4, 9]) // squares of 1,2,3
225
197
  console.log('โœ… Streaming after hibernation successful')
226
198
 
227
199
  console.log('๐ŸŽ‰ All RPC operations successful after hibernation!')
228
200
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
229
201
  )
230
202
 
231
- Vitest.scopedLive('should handle rapid operations after hibernation', (test) =>
203
+ Vitest.live('should handle rapid operations after hibernation', (test) =>
232
204
  Effect.gen(function* () {
233
205
  console.log('๐Ÿงช Testing rapid operations after hibernation...')
234
206