@livestore/webmesh 0.3.0-dev.37 → 0.3.0-dev.39
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/README.md +19 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/channel/{message-channel-internal.d.ts → direct-channel-internal.d.ts} +7 -7
- package/dist/channel/direct-channel-internal.d.ts.map +1 -0
- package/dist/channel/{message-channel-internal.js → direct-channel-internal.js} +22 -22
- package/dist/channel/direct-channel-internal.js.map +1 -0
- package/dist/channel/{message-channel.d.ts → direct-channel.d.ts} +3 -3
- package/dist/channel/direct-channel.d.ts.map +1 -0
- package/dist/channel/{message-channel.js → direct-channel.js} +17 -17
- package/dist/channel/direct-channel.js.map +1 -0
- package/dist/channel/proxy-channel.d.ts.map +1 -1
- package/dist/channel/proxy-channel.js +84 -21
- package/dist/channel/proxy-channel.js.map +1 -1
- package/dist/common.d.ts +11 -5
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +6 -1
- package/dist/common.js.map +1 -1
- package/dist/mesh-schema.d.ts +15 -15
- package/dist/mesh-schema.d.ts.map +1 -1
- package/dist/mesh-schema.js +9 -9
- package/dist/mesh-schema.js.map +1 -1
- package/dist/node.d.ts +10 -5
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +68 -30
- package/dist/node.js.map +1 -1
- package/dist/node.test.js +114 -17
- package/dist/node.test.js.map +1 -1
- package/dist/websocket-edge.d.ts +2 -1
- package/dist/websocket-edge.d.ts.map +1 -1
- package/dist/websocket-edge.js +6 -2
- package/dist/websocket-edge.js.map +1 -1
- package/package.json +3 -4
- package/src/channel/{message-channel-internal.ts → direct-channel-internal.ts} +29 -29
- package/src/channel/{message-channel.ts → direct-channel.ts} +20 -20
- package/src/channel/proxy-channel.ts +107 -25
- package/src/common.ts +12 -4
- package/src/mesh-schema.ts +16 -19
- package/src/node.test.ts +185 -17
- package/src/node.ts +97 -35
- package/src/websocket-edge.ts +7 -1
- package/dist/channel/message-channel-internal.d.ts.map +0 -1
- package/dist/channel/message-channel-internal.js.map +0 -1
- package/dist/channel/message-channel.d.ts.map +0 -1
- package/dist/channel/message-channel.js.map +0 -1
package/src/mesh-schema.ts
CHANGED
|
@@ -17,13 +17,13 @@ const defaultPacketFields = {
|
|
|
17
17
|
const remainingHopsUndefined = Schema.Undefined.pipe(Schema.optional)
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Needs to go through already existing
|
|
20
|
+
* Needs to go through already existing DirectChannel edges, times out otherwise
|
|
21
21
|
*
|
|
22
22
|
* Can't yet contain the `port` because the request might be duplicated while forwarding to multiple nodes.
|
|
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
|
|
26
|
+
export class DirectChannelRequest extends Schema.TaggedStruct('DirectChannelRequest', {
|
|
27
27
|
...defaultPacketFields,
|
|
28
28
|
remainingHops: Schema.Array(Schema.String).pipe(Schema.optional),
|
|
29
29
|
channelVersion: Schema.Number,
|
|
@@ -36,7 +36,7 @@ export class MessageChannelRequest extends Schema.TaggedStruct('MessageChannelRe
|
|
|
36
36
|
sourceId: Schema.String,
|
|
37
37
|
}) {}
|
|
38
38
|
|
|
39
|
-
export class
|
|
39
|
+
export class DirectChannelResponseSuccess extends Schema.TaggedStruct('DirectChannelResponseSuccess', {
|
|
40
40
|
...defaultPacketFields,
|
|
41
41
|
reqId: Schema.String,
|
|
42
42
|
port: Transferable.MessagePort,
|
|
@@ -45,14 +45,11 @@ export class MessageChannelResponseSuccess extends Schema.TaggedStruct('MessageC
|
|
|
45
45
|
channelVersion: Schema.Number,
|
|
46
46
|
}) {}
|
|
47
47
|
|
|
48
|
-
export class
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
remainingHops: Schema.Array(Schema.String),
|
|
54
|
-
},
|
|
55
|
-
) {}
|
|
48
|
+
export class DirectChannelResponseNoTransferables extends Schema.TaggedStruct('DirectChannelResponseNoTransferables', {
|
|
49
|
+
...defaultPacketFields,
|
|
50
|
+
reqId: Schema.String,
|
|
51
|
+
remainingHops: Schema.Array(Schema.String),
|
|
52
|
+
}) {}
|
|
56
53
|
|
|
57
54
|
export class ProxyChannelRequest extends Schema.TaggedStruct('ProxyChannelRequest', {
|
|
58
55
|
...defaultPacketFields,
|
|
@@ -124,10 +121,10 @@ export const BroadcastChannelPacket = Schema.TaggedStruct('BroadcastChannelPacke
|
|
|
124
121
|
target: Schema.Literal('-'),
|
|
125
122
|
})
|
|
126
123
|
|
|
127
|
-
export class
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
export class DirectChannelPacket extends Schema.Union(
|
|
125
|
+
DirectChannelRequest,
|
|
126
|
+
DirectChannelResponseSuccess,
|
|
127
|
+
DirectChannelResponseNoTransferables,
|
|
131
128
|
) {}
|
|
132
129
|
|
|
133
130
|
export class ProxyChannelPacket extends Schema.Union(
|
|
@@ -138,7 +135,7 @@ export class ProxyChannelPacket extends Schema.Union(
|
|
|
138
135
|
) {}
|
|
139
136
|
|
|
140
137
|
export class Packet extends Schema.Union(
|
|
141
|
-
|
|
138
|
+
DirectChannelPacket,
|
|
142
139
|
ProxyChannelPacket,
|
|
143
140
|
NetworkEdgeAdded,
|
|
144
141
|
NetworkTopologyRequest,
|
|
@@ -146,5 +143,5 @@ export class Packet extends Schema.Union(
|
|
|
146
143
|
BroadcastChannelPacket,
|
|
147
144
|
) {}
|
|
148
145
|
|
|
149
|
-
export class
|
|
150
|
-
export class
|
|
146
|
+
export class DirectChannelPing extends Schema.TaggedStruct('DirectChannelPing', {}) {}
|
|
147
|
+
export class DirectChannelPong extends Schema.TaggedStruct('DirectChannelPong', {}) {}
|
package/src/node.test.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
identity,
|
|
10
10
|
Layer,
|
|
11
11
|
Logger,
|
|
12
|
+
LogLevel,
|
|
12
13
|
Schema,
|
|
13
14
|
Scope,
|
|
14
15
|
Stream,
|
|
@@ -80,7 +81,7 @@ const createChannel = (source: MeshNode, target: string, options?: Partial<Param
|
|
|
80
81
|
channelName: options?.channelName ?? 'test',
|
|
81
82
|
schema: ExampleSchema,
|
|
82
83
|
// transferables: options?.transferables ?? 'prefer',
|
|
83
|
-
mode: options?.mode ?? '
|
|
84
|
+
mode: options?.mode ?? 'direct',
|
|
84
85
|
timeout: options?.timeout ?? 200,
|
|
85
86
|
})
|
|
86
87
|
|
|
@@ -108,7 +109,7 @@ const propTestTimeout = IS_CI ? 60_000 : 20_000
|
|
|
108
109
|
Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
109
110
|
const Delay = Schema.UndefinedOr(Schema.Literal(0, 1, 10, 50))
|
|
110
111
|
// NOTE for message channels, we test both with and without transferables (i.e. proxying)
|
|
111
|
-
const ChannelType = Schema.Literal('
|
|
112
|
+
const ChannelType = Schema.Literal('direct', 'proxy(via-messagechannel-edge)', 'proxy')
|
|
112
113
|
const NodeNames = Schema.Union(
|
|
113
114
|
Schema.Tuple(Schema.Literal('A'), Schema.Literal('B')),
|
|
114
115
|
Schema.Tuple(Schema.Literal('B'), Schema.Literal('A')),
|
|
@@ -117,17 +118,17 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
117
118
|
const fromChannelType = (
|
|
118
119
|
channelType: typeof ChannelType.Type,
|
|
119
120
|
): {
|
|
120
|
-
mode: '
|
|
121
|
+
mode: 'direct' | 'proxy'
|
|
121
122
|
connectNodes: typeof connectNodesViaMessageChannel | typeof connectNodesViaBroadcastChannel
|
|
122
123
|
} => {
|
|
123
124
|
switch (channelType) {
|
|
124
125
|
case 'proxy': {
|
|
125
126
|
return { mode: 'proxy', connectNodes: connectNodesViaBroadcastChannel }
|
|
126
127
|
}
|
|
127
|
-
case '
|
|
128
|
-
return { mode: '
|
|
128
|
+
case 'direct': {
|
|
129
|
+
return { mode: 'direct', connectNodes: connectNodesViaMessageChannel }
|
|
129
130
|
}
|
|
130
|
-
case 'messagechannel
|
|
131
|
+
case 'proxy(via-messagechannel-edge)': {
|
|
131
132
|
return { mode: 'proxy', connectNodes: connectNodesViaMessageChannel }
|
|
132
133
|
}
|
|
133
134
|
}
|
|
@@ -142,7 +143,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
142
143
|
}: {
|
|
143
144
|
nodeX: MeshNode
|
|
144
145
|
nodeY: MeshNode
|
|
145
|
-
channelType: '
|
|
146
|
+
channelType: 'direct' | 'proxy' | 'proxy(via-messagechannel-edge)'
|
|
146
147
|
numberOfMessages?: number
|
|
147
148
|
delays?: { x?: number; y?: number; connect?: number }
|
|
148
149
|
}) =>
|
|
@@ -177,12 +178,13 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
177
178
|
{ concurrency: 'unbounded' },
|
|
178
179
|
).pipe(Effect.withSpan(`exchangeMessages(${nodeLabel.x}↔${nodeLabel.y})`))
|
|
179
180
|
})
|
|
181
|
+
|
|
180
182
|
Vitest.describe('A <> B', () => {
|
|
181
183
|
Vitest.describe('prop tests', { timeout: propTestTimeout }, () => {
|
|
182
184
|
// const delayX = 40
|
|
183
185
|
// const delayY = undefined
|
|
184
186
|
// const connectDelay = undefined
|
|
185
|
-
// const channelType = '
|
|
187
|
+
// const channelType = 'direct'
|
|
186
188
|
// const nodeNames = ['B', 'A'] as const
|
|
187
189
|
// Vitest.scopedLive(
|
|
188
190
|
// 'a / b connect at different times with different channel types',
|
|
@@ -218,7 +220,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
218
220
|
{
|
|
219
221
|
// const waitForOfflineDelay = undefined
|
|
220
222
|
// const sleepDelay = 0
|
|
221
|
-
// const channelType = '
|
|
223
|
+
// const channelType = 'direct'
|
|
222
224
|
// Vitest.scopedLive(
|
|
223
225
|
// 'b reconnects',
|
|
224
226
|
// (test) =>
|
|
@@ -342,22 +344,22 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
342
344
|
}).pipe(withCtx(test)),
|
|
343
345
|
)
|
|
344
346
|
|
|
345
|
-
const ChannelTypeWithoutMessageChannelProxy = Schema.Literal('proxy', '
|
|
347
|
+
const ChannelTypeWithoutMessageChannelProxy = Schema.Literal('proxy', 'direct')
|
|
346
348
|
// TODO there seems to be a flaky case here which gets hit sometimes (e.g. 2025-02-28-17:11)
|
|
347
349
|
// Log output:
|
|
348
350
|
// test: { seed: -964670352, path: "1", endOnFailure: true }
|
|
349
|
-
// test: Counterexample: ["
|
|
351
|
+
// test: Counterexample: ["direct",["A","B"]]
|
|
350
352
|
// test: Shrunk 0 time(s)
|
|
351
353
|
// test: Got AssertionError: expected { _tag: 'MessageChannelPing' } to deeply equal { message: 'A1' }
|
|
352
354
|
// test: at next (/Users/schickling/Code/overtone/submodules/livestore/packages/@livestore/webmesh/src/node.test.ts:376:59)
|
|
353
|
-
// test: at prop tests:replace edge while keeping the channel:channelType=
|
|
355
|
+
// test: at prop tests:replace edge while keeping the channel:channelType=direct nodeNames=A,B (/Users/schickling/Code/overtone/submodules/livestore/packages/@livestore/webmesh/src/node.test.ts:801:14)
|
|
354
356
|
// test: Hint: Enable verbose mode in order to have the list of all failing values encountered during the run
|
|
355
357
|
// test: ✓ webmesh node > A <> B > prop tests > TODO improve latency > concurrent messages 2110ms
|
|
356
358
|
// test: ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
|
|
357
359
|
// test: FAIL src/node.test.ts > webmesh node > A <> B > prop tests > replace edge while keeping the channel
|
|
358
360
|
// test: Error: Property failed after 2 tests
|
|
359
361
|
// test: { seed: -964670352, path: "1", endOnFailure: true }
|
|
360
|
-
// test: Counterexample: ["
|
|
362
|
+
// test: Counterexample: ["direct",["A","B"]]
|
|
361
363
|
Vitest.scopedLive.prop(
|
|
362
364
|
'replace edge while keeping the channel',
|
|
363
365
|
[ChannelTypeWithoutMessageChannelProxy, NodeNames],
|
|
@@ -540,7 +542,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
540
542
|
|
|
541
543
|
yield* connectNodesViaBroadcastChannel(nodeA, nodeB)
|
|
542
544
|
|
|
543
|
-
const err = yield* createChannel(nodeA, 'B', { mode: '
|
|
545
|
+
const err = yield* createChannel(nodeA, 'B', { mode: 'direct' }).pipe(Effect.timeout(200), Effect.flip)
|
|
544
546
|
expect(err._tag).toBe('TimeoutException')
|
|
545
547
|
}).pipe(withCtx(test)),
|
|
546
548
|
)
|
|
@@ -805,9 +807,9 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
805
807
|
)
|
|
806
808
|
})
|
|
807
809
|
|
|
808
|
-
Vitest.describe('mixture of
|
|
810
|
+
Vitest.describe('mixture of direct and proxy edge connections', () => {
|
|
809
811
|
// TODO test case to better guard against case where side A tries to create a proxy channel to B
|
|
810
|
-
// and side B tries to create a
|
|
812
|
+
// and side B tries to create a direct to A
|
|
811
813
|
Vitest.scopedLive('should work for proxy channels', (test) =>
|
|
812
814
|
Effect.gen(function* () {
|
|
813
815
|
const nodeA = yield* makeMeshNode('A')
|
|
@@ -820,7 +822,7 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
820
822
|
}).pipe(withCtx(test)),
|
|
821
823
|
)
|
|
822
824
|
|
|
823
|
-
Vitest.scopedLive('should work for
|
|
825
|
+
Vitest.scopedLive('should work for directs', (test) =>
|
|
824
826
|
Effect.gen(function* () {
|
|
825
827
|
const nodeA = yield* makeMeshNode('A')
|
|
826
828
|
const nodeB = yield* makeMeshNode('B')
|
|
@@ -846,6 +848,171 @@ Vitest.describe('webmesh node', { timeout: testTimeout }, () => {
|
|
|
846
848
|
)
|
|
847
849
|
})
|
|
848
850
|
|
|
851
|
+
Vitest.describe('listenForChannel', () => {
|
|
852
|
+
Vitest.scopedLive('connect later', (test) =>
|
|
853
|
+
Effect.gen(function* () {
|
|
854
|
+
const nodeA = yield* makeMeshNode('A')
|
|
855
|
+
|
|
856
|
+
const mode = 'direct' as 'proxy' | 'direct'
|
|
857
|
+
const connect = mode === 'direct' ? connectNodesViaMessageChannel : connectNodesViaBroadcastChannel
|
|
858
|
+
|
|
859
|
+
const nodeACode = Effect.gen(function* () {
|
|
860
|
+
const channelAToB = yield* createChannel(nodeA, 'B', { channelName: 'test', mode })
|
|
861
|
+
yield* channelAToB.send({ message: 'A1' })
|
|
862
|
+
expect(yield* getFirstMessage(channelAToB)).toEqual({ message: 'B1' })
|
|
863
|
+
})
|
|
864
|
+
|
|
865
|
+
const nodeBCode = Effect.gen(function* () {
|
|
866
|
+
const nodeB = yield* makeMeshNode('B')
|
|
867
|
+
yield* connect(nodeA, nodeB)
|
|
868
|
+
|
|
869
|
+
yield* nodeB.listenForChannel.pipe(
|
|
870
|
+
Stream.filter((_) => _.channelName === 'test' && _.source === 'A' && _.mode === mode),
|
|
871
|
+
Stream.tap(
|
|
872
|
+
Effect.fn(function* (channelInfo) {
|
|
873
|
+
const channel = yield* createChannel(nodeB, channelInfo.source, {
|
|
874
|
+
channelName: channelInfo.channelName,
|
|
875
|
+
mode,
|
|
876
|
+
})
|
|
877
|
+
yield* channel.send({ message: 'B1' })
|
|
878
|
+
expect(yield* getFirstMessage(channel)).toEqual({ message: 'A1' })
|
|
879
|
+
}),
|
|
880
|
+
),
|
|
881
|
+
Stream.take(1),
|
|
882
|
+
Stream.runDrain,
|
|
883
|
+
)
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
yield* Effect.all([nodeACode, nodeBCode.pipe(Effect.delay(500))], { concurrency: 'unbounded' })
|
|
887
|
+
}).pipe(withCtx(test)),
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
// TODO provide a way to allow for reconnecting in the `listenForChannel` case
|
|
891
|
+
Vitest.scopedLive.skip('reconnect', (test) =>
|
|
892
|
+
Effect.gen(function* () {
|
|
893
|
+
const nodeA = yield* makeMeshNode('A')
|
|
894
|
+
|
|
895
|
+
const mode = 'direct' as 'proxy' | 'direct'
|
|
896
|
+
const connect = mode === 'direct' ? connectNodesViaMessageChannel : connectNodesViaBroadcastChannel
|
|
897
|
+
|
|
898
|
+
const nodeACode = Effect.gen(function* () {
|
|
899
|
+
const channelAToB = yield* createChannel(nodeA, 'B', { channelName: 'test', mode })
|
|
900
|
+
yield* channelAToB.send({ message: 'A1' })
|
|
901
|
+
expect(yield* getFirstMessage(channelAToB)).toEqual({ message: 'B1' })
|
|
902
|
+
})
|
|
903
|
+
|
|
904
|
+
const nodeBCode = Effect.gen(function* () {
|
|
905
|
+
const nodeB = yield* makeMeshNode('B')
|
|
906
|
+
yield* connect(nodeA, nodeB)
|
|
907
|
+
|
|
908
|
+
yield* nodeB.listenForChannel.pipe(
|
|
909
|
+
Stream.filter((_) => _.channelName === 'test' && _.source === 'A' && _.mode === mode),
|
|
910
|
+
Stream.tap(
|
|
911
|
+
Effect.fn(function* (channelInfo) {
|
|
912
|
+
const channel = yield* createChannel(nodeB, channelInfo.source, {
|
|
913
|
+
channelName: channelInfo.channelName,
|
|
914
|
+
mode,
|
|
915
|
+
})
|
|
916
|
+
yield* channel.send({ message: 'B1' })
|
|
917
|
+
expect(yield* getFirstMessage(channel)).toEqual({ message: 'A1' })
|
|
918
|
+
}),
|
|
919
|
+
),
|
|
920
|
+
Stream.take(1),
|
|
921
|
+
Stream.runDrain,
|
|
922
|
+
)
|
|
923
|
+
}).pipe(
|
|
924
|
+
Effect.withSpan('nodeBCode:gen1'),
|
|
925
|
+
Effect.andThen(
|
|
926
|
+
Effect.gen(function* () {
|
|
927
|
+
const nodeB = yield* makeMeshNode('B')
|
|
928
|
+
yield* connect(nodeA, nodeB, { replaceIfExists: true })
|
|
929
|
+
|
|
930
|
+
yield* nodeB.listenForChannel.pipe(
|
|
931
|
+
Stream.filter((_) => _.channelName === 'test' && _.source === 'A' && _.mode === mode),
|
|
932
|
+
Stream.tap(
|
|
933
|
+
Effect.fn(function* (channelInfo) {
|
|
934
|
+
const channel = yield* createChannel(nodeB, channelInfo.source, {
|
|
935
|
+
channelName: channelInfo.channelName,
|
|
936
|
+
mode,
|
|
937
|
+
})
|
|
938
|
+
console.log('recreated channel', channel)
|
|
939
|
+
// yield* channel.send({ message: 'B1' })
|
|
940
|
+
// expect(yield* getFirstMessage(channel)).toEqual({ message: 'A1' })
|
|
941
|
+
}),
|
|
942
|
+
),
|
|
943
|
+
Stream.take(1),
|
|
944
|
+
Stream.runDrain,
|
|
945
|
+
)
|
|
946
|
+
}).pipe(Effect.withSpan('nodeBCode:gen2')),
|
|
947
|
+
),
|
|
948
|
+
)
|
|
949
|
+
|
|
950
|
+
yield* Effect.all([nodeACode, nodeBCode], { concurrency: 'unbounded' })
|
|
951
|
+
}).pipe(withCtx(test)),
|
|
952
|
+
)
|
|
953
|
+
|
|
954
|
+
Vitest.describe('prop tests', { timeout: propTestTimeout }, () => {
|
|
955
|
+
Vitest.scopedLive.prop(
|
|
956
|
+
'listenForChannel A <> B <> C',
|
|
957
|
+
[Delay, Delay, Delay, Delay, ChannelType],
|
|
958
|
+
([delayNodeA, delayNodeC, delayConnectAB, delayConnectBC, channelType], test) =>
|
|
959
|
+
Effect.gen(function* () {
|
|
960
|
+
const nodeA = yield* makeMeshNode('A')
|
|
961
|
+
const nodeB = yield* makeMeshNode('B')
|
|
962
|
+
const nodeC = yield* makeMeshNode('C')
|
|
963
|
+
|
|
964
|
+
const mode = channelType.includes('proxy') ? 'proxy' : 'direct'
|
|
965
|
+
const connect = channelType === 'direct' ? connectNodesViaMessageChannel : connectNodesViaBroadcastChannel
|
|
966
|
+
yield* connect(nodeA, nodeB).pipe(maybeDelay(delayConnectAB, 'delayConnectAB'))
|
|
967
|
+
yield* connect(nodeB, nodeC).pipe(maybeDelay(delayConnectBC, 'delayConnectBC'))
|
|
968
|
+
|
|
969
|
+
const nodeACode = Effect.gen(function* () {
|
|
970
|
+
const _channel2AToC = yield* createChannel(nodeA, 'C', { channelName: 'test-2', mode })
|
|
971
|
+
|
|
972
|
+
const channelAToC = yield* createChannel(nodeA, 'C', { channelName: 'test-1', mode })
|
|
973
|
+
yield* channelAToC.send({ message: 'A1' })
|
|
974
|
+
expect(yield* getFirstMessage(channelAToC)).toEqual({ message: 'C1' })
|
|
975
|
+
})
|
|
976
|
+
|
|
977
|
+
const nodeCCode = Effect.gen(function* () {
|
|
978
|
+
const _channel2CToA = yield* createChannel(nodeC, 'A', { channelName: 'test-2', mode })
|
|
979
|
+
|
|
980
|
+
yield* nodeC.listenForChannel.pipe(
|
|
981
|
+
Stream.filter((_) => _.channelName === 'test-1' && _.source === 'A' && _.mode === mode),
|
|
982
|
+
Stream.tap(
|
|
983
|
+
Effect.fn(function* (channelInfo) {
|
|
984
|
+
const channel = yield* createChannel(nodeC, channelInfo.source, {
|
|
985
|
+
channelName: channelInfo.channelName,
|
|
986
|
+
mode,
|
|
987
|
+
})
|
|
988
|
+
yield* channel.send({ message: 'C1' })
|
|
989
|
+
expect(yield* getFirstMessage(channel)).toEqual({ message: 'A1' })
|
|
990
|
+
}),
|
|
991
|
+
),
|
|
992
|
+
Stream.take(1),
|
|
993
|
+
Stream.runDrain,
|
|
994
|
+
)
|
|
995
|
+
})
|
|
996
|
+
|
|
997
|
+
yield* Effect.all(
|
|
998
|
+
[
|
|
999
|
+
nodeACode.pipe(maybeDelay(delayNodeA, 'nodeACode')),
|
|
1000
|
+
nodeCCode.pipe(maybeDelay(delayNodeC, 'nodeCCode')),
|
|
1001
|
+
],
|
|
1002
|
+
{ concurrency: 'unbounded' },
|
|
1003
|
+
)
|
|
1004
|
+
}).pipe(
|
|
1005
|
+
withCtx(test, {
|
|
1006
|
+
skipOtel: true,
|
|
1007
|
+
suffix: `delayNodeA=${delayNodeA} delayNodeC=${delayNodeC} delayConnectAB=${delayConnectAB} delayConnectBC=${delayConnectBC} channelType=${channelType}`,
|
|
1008
|
+
timeout: testTimeout * 2,
|
|
1009
|
+
}),
|
|
1010
|
+
),
|
|
1011
|
+
{ fastCheck: { numRuns: 10 } },
|
|
1012
|
+
)
|
|
1013
|
+
})
|
|
1014
|
+
})
|
|
1015
|
+
|
|
849
1016
|
Vitest.describe('broadcast channel', () => {
|
|
850
1017
|
Vitest.scopedLive('should work', (test) =>
|
|
851
1018
|
Effect.gen(function* () {
|
|
@@ -893,6 +1060,7 @@ const withCtx =
|
|
|
893
1060
|
self.pipe(
|
|
894
1061
|
Effect.timeout(timeout),
|
|
895
1062
|
Effect.provide(Logger.pretty),
|
|
1063
|
+
Logger.withMinimumLogLevel(LogLevel.Debug),
|
|
896
1064
|
Effect.scoped, // We need to scope the effect manually here because otherwise the span is not closed
|
|
897
1065
|
Effect.withSpan(`${testContext.task.suite?.name}:${testContext.task.name}${suffix ? `:${suffix}` : ''}`),
|
|
898
1066
|
skipOtel ? identity : Effect.provide(otelLayer),
|