@livestore/webmesh 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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/channel/direct-channel-internal.d.ts +2 -3
- package/dist/channel/direct-channel-internal.d.ts.map +1 -1
- package/dist/channel/direct-channel-internal.js +26 -21
- package/dist/channel/direct-channel-internal.js.map +1 -1
- package/dist/channel/direct-channel.d.ts.map +1 -1
- package/dist/channel/direct-channel.js +22 -23
- package/dist/channel/direct-channel.js.map +1 -1
- package/dist/channel/proxy-channel.d.ts +7 -8
- package/dist/channel/proxy-channel.d.ts.map +1 -1
- package/dist/channel/proxy-channel.js +28 -24
- package/dist/channel/proxy-channel.js.map +1 -1
- package/dist/common.d.ts +39 -41
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +3 -2
- package/dist/common.js.map +1 -1
- package/dist/mesh-schema.d.ts +262 -136
- package/dist/mesh-schema.d.ts.map +1 -1
- package/dist/mesh-schema.js +45 -44
- package/dist/mesh-schema.js.map +1 -1
- package/dist/mod.js +4 -4
- package/dist/node.d.ts +6 -6
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +27 -26
- package/dist/node.js.map +1 -1
- package/dist/node.test.js +80 -77
- package/dist/node.test.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/websocket-edge.d.ts +18 -16
- package/dist/websocket-edge.d.ts.map +1 -1
- package/dist/websocket-edge.js +32 -43
- package/dist/websocket-edge.js.map +1 -1
- package/dist/websocket-edge.test.js +17 -17
- package/dist/websocket-edge.test.js.map +1 -1
- package/dist/worker/mod.d.ts +24 -0
- package/dist/worker/mod.d.ts.map +1 -0
- package/dist/worker/mod.js +44 -0
- package/dist/worker/mod.js.map +1 -0
- package/dist/worker/schema.d.ts +10 -0
- package/dist/worker/schema.d.ts.map +1 -0
- package/dist/worker/schema.js +7 -0
- package/dist/worker/schema.js.map +1 -0
- package/package.json +35 -52
- package/src/channel/direct-channel-internal.ts +30 -22
- package/src/channel/direct-channel.ts +26 -23
- package/src/channel/proxy-channel.ts +39 -36
- package/src/common.ts +6 -3
- package/src/mesh-schema.ts +34 -33
- package/src/node.test.ts +104 -91
- package/src/node.ts +32 -32
- package/src/utils.ts +3 -3
- package/src/websocket-edge.test.ts +16 -16
- package/src/websocket-edge.ts +48 -49
- package/src/worker/mod.ts +89 -0
- package/src/worker/schema.ts +8 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { LS_DEV } from '@livestore/utils';
|
|
2
|
+
import { Context, Deferred, Effect, Layer, Queue, Stream, WebChannel } from '@livestore/utils/effect';
|
|
3
|
+
import * as WebmeshSchema from '../mesh-schema.js';
|
|
4
|
+
import { makeMeshNode } from '../node.js';
|
|
5
|
+
import * as WorkerSchema from './schema.js';
|
|
6
|
+
export * as Schema from './schema.js';
|
|
7
|
+
export class CacheService extends Context.Service()('@livestore/webmesh:worker:CacheService') {
|
|
8
|
+
static layer = ({ nodeName }) => Effect.gen(function* () {
|
|
9
|
+
const node = yield* makeMeshNode(nodeName);
|
|
10
|
+
globalThis.__debugWebmeshNode = node;
|
|
11
|
+
return CacheService.of({ node });
|
|
12
|
+
}).pipe(Layer.effect(CacheService));
|
|
13
|
+
}
|
|
14
|
+
export const CreateConnection = ({ from, port }) => Stream.callback((emit) => Effect.gen(function* () {
|
|
15
|
+
const { node } = yield* CacheService;
|
|
16
|
+
const messagePortChannel = yield* WebChannel.messagePortChannel({ port, schema: WebmeshSchema.Packet });
|
|
17
|
+
yield* node.addEdge({ target: from, edgeChannel: messagePortChannel, replaceIfExists: true });
|
|
18
|
+
if (LS_DEV === true) {
|
|
19
|
+
yield* Effect.logDebug(`@livestore/webmesh:worker: accepted edge: ${node.nodeName} <- ${from}`);
|
|
20
|
+
}
|
|
21
|
+
// `Stream.callback` exposes a queue-like emitter in Effect v4. Emit the single success value
|
|
22
|
+
// and then end the stream to preserve the old one-response worker command behavior.
|
|
23
|
+
yield* Queue.offer(emit, {});
|
|
24
|
+
yield* Queue.end(emit);
|
|
25
|
+
yield* Effect.spanEvent({ connectedTo: [...node.edgeKeys] });
|
|
26
|
+
}).pipe(Effect.orDie)).pipe(Stream.withSpan(`@livestore/webmesh:worker:create-connection:${from}`));
|
|
27
|
+
export const connectViaWorker = ({ node, target, worker, }) => Effect.gen(function* () {
|
|
28
|
+
const mc = new MessageChannel();
|
|
29
|
+
const isConnected = yield* Deferred.make();
|
|
30
|
+
if (LS_DEV === true) {
|
|
31
|
+
yield* Effect.addFinalizerLog(`@livestore/webmesh:worker: closing message channel ${node.nodeName} -> ${target}`);
|
|
32
|
+
}
|
|
33
|
+
yield* worker.execute(WorkerSchema.CreateConnection.make({ from: node.nodeName, port: mc.port1 })).pipe(Stream.tap(() => Deferred.succeed(isConnected, true)), Stream.runDrain, Effect.tapCauseLogPretty, Effect.forkScoped);
|
|
34
|
+
yield* Deferred.await(isConnected);
|
|
35
|
+
const workerConnection = yield* WebChannel.messagePortChannel({
|
|
36
|
+
port: mc.port2,
|
|
37
|
+
schema: WebmeshSchema.Packet,
|
|
38
|
+
});
|
|
39
|
+
yield* node.addEdge({ target, edgeChannel: workerConnection, replaceIfExists: true });
|
|
40
|
+
if (LS_DEV === true) {
|
|
41
|
+
yield* Effect.logDebug(`@livestore/webmesh:worker: initiated connection: ${node.nodeName} -> ${target}`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=mod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../../src/worker/mod.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Schema, Transferable } from '@livestore/utils/effect';
|
|
2
|
+
export declare const CreateConnection: Schema.TaggedStruct<"WebmeshWorker.CreateConnection", {
|
|
3
|
+
readonly from: Schema.String;
|
|
4
|
+
readonly port: Transferable.Transferable<Schema.declare<MessagePort, MessagePort>>;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const Request: Schema.Union<readonly [Schema.TaggedStruct<"WebmeshWorker.CreateConnection", {
|
|
7
|
+
readonly from: Schema.String;
|
|
8
|
+
readonly port: Transferable.Transferable<Schema.declare<MessagePort, MessagePort>>;
|
|
9
|
+
}>]>;
|
|
10
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/worker/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE9D,eAAO,MAAM,gBAAgB;;;EAG3B,CAAA;AAEF,eAAO,MAAM,OAAO;;;IAAmC,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Schema, Transferable } from '@livestore/utils/effect';
|
|
2
|
+
export const CreateConnection = Schema.TaggedStruct('WebmeshWorker.CreateConnection', {
|
|
3
|
+
from: Schema.String,
|
|
4
|
+
port: Transferable.MessagePort,
|
|
5
|
+
});
|
|
6
|
+
export const Request = Schema.Union([CreateConnection]);
|
|
7
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/worker/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE9D,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,YAAY,CAAC,gCAAgC,EAAE;IACpF,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,IAAI,EAAE,YAAY,CAAC,WAAW;CAC/B,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,79 +1,62 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$genie": {
|
|
3
|
+
"source": "package.json.genie.ts",
|
|
4
|
+
"warning": "DO NOT EDIT - changes will be overwritten",
|
|
5
|
+
"workspaceClosureDirs": [
|
|
6
|
+
"packages/@livestore/utils",
|
|
7
|
+
"packages/@livestore/utils-dev",
|
|
8
|
+
"packages/@livestore/webmesh"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
2
11
|
"name": "@livestore/webmesh",
|
|
3
|
-
"version": "0.
|
|
12
|
+
"version": "0.5.0-dev.1",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": false,
|
|
4
15
|
"license": "Apache-2.0",
|
|
5
16
|
"repository": {
|
|
6
17
|
"type": "git",
|
|
7
18
|
"url": "git+https://github.com/livestorejs/livestore.git"
|
|
8
19
|
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./dist/mod.js",
|
|
22
|
+
"./worker": "./dist/worker/mod.js"
|
|
23
|
+
},
|
|
9
24
|
"files": [
|
|
10
25
|
"dist",
|
|
11
26
|
"package.json",
|
|
12
27
|
"src"
|
|
13
28
|
],
|
|
14
|
-
"type": "module",
|
|
15
|
-
"sideEffects": false,
|
|
16
|
-
"exports": {
|
|
17
|
-
".": "./dist/mod.js"
|
|
18
|
-
},
|
|
19
|
-
"publishConfig": {
|
|
20
|
-
"access": "public"
|
|
21
|
-
},
|
|
22
29
|
"dependencies": {
|
|
23
|
-
"@livestore/utils": "^0.
|
|
30
|
+
"@livestore/utils": "^0.5.0-dev.1"
|
|
24
31
|
},
|
|
25
32
|
"devDependencies": {
|
|
26
|
-
"@effect/
|
|
27
|
-
"@effect/
|
|
28
|
-
"@effect/
|
|
29
|
-
"@effect/
|
|
30
|
-
"@effect/
|
|
31
|
-
"@effect/
|
|
32
|
-
"@
|
|
33
|
-
"@effect/platform-bun": "0.89.0",
|
|
34
|
-
"@effect/platform-node": "0.106.0",
|
|
35
|
-
"@effect/printer": "0.49.0",
|
|
36
|
-
"@effect/printer-ansi": "0.49.0",
|
|
37
|
-
"@effect/rpc": "0.75.1",
|
|
38
|
-
"@effect/sql": "0.51.1",
|
|
39
|
-
"@effect/typeclass": "0.40.0",
|
|
40
|
-
"@effect/vitest": "0.29.0",
|
|
33
|
+
"@effect/opentelemetry": "4.0.3",
|
|
34
|
+
"@effect/platform-browser": "4.0.3",
|
|
35
|
+
"@effect/platform-bun": "4.0.3",
|
|
36
|
+
"@effect/platform-node": "4.0.3",
|
|
37
|
+
"@effect/platform-node-shared": "4.0.3",
|
|
38
|
+
"@effect/vitest": "4.0.3",
|
|
39
|
+
"@livestore/utils-dev": "^0.5.0-dev.1",
|
|
41
40
|
"@opentelemetry/api": "1.9.0",
|
|
42
41
|
"@opentelemetry/resources": "2.2.0",
|
|
43
42
|
"@standard-schema/spec": "1.1.0",
|
|
44
|
-
"effect": "
|
|
45
|
-
"vitest": "
|
|
46
|
-
"@livestore/utils-dev": "^0.4.0"
|
|
43
|
+
"effect": "4.0.3",
|
|
44
|
+
"vitest": "4.1.9"
|
|
47
45
|
},
|
|
48
46
|
"peerDependencies": {
|
|
49
|
-
"@effect/
|
|
50
|
-
"@effect/
|
|
51
|
-
"@effect/
|
|
52
|
-
"@effect/
|
|
53
|
-
"@effect/
|
|
54
|
-
"@effect/
|
|
55
|
-
"@effect/platform-browser": "^0.76.0",
|
|
56
|
-
"@effect/platform-bun": "^0.89.0",
|
|
57
|
-
"@effect/platform-node": "^0.106.0",
|
|
58
|
-
"@effect/printer": "^0.49.0",
|
|
59
|
-
"@effect/printer-ansi": "^0.49.0",
|
|
60
|
-
"@effect/rpc": "^0.75.1",
|
|
61
|
-
"@effect/sql": "^0.51.1",
|
|
62
|
-
"@effect/typeclass": "^0.40.0",
|
|
63
|
-
"@effect/vitest": "^0.29.0",
|
|
47
|
+
"@effect/opentelemetry": "^4.0.3",
|
|
48
|
+
"@effect/platform-browser": "^4.0.3",
|
|
49
|
+
"@effect/platform-bun": "^4.0.3",
|
|
50
|
+
"@effect/platform-node": "^4.0.3",
|
|
51
|
+
"@effect/platform-node-shared": "^4.0.3",
|
|
52
|
+
"@effect/vitest": "^4.0.3",
|
|
64
53
|
"@opentelemetry/api": "^1.9.0",
|
|
65
54
|
"@opentelemetry/resources": "^2.2.0",
|
|
66
55
|
"@standard-schema/spec": "^1.1.0",
|
|
67
|
-
"effect": "^
|
|
56
|
+
"effect": "^4.0.3"
|
|
68
57
|
},
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"warning": "DO NOT EDIT - changes will be overwritten",
|
|
72
|
-
"workspaceClosureDirs": [
|
|
73
|
-
"packages/@livestore/utils",
|
|
74
|
-
"packages/@livestore/utils-dev",
|
|
75
|
-
"packages/@livestore/webmesh"
|
|
76
|
-
]
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
77
60
|
},
|
|
78
61
|
"scripts": {
|
|
79
62
|
"test": "vitest"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { casesHandled, shouldNeverHappen } from '@livestore/utils'
|
|
2
|
-
import type { PubSub } from '@livestore/utils/effect'
|
|
3
2
|
import {
|
|
3
|
+
type PubSub,
|
|
4
4
|
Deferred,
|
|
5
5
|
Effect,
|
|
6
6
|
Exit,
|
|
7
|
+
Fiber,
|
|
7
8
|
Predicate,
|
|
8
9
|
Queue,
|
|
9
10
|
Schema,
|
|
@@ -52,7 +53,7 @@ export const makeDirectChannelInternal = ({
|
|
|
52
53
|
}: MakeDirectChannelArgs & {
|
|
53
54
|
channelVersion: number
|
|
54
55
|
/** We're passing in the closeable scope from the wrapping direct channel */
|
|
55
|
-
scope: Scope.
|
|
56
|
+
scope: Scope.Closeable
|
|
56
57
|
sourceId: string
|
|
57
58
|
}): Effect.Effect<
|
|
58
59
|
WebChannel.WebChannel<any, any>,
|
|
@@ -89,8 +90,8 @@ export const makeDirectChannelInternal = ({
|
|
|
89
90
|
const deferred = yield* makeDeferredResult()
|
|
90
91
|
|
|
91
92
|
const schema = {
|
|
92
|
-
send: Schema.Union(schema_.send, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong),
|
|
93
|
-
listen: Schema.Union(schema_.listen, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong),
|
|
93
|
+
send: Schema.Union([schema_.send, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong]),
|
|
94
|
+
listen: Schema.Union([schema_.listen, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong]),
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
const channelStateRef: { current: ChannelState } = {
|
|
@@ -105,7 +106,9 @@ export const makeDirectChannelInternal = ({
|
|
|
105
106
|
channelState: channelState._tag,
|
|
106
107
|
packetId: packet.id,
|
|
107
108
|
packetReqId: packet.reqId,
|
|
108
|
-
...(Predicate.hasProperty('channelVersion')(packet) === true
|
|
109
|
+
...(Predicate.hasProperty('channelVersion')(packet) === true
|
|
110
|
+
? { packetChannelVersion: packet.channelVersion }
|
|
111
|
+
: {}),
|
|
109
112
|
})
|
|
110
113
|
|
|
111
114
|
// const reqIdStr =
|
|
@@ -185,7 +188,9 @@ export const makeDirectChannelInternal = ({
|
|
|
185
188
|
remainingHops: packet.hops,
|
|
186
189
|
reqId: packet.id,
|
|
187
190
|
})
|
|
188
|
-
yield* Effect.spanEvent(
|
|
191
|
+
yield* Effect.spanEvent(
|
|
192
|
+
`Re-sending new request (${newRequestPacket.id}) for incoming request (${packet.id})`,
|
|
193
|
+
)
|
|
189
194
|
|
|
190
195
|
yield* sendPacket(newRequestPacket)
|
|
191
196
|
}
|
|
@@ -223,7 +228,7 @@ export const makeDirectChannelInternal = ({
|
|
|
223
228
|
|
|
224
229
|
// Now we wait for the other side to respond via the channel
|
|
225
230
|
yield* channel.listen.pipe(
|
|
226
|
-
Stream.
|
|
231
|
+
Stream.mapEffect(Effect.fromResult),
|
|
227
232
|
Stream.filter(Schema.is(MeshSchema.DirectChannelPing)),
|
|
228
233
|
Stream.take(1),
|
|
229
234
|
Stream.runDrain,
|
|
@@ -258,11 +263,11 @@ export const makeDirectChannelInternal = ({
|
|
|
258
263
|
}).pipe(Effect.andThen(WebChannel.toOpenChannel))
|
|
259
264
|
|
|
260
265
|
const waitForPongFiber = yield* channel.listen.pipe(
|
|
261
|
-
Stream.
|
|
266
|
+
Stream.mapEffect(Effect.fromResult),
|
|
262
267
|
Stream.filter(Schema.is(MeshSchema.DirectChannelPong)),
|
|
263
268
|
Stream.take(1),
|
|
264
269
|
Stream.runDrain,
|
|
265
|
-
Effect.
|
|
270
|
+
Effect.forkChild,
|
|
266
271
|
)
|
|
267
272
|
|
|
268
273
|
// There seems to be some scenario where the initial ping message is lost.
|
|
@@ -273,7 +278,7 @@ export const makeDirectChannelInternal = ({
|
|
|
273
278
|
.send(MeshSchema.DirectChannelPing.make({}))
|
|
274
279
|
.pipe(Effect.timeout(10), Effect.retry({ times: 2 }))
|
|
275
280
|
|
|
276
|
-
yield* waitForPongFiber
|
|
281
|
+
yield* Fiber.join(waitForPongFiber)
|
|
277
282
|
|
|
278
283
|
yield* Effect.spanEvent(`loser side: established`)
|
|
279
284
|
channelStateRef.current = { _tag: 'Established', otherSourceId: channelState.otherSourceId }
|
|
@@ -292,17 +297,6 @@ export const makeDirectChannelInternal = ({
|
|
|
292
297
|
}),
|
|
293
298
|
)
|
|
294
299
|
|
|
295
|
-
yield* Effect.gen(function* () {
|
|
296
|
-
while (true) {
|
|
297
|
-
const packet = yield* Queue.take(incomingPacketsQueue)
|
|
298
|
-
const res = yield* processMessagePacket(packet)
|
|
299
|
-
// We want to give requests another chance to be processed
|
|
300
|
-
if (res === 'close') {
|
|
301
|
-
return
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}).pipe(Effect.interruptible, Effect.tapCauseLogPretty, Effect.forkScoped)
|
|
305
|
-
|
|
306
300
|
const channelState = channelStateRef.current
|
|
307
301
|
|
|
308
302
|
if (channelState._tag !== 'Initial') {
|
|
@@ -336,7 +330,21 @@ export const makeDirectChannelInternal = ({
|
|
|
336
330
|
|
|
337
331
|
yield* edgeRequest
|
|
338
332
|
|
|
339
|
-
|
|
333
|
+
// Start draining only after `edgeRequest` moved the channel to `RequestSent`. Under Effect v4
|
|
334
|
+
// an eagerly forked processor can otherwise handle an already-buffered packet while still
|
|
335
|
+
// `Initial`.
|
|
336
|
+
yield* Effect.gen(function* () {
|
|
337
|
+
while (true) {
|
|
338
|
+
const packet = yield* Queue.take(incomingPacketsQueue)
|
|
339
|
+
const res = yield* processMessagePacket(packet)
|
|
340
|
+
// We want to give requests another chance to be processed
|
|
341
|
+
if (res === 'close') {
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}).pipe(Effect.interruptible, Effect.tapCauseLogPretty, Effect.forkScoped)
|
|
346
|
+
|
|
347
|
+
const channel = yield* Deferred.await(deferred)
|
|
340
348
|
|
|
341
349
|
return channel
|
|
342
350
|
}).pipe(Effect.withSpanScoped(`makeDirectChannel:${channelVersion}`))
|
|
@@ -2,14 +2,16 @@ import {
|
|
|
2
2
|
Cause,
|
|
3
3
|
Deferred,
|
|
4
4
|
Effect,
|
|
5
|
-
|
|
5
|
+
Fiber,
|
|
6
|
+
Result,
|
|
6
7
|
Exit,
|
|
7
8
|
Option,
|
|
8
9
|
Queue,
|
|
10
|
+
References,
|
|
9
11
|
Schema,
|
|
10
12
|
Scope,
|
|
11
13
|
Stream,
|
|
12
|
-
|
|
14
|
+
TxQueue,
|
|
13
15
|
WebChannel,
|
|
14
16
|
} from '@livestore/utils/effect'
|
|
15
17
|
import { nanoid } from '@livestore/utils/nanoid'
|
|
@@ -49,7 +51,7 @@ export const makeDirectChannel = ({
|
|
|
49
51
|
const sourceId = nanoid()
|
|
50
52
|
|
|
51
53
|
const listenQueue = yield* Queue.unbounded<any>()
|
|
52
|
-
const sendQueue = yield*
|
|
54
|
+
const sendQueue = yield* TxQueue.unbounded<[msg: any, deferred: Deferred.Deferred<void>]>()
|
|
53
55
|
|
|
54
56
|
const initialEdgeDeferred = yield* Deferred.make<void>()
|
|
55
57
|
|
|
@@ -66,7 +68,7 @@ export const makeDirectChannel = ({
|
|
|
66
68
|
const resultDeferred = yield* Deferred.make<{
|
|
67
69
|
channel: WebChannel.WebChannel<any, any>
|
|
68
70
|
channelVersion: number
|
|
69
|
-
makeDirectChannelScope: Scope.
|
|
71
|
+
makeDirectChannelScope: Scope.Closeable
|
|
70
72
|
}>()
|
|
71
73
|
|
|
72
74
|
while (true) {
|
|
@@ -99,7 +101,7 @@ export const makeDirectChannel = ({
|
|
|
99
101
|
Stream.take(1),
|
|
100
102
|
Stream.runDrain,
|
|
101
103
|
Effect.as('new-edge' as const),
|
|
102
|
-
Effect.
|
|
104
|
+
Effect.forkChild,
|
|
103
105
|
)
|
|
104
106
|
|
|
105
107
|
const makeChannel = makeDirectChannelInternal({
|
|
@@ -115,29 +117,30 @@ export const makeDirectChannel = ({
|
|
|
115
117
|
sendPacket,
|
|
116
118
|
scope: makeDirectChannelScope,
|
|
117
119
|
}).pipe(
|
|
118
|
-
Scope.
|
|
120
|
+
Scope.provide(makeDirectChannelScope),
|
|
119
121
|
Effect.forkIn(makeDirectChannelScope),
|
|
120
|
-
// Given we only
|
|
122
|
+
// Given we only await the exit later when observing the fiber,
|
|
121
123
|
// we don't want Effect to produce a "unhandled error" log message
|
|
122
|
-
Effect.
|
|
124
|
+
Effect.provideService(References.UnhandledLogLevel, undefined),
|
|
123
125
|
)
|
|
124
126
|
|
|
125
|
-
const raceResult = yield* Effect.raceFirst(makeChannel,
|
|
127
|
+
const raceResult = yield* Effect.raceFirst(makeChannel, Fiber.join(waitForNewEdgeFiber))
|
|
126
128
|
|
|
127
129
|
if (raceResult === 'new-edge') {
|
|
128
130
|
yield* Scope.close(makeDirectChannelScope, Exit.fail('new-edge'))
|
|
129
131
|
// We'll try again
|
|
130
132
|
} else {
|
|
131
|
-
const channelExit = yield*
|
|
133
|
+
const channelExit = yield* Fiber.await(raceResult)
|
|
132
134
|
if (channelExit._tag === 'Failure') {
|
|
133
135
|
yield* Scope.close(makeDirectChannelScope, channelExit)
|
|
134
136
|
|
|
135
137
|
if (
|
|
136
|
-
Cause.
|
|
137
|
-
|
|
138
|
+
Option.exists(Cause.findErrorOption(channelExit.cause), (error) =>
|
|
139
|
+
Schema.is(WebmeshSchema.DirectChannelResponseNoTransferables)(error),
|
|
140
|
+
) === true
|
|
138
141
|
) {
|
|
139
142
|
// Only retry when there is a new edge available
|
|
140
|
-
yield*
|
|
143
|
+
yield* Effect.exit(Fiber.join(waitForNewEdgeFiber))
|
|
141
144
|
}
|
|
142
145
|
} else {
|
|
143
146
|
const channel = channelExit.value
|
|
@@ -149,7 +152,7 @@ export const makeDirectChannel = ({
|
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
// Now we wait until the first channel is established
|
|
152
|
-
const { channel, makeDirectChannelScope, channelVersion } = yield* resultDeferred
|
|
155
|
+
const { channel, makeDirectChannelScope, channelVersion } = yield* Deferred.await(resultDeferred)
|
|
153
156
|
|
|
154
157
|
yield* Effect.spanEvent(`Connected#${channelVersion}`)
|
|
155
158
|
debugInfo.isConnected = true
|
|
@@ -159,9 +162,9 @@ export const makeDirectChannel = ({
|
|
|
159
162
|
|
|
160
163
|
// We'll now forward all incoming messages to the listen queue
|
|
161
164
|
yield* channel.listen.pipe(
|
|
162
|
-
Stream.
|
|
165
|
+
Stream.mapEffect(Effect.fromResult),
|
|
163
166
|
// Stream.tap((msg) => Effect.log(`${target}→${channelName}→${nodeName}:message:${msg.message}`)),
|
|
164
|
-
Stream.
|
|
167
|
+
Stream.tapArray((array) => Queue.offerAll(listenQueue, array)),
|
|
165
168
|
Stream.runDrain,
|
|
166
169
|
Effect.tapCauseLogPretty,
|
|
167
170
|
Effect.forkIn(makeDirectChannelScope),
|
|
@@ -169,18 +172,18 @@ export const makeDirectChannel = ({
|
|
|
169
172
|
|
|
170
173
|
yield* Effect.gen(function* () {
|
|
171
174
|
while (true) {
|
|
172
|
-
const [msg, deferred] = yield*
|
|
175
|
+
const [msg, deferred] = yield* TxQueue.peek(sendQueue)
|
|
173
176
|
// NOTE we don't need an explicit retry flow here since in case of the channel being closed,
|
|
174
177
|
// the send will never succeed. Meanwhile the send-loop fiber will be interrupted and
|
|
175
178
|
// given we only peeked at the queue, the message to send is still there.
|
|
176
179
|
yield* channel.send(msg)
|
|
177
180
|
yield* Deferred.succeed(deferred, void 0)
|
|
178
|
-
yield*
|
|
181
|
+
yield* TxQueue.take(sendQueue) // Remove the message from the queue
|
|
179
182
|
}
|
|
180
183
|
}).pipe(Effect.forkIn(makeDirectChannelScope))
|
|
181
184
|
|
|
182
185
|
// Wait until the channel is closed and then try to reconnect
|
|
183
|
-
yield* channel.closedDeferred
|
|
186
|
+
yield* Deferred.await(channel.closedDeferred)
|
|
184
187
|
|
|
185
188
|
yield* Scope.close(makeDirectChannelScope, Exit.succeed('channel-closed'))
|
|
186
189
|
|
|
@@ -204,16 +207,16 @@ export const makeDirectChannel = ({
|
|
|
204
207
|
debugInfo.pendingSends++
|
|
205
208
|
debugInfo.totalSends++
|
|
206
209
|
|
|
207
|
-
yield*
|
|
210
|
+
yield* TxQueue.offer(sendQueue, [message, sentDeferred])
|
|
208
211
|
|
|
209
|
-
yield* sentDeferred
|
|
212
|
+
yield* Deferred.await(sentDeferred)
|
|
210
213
|
|
|
211
214
|
debugInfo.pendingSends--
|
|
212
215
|
}).pipe(Effect.scoped, Effect.withParentSpan(parentSpan))
|
|
213
216
|
|
|
214
|
-
const listen = Stream.fromQueue(listenQueue
|
|
217
|
+
const listen = Stream.fromQueue(listenQueue).pipe(Stream.rechunk(1), Stream.map(Result.succeed))
|
|
215
218
|
|
|
216
|
-
const closedDeferred = yield* Deferred.make<void>()
|
|
219
|
+
const closedDeferred = yield* Effect.acquireRelease(Deferred.make<void>(), Deferred.done(Exit.void))
|
|
217
220
|
|
|
218
221
|
const webChannel = {
|
|
219
222
|
[WebChannel.WebChannelSymbol]: WebChannel.WebChannelSymbol,
|