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

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 (51) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/declare/cf-declare.d.ts +1 -21
  3. package/dist/declare/cf-declare.d.ts.map +1 -1
  4. package/dist/declare/mod.js +1 -1
  5. package/dist/do-rpc/client.d.ts +8 -0
  6. package/dist/do-rpc/client.d.ts.map +1 -1
  7. package/dist/do-rpc/client.js +71 -51
  8. package/dist/do-rpc/client.js.map +1 -1
  9. package/dist/do-rpc/client.test.d.ts +2 -0
  10. package/dist/do-rpc/client.test.d.ts.map +1 -0
  11. package/dist/do-rpc/client.test.js +189 -0
  12. package/dist/do-rpc/client.test.js.map +1 -0
  13. package/dist/do-rpc/mod.d.ts +1 -1
  14. package/dist/do-rpc/mod.d.ts.map +1 -1
  15. package/dist/do-rpc/mod.js +2 -2
  16. package/dist/do-rpc/mod.js.map +1 -1
  17. package/dist/do-rpc/rpc.test.js +27 -59
  18. package/dist/do-rpc/rpc.test.js.map +1 -1
  19. package/dist/do-rpc/server.d.ts +10 -4
  20. package/dist/do-rpc/server.d.ts.map +1 -1
  21. package/dist/do-rpc/server.js +107 -74
  22. package/dist/do-rpc/server.js.map +1 -1
  23. package/dist/do-rpc/test-fixtures/rpc-schema.d.ts +28 -28
  24. package/dist/do-rpc/test-fixtures/rpc-schema.js +11 -11
  25. package/dist/do-rpc/test-fixtures/worker.d.ts.map +1 -1
  26. package/dist/do-rpc/test-fixtures/worker.js +10 -10
  27. package/dist/do-rpc/test-fixtures/worker.js.map +1 -1
  28. package/dist/mod.js +3 -3
  29. package/dist/ws-rpc/test-fixtures/rpc-schema.d.ts +28 -28
  30. package/dist/ws-rpc/test-fixtures/rpc-schema.js +9 -9
  31. package/dist/ws-rpc/test-fixtures/worker.d.ts.map +1 -1
  32. package/dist/ws-rpc/test-fixtures/worker.js +2 -2
  33. package/dist/ws-rpc/ws-rpc-server.d.ts +14 -7
  34. package/dist/ws-rpc/ws-rpc-server.d.ts.map +1 -1
  35. package/dist/ws-rpc/ws-rpc-server.js +60 -28
  36. package/dist/ws-rpc/ws-rpc-server.js.map +1 -1
  37. package/dist/ws-rpc/ws-rpc.test.js +28 -60
  38. package/dist/ws-rpc/ws-rpc.test.js.map +1 -1
  39. package/package.json +29 -38
  40. package/src/declare/cf-declare.ts +1 -1
  41. package/src/do-rpc/README.md +2 -2
  42. package/src/do-rpc/client.test.ts +263 -0
  43. package/src/do-rpc/client.ts +99 -68
  44. package/src/do-rpc/mod.ts +1 -1
  45. package/src/do-rpc/rpc.test.ts +30 -60
  46. package/src/do-rpc/server.ts +168 -86
  47. package/src/do-rpc/test-fixtures/rpc-schema.ts +11 -11
  48. package/src/do-rpc/test-fixtures/worker.ts +9 -12
  49. package/src/ws-rpc/test-fixtures/rpc-schema.ts +9 -9
  50. package/src/ws-rpc/ws-rpc-server.ts +114 -39
  51. package/src/ws-rpc/ws-rpc.test.ts +31 -61
@@ -18,16 +18,17 @@
18
18
  import { notYetImplemented, omitUndefineds } from '@livestore/utils'
19
19
  import {
20
20
  Context,
21
- constVoid,
22
21
  Effect,
22
+ Function,
23
23
  Exit,
24
24
  Layer,
25
25
  Logger,
26
- LogLevel,
27
- Mailbox,
26
+ Queue,
27
+ References,
28
28
  RpcMessage,
29
29
  RpcSerialization,
30
30
  RpcServer,
31
+ Schema,
31
32
  Scope,
32
33
  Stream,
33
34
  } from '@livestore/utils/effect'
@@ -39,7 +40,7 @@ import type * as CfTypes from '../cf-types.ts'
39
40
  * This is useful for reading WebSocket attachment data (e.g., forwarded headers)
40
41
  * inside RPC handlers.
41
42
  */
42
- export class WsContext extends Context.Tag('WsContext')<WsContext, { readonly ws: CfTypes.WebSocket }>() {}
43
+ export class WsContext extends Context.Service<WsContext, { readonly ws: CfTypes.WebSocket }>()('WsContext') {}
43
44
 
44
45
  /**
45
46
  * Configuration options for setting up WebSocket RPC on a Durable Object.
@@ -55,7 +56,7 @@ export interface DurableObjectWebSocketRpcConfig {
55
56
  webSocketMode: 'hibernate' | 'accept'
56
57
  /**
57
58
  * Effect RPC layer that requires `RpcServer.Protocol` (and `WsContext` if used)
58
- * and provides the RPC server runtime.
59
+ * and provides the RPC server services.
59
60
  *
60
61
  * This is typically created by:
61
62
  * ```typescript
@@ -71,6 +72,10 @@ export interface DurableObjectWebSocketRpcConfig {
71
72
  rpcLayer: Layer.Layer<never, never, RpcServer.Protocol | WsContext>
72
73
  /** Function to get access to incoming requests */
73
74
  onMessage?: (msg: RpcMessage.FromClientEncoded, ws: CfTypes.WebSocket) => void
75
+ /** Called after a terminal RPC response has been accepted by the serializer. */
76
+ onRequestExit?: (requestId: string | number, ws: CfTypes.WebSocket) => void
77
+ /** Called after a connection-level RPC defect has been accepted by the serializer. */
78
+ onProtocolDefect?: (ws: CfTypes.WebSocket) => void
74
79
  mainLayer?: Layer.Layer<never>
75
80
  }
76
81
 
@@ -139,6 +144,8 @@ export const setupDurableObjectWebSocketRpc = ({
139
144
  rpcLayer,
140
145
  webSocketMode,
141
146
  onMessage,
147
+ onRequestExit,
148
+ onProtocolDefect,
142
149
  mainLayer,
143
150
  }: DurableObjectWebSocketRpcConfig) => {
144
151
  if (webSocketMode === 'accept') {
@@ -148,7 +155,7 @@ export const setupDurableObjectWebSocketRpc = ({
148
155
  const serverCtxMap = new Map<
149
156
  CfTypes.WebSocket,
150
157
  {
151
- scope: Scope.CloseableScope
158
+ scope: Scope.Closeable
152
159
  onMessage: (message: string | ArrayBuffer) => Promise<void>
153
160
  }
154
161
  >()
@@ -163,33 +170,31 @@ export const setupDurableObjectWebSocketRpc = ({
163
170
 
164
171
  const scope = yield* Scope.make()
165
172
 
166
- const incomingQueue = yield* Mailbox.make<Uint8Array | string>()
173
+ const incomingQueue = yield* Queue.unbounded<Uint8Array | string>()
167
174
 
168
- yield* Scope.addFinalizer(scope, incomingQueue.shutdown)
175
+ yield* Scope.addFinalizer(scope, Queue.shutdown(incomingQueue).pipe(Effect.asVoid))
169
176
 
170
177
  const ProtocolLive = layerRpcServerWebsocket({
171
178
  ws,
179
+ scope,
172
180
  incomingQueue,
173
- ...omitUndefineds({ onMessage }),
181
+ ...omitUndefineds({ onMessage, onRequestExit, onProtocolDefect }),
174
182
  }).pipe(Layer.provide(RpcSerialization.layerJson))
175
183
 
176
184
  const ServerLive = rpcLayer.pipe(Layer.provide(ProtocolLive))
177
185
 
178
186
  yield* Layer.launch(ServerLive).pipe(Effect.tapCauseLogPretty, Effect.forkIn(scope))
179
187
 
180
- const runtime = yield* Effect.runtime()
188
+ const services = yield* Effect.context()
181
189
 
182
190
  const ctx = {
183
191
  scope,
184
192
  onMessage: (message: string | ArrayBuffer) =>
185
- incomingQueue
186
- .offer(message as Uint8Array | string)
187
- .pipe(
188
- Effect.asVoid,
189
- Effect.withSpan('ws-rpc-server/onMessage', { root: true }),
190
- Effect.provide(runtime),
191
- Effect.runPromise,
192
- ),
193
+ Queue.offer(incomingQueue, message as Uint8Array | string).pipe(
194
+ Effect.asVoid,
195
+ Effect.withSpan('ws-rpc-server/onMessage', { root: true }),
196
+ Effect.runPromiseWith(services),
197
+ ),
193
198
  }
194
199
 
195
200
  serverCtxMap.set(ws, ctx)
@@ -197,8 +202,14 @@ export const setupDurableObjectWebSocketRpc = ({
197
202
  return ctx
198
203
  }).pipe(
199
204
  Effect.tapCauseLogPretty,
200
- Logger.withMinimumLogLevel(LogLevel.Debug), // Useful for debugging
201
- Effect.provide(Layer.mergeAll(Logger.consoleWithThread('ws-rpc-server'), mainLayer ?? Layer.empty)),
205
+ Effect.annotateLogs({ thread: 'ws-rpc-server' }),
206
+ Effect.provide(
207
+ Layer.mergeAll(
208
+ Logger.layer([Logger.consoleStructured]),
209
+ Layer.succeed(References.MinimumLogLevel, 'Debug'), // Useful for debugging
210
+ mainLayer ?? Layer.empty,
211
+ ),
212
+ ),
202
213
  Effect.withSpan('effect-ws-rpc-server'),
203
214
  Effect.runPromise,
204
215
  )
@@ -233,9 +244,12 @@ export const setupDurableObjectWebSocketRpc = ({
233
244
  */
234
245
  export interface WsRpcServerArgs {
235
246
  ws: CfTypes.WebSocket
247
+ scope: Scope.Scope
236
248
  onMessage?: (message: RpcMessage.FromClientEncoded, ws: CfTypes.WebSocket) => void
237
- /** Mailbox queue for receiving incoming messages from the WebSocket */
238
- incomingQueue: Mailbox.Mailbox<Uint8Array | string>
249
+ onRequestExit?: (requestId: string | number, ws: CfTypes.WebSocket) => void
250
+ onProtocolDefect?: (ws: CfTypes.WebSocket) => void
251
+ /** Queue for receiving incoming messages from the WebSocket */
252
+ incomingQueue: Queue.Queue<Uint8Array | string>
239
253
  }
240
254
 
241
255
  /**
@@ -253,7 +267,10 @@ export interface WsRpcServerArgs {
253
267
  * @internal This is typically used internally by `setupDurableObjectWebSocketRpc`
254
268
  */
255
269
  export const layerRpcServerWebsocket = (args: WsRpcServerArgs) =>
256
- Layer.mergeAll(Layer.scoped(RpcServer.Protocol, makeSocketProtocol(args)), Layer.succeed(WsContext, { ws: args.ws }))
270
+ Layer.mergeAll(
271
+ Layer.effect(RpcServer.Protocol, makeSocketProtocol(args)),
272
+ Layer.succeed(WsContext, WsContext.of({ ws: args.ws })),
273
+ )
257
274
 
258
275
  /**
259
276
  * Creates the low-level RPC protocol implementation for WebSocket communication.
@@ -266,35 +283,84 @@ export const layerRpcServerWebsocket = (args: WsRpcServerArgs) =>
266
283
  *
267
284
  * @internal Used internally by `layerRpcServerWebsocket`
268
285
  */
269
- const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =>
286
+ const makeSocketProtocol = ({
287
+ incomingQueue,
288
+ scope,
289
+ ws,
290
+ onMessage,
291
+ onRequestExit,
292
+ onProtocolDefect,
293
+ }: WsRpcServerArgs) =>
270
294
  Effect.gen(function* () {
271
295
  const serialization = yield* RpcSerialization.RpcSerialization
272
- const disconnects = yield* Mailbox.make<number>()
296
+ const disconnects = yield* Queue.unbounded<number>()
297
+ const requestIdsWithSchemas = new Set<string | number>()
298
+ const interruptedExitEncoded = yield* Schema.encodeUnknownEffect(
299
+ serialization.codecFor(Schema.Exit(Schema.Void, Schema.Never, Schema.Never)),
300
+ )(Exit.interrupt()).pipe(Effect.orDie)
273
301
 
274
- const writeRaw = (msg: Uint8Array | string) => Effect.succeed(ws.send(msg))
302
+ const writeRaw = (msg: Uint8Array | string) => Effect.sync(() => ws.send(msg))
275
303
 
276
304
  let writeRequest!: (clientId: number, message: RpcMessage.FromClientEncoded) => Effect.Effect<void>
277
305
 
278
- const parser = serialization.unsafeMake()
306
+ const parser = serialization.makeUnsafe()
279
307
  const id = 0
280
308
 
281
- const write = (response: RpcMessage.FromServerEncoded) => {
309
+ const runLifecycleHook = (name: string, hook: () => void) =>
310
+ Effect.sync(hook).pipe(
311
+ Effect.catchCause((cause) => Effect.logError(`WebSocket RPC ${name} hook failed`, { cause })),
312
+ )
313
+
314
+ const writeDefect = (defect: unknown) => {
315
+ try {
316
+ const encoded = parser.encode(RpcMessage.ResponseDefectEncoded(defect))
317
+ const responseEffect = encoded === undefined ? Effect.void : Effect.orDie(writeRaw(encoded))
318
+ const onAccepted = Effect.sync(() => requestIdsWithSchemas.clear()).pipe(
319
+ Effect.andThen(
320
+ onProtocolDefect !== undefined
321
+ ? runLifecycleHook('onProtocolDefect', () => onProtocolDefect(ws))
322
+ : Effect.void,
323
+ ),
324
+ )
325
+ return Effect.ensuring(responseEffect, onAccepted)
326
+ } catch (cause) {
327
+ return Effect.die(cause)
328
+ }
329
+ }
330
+
331
+ const writeResponse = (response: unknown, onAccepted?: Effect.Effect<void>) => {
282
332
  try {
283
333
  const encoded = parser.encode(response)
284
- if (encoded === undefined) {
285
- return Effect.void
286
- }
287
- return Effect.orDie(writeRaw(encoded))
334
+ const responseEffect = encoded === undefined ? Effect.void : Effect.orDie(writeRaw(encoded))
335
+ return onAccepted === undefined ? responseEffect : Effect.ensuring(responseEffect, onAccepted)
288
336
  } catch (cause) {
289
- return Effect.orDie(writeRaw(parser.encode(RpcMessage.ResponseDefectEncoded(cause))!))
337
+ return writeDefect(cause)
290
338
  }
291
339
  }
292
340
 
341
+ const writeExit = (requestId: string | number, exit: unknown) =>
342
+ writeResponse(
343
+ { _tag: 'Exit', requestId, exit },
344
+ Effect.sync(() => requestIdsWithSchemas.delete(requestId)).pipe(
345
+ Effect.andThen(
346
+ onRequestExit !== undefined
347
+ ? runLifecycleHook('onRequestExit', () => onRequestExit(requestId, ws))
348
+ : Effect.void,
349
+ ),
350
+ ),
351
+ )
352
+
353
+ const write = (response: RpcMessage.FromServerEncoded) => {
354
+ if (response._tag === 'Exit') return writeExit(response.requestId, response.exit)
355
+ if (response._tag === 'Defect') return writeDefect(response.defect)
356
+ return writeResponse(response)
357
+ }
358
+
293
359
  const protocol = yield* RpcServer.Protocol.make((writeRequest_) => {
294
360
  writeRequest = writeRequest_
295
361
 
296
362
  // Start processing messages now that writeRequest is available
297
- const startProcessing = Mailbox.toStream(incomingQueue).pipe(
363
+ const startProcessing = Stream.fromQueue(incomingQueue).pipe(
298
364
  Stream.tap((data) => {
299
365
  try {
300
366
  const decoded = parser.decode(data) as ReadonlyArray<RpcMessage.FromClientEncoded>
@@ -304,20 +370,27 @@ const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =
304
370
  while: () => i < decoded.length,
305
371
  body: () => {
306
372
  const request = decoded[i++]!
307
- if (onMessage !== undefined) {
308
- onMessage(request, ws)
373
+ if (onMessage !== undefined) onMessage(request, ws)
374
+
375
+ if (request._tag === 'Request') {
376
+ requestIdsWithSchemas.add(request.id)
377
+ } else if (request._tag === 'Interrupt' && requestIdsWithSchemas.has(request.requestId) === false) {
378
+ // Effect creates this Exit for an unknown request, but its encoder drops it when hibernation has
379
+ // discarded the request schema.
380
+ return writeExit(request.requestId, interruptedExitEncoded)
309
381
  }
382
+
310
383
  return writeRequest(id, request)
311
384
  },
312
- step: constVoid,
385
+ step: Function.constVoid,
313
386
  })
314
387
  } catch (cause) {
315
- return Effect.orDie(writeRaw(parser.encode(RpcMessage.ResponseDefectEncoded(cause))!))
388
+ return writeDefect(cause)
316
389
  }
317
390
  }),
318
391
  Stream.runDrain,
319
392
  Effect.tapCauseLogPretty,
320
- Effect.fork,
393
+ Effect.forkIn(scope),
321
394
  )
322
395
 
323
396
  // Start the message processing
@@ -333,6 +406,8 @@ const makeSocketProtocol = ({ incomingQueue, ws, onMessage }: WsRpcServerArgs) =
333
406
  supportsAck: true,
334
407
  supportsTransferables: false,
335
408
  supportsSpanPropagation: true,
409
+ supportsNotifications: true,
410
+ codecFor: serialization.codecFor,
336
411
  }))
337
412
  })
338
413
 
@@ -1,15 +1,13 @@
1
1
  import { expect } from 'vitest'
2
2
 
3
3
  import { Vitest } from '@livestore/utils-dev/node-vitest'
4
- import { WranglerDevServerService } from '@livestore/utils-dev/wrangler'
4
+ import { WranglerDevServer } from '@livestore/utils-dev/wrangler'
5
5
  import {
6
- Chunk,
7
6
  Effect,
8
7
  FetchHttpClient,
9
8
  Layer,
10
- Logger,
11
- LogLevel,
12
9
  Option,
10
+ References,
13
11
  RpcClient,
14
12
  RpcSerialization,
15
13
  Socket,
@@ -24,29 +22,33 @@ const testTimeout = 60_000
24
22
  const withWranglerTest = Vitest.makeWithTestCtx({
25
23
  timeout: testTimeout,
26
24
  makeLayer: () =>
27
- WranglerDevServerService.Default({
25
+ WranglerDevServer.layer({
28
26
  cwd: `${import.meta.dirname}/test-fixtures`,
29
27
  }).pipe(
30
28
  Layer.provide(
31
- 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
+ ),
32
34
  ),
33
35
  ),
34
36
  })
35
37
 
36
38
  const ProtocolLive = Layer.suspend(() =>
37
39
  Effect.gen(function* () {
38
- const server = yield* WranglerDevServerService
40
+ const server = yield* WranglerDevServer.WranglerDevServer
39
41
  return RpcClient.layerProtocolSocket().pipe(
40
42
  Layer.provide(Socket.layerWebSocket(`ws://localhost:${server.port}`)),
41
43
  Layer.provide(Socket.layerWebSocketConstructorGlobal),
42
44
  Layer.provide(RpcSerialization.layerJson),
43
45
  )
44
- }).pipe(Layer.unwrapEffect),
46
+ }).pipe(Layer.unwrap),
45
47
  )
46
48
 
47
49
  Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () => {
48
50
  // Direct HTTP RPC client tests
49
- Vitest.scopedLive('should call ping method', (test) =>
51
+ Vitest.live('should call ping method', (test) =>
50
52
  Effect.gen(function* () {
51
53
  const client = yield* RpcClient.make(TestRpcs)
52
54
  const result = yield* client.Ping({ message: 'Hello HTTP RPC' })
@@ -54,7 +56,7 @@ Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () =>
54
56
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
55
57
  )
56
58
 
57
- Vitest.scopedLive('should call echo method', (test) =>
59
+ Vitest.live('should call echo method', (test) =>
58
60
  Effect.gen(function* () {
59
61
  const client = yield* RpcClient.make(TestRpcs)
60
62
  const result = yield* client.Echo({ text: 'Echo' })
@@ -62,7 +64,7 @@ Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () =>
62
64
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
63
65
  )
64
66
 
65
- Vitest.scopedLive('should call add method', (test) =>
67
+ Vitest.live('should call add method', (test) =>
66
68
  Effect.gen(function* () {
67
69
  const client = yield* RpcClient.make(TestRpcs)
68
70
  const result = yield* client.Add({ a: 15, b: 25 })
@@ -70,104 +72,72 @@ Vitest.describe('Durable Object WebSocket RPC', { timeout: testTimeout }, () =>
70
72
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
71
73
  )
72
74
 
73
- Vitest.scopedLive('should handle RPC fail method', (test) =>
75
+ Vitest.live('should handle RPC fail method', (test) =>
74
76
  Effect.gen(function* () {
75
77
  const client = yield* RpcClient.make(TestRpcs)
76
78
  const error = yield* client.Fail({ message: 'test http failure' }).pipe(Effect.exit)
77
79
  expect(error.toString()).toMatchInlineSnapshot(`
78
- "{
79
- "_id": "Exit",
80
- "_tag": "Failure",
81
- "cause": {
82
- "_id": "Cause",
83
- "_tag": "Fail",
84
- "failure": "RPC failure: test http failure"
85
- }
86
- }"
80
+ "Failure(Cause([Fail("RPC failure: test http failure")]))"
87
81
  `)
88
82
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
89
83
  )
90
84
 
91
- Vitest.scopedLive('should handle defect method', (test) =>
85
+ Vitest.live('should handle defect method', (test) =>
92
86
  Effect.gen(function* () {
93
87
  const client = yield* RpcClient.make(TestRpcs)
94
88
  const error = yield* client.Defect({ message: 'test http defect' }).pipe(Effect.exit)
95
89
  expect(error.toString()).toMatchInlineSnapshot(`
96
- "{
97
- "_id": "Exit",
98
- "_tag": "Failure",
99
- "cause": {
100
- "_id": "Cause",
101
- "_tag": "Die",
102
- "defect": "some defect: test http defect"
103
- }
104
- }"
90
+ "Failure(Cause([Die("some defect: test http defect")]))"
105
91
  `)
106
92
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
107
93
  )
108
94
 
109
- Vitest.scopedLive('should handle streaming RPC via HTTP', (test) =>
95
+ Vitest.live('should handle streaming RPC via HTTP', (test) =>
110
96
  Effect.gen(function* () {
111
97
  const client = yield* RpcClient.make(TestRpcs)
112
98
  const stream = client.Stream({}).pipe(
113
99
  Stream.take(4),
114
100
  Stream.map((c) => c.maybeNumber.pipe(Option.getOrUndefined)),
115
101
  )
116
- const chunks = yield* Stream.runCollect(stream)
117
- 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
118
104
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
119
105
  )
120
106
 
121
- Vitest.scopedLive('should handle streaming RPC with error via HTTP', (test) =>
107
+ Vitest.live('should handle streaming RPC with error via HTTP', (test) =>
122
108
  Effect.gen(function* () {
123
109
  const client = yield* RpcClient.make(TestRpcs)
124
110
  const stream = client.StreamError({ count: 5, errorAfter: 4 })
125
111
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
126
112
  expect(error.toString()).toMatchInlineSnapshot(`
127
- "{
128
- "_id": "Exit",
129
- "_tag": "Failure",
130
- "cause": {
131
- "_id": "Cause",
132
- "_tag": "Fail",
133
- "failure": "Stream error after 4: got 9"
134
- }
135
- }"
113
+ "Failure(Cause([Fail("Stream error after 4: got 9")]))"
136
114
  `)
137
115
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
138
116
  )
139
117
 
140
- Vitest.scopedLive('should handle streaming RPC with defect via HTTP', (test) =>
118
+ Vitest.live('should handle streaming RPC with defect via HTTP', (test) =>
141
119
  Effect.gen(function* () {
142
120
  const client = yield* RpcClient.make(TestRpcs)
143
121
  const stream = client.StreamDefect({ count: 4, defectAfter: 1 })
144
122
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
145
123
  expect(error.toString()).toMatchInlineSnapshot(`
146
- "{
147
- "_id": "Exit",
148
- "_tag": "Failure",
149
- "cause": {
150
- "_id": "Cause",
151
- "_tag": "Die",
152
- "defect": "Stream defect after 1: got 4"
153
- }
154
- }"
124
+ "Failure(Cause([Die("Stream defect after 1: got 4")]))"
155
125
  `)
156
126
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
157
127
  )
158
128
 
159
- Vitest.scopedLive('should handle stream interruption via HTTP', (test) =>
129
+ Vitest.live('should handle stream interruption via HTTP', (test) =>
160
130
  Effect.gen(function* () {
161
131
  const client = yield* RpcClient.make(TestRpcs)
162
132
  const stream = client.StreamInterruptible({ delay: 50, interruptAfterCount: 3 }).pipe(Stream.take(3))
163
- const chunks = yield* Stream.runCollect(stream)
164
- expect(Chunk.toReadonlyArray(chunks)).toEqual([1, 2, 3])
133
+ const result = yield* Stream.runCollect(stream)
134
+ expect(result).toEqual([1, 2, 3])
165
135
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
166
136
  )
167
137
  })
168
138
 
169
139
  Vitest.describe('Hibernation Tests', { timeout: 25000 }, () => {
170
- Vitest.scopedLive('should maintain RPC functionality after hibernation', (test) =>
140
+ Vitest.live('should maintain RPC functionality after hibernation', (test) =>
171
141
  Effect.gen(function* () {
172
142
  console.log('๐Ÿงช Testing RPC server persistence across hibernation...')
173
143
 
@@ -222,15 +192,15 @@ Vitest.describe('Hibernation Tests', { timeout: 25000 }, () => {
222
192
  Stream.take(3),
223
193
  Stream.map((c) => c.maybeNumber.pipe(Option.getOrUndefined)),
224
194
  )
225
- const chunks = yield* Stream.runCollect(stream)
226
- 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
227
197
  console.log('โœ… Streaming after hibernation successful')
228
198
 
229
199
  console.log('๐ŸŽ‰ All RPC operations successful after hibernation!')
230
200
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
231
201
  )
232
202
 
233
- Vitest.scopedLive('should handle rapid operations after hibernation', (test) =>
203
+ Vitest.live('should handle rapid operations after hibernation', (test) =>
234
204
  Effect.gen(function* () {
235
205
  console.log('๐Ÿงช Testing rapid operations after hibernation...')
236
206