@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/cf-types.js +2 -0
- package/dist/cf-types.js.map +1 -1
- package/dist/declare/cf-declare.d.ts +32 -5
- package/dist/declare/cf-declare.d.ts.map +1 -1
- package/dist/declare/cf-declare.js +9 -10
- package/dist/declare/cf-declare.js.map +1 -1
- package/dist/do-rpc/client.d.ts +1 -1
- package/dist/do-rpc/client.d.ts.map +1 -1
- package/dist/do-rpc/client.js +23 -46
- package/dist/do-rpc/client.js.map +1 -1
- package/dist/do-rpc/client.test.d.ts +2 -0
- package/dist/do-rpc/client.test.d.ts.map +1 -0
- package/dist/do-rpc/client.test.js +99 -0
- package/dist/do-rpc/client.test.js.map +1 -0
- package/dist/do-rpc/rpc.test.js +27 -59
- package/dist/do-rpc/rpc.test.js.map +1 -1
- package/dist/do-rpc/server.d.ts +10 -5
- package/dist/do-rpc/server.d.ts.map +1 -1
- package/dist/do-rpc/server.js +104 -54
- package/dist/do-rpc/server.js.map +1 -1
- package/dist/do-rpc/test-fixtures/rpc-schema.d.ts +27 -27
- package/dist/do-rpc/test-fixtures/rpc-schema.js +11 -11
- package/dist/do-rpc/test-fixtures/worker.d.ts.map +1 -1
- package/dist/do-rpc/test-fixtures/worker.js +10 -10
- package/dist/do-rpc/test-fixtures/worker.js.map +1 -1
- package/dist/ws-rpc/test-fixtures/rpc-schema.d.ts +27 -27
- package/dist/ws-rpc/test-fixtures/rpc-schema.js +9 -9
- package/dist/ws-rpc/test-fixtures/worker.d.ts.map +1 -1
- package/dist/ws-rpc/test-fixtures/worker.js +2 -2
- package/dist/ws-rpc/test-fixtures/worker.js.map +1 -1
- package/dist/ws-rpc/ws-rpc-server.d.ts +47 -6
- package/dist/ws-rpc/ws-rpc-server.d.ts.map +1 -1
- package/dist/ws-rpc/ws-rpc-server.js +30 -20
- package/dist/ws-rpc/ws-rpc-server.js.map +1 -1
- package/dist/ws-rpc/ws-rpc.test.js +29 -61
- package/dist/ws-rpc/ws-rpc.test.js.map +1 -1
- package/package.json +40 -16
- package/src/cf-types.ts +2 -0
- package/src/declare/cf-declare.ts +18 -10
- package/src/do-rpc/README.md +1 -2
- package/src/do-rpc/client.test.ts +122 -0
- package/src/do-rpc/client.ts +30 -50
- package/src/do-rpc/rpc.test.ts +33 -61
- package/src/do-rpc/server.ts +157 -80
- package/src/do-rpc/test-fixtures/rpc-schema.ts +11 -11
- package/src/do-rpc/test-fixtures/worker.ts +13 -14
- package/src/ws-rpc/README.md +1 -1
- package/src/ws-rpc/test-fixtures/rpc-schema.ts +9 -9
- package/src/ws-rpc/test-fixtures/worker.ts +6 -4
- package/src/ws-rpc/ws-rpc-server.ts +69 -34
- package/src/ws-rpc/ws-rpc.test.ts +35 -63
- package/src/do-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite +0 -0
- package/src/do-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite-shm +0 -0
- package/src/do-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite-wal +0 -0
- package/src/ws-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite +0 -0
- package/src/ws-rpc/test-fixtures/.wrangler/state/v3/do/test-durable-object-rpc-TestRpcDurableObject/9ad3dfcb5436dfc3f2e14f5b554a0fd6d8b68206ad64fdde7320232d04e16dfe.sqlite-shm +0 -0
- 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
|
-
|
|
20
|
+
Context,
|
|
21
21
|
Effect,
|
|
22
|
+
Function,
|
|
22
23
|
Exit,
|
|
23
24
|
Layer,
|
|
24
25
|
Logger,
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
/**
|
|
48
|
-
|
|
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
|
|
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.
|
|
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*
|
|
166
|
+
const incomingQueue = yield* Queue.unbounded<Uint8Array | string>()
|
|
144
167
|
|
|
145
|
-
yield* Scope.addFinalizer(scope,
|
|
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
|
|
181
|
+
const services = yield* Effect.context()
|
|
158
182
|
|
|
159
183
|
const ctx = {
|
|
160
184
|
scope,
|
|
161
185
|
onMessage: (message: string | ArrayBuffer) =>
|
|
162
|
-
incomingQueue
|
|
163
|
-
.
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
178
|
-
Effect.provide(
|
|
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
|
-
/**
|
|
215
|
-
incomingQueue:
|
|
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.
|
|
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*
|
|
280
|
+
const disconnects = yield* Queue.unbounded<number>()
|
|
247
281
|
|
|
248
|
-
const writeRaw = (msg: Uint8Array
|
|
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.
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
25
|
+
WranglerDevServer.layer({
|
|
26
26
|
cwd: `${import.meta.dirname}/test-fixtures`,
|
|
27
27
|
}).pipe(
|
|
28
28
|
Layer.provide(
|
|
29
|
-
Layer.mergeAll(
|
|
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*
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
115
|
-
expect(
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
162
|
-
expect(
|
|
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.
|
|
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
|
|
224
|
-
expect(
|
|
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.
|
|
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
|
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|