@livestore/webmesh 0.4.0 → 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 (56) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/channel/direct-channel-internal.d.ts +2 -3
  3. package/dist/channel/direct-channel-internal.d.ts.map +1 -1
  4. package/dist/channel/direct-channel-internal.js +23 -18
  5. package/dist/channel/direct-channel-internal.js.map +1 -1
  6. package/dist/channel/direct-channel.d.ts.map +1 -1
  7. package/dist/channel/direct-channel.js +20 -21
  8. package/dist/channel/direct-channel.js.map +1 -1
  9. package/dist/channel/proxy-channel.d.ts +7 -8
  10. package/dist/channel/proxy-channel.d.ts.map +1 -1
  11. package/dist/channel/proxy-channel.js +26 -22
  12. package/dist/channel/proxy-channel.js.map +1 -1
  13. package/dist/common.d.ts +33 -35
  14. package/dist/common.d.ts.map +1 -1
  15. package/dist/common.js +3 -2
  16. package/dist/common.js.map +1 -1
  17. package/dist/mesh-schema.d.ts +262 -136
  18. package/dist/mesh-schema.d.ts.map +1 -1
  19. package/dist/mesh-schema.js +45 -44
  20. package/dist/mesh-schema.js.map +1 -1
  21. package/dist/node.d.ts +6 -6
  22. package/dist/node.d.ts.map +1 -1
  23. package/dist/node.js +22 -21
  24. package/dist/node.js.map +1 -1
  25. package/dist/node.test.js +74 -71
  26. package/dist/node.test.js.map +1 -1
  27. package/dist/utils.d.ts +1 -1
  28. package/dist/utils.d.ts.map +1 -1
  29. package/dist/utils.js.map +1 -1
  30. package/dist/websocket-edge.d.ts +18 -16
  31. package/dist/websocket-edge.d.ts.map +1 -1
  32. package/dist/websocket-edge.js +30 -36
  33. package/dist/websocket-edge.js.map +1 -1
  34. package/dist/websocket-edge.test.js +14 -14
  35. package/dist/websocket-edge.test.js.map +1 -1
  36. package/dist/worker/mod.d.ts +24 -0
  37. package/dist/worker/mod.d.ts.map +1 -0
  38. package/dist/worker/mod.js +44 -0
  39. package/dist/worker/mod.js.map +1 -0
  40. package/dist/worker/schema.d.ts +10 -0
  41. package/dist/worker/schema.d.ts.map +1 -0
  42. package/dist/worker/schema.js +7 -0
  43. package/dist/worker/schema.js.map +1 -0
  44. package/package.json +20 -37
  45. package/src/channel/direct-channel-internal.ts +30 -22
  46. package/src/channel/direct-channel.ts +26 -23
  47. package/src/channel/proxy-channel.ts +39 -36
  48. package/src/common.ts +6 -3
  49. package/src/mesh-schema.ts +34 -33
  50. package/src/node.test.ts +99 -86
  51. package/src/node.ts +32 -32
  52. package/src/utils.ts +3 -3
  53. package/src/websocket-edge.test.ts +14 -14
  54. package/src/websocket-edge.ts +47 -41
  55. package/src/worker/mod.ts +89 -0
  56. package/src/worker/schema.ts +8 -0
package/src/node.test.ts CHANGED
@@ -1,7 +1,8 @@
1
+ import { expect } from 'vitest'
2
+
1
3
  import { IS_CI, omitUndefineds } from '@livestore/utils'
2
- import { Chunk, Deferred, Effect, Exit, Schema, Scope, Stream, WebChannel } from '@livestore/utils/effect'
3
4
  import { Vitest } from '@livestore/utils-dev/node-vitest'
4
- import { expect } from 'vitest'
5
+ import { Deferred, Effect, Exit, Fiber, Schema, Scope, Stream, WebChannel } from '@livestore/utils/effect'
5
6
 
6
7
  import { Packet } from './mesh-schema.ts'
7
8
  import type { MeshNode } from './node.ts'
@@ -71,7 +72,7 @@ const createChannel = (source: MeshNode, target: string, options?: Partial<Param
71
72
 
72
73
  const getFirstMessage = <T1, T2>(channel: WebChannel.WebChannel<T1, T2>) =>
73
74
  channel.listen.pipe(
74
- Stream.flatten(),
75
+ Stream.mapEffect(Effect.fromResult),
75
76
  Stream.take(1),
76
77
  Stream.runCollect,
77
78
  Effect.map(([message]) => message),
@@ -88,16 +89,16 @@ const maybeDelay =
88
89
  const testTimeout = IS_CI === true ? 30_000 : 1000
89
90
  const propTestTimeout = IS_CI === true ? 60_000 : 20_000
90
91
 
91
- // TODO also make work without `Vitest.scopedLive` (i.e. with `Vitest.scoped`)
92
+ // TODO also make work without `Vitest.live` (i.e. with `Vitest.effect`)
92
93
  // probably requires controlling the clocks
93
94
  Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
94
- const Delay = Schema.UndefinedOr(Schema.Literal(0, 1, 10, 50))
95
+ const Delay = Schema.UndefinedOr(Schema.Literals([0, 1, 10, 50]))
95
96
  // NOTE for message channels, we test both with and without transferables (i.e. proxying)
96
- const ChannelType = Schema.Literal('direct', 'proxy(via-messagechannel-edge)', 'proxy')
97
- const NodeNames = Schema.Union(
98
- Schema.Tuple(Schema.Literal('A'), Schema.Literal('B')),
99
- Schema.Tuple(Schema.Literal('B'), Schema.Literal('A')),
100
- )
97
+ const ChannelType = Schema.Literals(['direct', 'proxy(via-messagechannel-edge)', 'proxy'])
98
+ const NodeNames = Schema.Union([
99
+ Schema.Tuple([Schema.Literal('A'), Schema.Literal('B')]),
100
+ Schema.Tuple([Schema.Literal('B'), Schema.Literal('A')]),
101
+ ])
101
102
 
102
103
  const fromChannelType = (
103
104
  channelType: typeof ChannelType.Type,
@@ -170,10 +171,10 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
170
171
  // const connectDelay = undefined
171
172
  // const channelType = 'direct'
172
173
  // const nodeNames = ['B', 'A'] as const
173
- // Vitest.scopedLive(
174
+ // Vitest.live(
174
175
  // 'a / b connect at different times with different channel types',
175
176
  // (test) =>
176
- Vitest.scopedLive.prop(
177
+ Vitest.live.prop(
177
178
  'a / b connect at different times with different channel types',
178
179
  [Delay, Delay, Delay, ChannelType, NodeNames],
179
180
  ([delayX, delayY, connectDelay, channelType, nodeNames], test) =>
@@ -208,10 +209,10 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
208
209
  // const waitForOfflineDelay = undefined
209
210
  // const sleepDelay = 0
210
211
  // const channelType = 'direct'
211
- // Vitest.scopedLive(
212
+ // Vitest.live(
212
213
  // 'b reconnects',
213
214
  // (test) =>
214
- Vitest.scopedLive.prop(
215
+ Vitest.live.prop(
215
216
  'b reconnects',
216
217
  [Delay, Delay, ChannelType],
217
218
  ([waitForOfflineDelay, sleepDelay, channelType], test) =>
@@ -240,7 +241,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
240
241
 
241
242
  console.log('nodeACode:waiting for B to be offline')
242
243
  if (waitForBToBeOffline !== undefined) {
243
- yield* waitForBToBeOffline
244
+ yield* Deferred.await(waitForBToBeOffline)
244
245
  }
245
246
 
246
247
  yield* channelAToB.send({ message: 'A2' })
@@ -285,14 +286,14 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
285
286
  { fastCheck: { numRuns: 20 } },
286
287
  )
287
288
 
288
- Vitest.scopedLive('reconnect with re-created node', (test) =>
289
+ Vitest.live('reconnect with re-created node', (test) =>
289
290
  Effect.gen(function* () {
290
291
  const nodeBgen1Scope = yield* Scope.make()
291
292
 
292
293
  const nodeA = yield* makeMeshNode('A')
293
- const nodeBgen1 = yield* makeMeshNode('B').pipe(Scope.extend(nodeBgen1Scope))
294
+ const nodeBgen1 = yield* makeMeshNode('B').pipe(Scope.provide(nodeBgen1Scope))
294
295
 
295
- yield* connectNodesViaMessageChannel(nodeA, nodeBgen1).pipe(Scope.extend(nodeBgen1Scope))
296
+ yield* connectNodesViaMessageChannel(nodeA, nodeBgen1).pipe(Scope.provide(nodeBgen1Scope))
296
297
 
297
298
  // yield* Effect.sleep(100)
298
299
 
@@ -313,7 +314,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
313
314
  // expect(channelBToA.debugInfo.connectCounter).toBe(1)
314
315
  })
315
316
 
316
- yield* Effect.all([nodeACode, nodeBCode(nodeBgen1).pipe(Scope.extend(nodeBgen1Scope))], {
317
+ yield* Effect.all([nodeACode, nodeBCode(nodeBgen1).pipe(Scope.provide(nodeBgen1Scope))], {
317
318
  concurrency: 'unbounded',
318
319
  }).pipe(Effect.withSpan('test1'))
319
320
 
@@ -328,7 +329,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
328
329
  }).pipe(Vitest.withTestCtx(test)),
329
330
  )
330
331
 
331
- const ChannelTypeWithoutMessageChannelProxy = Schema.Literal('proxy', 'direct')
332
+ const ChannelTypeWithoutMessageChannelProxy = Schema.Literals(['proxy', 'direct'])
332
333
  // TODO there seems to be a flaky case here which gets hit sometimes (e.g. 2025-02-28-17:11)
333
334
  // Log output:
334
335
  // test: { seed: -964670352, path: "1", endOnFailure: true }
@@ -344,7 +345,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
344
345
  // test: Error: Property failed after 2 tests
345
346
  // test: { seed: -964670352, path: "1", endOnFailure: true }
346
347
  // test: Counterexample: ["direct",["A","B"]]
347
- Vitest.scopedLive.prop(
348
+ Vitest.live.prop(
348
349
  'replace edge while keeping the channel',
349
350
  [ChannelTypeWithoutMessageChannelProxy, NodeNames],
350
351
  ([channelType, nodeNames], test) =>
@@ -366,7 +367,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
366
367
  yield* channelXToY.send({ message: `${nodeLabel.x}1` })
367
368
  expect(yield* getFirstMessage(channelXToY)).toEqual({ message: `${nodeLabel.y}1` })
368
369
 
369
- yield* waitForEdgeReplacement
370
+ yield* Deferred.await(waitForEdgeReplacement)
370
371
 
371
372
  yield* channelXToY.send({ message: `${nodeLabel.x}2` })
372
373
  expect(yield* getFirstMessage(channelXToY)).toEqual({ message: `${nodeLabel.y}2` })
@@ -399,9 +400,9 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
399
400
 
400
401
  Vitest.describe('TODO improve latency', () => {
401
402
  // TODO we need to improve latency when sending messages concurrently
402
- Vitest.scopedLive.prop(
403
+ Vitest.live.prop(
403
404
  'concurrent messages',
404
- [ChannelType, Schema.Int.pipe(Schema.between(1, 50))],
405
+ [ChannelType, Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 50 }))],
405
406
  ([channelType, count], test) =>
406
407
  Effect.gen(function* () {
407
408
  const nodeA = yield* makeMeshNode('A')
@@ -415,14 +416,18 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
415
416
 
416
417
  // send 10 times A1
417
418
  yield* Effect.forEach(
418
- Chunk.makeBy(count, (i) => ({ message: `A${i}` })),
419
+ Array.from({ length: count }, (_, i) => ({ message: `A${i}` })),
419
420
  channelAToB.send,
420
421
  { concurrency: 'unbounded' },
421
422
  )
422
423
 
423
- expect(yield* channelAToB.listen.pipe(Stream.flatten(), Stream.take(count), Stream.runCollect)).toEqual(
424
- Chunk.makeBy(count, (i) => ({ message: `B${i}` })),
425
- )
424
+ expect(
425
+ yield* channelAToB.listen.pipe(
426
+ Stream.mapEffect(Effect.fromResult),
427
+ Stream.take(count),
428
+ Stream.runCollect,
429
+ ),
430
+ ).toEqual(Array.from({ length: count }, (_, i) => ({ message: `B${i}` })))
426
431
  // expect(yield* getFirstMessage(channelAToB)).toEqual({ message: 'A2' })
427
432
  })
428
433
 
@@ -431,14 +436,18 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
431
436
 
432
437
  // send 10 times B1
433
438
  yield* Effect.forEach(
434
- Chunk.makeBy(count, (i) => ({ message: `B${i}` })),
439
+ Array.from({ length: count }, (_, i) => ({ message: `B${i}` })),
435
440
  channelBToA.send,
436
441
  { concurrency: 'unbounded' },
437
442
  )
438
443
 
439
- expect(yield* channelBToA.listen.pipe(Stream.flatten(), Stream.take(count), Stream.runCollect)).toEqual(
440
- Chunk.makeBy(count, (i) => ({ message: `A${i}` })),
441
- )
444
+ expect(
445
+ yield* channelBToA.listen.pipe(
446
+ Stream.mapEffect(Effect.fromResult),
447
+ Stream.take(count),
448
+ Stream.runCollect,
449
+ ),
450
+ ).toEqual(Array.from({ length: count }, (_, i) => ({ message: `A${i}` })))
442
451
  })
443
452
 
444
453
  yield* Effect.all([nodeACode, nodeBCode, connectNodes(nodeA, nodeB).pipe(Effect.delay(100))], {
@@ -456,7 +465,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
456
465
  })
457
466
 
458
467
  Vitest.describe('message channel specific tests', () => {
459
- Vitest.scopedLive('differing initial edge counter', (test) =>
468
+ Vitest.live('differing initial edge counter', (test) =>
460
469
  Effect.gen(function* () {
461
470
  const nodeA = yield* makeMeshNode('A')
462
471
  const nodeB = yield* makeMeshNode('B')
@@ -468,28 +477,32 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
468
477
  const bFiber = yield* Effect.gen(function* () {
469
478
  const channelBToA = yield* createChannel(nodeB, 'A')
470
479
  yield* channelBToA.listen.pipe(
471
- Stream.flatten(),
480
+ Stream.mapEffect(Effect.fromResult),
472
481
  Stream.tap((msg) => channelBToA.send({ message: `resp:${msg.message}` })),
473
482
  Stream.take(messageCount),
474
483
  Stream.runDrain,
475
484
  )
476
- }).pipe(Effect.scoped, Effect.fork)
485
+ }).pipe(Effect.scoped, Effect.forkChild)
477
486
 
478
487
  // yield* createChannel(nodeA, 'B').pipe(Effect.andThen(WebChannel.shutdown))
479
488
  // // yield* createChannel(nodeA, 'B').pipe(Effect.andThen(WebChannel.shutdown))
480
489
  // // yield* createChannel(nodeA, 'B').pipe(Effect.andThen(WebChannel.shutdown))
481
- yield* Effect.gen(function* () {
482
- const channelAToB = yield* createChannel(nodeA, 'B')
483
- yield* channelAToB.send({ message: 'A' })
484
- expect(yield* getFirstMessage(channelAToB)).toEqual({ message: 'resp:A' })
485
- }).pipe(Effect.scoped, Effect.repeatN(messageCount))
490
+ yield* Effect.replicateEffect(
491
+ Effect.gen(function* () {
492
+ const channelAToB = yield* createChannel(nodeA, 'B')
493
+ yield* channelAToB.send({ message: 'A' })
494
+ expect(yield* getFirstMessage(channelAToB)).toEqual({ message: 'resp:A' })
495
+ }).pipe(Effect.scoped),
496
+ messageCount,
497
+ { discard: true },
498
+ )
486
499
 
487
- yield* bFiber
500
+ yield* Fiber.join(bFiber)
488
501
  }).pipe(Vitest.withTestCtx(test)),
489
502
  )
490
503
  })
491
504
 
492
- Vitest.scopedLive('manual debug test', (test) =>
505
+ Vitest.live('manual debug test', (test) =>
493
506
  Effect.gen(function* () {
494
507
  const nodeA = yield* makeMeshNode('A')
495
508
  const nodeB = yield* makeMeshNode('B')
@@ -517,7 +530,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
517
530
  }).pipe(Vitest.withTestCtx(test)),
518
531
  )
519
532
 
520
- Vitest.scopedLive('broadcast edge with message channel', (test) =>
533
+ Vitest.live('broadcast edge with message channel', (test) =>
521
534
  Effect.gen(function* () {
522
535
  const nodeA = yield* makeMeshNode('A')
523
536
  const nodeB = yield* makeMeshNode('B')
@@ -525,13 +538,13 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
525
538
  yield* connectNodesViaBroadcastChannel(nodeA, nodeB)
526
539
 
527
540
  const err = yield* createChannel(nodeA, 'B', { mode: 'direct' }).pipe(Effect.timeout(200), Effect.flip)
528
- expect(err._tag).toBe('TimeoutException')
541
+ expect(err._tag).toBe('TimeoutError')
529
542
  }).pipe(Vitest.withTestCtx(test)),
530
543
  )
531
544
  })
532
545
 
533
546
  Vitest.describe('A <> B <> C', () => {
534
- Vitest.scopedLive('should work', (test) =>
547
+ Vitest.live('should work', (test) =>
535
548
  Effect.gen(function* () {
536
549
  const nodeA = yield* makeMeshNode('A')
537
550
  const nodeB = yield* makeMeshNode('B')
@@ -561,7 +574,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
561
574
  }).pipe(Vitest.withTestCtx(test)),
562
575
  )
563
576
 
564
- Vitest.scopedLive('should work - delayed edge', (test) =>
577
+ Vitest.live('should work - delayed edge', (test) =>
565
578
  Effect.gen(function* () {
566
579
  const nodeA = yield* makeMeshNode('A')
567
580
  const nodeB = yield* makeMeshNode('B')
@@ -596,7 +609,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
596
609
  }).pipe(Vitest.withTestCtx(test)),
597
610
  )
598
611
 
599
- Vitest.scopedLive('proxy channel', (test) =>
612
+ Vitest.live('proxy channel', (test) =>
600
613
  Effect.gen(function* () {
601
614
  const nodeA = yield* makeMeshNode('A')
602
615
  const nodeB = yield* makeMeshNode('B')
@@ -639,7 +652,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
639
652
  * 2. Verify the pattern works correctly with forked ACKs
640
653
  * 3. Serve as a reference for the fix in proxy-channel.ts
641
654
  */
642
- Vitest.scopedLive('ACK sending should not block message processing', (test) =>
655
+ Vitest.live('ACK sending should not block message processing', (test) =>
643
656
  Effect.gen(function* () {
644
657
  const nodeA = yield* makeMeshNode('A')
645
658
  const nodeB = yield* makeMeshNode('B')
@@ -655,34 +668,34 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
655
668
  const channelAToC = yield* createChannel(nodeA, 'C', { mode: 'proxy' })
656
669
  // Send multiple messages concurrently
657
670
  yield* Effect.forEach(
658
- Chunk.makeBy(messageCount, (i) => ({ message: `A${i}` })),
671
+ Array.from({ length: messageCount }, (_, i) => ({ message: `A${i}` })),
659
672
  channelAToC.send,
660
673
  { concurrency: 'unbounded' },
661
674
  )
662
675
  // Receive responses
663
676
  const responses = yield* channelAToC.listen.pipe(
664
- Stream.flatten(),
677
+ Stream.mapEffect(Effect.fromResult),
665
678
  Stream.take(messageCount),
666
679
  Stream.runCollect,
667
680
  )
668
- expect(Chunk.size(responses)).toBe(messageCount)
681
+ expect(responses.length).toBe(messageCount)
669
682
  })
670
683
 
671
684
  const nodeCCode = Effect.gen(function* () {
672
685
  const channelCToA = yield* createChannel(nodeC, 'A', { mode: 'proxy' })
673
686
  // Send multiple messages concurrently
674
687
  yield* Effect.forEach(
675
- Chunk.makeBy(messageCount, (i) => ({ message: `C${i}` })),
688
+ Array.from({ length: messageCount }, (_, i) => ({ message: `C${i}` })),
676
689
  channelCToA.send,
677
690
  { concurrency: 'unbounded' },
678
691
  )
679
692
  // Receive responses
680
693
  const responses = yield* channelCToA.listen.pipe(
681
- Stream.flatten(),
694
+ Stream.mapEffect(Effect.fromResult),
682
695
  Stream.take(messageCount),
683
696
  Stream.runCollect,
684
697
  )
685
- expect(Chunk.size(responses)).toBe(messageCount)
698
+ expect(responses.length).toBe(messageCount)
686
699
  })
687
700
 
688
701
  yield* Effect.all([nodeACode, nodeCCode], { concurrency: 'unbounded' })
@@ -716,7 +729,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
716
729
  * without being blocked by slow ACK sends. We measure how long it takes to receive
717
730
  * all messages - with forked ACKs it should be fast, with blocking ACKs it would be slow.
718
731
  */
719
- Vitest.scopedLive('messages arrive in listen queue without waiting for ACK send', (test) =>
732
+ Vitest.live('messages arrive in listen queue without waiting for ACK send', (test) =>
720
733
  Effect.gen(function* () {
721
734
  const ACK_DELAY_MS = 50
722
735
  const MESSAGE_COUNT = 5
@@ -741,7 +754,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
741
754
  // With forked ACKs, all messages get processed immediately at receiver
742
755
  // With blocking ACKs, messages would be processed one at a time with delays
743
756
  yield* Effect.forEach(
744
- Chunk.makeBy(MESSAGE_COUNT, (i) => ({ message: `msg${i}` })),
757
+ Array.from({ length: MESSAGE_COUNT }, (_, i) => ({ message: `msg${i}` })),
745
758
  channelAToB.send,
746
759
  { concurrency: 'unbounded' },
747
760
  )
@@ -767,7 +780,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
767
780
  })
768
781
 
769
782
  yield* channelBToA.listen.pipe(
770
- Stream.flatten(),
783
+ Stream.mapEffect(Effect.fromResult),
771
784
  Stream.tap((msg) =>
772
785
  Effect.sync(() => {
773
786
  receivedMessages.push(msg.message)
@@ -810,7 +823,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
810
823
  * This test sends multiple messages concurrently and verifies they're all
811
824
  * processed even when ACK sends are slow.
812
825
  */
813
- Vitest.scopedLive('concurrent messages processed despite slow ACK sends', (test) =>
826
+ Vitest.live('concurrent messages processed despite slow ACK sends', (test) =>
814
827
  Effect.gen(function* () {
815
828
  const ACK_DELAY_MS = 50
816
829
  const MESSAGE_COUNT = 5
@@ -833,7 +846,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
833
846
 
834
847
  // Send all messages concurrently
835
848
  yield* Effect.forEach(
836
- Chunk.makeBy(MESSAGE_COUNT, (i) => ({ message: `msg${i}` })),
849
+ Array.from({ length: MESSAGE_COUNT }, (_, i) => ({ message: `msg${i}` })),
837
850
  channelAToB.send,
838
851
  { concurrency: 'unbounded' },
839
852
  )
@@ -856,7 +869,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
856
869
  })
857
870
 
858
871
  yield* channelBToA.listen.pipe(
859
- Stream.flatten(),
872
+ Stream.mapEffect(Effect.fromResult),
860
873
  Stream.tap((msg) =>
861
874
  Effect.sync(() => {
862
875
  receivedMessages.push(msg.message)
@@ -885,7 +898,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
885
898
  )
886
899
  })
887
900
 
888
- Vitest.scopedLive('should fail with timeout due to missing edge', (test) =>
901
+ Vitest.live('should fail with timeout due to missing edge', (test) =>
889
902
  Effect.gen(function* () {
890
903
  const nodeA = yield* makeMeshNode('A')
891
904
  const nodeB = yield* makeMeshNode('B')
@@ -896,19 +909,19 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
896
909
 
897
910
  const nodeACode = Effect.gen(function* () {
898
911
  const err = yield* createChannel(nodeA, 'C').pipe(Effect.timeout(200), Effect.flip)
899
- expect(err._tag).toBe('TimeoutException')
912
+ expect(err._tag).toBe('TimeoutError')
900
913
  })
901
914
 
902
915
  const nodeCCode = Effect.gen(function* () {
903
916
  const err = yield* createChannel(nodeC, 'A').pipe(Effect.timeout(200), Effect.flip)
904
- expect(err._tag).toBe('TimeoutException')
917
+ expect(err._tag).toBe('TimeoutError')
905
918
  })
906
919
 
907
920
  yield* Effect.all([nodeACode, nodeCCode], { concurrency: 'unbounded' })
908
921
  }).pipe(Vitest.withTestCtx(test)),
909
922
  )
910
923
 
911
- Vitest.scopedLive('should fail with timeout due no transferable', (test) =>
924
+ Vitest.live('should fail with timeout due no transferable', (test) =>
912
925
  Effect.gen(function* () {
913
926
  const nodeA = yield* makeMeshNode('A')
914
927
  const nodeB = yield* makeMeshNode('B')
@@ -917,28 +930,28 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
917
930
 
918
931
  const nodeACode = Effect.gen(function* () {
919
932
  const err = yield* createChannel(nodeA, 'B').pipe(Effect.timeout(200), Effect.flip)
920
- expect(err._tag).toBe('TimeoutException')
933
+ expect(err._tag).toBe('TimeoutError')
921
934
  })
922
935
 
923
936
  const nodeBCode = Effect.gen(function* () {
924
937
  const err = yield* createChannel(nodeB, 'A').pipe(Effect.timeout(200), Effect.flip)
925
- expect(err._tag).toBe('TimeoutException')
938
+ expect(err._tag).toBe('TimeoutError')
926
939
  })
927
940
 
928
941
  yield* Effect.all([nodeACode, nodeBCode], { concurrency: 'unbounded' })
929
942
  }).pipe(Vitest.withTestCtx(test)),
930
943
  )
931
944
 
932
- Vitest.scopedLive('reconnect with re-created node', (test) =>
945
+ Vitest.live('reconnect with re-created node', (test) =>
933
946
  Effect.gen(function* () {
934
947
  const nodeCgen1Scope = yield* Scope.make()
935
948
 
936
949
  const nodeA = yield* makeMeshNode('A')
937
950
  const nodeB = yield* makeMeshNode('B')
938
- const nodeCgen1 = yield* makeMeshNode('C').pipe(Scope.extend(nodeCgen1Scope))
951
+ const nodeCgen1 = yield* makeMeshNode('C').pipe(Scope.provide(nodeCgen1Scope))
939
952
 
940
953
  yield* connectNodesViaMessageChannel(nodeA, nodeB)
941
- yield* connectNodesViaMessageChannel(nodeB, nodeCgen1).pipe(Scope.extend(nodeCgen1Scope))
954
+ yield* connectNodesViaMessageChannel(nodeB, nodeCgen1).pipe(Scope.provide(nodeCgen1Scope))
942
955
 
943
956
  const nodeACode = Effect.gen(function* () {
944
957
  const channelAToB = yield* createChannel(nodeA, 'C')
@@ -957,7 +970,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
957
970
 
958
971
  yield* Effect.all([nodeACode, nodeCCode(nodeCgen1)], { concurrency: 'unbounded' }).pipe(
959
972
  Effect.withSpan('test1'),
960
- Scope.extend(nodeCgen1Scope),
973
+ Scope.provide(nodeCgen1Scope),
961
974
  )
962
975
 
963
976
  yield* Scope.close(nodeCgen1Scope, Exit.void)
@@ -980,7 +993,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
980
993
  * D
981
994
  */
982
995
  Vitest.describe('diamond topology', () => {
983
- Vitest.scopedLive('should work', (test) =>
996
+ Vitest.live('should work', (test) =>
984
997
  Effect.gen(function* () {
985
998
  const nodeA = yield* makeMeshNode('A')
986
999
  const nodeB = yield* makeMeshNode('B')
@@ -1019,7 +1032,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1019
1032
  * Topology: Butterfly topology with two connected hubs (C-D) each serving multiple nodes
1020
1033
  */
1021
1034
  Vitest.describe('butterfly topology', () => {
1022
- Vitest.scopedLive('should work', (test) =>
1035
+ Vitest.live('should work', (test) =>
1023
1036
  Effect.gen(function* () {
1024
1037
  const nodeA = yield* makeMeshNode('A')
1025
1038
  const nodeB = yield* makeMeshNode('B')
@@ -1056,7 +1069,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1056
1069
  Vitest.describe('mixture of direct and proxy edge connections', () => {
1057
1070
  // TODO test case to better guard against case where side A tries to create a proxy channel to B
1058
1071
  // and side B tries to create a direct to A
1059
- Vitest.scopedLive('should work for proxy channels', (test) =>
1072
+ Vitest.live('should work for proxy channels', (test) =>
1060
1073
  Effect.gen(function* () {
1061
1074
  const nodeA = yield* makeMeshNode('A')
1062
1075
  const nodeB = yield* makeMeshNode('B')
@@ -1068,7 +1081,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1068
1081
  }).pipe(Vitest.withTestCtx(test)),
1069
1082
  )
1070
1083
 
1071
- Vitest.scopedLive('should work for directs', (test) =>
1084
+ Vitest.live('should work for directs', (test) =>
1072
1085
  Effect.gen(function* () {
1073
1086
  const nodeA = yield* makeMeshNode('A')
1074
1087
  const nodeB = yield* makeMeshNode('B')
@@ -1095,7 +1108,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1095
1108
  })
1096
1109
 
1097
1110
  Vitest.describe('listenForChannel', () => {
1098
- Vitest.scopedLive('connect later', (test) =>
1111
+ Vitest.live('connect later', (test) =>
1099
1112
  Effect.gen(function* () {
1100
1113
  const nodeA = yield* makeMeshNode('A')
1101
1114
 
@@ -1134,7 +1147,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1134
1147
  )
1135
1148
 
1136
1149
  // TODO provide a way to allow for reconnecting in the `listenForChannel` case
1137
- Vitest.scopedLive.skip('reconnect', (test) =>
1150
+ Vitest.live.skip('reconnect', (test) =>
1138
1151
  Effect.gen(function* () {
1139
1152
  const nodeA = yield* makeMeshNode('A')
1140
1153
 
@@ -1198,7 +1211,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1198
1211
  )
1199
1212
 
1200
1213
  Vitest.describe('prop tests', { timeout: propTestTimeout }, () => {
1201
- Vitest.scopedLive.prop(
1214
+ Vitest.live.prop(
1202
1215
  'listenForChannel A <> B <> C',
1203
1216
  [Delay, Delay, Delay, Delay, ChannelType],
1204
1217
  ([delayNodeA, delayNodeC, delayConnectAB, delayConnectBC, channelType], test) =>
@@ -1259,7 +1272,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1259
1272
  })
1260
1273
 
1261
1274
  Vitest.describe('broadcast channel', () => {
1262
- Vitest.scopedLive('should work', (test) =>
1275
+ Vitest.live('should work', (test) =>
1263
1276
  Effect.gen(function* () {
1264
1277
  const nodeA = yield* makeMeshNode('A')
1265
1278
  const nodeB = yield* makeMeshNode('B')
@@ -1272,23 +1285,23 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
1272
1285
  const channelOnC = yield* nodeC.makeBroadcastChannel({ channelName: 'test', schema: Schema.String })
1273
1286
 
1274
1287
  const listenOnAFiber = yield* channelOnA.listen.pipe(
1275
- Stream.flatten(),
1288
+ Stream.mapEffect(Effect.fromResult),
1276
1289
  Stream.runHead,
1277
- Effect.flatten,
1278
- Effect.fork,
1290
+ Effect.flatMap(Effect.fromOption),
1291
+ Effect.forkChild,
1279
1292
  )
1280
1293
  const listenOnCFiber = yield* channelOnC.listen.pipe(
1281
- Stream.flatten(),
1294
+ Stream.mapEffect(Effect.fromResult),
1282
1295
  Stream.runHead,
1283
- Effect.flatten,
1284
- Effect.fork,
1296
+ Effect.flatMap(Effect.fromOption),
1297
+ Effect.forkChild,
1285
1298
  )
1286
1299
 
1287
1300
  yield* channelOnA.send('A1')
1288
1301
  yield* channelOnC.send('C1')
1289
1302
 
1290
- expect(yield* listenOnAFiber).toEqual('C1')
1291
- expect(yield* listenOnCFiber).toEqual('A1')
1303
+ expect(yield* Fiber.join(listenOnAFiber)).toEqual('C1')
1304
+ expect(yield* Fiber.join(listenOnCFiber)).toEqual('A1')
1292
1305
  }).pipe(Vitest.withTestCtx(test)),
1293
1306
  )
1294
1307
  })