@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
@@ -4,13 +4,21 @@
4
4
 
5
5
  import type * as CF from '@cloudflare/workers-types'
6
6
 
7
- export const ReadableStream = globalThis.ReadableStream as unknown as typeof CF.ReadableStream
8
- export const Request = globalThis.Request as unknown as typeof CF.Request
9
- export const Response = globalThis.Response as unknown as typeof CF.Response
10
- export const WebSocket = globalThis.WebSocket as unknown as typeof CF.WebSocket
11
- export const Rpc = globalThis.Rpc as unknown as typeof CF.Rpc
12
- // @ts-expect-error WebSocketPair is not defined in the globalThis object
13
- export const WebSocketPair = globalThis.WebSocketPair as unknown as typeof CF.WebSocketPair
14
- export const WebSocketRequestResponsePair =
15
- // @ts-expect-error WebSocketRequestResponsePair is not defined in the globalThis object
16
- globalThis.WebSocketRequestResponsePair as unknown as typeof CF.WebSocketRequestResponsePair
7
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- bridging standard Web API types to Cloudflare Worker types; inherent platform type mismatch
8
+ const cfGlobalThis = globalThis as unknown as {
9
+ ReadableStream: typeof CF.ReadableStream
10
+ Request: typeof CF.Request
11
+ Response: typeof CF.Response
12
+ WebSocket: typeof CF.WebSocket
13
+ Rpc: typeof CF.Rpc
14
+ WebSocketPair: typeof CF.WebSocketPair
15
+ WebSocketRequestResponsePair: typeof CF.WebSocketRequestResponsePair
16
+ }
17
+
18
+ export const ReadableStream = cfGlobalThis.ReadableStream
19
+ export const Request = cfGlobalThis.Request
20
+ export const Response = cfGlobalThis.Response
21
+ export const WebSocket = cfGlobalThis.WebSocket
22
+ export const Rpc = cfGlobalThis.Rpc
23
+ export const WebSocketPair = cfGlobalThis.WebSocketPair
24
+ export const WebSocketRequestResponsePair = cfGlobalThis.WebSocketRequestResponsePair
@@ -68,7 +68,7 @@ async rpc(payload: Uint8Array): Promise<Uint8Array | ReadableStream> {
68
68
  return handler(payload).pipe(Effect.runPromise)
69
69
  }
70
70
 
71
- // Client setup
71
+ // Client setup
72
72
  const client = RpcClient.make(MyRpcs, {
73
73
  layer: layerProtocolDurableObject(
74
74
  (payload) => serverDO.rpc(payload)
@@ -82,4 +82,3 @@ const result3 = yield* client.CountUp({ startValue: 0 }).pipe(Stream.take(10), S
82
82
  ```
83
83
 
84
84
  ## Effect RPC reference
85
-
@@ -0,0 +1,122 @@
1
+ import { Vitest } from '@livestore/utils-dev/node-vitest'
2
+ import { Effect, Fiber, Rpc, RpcClient, RpcGroup, Schema, Stream } from '@livestore/utils/effect'
3
+
4
+ import type * as CfTypes from '../cf-types.ts'
5
+ import { layerProtocolDurableObject } from './client.ts'
6
+ import { toDurableObjectHandler } from './server.ts'
7
+
8
+ class Rpcs extends RpcGroup.make(
9
+ Rpc.make('BigStream', {
10
+ payload: Schema.Struct({ n: Schema.Finite }),
11
+ success: Schema.Struct({
12
+ seqNum: Schema.Finite,
13
+ name: Schema.String,
14
+ args: Schema.Struct({ a: Schema.Finite, b: Schema.String }),
15
+ }),
16
+ stream: true,
17
+ }),
18
+ Rpc.make('Echo', {
19
+ payload: Schema.Struct({ text: Schema.String }),
20
+ success: Schema.Struct({ echo: Schema.String }),
21
+ }),
22
+ ) {}
23
+
24
+ const READ_CHUNK_SIZE = 4096
25
+
26
+ const expectedRows = [
27
+ {
28
+ seqNum: 0,
29
+ name: 'event-0',
30
+ args: { a: 0, b: 'x'.repeat(READ_CHUNK_SIZE) },
31
+ },
32
+ ]
33
+
34
+ const ServerLive = Rpcs.toLayer({
35
+ BigStream: ({ n }) => Stream.fromIterable(expectedRows.slice(0, n)),
36
+ Echo: ({ text }) => Effect.succeed({ echo: `Echo: ${text}` }),
37
+ })
38
+
39
+ Vitest.live('keeps a straddling stream frame isolated from a concurrent unary response', () =>
40
+ Effect.gen(function* () {
41
+ let signalFirstStreamRead = () => {}
42
+ const firstStreamReadDone = new Promise<void>((resolve) => {
43
+ signalFirstStreamRead = resolve
44
+ })
45
+ let releaseStreamTail = () => {}
46
+ const streamTailReleased = new Promise<void>((resolve) => {
47
+ releaseStreamTail = resolve
48
+ })
49
+
50
+ const streamWithGatedTail = (bytes: Uint8Array) => {
51
+ let pos = 0
52
+ let isFirstRead = true
53
+ const stream = new ReadableStream<Uint8Array>({
54
+ async pull(controller) {
55
+ if (pos >= bytes.length) return controller.close()
56
+ if (isFirstRead === false) await streamTailReleased
57
+ controller.enqueue(bytes.subarray(pos, pos + READ_CHUNK_SIZE))
58
+ pos += READ_CHUNK_SIZE
59
+ if (isFirstRead === true) {
60
+ isFirstRead = false
61
+ signalFirstStreamRead()
62
+ }
63
+ },
64
+ })
65
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- bridge platform ReadableStream to the CF type, like server.ts
66
+ return stream as unknown as CfTypes.ReadableStream
67
+ }
68
+
69
+ const ProtocolLive = layerProtocolDurableObject({
70
+ callRpc: makeGatedCallRpc(streamWithGatedTail),
71
+ callerContext: { bindingName: 'TEST', durableObjectId: 'id' },
72
+ })
73
+
74
+ const result = yield* Effect.gen(function* () {
75
+ const client = yield* RpcClient.make(Rpcs)
76
+ const streamFiber = yield* client.BigStream({ n: expectedRows.length }).pipe(Stream.runCollect, Effect.forkChild)
77
+
78
+ yield* Effect.promise(() => firstStreamReadDone)
79
+ const echo = yield* client.Echo({ text: 'hi' }).pipe(Effect.timeout('500 millis'))
80
+ yield* Effect.sync(() => releaseStreamTail())
81
+
82
+ const rows = yield* Fiber.join(streamFiber)
83
+ return { rows: Array.from(rows), echo }
84
+ }).pipe(Effect.provide(ProtocolLive), Effect.timeout('2 seconds'))
85
+
86
+ Vitest.expect(result.echo).toEqual({ echo: 'Echo: hi' })
87
+ Vitest.expect(result.rows).toEqual(expectedRows)
88
+ }),
89
+ )
90
+
91
+ const makeGatedCallRpc =
92
+ (gateStream: (bytes: Uint8Array) => CfTypes.ReadableStream) =>
93
+ (payload: Uint8Array): Promise<Uint8Array | CfTypes.ReadableStream> =>
94
+ toDurableObjectHandler(Rpcs, { layer: ServerLive })(new Uint8Array(payload)).pipe(
95
+ Effect.flatMap(
96
+ // Narrow on `Uint8Array`; the `ReadableStream` global differs from `CfTypes` across envs.
97
+ (result): Effect.Effect<Uint8Array | CfTypes.ReadableStream> =>
98
+ result instanceof Uint8Array
99
+ ? Effect.succeed(result)
100
+ : Effect.promise(() => collectBytes(result)).pipe(Effect.map(gateStream)),
101
+ ),
102
+ Effect.runPromise,
103
+ )
104
+
105
+ const collectBytes = async (stream: CfTypes.ReadableStream) => {
106
+ const reader = stream.getReader()
107
+ const parts: Uint8Array[] = []
108
+ let total = 0
109
+ for (;;) {
110
+ const { done, value } = await reader.read()
111
+ if (done === true) break
112
+ parts.push(value)
113
+ total += value.length
114
+ }
115
+ const out = new Uint8Array(total)
116
+ let offset = 0
117
+ for (const part of parts) {
118
+ out.set(part, offset)
119
+ offset += part.length
120
+ }
121
+ return out
122
+ }
@@ -3,52 +3,36 @@ import {
3
3
  Fiber,
4
4
  FiberMap,
5
5
  Layer,
6
+ Option,
6
7
  RpcClient,
7
8
  type RpcMessage,
8
9
  RpcSerialization,
9
10
  type Scope,
10
11
  } from '@livestore/utils/effect'
12
+
11
13
  import type * as CfTypes from '../cf-types.ts'
12
14
 
13
- /**
14
- * Processes a ReadableStream response from streaming RPCs.
15
- * Reads chunks from the stream and writes them as RPC responses.
16
- */
15
+ /** Decodes a streaming-RPC `ReadableStream`'s msgpack frames, writing each out as it arrives. */
17
16
  const processReadableStream = (
18
17
  stream: CfTypes.ReadableStream,
19
- parser: ReturnType<typeof RpcSerialization.msgPack.unsafeMake>,
20
- writeResponse: (response: any) => Effect.Effect<void, never, never>,
21
- ): Effect.Effect<void, never, never> =>
18
+ parser: ReturnType<typeof RpcSerialization.msgPack.makeUnsafe>,
19
+ writeResponse: (response: any) => Effect.Effect<void>,
20
+ ): Effect.Effect<void> =>
22
21
  Effect.gen(function* () {
23
22
  const reader = stream.getReader()
24
23
 
25
24
  yield* Effect.gen(function* () {
26
25
  while (true) {
27
- const { done, value } = yield* Effect.tryPromise({
28
- try: () => reader.read(),
29
- catch: (cause) => cause,
30
- }).pipe(Effect.orDie)
26
+ const { done, value } = yield* Effect.tryPromise(() => reader.read()).pipe(Effect.orDie)
31
27
 
32
- if (done) {
28
+ if (done === true) {
33
29
  break
34
30
  }
35
31
 
36
- // Decode the chunk
32
+ // Server encodes `[message]` per enqueue; merged enqueues arrive as
33
+ // `[[msg1], [msg2], ...]`. `flat(1)` normalizes both to `[msg1, ...]`.
37
34
  const decoded = parser.decode(value as Uint8Array)
38
-
39
- // Handle array of messages - we get [[message]] from server
40
- let messages: any[]
41
- if (Array.isArray(decoded) && decoded.length === 1 && Array.isArray(decoded[0])) {
42
- // Double-wrapped array [[message]] -> [message]
43
- messages = decoded[0]
44
- } else if (Array.isArray(decoded)) {
45
- // Single array [message]
46
- messages = decoded
47
- } else {
48
- messages = [decoded]
49
- }
50
-
51
- // Write each message
35
+ const messages = Array.isArray(decoded) === true ? decoded.flat(1) : [decoded]
52
36
  for (const message of messages) {
53
37
  yield* writeResponse(message)
54
38
  }
@@ -73,9 +57,8 @@ interface MakeDoRpcProtocolArgs {
73
57
  * Creates a Protocol layer that uses Cloudflare Durable Object RPC calls.
74
58
  * This enables direct RPC communication with Durable Objects using Cloudflare's native RPC.
75
59
  */
76
- export const layerProtocolDurableObject = (
77
- args: MakeDoRpcProtocolArgs,
78
- ): Layer.Layer<RpcClient.Protocol, never, never> => Layer.scoped(RpcClient.Protocol, makeProtocolDurableObject(args))
60
+ export const layerProtocolDurableObject = (args: MakeDoRpcProtocolArgs): Layer.Layer<RpcClient.Protocol> =>
61
+ Layer.effect(RpcClient.Protocol, makeProtocolDurableObject(args))
79
62
 
80
63
  /**
81
64
  * Implementation of the RPC Protocol interface using Cloudflare Durable Object RPC calls.
@@ -83,50 +66,51 @@ export const layerProtocolDurableObject = (
83
66
  */
84
67
  const makeProtocolDurableObject = ({
85
68
  callRpc,
86
- }: MakeDoRpcProtocolArgs): Effect.Effect<RpcClient.Protocol['Type'], never, Scope.Scope> =>
69
+ }: MakeDoRpcProtocolArgs): Effect.Effect<RpcClient.Protocol['Service'], never, Scope.Scope> =>
87
70
  RpcClient.Protocol.make(
88
71
  Effect.fnUntraced(function* (writeResponse) {
89
- const parser = RpcSerialization.msgPack.unsafeMake()
90
72
  // Not using an actual `FiberMap` here because it seems to shutdown to early
91
- // const fiberMap = new Map<string, Fiber.RuntimeFiber<void, never>>()
73
+ // const fiberMap = new Map<string, Fiber.Fiber<void, never>>()
92
74
  const fiberMap = yield* FiberMap.make<string, void, never>()
93
75
 
94
- const send = (message: RpcMessage.FromClientEncoded): Effect.Effect<void, never, never> => {
76
+ const send = (clientId: number, message: RpcMessage.FromClientEncoded): Effect.Effect<void> => {
95
77
  if (message._tag !== 'Request') {
96
78
  if (message._tag === 'Interrupt') {
97
79
  return Effect.gen(function* () {
98
80
  const fiber = yield* FiberMap.get(fiberMap, message.requestId)
99
- yield* Fiber.interrupt(fiber)
81
+ if (Option.isSome(fiber) === true) {
82
+ yield* Fiber.interrupt(fiber.value)
83
+ }
100
84
  }).pipe(Effect.orDie)
101
85
  }
102
86
 
103
87
  return Effect.void
104
88
  }
105
89
 
90
+ // MessagePack parsers buffer incomplete frames, so scope one parser to one DO RPC call.
91
+ const parser = RpcSerialization.msgPack.makeUnsafe()
92
+
106
93
  // Wrap single Request in array to match server expected format
107
94
  const serializedPayload = parser.encode([message]) as Uint8Array
108
95
 
109
96
  return Effect.gen(function* () {
110
- const serializedResponse = yield* Effect.tryPromise({
111
- try: () => callRpc(serializedPayload),
112
- catch: (cause) => cause,
113
- }).pipe(Effect.orDie) // Convert errors to defects to match never error type
97
+ const serializedResponse = yield* Effect.tryPromise(() => callRpc(serializedPayload)).pipe(Effect.orDie) // Convert errors to defects to match never error type
114
98
 
115
99
  // Handle ReadableStream for streaming responses
116
100
  if (serializedResponse instanceof ReadableStream) {
117
101
  const fiber = yield* processReadableStream(
118
102
  serializedResponse as CfTypes.ReadableStream,
119
103
  parser,
120
- writeResponse,
104
+ (response) => writeResponse(clientId, response),
121
105
  ).pipe(
122
106
  // Effect.tapCauseLogPretty,
123
- Effect.fork,
107
+ Effect.forkChild,
124
108
  )
125
109
 
126
110
  // fiberMap.set(message.id, fiber)
127
111
  yield* FiberMap.set(fiberMap, message.id, fiber)
128
112
 
129
- yield* fiber
113
+ yield* Fiber.join(fiber)
130
114
 
131
115
  return
132
116
  }
@@ -134,21 +118,17 @@ const makeProtocolDurableObject = ({
134
118
  // Handle regular Uint8Array responses
135
119
  const decoded = parser.decode(serializedResponse as Uint8Array)
136
120
 
137
- // Handle potential nested array from server serialization
121
+ // Normalize nested arrays from server serialization (same as streaming path)
138
122
  let responseArray: any[]
139
- if (Array.isArray(decoded) && decoded.length === 1 && Array.isArray(decoded[0])) {
140
- // Double-wrapped array [[Exit]] -> [Exit]
141
- responseArray = decoded[0]
142
- } else if (Array.isArray(decoded)) {
143
- // Single array [Exit]
144
- responseArray = decoded
123
+ if (Array.isArray(decoded) === true) {
124
+ responseArray = decoded.flat(1)
145
125
  } else {
146
126
  responseArray = [decoded]
147
127
  }
148
128
 
149
129
  // Process each response
150
130
  for (const response of responseArray) {
151
- yield* writeResponse(response)
131
+ yield* writeResponse(clientId, response)
152
132
  }
153
133
  }).pipe(Effect.withSpan('do-rpc-client:send'), Effect.orDie) // Ensure never error type
154
134
  }
@@ -1,19 +1,19 @@
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
  Stream,
12
14
  } from '@livestore/utils/effect'
13
15
  import { PlatformNode } from '@livestore/utils/node'
14
- import { Vitest } from '@livestore/utils-dev/node-vitest'
15
- import { WranglerDevServerService } from '@livestore/utils-dev/wrangler'
16
- import { expect } from 'vitest'
16
+
17
17
  import { TestRpcs } from './test-fixtures/rpc-schema.ts'
18
18
 
19
19
  const testTimeout = 60_000
@@ -21,22 +21,26 @@ const testTimeout = 60_000
21
21
  const withWranglerTest = Vitest.makeWithTestCtx({
22
22
  timeout: testTimeout,
23
23
  makeLayer: () =>
24
- WranglerDevServerService.Default({
24
+ WranglerDevServer.layer({
25
25
  cwd: `${import.meta.dirname}/test-fixtures`,
26
26
  }).pipe(
27
27
  Layer.provide(
28
- 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
+ ),
29
33
  ),
30
34
  ),
31
35
  })
32
36
 
33
37
  const ProtocolLive = Layer.suspend(() =>
34
38
  Effect.gen(function* () {
35
- const server = yield* WranglerDevServerService
39
+ const server = yield* WranglerDevServer.WranglerDevServer
36
40
  return RpcClient.layerProtocolHttp({
37
41
  url: `${server.url}/rpc`,
38
42
  }).pipe(Layer.provide([FetchHttpClient.layer, RpcSerialization.layerJson]))
39
- }).pipe(Layer.unwrapEffect),
43
+ }).pipe(Layer.unwrap),
40
44
  )
41
45
 
42
46
  /**
@@ -59,7 +63,7 @@ const ProtocolLive = Layer.suspend(() =>
59
63
 
60
64
  Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
61
65
  // Direct HTTP RPC client tests
62
- Vitest.scopedLive('should call ping method', (test) =>
66
+ Vitest.live('should call ping method', (test) =>
63
67
  Effect.gen(function* () {
64
68
  const client = yield* RpcClient.make(TestRpcs)
65
69
  const result = yield* client.Ping({ message: 'Hello HTTP RPC' })
@@ -67,7 +71,7 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
67
71
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
68
72
  )
69
73
 
70
- Vitest.scopedLive('should call echo method', (test) =>
74
+ Vitest.live('should call echo method', (test) =>
71
75
  Effect.gen(function* () {
72
76
  const client = yield* RpcClient.make(TestRpcs)
73
77
  const result = yield* client.Echo({ text: 'Echo' })
@@ -75,7 +79,7 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
75
79
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
76
80
  )
77
81
 
78
- Vitest.scopedLive('should call add method', (test) =>
82
+ Vitest.live('should call add method', (test) =>
79
83
  Effect.gen(function* () {
80
84
  const client = yield* RpcClient.make(TestRpcs)
81
85
  const result = yield* client.Add({ a: 15, b: 25 })
@@ -83,103 +87,71 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
83
87
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
84
88
  )
85
89
 
86
- Vitest.scopedLive('should handle RPC fail method', (test) =>
90
+ Vitest.live('should handle RPC fail method', (test) =>
87
91
  Effect.gen(function* () {
88
92
  const client = yield* RpcClient.make(TestRpcs)
89
93
  const error = yield* client.Fail({ message: 'test http failure' }).pipe(Effect.exit)
90
94
  expect(error.toString()).toMatchInlineSnapshot(`
91
- "{
92
- "_id": "Exit",
93
- "_tag": "Failure",
94
- "cause": {
95
- "_id": "Cause",
96
- "_tag": "Die",
97
- "defect": "RPC failure: test http failure"
98
- }
99
- }"
95
+ "Failure(Cause([Die("RPC failure: test http failure")]))"
100
96
  `)
101
97
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
102
98
  )
103
99
 
104
- Vitest.scopedLive('should handle defect method', (test) =>
100
+ Vitest.live('should handle defect method', (test) =>
105
101
  Effect.gen(function* () {
106
102
  const client = yield* RpcClient.make(TestRpcs)
107
103
  const error = yield* client.Defect({ message: 'test http defect' }).pipe(Effect.exit)
108
104
  expect(error.toString()).toMatchInlineSnapshot(`
109
- "{
110
- "_id": "Exit",
111
- "_tag": "Failure",
112
- "cause": {
113
- "_id": "Cause",
114
- "_tag": "Die",
115
- "defect": "some defect: test http defect"
116
- }
117
- }"
105
+ "Failure(Cause([Die("some defect: test http defect")]))"
118
106
  `)
119
107
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
120
108
  )
121
109
 
122
- Vitest.scopedLive('should handle streaming RPC via HTTP', (test) =>
110
+ Vitest.live('should handle streaming RPC via HTTP', (test) =>
123
111
  Effect.gen(function* () {
124
112
  const client = yield* RpcClient.make(TestRpcs)
125
113
  const stream = client.Stream({}).pipe(
126
114
  Stream.take(4),
127
115
  Stream.map((c) => c.maybeNumber.pipe(Option.getOrUndefined)),
128
116
  )
129
- const chunks = yield* Stream.runCollect(stream)
130
- 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
131
119
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
132
120
  )
133
121
 
134
- Vitest.scopedLive('should handle streaming RPC with error via HTTP', (test) =>
122
+ Vitest.live('should handle streaming RPC with error via HTTP', (test) =>
135
123
  Effect.gen(function* () {
136
124
  const client = yield* RpcClient.make(TestRpcs)
137
125
  const stream = client.StreamError({ count: 5, errorAfter: 4 })
138
126
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
139
127
  expect(error.toString()).toMatchInlineSnapshot(`
140
- "{
141
- "_id": "Exit",
142
- "_tag": "Failure",
143
- "cause": {
144
- "_id": "Cause",
145
- "_tag": "Fail",
146
- "failure": "Stream error after 4: got 9"
147
- }
148
- }"
128
+ "Failure(Cause([Fail("Stream error after 4: got 9")]))"
149
129
  `)
150
130
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
151
131
  )
152
132
 
153
- Vitest.scopedLive('should handle streaming RPC with defect via HTTP', (test) =>
133
+ Vitest.live('should handle streaming RPC with defect via HTTP', (test) =>
154
134
  Effect.gen(function* () {
155
135
  const client = yield* RpcClient.make(TestRpcs)
156
136
  const stream = client.StreamDefect({ count: 4, defectAfter: 1 })
157
137
  const error = yield* Stream.runCollect(stream).pipe(Effect.exit)
158
138
  expect(error.toString()).toMatchInlineSnapshot(`
159
- "{
160
- "_id": "Exit",
161
- "_tag": "Failure",
162
- "cause": {
163
- "_id": "Cause",
164
- "_tag": "Die",
165
- "defect": "Stream defect after 1: got 4"
166
- }
167
- }"
139
+ "Failure(Cause([Die("Stream defect after 1: got 4")]))"
168
140
  `)
169
141
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
170
142
  )
171
143
 
172
- Vitest.scopedLive('should handle stream interruption via HTTP', (test) =>
144
+ Vitest.live('should handle stream interruption via HTTP', (test) =>
173
145
  Effect.gen(function* () {
174
146
  const client = yield* RpcClient.make(TestRpcs)
175
147
  const stream = client.StreamInterruptible({ delay: 50, interruptAfterCount: 3 }).pipe(Stream.take(3))
176
- const chunks = yield* Stream.runCollect(stream)
177
- expect(Chunk.toReadonlyArray(chunks)).toEqual([1, 2, 3])
148
+ const result = yield* Stream.runCollect(stream)
149
+ expect(result).toEqual([1, 2, 3])
178
150
  }).pipe(Effect.provide(ProtocolLive), withWranglerTest(test)),
179
151
  )
180
152
 
181
153
  // TODO @IMax153
182
- Vitest.scopedLive.skip('should handle streaming RPC bug scenario', (test) =>
154
+ Vitest.live.skip('should handle streaming RPC bug scenario', (test) =>
183
155
  Effect.gen(function* () {
184
156
  const client = yield* RpcClient.make(TestRpcs)
185
157
  yield* client.StreamBugScenarioDoClient({})
@@ -223,7 +195,7 @@ Vitest.describe('Durable Object RPC', { timeout: testTimeout }, () => {
223
195
  errorSchema: [Function: Never],
224
196
  annotations: { _id: 'Context', services: [] },
225
197
  middlewares: Set(0) {},
226
- key: '@effect/rpc/Rpc/StreamBugScenarioDoServer'
198
+ key: 'effect/unstable/rpc/Rpc/StreamBugScenarioDoServer'
227
199
  } {}
228
200
 
229
201
  Killing wrangler process...