@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
@@ -1,59 +1,79 @@
1
1
  import {
2
+ Data,
2
3
  Effect,
3
4
  Fiber,
4
5
  FiberMap,
5
6
  Layer,
7
+ Option,
6
8
  RpcClient,
7
- type RpcMessage,
9
+ RpcClientError,
10
+ RpcMessage,
8
11
  RpcSerialization,
12
+ Schema,
9
13
  type Scope,
10
14
  } from '@livestore/utils/effect'
11
15
 
12
16
  import type * as CfTypes from '../cf-types.ts'
13
17
 
14
- /**
15
- * Processes a ReadableStream response from streaming RPCs.
16
- * Reads chunks from the stream and writes them as RPC responses.
17
- */
18
+ /** A transferred stream can lose Cloudflare's RPC error flags when its connection breaks. */
19
+ export class DoRpcReadError extends Data.TaggedError('DoRpcReadError')<{ cause: unknown }> {}
20
+
21
+ const isEncodedRpcMessage = Schema.is(RpcMessage.EncodedSchema)
22
+
23
+ const isFromServerEncoded = (message: unknown): message is RpcMessage.FromServerEncoded => {
24
+ if (isEncodedRpcMessage(message) === false) return false
25
+
26
+ return (
27
+ message._tag === 'Chunk' ||
28
+ message._tag === 'Exit' ||
29
+ message._tag === 'Defect' ||
30
+ message._tag === 'Pong' ||
31
+ message._tag === 'Request'
32
+ )
33
+ }
34
+
35
+ /** Decodes a streaming-RPC `ReadableStream`'s binary frames, writing each out as it arrives. */
18
36
  const processReadableStream = (
19
37
  stream: CfTypes.ReadableStream,
20
- parser: ReturnType<typeof RpcSerialization.msgPack.unsafeMake>,
21
- writeResponse: (response: any) => Effect.Effect<void>,
22
- ): Effect.Effect<void> =>
38
+ parser: RpcSerialization.Parser,
39
+ writeResponse: (response: RpcMessage.FromServerEncoded) => Effect.Effect<void>,
40
+ ): Effect.Effect<void, RpcClientError.RpcClientError> =>
23
41
  Effect.gen(function* () {
24
42
  const reader = stream.getReader()
25
43
 
26
44
  yield* Effect.gen(function* () {
27
45
  while (true) {
28
- const { done, value } = yield* Effect.tryPromise(() => reader.read()).pipe(Effect.orDie)
46
+ const { done, value } = yield* Effect.tryPromise({
47
+ try: () => reader.read(),
48
+ catch: (cause) => transportFailure(new DoRpcReadError({ cause })),
49
+ })
29
50
 
30
51
  if (done === true) {
31
- break
52
+ return yield* transportFailure(new DoRpcReadError({ cause: new Error('RPC stream ended before its exit') }))
32
53
  }
33
54
 
34
- // Decode the chunk
35
- const decoded = parser.decode(value as Uint8Array)
36
-
37
- // Handle array of messages from server.
38
- // Server sends `parser.encode([message])` per enqueue, so each decoded value is `[message]`.
39
- // When CF DO RPC merges enqueues in production, we get `[[msg1], [msg2], ...]`.
40
- // `flat(1)` normalizes both single and merged cases to `[msg1, msg2, ...]`.
41
- let messages: any[]
42
- if (Array.isArray(decoded) === true) {
43
- messages = decoded.flat(1)
44
- } else {
45
- messages = [decoded]
55
+ if (value instanceof Uint8Array === false) {
56
+ return yield* Effect.die('Received a non-binary RPC response')
46
57
  }
47
58
 
48
- // Write each message
49
- for (const message of messages) {
59
+ for (const message of parser.decode(value)) {
60
+ if (isFromServerEncoded(message) === false) {
61
+ return yield* Effect.die('Received an invalid RPC response')
62
+ }
50
63
  yield* writeResponse(message)
64
+ // The RPC exit completes the request; do not wait for the transferred stream to close too.
65
+ if (message._tag === 'Exit') return
51
66
  }
52
67
  }
53
68
  }).pipe(
54
69
  Effect.withSpan('do-rpc-client:processReadableStream'),
70
+ // Cancelling an errored stream rejects with that same error; ignore it so the original failure surfaces.
55
71
  Effect.ensuring(
56
- Effect.promise(() => reader.cancel()).pipe(Effect.andThen(() => Effect.sync(() => reader.releaseLock()))),
72
+ Effect.sync(() => {
73
+ // Cancellation is best effort: a disconnected remote producer may never acknowledge it.
74
+ void reader.cancel().catch(() => {})
75
+ reader.releaseLock()
76
+ }),
57
77
  ),
58
78
  )
59
79
  })
@@ -70,9 +90,10 @@ interface MakeDoRpcProtocolArgs {
70
90
  * Creates a Protocol layer that uses Cloudflare Durable Object RPC calls.
71
91
  * This enables direct RPC communication with Durable Objects using Cloudflare's native RPC.
72
92
  */
73
- export const layerProtocolDurableObject = (
74
- args: MakeDoRpcProtocolArgs,
75
- ): Layer.Layer<RpcClient.Protocol> => Layer.scoped(RpcClient.Protocol, makeProtocolDurableObject(args))
93
+ export const layerProtocolDurableObject = (args: MakeDoRpcProtocolArgs): Layer.Layer<RpcClient.Protocol> =>
94
+ Layer.effect(RpcClient.Protocol, makeProtocolDurableObject(args)).pipe(
95
+ Layer.provide(RpcSerialization.layerSchemaBinary()),
96
+ )
76
97
 
77
98
  /**
78
99
  * Implementation of the RPC Protocol interface using Cloudflare Durable Object RPC calls.
@@ -80,73 +101,83 @@ export const layerProtocolDurableObject = (
80
101
  */
81
102
  const makeProtocolDurableObject = ({
82
103
  callRpc,
83
- }: MakeDoRpcProtocolArgs): Effect.Effect<RpcClient.Protocol['Type'], never, Scope.Scope> =>
104
+ }: MakeDoRpcProtocolArgs): Effect.Effect<
105
+ RpcClient.Protocol['Service'],
106
+ never,
107
+ Scope.Scope | RpcSerialization.RpcSerialization
108
+ > =>
84
109
  RpcClient.Protocol.make(
85
110
  Effect.fnUntraced(function* (writeResponse) {
86
- const parser = RpcSerialization.msgPack.unsafeMake()
111
+ const serialization = yield* RpcSerialization.RpcSerialization
87
112
  // Not using an actual `FiberMap` here because it seems to shutdown to early
88
- // const fiberMap = new Map<string, Fiber.RuntimeFiber<void, never>>()
89
- const fiberMap = yield* FiberMap.make<string, void, never>()
113
+ // const fiberMap = new Map<string, Fiber.Fiber<void, never>>()
114
+ const fiberMap = yield* FiberMap.make<string, void, RpcClientError.RpcClientError>()
90
115
 
91
- const send = (message: RpcMessage.FromClientEncoded): Effect.Effect<void> => {
116
+ const send = (
117
+ clientId: number,
118
+ message: RpcMessage.FromClientEncoded,
119
+ ): Effect.Effect<void, RpcClientError.RpcClientError> => {
92
120
  if (message._tag !== 'Request') {
93
121
  if (message._tag === 'Interrupt') {
94
122
  return Effect.gen(function* () {
95
123
  const fiber = yield* FiberMap.get(fiberMap, message.requestId)
96
- yield* Fiber.interrupt(fiber)
124
+ if (Option.isSome(fiber) === true) {
125
+ yield* Fiber.interrupt(fiber.value)
126
+ }
97
127
  }).pipe(Effect.orDie)
98
128
  }
99
129
 
100
130
  return Effect.void
101
131
  }
102
132
 
133
+ // Binary parsers hold stream framing state, so scope one parser to one DO RPC call.
134
+ const parser = serialization.makeUnsafe()
135
+
103
136
  // Wrap single Request in array to match server expected format
104
- const serializedPayload = parser.encode([message]) as Uint8Array
137
+ const serializedPayload = parser.encode([message])
138
+ if (serializedPayload instanceof Uint8Array === false) {
139
+ return Effect.die('SchemaBinary RPC serialization did not produce bytes')
140
+ }
105
141
 
106
142
  return Effect.gen(function* () {
107
- const serializedResponse = yield* Effect.tryPromise(() => callRpc(serializedPayload)).pipe(Effect.orDie) // Convert errors to defects to match never error type
108
-
109
- // Handle ReadableStream for streaming responses
110
- if (serializedResponse instanceof ReadableStream) {
111
- const fiber = yield* processReadableStream(
112
- serializedResponse as CfTypes.ReadableStream,
113
- parser,
114
- writeResponse,
115
- ).pipe(
116
- // Effect.tapCauseLogPretty,
117
- Effect.fork,
118
- )
119
-
120
- // fiberMap.set(message.id, fiber)
121
- yield* FiberMap.set(fiberMap, message.id, fiber)
122
-
123
- yield* fiber
124
-
143
+ const serializedResponse = yield* Effect.tryPromise({
144
+ try: () => callRpc(serializedPayload),
145
+ catch: transportFailure,
146
+ })
147
+
148
+ if (serializedResponse instanceof Uint8Array) {
149
+ for (const response of parser.decode(serializedResponse)) {
150
+ if (isFromServerEncoded(response) === false) {
151
+ return yield* Effect.die('Received an invalid RPC response')
152
+ }
153
+ yield* writeResponse(clientId, response)
154
+ }
125
155
  return
126
156
  }
127
157
 
128
- // Handle regular Uint8Array responses
129
- const decoded = parser.decode(serializedResponse as Uint8Array)
130
-
131
- // Normalize nested arrays from server serialization (same as streaming path)
132
- let responseArray: any[]
133
- if (Array.isArray(decoded) === true) {
134
- responseArray = decoded.flat(1)
135
- } else {
136
- responseArray = [decoded]
137
- }
158
+ const fiber = yield* processReadableStream(serializedResponse, parser, (response) =>
159
+ writeResponse(clientId, response),
160
+ ).pipe(Effect.forkChild)
138
161
 
139
- // Process each response
140
- for (const response of responseArray) {
141
- yield* writeResponse(response)
142
- }
143
- }).pipe(Effect.withSpan('do-rpc-client:send'), Effect.orDie) // Ensure never error type
162
+ yield* FiberMap.set(fiberMap, message.id, fiber)
163
+ yield* Fiber.join(fiber)
164
+ }).pipe(Effect.withSpan('do-rpc-client:send'))
144
165
  }
145
166
 
146
167
  return {
147
168
  send,
148
169
  supportsAck: false, // DO RPC doesn't support ack mechanism like WebSockets
149
170
  supportsTransferables: false, // DO RPC doesn't support transferables yet
171
+ codecFor: serialization.codecFor,
150
172
  }
151
173
  }),
152
174
  )
175
+
176
+ /**
177
+ * A rejected DO call or stream read fails only the request that made it (Effect RPC routes `send` failures per
178
+ * request). `RpcClientError` reasons are a closed union, so the original rejection rides as `RpcClientDefect.cause`.
179
+ */
180
+ const transportFailure = (cause: unknown) =>
181
+ new RpcClientError.RpcClientError({
182
+ reason: new RpcClientError.RpcClientDefect({ message: 'Durable Object RPC call failed', cause }),
183
+ })
package/src/do-rpc/mod.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // Re-export client and server implementations for backward compatibility
2
- export { layerProtocolDurableObject } from './client.ts'
2
+ export { DoRpcReadError, layerProtocolDurableObject } from './client.ts'
3
3
  export { type ClientDoWithRpcCallback, emitStreamResponse, toDurableObjectHandler } from './server.ts'
@@ -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
  Stream,
@@ -23,22 +21,26 @@ const testTimeout = 60_000
23
21
  const withWranglerTest = Vitest.makeWithTestCtx({
24
22
  timeout: testTimeout,
25
23
  makeLayer: () =>
26
- WranglerDevServerService.Default({
24
+ WranglerDevServer.layer({
27
25
  cwd: `${import.meta.dirname}/test-fixtures`,
28
26
  }).pipe(
29
27
  Layer.provide(
30
- Layer.mergeAll(PlatformNode.NodeContext.layer, FetchHttpClient.layer, Logger.minimumLogLevel(LogLevel.Debug)),
28
+ Layer.mergeAll(
29
+ PlatformNode.NodeServices.layer,
30
+ FetchHttpClient.layer,
31
+ Layer.succeed(References.MinimumLogLevel, 'Debug'),
32
+ ),
31
33
  ),
32
34
  ),
33
35
  })
34
36
 
35
37
  const ProtocolLive = Layer.suspend(() =>
36
38
  Effect.gen(function* () {
37
- const server = yield* WranglerDevServerService
39
+ const server = yield* WranglerDevServer.WranglerDevServer
38
40
  return RpcClient.layerProtocolHttp({
39
41
  url: `${server.url}/rpc`,
40
42
  }).pipe(Layer.provide([FetchHttpClient.layer, RpcSerialization.layerJson]))
41
- }).pipe(Layer.unwrapEffect),
43
+ }).pipe(Layer.unwrap),
42
44
  )
43
45
 
44
46
  /**
@@ -61,7 +63,7 @@ const ProtocolLive = Layer.suspend(() =>
61
63
 
62
64
  Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
63
65
  // Direct HTTP RPC client tests
64
- Vitest.scopedLive('should call ping method', (test) =>
66
+ Vitest.live('should call ping method', (test) =>
65
67
  Effect.gen(function* () {
66
68
  const client = yield* RpcClient.make(TestRpcs)
67
69
  const result = yield* client.Ping({ message: 'Hello HTTP RPC' })
@@ -69,7 +71,7 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
69
71
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
70
72
  )
71
73
 
72
- Vitest.scopedLive('should call echo method', (test) =>
74
+ Vitest.live('should call echo method', (test) =>
73
75
  Effect.gen(function* () {
74
76
  const client = yield* RpcClient.make(TestRpcs)
75
77
  const result = yield* client.Echo({ text: 'Echo' })
@@ -77,7 +79,7 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
77
79
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
78
80
  )
79
81
 
80
- Vitest.scopedLive('should call add method', (test) =>
82
+ Vitest.live('should call add method', (test) =>
81
83
  Effect.gen(function* () {
82
84
  const client = yield* RpcClient.make(TestRpcs)
83
85
  const result = yield* client.Add({ a: 15, b: 25 })
@@ -85,110 +87,78 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
85
87
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
86
88
  )
87
89
 
88
- Vitest.scopedLive('should handle RPC fail method', (test) =>
90
+ Vitest.live('should handle RPC fail method', (test) =>
89
91
  Effect.gen(function* () {
90
92
  const client = yield* RpcClient.make(TestRpcs)
91
93
  const error = yield* client.Fail({ message: 'test http failure' }).pipe(Effect.exit)
92
94
  expect(error.toString()).toMatchInlineSnapshot(`
93
- "{
94
- "_id": "Exit",
95
- "_tag": "Failure",
96
- "cause": {
97
- "_id": "Cause",
98
- "_tag": "Die",
99
- "defect": "RPC failure: test http failure"
100
- }
101
- }"
95
+ "Failure(Cause([Die("RPC failure: test http failure")]))"
102
96
  `)
103
97
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
104
98
  )
105
99
 
106
- Vitest.scopedLive('should handle defect method', (test) =>
100
+ Vitest.live('should handle defect method', (test) =>
107
101
  Effect.gen(function* () {
108
102
  const client = yield* RpcClient.make(TestRpcs)
109
103
  const error = yield* client.Defect({ message: 'test http defect' }).pipe(Effect.exit)
110
104
  expect(error.toString()).toMatchInlineSnapshot(`
111
- "{
112
- "_id": "Exit",
113
- "_tag": "Failure",
114
- "cause": {
115
- "_id": "Cause",
116
- "_tag": "Die",
117
- "defect": "some defect: test http defect"
118
- }
119
- }"
105
+ "Failure(Cause([Die("some defect: test http defect")]))"
120
106
  `)
121
107
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
122
108
  )
123
109
 
124
- Vitest.scopedLive('should handle streaming RPC via HTTP', (test) =>
110
+ Vitest.live('should handle streaming RPC via HTTP', (test) =>
125
111
  Effect.gen(function* () {
126
112
  const client = yield* RpcClient.make(TestRpcs)
127
113
  const stream = client.Stream({}).pipe(
128
114
  Stream.take(4),
129
115
  Stream.map((c) => c.maybeNumber.pipe(Option.getOrUndefined)),
130
116
  )
131
- const chunks = yield* Stream.runCollect(stream)
132
- expect(Chunk.toReadonlyArray(chunks)).toEqual([1, 4, 9, 16]) // squares of 1,2,3,4
117
+ const result = yield* Stream.runCollect(stream)
118
+ expect(result).toEqual([1, 4, 9, 16]) // squares of 1,2,3,4
133
119
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
134
120
  )
135
121
 
136
- Vitest.scopedLive('should handle streaming RPC with error via HTTP', (test) =>
122
+ Vitest.live('should handle streaming RPC with error via HTTP', (test) =>
137
123
  Effect.gen(function* () {
138
124
  const client = yield* RpcClient.make(TestRpcs)
139
125
  const stream = client.StreamError({ count: 5, errorAfter: 4 })
140
126
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
141
127
  expect(error.toString()).toMatchInlineSnapshot(`
142
- "{
143
- "_id": "Exit",
144
- "_tag": "Failure",
145
- "cause": {
146
- "_id": "Cause",
147
- "_tag": "Fail",
148
- "failure": "Stream error after 4: got 9"
149
- }
150
- }"
128
+ "Failure(Cause([Fail("Stream error after 4: got 9")]))"
151
129
  `)
152
130
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
153
131
  )
154
132
 
155
- Vitest.scopedLive('should handle streaming RPC with defect via HTTP', (test) =>
133
+ Vitest.live('should handle streaming RPC with defect via HTTP', (test) =>
156
134
  Effect.gen(function* () {
157
135
  const client = yield* RpcClient.make(TestRpcs)
158
136
  const stream = client.StreamDefect({ count: 4, defectAfter: 1 })
159
137
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
160
138
  expect(error.toString()).toMatchInlineSnapshot(`
161
- "{
162
- "_id": "Exit",
163
- "_tag": "Failure",
164
- "cause": {
165
- "_id": "Cause",
166
- "_tag": "Die",
167
- "defect": "Stream defect after 1: got 4"
168
- }
169
- }"
139
+ "Failure(Cause([Die("Stream defect after 1: got 4")]))"
170
140
  `)
171
141
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
172
142
  )
173
143
 
174
- Vitest.scopedLive('should handle stream interruption via HTTP', (test) =>
144
+ Vitest.live('should handle stream interruption via HTTP', (test) =>
175
145
  Effect.gen(function* () {
176
146
  const client = yield* RpcClient.make(TestRpcs)
177
147
  const stream = client.StreamInterruptible({ delay: 50, interruptAfterCount: 3 }).pipe(Stream.take(3))
178
- const chunks = yield* Stream.runCollect(stream)
179
- expect(Chunk.toReadonlyArray(chunks)).toEqual([1, 2, 3])
148
+ const result = yield* Stream.runCollect(stream)
149
+ expect(result).toEqual([1, 2, 3])
180
150
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
181
151
  )
182
152
 
183
153
  // TODO @IMax153
184
- Vitest.scopedLive.skip('should handle streaming RPC bug scenario', (test) =>
154
+ Vitest.live.skip('should handle streaming RPC bug scenario', (test) =>
185
155
  Effect.gen(function* () {
186
156
  const client = yield* RpcClient.make(TestRpcs)
187
157
  yield* client.StreamBugScenarioDoClient({})
188
158
  /*
189
159
 
190
160
 
191
- In `node_modules/.pnpm/@effect+rpc@0.68.4_@effect+platform@0.90.0_effect@3.17.7__effect@3.17.7/node_modules/@effect/rpc/dist/esm/RpcClient.js`:
161
+ In the installed `@effect/rpc` `RpcClient.js`:
192
162
  add the console log
193
163
  ```
194
164
  yield* Scope.addFinalizerExit(scope, exit => {
@@ -225,7 +195,7 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
225
195
  errorSchema: [Function: Never],
226
196
  annotations: { _id: 'Context', services: [] },
227
197
  middlewares: Set(0) {},
228
- key: '@effect/rpc/Rpc/StreamBugScenarioDoServer'
198
+ key: 'effect/unstable/rpc/Rpc/StreamBugScenarioDoServer'
229
199
  } {}
230
200
 
231
201
  Killing wrangler process...