@dxos/edge-client 0.6.6-main.e1a6e1f
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/LICENSE +8 -0
- package/README.md +0 -0
- package/dist/lib/browser/index.mjs +308 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/node/index.cjs +337 -0
- package/dist/lib/node/index.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -0
- package/dist/types/src/client.d.ts +57 -0
- package/dist/types/src/client.d.ts.map +1 -0
- package/dist/types/src/defs.d.ts +3 -0
- package/dist/types/src/defs.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/protocol.d.ts +35 -0
- package/dist/types/src/protocol.d.ts.map +1 -0
- package/dist/types/src/protocol.test.d.ts +2 -0
- package/dist/types/src/protocol.test.d.ts.map +1 -0
- package/package.json +41 -0
- package/src/client.ts +157 -0
- package/src/defs.ts +9 -0
- package/src/index.ts +9 -0
- package/src/protocol.test.ts +36 -0
- package/src/protocol.ts +106 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
Copyright (c) 2022 DXOS
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// packages/core/mesh/edge-client/src/index.ts
|
|
2
|
+
export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
3
|
+
|
|
4
|
+
// packages/core/mesh/edge-client/src/client.ts
|
|
5
|
+
import WebSocket from "isomorphic-ws";
|
|
6
|
+
import { Trigger } from "@dxos/async";
|
|
7
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
8
|
+
import { log } from "@dxos/log";
|
|
9
|
+
import { buf as buf2 } from "@dxos/protocols/buf";
|
|
10
|
+
import { MessageSchema as MessageSchema2 } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
11
|
+
|
|
12
|
+
// packages/core/mesh/edge-client/src/defs.ts
|
|
13
|
+
import { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
14
|
+
|
|
15
|
+
// packages/core/mesh/edge-client/src/protocol.ts
|
|
16
|
+
import { invariant } from "@dxos/invariant";
|
|
17
|
+
import { buf, bufWkt } from "@dxos/protocols/buf";
|
|
18
|
+
import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
19
|
+
import { bufferToArray } from "@dxos/util";
|
|
20
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/protocol.ts";
|
|
21
|
+
var getTypename = (typeName) => `type.googleapis.com/${typeName}`;
|
|
22
|
+
var Protocol = class {
|
|
23
|
+
constructor(types) {
|
|
24
|
+
this._typeRegistry = buf.createRegistry(...types);
|
|
25
|
+
}
|
|
26
|
+
get typeRegistry() {
|
|
27
|
+
return this._typeRegistry;
|
|
28
|
+
}
|
|
29
|
+
toJson(message) {
|
|
30
|
+
try {
|
|
31
|
+
return buf.toJson(MessageSchema, message, {
|
|
32
|
+
registry: this.typeRegistry
|
|
33
|
+
});
|
|
34
|
+
} catch (err) {
|
|
35
|
+
return {
|
|
36
|
+
type: this.getPayloadType(message)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Return the payload with the given type.
|
|
42
|
+
*/
|
|
43
|
+
getPayload(message, type) {
|
|
44
|
+
invariant(message.payload, void 0, {
|
|
45
|
+
F: __dxlog_file,
|
|
46
|
+
L: 40,
|
|
47
|
+
S: this,
|
|
48
|
+
A: [
|
|
49
|
+
"message.payload",
|
|
50
|
+
""
|
|
51
|
+
]
|
|
52
|
+
});
|
|
53
|
+
const payloadTypename = this.getPayloadType(message);
|
|
54
|
+
if (type && type.typeName !== payloadTypename) {
|
|
55
|
+
throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);
|
|
56
|
+
}
|
|
57
|
+
invariant(bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`, {
|
|
58
|
+
F: __dxlog_file,
|
|
59
|
+
L: 46,
|
|
60
|
+
S: this,
|
|
61
|
+
A: [
|
|
62
|
+
"bufWkt.anyIs(message.payload, type)",
|
|
63
|
+
"`Unexpected payload type: ${payloadTypename}}`"
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
const payload = bufWkt.anyUnpack(message.payload, this.typeRegistry);
|
|
67
|
+
invariant(payload, `Empty payload: ${payloadTypename}}`, {
|
|
68
|
+
F: __dxlog_file,
|
|
69
|
+
L: 48,
|
|
70
|
+
S: this,
|
|
71
|
+
A: [
|
|
72
|
+
"payload",
|
|
73
|
+
"`Empty payload: ${payloadTypename}}`"
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
return payload;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get the payload type.
|
|
80
|
+
*/
|
|
81
|
+
getPayloadType(message) {
|
|
82
|
+
if (!message.payload) {
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
const [, type] = message.payload.typeUrl.split("/");
|
|
86
|
+
return type;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create a packed message.
|
|
90
|
+
*/
|
|
91
|
+
createMessage(type, { source, target, payload, serviceId }) {
|
|
92
|
+
return buf.create(MessageSchema, {
|
|
93
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
94
|
+
source,
|
|
95
|
+
target,
|
|
96
|
+
serviceId,
|
|
97
|
+
payload: payload ? bufWkt.anyPack(type, buf.create(type, payload)) : void 0
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var toUint8Array = async (data) => {
|
|
102
|
+
if (data instanceof Buffer) {
|
|
103
|
+
return bufferToArray(data);
|
|
104
|
+
}
|
|
105
|
+
if (data instanceof Blob) {
|
|
106
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`Unexpected datatype: ${data}`);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// packages/core/mesh/edge-client/src/defs.ts
|
|
112
|
+
var protocol = new Protocol([
|
|
113
|
+
SwarmRequestSchema,
|
|
114
|
+
SwarmResponseSchema,
|
|
115
|
+
TextMessageSchema
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
// packages/core/mesh/edge-client/src/client.ts
|
|
119
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/client.ts";
|
|
120
|
+
var DEFAULT_TIMEOUT = 5e3;
|
|
121
|
+
var EdgeClient = class {
|
|
122
|
+
constructor(_identityKey, _deviceKey, _config) {
|
|
123
|
+
this._identityKey = _identityKey;
|
|
124
|
+
this._deviceKey = _deviceKey;
|
|
125
|
+
this._config = _config;
|
|
126
|
+
this._listeners = /* @__PURE__ */ new Set();
|
|
127
|
+
this._protocol = this._config.protocol ?? protocol;
|
|
128
|
+
}
|
|
129
|
+
// TODO(burdon): Attach logging.
|
|
130
|
+
get info() {
|
|
131
|
+
return {
|
|
132
|
+
open: this.isOpen,
|
|
133
|
+
identity: this._identityKey,
|
|
134
|
+
device: this._deviceKey
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
get identityKey() {
|
|
138
|
+
return this._identityKey;
|
|
139
|
+
}
|
|
140
|
+
get deviceKey() {
|
|
141
|
+
return this._deviceKey;
|
|
142
|
+
}
|
|
143
|
+
get isOpen() {
|
|
144
|
+
return !!this._ws;
|
|
145
|
+
}
|
|
146
|
+
addListener(listener) {
|
|
147
|
+
this._listeners.add(listener);
|
|
148
|
+
return () => this._listeners.delete(listener);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Open connection to messaging service.
|
|
152
|
+
*/
|
|
153
|
+
async open() {
|
|
154
|
+
invariant2(!this._ws, void 0, {
|
|
155
|
+
F: __dxlog_file2,
|
|
156
|
+
L: 88,
|
|
157
|
+
S: this,
|
|
158
|
+
A: [
|
|
159
|
+
"!this._ws",
|
|
160
|
+
""
|
|
161
|
+
]
|
|
162
|
+
});
|
|
163
|
+
log.info("opening...", {
|
|
164
|
+
info: this.info
|
|
165
|
+
}, {
|
|
166
|
+
F: __dxlog_file2,
|
|
167
|
+
L: 89,
|
|
168
|
+
S: this,
|
|
169
|
+
C: (f, a) => f(...a)
|
|
170
|
+
});
|
|
171
|
+
const ready = new Trigger();
|
|
172
|
+
const url = new URL(`/ws/${this._identityKey.toHex()}/${this._deviceKey.toHex()}`, this._config.socketEndpoint);
|
|
173
|
+
this._ws = new WebSocket(url);
|
|
174
|
+
Object.assign(this._ws, {
|
|
175
|
+
onopen: () => {
|
|
176
|
+
log.info("opened", this.info, {
|
|
177
|
+
F: __dxlog_file2,
|
|
178
|
+
L: 97,
|
|
179
|
+
S: this,
|
|
180
|
+
C: (f, a) => f(...a)
|
|
181
|
+
});
|
|
182
|
+
ready.wake(true);
|
|
183
|
+
},
|
|
184
|
+
onclose: () => {
|
|
185
|
+
log.info("closed", this.info, {
|
|
186
|
+
F: __dxlog_file2,
|
|
187
|
+
L: 102,
|
|
188
|
+
S: this,
|
|
189
|
+
C: (f, a) => f(...a)
|
|
190
|
+
});
|
|
191
|
+
ready.wake(false);
|
|
192
|
+
},
|
|
193
|
+
onerror: (event) => {
|
|
194
|
+
log.catch(event.error, this.info, {
|
|
195
|
+
F: __dxlog_file2,
|
|
196
|
+
L: 107,
|
|
197
|
+
S: this,
|
|
198
|
+
C: (f, a) => f(...a)
|
|
199
|
+
});
|
|
200
|
+
ready.throw(event.error);
|
|
201
|
+
},
|
|
202
|
+
/**
|
|
203
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data
|
|
204
|
+
*/
|
|
205
|
+
onmessage: async (event) => {
|
|
206
|
+
const data = await toUint8Array(event.data);
|
|
207
|
+
const message = buf2.fromBinary(MessageSchema2, data);
|
|
208
|
+
log("received", {
|
|
209
|
+
deviceKey: this._deviceKey,
|
|
210
|
+
payload: protocol.getPayloadType(message)
|
|
211
|
+
}, {
|
|
212
|
+
F: __dxlog_file2,
|
|
213
|
+
L: 117,
|
|
214
|
+
S: this,
|
|
215
|
+
C: (f, a) => f(...a)
|
|
216
|
+
});
|
|
217
|
+
if (message) {
|
|
218
|
+
for (const listener of this._listeners) {
|
|
219
|
+
try {
|
|
220
|
+
await listener(message);
|
|
221
|
+
} catch (err) {
|
|
222
|
+
log.error("processing", {
|
|
223
|
+
err,
|
|
224
|
+
payload: protocol.getPayloadType(message)
|
|
225
|
+
}, {
|
|
226
|
+
F: __dxlog_file2,
|
|
227
|
+
L: 123,
|
|
228
|
+
S: this,
|
|
229
|
+
C: (f, a) => f(...a)
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
const result = await ready.wait({
|
|
237
|
+
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
238
|
+
});
|
|
239
|
+
log.info("opened", {
|
|
240
|
+
info: this.info
|
|
241
|
+
}, {
|
|
242
|
+
F: __dxlog_file2,
|
|
243
|
+
L: 131,
|
|
244
|
+
S: this,
|
|
245
|
+
C: (f, a) => f(...a)
|
|
246
|
+
});
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Close connection and free resources.
|
|
251
|
+
*/
|
|
252
|
+
async close() {
|
|
253
|
+
if (this._ws) {
|
|
254
|
+
log.info("closing...", {
|
|
255
|
+
deviceKey: this._deviceKey
|
|
256
|
+
}, {
|
|
257
|
+
F: __dxlog_file2,
|
|
258
|
+
L: 140,
|
|
259
|
+
S: this,
|
|
260
|
+
C: (f, a) => f(...a)
|
|
261
|
+
});
|
|
262
|
+
this._ws.close();
|
|
263
|
+
this._ws = void 0;
|
|
264
|
+
log.info("closed", {
|
|
265
|
+
deviceKey: this._deviceKey
|
|
266
|
+
}, {
|
|
267
|
+
F: __dxlog_file2,
|
|
268
|
+
L: 143,
|
|
269
|
+
S: this,
|
|
270
|
+
C: (f, a) => f(...a)
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Send message.
|
|
276
|
+
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
277
|
+
*/
|
|
278
|
+
// TODO(burdon): Implement ACK?
|
|
279
|
+
async send(message) {
|
|
280
|
+
invariant2(this._ws, void 0, {
|
|
281
|
+
F: __dxlog_file2,
|
|
282
|
+
L: 153,
|
|
283
|
+
S: this,
|
|
284
|
+
A: [
|
|
285
|
+
"this._ws",
|
|
286
|
+
""
|
|
287
|
+
]
|
|
288
|
+
});
|
|
289
|
+
log("sending...", {
|
|
290
|
+
deviceKey: this._deviceKey,
|
|
291
|
+
payload: protocol.getPayloadType(message)
|
|
292
|
+
}, {
|
|
293
|
+
F: __dxlog_file2,
|
|
294
|
+
L: 154,
|
|
295
|
+
S: this,
|
|
296
|
+
C: (f, a) => f(...a)
|
|
297
|
+
});
|
|
298
|
+
this._ws.send(buf2.toBinary(MessageSchema2, message));
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
export {
|
|
302
|
+
EdgeClient,
|
|
303
|
+
Protocol,
|
|
304
|
+
getTypename,
|
|
305
|
+
protocol,
|
|
306
|
+
toUint8Array
|
|
307
|
+
};
|
|
308
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/index.ts", "../../../src/client.ts", "../../../src/defs.ts", "../../../src/protocol.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nexport * from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nexport * from './client';\nexport * from './defs';\nexport * from './protocol';\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Trigger } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\nimport { type PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { buf } from '@dxos/protocols/buf';\nimport { type Message, MessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nimport { protocol } from './defs';\nimport { type Protocol, toUint8Array } from './protocol';\n\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type MessageListener = (message: Message) => void | Promise<void>;\n\n/**\n *\n */\nexport interface EdgeConnection {\n get info(): any;\n get identityKey(): PublicKey;\n get deviceKey(): PublicKey;\n get isOpen(): boolean;\n addListener(listener: MessageListener): () => void;\n open(): Promise<boolean>;\n close(): Promise<void>;\n send(message: Message): Promise<void>;\n}\n\nexport type MessengerConfig = {\n socketEndpoint: string;\n timeout?: number;\n protocol?: Protocol;\n};\n\n/**\n * Messenger client.\n */\n// TODO(dmaretskyi): Rename EdgeClient.\nexport class EdgeClient implements EdgeConnection {\n private readonly _listeners: Set<MessageListener> = new Set();\n private readonly _protocol: Protocol;\n private _ws?: WebSocket;\n\n constructor(\n private readonly _identityKey: PublicKey,\n private readonly _deviceKey: PublicKey,\n private readonly _config: MessengerConfig,\n ) {\n this._protocol = this._config.protocol ?? protocol;\n }\n\n // TODO(burdon): Attach logging.\n public get info() {\n return {\n open: this.isOpen,\n identity: this._identityKey,\n device: this._deviceKey,\n };\n }\n\n get identityKey() {\n return this._identityKey;\n }\n\n get deviceKey() {\n return this._deviceKey;\n }\n\n public get isOpen() {\n return !!this._ws;\n }\n\n public addListener(listener: MessageListener): () => void {\n this._listeners.add(listener);\n return () => this._listeners.delete(listener);\n }\n\n /**\n * Open connection to messaging service.\n */\n public async open(): Promise<boolean> {\n invariant(!this._ws);\n log.info('opening...', { info: this.info });\n const ready = new Trigger<boolean>();\n\n // TODO: handle reconnects\n const url = new URL(`/ws/${this._identityKey.toHex()}/${this._deviceKey.toHex()}`, this._config.socketEndpoint);\n this._ws = new WebSocket(url);\n Object.assign<WebSocket, Partial<WebSocket>>(this._ws, {\n onopen: () => {\n log.info('opened', this.info);\n ready.wake(true);\n },\n\n onclose: () => {\n log.info('closed', this.info);\n ready.wake(false);\n },\n\n onerror: (event) => {\n log.catch(event.error, this.info);\n ready.throw(event.error);\n },\n\n /**\n * https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data\n */\n onmessage: async (event) => {\n const data = await toUint8Array(event.data);\n const message = buf.fromBinary(MessageSchema, data);\n log('received', { deviceKey: this._deviceKey, payload: protocol.getPayloadType(message) });\n if (message) {\n for (const listener of this._listeners) {\n try {\n await listener(message);\n } catch (err) {\n log.error('processing', { err, payload: protocol.getPayloadType(message) });\n }\n }\n }\n },\n });\n\n const result = await ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n log.info('opened', { info: this.info });\n return result;\n }\n\n /**\n * Close connection and free resources.\n */\n public async close() {\n if (this._ws) {\n log.info('closing...', { deviceKey: this._deviceKey });\n this._ws.close();\n this._ws = undefined;\n log.info('closed', { deviceKey: this._deviceKey });\n }\n }\n\n /**\n * Send message.\n * NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.\n */\n // TODO(burdon): Implement ACK?\n public async send(message: Message): Promise<void> {\n invariant(this._ws);\n log('sending...', { deviceKey: this._deviceKey, payload: protocol.getPayloadType(message) });\n this._ws.send(buf.toBinary(MessageSchema, message));\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nimport { Protocol } from './protocol';\n\nexport const protocol = new Protocol([SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema]);\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\nimport { buf, bufWkt } from '@dxos/protocols/buf';\nimport { type Message, MessageSchema, type Peer as PeerProto } from '@dxos/protocols/buf/dxos/edge/messenger_pb';\nimport { bufferToArray } from '@dxos/util';\n\nexport type PeerData = Partial<PeerProto>;\n\nexport const getTypename = (typeName: string) => `type.googleapis.com/${typeName}`;\n\n/**\n * NOTE: The type registry should be extended with all message types.\n */\nexport class Protocol {\n private readonly _typeRegistry: buf.Registry;\n\n constructor(types: buf.DescMessage[]) {\n this._typeRegistry = buf.createRegistry(...types);\n }\n\n get typeRegistry(): buf.Registry {\n return this._typeRegistry;\n }\n\n toJson(message: Message): any {\n try {\n return buf.toJson(MessageSchema, message, { registry: this.typeRegistry });\n } catch (err) {\n return { type: this.getPayloadType(message) };\n }\n }\n\n /**\n * Return the payload with the given type.\n */\n getPayload<Desc extends buf.DescMessage>(message: Message, type: Desc): buf.MessageShape<Desc> {\n invariant(message.payload);\n const payloadTypename = this.getPayloadType(message);\n if (type && type.typeName !== payloadTypename) {\n throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);\n }\n\n invariant(bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`);\n const payload = bufWkt.anyUnpack(message.payload, this.typeRegistry) as buf.MessageShape<Desc>;\n invariant(payload, `Empty payload: ${payloadTypename}}`);\n return payload;\n }\n\n /**\n * Get the payload type.\n */\n getPayloadType(message: Message): string | undefined {\n if (!message.payload) {\n return undefined;\n }\n\n const [, type] = message.payload.typeUrl.split('/');\n return type;\n }\n\n /**\n * Create a packed message.\n */\n createMessage<Desc extends buf.DescMessage>(\n type: Desc,\n {\n source,\n target,\n payload,\n serviceId,\n }: {\n source?: PeerData;\n target?: PeerData[];\n payload?: buf.MessageInitShape<Desc>;\n serviceId?: string;\n },\n ) {\n return buf.create(MessageSchema, {\n timestamp: new Date().toISOString(),\n source,\n target,\n serviceId,\n payload: payload ? bufWkt.anyPack(type, buf.create(type, payload)) : undefined,\n });\n }\n}\n\n/**\n * Convert websocket data to Uint8Array.\n */\nexport const toUint8Array = async (data: any): Promise<Uint8Array> => {\n // Node.\n if (data instanceof Buffer) {\n return bufferToArray(data);\n }\n\n // Browser.\n if (data instanceof Blob) {\n return new Uint8Array(await (data as Blob).arrayBuffer());\n }\n\n throw new Error(`Unexpected datatype: ${data}`);\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["WebSocket", "Trigger", "invariant", "log", "buf", "MessageSchema", "SwarmRequestSchema", "SwarmResponseSchema", "TextMessageSchema", "invariant", "buf", "bufWkt", "MessageSchema", "bufferToArray", "getTypename", "typeName", "Protocol", "constructor", "types", "_typeRegistry", "createRegistry", "typeRegistry", "toJson", "message", "registry", "err", "type", "getPayloadType", "getPayload", "payload", "payloadTypename", "Error", "anyIs", "anyUnpack", "undefined", "typeUrl", "split", "createMessage", "source", "target", "serviceId", "create", "timestamp", "Date", "toISOString", "anyPack", "toUint8Array", "data", "Buffer", "Blob", "Uint8Array", "arrayBuffer", "protocol", "Protocol", "SwarmRequestSchema", "SwarmResponseSchema", "TextMessageSchema", "DEFAULT_TIMEOUT", "EdgeClient", "constructor", "_identityKey", "_deviceKey", "_config", "_listeners", "Set", "_protocol", "protocol", "info", "open", "isOpen", "identity", "device", "identityKey", "deviceKey", "_ws", "addListener", "listener", "add", "delete", "invariant", "log", "ready", "Trigger", "url", "URL", "toHex", "socketEndpoint", "WebSocket", "Object", "assign", "onopen", "wake", "onclose", "onerror", "event", "catch", "error", "throw", "onmessage", "data", "toUint8Array", "message", "buf", "fromBinary", "MessageSchema", "payload", "getPayloadType", "err", "result", "wait", "timeout", "close", "undefined", "send", "toBinary"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10314,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/edge-client/src/defs.ts":{"bytes":1359,"imports":[{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"packages/core/mesh/edge-client/src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/client.ts":{"bytes":16849,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"packages/core/mesh/edge-client/src/defs.ts","kind":"import-statement","original":"./defs"},{"path":"packages/core/mesh/edge-client/src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/index.ts":{"bytes":836,"imports":[{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"packages/core/mesh/edge-client/src/client.ts","kind":"import-statement","original":"./client"},{"path":"packages/core/mesh/edge-client/src/defs.ts","kind":"import-statement","original":"./defs"},{"path":"packages/core/mesh/edge-client/src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"}},"outputs":{"packages/core/mesh/edge-client/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":13078},"packages/core/mesh/edge-client/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["EdgeClient","Protocol","getTypename","protocol","toUint8Array"],"entryPoint":"packages/core/mesh/edge-client/src/index.ts","inputs":{"packages/core/mesh/edge-client/src/index.ts":{"bytesInOutput":60},"packages/core/mesh/edge-client/src/client.ts":{"bytesInOutput":4723},"packages/core/mesh/edge-client/src/defs.ts":{"bytesInOutput":220},"packages/core/mesh/edge-client/src/protocol.ts":{"bytesInOutput":2600}},"bytes":8008}}}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var node_exports = {};
|
|
31
|
+
__export(node_exports, {
|
|
32
|
+
EdgeClient: () => EdgeClient,
|
|
33
|
+
Protocol: () => Protocol,
|
|
34
|
+
getTypename: () => getTypename,
|
|
35
|
+
protocol: () => protocol,
|
|
36
|
+
toUint8Array: () => toUint8Array
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(node_exports);
|
|
39
|
+
__reExport(node_exports, require("@dxos/protocols/buf/dxos/edge/messenger_pb"), module.exports);
|
|
40
|
+
var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
|
|
41
|
+
var import_async = require("@dxos/async");
|
|
42
|
+
var import_invariant = require("@dxos/invariant");
|
|
43
|
+
var import_log = require("@dxos/log");
|
|
44
|
+
var import_buf = require("@dxos/protocols/buf");
|
|
45
|
+
var import_messenger_pb = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
|
|
46
|
+
var import_messenger_pb2 = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
|
|
47
|
+
var import_invariant2 = require("@dxos/invariant");
|
|
48
|
+
var import_buf2 = require("@dxos/protocols/buf");
|
|
49
|
+
var import_messenger_pb3 = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
|
|
50
|
+
var import_util = require("@dxos/util");
|
|
51
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/protocol.ts";
|
|
52
|
+
var getTypename = (typeName) => `type.googleapis.com/${typeName}`;
|
|
53
|
+
var Protocol = class {
|
|
54
|
+
constructor(types) {
|
|
55
|
+
this._typeRegistry = import_buf2.buf.createRegistry(...types);
|
|
56
|
+
}
|
|
57
|
+
get typeRegistry() {
|
|
58
|
+
return this._typeRegistry;
|
|
59
|
+
}
|
|
60
|
+
toJson(message) {
|
|
61
|
+
try {
|
|
62
|
+
return import_buf2.buf.toJson(import_messenger_pb3.MessageSchema, message, {
|
|
63
|
+
registry: this.typeRegistry
|
|
64
|
+
});
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return {
|
|
67
|
+
type: this.getPayloadType(message)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Return the payload with the given type.
|
|
73
|
+
*/
|
|
74
|
+
getPayload(message, type) {
|
|
75
|
+
(0, import_invariant2.invariant)(message.payload, void 0, {
|
|
76
|
+
F: __dxlog_file,
|
|
77
|
+
L: 40,
|
|
78
|
+
S: this,
|
|
79
|
+
A: [
|
|
80
|
+
"message.payload",
|
|
81
|
+
""
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
const payloadTypename = this.getPayloadType(message);
|
|
85
|
+
if (type && type.typeName !== payloadTypename) {
|
|
86
|
+
throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);
|
|
87
|
+
}
|
|
88
|
+
(0, import_invariant2.invariant)(import_buf2.bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`, {
|
|
89
|
+
F: __dxlog_file,
|
|
90
|
+
L: 46,
|
|
91
|
+
S: this,
|
|
92
|
+
A: [
|
|
93
|
+
"bufWkt.anyIs(message.payload, type)",
|
|
94
|
+
"`Unexpected payload type: ${payloadTypename}}`"
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
const payload = import_buf2.bufWkt.anyUnpack(message.payload, this.typeRegistry);
|
|
98
|
+
(0, import_invariant2.invariant)(payload, `Empty payload: ${payloadTypename}}`, {
|
|
99
|
+
F: __dxlog_file,
|
|
100
|
+
L: 48,
|
|
101
|
+
S: this,
|
|
102
|
+
A: [
|
|
103
|
+
"payload",
|
|
104
|
+
"`Empty payload: ${payloadTypename}}`"
|
|
105
|
+
]
|
|
106
|
+
});
|
|
107
|
+
return payload;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get the payload type.
|
|
111
|
+
*/
|
|
112
|
+
getPayloadType(message) {
|
|
113
|
+
if (!message.payload) {
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
116
|
+
const [, type] = message.payload.typeUrl.split("/");
|
|
117
|
+
return type;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create a packed message.
|
|
121
|
+
*/
|
|
122
|
+
createMessage(type, { source, target, payload, serviceId }) {
|
|
123
|
+
return import_buf2.buf.create(import_messenger_pb3.MessageSchema, {
|
|
124
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
125
|
+
source,
|
|
126
|
+
target,
|
|
127
|
+
serviceId,
|
|
128
|
+
payload: payload ? import_buf2.bufWkt.anyPack(type, import_buf2.buf.create(type, payload)) : void 0
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var toUint8Array = async (data) => {
|
|
133
|
+
if (data instanceof Buffer) {
|
|
134
|
+
return (0, import_util.bufferToArray)(data);
|
|
135
|
+
}
|
|
136
|
+
if (data instanceof Blob) {
|
|
137
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`Unexpected datatype: ${data}`);
|
|
140
|
+
};
|
|
141
|
+
var protocol = new Protocol([
|
|
142
|
+
import_messenger_pb2.SwarmRequestSchema,
|
|
143
|
+
import_messenger_pb2.SwarmResponseSchema,
|
|
144
|
+
import_messenger_pb2.TextMessageSchema
|
|
145
|
+
]);
|
|
146
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/client.ts";
|
|
147
|
+
var DEFAULT_TIMEOUT = 5e3;
|
|
148
|
+
var EdgeClient = class {
|
|
149
|
+
constructor(_identityKey, _deviceKey, _config) {
|
|
150
|
+
this._identityKey = _identityKey;
|
|
151
|
+
this._deviceKey = _deviceKey;
|
|
152
|
+
this._config = _config;
|
|
153
|
+
this._listeners = /* @__PURE__ */ new Set();
|
|
154
|
+
this._protocol = this._config.protocol ?? protocol;
|
|
155
|
+
}
|
|
156
|
+
// TODO(burdon): Attach logging.
|
|
157
|
+
get info() {
|
|
158
|
+
return {
|
|
159
|
+
open: this.isOpen,
|
|
160
|
+
identity: this._identityKey,
|
|
161
|
+
device: this._deviceKey
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
get identityKey() {
|
|
165
|
+
return this._identityKey;
|
|
166
|
+
}
|
|
167
|
+
get deviceKey() {
|
|
168
|
+
return this._deviceKey;
|
|
169
|
+
}
|
|
170
|
+
get isOpen() {
|
|
171
|
+
return !!this._ws;
|
|
172
|
+
}
|
|
173
|
+
addListener(listener) {
|
|
174
|
+
this._listeners.add(listener);
|
|
175
|
+
return () => this._listeners.delete(listener);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Open connection to messaging service.
|
|
179
|
+
*/
|
|
180
|
+
async open() {
|
|
181
|
+
(0, import_invariant.invariant)(!this._ws, void 0, {
|
|
182
|
+
F: __dxlog_file2,
|
|
183
|
+
L: 88,
|
|
184
|
+
S: this,
|
|
185
|
+
A: [
|
|
186
|
+
"!this._ws",
|
|
187
|
+
""
|
|
188
|
+
]
|
|
189
|
+
});
|
|
190
|
+
import_log.log.info("opening...", {
|
|
191
|
+
info: this.info
|
|
192
|
+
}, {
|
|
193
|
+
F: __dxlog_file2,
|
|
194
|
+
L: 89,
|
|
195
|
+
S: this,
|
|
196
|
+
C: (f, a) => f(...a)
|
|
197
|
+
});
|
|
198
|
+
const ready = new import_async.Trigger();
|
|
199
|
+
const url = new URL(`/ws/${this._identityKey.toHex()}/${this._deviceKey.toHex()}`, this._config.socketEndpoint);
|
|
200
|
+
this._ws = new import_isomorphic_ws.default(url);
|
|
201
|
+
Object.assign(this._ws, {
|
|
202
|
+
onopen: () => {
|
|
203
|
+
import_log.log.info("opened", this.info, {
|
|
204
|
+
F: __dxlog_file2,
|
|
205
|
+
L: 97,
|
|
206
|
+
S: this,
|
|
207
|
+
C: (f, a) => f(...a)
|
|
208
|
+
});
|
|
209
|
+
ready.wake(true);
|
|
210
|
+
},
|
|
211
|
+
onclose: () => {
|
|
212
|
+
import_log.log.info("closed", this.info, {
|
|
213
|
+
F: __dxlog_file2,
|
|
214
|
+
L: 102,
|
|
215
|
+
S: this,
|
|
216
|
+
C: (f, a) => f(...a)
|
|
217
|
+
});
|
|
218
|
+
ready.wake(false);
|
|
219
|
+
},
|
|
220
|
+
onerror: (event) => {
|
|
221
|
+
import_log.log.catch(event.error, this.info, {
|
|
222
|
+
F: __dxlog_file2,
|
|
223
|
+
L: 107,
|
|
224
|
+
S: this,
|
|
225
|
+
C: (f, a) => f(...a)
|
|
226
|
+
});
|
|
227
|
+
ready.throw(event.error);
|
|
228
|
+
},
|
|
229
|
+
/**
|
|
230
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data
|
|
231
|
+
*/
|
|
232
|
+
onmessage: async (event) => {
|
|
233
|
+
const data = await toUint8Array(event.data);
|
|
234
|
+
const message = import_buf.buf.fromBinary(import_messenger_pb.MessageSchema, data);
|
|
235
|
+
(0, import_log.log)("received", {
|
|
236
|
+
deviceKey: this._deviceKey,
|
|
237
|
+
payload: protocol.getPayloadType(message)
|
|
238
|
+
}, {
|
|
239
|
+
F: __dxlog_file2,
|
|
240
|
+
L: 117,
|
|
241
|
+
S: this,
|
|
242
|
+
C: (f, a) => f(...a)
|
|
243
|
+
});
|
|
244
|
+
if (message) {
|
|
245
|
+
for (const listener of this._listeners) {
|
|
246
|
+
try {
|
|
247
|
+
await listener(message);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
import_log.log.error("processing", {
|
|
250
|
+
err,
|
|
251
|
+
payload: protocol.getPayloadType(message)
|
|
252
|
+
}, {
|
|
253
|
+
F: __dxlog_file2,
|
|
254
|
+
L: 123,
|
|
255
|
+
S: this,
|
|
256
|
+
C: (f, a) => f(...a)
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
const result = await ready.wait({
|
|
264
|
+
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
265
|
+
});
|
|
266
|
+
import_log.log.info("opened", {
|
|
267
|
+
info: this.info
|
|
268
|
+
}, {
|
|
269
|
+
F: __dxlog_file2,
|
|
270
|
+
L: 131,
|
|
271
|
+
S: this,
|
|
272
|
+
C: (f, a) => f(...a)
|
|
273
|
+
});
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Close connection and free resources.
|
|
278
|
+
*/
|
|
279
|
+
async close() {
|
|
280
|
+
if (this._ws) {
|
|
281
|
+
import_log.log.info("closing...", {
|
|
282
|
+
deviceKey: this._deviceKey
|
|
283
|
+
}, {
|
|
284
|
+
F: __dxlog_file2,
|
|
285
|
+
L: 140,
|
|
286
|
+
S: this,
|
|
287
|
+
C: (f, a) => f(...a)
|
|
288
|
+
});
|
|
289
|
+
this._ws.close();
|
|
290
|
+
this._ws = void 0;
|
|
291
|
+
import_log.log.info("closed", {
|
|
292
|
+
deviceKey: this._deviceKey
|
|
293
|
+
}, {
|
|
294
|
+
F: __dxlog_file2,
|
|
295
|
+
L: 143,
|
|
296
|
+
S: this,
|
|
297
|
+
C: (f, a) => f(...a)
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Send message.
|
|
303
|
+
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
304
|
+
*/
|
|
305
|
+
// TODO(burdon): Implement ACK?
|
|
306
|
+
async send(message) {
|
|
307
|
+
(0, import_invariant.invariant)(this._ws, void 0, {
|
|
308
|
+
F: __dxlog_file2,
|
|
309
|
+
L: 153,
|
|
310
|
+
S: this,
|
|
311
|
+
A: [
|
|
312
|
+
"this._ws",
|
|
313
|
+
""
|
|
314
|
+
]
|
|
315
|
+
});
|
|
316
|
+
(0, import_log.log)("sending...", {
|
|
317
|
+
deviceKey: this._deviceKey,
|
|
318
|
+
payload: protocol.getPayloadType(message)
|
|
319
|
+
}, {
|
|
320
|
+
F: __dxlog_file2,
|
|
321
|
+
L: 154,
|
|
322
|
+
S: this,
|
|
323
|
+
C: (f, a) => f(...a)
|
|
324
|
+
});
|
|
325
|
+
this._ws.send(import_buf.buf.toBinary(import_messenger_pb.MessageSchema, message));
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
329
|
+
0 && (module.exports = {
|
|
330
|
+
EdgeClient,
|
|
331
|
+
Protocol,
|
|
332
|
+
getTypename,
|
|
333
|
+
protocol,
|
|
334
|
+
toUint8Array,
|
|
335
|
+
...require("@dxos/protocols/buf/dxos/edge/messenger_pb")
|
|
336
|
+
});
|
|
337
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/index.ts", "../../../src/client.ts", "../../../src/defs.ts", "../../../src/protocol.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nexport * from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nexport * from './client';\nexport * from './defs';\nexport * from './protocol';\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Trigger } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\nimport { type PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { buf } from '@dxos/protocols/buf';\nimport { type Message, MessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nimport { protocol } from './defs';\nimport { type Protocol, toUint8Array } from './protocol';\n\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type MessageListener = (message: Message) => void | Promise<void>;\n\n/**\n *\n */\nexport interface EdgeConnection {\n get info(): any;\n get identityKey(): PublicKey;\n get deviceKey(): PublicKey;\n get isOpen(): boolean;\n addListener(listener: MessageListener): () => void;\n open(): Promise<boolean>;\n close(): Promise<void>;\n send(message: Message): Promise<void>;\n}\n\nexport type MessengerConfig = {\n socketEndpoint: string;\n timeout?: number;\n protocol?: Protocol;\n};\n\n/**\n * Messenger client.\n */\n// TODO(dmaretskyi): Rename EdgeClient.\nexport class EdgeClient implements EdgeConnection {\n private readonly _listeners: Set<MessageListener> = new Set();\n private readonly _protocol: Protocol;\n private _ws?: WebSocket;\n\n constructor(\n private readonly _identityKey: PublicKey,\n private readonly _deviceKey: PublicKey,\n private readonly _config: MessengerConfig,\n ) {\n this._protocol = this._config.protocol ?? protocol;\n }\n\n // TODO(burdon): Attach logging.\n public get info() {\n return {\n open: this.isOpen,\n identity: this._identityKey,\n device: this._deviceKey,\n };\n }\n\n get identityKey() {\n return this._identityKey;\n }\n\n get deviceKey() {\n return this._deviceKey;\n }\n\n public get isOpen() {\n return !!this._ws;\n }\n\n public addListener(listener: MessageListener): () => void {\n this._listeners.add(listener);\n return () => this._listeners.delete(listener);\n }\n\n /**\n * Open connection to messaging service.\n */\n public async open(): Promise<boolean> {\n invariant(!this._ws);\n log.info('opening...', { info: this.info });\n const ready = new Trigger<boolean>();\n\n // TODO: handle reconnects\n const url = new URL(`/ws/${this._identityKey.toHex()}/${this._deviceKey.toHex()}`, this._config.socketEndpoint);\n this._ws = new WebSocket(url);\n Object.assign<WebSocket, Partial<WebSocket>>(this._ws, {\n onopen: () => {\n log.info('opened', this.info);\n ready.wake(true);\n },\n\n onclose: () => {\n log.info('closed', this.info);\n ready.wake(false);\n },\n\n onerror: (event) => {\n log.catch(event.error, this.info);\n ready.throw(event.error);\n },\n\n /**\n * https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data\n */\n onmessage: async (event) => {\n const data = await toUint8Array(event.data);\n const message = buf.fromBinary(MessageSchema, data);\n log('received', { deviceKey: this._deviceKey, payload: protocol.getPayloadType(message) });\n if (message) {\n for (const listener of this._listeners) {\n try {\n await listener(message);\n } catch (err) {\n log.error('processing', { err, payload: protocol.getPayloadType(message) });\n }\n }\n }\n },\n });\n\n const result = await ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n log.info('opened', { info: this.info });\n return result;\n }\n\n /**\n * Close connection and free resources.\n */\n public async close() {\n if (this._ws) {\n log.info('closing...', { deviceKey: this._deviceKey });\n this._ws.close();\n this._ws = undefined;\n log.info('closed', { deviceKey: this._deviceKey });\n }\n }\n\n /**\n * Send message.\n * NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.\n */\n // TODO(burdon): Implement ACK?\n public async send(message: Message): Promise<void> {\n invariant(this._ws);\n log('sending...', { deviceKey: this._deviceKey, payload: protocol.getPayloadType(message) });\n this._ws.send(buf.toBinary(MessageSchema, message));\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nimport { Protocol } from './protocol';\n\nexport const protocol = new Protocol([SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema]);\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\nimport { buf, bufWkt } from '@dxos/protocols/buf';\nimport { type Message, MessageSchema, type Peer as PeerProto } from '@dxos/protocols/buf/dxos/edge/messenger_pb';\nimport { bufferToArray } from '@dxos/util';\n\nexport type PeerData = Partial<PeerProto>;\n\nexport const getTypename = (typeName: string) => `type.googleapis.com/${typeName}`;\n\n/**\n * NOTE: The type registry should be extended with all message types.\n */\nexport class Protocol {\n private readonly _typeRegistry: buf.Registry;\n\n constructor(types: buf.DescMessage[]) {\n this._typeRegistry = buf.createRegistry(...types);\n }\n\n get typeRegistry(): buf.Registry {\n return this._typeRegistry;\n }\n\n toJson(message: Message): any {\n try {\n return buf.toJson(MessageSchema, message, { registry: this.typeRegistry });\n } catch (err) {\n return { type: this.getPayloadType(message) };\n }\n }\n\n /**\n * Return the payload with the given type.\n */\n getPayload<Desc extends buf.DescMessage>(message: Message, type: Desc): buf.MessageShape<Desc> {\n invariant(message.payload);\n const payloadTypename = this.getPayloadType(message);\n if (type && type.typeName !== payloadTypename) {\n throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);\n }\n\n invariant(bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`);\n const payload = bufWkt.anyUnpack(message.payload, this.typeRegistry) as buf.MessageShape<Desc>;\n invariant(payload, `Empty payload: ${payloadTypename}}`);\n return payload;\n }\n\n /**\n * Get the payload type.\n */\n getPayloadType(message: Message): string | undefined {\n if (!message.payload) {\n return undefined;\n }\n\n const [, type] = message.payload.typeUrl.split('/');\n return type;\n }\n\n /**\n * Create a packed message.\n */\n createMessage<Desc extends buf.DescMessage>(\n type: Desc,\n {\n source,\n target,\n payload,\n serviceId,\n }: {\n source?: PeerData;\n target?: PeerData[];\n payload?: buf.MessageInitShape<Desc>;\n serviceId?: string;\n },\n ) {\n return buf.create(MessageSchema, {\n timestamp: new Date().toISOString(),\n source,\n target,\n serviceId,\n payload: payload ? bufWkt.anyPack(type, buf.create(type, payload)) : undefined,\n });\n }\n}\n\n/**\n * Convert websocket data to Uint8Array.\n */\nexport const toUint8Array = async (data: any): Promise<Uint8Array> => {\n // Node.\n if (data instanceof Buffer) {\n return bufferToArray(data);\n }\n\n // Browser.\n if (data instanceof Blob) {\n return new Uint8Array(await (data as Blob).arrayBuffer());\n }\n\n throw new Error(`Unexpected datatype: ${data}`);\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_messenger_pb", "import_invariant", "import_buf", "getTypename", "typeName", "Protocol", "constructor", "types", "_typeRegistry", "buf", "createRegistry", "typeRegistry", "toJson", "message", "MessageSchema", "registry", "err", "type", "getPayloadType", "getPayload", "invariant", "payload", "payloadTypename", "Error", "bufWkt", "anyIs", "anyUnpack", "undefined", "typeUrl", "split", "createMessage", "source", "target", "serviceId", "create", "timestamp", "Date", "toISOString", "anyPack", "toUint8Array", "data", "Buffer", "bufferToArray", "Blob", "Uint8Array", "arrayBuffer", "protocol", "SwarmRequestSchema", "SwarmResponseSchema", "TextMessageSchema", "DEFAULT_TIMEOUT", "EdgeClient", "_identityKey", "_deviceKey", "_config", "_listeners", "Set", "_protocol", "info", "open", "isOpen", "identity", "device", "identityKey", "deviceKey", "_ws", "addListener", "listener", "add", "delete", "log", "ready", "Trigger", "url", "URL", "toHex", "socketEndpoint", "WebSocket", "Object", "assign", "onopen", "wake", "onclose", "onerror", "event", "catch", "error", "throw", "onmessage", "fromBinary", "result", "wait", "timeout", "close", "send", "toBinary"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10314,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/edge-client/src/defs.ts":{"bytes":1359,"imports":[{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"packages/core/mesh/edge-client/src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/client.ts":{"bytes":16849,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"packages/core/mesh/edge-client/src/defs.ts","kind":"import-statement","original":"./defs"},{"path":"packages/core/mesh/edge-client/src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/index.ts":{"bytes":836,"imports":[{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"packages/core/mesh/edge-client/src/client.ts","kind":"import-statement","original":"./client"},{"path":"packages/core/mesh/edge-client/src/defs.ts","kind":"import-statement","original":"./defs"},{"path":"packages/core/mesh/edge-client/src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"}},"outputs":{"packages/core/mesh/edge-client/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":13078},"packages/core/mesh/edge-client/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["EdgeClient","Protocol","getTypename","protocol","toUint8Array"],"entryPoint":"packages/core/mesh/edge-client/src/index.ts","inputs":{"packages/core/mesh/edge-client/src/index.ts":{"bytesInOutput":60},"packages/core/mesh/edge-client/src/client.ts":{"bytesInOutput":4723},"packages/core/mesh/edge-client/src/defs.ts":{"bytesInOutput":220},"packages/core/mesh/edge-client/src/protocol.ts":{"bytesInOutput":2600}},"bytes":8008}}}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type PublicKey } from '@dxos/keys';
|
|
2
|
+
import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
3
|
+
import { type Protocol } from './protocol';
|
|
4
|
+
export type MessageListener = (message: Message) => void | Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
export interface EdgeConnection {
|
|
9
|
+
get info(): any;
|
|
10
|
+
get identityKey(): PublicKey;
|
|
11
|
+
get deviceKey(): PublicKey;
|
|
12
|
+
get isOpen(): boolean;
|
|
13
|
+
addListener(listener: MessageListener): () => void;
|
|
14
|
+
open(): Promise<boolean>;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
send(message: Message): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export type MessengerConfig = {
|
|
19
|
+
socketEndpoint: string;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
protocol?: Protocol;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Messenger client.
|
|
25
|
+
*/
|
|
26
|
+
export declare class EdgeClient implements EdgeConnection {
|
|
27
|
+
private readonly _identityKey;
|
|
28
|
+
private readonly _deviceKey;
|
|
29
|
+
private readonly _config;
|
|
30
|
+
private readonly _listeners;
|
|
31
|
+
private readonly _protocol;
|
|
32
|
+
private _ws?;
|
|
33
|
+
constructor(_identityKey: PublicKey, _deviceKey: PublicKey, _config: MessengerConfig);
|
|
34
|
+
get info(): {
|
|
35
|
+
open: boolean;
|
|
36
|
+
identity: PublicKey;
|
|
37
|
+
device: PublicKey;
|
|
38
|
+
};
|
|
39
|
+
get identityKey(): PublicKey;
|
|
40
|
+
get deviceKey(): PublicKey;
|
|
41
|
+
get isOpen(): boolean;
|
|
42
|
+
addListener(listener: MessageListener): () => void;
|
|
43
|
+
/**
|
|
44
|
+
* Open connection to messaging service.
|
|
45
|
+
*/
|
|
46
|
+
open(): Promise<boolean>;
|
|
47
|
+
/**
|
|
48
|
+
* Close connection and free resources.
|
|
49
|
+
*/
|
|
50
|
+
close(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Send message.
|
|
53
|
+
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
54
|
+
*/
|
|
55
|
+
send(message: Message): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,4CAA4C,CAAC;AAGzF,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAIzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,IAAI,IAAI,GAAG,CAAC;IAChB,IAAI,WAAW,IAAI,SAAS,CAAC;IAC7B,IAAI,SAAS,IAAI,SAAS,CAAC;IAC3B,IAAI,MAAM,IAAI,OAAO,CAAC;IACtB,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI,CAAC;IACnD,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF;;GAEG;AAEH,qBAAa,UAAW,YAAW,cAAc;IAM7C,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,GAAG,CAAC,CAAY;gBAGL,YAAY,EAAE,SAAS,EACvB,UAAU,EAAE,SAAS,EACrB,OAAO,EAAE,eAAe;IAM3C,IAAW,IAAI;;;;MAMd;IAED,IAAI,WAAW,cAEd;IAED,IAAI,SAAS,cAEZ;IAED,IAAW,MAAM,YAEhB;IAEM,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IAKzD;;OAEG;IACU,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAgDrC;;OAEG;IACU,KAAK;IASlB;;;OAGG;IAEU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAKnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,eAAO,MAAM,QAAQ,UAA6E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,4CAA4C,CAAC;AAE3D,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { buf } from '@dxos/protocols/buf';
|
|
2
|
+
import { type Message, type Peer as PeerProto } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
3
|
+
export type PeerData = Partial<PeerProto>;
|
|
4
|
+
export declare const getTypename: (typeName: string) => string;
|
|
5
|
+
/**
|
|
6
|
+
* NOTE: The type registry should be extended with all message types.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Protocol {
|
|
9
|
+
private readonly _typeRegistry;
|
|
10
|
+
constructor(types: buf.DescMessage[]);
|
|
11
|
+
get typeRegistry(): buf.Registry;
|
|
12
|
+
toJson(message: Message): any;
|
|
13
|
+
/**
|
|
14
|
+
* Return the payload with the given type.
|
|
15
|
+
*/
|
|
16
|
+
getPayload<Desc extends buf.DescMessage>(message: Message, type: Desc): buf.MessageShape<Desc>;
|
|
17
|
+
/**
|
|
18
|
+
* Get the payload type.
|
|
19
|
+
*/
|
|
20
|
+
getPayloadType(message: Message): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Create a packed message.
|
|
23
|
+
*/
|
|
24
|
+
createMessage<Desc extends buf.DescMessage>(type: Desc, { source, target, payload, serviceId, }: {
|
|
25
|
+
source?: PeerData;
|
|
26
|
+
target?: PeerData[];
|
|
27
|
+
payload?: buf.MessageInitShape<Desc>;
|
|
28
|
+
serviceId?: string;
|
|
29
|
+
}): Message;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Convert websocket data to Uint8Array.
|
|
33
|
+
*/
|
|
34
|
+
export declare const toUint8Array: (data: any) => Promise<Uint8Array>;
|
|
35
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/protocol.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,GAAG,EAAU,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,KAAK,OAAO,EAAiB,KAAK,IAAI,IAAI,SAAS,EAAE,MAAM,4CAA4C,CAAC;AAGjH,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAE1C,eAAO,MAAM,WAAW,aAAc,MAAM,WAAsC,CAAC;AAEnF;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;gBAEjC,KAAK,EAAE,GAAG,CAAC,WAAW,EAAE;IAIpC,IAAI,YAAY,IAAI,GAAG,CAAC,QAAQ,CAE/B;IAED,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,GAAG;IAQ7B;;OAEG;IACH,UAAU,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;IAa9F;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;IASpD;;OAEG;IACH,aAAa,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,EACxC,IAAI,EAAE,IAAI,EACV,EACE,MAAM,EACN,MAAM,EACN,OAAO,EACP,SAAS,GACV,EAAE;QACD,MAAM,CAAC,EAAE,QAAQ,CAAC;QAClB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;QACpB,OAAO,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;CAUJ;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,SAAgB,GAAG,KAAG,QAAQ,UAAU,CAYhE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.test.d.ts","sourceRoot":"","sources":["../../../src/protocol.test.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/edge-client",
|
|
3
|
+
"version": "0.6.6-main.e1a6e1f",
|
|
4
|
+
"description": "EDGE Client",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "DXOS.org",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"browser": "./dist/lib/browser/index.mjs",
|
|
12
|
+
"node": {
|
|
13
|
+
"default": "./dist/lib/node/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"types": "./dist/types/src/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"types": "dist/types/src/index.d.ts",
|
|
19
|
+
"typesVersions": {
|
|
20
|
+
"*": {}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"isomorphic-ws": "^5.0.0",
|
|
29
|
+
"ws": "^8.14.2",
|
|
30
|
+
"@dxos/log": "0.6.6-main.e1a6e1f",
|
|
31
|
+
"@dxos/async": "0.6.6-main.e1a6e1f",
|
|
32
|
+
"@dxos/node-std": "0.6.6-main.e1a6e1f",
|
|
33
|
+
"@dxos/protocols": "0.6.6-main.e1a6e1f",
|
|
34
|
+
"@dxos/invariant": "0.6.6-main.e1a6e1f",
|
|
35
|
+
"@dxos/util": "0.6.6-main.e1a6e1f"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import WebSocket from 'isomorphic-ws';
|
|
6
|
+
|
|
7
|
+
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { invariant } from '@dxos/invariant';
|
|
9
|
+
import { type PublicKey } from '@dxos/keys';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
11
|
+
import { buf } from '@dxos/protocols/buf';
|
|
12
|
+
import { type Message, MessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
13
|
+
|
|
14
|
+
import { protocol } from './defs';
|
|
15
|
+
import { type Protocol, toUint8Array } from './protocol';
|
|
16
|
+
|
|
17
|
+
const DEFAULT_TIMEOUT = 5_000;
|
|
18
|
+
|
|
19
|
+
export type MessageListener = (message: Message) => void | Promise<void>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
export interface EdgeConnection {
|
|
25
|
+
get info(): any;
|
|
26
|
+
get identityKey(): PublicKey;
|
|
27
|
+
get deviceKey(): PublicKey;
|
|
28
|
+
get isOpen(): boolean;
|
|
29
|
+
addListener(listener: MessageListener): () => void;
|
|
30
|
+
open(): Promise<boolean>;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
send(message: Message): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type MessengerConfig = {
|
|
36
|
+
socketEndpoint: string;
|
|
37
|
+
timeout?: number;
|
|
38
|
+
protocol?: Protocol;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Messenger client.
|
|
43
|
+
*/
|
|
44
|
+
// TODO(dmaretskyi): Rename EdgeClient.
|
|
45
|
+
export class EdgeClient implements EdgeConnection {
|
|
46
|
+
private readonly _listeners: Set<MessageListener> = new Set();
|
|
47
|
+
private readonly _protocol: Protocol;
|
|
48
|
+
private _ws?: WebSocket;
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
private readonly _identityKey: PublicKey,
|
|
52
|
+
private readonly _deviceKey: PublicKey,
|
|
53
|
+
private readonly _config: MessengerConfig,
|
|
54
|
+
) {
|
|
55
|
+
this._protocol = this._config.protocol ?? protocol;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// TODO(burdon): Attach logging.
|
|
59
|
+
public get info() {
|
|
60
|
+
return {
|
|
61
|
+
open: this.isOpen,
|
|
62
|
+
identity: this._identityKey,
|
|
63
|
+
device: this._deviceKey,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get identityKey() {
|
|
68
|
+
return this._identityKey;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get deviceKey() {
|
|
72
|
+
return this._deviceKey;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public get isOpen() {
|
|
76
|
+
return !!this._ws;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public addListener(listener: MessageListener): () => void {
|
|
80
|
+
this._listeners.add(listener);
|
|
81
|
+
return () => this._listeners.delete(listener);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Open connection to messaging service.
|
|
86
|
+
*/
|
|
87
|
+
public async open(): Promise<boolean> {
|
|
88
|
+
invariant(!this._ws);
|
|
89
|
+
log.info('opening...', { info: this.info });
|
|
90
|
+
const ready = new Trigger<boolean>();
|
|
91
|
+
|
|
92
|
+
// TODO: handle reconnects
|
|
93
|
+
const url = new URL(`/ws/${this._identityKey.toHex()}/${this._deviceKey.toHex()}`, this._config.socketEndpoint);
|
|
94
|
+
this._ws = new WebSocket(url);
|
|
95
|
+
Object.assign<WebSocket, Partial<WebSocket>>(this._ws, {
|
|
96
|
+
onopen: () => {
|
|
97
|
+
log.info('opened', this.info);
|
|
98
|
+
ready.wake(true);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
onclose: () => {
|
|
102
|
+
log.info('closed', this.info);
|
|
103
|
+
ready.wake(false);
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
onerror: (event) => {
|
|
107
|
+
log.catch(event.error, this.info);
|
|
108
|
+
ready.throw(event.error);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data
|
|
113
|
+
*/
|
|
114
|
+
onmessage: async (event) => {
|
|
115
|
+
const data = await toUint8Array(event.data);
|
|
116
|
+
const message = buf.fromBinary(MessageSchema, data);
|
|
117
|
+
log('received', { deviceKey: this._deviceKey, payload: protocol.getPayloadType(message) });
|
|
118
|
+
if (message) {
|
|
119
|
+
for (const listener of this._listeners) {
|
|
120
|
+
try {
|
|
121
|
+
await listener(message);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
log.error('processing', { err, payload: protocol.getPayloadType(message) });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const result = await ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });
|
|
131
|
+
log.info('opened', { info: this.info });
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Close connection and free resources.
|
|
137
|
+
*/
|
|
138
|
+
public async close() {
|
|
139
|
+
if (this._ws) {
|
|
140
|
+
log.info('closing...', { deviceKey: this._deviceKey });
|
|
141
|
+
this._ws.close();
|
|
142
|
+
this._ws = undefined;
|
|
143
|
+
log.info('closed', { deviceKey: this._deviceKey });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Send message.
|
|
149
|
+
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
150
|
+
*/
|
|
151
|
+
// TODO(burdon): Implement ACK?
|
|
152
|
+
public async send(message: Message): Promise<void> {
|
|
153
|
+
invariant(this._ws);
|
|
154
|
+
log('sending...', { deviceKey: this._deviceKey, payload: protocol.getPayloadType(message) });
|
|
155
|
+
this._ws.send(buf.toBinary(MessageSchema, message));
|
|
156
|
+
}
|
|
157
|
+
}
|
package/src/defs.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
6
|
+
|
|
7
|
+
import { Protocol } from './protocol';
|
|
8
|
+
|
|
9
|
+
export const protocol = new Protocol([SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema]);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import { describe, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { buf } from '@dxos/protocols/buf';
|
|
9
|
+
import {
|
|
10
|
+
MessageSchema,
|
|
11
|
+
SwarmRequest_Action as SwarmRequestAction,
|
|
12
|
+
SwarmRequestSchema,
|
|
13
|
+
SwarmResponseSchema,
|
|
14
|
+
} from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
15
|
+
|
|
16
|
+
import { Protocol } from './protocol';
|
|
17
|
+
|
|
18
|
+
describe('protocol', () => {
|
|
19
|
+
test('protocol', () => {
|
|
20
|
+
const protocol = new Protocol([SwarmRequestSchema, SwarmResponseSchema]);
|
|
21
|
+
const message1 = protocol.createMessage(SwarmRequestSchema, { payload: { action: SwarmRequestAction.JOIN } });
|
|
22
|
+
const data = buf.toBinary(MessageSchema, message1);
|
|
23
|
+
const message2 = buf.fromBinary(MessageSchema, data);
|
|
24
|
+
const request = protocol.getPayload(message2, SwarmRequestSchema);
|
|
25
|
+
expect(request.action).to.eq(SwarmRequestAction.JOIN);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('json', () => {
|
|
29
|
+
const protocol = new Protocol([SwarmRequestSchema, SwarmResponseSchema]);
|
|
30
|
+
const message1 = protocol.createMessage(SwarmRequestSchema, { payload: { action: SwarmRequestAction.JOIN } });
|
|
31
|
+
const json = buf.toJson(MessageSchema, message1, { registry: protocol.typeRegistry });
|
|
32
|
+
const message2 = buf.fromJson(MessageSchema, json, { registry: protocol.typeRegistry });
|
|
33
|
+
const request = protocol.getPayload(message2, SwarmRequestSchema);
|
|
34
|
+
expect(request.action).to.eq(SwarmRequestAction.JOIN);
|
|
35
|
+
});
|
|
36
|
+
});
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { invariant } from '@dxos/invariant';
|
|
6
|
+
import { buf, bufWkt } from '@dxos/protocols/buf';
|
|
7
|
+
import { type Message, MessageSchema, type Peer as PeerProto } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
8
|
+
import { bufferToArray } from '@dxos/util';
|
|
9
|
+
|
|
10
|
+
export type PeerData = Partial<PeerProto>;
|
|
11
|
+
|
|
12
|
+
export const getTypename = (typeName: string) => `type.googleapis.com/${typeName}`;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* NOTE: The type registry should be extended with all message types.
|
|
16
|
+
*/
|
|
17
|
+
export class Protocol {
|
|
18
|
+
private readonly _typeRegistry: buf.Registry;
|
|
19
|
+
|
|
20
|
+
constructor(types: buf.DescMessage[]) {
|
|
21
|
+
this._typeRegistry = buf.createRegistry(...types);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get typeRegistry(): buf.Registry {
|
|
25
|
+
return this._typeRegistry;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
toJson(message: Message): any {
|
|
29
|
+
try {
|
|
30
|
+
return buf.toJson(MessageSchema, message, { registry: this.typeRegistry });
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return { type: this.getPayloadType(message) };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Return the payload with the given type.
|
|
38
|
+
*/
|
|
39
|
+
getPayload<Desc extends buf.DescMessage>(message: Message, type: Desc): buf.MessageShape<Desc> {
|
|
40
|
+
invariant(message.payload);
|
|
41
|
+
const payloadTypename = this.getPayloadType(message);
|
|
42
|
+
if (type && type.typeName !== payloadTypename) {
|
|
43
|
+
throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
invariant(bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`);
|
|
47
|
+
const payload = bufWkt.anyUnpack(message.payload, this.typeRegistry) as buf.MessageShape<Desc>;
|
|
48
|
+
invariant(payload, `Empty payload: ${payloadTypename}}`);
|
|
49
|
+
return payload;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the payload type.
|
|
54
|
+
*/
|
|
55
|
+
getPayloadType(message: Message): string | undefined {
|
|
56
|
+
if (!message.payload) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const [, type] = message.payload.typeUrl.split('/');
|
|
61
|
+
return type;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a packed message.
|
|
66
|
+
*/
|
|
67
|
+
createMessage<Desc extends buf.DescMessage>(
|
|
68
|
+
type: Desc,
|
|
69
|
+
{
|
|
70
|
+
source,
|
|
71
|
+
target,
|
|
72
|
+
payload,
|
|
73
|
+
serviceId,
|
|
74
|
+
}: {
|
|
75
|
+
source?: PeerData;
|
|
76
|
+
target?: PeerData[];
|
|
77
|
+
payload?: buf.MessageInitShape<Desc>;
|
|
78
|
+
serviceId?: string;
|
|
79
|
+
},
|
|
80
|
+
) {
|
|
81
|
+
return buf.create(MessageSchema, {
|
|
82
|
+
timestamp: new Date().toISOString(),
|
|
83
|
+
source,
|
|
84
|
+
target,
|
|
85
|
+
serviceId,
|
|
86
|
+
payload: payload ? bufWkt.anyPack(type, buf.create(type, payload)) : undefined,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Convert websocket data to Uint8Array.
|
|
93
|
+
*/
|
|
94
|
+
export const toUint8Array = async (data: any): Promise<Uint8Array> => {
|
|
95
|
+
// Node.
|
|
96
|
+
if (data instanceof Buffer) {
|
|
97
|
+
return bufferToArray(data);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Browser.
|
|
101
|
+
if (data instanceof Blob) {
|
|
102
|
+
return new Uint8Array(await (data as Blob).arrayBuffer());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
throw new Error(`Unexpected datatype: ${data}`);
|
|
106
|
+
};
|