@livestore/webmesh 0.4.0-dev.22 → 0.4.0-dev.23
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 +5 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/channel/direct-channel-internal.d.ts.map +1 -1
- package/dist/channel/direct-channel-internal.js +15 -23
- package/dist/channel/direct-channel-internal.js.map +1 -1
- package/dist/channel/direct-channel.js +4 -4
- package/dist/channel/direct-channel.js.map +1 -1
- package/dist/channel/proxy-channel.d.ts +26 -1
- package/dist/channel/proxy-channel.d.ts.map +1 -1
- package/dist/channel/proxy-channel.js +64 -18
- package/dist/channel/proxy-channel.js.map +1 -1
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +5 -6
- package/dist/common.js.map +1 -1
- package/dist/node.d.ts +6 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +25 -23
- package/dist/node.js.map +1 -1
- package/dist/node.test.js +192 -5
- package/dist/node.test.js.map +1 -1
- package/dist/websocket-edge.d.ts +1 -1
- package/dist/websocket-edge.d.ts.map +1 -1
- package/dist/websocket-edge.js +5 -7
- package/dist/websocket-edge.js.map +1 -1
- package/dist/websocket-edge.test.d.ts +7 -0
- package/dist/websocket-edge.test.d.ts.map +1 -0
- package/dist/websocket-edge.test.js +74 -0
- package/dist/websocket-edge.test.js.map +1 -0
- package/package.json +65 -12
- package/src/channel/direct-channel-internal.ts +17 -40
- package/src/channel/direct-channel.ts +4 -4
- package/src/channel/proxy-channel.ts +85 -25
- package/src/common.ts +5 -6
- package/src/node.test.ts +270 -7
- package/src/node.ts +31 -23
- package/src/websocket-edge.test.ts +98 -0
- package/src/websocket-edge.ts +7 -9
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
Deferred,
|
|
5
5
|
Effect,
|
|
6
6
|
Exit,
|
|
7
|
-
OtelTracer,
|
|
8
7
|
Predicate,
|
|
9
8
|
Queue,
|
|
10
9
|
Schema,
|
|
@@ -16,11 +15,6 @@ import {
|
|
|
16
15
|
import { type ChannelName, type MeshNodeName, type MessageQueueItem, packetAsOtelAttributes } from '../common.ts'
|
|
17
16
|
import * as MeshSchema from '../mesh-schema.ts'
|
|
18
17
|
|
|
19
|
-
// WORKAROUND: @effect/opentelemetry mis-parses `Span.addEvent(name, attributes)` and treats the attributes object as a
|
|
20
|
-
// time input, causing `TypeError: {} is not iterable` at runtime.
|
|
21
|
-
// Upstream: https://github.com/Effect-TS/effect/pull/5929
|
|
22
|
-
// TODO: simplify back to the 2-arg overload once the upstream fix is released and adopted.
|
|
23
|
-
|
|
24
18
|
export interface MakeDirectChannelArgs {
|
|
25
19
|
nodeName: MeshNodeName
|
|
26
20
|
/** Queue of incoming messages for this channel */
|
|
@@ -94,11 +88,6 @@ export const makeDirectChannelInternal = ({
|
|
|
94
88
|
|
|
95
89
|
const deferred = yield* makeDeferredResult()
|
|
96
90
|
|
|
97
|
-
const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.catchAll(() => Effect.succeed(undefined)))
|
|
98
|
-
// const span = {
|
|
99
|
-
// addEvent: (...msg: any[]) => console.log(`${nodeName}→${channelName}→${target}[${channelVersion}]`, ...msg),
|
|
100
|
-
// }
|
|
101
|
-
|
|
102
91
|
const schema = {
|
|
103
92
|
send: Schema.Union(schema_.send, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong),
|
|
104
93
|
listen: Schema.Union(schema_.listen, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong),
|
|
@@ -112,16 +101,12 @@ export const makeDirectChannelInternal = ({
|
|
|
112
101
|
Effect.gen(function* () {
|
|
113
102
|
const channelState = channelStateRef.current
|
|
114
103
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
packetChannelVersion: Predicate.hasProperty('channelVersion')(packet) ? packet.channelVersion : undefined,
|
|
122
|
-
},
|
|
123
|
-
undefined,
|
|
124
|
-
)
|
|
104
|
+
yield* Effect.spanEvent(`process:${packet._tag}`, {
|
|
105
|
+
channelState: channelState._tag,
|
|
106
|
+
packetId: packet.id,
|
|
107
|
+
packetReqId: packet.reqId,
|
|
108
|
+
...(Predicate.hasProperty('channelVersion')(packet) === true ? { packetChannelVersion: packet.channelVersion } : {}),
|
|
109
|
+
})
|
|
125
110
|
|
|
126
111
|
// const reqIdStr =
|
|
127
112
|
// Predicate.hasProperty('reqId')(packet) && packet.reqId !== undefined ? ` for ${packet.reqId}` : ''
|
|
@@ -139,7 +124,7 @@ export const makeDirectChannelInternal = ({
|
|
|
139
124
|
// If the other side has a higher version, we need to close this channel and
|
|
140
125
|
// recreate it with the new version
|
|
141
126
|
if (packet.channelVersion > channelVersion) {
|
|
142
|
-
|
|
127
|
+
yield* Effect.spanEvent(`incoming packet has higher version (${packet.channelVersion}), closing channel`)
|
|
143
128
|
yield* Scope.close(scope, Exit.succeed('higher-version-expected'))
|
|
144
129
|
// TODO include expected version in the error so the channel gets recreated with the new version
|
|
145
130
|
return 'close'
|
|
@@ -158,7 +143,7 @@ export const makeDirectChannelInternal = ({
|
|
|
158
143
|
remainingHops: packet.hops,
|
|
159
144
|
reqId: undefined,
|
|
160
145
|
})
|
|
161
|
-
|
|
146
|
+
yield* Effect.spanEvent(
|
|
162
147
|
`incoming packet has lower version (${packet.channelVersion}), sending request to reconnect (${newPacket.id})`,
|
|
163
148
|
)
|
|
164
149
|
|
|
@@ -173,7 +158,7 @@ export const makeDirectChannelInternal = ({
|
|
|
173
158
|
} else {
|
|
174
159
|
// In case the instance of the source has changed, we need to close the channel
|
|
175
160
|
// and reconnect with a new channel
|
|
176
|
-
|
|
161
|
+
yield* Effect.spanEvent(`force-new-channel`)
|
|
177
162
|
yield* Scope.close(scope, Exit.succeed('force-new-channel'))
|
|
178
163
|
return 'close'
|
|
179
164
|
}
|
|
@@ -200,15 +185,15 @@ export const makeDirectChannelInternal = ({
|
|
|
200
185
|
remainingHops: packet.hops,
|
|
201
186
|
reqId: packet.id,
|
|
202
187
|
})
|
|
203
|
-
|
|
188
|
+
yield* Effect.spanEvent(`Re-sending new request (${newRequestPacket.id}) for incoming request (${packet.id})`)
|
|
204
189
|
|
|
205
190
|
yield* sendPacket(newRequestPacket)
|
|
206
191
|
}
|
|
207
192
|
|
|
208
193
|
const isWinner = nodeName > target
|
|
209
194
|
|
|
210
|
-
if (isWinner) {
|
|
211
|
-
|
|
195
|
+
if (isWinner === true) {
|
|
196
|
+
yield* Effect.spanEvent(`winner side: creating direct channel and sending response`)
|
|
212
197
|
const mc = new MessageChannel()
|
|
213
198
|
|
|
214
199
|
// We're using a direct channel with acks here to make sure messages are not lost
|
|
@@ -236,8 +221,6 @@ export const makeDirectChannelInternal = ({
|
|
|
236
221
|
|
|
237
222
|
channelStateRef.current = { _tag: 'winner:ResponseSent', channel, otherSourceId: packet.sourceId }
|
|
238
223
|
|
|
239
|
-
// span?.addEvent(`winner side: waiting for ping`)
|
|
240
|
-
|
|
241
224
|
// Now we wait for the other side to respond via the channel
|
|
242
225
|
yield* channel.listen.pipe(
|
|
243
226
|
Stream.flatten(),
|
|
@@ -246,21 +229,19 @@ export const makeDirectChannelInternal = ({
|
|
|
246
229
|
Stream.runDrain,
|
|
247
230
|
)
|
|
248
231
|
|
|
249
|
-
// span?.addEvent(`winner side: sending pong`)
|
|
250
|
-
|
|
251
232
|
yield* channel.send(MeshSchema.DirectChannelPong.make({}))
|
|
252
233
|
|
|
253
|
-
|
|
234
|
+
yield* Effect.spanEvent(`winner side: established`)
|
|
254
235
|
channelStateRef.current = { _tag: 'Established', otherSourceId: packet.sourceId }
|
|
255
236
|
|
|
256
237
|
yield* Deferred.succeed(deferred, channel)
|
|
257
238
|
} else {
|
|
258
|
-
|
|
239
|
+
yield* Effect.spanEvent(`loser side: waiting for response`)
|
|
259
240
|
// Wait for `DirectChannelResponseSuccess` packet
|
|
260
241
|
channelStateRef.current = { _tag: 'loser:WaitingForResponse', otherSourceId: packet.sourceId }
|
|
261
242
|
}
|
|
262
243
|
|
|
263
|
-
|
|
244
|
+
return
|
|
264
245
|
}
|
|
265
246
|
case 'DirectChannelResponseSuccess': {
|
|
266
247
|
if (channelState._tag !== 'loser:WaitingForResponse') {
|
|
@@ -284,8 +265,6 @@ export const makeDirectChannelInternal = ({
|
|
|
284
265
|
Effect.fork,
|
|
285
266
|
)
|
|
286
267
|
|
|
287
|
-
// span?.addEvent(`loser side: sending ping`)
|
|
288
|
-
|
|
289
268
|
// There seems to be some scenario where the initial ping message is lost.
|
|
290
269
|
// As a workaround until we find the root cause, we're retrying the ping a few times.
|
|
291
270
|
// TODO write a test that reproduces this issue and fix the root cause ()
|
|
@@ -294,11 +273,9 @@ export const makeDirectChannelInternal = ({
|
|
|
294
273
|
.send(MeshSchema.DirectChannelPing.make({}))
|
|
295
274
|
.pipe(Effect.timeout(10), Effect.retry({ times: 2 }))
|
|
296
275
|
|
|
297
|
-
// span?.addEvent(`loser side: waiting for pong`)
|
|
298
|
-
|
|
299
276
|
yield* waitForPongFiber
|
|
300
277
|
|
|
301
|
-
|
|
278
|
+
yield* Effect.spanEvent(`loser side: established`)
|
|
302
279
|
channelStateRef.current = { _tag: 'Established', otherSourceId: channelState.otherSourceId }
|
|
303
280
|
|
|
304
281
|
yield* Deferred.succeed(deferred, channel)
|
|
@@ -354,7 +331,7 @@ export const makeDirectChannelInternal = ({
|
|
|
354
331
|
}
|
|
355
332
|
|
|
356
333
|
yield* sendPacket(packet)
|
|
357
|
-
|
|
334
|
+
yield* Effect.spanEvent(`initial edge request sent (${packet.id})`)
|
|
358
335
|
})
|
|
359
336
|
|
|
360
337
|
yield* edgeRequest
|
|
@@ -61,7 +61,7 @@ export const makeDirectChannel = ({
|
|
|
61
61
|
innerChannelRef: { current: undefined as WebChannel.WebChannel<any, any> | undefined },
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
//#region reconnect-loop
|
|
65
65
|
yield* Effect.gen(function* () {
|
|
66
66
|
const resultDeferred = yield* Deferred.make<{
|
|
67
67
|
channel: WebChannel.WebChannel<any, any>
|
|
@@ -133,8 +133,8 @@ export const makeDirectChannel = ({
|
|
|
133
133
|
yield* Scope.close(makeDirectChannelScope, channelExit)
|
|
134
134
|
|
|
135
135
|
if (
|
|
136
|
-
Cause.isFailType(channelExit.cause) &&
|
|
137
|
-
Schema.is(WebmeshSchema.DirectChannelResponseNoTransferables)(channelExit.cause.error)
|
|
136
|
+
Cause.isFailType(channelExit.cause) === true &&
|
|
137
|
+
Schema.is(WebmeshSchema.DirectChannelResponseNoTransferables)(channelExit.cause.error) === true
|
|
138
138
|
) {
|
|
139
139
|
// Only retry when there is a new edge available
|
|
140
140
|
yield* waitForNewEdgeFiber.pipe(Effect.exit)
|
|
@@ -193,7 +193,7 @@ export const makeDirectChannel = ({
|
|
|
193
193
|
Effect.tapCauseLogPretty,
|
|
194
194
|
Effect.forkScoped,
|
|
195
195
|
)
|
|
196
|
-
|
|
196
|
+
//#endregion reconnect-loop
|
|
197
197
|
|
|
198
198
|
const parentSpan = yield* Effect.currentSpan.pipe(Effect.orDie)
|
|
199
199
|
|
|
@@ -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.pipe(Schema.between(0, 500)),
|
|
43
|
+
/** Delay after forking the ACK send, before adding message to listen queue */
|
|
44
|
+
afterAckFork: Schema.Int.pipe(Schema.between(0, 500)),
|
|
45
|
+
/** Delay after adding message to listen queue */
|
|
46
|
+
afterListenQueueOffer: Schema.Int.pipe(Schema.between(0, 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
|
|
@@ -37,6 +69,8 @@ interface MakeProxyChannelArgs {
|
|
|
37
69
|
send: Schema.Schema<any, any>
|
|
38
70
|
listen: Schema.Schema<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'
|
|
@@ -64,7 +107,7 @@ export const makeProxyChannel = ({
|
|
|
64
107
|
_tag: 'Established'
|
|
65
108
|
listenSchema: Schema.Schema<any, any>
|
|
66
109
|
listenQueue: Queue.Queue<any>
|
|
67
|
-
ackMap: Map<string, Deferred.Deferred<void
|
|
110
|
+
ackMap: Map<string, Deferred.Deferred<void>>
|
|
68
111
|
combinedChannelId: string
|
|
69
112
|
}
|
|
70
113
|
|
|
@@ -117,7 +160,7 @@ export const makeProxyChannel = ({
|
|
|
117
160
|
)
|
|
118
161
|
|
|
119
162
|
const getCombinedChannelId = (otherSideChannelIdCandidate: string) =>
|
|
120
|
-
[channelIdCandidate, otherSideChannelIdCandidate].
|
|
163
|
+
[channelIdCandidate, otherSideChannelIdCandidate].toSorted().join('_')
|
|
121
164
|
|
|
122
165
|
const earlyPayloadBuffer = yield* Queue.unbounded<typeof MeshSchema.ProxyChannelPayload.Type>().pipe(
|
|
123
166
|
Effect.acquireRelease(Queue.shutdown),
|
|
@@ -255,34 +298,46 @@ export const makeProxyChannel = ({
|
|
|
255
298
|
)
|
|
256
299
|
}
|
|
257
300
|
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
301
|
+
// Send ACK fire-and-forget to avoid blocking message processing
|
|
302
|
+
// This is critical because blocking ACK sends can prevent messages from reaching the listen queue
|
|
303
|
+
// See test: "ACK forkScoped regression tests" for documentation
|
|
304
|
+
|
|
305
|
+
// The ACK send effect with optional simulation delay INSIDE the fork
|
|
306
|
+
// This is key for testing: with forkScoped, the delay happens in background and doesn't block message processing
|
|
307
|
+
// Without forkScoped (blocking), the delay would block the message from being added to listen queue
|
|
308
|
+
const ackSendEffect = Effect.gen(function* () {
|
|
309
|
+
yield* simSleep('onPayload', 'beforeAckSend')
|
|
310
|
+
yield* respondToSender(
|
|
311
|
+
MeshSchema.ProxyChannelPayloadAck.make({
|
|
312
|
+
reqId: packet.id,
|
|
313
|
+
remainingHops: packet.hops,
|
|
314
|
+
hops: [],
|
|
315
|
+
target,
|
|
316
|
+
source: nodeName,
|
|
317
|
+
channelName,
|
|
318
|
+
combinedChannelId:
|
|
319
|
+
channelState._tag === 'Established' ? channelState.combinedChannelId : packet.combinedChannelId,
|
|
320
|
+
}),
|
|
321
|
+
)
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
yield* ackSendEffect.pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
|
|
325
|
+
|
|
326
|
+
// Simulation point: delay after forking ACK (before processing message)
|
|
327
|
+
yield* simSleep('onPayload', 'afterAckFork')
|
|
271
328
|
|
|
272
329
|
if (channelState._tag === 'Established') {
|
|
273
330
|
const decodedMessage = yield* Schema.decodeUnknown(channelState.listenSchema)(packet.payload)
|
|
274
331
|
yield* channelState.listenQueue.pipe(Queue.offer(decodedMessage))
|
|
332
|
+
|
|
333
|
+
// Simulation point: delay after adding to listen queue
|
|
334
|
+
yield* simSleep('onPayload', 'afterListenQueueOffer')
|
|
275
335
|
} else {
|
|
276
|
-
// yield* Effect.logDebug(
|
|
277
|
-
// `[${nodeName}] Buffering early payload reqId: ${packet.id} (state: ${channelState._tag})`,
|
|
278
|
-
// )
|
|
279
336
|
yield* Queue.offer(earlyPayloadBuffer, packet)
|
|
280
337
|
}
|
|
281
338
|
return
|
|
282
339
|
}
|
|
283
340
|
case 'ProxyChannelPayloadAck': {
|
|
284
|
-
// yield* Effect.logDebug(`[${nodeName}] Received Ack for reqId: ${packet.reqId}`)
|
|
285
|
-
|
|
286
341
|
if (channelState._tag !== 'Established') {
|
|
287
342
|
yield* Effect.spanEvent(`Not yet connected to ${target}. dropping message`)
|
|
288
343
|
yield* Effect.logWarning(
|
|
@@ -291,9 +346,14 @@ export const makeProxyChannel = ({
|
|
|
291
346
|
return
|
|
292
347
|
}
|
|
293
348
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
349
|
+
// Handle missing ACK gracefully - can happen with synthetic ACKs from relay or duplicate ACKs
|
|
350
|
+
const ack = channelState.ackMap.get(packet.reqId)
|
|
351
|
+
if (ack === undefined) {
|
|
352
|
+
yield* Effect.logDebug(
|
|
353
|
+
`Received ACK for unknown reqId: ${packet.reqId} (may be synthetic or duplicate)`,
|
|
354
|
+
)
|
|
355
|
+
return
|
|
356
|
+
}
|
|
297
357
|
yield* Deferred.succeed(ack, void 0)
|
|
298
358
|
|
|
299
359
|
channelState.ackMap.delete(packet.reqId)
|
|
@@ -321,7 +381,7 @@ export const makeProxyChannel = ({
|
|
|
321
381
|
|
|
322
382
|
yield* Effect.spanEvent(`Connecting`)
|
|
323
383
|
|
|
324
|
-
const ackMap = new Map<string, Deferred.Deferred<void
|
|
384
|
+
const ackMap = new Map<string, Deferred.Deferred<void>>()
|
|
325
385
|
|
|
326
386
|
// check if already established via incoming `ProxyChannelRequest` from other side
|
|
327
387
|
// which indicates we already have a edge to the target node
|
|
@@ -366,7 +426,7 @@ export const makeProxyChannel = ({
|
|
|
366
426
|
|
|
367
427
|
const innerSend = Effect.gen(function* () {
|
|
368
428
|
// Note we're re-creating new packets every time otherwise they will be skipped because of `handledIds`
|
|
369
|
-
const ack = yield* Deferred.make<void
|
|
429
|
+
const ack = yield* Deferred.make<void>()
|
|
370
430
|
const packet = MeshSchema.ProxyChannelPayload.make({
|
|
371
431
|
channelName,
|
|
372
432
|
payload,
|
package/src/common.ts
CHANGED
|
@@ -19,19 +19,18 @@ 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>('~@livestore/webmesh/EdgeAlreadyExistsError')('EdgeAlreadyExistsError', {
|
|
28
27
|
target: Schema.String,
|
|
29
28
|
}) {}
|
|
30
29
|
|
|
31
30
|
export const packetAsOtelAttributes = (packet: typeof Packet.Type) => ({
|
|
32
31
|
packetId: packet.id,
|
|
33
32
|
'span.label':
|
|
34
|
-
packet.id + (Predicate.hasProperty(packet, 'reqId') && packet.reqId !== undefined ? ` for ${packet.reqId}` : ''),
|
|
33
|
+
packet.id + (Predicate.hasProperty(packet, 'reqId') === true && packet.reqId !== undefined ? ` for ${packet.reqId}` : ''),
|
|
35
34
|
...omitUndefineds({
|
|
36
35
|
packet:
|
|
37
36
|
packet._tag !== 'DirectChannelResponseSuccess' && packet._tag !== 'ProxyChannelPayload' ? packet : undefined,
|