@livestore/webmesh 0.0.0-snapshot-1d99fea7d2ce2c7a5d9ed0a3752f8a7bda6bc3db
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 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/channel/message-channel.d.ts +20 -0
- package/dist/channel/message-channel.d.ts.map +1 -0
- package/dist/channel/message-channel.js +183 -0
- package/dist/channel/message-channel.js.map +1 -0
- package/dist/channel/proxy-channel.d.ts +19 -0
- package/dist/channel/proxy-channel.d.ts.map +1 -0
- package/dist/channel/proxy-channel.js +179 -0
- package/dist/channel/proxy-channel.js.map +1 -0
- package/dist/common.d.ts +83 -0
- package/dist/common.d.ts.map +1 -0
- package/dist/common.js +13 -0
- package/dist/common.js.map +1 -0
- package/dist/mesh-schema.d.ts +104 -0
- package/dist/mesh-schema.d.ts.map +1 -0
- package/dist/mesh-schema.js +77 -0
- package/dist/mesh-schema.js.map +1 -0
- package/dist/mod.d.ts +5 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +5 -0
- package/dist/mod.js.map +1 -0
- package/dist/node.d.ts +65 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +216 -0
- package/dist/node.js.map +1 -0
- package/dist/node.test.d.ts +2 -0
- package/dist/node.test.d.ts.map +1 -0
- package/dist/node.test.js +351 -0
- package/dist/node.test.js.map +1 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +41 -0
- package/dist/utils.js.map +1 -0
- package/dist/websocket-connection.d.ts +51 -0
- package/dist/websocket-connection.d.ts.map +1 -0
- package/dist/websocket-connection.js +74 -0
- package/dist/websocket-connection.js.map +1 -0
- package/dist/websocket-server.d.ts +7 -0
- package/dist/websocket-server.d.ts.map +1 -0
- package/dist/websocket-server.js +24 -0
- package/dist/websocket-server.js.map +1 -0
- package/package.json +32 -0
- package/src/channel/message-channel.ts +354 -0
- package/src/channel/proxy-channel.ts +332 -0
- package/src/common.ts +36 -0
- package/src/mesh-schema.ts +94 -0
- package/src/mod.ts +4 -0
- package/src/node.test.ts +533 -0
- package/src/node.ts +408 -0
- package/src/utils.ts +47 -0
- package/src/websocket-connection.ts +158 -0
- package/src/websocket-server.ts +40 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { casesHandled, shouldNeverHappen } from '@livestore/utils'
|
|
2
|
+
import type { PubSub } from '@livestore/utils/effect'
|
|
3
|
+
import {
|
|
4
|
+
Deferred,
|
|
5
|
+
Effect,
|
|
6
|
+
Either,
|
|
7
|
+
Fiber,
|
|
8
|
+
FiberHandle,
|
|
9
|
+
Queue,
|
|
10
|
+
Schedule,
|
|
11
|
+
Schema,
|
|
12
|
+
Stream,
|
|
13
|
+
SubscriptionRef,
|
|
14
|
+
WebChannel,
|
|
15
|
+
} from '@livestore/utils/effect'
|
|
16
|
+
import { nanoid } from '@livestore/utils/nanoid'
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
type ChannelKey,
|
|
20
|
+
type ChannelName,
|
|
21
|
+
type MeshNodeName,
|
|
22
|
+
packetAsOtelAttributes,
|
|
23
|
+
type ProxyQueueItem,
|
|
24
|
+
} from '../common.js'
|
|
25
|
+
import * as MeshSchema from '../mesh-schema.js'
|
|
26
|
+
|
|
27
|
+
interface MakeProxyChannelArgs {
|
|
28
|
+
queue: Queue.Queue<ProxyQueueItem>
|
|
29
|
+
nodeName: MeshNodeName
|
|
30
|
+
newConnectionAvailablePubSub: PubSub.PubSub<MeshNodeName>
|
|
31
|
+
sendPacket: (packet: typeof MeshSchema.ProxyChannelPacket.Type) => Effect.Effect<void>
|
|
32
|
+
channelName: ChannelName
|
|
33
|
+
target: MeshNodeName
|
|
34
|
+
schema: {
|
|
35
|
+
send: Schema.Schema<any, any>
|
|
36
|
+
listen: Schema.Schema<any, any>
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const makeProxyChannel = ({
|
|
41
|
+
queue,
|
|
42
|
+
nodeName,
|
|
43
|
+
newConnectionAvailablePubSub,
|
|
44
|
+
sendPacket,
|
|
45
|
+
target,
|
|
46
|
+
channelName,
|
|
47
|
+
schema,
|
|
48
|
+
}: MakeProxyChannelArgs) =>
|
|
49
|
+
Effect.gen(function* () {
|
|
50
|
+
type ProxiedChannelState =
|
|
51
|
+
| {
|
|
52
|
+
_tag: 'Initial'
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
_tag: 'Pending'
|
|
56
|
+
initiatedVia: 'outgoing-request' | 'incoming-request'
|
|
57
|
+
}
|
|
58
|
+
| ProxiedChannelStateEstablished
|
|
59
|
+
|
|
60
|
+
type ProxiedChannelStateEstablished = {
|
|
61
|
+
_tag: 'Established'
|
|
62
|
+
listenSchema: Schema.Schema<any, any>
|
|
63
|
+
listenQueue: Queue.Queue<any>
|
|
64
|
+
ackMap: Map<string, Deferred.Deferred<void, never>>
|
|
65
|
+
combinedChannelId: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const channelStateRef = { current: { _tag: 'Initial' } as ProxiedChannelState }
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* We need to unique identify a channel as multiple channels might exist between the same two nodes.
|
|
72
|
+
* We do this by letting each channel end generate a unique id and then combining them in a deterministic way.
|
|
73
|
+
*/
|
|
74
|
+
const channelIdCandidate = nanoid(5)
|
|
75
|
+
yield* Effect.annotateCurrentSpan({ channelIdCandidate })
|
|
76
|
+
|
|
77
|
+
const channelSpan = yield* Effect.currentSpan.pipe(Effect.orDie)
|
|
78
|
+
|
|
79
|
+
const connectedStateRef = yield* SubscriptionRef.make<ProxiedChannelStateEstablished | false>(false)
|
|
80
|
+
|
|
81
|
+
const waitForEstablished = Effect.gen(function* () {
|
|
82
|
+
const state = yield* SubscriptionRef.waitUntil(connectedStateRef, (state) => state !== false)
|
|
83
|
+
|
|
84
|
+
return state as ProxiedChannelStateEstablished
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const setStateToEstablished = (channelId: string) =>
|
|
88
|
+
Effect.gen(function* () {
|
|
89
|
+
// TODO avoid "double" `Connected` events (we might call `setStateToEstablished` twice during initial connection)
|
|
90
|
+
yield* Effect.spanEvent(`Connected (${channelId})`).pipe(Effect.withParentSpan(channelSpan))
|
|
91
|
+
channelStateRef.current = {
|
|
92
|
+
_tag: 'Established',
|
|
93
|
+
listenSchema: schema.listen,
|
|
94
|
+
listenQueue,
|
|
95
|
+
ackMap,
|
|
96
|
+
combinedChannelId: channelId,
|
|
97
|
+
}
|
|
98
|
+
yield* SubscriptionRef.set(connectedStateRef, channelStateRef.current)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const connectionRequest = Effect.suspend(() =>
|
|
102
|
+
sendPacket(
|
|
103
|
+
MeshSchema.ProxyChannelRequest.make({ channelName, hops: [], source: nodeName, target, channelIdCandidate }),
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const getCombinedChannelId = (otherSideChannelIdCandidate: string) =>
|
|
108
|
+
[channelIdCandidate, otherSideChannelIdCandidate].sort().join('_')
|
|
109
|
+
|
|
110
|
+
const processProxyPacket = ({ packet, respondToSender }: ProxyQueueItem) =>
|
|
111
|
+
Effect.gen(function* () {
|
|
112
|
+
// yield* Effect.log(`${nodeName}:processing packet ${packet._tag} from ${packet.source}`)
|
|
113
|
+
|
|
114
|
+
const otherSideName = packet.source
|
|
115
|
+
const channelKey = `${otherSideName}-${packet.channelName}` satisfies ChannelKey
|
|
116
|
+
const channelState = channelStateRef.current
|
|
117
|
+
|
|
118
|
+
switch (packet._tag) {
|
|
119
|
+
case 'ProxyChannelRequest': {
|
|
120
|
+
const combinedChannelId = getCombinedChannelId(packet.channelIdCandidate)
|
|
121
|
+
|
|
122
|
+
if (channelState._tag === 'Initial' || channelState._tag === 'Established') {
|
|
123
|
+
yield* SubscriptionRef.set(connectedStateRef, false)
|
|
124
|
+
channelStateRef.current = { _tag: 'Pending', initiatedVia: 'incoming-request' }
|
|
125
|
+
yield* Effect.spanEvent(`Reconnecting`).pipe(Effect.withParentSpan(channelSpan))
|
|
126
|
+
|
|
127
|
+
// If we're already connected, we need to re-establish the connection
|
|
128
|
+
if (channelState._tag === 'Established' && channelState.combinedChannelId !== combinedChannelId) {
|
|
129
|
+
yield* connectionRequest
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
yield* respondToSender(
|
|
134
|
+
MeshSchema.ProxyChannelResponseSuccess.make({
|
|
135
|
+
reqId: packet.id,
|
|
136
|
+
remainingHops: packet.hops,
|
|
137
|
+
hops: [],
|
|
138
|
+
target,
|
|
139
|
+
source: nodeName,
|
|
140
|
+
channelName,
|
|
141
|
+
combinedChannelId,
|
|
142
|
+
channelIdCandidate,
|
|
143
|
+
}),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
case 'ProxyChannelResponseSuccess': {
|
|
149
|
+
if (channelState._tag !== 'Pending') {
|
|
150
|
+
// return shouldNeverHappen(`Expected proxy channel to be pending but got ${channelState._tag}`)
|
|
151
|
+
if (channelState._tag === 'Established' && channelState.combinedChannelId !== packet.combinedChannelId) {
|
|
152
|
+
return shouldNeverHappen(
|
|
153
|
+
`Expected proxy channel to have the same combinedChannelId as the packet:\n${channelState.combinedChannelId} (channel) === ${packet.combinedChannelId} (packet)`,
|
|
154
|
+
)
|
|
155
|
+
} else {
|
|
156
|
+
// for now just ignore it but should be looked into (there seems to be some kind of race condition/inefficiency)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const combinedChannelId = getCombinedChannelId(packet.channelIdCandidate)
|
|
161
|
+
if (combinedChannelId !== packet.combinedChannelId) {
|
|
162
|
+
return yield* Effect.die(
|
|
163
|
+
`Expected proxy channel to have the same combinedChannelId as the packet:\n${combinedChannelId} (channel) === ${packet.combinedChannelId} (packet)`,
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
yield* setStateToEstablished(packet.combinedChannelId)
|
|
168
|
+
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
case 'ProxyChannelPayload': {
|
|
172
|
+
if (channelState._tag !== 'Established') {
|
|
173
|
+
// return yield* Effect.die(`Not yet connected to ${target}. dropping message`)
|
|
174
|
+
yield* Effect.spanEvent(`Not yet connected to ${target}. dropping message`, { packet })
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (channelState.combinedChannelId !== packet.combinedChannelId) {
|
|
179
|
+
return yield* Effect.die(
|
|
180
|
+
`Expected proxy channel to have the same combinedChannelId as the packet:\n${channelState.combinedChannelId} (channel) === ${packet.combinedChannelId} (packet)`,
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
yield* respondToSender(
|
|
185
|
+
MeshSchema.ProxyChannelPayloadAck.make({
|
|
186
|
+
reqId: packet.id,
|
|
187
|
+
remainingHops: packet.hops,
|
|
188
|
+
hops: [],
|
|
189
|
+
target,
|
|
190
|
+
source: nodeName,
|
|
191
|
+
channelName,
|
|
192
|
+
combinedChannelId: channelState.combinedChannelId,
|
|
193
|
+
}),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
const decodedMessage = yield* Schema.decodeUnknown(channelState.listenSchema)(packet.payload)
|
|
197
|
+
yield* channelState.listenQueue.pipe(Queue.offer(decodedMessage))
|
|
198
|
+
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
case 'ProxyChannelPayloadAck': {
|
|
202
|
+
if (channelState._tag !== 'Established') {
|
|
203
|
+
yield* Effect.spanEvent(`Not yet connected to ${target}. dropping message`)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const ack =
|
|
208
|
+
channelState.ackMap.get(packet.reqId) ??
|
|
209
|
+
shouldNeverHappen(`Expected ack for ${packet.reqId} in proxy channel ${channelKey}`)
|
|
210
|
+
|
|
211
|
+
yield* Deferred.succeed(ack, void 0)
|
|
212
|
+
|
|
213
|
+
channelState.ackMap.delete(packet.reqId)
|
|
214
|
+
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
default: {
|
|
218
|
+
return casesHandled(packet)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}).pipe(
|
|
222
|
+
Effect.withSpan(`handleProxyPacket:${packet._tag}:${packet.source}->${packet.target}`, {
|
|
223
|
+
attributes: packetAsOtelAttributes(packet),
|
|
224
|
+
}),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
yield* Stream.fromQueue(queue).pipe(
|
|
228
|
+
Stream.tap(processProxyPacket),
|
|
229
|
+
Stream.runDrain,
|
|
230
|
+
Effect.tapCauseLogPretty,
|
|
231
|
+
Effect.forkScoped,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
const listenQueue = yield* Queue.unbounded<any>()
|
|
235
|
+
|
|
236
|
+
yield* Effect.spanEvent(`Connecting`)
|
|
237
|
+
|
|
238
|
+
const ackMap = new Map<string, Deferred.Deferred<void, never>>()
|
|
239
|
+
|
|
240
|
+
// check if already established via incoming `ProxyChannelRequest` from other side
|
|
241
|
+
// which indicates we already have a connection to the target node
|
|
242
|
+
// const channelState = channelStateRef.current
|
|
243
|
+
{
|
|
244
|
+
if (channelStateRef.current._tag !== 'Initial') {
|
|
245
|
+
return shouldNeverHappen('Expected proxy channel to be Initial')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
channelStateRef.current = { _tag: 'Pending', initiatedVia: 'outgoing-request' }
|
|
249
|
+
|
|
250
|
+
yield* connectionRequest
|
|
251
|
+
|
|
252
|
+
const retryOnNewConnectionFiber = yield* Stream.fromPubSub(newConnectionAvailablePubSub).pipe(
|
|
253
|
+
Stream.tap(() => connectionRequest),
|
|
254
|
+
Stream.runDrain,
|
|
255
|
+
Effect.forkScoped,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
const { combinedChannelId: channelId } = yield* waitForEstablished
|
|
259
|
+
|
|
260
|
+
yield* Fiber.interrupt(retryOnNewConnectionFiber)
|
|
261
|
+
|
|
262
|
+
yield* setStateToEstablished(channelId)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const send = (message: any) =>
|
|
266
|
+
Effect.gen(function* () {
|
|
267
|
+
const payload = yield* Schema.encodeUnknown(schema.send)(message)
|
|
268
|
+
const sendFiberHandle = yield* FiberHandle.make<void, never>()
|
|
269
|
+
|
|
270
|
+
const sentDeferred = yield* Deferred.make<void>()
|
|
271
|
+
|
|
272
|
+
const trySend = Effect.gen(function* () {
|
|
273
|
+
const { combinedChannelId } = (yield* SubscriptionRef.waitUntil(
|
|
274
|
+
connectedStateRef,
|
|
275
|
+
(channel) => channel !== false,
|
|
276
|
+
)) as ProxiedChannelStateEstablished
|
|
277
|
+
|
|
278
|
+
const innerSend = Effect.gen(function* () {
|
|
279
|
+
// Note we're re-creating new packets every time otherwise they will be skipped because of `handledIds`
|
|
280
|
+
const ack = yield* Deferred.make<void, never>()
|
|
281
|
+
const packet = MeshSchema.ProxyChannelPayload.make({
|
|
282
|
+
channelName,
|
|
283
|
+
payload,
|
|
284
|
+
hops: [],
|
|
285
|
+
source: nodeName,
|
|
286
|
+
target,
|
|
287
|
+
combinedChannelId,
|
|
288
|
+
})
|
|
289
|
+
ackMap.set(packet.id, ack)
|
|
290
|
+
|
|
291
|
+
yield* sendPacket(packet)
|
|
292
|
+
|
|
293
|
+
yield* ack
|
|
294
|
+
yield* Deferred.succeed(sentDeferred, void 0)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
yield* innerSend.pipe(Effect.timeout(100), Effect.retry(Schedule.exponential(100)), Effect.orDie)
|
|
298
|
+
}).pipe(Effect.tapErrorCause(Effect.logError))
|
|
299
|
+
|
|
300
|
+
const rerunOnNewChannelFiber = yield* connectedStateRef.changes.pipe(
|
|
301
|
+
Stream.filter((_) => _ === false),
|
|
302
|
+
Stream.tap(() => FiberHandle.run(sendFiberHandle, trySend)),
|
|
303
|
+
Stream.runDrain,
|
|
304
|
+
Effect.fork,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
yield* FiberHandle.run(sendFiberHandle, trySend)
|
|
308
|
+
|
|
309
|
+
yield* sentDeferred
|
|
310
|
+
|
|
311
|
+
yield* Fiber.interrupt(rerunOnNewChannelFiber)
|
|
312
|
+
}).pipe(
|
|
313
|
+
Effect.scoped,
|
|
314
|
+
Effect.withSpan(`sendAckWithRetry:ProxyChannelPayload`),
|
|
315
|
+
Effect.withParentSpan(channelSpan),
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
const listen = Stream.fromQueue(listenQueue).pipe(Stream.map(Either.right))
|
|
319
|
+
|
|
320
|
+
const closedDeferred = yield* Deferred.make<void>()
|
|
321
|
+
|
|
322
|
+
const webChannel = {
|
|
323
|
+
[WebChannel.WebChannelSymbol]: WebChannel.WebChannelSymbol,
|
|
324
|
+
send,
|
|
325
|
+
listen,
|
|
326
|
+
closedDeferred,
|
|
327
|
+
supportsTransferables: true,
|
|
328
|
+
schema,
|
|
329
|
+
} satisfies WebChannel.WebChannel<any, any>
|
|
330
|
+
|
|
331
|
+
return webChannel as WebChannel.WebChannel<any, any>
|
|
332
|
+
}).pipe(Effect.withSpanScoped('makeProxyChannel'))
|
package/src/common.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Effect, Schema } from '@livestore/utils/effect'
|
|
2
|
+
|
|
3
|
+
import type { MessageChannelPacket, Packet, ProxyChannelPacket } from './mesh-schema.js'
|
|
4
|
+
|
|
5
|
+
export type ProxyQueueItem = {
|
|
6
|
+
packet: typeof ProxyChannelPacket.Type
|
|
7
|
+
respondToSender: (msg: typeof ProxyChannelPacket.Type) => Effect.Effect<void>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type MessageQueueItem = {
|
|
11
|
+
packet: typeof MessageChannelPacket.Type
|
|
12
|
+
respondToSender: (msg: typeof MessageChannelPacket.Type) => Effect.Effect<void>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type MeshNodeName = string
|
|
16
|
+
|
|
17
|
+
export type ChannelName = string
|
|
18
|
+
export type ChannelKey = `${MeshNodeName}-${ChannelName}`
|
|
19
|
+
|
|
20
|
+
// TODO actually use this to avoid timeouts in certain cases
|
|
21
|
+
export class NoConnectionRouteSignal extends Schema.TaggedError<NoConnectionRouteSignal>()(
|
|
22
|
+
'NoConnectionRouteSignal',
|
|
23
|
+
{},
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
export class ConnectionAlreadyExistsError extends Schema.TaggedError<ConnectionAlreadyExistsError>()(
|
|
27
|
+
'ConnectionAlreadyExistsError',
|
|
28
|
+
{
|
|
29
|
+
target: Schema.String,
|
|
30
|
+
},
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
export const packetAsOtelAttributes = (packet: typeof Packet.Type) => ({
|
|
34
|
+
packetId: packet.id,
|
|
35
|
+
...(packet._tag !== 'MessageChannelResponseSuccess' && packet._tag !== 'ProxyChannelPayload' ? { packet } : {}),
|
|
36
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Schema, Transferable } from '@livestore/utils/effect'
|
|
2
|
+
import { nanoid } from '@livestore/utils/nanoid'
|
|
3
|
+
|
|
4
|
+
const id = Schema.String.pipe(
|
|
5
|
+
Schema.optional,
|
|
6
|
+
Schema.withDefaults({ constructor: () => nanoid(10), decoding: () => nanoid(10) }),
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
const defaultPacketFields = {
|
|
10
|
+
id,
|
|
11
|
+
target: Schema.String,
|
|
12
|
+
source: Schema.String,
|
|
13
|
+
channelName: Schema.String,
|
|
14
|
+
hops: Schema.Array(Schema.String),
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const remainingHopsUndefined = Schema.Undefined.pipe(Schema.optional)
|
|
18
|
+
|
|
19
|
+
// Needs to go through already existing MessageChannel connections, times out otherwise
|
|
20
|
+
export class MessageChannelRequest extends Schema.TaggedStruct('MessageChannelRequest', {
|
|
21
|
+
...defaultPacketFields,
|
|
22
|
+
remainingHops: remainingHopsUndefined,
|
|
23
|
+
}) {}
|
|
24
|
+
|
|
25
|
+
export class MessageChannelResponseSuccess extends Schema.TaggedStruct('MessageChannelResponseSuccess', {
|
|
26
|
+
...defaultPacketFields,
|
|
27
|
+
reqId: Schema.String,
|
|
28
|
+
port: Transferable.MessagePort,
|
|
29
|
+
// Since we can't copy this message, we need to follow the exact route back to the sender
|
|
30
|
+
remainingHops: Schema.Array(Schema.String),
|
|
31
|
+
}) {}
|
|
32
|
+
|
|
33
|
+
export class MessageChannelResponseNoTransferables extends Schema.TaggedStruct(
|
|
34
|
+
'MessageChannelResponseNoTransferables',
|
|
35
|
+
{
|
|
36
|
+
...defaultPacketFields,
|
|
37
|
+
reqId: Schema.String,
|
|
38
|
+
remainingHops: Schema.Array(Schema.String),
|
|
39
|
+
},
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
export class ProxyChannelRequest extends Schema.TaggedStruct('ProxyChannelRequest', {
|
|
43
|
+
...defaultPacketFields,
|
|
44
|
+
remainingHops: remainingHopsUndefined,
|
|
45
|
+
channelIdCandidate: Schema.String,
|
|
46
|
+
}) {}
|
|
47
|
+
|
|
48
|
+
export class ProxyChannelResponseSuccess extends Schema.TaggedStruct('ProxyChannelResponseSuccess', {
|
|
49
|
+
...defaultPacketFields,
|
|
50
|
+
reqId: Schema.String,
|
|
51
|
+
remainingHops: Schema.Array(Schema.String),
|
|
52
|
+
combinedChannelId: Schema.String,
|
|
53
|
+
channelIdCandidate: Schema.String,
|
|
54
|
+
}) {}
|
|
55
|
+
|
|
56
|
+
export class ProxyChannelPayload extends Schema.TaggedStruct('ProxyChannelPayload', {
|
|
57
|
+
...defaultPacketFields,
|
|
58
|
+
remainingHops: remainingHopsUndefined,
|
|
59
|
+
payload: Schema.Any,
|
|
60
|
+
combinedChannelId: Schema.String,
|
|
61
|
+
}) {}
|
|
62
|
+
|
|
63
|
+
export class ProxyChannelPayloadAck extends Schema.TaggedStruct('ProxyChannelPayloadAck', {
|
|
64
|
+
...defaultPacketFields,
|
|
65
|
+
reqId: Schema.String,
|
|
66
|
+
remainingHops: Schema.Array(Schema.String),
|
|
67
|
+
combinedChannelId: Schema.String,
|
|
68
|
+
}) {}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Broadcast to all nodes when a new connection is added.
|
|
72
|
+
* Mostly used for auto-reconnect purposes.
|
|
73
|
+
*/
|
|
74
|
+
// TODO actually use for this use case
|
|
75
|
+
export class NetworkConnectionAdded extends Schema.TaggedStruct('NetworkConnectionAdded', {
|
|
76
|
+
id,
|
|
77
|
+
source: Schema.String,
|
|
78
|
+
target: Schema.String,
|
|
79
|
+
}) {}
|
|
80
|
+
|
|
81
|
+
export class MessageChannelPacket extends Schema.Union(
|
|
82
|
+
MessageChannelRequest,
|
|
83
|
+
MessageChannelResponseSuccess,
|
|
84
|
+
MessageChannelResponseNoTransferables,
|
|
85
|
+
) {}
|
|
86
|
+
|
|
87
|
+
export class ProxyChannelPacket extends Schema.Union(
|
|
88
|
+
ProxyChannelRequest,
|
|
89
|
+
ProxyChannelResponseSuccess,
|
|
90
|
+
ProxyChannelPayload,
|
|
91
|
+
ProxyChannelPayloadAck,
|
|
92
|
+
) {}
|
|
93
|
+
|
|
94
|
+
export class Packet extends Schema.Union(MessageChannelPacket, ProxyChannelPacket, NetworkConnectionAdded) {}
|
package/src/mod.ts
ADDED