@livestore/webmesh 0.4.0-dev.21 → 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 +14 -22
- 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 +13 -27
- 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,
|
|
@@ -89,11 +88,6 @@ export const makeDirectChannelInternal = ({
|
|
|
89
88
|
|
|
90
89
|
const deferred = yield* makeDeferredResult()
|
|
91
90
|
|
|
92
|
-
const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.catchAll(() => Effect.succeed(undefined)))
|
|
93
|
-
// const span = {
|
|
94
|
-
// addEvent: (...msg: any[]) => console.log(`${nodeName}→${channelName}→${target}[${channelVersion}]`, ...msg),
|
|
95
|
-
// }
|
|
96
|
-
|
|
97
91
|
const schema = {
|
|
98
92
|
send: Schema.Union(schema_.send, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong),
|
|
99
93
|
listen: Schema.Union(schema_.listen, MeshSchema.DirectChannelPing, MeshSchema.DirectChannelPong),
|
|
@@ -107,11 +101,11 @@ export const makeDirectChannelInternal = ({
|
|
|
107
101
|
Effect.gen(function* () {
|
|
108
102
|
const channelState = channelStateRef.current
|
|
109
103
|
|
|
110
|
-
|
|
104
|
+
yield* Effect.spanEvent(`process:${packet._tag}`, {
|
|
111
105
|
channelState: channelState._tag,
|
|
112
106
|
packetId: packet.id,
|
|
113
107
|
packetReqId: packet.reqId,
|
|
114
|
-
|
|
108
|
+
...(Predicate.hasProperty('channelVersion')(packet) === true ? { packetChannelVersion: packet.channelVersion } : {}),
|
|
115
109
|
})
|
|
116
110
|
|
|
117
111
|
// const reqIdStr =
|
|
@@ -130,7 +124,7 @@ export const makeDirectChannelInternal = ({
|
|
|
130
124
|
// If the other side has a higher version, we need to close this channel and
|
|
131
125
|
// recreate it with the new version
|
|
132
126
|
if (packet.channelVersion > channelVersion) {
|
|
133
|
-
|
|
127
|
+
yield* Effect.spanEvent(`incoming packet has higher version (${packet.channelVersion}), closing channel`)
|
|
134
128
|
yield* Scope.close(scope, Exit.succeed('higher-version-expected'))
|
|
135
129
|
// TODO include expected version in the error so the channel gets recreated with the new version
|
|
136
130
|
return 'close'
|
|
@@ -149,7 +143,7 @@ export const makeDirectChannelInternal = ({
|
|
|
149
143
|
remainingHops: packet.hops,
|
|
150
144
|
reqId: undefined,
|
|
151
145
|
})
|
|
152
|
-
|
|
146
|
+
yield* Effect.spanEvent(
|
|
153
147
|
`incoming packet has lower version (${packet.channelVersion}), sending request to reconnect (${newPacket.id})`,
|
|
154
148
|
)
|
|
155
149
|
|
|
@@ -164,7 +158,7 @@ export const makeDirectChannelInternal = ({
|
|
|
164
158
|
} else {
|
|
165
159
|
// In case the instance of the source has changed, we need to close the channel
|
|
166
160
|
// and reconnect with a new channel
|
|
167
|
-
|
|
161
|
+
yield* Effect.spanEvent(`force-new-channel`)
|
|
168
162
|
yield* Scope.close(scope, Exit.succeed('force-new-channel'))
|
|
169
163
|
return 'close'
|
|
170
164
|
}
|
|
@@ -191,15 +185,15 @@ export const makeDirectChannelInternal = ({
|
|
|
191
185
|
remainingHops: packet.hops,
|
|
192
186
|
reqId: packet.id,
|
|
193
187
|
})
|
|
194
|
-
|
|
188
|
+
yield* Effect.spanEvent(`Re-sending new request (${newRequestPacket.id}) for incoming request (${packet.id})`)
|
|
195
189
|
|
|
196
190
|
yield* sendPacket(newRequestPacket)
|
|
197
191
|
}
|
|
198
192
|
|
|
199
193
|
const isWinner = nodeName > target
|
|
200
194
|
|
|
201
|
-
if (isWinner) {
|
|
202
|
-
|
|
195
|
+
if (isWinner === true) {
|
|
196
|
+
yield* Effect.spanEvent(`winner side: creating direct channel and sending response`)
|
|
203
197
|
const mc = new MessageChannel()
|
|
204
198
|
|
|
205
199
|
// We're using a direct channel with acks here to make sure messages are not lost
|
|
@@ -227,8 +221,6 @@ export const makeDirectChannelInternal = ({
|
|
|
227
221
|
|
|
228
222
|
channelStateRef.current = { _tag: 'winner:ResponseSent', channel, otherSourceId: packet.sourceId }
|
|
229
223
|
|
|
230
|
-
// span?.addEvent(`winner side: waiting for ping`)
|
|
231
|
-
|
|
232
224
|
// Now we wait for the other side to respond via the channel
|
|
233
225
|
yield* channel.listen.pipe(
|
|
234
226
|
Stream.flatten(),
|
|
@@ -237,21 +229,19 @@ export const makeDirectChannelInternal = ({
|
|
|
237
229
|
Stream.runDrain,
|
|
238
230
|
)
|
|
239
231
|
|
|
240
|
-
// span?.addEvent(`winner side: sending pong`)
|
|
241
|
-
|
|
242
232
|
yield* channel.send(MeshSchema.DirectChannelPong.make({}))
|
|
243
233
|
|
|
244
|
-
|
|
234
|
+
yield* Effect.spanEvent(`winner side: established`)
|
|
245
235
|
channelStateRef.current = { _tag: 'Established', otherSourceId: packet.sourceId }
|
|
246
236
|
|
|
247
237
|
yield* Deferred.succeed(deferred, channel)
|
|
248
238
|
} else {
|
|
249
|
-
|
|
239
|
+
yield* Effect.spanEvent(`loser side: waiting for response`)
|
|
250
240
|
// Wait for `DirectChannelResponseSuccess` packet
|
|
251
241
|
channelStateRef.current = { _tag: 'loser:WaitingForResponse', otherSourceId: packet.sourceId }
|
|
252
242
|
}
|
|
253
243
|
|
|
254
|
-
|
|
244
|
+
return
|
|
255
245
|
}
|
|
256
246
|
case 'DirectChannelResponseSuccess': {
|
|
257
247
|
if (channelState._tag !== 'loser:WaitingForResponse') {
|
|
@@ -275,8 +265,6 @@ export const makeDirectChannelInternal = ({
|
|
|
275
265
|
Effect.fork,
|
|
276
266
|
)
|
|
277
267
|
|
|
278
|
-
// span?.addEvent(`loser side: sending ping`)
|
|
279
|
-
|
|
280
268
|
// There seems to be some scenario where the initial ping message is lost.
|
|
281
269
|
// As a workaround until we find the root cause, we're retrying the ping a few times.
|
|
282
270
|
// TODO write a test that reproduces this issue and fix the root cause ()
|
|
@@ -285,11 +273,9 @@ export const makeDirectChannelInternal = ({
|
|
|
285
273
|
.send(MeshSchema.DirectChannelPing.make({}))
|
|
286
274
|
.pipe(Effect.timeout(10), Effect.retry({ times: 2 }))
|
|
287
275
|
|
|
288
|
-
// span?.addEvent(`loser side: waiting for pong`)
|
|
289
|
-
|
|
290
276
|
yield* waitForPongFiber
|
|
291
277
|
|
|
292
|
-
|
|
278
|
+
yield* Effect.spanEvent(`loser side: established`)
|
|
293
279
|
channelStateRef.current = { _tag: 'Established', otherSourceId: channelState.otherSourceId }
|
|
294
280
|
|
|
295
281
|
yield* Deferred.succeed(deferred, channel)
|
|
@@ -345,7 +331,7 @@ export const makeDirectChannelInternal = ({
|
|
|
345
331
|
}
|
|
346
332
|
|
|
347
333
|
yield* sendPacket(packet)
|
|
348
|
-
|
|
334
|
+
yield* Effect.spanEvent(`initial edge request sent (${packet.id})`)
|
|
349
335
|
})
|
|
350
336
|
|
|
351
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,
|