@livestore/webmesh 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 (59) hide show
  1. package/README.md +5 -3
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/channel/direct-channel-internal.d.ts +2 -3
  4. package/dist/channel/direct-channel-internal.d.ts.map +1 -1
  5. package/dist/channel/direct-channel-internal.js +35 -38
  6. package/dist/channel/direct-channel-internal.js.map +1 -1
  7. package/dist/channel/direct-channel.d.ts.map +1 -1
  8. package/dist/channel/direct-channel.js +22 -23
  9. package/dist/channel/direct-channel.js.map +1 -1
  10. package/dist/channel/proxy-channel.d.ts +29 -5
  11. package/dist/channel/proxy-channel.d.ts.map +1 -1
  12. package/dist/channel/proxy-channel.js +87 -37
  13. package/dist/channel/proxy-channel.js.map +1 -1
  14. package/dist/common.d.ts +33 -35
  15. package/dist/common.d.ts.map +1 -1
  16. package/dist/common.js +7 -7
  17. package/dist/common.js.map +1 -1
  18. package/dist/mesh-schema.d.ts +262 -136
  19. package/dist/mesh-schema.d.ts.map +1 -1
  20. package/dist/mesh-schema.js +45 -44
  21. package/dist/mesh-schema.js.map +1 -1
  22. package/dist/node.d.ts +12 -6
  23. package/dist/node.d.ts.map +1 -1
  24. package/dist/node.js +47 -44
  25. package/dist/node.js.map +1 -1
  26. package/dist/node.test.js +253 -63
  27. package/dist/node.test.js.map +1 -1
  28. package/dist/utils.d.ts +1 -1
  29. package/dist/utils.d.ts.map +1 -1
  30. package/dist/utils.js.map +1 -1
  31. package/dist/websocket-edge.d.ts +20 -18
  32. package/dist/websocket-edge.d.ts.map +1 -1
  33. package/dist/websocket-edge.js +33 -41
  34. package/dist/websocket-edge.js.map +1 -1
  35. package/dist/websocket-edge.test.d.ts +7 -0
  36. package/dist/websocket-edge.test.d.ts.map +1 -0
  37. package/dist/websocket-edge.test.js +74 -0
  38. package/dist/websocket-edge.test.js.map +1 -0
  39. package/dist/worker/mod.d.ts +24 -0
  40. package/dist/worker/mod.d.ts.map +1 -0
  41. package/dist/worker/mod.js +44 -0
  42. package/dist/worker/mod.js.map +1 -0
  43. package/dist/worker/schema.d.ts +10 -0
  44. package/dist/worker/schema.d.ts.map +1 -0
  45. package/dist/worker/schema.js +7 -0
  46. package/dist/worker/schema.js.map +1 -0
  47. package/package.json +48 -12
  48. package/src/channel/direct-channel-internal.ts +41 -47
  49. package/src/channel/direct-channel.ts +28 -25
  50. package/src/channel/proxy-channel.ts +120 -57
  51. package/src/common.ts +9 -7
  52. package/src/mesh-schema.ts +34 -33
  53. package/src/node.test.ts +356 -80
  54. package/src/node.ts +63 -55
  55. package/src/utils.ts +3 -3
  56. package/src/websocket-edge.test.ts +98 -0
  57. package/src/websocket-edge.ts +52 -48
  58. package/src/worker/mod.ts +89 -0
  59. package/src/worker/schema.ts +8 -0
@@ -1,9 +1,9 @@
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
- Either,
6
+ Result,
7
7
  Exit,
8
8
  Fiber,
9
9
  FiberHandle,
@@ -26,6 +26,38 @@ import {
26
26
  } from '../common.ts'
27
27
  import * as MeshSchema from '../mesh-schema.ts'
28
28
 
29
+ /**
30
+ * Simulation parameters for proxy channel operations.
31
+ * Used for testing race conditions and timing-sensitive behavior.
32
+ *
33
+ * Each parameter represents a delay (in ms) injected at a specific point in the code.
34
+ * Values are bounded 0-500ms to prevent tests from running too long.
35
+ */
36
+ export const ProxyChannelSimulationParams = Schema.Struct({
37
+ /**
38
+ * Delays related to receiving and processing payload messages
39
+ */
40
+ onPayload: Schema.Struct({
41
+ /** Delay before sending the ACK response (simulates slow ACK send) */
42
+ beforeAckSend: Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 500 })),
43
+ /** Delay after forking the ACK send, before adding message to listen queue */
44
+ afterAckFork: Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 500 })),
45
+ /** Delay after adding message to listen queue */
46
+ afterListenQueueOffer: Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 500 })),
47
+ }),
48
+ })
49
+
50
+ export type ProxyChannelSimulationParams = typeof ProxyChannelSimulationParams.Type
51
+
52
+ /** Default simulation params with no delays */
53
+ export const defaultSimulationParams: ProxyChannelSimulationParams = {
54
+ onPayload: {
55
+ beforeAckSend: 0,
56
+ afterAckFork: 0,
57
+ afterListenQueueOffer: 0,
58
+ },
59
+ }
60
+
29
61
  interface MakeProxyChannelArgs {
30
62
  queue: Queue.Queue<ProxyQueueItem>
31
63
  nodeName: MeshNodeName
@@ -34,9 +66,11 @@ interface MakeProxyChannelArgs {
34
66
  channelName: ChannelName
35
67
  target: MeshNodeName
36
68
  schema: {
37
- send: Schema.Schema<any, any>
38
- listen: Schema.Schema<any, any>
69
+ send: Schema.Codec<any, any>
70
+ listen: Schema.Codec<any, any>
39
71
  }
72
+ /** Optional simulation parameters for testing timing-sensitive behavior */
73
+ simulation?: ProxyChannelSimulationParams
40
74
  }
41
75
 
42
76
  export const makeProxyChannel = ({
@@ -47,9 +81,18 @@ export const makeProxyChannel = ({
47
81
  target,
48
82
  channelName,
49
83
  schema,
84
+ simulation = defaultSimulationParams,
50
85
  }: MakeProxyChannelArgs) =>
51
86
  Effect.scopeWithCloseable((scope) =>
52
87
  Effect.gen(function* () {
88
+ /** Helper to inject simulation delays at specific code points */
89
+ const simSleep = <TKey extends keyof ProxyChannelSimulationParams>(
90
+ key: TKey,
91
+ key2: keyof ProxyChannelSimulationParams[TKey],
92
+ ) => {
93
+ const delay = (simulation[key]?.[key2] ?? 0) as number
94
+ return delay > 0 ? Effect.sleep(delay) : Effect.void
95
+ }
53
96
  type ProxiedChannelState =
54
97
  | {
55
98
  _tag: 'Initial'
@@ -62,9 +105,9 @@ export const makeProxyChannel = ({
62
105
 
63
106
  type ProxiedChannelStateEstablished = {
64
107
  _tag: 'Established'
65
- listenSchema: Schema.Schema<any, any>
108
+ listenSchema: Schema.Codec<any, any>
66
109
  listenQueue: Queue.Queue<any>
67
- ackMap: Map<string, Deferred.Deferred<void, never>>
110
+ ackMap: Map<string, Deferred.Deferred<void>>
68
111
  combinedChannelId: string
69
112
  }
70
113
 
@@ -88,6 +131,10 @@ export const makeProxyChannel = ({
88
131
  const channelSpan = yield* Effect.currentSpan.pipe(Effect.orDie)
89
132
 
90
133
  const connectedStateRef = yield* SubscriptionRef.make<ProxiedChannelStateEstablished | false>(false)
134
+ // Incoming packets can be processed as soon as the queue-draining fiber starts, so state
135
+ // referenced by established channels must exist before that processor is forked.
136
+ const listenQueue = yield* Queue.unbounded<any>()
137
+ const ackMap = new Map<string, Deferred.Deferred<void>>()
91
138
 
92
139
  const waitForEstablished = Effect.gen(function* () {
93
140
  const state = yield* SubscriptionRef.waitUntil(connectedStateRef, (state) => state !== false)
@@ -117,10 +164,11 @@ export const makeProxyChannel = ({
117
164
  )
118
165
 
119
166
  const getCombinedChannelId = (otherSideChannelIdCandidate: string) =>
120
- [channelIdCandidate, otherSideChannelIdCandidate].sort().join('_')
167
+ [channelIdCandidate, otherSideChannelIdCandidate].toSorted().join('_')
121
168
 
122
- const earlyPayloadBuffer = yield* Queue.unbounded<typeof MeshSchema.ProxyChannelPayload.Type>().pipe(
123
- Effect.acquireRelease(Queue.shutdown),
169
+ const earlyPayloadBuffer = yield* Effect.acquireRelease(
170
+ Queue.unbounded<typeof MeshSchema.ProxyChannelPayload.Type>(),
171
+ Queue.shutdown,
124
172
  )
125
173
 
126
174
  const processProxyPacket = ({ packet, respondToSender }: ProxyQueueItem) =>
@@ -224,7 +272,7 @@ export const makeProxyChannel = ({
224
272
  const establishedState = channelStateRef.current
225
273
  if (establishedState._tag === 'Established') {
226
274
  //
227
- const bufferedPackets = yield* Queue.takeAll(earlyPayloadBuffer)
275
+ const bufferedPackets = yield* Queue.clear(earlyPayloadBuffer)
228
276
  // yield* Effect.logDebug(
229
277
  // `[${nodeName}] Draining early payload buffer (${bufferedPackets.length}) after ResponseSuccess`,
230
278
  // )
@@ -235,10 +283,10 @@ export const makeProxyChannel = ({
235
283
  )
236
284
  continue
237
285
  }
238
- const decodedMessage = yield* Schema.decodeUnknown(establishedState.listenSchema)(
286
+ const decodedMessage = yield* Schema.decodeUnknownEffect(establishedState.listenSchema)(
239
287
  bufferedPacket.payload,
240
288
  )
241
- yield* establishedState.listenQueue.pipe(Queue.offer(decodedMessage))
289
+ yield* Queue.offer(establishedState.listenQueue, decodedMessage)
242
290
  }
243
291
  } else {
244
292
  yield* Effect.logError(
@@ -255,34 +303,46 @@ export const makeProxyChannel = ({
255
303
  )
256
304
  }
257
305
 
258
- // yield* Effect.logDebug(`[${nodeName}] Received payload reqId: ${packet.id}. Sending Ack.`)
259
- yield* respondToSender(
260
- MeshSchema.ProxyChannelPayloadAck.make({
261
- reqId: packet.id,
262
- remainingHops: packet.hops,
263
- hops: [],
264
- target,
265
- source: nodeName,
266
- channelName,
267
- combinedChannelId:
268
- channelState._tag === 'Established' ? channelState.combinedChannelId : packet.combinedChannelId,
269
- }),
270
- )
306
+ // Send ACK fire-and-forget to avoid blocking message processing
307
+ // This is critical because blocking ACK sends can prevent messages from reaching the listen queue
308
+ // See test: "ACK forkScoped regression tests" for documentation
309
+
310
+ // The ACK send effect with optional simulation delay INSIDE the fork
311
+ // This is key for testing: with forkScoped, the delay happens in background and doesn't block message processing
312
+ // Without forkScoped (blocking), the delay would block the message from being added to listen queue
313
+ const ackSendEffect = Effect.gen(function* () {
314
+ yield* simSleep('onPayload', 'beforeAckSend')
315
+ yield* respondToSender(
316
+ MeshSchema.ProxyChannelPayloadAck.make({
317
+ reqId: packet.id,
318
+ remainingHops: packet.hops,
319
+ hops: [],
320
+ target,
321
+ source: nodeName,
322
+ channelName,
323
+ combinedChannelId:
324
+ channelState._tag === 'Established' ? channelState.combinedChannelId : packet.combinedChannelId,
325
+ }),
326
+ )
327
+ })
328
+
329
+ yield* ackSendEffect.pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
330
+
331
+ // Simulation point: delay after forking ACK (before processing message)
332
+ yield* simSleep('onPayload', 'afterAckFork')
271
333
 
272
334
  if (channelState._tag === 'Established') {
273
- const decodedMessage = yield* Schema.decodeUnknown(channelState.listenSchema)(packet.payload)
274
- yield* channelState.listenQueue.pipe(Queue.offer(decodedMessage))
335
+ const decodedMessage = yield* Schema.decodeUnknownEffect(channelState.listenSchema)(packet.payload)
336
+ yield* Queue.offer(channelState.listenQueue, decodedMessage)
337
+
338
+ // Simulation point: delay after adding to listen queue
339
+ yield* simSleep('onPayload', 'afterListenQueueOffer')
275
340
  } else {
276
- // yield* Effect.logDebug(
277
- // `[${nodeName}] Buffering early payload reqId: ${packet.id} (state: ${channelState._tag})`,
278
- // )
279
341
  yield* Queue.offer(earlyPayloadBuffer, packet)
280
342
  }
281
343
  return
282
344
  }
283
345
  case 'ProxyChannelPayloadAck': {
284
- // yield* Effect.logDebug(`[${nodeName}] Received Ack for reqId: ${packet.reqId}`)
285
-
286
346
  if (channelState._tag !== 'Established') {
287
347
  yield* Effect.spanEvent(`Not yet connected to ${target}. dropping message`)
288
348
  yield* Effect.logWarning(
@@ -291,9 +351,14 @@ export const makeProxyChannel = ({
291
351
  return
292
352
  }
293
353
 
294
- const ack =
295
- channelState.ackMap.get(packet.reqId) ??
296
- shouldNeverHappen(`[ProxyChannel[${channelKey}]] Expected ack for ${packet.reqId}`)
354
+ // Handle missing ACK gracefully - can happen with synthetic ACKs from relay or duplicate ACKs
355
+ const ack = channelState.ackMap.get(packet.reqId)
356
+ if (ack === undefined) {
357
+ yield* Effect.logDebug(
358
+ `Received ACK for unknown reqId: ${packet.reqId} (may be synthetic or duplicate)`,
359
+ )
360
+ return
361
+ }
297
362
  yield* Deferred.succeed(ack, void 0)
298
363
 
299
364
  channelState.ackMap.delete(packet.reqId)
@@ -310,19 +375,8 @@ export const makeProxyChannel = ({
310
375
  }),
311
376
  )
312
377
 
313
- yield* Stream.fromQueue(queue).pipe(
314
- Stream.tap(processProxyPacket),
315
- Stream.runDrain,
316
- Effect.tapCauseLogPretty,
317
- Effect.forkScoped,
318
- )
319
-
320
- const listenQueue = yield* Queue.unbounded<any>()
321
-
322
378
  yield* Effect.spanEvent(`Connecting`)
323
379
 
324
- const ackMap = new Map<string, Deferred.Deferred<void, never>>()
325
-
326
380
  // check if already established via incoming `ProxyChannelRequest` from other side
327
381
  // which indicates we already have a edge to the target node
328
382
  // const channelState = channelStateRef.current
@@ -341,6 +395,15 @@ export const makeProxyChannel = ({
341
395
  Effect.forkScoped,
342
396
  )
343
397
 
398
+ // Drain queued proxy packets only after the local side entered `Pending` and sent its
399
+ // request. Otherwise an already-buffered response can race initialization.
400
+ yield* Stream.fromQueue(queue).pipe(
401
+ Stream.tap(processProxyPacket),
402
+ Stream.runDrain,
403
+ Effect.tapCauseLogPretty,
404
+ Effect.forkScoped,
405
+ )
406
+
344
407
  const { combinedChannelId: channelId } = yield* waitForEstablished
345
408
 
346
409
  yield* Fiber.interrupt(retryOnNewEdgeFiber)
@@ -350,7 +413,7 @@ export const makeProxyChannel = ({
350
413
 
351
414
  const send = (message: any) =>
352
415
  Effect.gen(function* () {
353
- const payload = yield* Schema.encodeUnknown(schema.send)(message)
416
+ const payload = yield* Schema.encodeUnknownEffect(schema.send)(message)
354
417
  const sendFiberHandle = yield* FiberHandle.make<void, never>()
355
418
 
356
419
  const sentDeferred = yield* Deferred.make<void>()
@@ -366,7 +429,7 @@ export const makeProxyChannel = ({
366
429
 
367
430
  const innerSend = Effect.gen(function* () {
368
431
  // Note we're re-creating new packets every time otherwise they will be skipped because of `handledIds`
369
- const ack = yield* Deferred.make<void, never>()
432
+ const ack = yield* Deferred.make<void>()
370
433
  const packet = MeshSchema.ProxyChannelPayload.make({
371
434
  channelName,
372
435
  payload,
@@ -381,7 +444,7 @@ export const makeProxyChannel = ({
381
444
 
382
445
  yield* sendPacket(packet)
383
446
 
384
- yield* ack
447
+ yield* Deferred.await(ack)
385
448
  yield* Deferred.succeed(sentDeferred, void 0)
386
449
 
387
450
  debugInfo.pendingSends--
@@ -390,18 +453,18 @@ export const makeProxyChannel = ({
390
453
  // TODO make this configurable
391
454
  // Schedule.exponential(10): 10, 20, 40, 80, 160, 320, ...
392
455
  yield* innerSend.pipe(Effect.timeout(100), Effect.retry(Schedule.exponential(10)), Effect.orDie)
393
- }).pipe(Effect.tapErrorCause(Effect.logError))
456
+ }).pipe(Effect.tapCause(Effect.logError))
394
457
 
395
- const rerunOnNewChannelFiber = yield* connectedStateRef.changes.pipe(
458
+ const rerunOnNewChannelFiber = yield* SubscriptionRef.changes(connectedStateRef).pipe(
396
459
  Stream.filter((_) => _ === false),
397
460
  Stream.tap(() => FiberHandle.run(sendFiberHandle, trySend)),
398
461
  Stream.runDrain,
399
- Effect.fork,
462
+ Effect.forkChild,
400
463
  )
401
464
 
402
465
  yield* FiberHandle.run(sendFiberHandle, trySend)
403
466
 
404
- yield* sentDeferred
467
+ yield* Deferred.await(sentDeferred)
405
468
 
406
469
  yield* Fiber.interrupt(rerunOnNewChannelFiber)
407
470
  }).pipe(
@@ -410,11 +473,11 @@ export const makeProxyChannel = ({
410
473
  Effect.withParentSpan(channelSpan),
411
474
  )
412
475
 
413
- const listen = Stream.fromQueue(listenQueue).pipe(Stream.map(Either.right))
476
+ const listen = Stream.fromQueue(listenQueue).pipe(Stream.map(Result.succeed))
414
477
 
415
- const closedDeferred = yield* Deferred.make<void>().pipe(Effect.acquireRelease(Deferred.done(Exit.void)))
478
+ const closedDeferred = yield* Effect.acquireRelease(Deferred.make<void>(), Deferred.done(Exit.void))
416
479
 
417
- const runtime = yield* Effect.runtime()
480
+ const services = yield* Effect.context()
418
481
 
419
482
  const webChannel = {
420
483
  [WebChannel.WebChannelSymbol]: WebChannel.WebChannelSymbol,
@@ -429,7 +492,7 @@ export const makeProxyChannel = ({
429
492
  debug: {
430
493
  ping: (message = 'ping') =>
431
494
  send(WebChannel.DebugPingMessage.make({ message })).pipe(
432
- Effect.provide(runtime),
495
+ Effect.provide(services),
433
496
  Effect.tapCauseLogPretty,
434
497
  Effect.runFork,
435
498
  ),
package/src/common.ts CHANGED
@@ -19,19 +19,21 @@ export type ChannelName = string
19
19
  export type ChannelKey = `target:${MeshNodeName}, channelName:${ChannelName}`
20
20
 
21
21
  // TODO actually use this to avoid timeouts in certain cases
22
- // export class NoConnectionRouteSignal extends Schema.TaggedError<NoConnectionRouteSignal>()(
23
- // 'NoConnectionRouteSignal',
24
- // {},
25
- // ) {}
22
+ // export class NoConnectionRouteSignal extends Schema.TaggedError<NoConnectionRouteSignal>(
23
+ // '~@livestore/webmesh/NoConnectionRouteSignal',
24
+ // )('NoConnectionRouteSignal', {}) {}
26
25
 
27
- export class EdgeAlreadyExistsError extends Schema.TaggedError<EdgeAlreadyExistsError>()('EdgeAlreadyExistsError', {
26
+ export class EdgeAlreadyExistsError extends Schema.TaggedError<EdgeAlreadyExistsError>(
27
+ '~@livestore/webmesh/EdgeAlreadyExistsError',
28
+ )('EdgeAlreadyExistsError', {
28
29
  target: Schema.String,
29
30
  }) {}
30
31
 
31
32
  export const packetAsOtelAttributes = (packet: typeof Packet.Type) => ({
32
33
  packetId: packet.id,
33
34
  'span.label':
34
- packet.id + (Predicate.hasProperty(packet, 'reqId') && packet.reqId !== undefined ? ` for ${packet.reqId}` : ''),
35
+ packet.id +
36
+ (Predicate.hasProperty(packet, 'reqId') === true && packet.reqId !== undefined ? ` for ${packet.reqId}` : ''),
35
37
  ...omitUndefineds({
36
38
  packet:
37
39
  packet._tag !== 'DirectChannelResponseSuccess' && packet._tag !== 'ProxyChannelPayload' ? packet : undefined,
@@ -41,7 +43,7 @@ export const packetAsOtelAttributes = (packet: typeof Packet.Type) => ({
41
43
  export const ListenForChannelResult = Schema.Struct({
42
44
  channelName: Schema.String,
43
45
  source: Schema.String,
44
- mode: Schema.Union(Schema.Literal('proxy'), Schema.Literal('direct')),
46
+ mode: Schema.Literals(['proxy', 'direct']),
45
47
  })
46
48
 
47
49
  export type ListenForChannelResult = typeof ListenForChannelResult.Type
@@ -1,9 +1,9 @@
1
- import { Schema, Transferable } from '@livestore/utils/effect'
1
+ import { Effect, Schema, Transferable } from '@livestore/utils/effect'
2
2
  import { nanoid } from '@livestore/utils/nanoid'
3
3
 
4
4
  const id = Schema.String.pipe(
5
- Schema.optional,
6
- Schema.withDefaults({ constructor: () => nanoid(10), decoding: () => nanoid(10) }),
5
+ Schema.withDecodingDefaultType(Effect.sync(() => nanoid(10))),
6
+ Schema.withConstructorDefault(Effect.sync(() => nanoid(10))),
7
7
  )
8
8
 
9
9
  const defaultPacketFields = {
@@ -23,10 +23,10 @@ const remainingHopsUndefined = Schema.Undefined.pipe(Schema.optional)
23
23
  * We need a clear path back to the sender to avoid this, thus we respond with a separate
24
24
  * `DirectChannelResponseSuccess` which contains the `port`.
25
25
  */
26
- export class DirectChannelRequest extends Schema.TaggedStruct('DirectChannelRequest', {
26
+ export const DirectChannelRequest = Schema.TaggedStruct('DirectChannelRequest', {
27
27
  ...defaultPacketFields,
28
28
  remainingHops: Schema.Array(Schema.String).pipe(Schema.optional),
29
- channelVersion: Schema.Number,
29
+ channelVersion: Schema.Finite,
30
30
  /** Only set if the request is in response to an incoming request */
31
31
  reqId: Schema.UndefinedOr(Schema.String),
32
32
  /**
@@ -34,70 +34,70 @@ export class DirectChannelRequest extends Schema.TaggedStruct('DirectChannelRequ
34
34
  * source has changed.
35
35
  */
36
36
  sourceId: Schema.String,
37
- }) {}
37
+ })
38
38
 
39
- export class DirectChannelResponseSuccess extends Schema.TaggedStruct('DirectChannelResponseSuccess', {
39
+ export const DirectChannelResponseSuccess = Schema.TaggedStruct('DirectChannelResponseSuccess', {
40
40
  ...defaultPacketFields,
41
41
  reqId: Schema.String,
42
42
  port: Transferable.MessagePort,
43
43
  // Since we can't copy this message, we need to follow the exact route back to the sender
44
44
  remainingHops: Schema.Array(Schema.String),
45
- channelVersion: Schema.Number,
46
- }) {}
45
+ channelVersion: Schema.Finite,
46
+ })
47
47
 
48
- export class DirectChannelResponseNoTransferables extends Schema.TaggedStruct('DirectChannelResponseNoTransferables', {
48
+ export const DirectChannelResponseNoTransferables = Schema.TaggedStruct('DirectChannelResponseNoTransferables', {
49
49
  ...defaultPacketFields,
50
50
  reqId: Schema.String,
51
51
  remainingHops: Schema.Array(Schema.String),
52
- }) {}
52
+ })
53
53
 
54
- export class ProxyChannelRequest extends Schema.TaggedStruct('ProxyChannelRequest', {
54
+ export const ProxyChannelRequest = Schema.TaggedStruct('ProxyChannelRequest', {
55
55
  ...defaultPacketFields,
56
56
  remainingHops: remainingHopsUndefined,
57
57
  channelIdCandidate: Schema.String,
58
- }) {}
58
+ })
59
59
 
60
- export class ProxyChannelResponseSuccess extends Schema.TaggedStruct('ProxyChannelResponseSuccess', {
60
+ export const ProxyChannelResponseSuccess = Schema.TaggedStruct('ProxyChannelResponseSuccess', {
61
61
  ...defaultPacketFields,
62
62
  reqId: Schema.String,
63
63
  remainingHops: Schema.Array(Schema.String),
64
64
  combinedChannelId: Schema.String,
65
65
  channelIdCandidate: Schema.String,
66
- }) {}
66
+ })
67
67
 
68
- export class ProxyChannelPayload extends Schema.TaggedStruct('ProxyChannelPayload', {
68
+ export const ProxyChannelPayload = Schema.TaggedStruct('ProxyChannelPayload', {
69
69
  ...defaultPacketFields,
70
70
  remainingHops: remainingHopsUndefined,
71
71
  payload: Schema.Any,
72
72
  combinedChannelId: Schema.String,
73
- }) {}
73
+ })
74
74
 
75
- export class ProxyChannelPayloadAck extends Schema.TaggedStruct('ProxyChannelPayloadAck', {
75
+ export const ProxyChannelPayloadAck = Schema.TaggedStruct('ProxyChannelPayloadAck', {
76
76
  ...defaultPacketFields,
77
77
  reqId: Schema.String,
78
78
  remainingHops: Schema.Array(Schema.String),
79
79
  combinedChannelId: Schema.String,
80
- }) {}
80
+ })
81
81
 
82
82
  /**
83
83
  * Broadcast to all nodes when a new edge is added.
84
84
  * Mostly used for auto-reconnect purposes.
85
85
  */
86
- export class NetworkEdgeAdded extends Schema.TaggedStruct('NetworkEdgeAdded', {
86
+ export const NetworkEdgeAdded = Schema.TaggedStruct('NetworkEdgeAdded', {
87
87
  id,
88
88
  source: Schema.String,
89
89
  target: Schema.String,
90
- }) {}
90
+ })
91
91
 
92
- export class NetworkTopologyRequest extends Schema.TaggedStruct('NetworkTopologyRequest', {
92
+ export const NetworkTopologyRequest = Schema.TaggedStruct('NetworkTopologyRequest', {
93
93
  id,
94
94
  hops: Schema.Array(Schema.String),
95
95
  /** Always fixed to who requested the topology */
96
96
  source: Schema.String,
97
97
  target: Schema.Literal('-'),
98
- }) {}
98
+ })
99
99
 
100
- export class NetworkTopologyResponse extends Schema.TaggedStruct('NetworkTopologyResponse', {
100
+ export const NetworkTopologyResponse = Schema.TaggedStruct('NetworkTopologyResponse', {
101
101
  id,
102
102
  reqId: Schema.String,
103
103
  remainingHops: Schema.Array(Schema.String),
@@ -106,7 +106,7 @@ export class NetworkTopologyResponse extends Schema.TaggedStruct('NetworkTopolog
106
106
  /** Always fixed to who requested the topology */
107
107
  source: Schema.String,
108
108
  target: Schema.Literal('-'),
109
- }) {}
109
+ })
110
110
 
111
111
  export const BroadcastChannelPacket = Schema.TaggedStruct('BroadcastChannelPacket', {
112
112
  id,
@@ -121,27 +121,28 @@ export const BroadcastChannelPacket = Schema.TaggedStruct('BroadcastChannelPacke
121
121
  target: Schema.Literal('-'),
122
122
  })
123
123
 
124
- export class DirectChannelPacket extends Schema.Union(
124
+ export const DirectChannelPacket = Schema.Union([
125
125
  DirectChannelRequest,
126
126
  DirectChannelResponseSuccess,
127
127
  DirectChannelResponseNoTransferables,
128
- ) {}
128
+ ])
129
129
 
130
- export class ProxyChannelPacket extends Schema.Union(
130
+ export const ProxyChannelPacket = Schema.Union([
131
131
  ProxyChannelRequest,
132
132
  ProxyChannelResponseSuccess,
133
133
  ProxyChannelPayload,
134
134
  ProxyChannelPayloadAck,
135
- ) {}
135
+ ])
136
136
 
137
- export class Packet extends Schema.Union(
137
+ export const Packet = Schema.Union([
138
138
  DirectChannelPacket,
139
139
  ProxyChannelPacket,
140
140
  NetworkEdgeAdded,
141
141
  NetworkTopologyRequest,
142
142
  NetworkTopologyResponse,
143
143
  BroadcastChannelPacket,
144
- ) {}
144
+ ])
145
+
146
+ export const DirectChannelPing = Schema.TaggedStruct('DirectChannelPing', {})
145
147
 
146
- export class DirectChannelPing extends Schema.TaggedStruct('DirectChannelPing', {}) {}
147
- export class DirectChannelPong extends Schema.TaggedStruct('DirectChannelPong', {}) {}
148
+ export const DirectChannelPong = Schema.TaggedStruct('DirectChannelPong', {})