@livestore/webmesh 0.0.0-snapshot-1d99fea7d2ce2c7a5d9ed0a3752f8a7bda6bc3db → 0.0.0-snapshot-aed277ba0960f72b8d464508961ab4aec1881230

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 (43) hide show
  1. package/README.md +19 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/channel/message-channel-internal.d.ts +26 -0
  4. package/dist/channel/message-channel-internal.d.ts.map +1 -0
  5. package/dist/channel/message-channel-internal.js +202 -0
  6. package/dist/channel/message-channel-internal.js.map +1 -0
  7. package/dist/channel/message-channel.d.ts +21 -19
  8. package/dist/channel/message-channel.d.ts.map +1 -1
  9. package/dist/channel/message-channel.js +125 -162
  10. package/dist/channel/message-channel.js.map +1 -1
  11. package/dist/channel/proxy-channel.d.ts +2 -2
  12. package/dist/channel/proxy-channel.d.ts.map +1 -1
  13. package/dist/channel/proxy-channel.js +7 -5
  14. package/dist/channel/proxy-channel.js.map +1 -1
  15. package/dist/common.d.ts +8 -4
  16. package/dist/common.d.ts.map +1 -1
  17. package/dist/common.js +2 -1
  18. package/dist/common.js.map +1 -1
  19. package/dist/mesh-schema.d.ts +23 -1
  20. package/dist/mesh-schema.d.ts.map +1 -1
  21. package/dist/mesh-schema.js +21 -2
  22. package/dist/mesh-schema.js.map +1 -1
  23. package/dist/node.d.ts +12 -1
  24. package/dist/node.d.ts.map +1 -1
  25. package/dist/node.js +39 -9
  26. package/dist/node.js.map +1 -1
  27. package/dist/node.test.d.ts +1 -1
  28. package/dist/node.test.d.ts.map +1 -1
  29. package/dist/node.test.js +256 -124
  30. package/dist/node.test.js.map +1 -1
  31. package/dist/websocket-connection.d.ts +1 -2
  32. package/dist/websocket-connection.d.ts.map +1 -1
  33. package/dist/websocket-connection.js +5 -4
  34. package/dist/websocket-connection.js.map +1 -1
  35. package/package.json +3 -3
  36. package/src/channel/message-channel-internal.ts +337 -0
  37. package/src/channel/message-channel.ts +177 -308
  38. package/src/channel/proxy-channel.ts +238 -230
  39. package/src/common.ts +3 -1
  40. package/src/mesh-schema.ts +20 -2
  41. package/src/node.test.ts +367 -150
  42. package/src/node.ts +68 -12
  43. package/src/websocket-connection.ts +83 -79
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-connection.d.ts","sourceRoot":"","sources":["../src/websocket-connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAEL,MAAM,EAIN,QAAQ,EACR,MAAM,EAEN,UAAU,EAEX,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,aAAa,MAAM,IAAI,CAAA;AAEnC,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;;;;AAEzC,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG;;;;;AAEL,qBAAa,mBAAoB,SAAQ,wBAGvC;CAAG;;AAEL,qBAAa,mBAAoB,SAAQ,wBAAmD;CAAG;AAE/F,eAAO,MAAM,cAAc;;;;;;;;;;;;;;UAAsC,CAAA;AAEjE,MAAM,MAAM,UAAU,GAClB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,GACD;IACE,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AAEL,eAAO,MAAM,mBAAmB,8BAI7B;IACD,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,KAAK,CAAA;CAC/C,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAkBtC,CAAA;AAEJ,eAAO,MAAM,uBAAuB,WAC1B,UAAU,CAAC,SAAS,GAAG,aAAa,CAAC,SAAS,cAC1C,UAAU,KACrB,MAAM,CAAC,MAAM,CACd;IACE,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC/F,IAAI,EAAE,MAAM,CAAA;CACb,EACD,KAAK,EACL,KAAK,CAAC,KAAK,CAgFT,CAAA"}
1
+ {"version":3,"file":"websocket-connection.d.ts","sourceRoot":"","sources":["../src/websocket-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAKN,QAAQ,EACR,MAAM,EACN,KAAK,EAEL,UAAU,EAEX,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,aAAa,MAAM,IAAI,CAAA;AAEnC,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;;;;AAEzC,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG;;;;;AAEL,qBAAa,mBAAoB,SAAQ,wBAGvC;CAAG;;AAEL,qBAAa,mBAAoB,SAAQ,wBAAmD;CAAG;AAE/F,eAAO,MAAM,cAAc;;;;;;;;;;;;;;UAAsC,CAAA;AAEjE,MAAM,MAAM,UAAU,GAClB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,GACD;IACE,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AAEL,eAAO,MAAM,mBAAmB,8BAI7B;IACD,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,KAAK,CAAA;CAC/C,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAkBtC,CAAA;AAEJ,eAAO,MAAM,uBAAuB,WAC1B,UAAU,CAAC,SAAS,GAAG,aAAa,CAAC,SAAS,cAC1C,UAAU,KACrB,MAAM,CAAC,MAAM,CACd;IACE,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC/F,IAAI,EAAE,MAAM,CAAA;CACb,EACD,KAAK,EACL,KAAK,CAAC,KAAK,CAmFV,CAAA"}
@@ -1,4 +1,4 @@
1
- import { Deferred, Effect, Either, FiberHandle, Queue, Schedule, Schema, Stream, WebChannel, WebSocket, } from '@livestore/utils/effect';
1
+ import { Deferred, Effect, Either, Exit, FiberHandle, Queue, Schedule, Schema, Scope, Stream, WebChannel, WebSocket, } from '@livestore/utils/effect';
2
2
  import * as MeshSchema from './mesh-schema.js';
3
3
  export class WSConnectionInit extends Schema.TaggedStruct('WSConnectionInit', {
4
4
  from: Schema.String,
@@ -24,7 +24,7 @@ export const connectViaWebSocket = ({ node, url, reconnect = Schedule.exponentia
24
24
  }).pipe(Effect.scoped, Effect.withSpan('@livestore/webmesh:websocket-connection:connect'));
25
25
  yield* FiberHandle.run(fiberHandle, connect);
26
26
  });
27
- export const makeWebSocketConnection = (socket, socketType) => Effect.gen(function* () {
27
+ export const makeWebSocketConnection = (socket, socketType) => Effect.scopeWithCloseable((scope) => Effect.gen(function* () {
28
28
  socket.binaryType = 'arraybuffer';
29
29
  const fromDeferred = yield* Deferred.make();
30
30
  yield* Stream.fromEventListener(socket, 'message').pipe(Stream.map((msg) => Schema.decodeUnknownEither(MessageMsgPack)(new Uint8Array(msg.data))), Stream.flatten(), Stream.tap((msg) => Effect.gen(function* () {
@@ -47,7 +47,7 @@ export const makeWebSocketConnection = (socket, socketType) => Effect.gen(functi
47
47
  initHandshake(from);
48
48
  }
49
49
  const isConnectedLatch = yield* Effect.makeLatch(true);
50
- const closedDeferred = yield* Deferred.make();
50
+ const closedDeferred = yield* Deferred.make().pipe(Effect.acquireRelease(Deferred.done(Exit.void)));
51
51
  yield* Effect.eventListener(socket, 'close', () => Effect.gen(function* () {
52
52
  yield* isConnectedLatch.close;
53
53
  yield* Deferred.succeed(closedDeferred, undefined);
@@ -65,10 +65,11 @@ export const makeWebSocketConnection = (socket, socketType) => Effect.gen(functi
65
65
  closedDeferred,
66
66
  schema: { listen: MeshSchema.Packet, send: MeshSchema.Packet },
67
67
  supportsTransferables: false,
68
+ shutdown: Scope.close(scope, Exit.void),
68
69
  };
69
70
  return {
70
71
  webChannel: webChannel,
71
72
  from,
72
73
  };
73
- });
74
+ }).pipe(Effect.withSpanScoped('makeWebSocketConnection')));
74
75
  //# sourceMappingURL=websocket-connection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-connection.js","sourceRoot":"","sources":["../src/websocket-connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"websocket-connection.js","sourceRoot":"","sources":["../src/websocket-connection.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/webmesh",
3
- "version": "0.0.0-snapshot-1d99fea7d2ce2c7a5d9ed0a3752f8a7bda6bc3db",
3
+ "version": "0.0.0-snapshot-aed277ba0960f72b8d464508961ab4aec1881230",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -16,8 +16,8 @@
16
16
  "types": "./dist/mod.d.ts",
17
17
  "dependencies": {
18
18
  "ws": "8.18.0",
19
- "@livestore/common": "0.0.0-snapshot-1d99fea7d2ce2c7a5d9ed0a3752f8a7bda6bc3db",
20
- "@livestore/utils": "0.0.0-snapshot-1d99fea7d2ce2c7a5d9ed0a3752f8a7bda6bc3db"
19
+ "@livestore/common": "0.0.0-snapshot-aed277ba0960f72b8d464508961ab4aec1881230",
20
+ "@livestore/utils": "0.0.0-snapshot-aed277ba0960f72b8d464508961ab4aec1881230"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/ws": "^8.5.12",
@@ -0,0 +1,337 @@
1
+ import { casesHandled, shouldNeverHappen } from '@livestore/utils'
2
+ import type { PubSub } from '@livestore/utils/effect'
3
+ import {
4
+ Deferred,
5
+ Effect,
6
+ Exit,
7
+ OtelTracer,
8
+ Predicate,
9
+ Queue,
10
+ Schema,
11
+ Scope,
12
+ Stream,
13
+ WebChannel,
14
+ } from '@livestore/utils/effect'
15
+
16
+ import { type ChannelName, type MeshNodeName, type MessageQueueItem, packetAsOtelAttributes } from '../common.js'
17
+ import * as MeshSchema from '../mesh-schema.js'
18
+
19
+ export interface MakeMessageChannelArgs {
20
+ nodeName: MeshNodeName
21
+ /** Queue of incoming messages for this channel */
22
+ incomingPacketsQueue: Queue.Queue<MessageQueueItem>
23
+ newConnectionAvailablePubSub: PubSub.PubSub<MeshNodeName>
24
+ channelName: ChannelName
25
+ target: MeshNodeName
26
+ sendPacket: (packet: typeof MeshSchema.MessageChannelPacket.Type) => Effect.Effect<void>
27
+ checkTransferableConnections: (
28
+ packet: typeof MeshSchema.MessageChannelPacket.Type,
29
+ ) => typeof MeshSchema.MessageChannelResponseNoTransferables.Type | undefined
30
+ schema: WebChannel.OutputSchema<any, any, any, any>
31
+ }
32
+
33
+ const makeDeferredResult = Deferred.make<
34
+ WebChannel.WebChannel<any, any>,
35
+ typeof MeshSchema.MessageChannelResponseNoTransferables.Type
36
+ >
37
+
38
+ /**
39
+ * The channel version is important here, as a channel will only be established once both sides have the same version.
40
+ * The version is used to avoid concurrency issues where both sides have different incompatible message ports.
41
+ */
42
+ export const makeMessageChannelInternal = ({
43
+ nodeName,
44
+ incomingPacketsQueue,
45
+ target,
46
+ checkTransferableConnections,
47
+ channelName,
48
+ schema: schema_,
49
+ sendPacket,
50
+ channelVersion,
51
+ scope,
52
+ sourceId,
53
+ }: MakeMessageChannelArgs & {
54
+ channelVersion: number
55
+ /** We're passing in the closeable scope from the wrapping message channel */
56
+ scope: Scope.CloseableScope
57
+ sourceId: string
58
+ }): Effect.Effect<
59
+ WebChannel.WebChannel<any, any>,
60
+ typeof MeshSchema.MessageChannelResponseNoTransferables.Type,
61
+ Scope.Scope
62
+ > =>
63
+ Effect.gen(function* () {
64
+ // yield* Effect.addFinalizer((exit) =>
65
+ // Effect.spanEvent(`shutdown:${exit._tag === 'Success' ? 'Success' : Cause.pretty(exit.cause)}`),
66
+ // )
67
+
68
+ type ChannelState =
69
+ | {
70
+ _tag: 'Initial'
71
+ }
72
+ | {
73
+ _tag: 'RequestSent'
74
+ reqPacketId: string
75
+ }
76
+ | {
77
+ _tag: 'winner:ResponseSent'
78
+ channel: WebChannel.WebChannel<any, any>
79
+ otherSourceId: string
80
+ }
81
+ | {
82
+ _tag: 'loser:WaitingForResponse'
83
+ otherSourceId: string
84
+ }
85
+ | {
86
+ _tag: 'Established'
87
+ otherSourceId: string
88
+ }
89
+
90
+ const deferred = yield* makeDeferredResult()
91
+
92
+ const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.catchAll(() => Effect.succeed(undefined)))
93
+
94
+ const schema = {
95
+ send: Schema.Union(schema_.send, MeshSchema.MessageChannelPing, MeshSchema.MessageChannelPong),
96
+ listen: Schema.Union(schema_.listen, MeshSchema.MessageChannelPing, MeshSchema.MessageChannelPong),
97
+ }
98
+
99
+ const channelStateRef: { current: ChannelState } = {
100
+ current: { _tag: 'Initial' },
101
+ }
102
+
103
+ const processMessagePacket = ({ packet, respondToSender }: MessageQueueItem) =>
104
+ Effect.gen(function* () {
105
+ const channelState = channelStateRef.current
106
+
107
+ span?.addEvent(`process:${packet._tag}`, {
108
+ channelState: channelState._tag,
109
+ packetId: packet.id,
110
+ packetReqId: packet.reqId,
111
+ packetChannelVersion: Predicate.hasProperty('channelVersion')(packet) ? packet.channelVersion : undefined,
112
+ })
113
+
114
+ // yield* Effect.log(
115
+ // `${nodeName}→${channelName}→${target}:process packet ${packet._tag} [${channelVersion}], channel state: ${channelState._tag}`,
116
+ // )
117
+
118
+ if (channelState._tag === 'Initial') return shouldNeverHappen()
119
+
120
+ if (packet._tag === 'MessageChannelResponseNoTransferables') {
121
+ yield* Deferred.fail(deferred, packet)
122
+ return 'close'
123
+ }
124
+
125
+ // If the other side has a higher version, we need to close this channel and
126
+ // recreate it with the new version
127
+ if (packet.channelVersion > channelVersion) {
128
+ span?.addEvent(`incoming packet has higher version (${packet.channelVersion}), closing channel`)
129
+ yield* Scope.close(scope, Exit.succeed('higher-version-expected'))
130
+ // TODO include expected version in the error so the channel gets recreated with the new version
131
+ return 'close'
132
+ }
133
+
134
+ // If this channel has a higher version, we need to signal the other side to close
135
+ // and recreate the channel with the new version
136
+ if (packet.channelVersion < channelVersion) {
137
+ const newPacket = MeshSchema.MessageChannelRequest.make({
138
+ source: nodeName,
139
+ sourceId,
140
+ target,
141
+ channelName,
142
+ channelVersion,
143
+ hops: [],
144
+ remainingHops: packet.hops,
145
+ reqId: undefined,
146
+ })
147
+ span?.addEvent(
148
+ `incoming packet has lower version (${packet.channelVersion}), sending request to reconnect (${newPacket.id})`,
149
+ )
150
+
151
+ yield* sendPacket(newPacket)
152
+
153
+ return
154
+ }
155
+
156
+ if (channelState._tag === 'Established' && packet._tag === 'MessageChannelRequest') {
157
+ if (packet.sourceId === channelState.otherSourceId) {
158
+ return
159
+ } else {
160
+ // In case the instance of the source has changed, we need to close the channel
161
+ // and reconnect with a new channel
162
+ span?.addEvent(`force-new-channel`)
163
+ yield* Scope.close(scope, Exit.succeed('force-new-channel'))
164
+ return 'close'
165
+ }
166
+ }
167
+
168
+ switch (packet._tag) {
169
+ // Assumption: Each side has sent an initial request and another request as a response for an incoming request
170
+ case 'MessageChannelRequest': {
171
+ if (channelState._tag !== 'RequestSent') {
172
+ // We can safely ignore further incoming requests as we're already creating a channel
173
+ return
174
+ }
175
+
176
+ if (packet.reqId === channelState.reqPacketId) {
177
+ // Circuit-breaker: We've already sent a request so we don't need to send another one
178
+ } else {
179
+ const newRequestPacket = MeshSchema.MessageChannelRequest.make({
180
+ source: nodeName,
181
+ sourceId,
182
+ target,
183
+ channelName,
184
+ channelVersion,
185
+ hops: [],
186
+ remainingHops: packet.hops,
187
+ reqId: packet.id,
188
+ })
189
+ span?.addEvent(`Re-sending new request (${newRequestPacket.id}) for incoming request (${packet.id})`)
190
+
191
+ yield* sendPacket(newRequestPacket)
192
+ }
193
+
194
+ const isWinner = nodeName > target
195
+
196
+ if (isWinner) {
197
+ span?.addEvent(`winner side: creating message channel and sending response`)
198
+ const mc = new MessageChannel()
199
+
200
+ // We're using a message channel with acks here to make sure messages are not lost
201
+ // which might happen during re-connection scenarios.
202
+ // Also we need to eagerly start listening since we're using the channel "ourselves"
203
+ // for the initial ping-pong sequence.
204
+ const channel = yield* WebChannel.messagePortChannelWithAck({
205
+ port: mc.port1,
206
+ schema,
207
+ debugId: channelVersion,
208
+ }).pipe(Effect.andThen(WebChannel.toOpenChannel))
209
+
210
+ yield* respondToSender(
211
+ MeshSchema.MessageChannelResponseSuccess.make({
212
+ reqId: packet.id,
213
+ target,
214
+ source: nodeName,
215
+ channelName: packet.channelName,
216
+ hops: [],
217
+ remainingHops: packet.hops,
218
+ port: mc.port2,
219
+ channelVersion,
220
+ }),
221
+ )
222
+
223
+ channelStateRef.current = { _tag: 'winner:ResponseSent', channel, otherSourceId: packet.sourceId }
224
+
225
+ // Now we wait for the other side to respond via the channel
226
+ yield* channel.listen.pipe(
227
+ Stream.flatten(),
228
+ Stream.filter(Schema.is(MeshSchema.MessageChannelPing)),
229
+ Stream.take(1),
230
+ Stream.runDrain,
231
+ )
232
+
233
+ yield* channel.send(MeshSchema.MessageChannelPong.make({}))
234
+
235
+ span?.addEvent(`winner side: established`)
236
+ channelStateRef.current = { _tag: 'Established', otherSourceId: packet.sourceId }
237
+
238
+ yield* Deferred.succeed(deferred, channel)
239
+ } else {
240
+ span?.addEvent(`loser side: waiting for response`)
241
+ // Wait for `MessageChannelResponseSuccess` packet
242
+ channelStateRef.current = { _tag: 'loser:WaitingForResponse', otherSourceId: packet.sourceId }
243
+ }
244
+
245
+ break
246
+ }
247
+ case 'MessageChannelResponseSuccess': {
248
+ if (channelState._tag !== 'loser:WaitingForResponse') {
249
+ return shouldNeverHappen(
250
+ `Expected to find message channel response from ${target}, but was in ${channelState._tag} state`,
251
+ )
252
+ }
253
+
254
+ // See message-channel notes above
255
+ const channel = yield* WebChannel.messagePortChannelWithAck({
256
+ port: packet.port,
257
+ schema,
258
+ debugId: channelVersion,
259
+ }).pipe(Effect.andThen(WebChannel.toOpenChannel))
260
+
261
+ const waitForPongFiber = yield* channel.listen.pipe(
262
+ Stream.flatten(),
263
+ Stream.filter(Schema.is(MeshSchema.MessageChannelPong)),
264
+ Stream.take(1),
265
+ Stream.runDrain,
266
+ Effect.fork,
267
+ )
268
+
269
+ yield* channel.send(MeshSchema.MessageChannelPing.make({}))
270
+
271
+ yield* waitForPongFiber
272
+
273
+ span?.addEvent(`loser side: established`)
274
+ channelStateRef.current = { _tag: 'Established', otherSourceId: channelState.otherSourceId }
275
+
276
+ yield* Deferred.succeed(deferred, channel)
277
+
278
+ return
279
+ }
280
+ default: {
281
+ return casesHandled(packet)
282
+ }
283
+ }
284
+ }).pipe(
285
+ Effect.withSpan(`handleMessagePacket:${packet._tag}:${packet.source}→${packet.target}`, {
286
+ attributes: packetAsOtelAttributes(packet),
287
+ }),
288
+ )
289
+
290
+ yield* Effect.gen(function* () {
291
+ while (true) {
292
+ const packet = yield* Queue.take(incomingPacketsQueue)
293
+ const res = yield* processMessagePacket(packet)
294
+ // We want to give requests another chance to be processed
295
+ if (res === 'close') {
296
+ return
297
+ }
298
+ }
299
+ }).pipe(Effect.interruptible, Effect.tapCauseLogPretty, Effect.forkScoped)
300
+
301
+ const channelState = channelStateRef.current
302
+
303
+ if (channelState._tag !== 'Initial') {
304
+ return shouldNeverHappen(`Expected channel to be in Initial state, but was in ${channelState._tag} state`)
305
+ }
306
+
307
+ const connectionRequest = Effect.gen(function* () {
308
+ const packet = MeshSchema.MessageChannelRequest.make({
309
+ source: nodeName,
310
+ sourceId,
311
+ target,
312
+ channelName,
313
+ channelVersion,
314
+ hops: [],
315
+ reqId: undefined,
316
+ })
317
+
318
+ channelStateRef.current = { _tag: 'RequestSent', reqPacketId: packet.id }
319
+
320
+ // yield* Effect.log(`${nodeName}→${channelName}→${target}:connectionRequest [${channelVersion}]`)
321
+
322
+ const noTransferableResponse = checkTransferableConnections(packet)
323
+ if (noTransferableResponse !== undefined) {
324
+ yield* Effect.spanEvent(`No transferable connections found for ${packet.source}→${packet.target}`)
325
+ return yield* Effect.fail(noTransferableResponse)
326
+ }
327
+
328
+ yield* sendPacket(packet)
329
+ span?.addEvent(`initial connection request sent (${packet.id})`)
330
+ })
331
+
332
+ yield* connectionRequest
333
+
334
+ const channel = yield* deferred
335
+
336
+ return channel
337
+ }).pipe(Effect.withSpanScoped(`makeMessageChannel:${channelVersion}`))