@dxos/edge-client 0.6.9 → 0.6.10-main.3cfcc89
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/dist/lib/browser/index.mjs +276 -138
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +263 -129
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/{client.d.ts → edge-client.d.ts} +21 -16
- package/dist/types/src/edge-client.d.ts.map +1 -0
- package/dist/types/src/edge-client.test.d.ts +2 -0
- package/dist/types/src/edge-client.test.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +4 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/persistent-lifecycle.d.ts +42 -0
- package/dist/types/src/persistent-lifecycle.d.ts.map +1 -0
- package/dist/types/src/persistent-lifecycle.test.d.ts +2 -0
- package/dist/types/src/persistent-lifecycle.test.d.ts.map +1 -0
- package/dist/types/src/test-utils.d.ts +11 -0
- package/dist/types/src/test-utils.d.ts.map +1 -0
- package/dist/types/src/websocket.test.d.ts +2 -0
- package/dist/types/src/websocket.test.d.ts.map +1 -0
- package/package.json +13 -9
- package/src/edge-client.test.ts +50 -0
- package/src/edge-client.ts +226 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +1 -1
- package/src/persistent-lifecycle.test.ts +71 -0
- package/src/persistent-lifecycle.ts +106 -0
- package/src/protocol.test.ts +1 -1
- package/src/test-utils.ts +49 -0
- package/src/websocket.test.ts +35 -0
- package/dist/types/src/client.d.ts.map +0 -1
- package/src/client.ts +0 -179
|
@@ -1 +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":1578,"imports":[{"path":"@bufbuild/protobuf/wkt","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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/client.ts":{"bytes":
|
|
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":1578,"imports":[{"path":"@bufbuild/protobuf/wkt","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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/errors.ts":{"bytes":846,"imports":[],"format":"esm"},"packages/core/mesh/edge-client/src/persistent-lifecycle.ts":{"bytes":10868,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/edge-client/src/edge-client.ts":{"bytes":26825,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","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/errors.ts","kind":"import-statement","original":"./errors"},{"path":"packages/core/mesh/edge-client/src/persistent-lifecycle.ts","kind":"import-statement","original":"./persistent-lifecycle"},{"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":849,"imports":[{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true},{"path":"packages/core/mesh/edge-client/src/edge-client.ts","kind":"import-statement","original":"./edge-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":23321},"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/context","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":"@bufbuild/protobuf/wkt","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},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/log","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/edge-client.ts":{"bytesInOutput":7041},"packages/core/mesh/edge-client/src/defs.ts":{"bytesInOutput":285},"packages/core/mesh/edge-client/src/protocol.ts":{"bytesInOutput":2600},"packages/core/mesh/edge-client/src/errors.ts":{"bytesInOutput":116},"packages/core/mesh/edge-client/src/persistent-lifecycle.ts":{"bytesInOutput":3041}},"bytes":13670}}}
|
package/dist/lib/node/index.cjs
CHANGED
|
@@ -50,6 +50,10 @@ var import_invariant2 = require("@dxos/invariant");
|
|
|
50
50
|
var import_buf2 = require("@dxos/protocols/buf");
|
|
51
51
|
var import_messenger_pb3 = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
|
|
52
52
|
var import_util = require("@dxos/util");
|
|
53
|
+
var import_async2 = require("@dxos/async");
|
|
54
|
+
var import_context2 = require("@dxos/context");
|
|
55
|
+
var import_debug = require("@dxos/debug");
|
|
56
|
+
var import_log2 = require("@dxos/log");
|
|
53
57
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/protocol.ts";
|
|
54
58
|
var getTypename = (typeName) => `type.googleapis.com/${typeName}`;
|
|
55
59
|
var Protocol = class {
|
|
@@ -146,18 +150,122 @@ var protocol = new Protocol([
|
|
|
146
150
|
import_messenger_pb2.TextMessageSchema,
|
|
147
151
|
import_wkt.AnySchema
|
|
148
152
|
]);
|
|
149
|
-
var
|
|
150
|
-
|
|
153
|
+
var WebsocketClosedError = class extends Error {
|
|
154
|
+
constructor() {
|
|
155
|
+
super("WebSocket connection closed");
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
159
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
160
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
161
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
162
|
+
else
|
|
163
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
164
|
+
if (d = decorators[i])
|
|
165
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
166
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
167
|
+
}
|
|
168
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/persistent-lifecycle.ts";
|
|
169
|
+
var INIT_RESTART_DELAY = 100;
|
|
170
|
+
var DEFAULT_MAX_RESTART_DELAY = 5e3;
|
|
171
|
+
var PersistentLifecycle = class extends import_context2.Resource {
|
|
172
|
+
constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
|
|
173
|
+
super();
|
|
174
|
+
this._restartTask = void 0;
|
|
175
|
+
this._restartAfter = 0;
|
|
176
|
+
this._start = start;
|
|
177
|
+
this._stop = stop;
|
|
178
|
+
this._onRestart = onRestart;
|
|
179
|
+
this._maxRestartDelay = maxRestartDelay;
|
|
180
|
+
}
|
|
181
|
+
async _open() {
|
|
182
|
+
this._restartTask = new import_async2.DeferredTask(this._ctx, async () => {
|
|
183
|
+
try {
|
|
184
|
+
await this._restart();
|
|
185
|
+
} catch (err) {
|
|
186
|
+
import_log2.log.warn("Restart failed", {
|
|
187
|
+
err
|
|
188
|
+
}, {
|
|
189
|
+
F: __dxlog_file2,
|
|
190
|
+
L: 64,
|
|
191
|
+
S: this,
|
|
192
|
+
C: (f, a) => f(...a)
|
|
193
|
+
});
|
|
194
|
+
this._restartTask?.schedule();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
await this._start().catch((err) => {
|
|
198
|
+
import_log2.log.warn("Start failed", {
|
|
199
|
+
err
|
|
200
|
+
}, {
|
|
201
|
+
F: __dxlog_file2,
|
|
202
|
+
L: 69,
|
|
203
|
+
S: this,
|
|
204
|
+
C: (f, a) => f(...a)
|
|
205
|
+
});
|
|
206
|
+
this._restartTask?.schedule();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async _close() {
|
|
210
|
+
await this._restartTask?.join();
|
|
211
|
+
await this._stop();
|
|
212
|
+
this._restartTask = void 0;
|
|
213
|
+
}
|
|
214
|
+
async _restart() {
|
|
215
|
+
(0, import_log2.log)(`restarting in ${this._restartAfter}ms`, {
|
|
216
|
+
state: this._lifecycleState
|
|
217
|
+
}, {
|
|
218
|
+
F: __dxlog_file2,
|
|
219
|
+
L: 81,
|
|
220
|
+
S: this,
|
|
221
|
+
C: (f, a) => f(...a)
|
|
222
|
+
});
|
|
223
|
+
await this._stop();
|
|
224
|
+
if (this._lifecycleState !== import_context2.LifecycleState.OPEN) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
await (0, import_context2.cancelWithContext)(this._ctx, (0, import_async2.sleep)(this._restartAfter));
|
|
228
|
+
this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
|
|
229
|
+
await (0, import_debug.warnAfterTimeout)(5e3, "Connection establishment takes too long", () => this._start());
|
|
230
|
+
this._restartAfter = 0;
|
|
231
|
+
await this._onRestart?.();
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Scheduling restart should be done from outside.
|
|
235
|
+
*/
|
|
236
|
+
scheduleRestart() {
|
|
237
|
+
if (this._lifecycleState !== import_context2.LifecycleState.OPEN) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
this._restartTask.schedule();
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
_ts_decorate([
|
|
244
|
+
import_async2.synchronized
|
|
245
|
+
], PersistentLifecycle.prototype, "_open", null);
|
|
246
|
+
_ts_decorate([
|
|
247
|
+
import_async2.synchronized
|
|
248
|
+
], PersistentLifecycle.prototype, "scheduleRestart", null);
|
|
249
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
|
|
250
|
+
var DEFAULT_TIMEOUT = 1e4;
|
|
251
|
+
var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
|
|
151
252
|
var EdgeClient = class extends import_context.Resource {
|
|
152
|
-
constructor(_identityKey,
|
|
253
|
+
constructor(_identityKey, _peerKey, _config) {
|
|
153
254
|
super();
|
|
154
255
|
this._identityKey = _identityKey;
|
|
155
|
-
this.
|
|
256
|
+
this._peerKey = _peerKey;
|
|
156
257
|
this._config = _config;
|
|
258
|
+
this.reconnect = new import_async.Event();
|
|
259
|
+
this._persistentLifecycle = new PersistentLifecycle({
|
|
260
|
+
start: async () => this._openWebSocket(),
|
|
261
|
+
stop: async () => this._closeWebSocket(),
|
|
262
|
+
onRestart: async () => this.reconnect.emit()
|
|
263
|
+
});
|
|
157
264
|
this._listeners = /* @__PURE__ */ new Set();
|
|
158
|
-
this._reconnect = void 0;
|
|
159
265
|
this._ready = new import_async.Trigger();
|
|
160
266
|
this._ws = void 0;
|
|
267
|
+
this._keepaliveCtx = void 0;
|
|
268
|
+
this._heartBeatContext = void 0;
|
|
161
269
|
this._protocol = this._config.protocol ?? protocol;
|
|
162
270
|
}
|
|
163
271
|
// TODO(burdon): Attach logging.
|
|
@@ -165,29 +273,22 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
165
273
|
return {
|
|
166
274
|
open: this.isOpen,
|
|
167
275
|
identity: this._identityKey,
|
|
168
|
-
device: this.
|
|
276
|
+
device: this._peerKey
|
|
169
277
|
};
|
|
170
278
|
}
|
|
171
279
|
get identityKey() {
|
|
172
280
|
return this._identityKey;
|
|
173
281
|
}
|
|
174
|
-
get
|
|
175
|
-
return this.
|
|
282
|
+
get peerKey() {
|
|
283
|
+
return this._peerKey;
|
|
176
284
|
}
|
|
177
285
|
get isOpen() {
|
|
178
286
|
return this._lifecycleState === import_context.LifecycleState.OPEN;
|
|
179
287
|
}
|
|
180
|
-
setIdentity({
|
|
181
|
-
this.
|
|
288
|
+
setIdentity({ peerKey, identityKey }) {
|
|
289
|
+
this._peerKey = peerKey;
|
|
182
290
|
this._identityKey = identityKey;
|
|
183
|
-
this.
|
|
184
|
-
await this._openWebSocket();
|
|
185
|
-
}).catch((err) => import_log.log.catch(err, void 0, {
|
|
186
|
-
F: __dxlog_file2,
|
|
187
|
-
L: 87,
|
|
188
|
-
S: this,
|
|
189
|
-
C: (f, a) => f(...a)
|
|
190
|
-
}));
|
|
291
|
+
this._persistentLifecycle.scheduleRestart();
|
|
191
292
|
}
|
|
192
293
|
addListener(listener) {
|
|
193
294
|
this._listeners.add(listener);
|
|
@@ -197,35 +298,23 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
197
298
|
* Open connection to messaging service.
|
|
198
299
|
*/
|
|
199
300
|
async _open() {
|
|
200
|
-
|
|
201
|
-
if (this._ws) {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
(0, import_invariant.invariant)(this._deviceKey && this._identityKey, void 0, {
|
|
205
|
-
F: __dxlog_file2,
|
|
206
|
-
L: 103,
|
|
207
|
-
S: this,
|
|
208
|
-
A: [
|
|
209
|
-
"this._deviceKey && this._identityKey",
|
|
210
|
-
""
|
|
211
|
-
]
|
|
212
|
-
});
|
|
213
|
-
import_log.log.info("opening...", {
|
|
301
|
+
(0, import_log.log)("opening...", {
|
|
214
302
|
info: this.info
|
|
215
303
|
}, {
|
|
216
|
-
F:
|
|
217
|
-
L:
|
|
304
|
+
F: __dxlog_file3,
|
|
305
|
+
L: 105,
|
|
218
306
|
S: this,
|
|
219
307
|
C: (f, a) => f(...a)
|
|
220
308
|
});
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
309
|
+
this._persistentLifecycle.open().catch((err) => {
|
|
310
|
+
import_log.log.warn("Error while opening connection", {
|
|
311
|
+
err
|
|
312
|
+
}, {
|
|
313
|
+
F: __dxlog_file3,
|
|
314
|
+
L: 107,
|
|
315
|
+
S: this,
|
|
316
|
+
C: (f, a) => f(...a)
|
|
317
|
+
});
|
|
229
318
|
});
|
|
230
319
|
}
|
|
231
320
|
/**
|
|
@@ -233,135 +322,180 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
233
322
|
*/
|
|
234
323
|
async _close() {
|
|
235
324
|
(0, import_log.log)("closing...", {
|
|
236
|
-
|
|
325
|
+
peerKey: this._peerKey
|
|
237
326
|
}, {
|
|
238
|
-
F:
|
|
327
|
+
F: __dxlog_file3,
|
|
239
328
|
L: 115,
|
|
240
329
|
S: this,
|
|
241
330
|
C: (f, a) => f(...a)
|
|
242
331
|
});
|
|
243
|
-
await this.
|
|
244
|
-
await this._closeWebSocket();
|
|
245
|
-
(0, import_log.log)("closed", {
|
|
246
|
-
deviceKey: this._deviceKey
|
|
247
|
-
}, {
|
|
248
|
-
F: __dxlog_file2,
|
|
249
|
-
L: 118,
|
|
250
|
-
S: this,
|
|
251
|
-
C: (f, a) => f(...a)
|
|
252
|
-
});
|
|
332
|
+
await this._persistentLifecycle.close();
|
|
253
333
|
}
|
|
254
334
|
async _openWebSocket() {
|
|
255
|
-
const url = new URL(`/ws/${this._identityKey
|
|
335
|
+
const url = new URL(`/ws/${this._identityKey}/${this._peerKey}`, this._config.socketEndpoint);
|
|
256
336
|
this._ws = new import_isomorphic_ws.default(url);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
this
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
337
|
+
this._ws.onopen = () => {
|
|
338
|
+
(0, import_log.log)("opened", this.info, {
|
|
339
|
+
F: __dxlog_file3,
|
|
340
|
+
L: 124,
|
|
341
|
+
S: this,
|
|
342
|
+
C: (f, a) => f(...a)
|
|
343
|
+
});
|
|
344
|
+
this._ready.wake();
|
|
345
|
+
};
|
|
346
|
+
this._ws.onclose = () => {
|
|
347
|
+
(0, import_log.log)("closed", this.info, {
|
|
348
|
+
F: __dxlog_file3,
|
|
349
|
+
L: 128,
|
|
350
|
+
S: this,
|
|
351
|
+
C: (f, a) => f(...a)
|
|
352
|
+
});
|
|
353
|
+
this._persistentLifecycle.scheduleRestart();
|
|
354
|
+
};
|
|
355
|
+
this._ws.onerror = (event) => {
|
|
356
|
+
import_log.log.warn("EdgeClient socket error", {
|
|
357
|
+
error: event.error,
|
|
358
|
+
info: event.message
|
|
359
|
+
}, {
|
|
360
|
+
F: __dxlog_file3,
|
|
361
|
+
L: 132,
|
|
362
|
+
S: this,
|
|
363
|
+
C: (f, a) => f(...a)
|
|
364
|
+
});
|
|
365
|
+
this._persistentLifecycle.scheduleRestart();
|
|
366
|
+
};
|
|
367
|
+
this._ws.onmessage = async (event) => {
|
|
368
|
+
if (event.data === "__pong__") {
|
|
369
|
+
this._onHeartbeat();
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const data = await toUint8Array(event.data);
|
|
373
|
+
const message = import_buf.buf.fromBinary(import_messenger_pb.MessageSchema, data);
|
|
374
|
+
(0, import_log.log)("received", {
|
|
375
|
+
peerKey: this._peerKey,
|
|
376
|
+
payload: protocol.getPayloadType(message)
|
|
377
|
+
}, {
|
|
378
|
+
F: __dxlog_file3,
|
|
379
|
+
L: 145,
|
|
380
|
+
S: this,
|
|
381
|
+
C: (f, a) => f(...a)
|
|
382
|
+
});
|
|
383
|
+
if (message) {
|
|
384
|
+
for (const listener of this._listeners) {
|
|
385
|
+
try {
|
|
386
|
+
await listener(message);
|
|
387
|
+
} catch (err) {
|
|
388
|
+
import_log.log.error("processing", {
|
|
389
|
+
err,
|
|
390
|
+
payload: protocol.getPayloadType(message)
|
|
391
|
+
}, {
|
|
392
|
+
F: __dxlog_file3,
|
|
393
|
+
L: 151,
|
|
394
|
+
S: this,
|
|
395
|
+
C: (f, a) => f(...a)
|
|
396
|
+
});
|
|
314
397
|
}
|
|
315
398
|
}
|
|
316
399
|
}
|
|
317
|
-
}
|
|
400
|
+
};
|
|
318
401
|
await this._ready.wait({
|
|
319
402
|
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
320
403
|
});
|
|
404
|
+
this._keepaliveCtx = new import_context.Context(void 0, {
|
|
405
|
+
F: __dxlog_file3,
|
|
406
|
+
L: 158
|
|
407
|
+
});
|
|
408
|
+
(0, import_async.scheduleTaskInterval)(this._keepaliveCtx, async () => {
|
|
409
|
+
this._ws?.send("__ping__");
|
|
410
|
+
}, SIGNAL_KEEPALIVE_INTERVAL);
|
|
411
|
+
this._ws.send("__ping__");
|
|
412
|
+
this._onHeartbeat();
|
|
321
413
|
}
|
|
322
414
|
async _closeWebSocket() {
|
|
323
|
-
this.
|
|
324
|
-
|
|
325
|
-
|
|
415
|
+
if (!this._ws) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
this._ready.throw(new WebsocketClosedError());
|
|
420
|
+
this._ready.reset();
|
|
421
|
+
void this._keepaliveCtx?.dispose();
|
|
422
|
+
this._keepaliveCtx = void 0;
|
|
423
|
+
void this._heartBeatContext?.dispose();
|
|
424
|
+
this._heartBeatContext = void 0;
|
|
425
|
+
this._ws.onopen = () => {
|
|
426
|
+
};
|
|
427
|
+
this._ws.onclose = () => {
|
|
428
|
+
};
|
|
429
|
+
this._ws.onerror = () => {
|
|
430
|
+
};
|
|
431
|
+
this._ws.close();
|
|
432
|
+
this._ws = void 0;
|
|
433
|
+
} catch (err) {
|
|
434
|
+
if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
import_log.log.warn("Error closing websocket", {
|
|
438
|
+
err
|
|
439
|
+
}, {
|
|
440
|
+
F: __dxlog_file3,
|
|
441
|
+
L: 194,
|
|
442
|
+
S: this,
|
|
443
|
+
C: (f, a) => f(...a)
|
|
444
|
+
});
|
|
445
|
+
}
|
|
326
446
|
}
|
|
327
447
|
/**
|
|
328
448
|
* Send message.
|
|
329
449
|
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
330
450
|
*/
|
|
331
|
-
// TODO(burdon): Implement ACK?
|
|
332
451
|
async send(message) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
452
|
+
if (this._ready.state !== import_async.TriggerState.RESOLVED) {
|
|
453
|
+
await this._ready.wait({
|
|
454
|
+
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
455
|
+
});
|
|
456
|
+
}
|
|
336
457
|
(0, import_invariant.invariant)(this._ws, void 0, {
|
|
337
|
-
F:
|
|
338
|
-
L:
|
|
458
|
+
F: __dxlog_file3,
|
|
459
|
+
L: 206,
|
|
339
460
|
S: this,
|
|
340
461
|
A: [
|
|
341
462
|
"this._ws",
|
|
342
463
|
""
|
|
343
464
|
]
|
|
344
465
|
});
|
|
345
|
-
(0, import_invariant.invariant)(!message.source || message.source.peerKey === this.
|
|
346
|
-
F:
|
|
347
|
-
L:
|
|
466
|
+
(0, import_invariant.invariant)(!message.source || message.source.peerKey === this._peerKey, void 0, {
|
|
467
|
+
F: __dxlog_file3,
|
|
468
|
+
L: 207,
|
|
348
469
|
S: this,
|
|
349
470
|
A: [
|
|
350
|
-
"!message.source || message.source.peerKey === this.
|
|
471
|
+
"!message.source || message.source.peerKey === this._peerKey",
|
|
351
472
|
""
|
|
352
473
|
]
|
|
353
474
|
});
|
|
354
475
|
(0, import_log.log)("sending...", {
|
|
355
|
-
|
|
476
|
+
peerKey: this._peerKey,
|
|
356
477
|
payload: protocol.getPayloadType(message)
|
|
357
478
|
}, {
|
|
358
|
-
F:
|
|
359
|
-
L:
|
|
479
|
+
F: __dxlog_file3,
|
|
480
|
+
L: 208,
|
|
360
481
|
S: this,
|
|
361
482
|
C: (f, a) => f(...a)
|
|
362
483
|
});
|
|
363
484
|
this._ws.send(import_buf.buf.toBinary(import_messenger_pb.MessageSchema, message));
|
|
364
485
|
}
|
|
486
|
+
_onHeartbeat() {
|
|
487
|
+
if (this._lifecycleState !== import_context.LifecycleState.OPEN) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
void this._heartBeatContext?.dispose();
|
|
491
|
+
this._heartBeatContext = new import_context.Context(void 0, {
|
|
492
|
+
F: __dxlog_file3,
|
|
493
|
+
L: 217
|
|
494
|
+
});
|
|
495
|
+
(0, import_async.scheduleTask)(this._heartBeatContext, () => {
|
|
496
|
+
this._persistentLifecycle.scheduleRestart();
|
|
497
|
+
}, 2 * SIGNAL_KEEPALIVE_INTERVAL);
|
|
498
|
+
}
|
|
365
499
|
};
|
|
366
500
|
// Annotate the CommonJS export names for ESM import in node:
|
|
367
501
|
0 && (module.exports = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
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 { LifecycleState, Resource, type Lifecycle } from '@dxos/context';\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\nexport interface EdgeConnection extends Required<Lifecycle> {\n get info(): any;\n get identityKey(): PublicKey;\n get deviceKey(): PublicKey;\n get isOpen(): boolean;\n setIdentity(params: { deviceKey: PublicKey; identityKey: PublicKey }): void;\n addListener(listener: MessageListener): () => 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.\n// TODO(mykola): Handle reconnections.\nexport class EdgeClient extends Resource implements EdgeConnection {\n private readonly _listeners = new Set<MessageListener>();\n private _reconnect?: Promise<void> = undefined;\n private readonly _protocol: Protocol;\n private _ready = new Trigger();\n private _ws?: WebSocket = undefined;\n\n constructor(\n private _identityKey: PublicKey,\n private _deviceKey: PublicKey,\n private readonly _config: MessengerConfig,\n ) {\n super();\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._lifecycleState === LifecycleState.OPEN;\n }\n\n setIdentity({ deviceKey, identityKey }: { deviceKey: PublicKey; identityKey: PublicKey }) {\n this._deviceKey = deviceKey;\n this._identityKey = identityKey;\n this._reconnect = this._closeWebSocket()\n .then(async () => {\n await this._openWebSocket();\n })\n .catch((err) => log.catch(err));\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 protected override async _open() {\n await this._reconnect;\n if (this._ws) {\n return;\n }\n invariant(this._deviceKey && this._identityKey);\n log.info('opening...', { info: this.info });\n\n // TODO: handle reconnects\n await this._openWebSocket();\n log.info('opened', { info: this.info });\n }\n\n /**\n * Close connection and free resources.\n */\n protected override async _close() {\n log('closing...', { deviceKey: this._deviceKey });\n await this._reconnect;\n await this._closeWebSocket();\n log('closed', { deviceKey: this._deviceKey });\n }\n\n private async _openWebSocket() {\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('opened', this.info);\n this._ready.wake();\n },\n\n onclose: () => {\n log('closed', this.info);\n },\n\n onerror: (event) => {\n log.catch(event.error, this.info);\n this._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 await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n\n private async _closeWebSocket() {\n this._ready.reset();\n this._ws!.close();\n this._ws = undefined;\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 await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n invariant(this._ws);\n invariant(!message.source || message.source.peerKey === this._deviceKey.toHex());\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 { AnySchema } from '@bufbuild/protobuf/wkt';\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, AnySchema]);\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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,yBAAc;ACAd,2BAAsB;AAEtB,
|
|
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", "AnySchema", "
|
|
3
|
+
"sources": ["../../../src/index.ts", "../../../src/edge-client.ts", "../../../src/defs.ts", "../../../src/protocol.ts", "../../../src/errors.ts", "../../../src/persistent-lifecycle.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nexport * from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nexport * from './edge-client';\nexport * from './defs';\nexport * from './protocol';\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Trigger, Event, scheduleTaskInterval, scheduleTask, TriggerState } from '@dxos/async';\nimport { Context, LifecycleState, Resource, type Lifecycle } from '@dxos/context';\nimport { invariant } from '@dxos/invariant';\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 { WebsocketClosedError } from './errors';\nimport { PersistentLifecycle } from './persistent-lifecycle';\nimport { type Protocol, toUint8Array } from './protocol';\n\nconst DEFAULT_TIMEOUT = 10_000;\nconst SIGNAL_KEEPALIVE_INTERVAL = 5_000;\n\nexport type MessageListener = (message: Message) => void | Promise<void>;\n\nexport interface EdgeConnection extends Required<Lifecycle> {\n reconnect: Event;\n\n get info(): any;\n get identityKey(): string;\n get peerKey(): string;\n get isOpen(): boolean;\n setIdentity(params: { peerKey: string; identityKey: string }): void;\n addListener(listener: MessageListener): () => 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 */\nexport class EdgeClient extends Resource implements EdgeConnection {\n public reconnect = new Event();\n private readonly _persistentLifecycle = new PersistentLifecycle({\n start: async () => this._openWebSocket(),\n stop: async () => this._closeWebSocket(),\n onRestart: async () => this.reconnect.emit(),\n });\n\n private readonly _listeners = new Set<MessageListener>();\n private readonly _protocol: Protocol;\n private _ready = new Trigger();\n private _ws?: WebSocket = undefined;\n private _keepaliveCtx?: Context = undefined;\n private _heartBeatContext?: Context = undefined;\n\n constructor(\n private _identityKey: string,\n private _peerKey: string,\n private readonly _config: MessengerConfig,\n ) {\n super();\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._peerKey,\n };\n }\n\n get identityKey() {\n return this._identityKey;\n }\n\n get peerKey() {\n return this._peerKey;\n }\n\n public get isOpen() {\n return this._lifecycleState === LifecycleState.OPEN;\n }\n\n setIdentity({ peerKey, identityKey }: { peerKey: string; identityKey: string }) {\n this._peerKey = peerKey;\n this._identityKey = identityKey;\n this._persistentLifecycle.scheduleRestart();\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 protected override async _open() {\n log('opening...', { info: this.info });\n this._persistentLifecycle.open().catch((err) => {\n log.warn('Error while opening connection', { err });\n });\n }\n\n /**\n * Close connection and free resources.\n */\n protected override async _close() {\n log('closing...', { peerKey: this._peerKey });\n await this._persistentLifecycle.close();\n }\n\n private async _openWebSocket() {\n const url = new URL(`/ws/${this._identityKey}/${this._peerKey}`, this._config.socketEndpoint);\n this._ws = new WebSocket(url);\n\n this._ws.onopen = () => {\n log('opened', this.info);\n this._ready.wake();\n };\n this._ws.onclose = () => {\n log('closed', this.info);\n this._persistentLifecycle.scheduleRestart();\n };\n this._ws.onerror = (event) => {\n log.warn('EdgeClient socket error', { error: event.error, info: event.message });\n this._persistentLifecycle.scheduleRestart();\n };\n /**\n * https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data\n */\n this._ws.onmessage = async (event) => {\n if (event.data === '__pong__') {\n this._onHeartbeat();\n return;\n }\n const data = await toUint8Array(event.data);\n const message = buf.fromBinary(MessageSchema, data);\n log('received', { peerKey: this._peerKey, 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 await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n this._keepaliveCtx = new Context();\n scheduleTaskInterval(\n this._keepaliveCtx,\n async () => {\n // TODO(mykola): use RFC6455 ping/pong once implemented in the browser?\n // Cloudflare's worker responds to this `without interrupting hibernation`. https://developers.cloudflare.com/durable-objects/api/websockets/#setwebsocketautoresponse\n this._ws?.send('__ping__');\n },\n SIGNAL_KEEPALIVE_INTERVAL,\n );\n this._ws.send('__ping__');\n this._onHeartbeat();\n }\n\n private async _closeWebSocket() {\n if (!this._ws) {\n return;\n }\n try {\n this._ready.throw(new WebsocketClosedError());\n this._ready.reset();\n void this._keepaliveCtx?.dispose();\n this._keepaliveCtx = undefined;\n void this._heartBeatContext?.dispose();\n this._heartBeatContext = undefined;\n\n // NOTE: Remove event handlers to avoid scheduling restart.\n this._ws.onopen = () => {};\n this._ws.onclose = () => {};\n this._ws.onerror = () => {};\n this._ws.close();\n this._ws = undefined;\n } catch (err) {\n if (err instanceof Error && err.message.includes('WebSocket is closed before the connection is established.')) {\n return;\n }\n log.warn('Error closing websocket', { err });\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 public async send(message: Message): Promise<void> {\n if (this._ready.state !== TriggerState.RESOLVED) {\n await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n invariant(this._ws);\n invariant(!message.source || message.source.peerKey === this._peerKey);\n log('sending...', { peerKey: this._peerKey, payload: protocol.getPayloadType(message) });\n this._ws.send(buf.toBinary(MessageSchema, message));\n }\n\n private _onHeartbeat() {\n if (this._lifecycleState !== LifecycleState.OPEN) {\n return;\n }\n void this._heartBeatContext?.dispose();\n this._heartBeatContext = new Context();\n scheduleTask(\n this._heartBeatContext,\n () => {\n this._persistentLifecycle.scheduleRestart();\n },\n 2 * SIGNAL_KEEPALIVE_INTERVAL,\n );\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { AnySchema } from '@bufbuild/protobuf/wkt';\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, AnySchema]);\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", "//\n// Copyright 2024 DXOS.org\n//\n\nexport class WebsocketClosedError extends Error {\n constructor() {\n super('WebSocket connection closed');\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { DeferredTask, sleep, synchronized } from '@dxos/async';\nimport { cancelWithContext, LifecycleState, Resource } from '@dxos/context';\nimport { warnAfterTimeout } from '@dxos/debug';\nimport { log } from '@dxos/log';\n\nconst INIT_RESTART_DELAY = 100;\nconst DEFAULT_MAX_RESTART_DELAY = 5000;\n\nexport type PersistentLifecycleParams = {\n /**\n * Create connection.\n * If promise resolves successfully, connection is considered established.\n */\n start: () => Promise<void>;\n\n /**\n * Reset connection to initial state.\n */\n stop: () => Promise<void>;\n\n /**\n * Called after successful start.\n */\n onRestart?: () => Promise<void>;\n\n /**\n * Maximum delay between restartion attempts.\n * Default: 5000ms\n */\n maxRestartDelay?: number;\n};\n\n/**\n * Handles restarts (e.g. persists connection).\n * Restarts are scheduled with exponential backoff.\n */\nexport class PersistentLifecycle extends Resource {\n private readonly _start: () => Promise<void>;\n private readonly _stop: () => Promise<void>;\n private readonly _onRestart?: () => Promise<void>;\n private readonly _maxRestartDelay: number;\n\n private _restartTask?: DeferredTask = undefined;\n private _restartAfter = 0;\n\n constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }: PersistentLifecycleParams) {\n super();\n this._start = start;\n this._stop = stop;\n this._onRestart = onRestart;\n this._maxRestartDelay = maxRestartDelay;\n }\n\n @synchronized\n protected override async _open() {\n this._restartTask = new DeferredTask(this._ctx, async () => {\n try {\n await this._restart();\n } catch (err) {\n log.warn('Restart failed', { err });\n this._restartTask?.schedule();\n }\n });\n await this._start().catch((err) => {\n log.warn('Start failed', { err });\n this._restartTask?.schedule();\n });\n }\n\n protected override async _close() {\n await this._restartTask?.join();\n await this._stop();\n this._restartTask = undefined;\n }\n\n private async _restart() {\n log(`restarting in ${this._restartAfter}ms`, { state: this._lifecycleState });\n await this._stop();\n if (this._lifecycleState !== LifecycleState.OPEN) {\n return;\n }\n await cancelWithContext(this._ctx!, sleep(this._restartAfter));\n this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);\n\n // May fail if the connection is not established.\n await warnAfterTimeout(5_000, 'Connection establishment takes too long', () => this._start());\n\n this._restartAfter = 0;\n await this._onRestart?.();\n }\n\n /**\n * Scheduling restart should be done from outside.\n */\n @synchronized\n scheduleRestart() {\n if (this._lifecycleState !== LifecycleState.OPEN) {\n return;\n }\n this._restartTask!.schedule();\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,yBAAc;ACAd,2BAAsB;AAEtB,mBAAiF;AACjF,qBAAkE;AAClE,uBAA0B;AAC1B,iBAAoB;AACpB,iBAAoB;AACpB,0BAA4C;ACP5C,iBAA0B;AAE1B,IAAAA,uBAA2E;ACF3E,IAAAC,oBAA0B;AAC1B,IAAAC,cAA4B;AAC5B,IAAAF,uBAAoE;AACpE,kBAA8B;AEH9B,IAAAG,gBAAkD;AAClD,IAAAC,kBAA4D;AAC5D,mBAAiC;AACjC,IAAAC,cAAoB;;AFIb,IAAMC,cAAc,CAACC,aAAqB,uBAAuBA,QAAAA;AAKjE,IAAMC,WAAN,MAAMA;EAGXC,YAAYC,OAA0B;AACpC,SAAKC,gBAAgBC,gBAAIC,eAAc,GAAIH,KAAAA;EAC7C;EAEA,IAAII,eAA6B;AAC/B,WAAO,KAAKH;EACd;EAEAI,OAAOC,SAAuB;AAC5B,QAAI;AACF,aAAOJ,gBAAIG,OAAOE,oCAAeD,SAAS;QAAEE,UAAU,KAAKJ;MAAa,CAAA;IAC1E,SAASK,KAAK;AACZ,aAAO;QAAEC,MAAM,KAAKC,eAAeL,OAAAA;MAAS;IAC9C;EACF;;;;EAKAM,WAAyCN,SAAkBI,MAAoC;AAC7FG,qCAAUP,QAAQQ,SAAO,QAAA;;;;;;;;;AACzB,UAAMC,kBAAkB,KAAKJ,eAAeL,OAAAA;AAC5C,QAAII,QAAQA,KAAKb,aAAakB,iBAAiB;AAC7C,YAAM,IAAIC,MAAM,4BAA4BD,eAAAA,cAA6BL,KAAKb,QAAQ,EAAE;IAC1F;AAEAgB,qCAAUI,mBAAOC,MAAMZ,QAAQQ,SAASJ,IAAAA,GAAO,4BAA4BK,eAAAA,KAAkB;;;;;;;;;AAC7F,UAAMD,UAAUG,mBAAOE,UAAUb,QAAQQ,SAAS,KAAKV,YAAY;AACnES,qCAAUC,SAAS,kBAAkBC,eAAAA,KAAkB;;;;;;;;;AACvD,WAAOD;EACT;;;;EAKAH,eAAeL,SAAsC;AACnD,QAAI,CAACA,QAAQQ,SAAS;AACpB,aAAOM;IACT;AAEA,UAAM,CAAA,EAAGV,IAAAA,IAAQJ,QAAQQ,QAAQO,QAAQC,MAAM,GAAA;AAC/C,WAAOZ;EACT;;;;EAKAa,cACEb,MACA,EACEc,QACAC,QACAX,SACAY,UAAS,GAOX;AACA,WAAOxB,gBAAIyB,OAAOpB,oCAAe;MAC/BqB,YAAW,oBAAIC,KAAAA,GAAOC,YAAW;MACjCN;MACAC;MACAC;MACAZ,SAASA,UAAUG,mBAAOc,QAAQrB,MAAMR,gBAAIyB,OAAOjB,MAAMI,OAAAA,CAAAA,IAAYM;IACvE,CAAA;EACF;AACF;AAKO,IAAMY,eAAe,OAAOC,SAAAA;AAEjC,MAAIA,gBAAgBC,QAAQ;AAC1B,eAAOC,2BAAcF,IAAAA;EACvB;AAGA,MAAIA,gBAAgBG,MAAM;AACxB,WAAO,IAAIC,WAAW,MAAOJ,KAAcK,YAAW,CAAA;EACxD;AAEA,QAAM,IAAItB,MAAM,wBAAwBiB,IAAAA,EAAM;AAChD;AD/FO,IAAMM,WAAW,IAAIzC,SAAS;EAAC0C;EAAoBC;EAAqBC;EAAmBC;CAAU;AENrG,IAAMC,uBAAN,cAAmC5B,MAAAA;EACxCjB,cAAc;AACZ,UAAM,6BAAA;EACR;AACF;;;;;;;;;;;;ACCA,IAAM8C,qBAAqB;AAC3B,IAAMC,4BAA4B;AA8B3B,IAAMC,sBAAN,cAAkCC,yBAAAA;EASvCjD,YAAY,EAAEkD,OAAOC,MAAMC,WAAWC,kBAAkBN,0BAAyB,GAA+B;AAC9G,UAAK;AAJCO,SAAAA,eAA8BjC;AAC9BkC,SAAAA,gBAAgB;AAItB,SAAKC,SAASN;AACd,SAAKO,QAAQN;AACb,SAAKO,aAAaN;AAClB,SAAKO,mBAAmBN;EAC1B;EAEA,MACyBO,QAAQ;AAC/B,SAAKN,eAAe,IAAIO,2BAAa,KAAKC,MAAM,YAAA;AAC9C,UAAI;AACF,cAAM,KAAKC,SAAQ;MACrB,SAASrD,KAAK;AACZsD,wBAAIC,KAAK,kBAAkB;UAAEvD;QAAI,GAAA;;;;;;AACjC,aAAK4C,cAAcY,SAAAA;MACrB;IACF,CAAA;AACA,UAAM,KAAKV,OAAM,EAAGW,MAAM,CAACzD,QAAAA;AACzBsD,sBAAIC,KAAK,gBAAgB;QAAEvD;MAAI,GAAA;;;;;;AAC/B,WAAK4C,cAAcY,SAAAA;IACrB,CAAA;EACF;EAEA,MAAyBE,SAAS;AAChC,UAAM,KAAKd,cAAce,KAAAA;AACzB,UAAM,KAAKZ,MAAK;AAChB,SAAKH,eAAejC;EACtB;EAEA,MAAc0C,WAAW;AACvBC,yBAAI,iBAAiB,KAAKT,aAAa,MAAM;MAAEe,OAAO,KAAKC;IAAgB,GAAA;;;;;;AAC3E,UAAM,KAAKd,MAAK;AAChB,QAAI,KAAKc,oBAAoBC,+BAAeC,MAAM;AAChD;IACF;AACA,cAAMC,mCAAkB,KAAKZ,UAAOa,qBAAM,KAAKpB,aAAa,CAAA;AAC5D,SAAKA,gBAAgBqB,KAAKC,IAAID,KAAKE,IAAI,KAAKvB,gBAAgB,GAAGT,kBAAAA,GAAqB,KAAKa,gBAAgB;AAGzG,cAAMoB,+BAAiB,KAAO,2CAA2C,MAAM,KAAKvB,OAAM,CAAA;AAE1F,SAAKD,gBAAgB;AACrB,UAAM,KAAKG,aAAU;EACvB;;;;EAMAsB,kBAAkB;AAChB,QAAI,KAAKT,oBAAoBC,+BAAeC,MAAM;AAChD;IACF;AACA,SAAKnB,aAAcY,SAAQ;EAC7B;AACF;;EAhDGe;GAjBUjC,oBAAAA,WAAAA,SAAAA,IAAAA;;EA0DViC;GA1DUjC,oBAAAA,WAAAA,mBAAAA,IAAAA;;AJtBb,IAAMkC,kBAAkB;AACxB,IAAMC,4BAA4B;AAyB3B,IAAMC,aAAN,cAAyBnC,eAAAA,SAAAA;EAe9BjD,YACUqF,cACAC,UACSC,SACjB;AACA,UAAK;SAJGF,eAAAA;SACAC,WAAAA;SACSC,UAAAA;SAjBZC,YAAY,IAAIC,mBAAAA;SACNC,uBAAuB,IAAI1C,oBAAoB;MAC9DE,OAAO,YAAY,KAAKyC,eAAc;MACtCxC,MAAM,YAAY,KAAKyC,gBAAe;MACtCxC,WAAW,YAAY,KAAKoC,UAAUK,KAAI;IAC5C,CAAA;SAEiBC,aAAa,oBAAIC,IAAAA;SAE1BC,SAAS,IAAIC,qBAAAA;SACbC,MAAkB7E;SAClB8E,gBAA0B9E;SAC1B+E,oBAA8B/E;AAQpC,SAAKgF,YAAY,KAAKd,QAAQ/C,YAAYA;EAC5C;;EAGA,IAAW8D,OAAO;AAChB,WAAO;MACLC,MAAM,KAAKC;MACXC,UAAU,KAAKpB;MACfqB,QAAQ,KAAKpB;IACf;EACF;EAEA,IAAIqB,cAAc;AAChB,WAAO,KAAKtB;EACd;EAEA,IAAIuB,UAAU;AACZ,WAAO,KAAKtB;EACd;EAEA,IAAWkB,SAAS;AAClB,WAAO,KAAKjC,oBAAoBC,eAAAA,eAAeC;EACjD;EAEAoC,YAAY,EAAED,SAASD,YAAW,GAA8C;AAC9E,SAAKrB,WAAWsB;AAChB,SAAKvB,eAAesB;AACpB,SAAKjB,qBAAqBV,gBAAe;EAC3C;EAEO8B,YAAYC,UAAuC;AACxD,SAAKjB,WAAWkB,IAAID,QAAAA;AACpB,WAAO,MAAM,KAAKjB,WAAWmB,OAAOF,QAAAA;EACtC;;;;EAKA,MAAyBnD,QAAQ;AAC/BI,mBAAAA,KAAI,cAAc;MAAEsC,MAAM,KAAKA;IAAK,GAAA;;;;;;AACpC,SAAKZ,qBAAqBa,KAAI,EAAGpC,MAAM,CAACzD,QAAAA;AACtCsD,iBAAAA,IAAIC,KAAK,kCAAkC;QAAEvD;MAAI,GAAA;;;;;;IACnD,CAAA;EACF;;;;EAKA,MAAyB0D,SAAS;AAChCJ,mBAAAA,KAAI,cAAc;MAAE4C,SAAS,KAAKtB;IAAS,GAAA;;;;;;AAC3C,UAAM,KAAKI,qBAAqBwB,MAAK;EACvC;EAEA,MAAcvB,iBAAiB;AAC7B,UAAMwB,MAAM,IAAIC,IAAI,OAAO,KAAK/B,YAAY,IAAI,KAAKC,QAAQ,IAAI,KAAKC,QAAQ8B,cAAc;AAC5F,SAAKnB,MAAM,IAAIoB,qBAAAA,QAAUH,GAAAA;AAEzB,SAAKjB,IAAIqB,SAAS,MAAA;AAChBvD,qBAAAA,KAAI,UAAU,KAAKsC,MAAI;;;;;;AACvB,WAAKN,OAAOwB,KAAI;IAClB;AACA,SAAKtB,IAAIuB,UAAU,MAAA;AACjBzD,qBAAAA,KAAI,UAAU,KAAKsC,MAAI;;;;;;AACvB,WAAKZ,qBAAqBV,gBAAe;IAC3C;AACA,SAAKkB,IAAIwB,UAAU,CAACC,UAAAA;AAClB3D,iBAAAA,IAAIC,KAAK,2BAA2B;QAAE2D,OAAOD,MAAMC;QAAOtB,MAAMqB,MAAMpH;MAAQ,GAAA;;;;;;AAC9E,WAAKmF,qBAAqBV,gBAAe;IAC3C;AAIA,SAAKkB,IAAI2B,YAAY,OAAOF,UAAAA;AAC1B,UAAIA,MAAMzF,SAAS,YAAY;AAC7B,aAAK4F,aAAY;AACjB;MACF;AACA,YAAM5F,OAAO,MAAMD,aAAa0F,MAAMzF,IAAI;AAC1C,YAAM3B,UAAUJ,WAAAA,IAAI4H,WAAWvH,oBAAAA,eAAe0B,IAAAA;AAC9C8B,qBAAAA,KAAI,YAAY;QAAE4C,SAAS,KAAKtB;QAAUvE,SAASyB,SAAS5B,eAAeL,OAAAA;MAAS,GAAA;;;;;;AACpF,UAAIA,SAAS;AACX,mBAAWwG,YAAY,KAAKjB,YAAY;AACtC,cAAI;AACF,kBAAMiB,SAASxG,OAAAA;UACjB,SAASG,KAAK;AACZsD,uBAAAA,IAAI4D,MAAM,cAAc;cAAElH;cAAKK,SAASyB,SAAS5B,eAAeL,OAAAA;YAAS,GAAA;;;;;;UAC3E;QACF;MACF;IACF;AAEA,UAAM,KAAKyF,OAAOgC,KAAK;MAAEC,SAAS,KAAK1C,QAAQ0C,WAAW/C;IAAgB,CAAA;AAC1E,SAAKiB,gBAAgB,IAAI+B,uBAAAA,QAAAA;;;;AACzBC,2CACE,KAAKhC,eACL,YAAA;AAGE,WAAKD,KAAKkC,KAAK,UAAA;IACjB,GACAjD,yBAAAA;AAEF,SAAKe,IAAIkC,KAAK,UAAA;AACd,SAAKN,aAAY;EACnB;EAEA,MAAclC,kBAAkB;AAC9B,QAAI,CAAC,KAAKM,KAAK;AACb;IACF;AACA,QAAI;AACF,WAAKF,OAAOqC,MAAM,IAAIxF,qBAAAA,CAAAA;AACtB,WAAKmD,OAAOsC,MAAK;AACjB,WAAK,KAAKnC,eAAeoC,QAAAA;AACzB,WAAKpC,gBAAgB9E;AACrB,WAAK,KAAK+E,mBAAmBmC,QAAAA;AAC7B,WAAKnC,oBAAoB/E;AAGzB,WAAK6E,IAAIqB,SAAS,MAAA;MAAO;AACzB,WAAKrB,IAAIuB,UAAU,MAAA;MAAO;AAC1B,WAAKvB,IAAIwB,UAAU,MAAA;MAAO;AAC1B,WAAKxB,IAAIgB,MAAK;AACd,WAAKhB,MAAM7E;IACb,SAASX,KAAK;AACZ,UAAIA,eAAeO,SAASP,IAAIH,QAAQiI,SAAS,2DAAA,GAA8D;AAC7G;MACF;AACAxE,iBAAAA,IAAIC,KAAK,2BAA2B;QAAEvD;MAAI,GAAA;;;;;;IAC5C;EACF;;;;;EAMA,MAAa0H,KAAK7H,SAAiC;AACjD,QAAI,KAAKyF,OAAO1B,UAAUmE,0BAAaC,UAAU;AAC/C,YAAM,KAAK1C,OAAOgC,KAAK;QAAEC,SAAS,KAAK1C,QAAQ0C,WAAW/C;MAAgB,CAAA;IAC5E;AACApE,yBAAAA,WAAU,KAAKoF,KAAG,QAAA;;;;;;;;;AAClBpF,yBAAAA,WAAU,CAACP,QAAQkB,UAAUlB,QAAQkB,OAAOmF,YAAY,KAAKtB,UAAQ,QAAA;;;;;;;;;AACrEtB,mBAAAA,KAAI,cAAc;MAAE4C,SAAS,KAAKtB;MAAUvE,SAASyB,SAAS5B,eAAeL,OAAAA;IAAS,GAAA;;;;;;AACtF,SAAK2F,IAAIkC,KAAKjI,WAAAA,IAAIwI,SAASnI,oBAAAA,eAAeD,OAAAA,CAAAA;EAC5C;EAEQuH,eAAe;AACrB,QAAI,KAAKvD,oBAAoBC,eAAAA,eAAeC,MAAM;AAChD;IACF;AACA,SAAK,KAAK2B,mBAAmBmC,QAAAA;AAC7B,SAAKnC,oBAAoB,IAAI8B,uBAAAA,QAAAA;;;;AAC7BU,mCACE,KAAKxC,mBACL,MAAA;AACE,WAAKV,qBAAqBV,gBAAe;IAC3C,GACA,IAAIG,yBAAAA;EAER;AACF;",
|
|
6
|
+
"names": ["import_messenger_pb", "import_invariant", "import_buf", "import_async", "import_context", "import_log", "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", "AnySchema", "WebsocketClosedError", "INIT_RESTART_DELAY", "DEFAULT_MAX_RESTART_DELAY", "PersistentLifecycle", "Resource", "start", "stop", "onRestart", "maxRestartDelay", "_restartTask", "_restartAfter", "_start", "_stop", "_onRestart", "_maxRestartDelay", "_open", "DeferredTask", "_ctx", "_restart", "log", "warn", "schedule", "catch", "_close", "join", "state", "_lifecycleState", "LifecycleState", "OPEN", "cancelWithContext", "sleep", "Math", "min", "max", "warnAfterTimeout", "scheduleRestart", "synchronized", "DEFAULT_TIMEOUT", "SIGNAL_KEEPALIVE_INTERVAL", "EdgeClient", "_identityKey", "_peerKey", "_config", "reconnect", "Event", "_persistentLifecycle", "_openWebSocket", "_closeWebSocket", "emit", "_listeners", "Set", "_ready", "Trigger", "_ws", "_keepaliveCtx", "_heartBeatContext", "_protocol", "info", "open", "isOpen", "identity", "device", "identityKey", "peerKey", "setIdentity", "addListener", "listener", "add", "delete", "close", "url", "URL", "socketEndpoint", "WebSocket", "onopen", "wake", "onclose", "onerror", "event", "error", "onmessage", "_onHeartbeat", "fromBinary", "wait", "timeout", "Context", "scheduleTaskInterval", "send", "throw", "reset", "dispose", "includes", "TriggerState", "RESOLVED", "toBinary", "scheduleTask"]
|
|
7
7
|
}
|