@dxos/edge-client 0.6.12-main.ed7cda7 → 0.6.12-main.f9d0246
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 +16 -13
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +16 -13
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +16 -13
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/edge-client.d.ts +2 -0
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/edge-client.test.ts +23 -0
- package/src/edge-client.ts +5 -0
|
@@ -152,6 +152,9 @@ var EdgeClient = class extends Resource2 {
|
|
|
152
152
|
device: this._peerKey
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
+
get isConnected() {
|
|
156
|
+
return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;
|
|
157
|
+
}
|
|
155
158
|
get identityKey() {
|
|
156
159
|
return this._identityKey;
|
|
157
160
|
}
|
|
@@ -175,7 +178,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
175
178
|
info: this.info
|
|
176
179
|
}, {
|
|
177
180
|
F: __dxlog_file2,
|
|
178
|
-
L:
|
|
181
|
+
L: 105,
|
|
179
182
|
S: this,
|
|
180
183
|
C: (f, a) => f(...a)
|
|
181
184
|
});
|
|
@@ -184,7 +187,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
184
187
|
err
|
|
185
188
|
}, {
|
|
186
189
|
F: __dxlog_file2,
|
|
187
|
-
L:
|
|
190
|
+
L: 107,
|
|
188
191
|
S: this,
|
|
189
192
|
C: (f, a) => f(...a)
|
|
190
193
|
});
|
|
@@ -198,7 +201,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
198
201
|
peerKey: this._peerKey
|
|
199
202
|
}, {
|
|
200
203
|
F: __dxlog_file2,
|
|
201
|
-
L:
|
|
204
|
+
L: 115,
|
|
202
205
|
S: this,
|
|
203
206
|
C: (f, a) => f(...a)
|
|
204
207
|
});
|
|
@@ -210,7 +213,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
210
213
|
this._ws.onopen = () => {
|
|
211
214
|
log2("opened", this.info, {
|
|
212
215
|
F: __dxlog_file2,
|
|
213
|
-
L:
|
|
216
|
+
L: 124,
|
|
214
217
|
S: this,
|
|
215
218
|
C: (f, a) => f(...a)
|
|
216
219
|
});
|
|
@@ -220,7 +223,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
220
223
|
this._ws.onclose = () => {
|
|
221
224
|
log2("closed", this.info, {
|
|
222
225
|
F: __dxlog_file2,
|
|
223
|
-
L:
|
|
226
|
+
L: 129,
|
|
224
227
|
S: this,
|
|
225
228
|
C: (f, a) => f(...a)
|
|
226
229
|
});
|
|
@@ -232,7 +235,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
232
235
|
info: event.message
|
|
233
236
|
}, {
|
|
234
237
|
F: __dxlog_file2,
|
|
235
|
-
L:
|
|
238
|
+
L: 133,
|
|
236
239
|
S: this,
|
|
237
240
|
C: (f, a) => f(...a)
|
|
238
241
|
});
|
|
@@ -250,7 +253,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
250
253
|
payload: protocol.getPayloadType(message)
|
|
251
254
|
}, {
|
|
252
255
|
F: __dxlog_file2,
|
|
253
|
-
L:
|
|
256
|
+
L: 146,
|
|
254
257
|
S: this,
|
|
255
258
|
C: (f, a) => f(...a)
|
|
256
259
|
});
|
|
@@ -264,7 +267,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
264
267
|
payload: protocol.getPayloadType(message)
|
|
265
268
|
}, {
|
|
266
269
|
F: __dxlog_file2,
|
|
267
|
-
L:
|
|
270
|
+
L: 152,
|
|
268
271
|
S: this,
|
|
269
272
|
C: (f, a) => f(...a)
|
|
270
273
|
});
|
|
@@ -277,7 +280,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
277
280
|
});
|
|
278
281
|
this._keepaliveCtx = new Context(void 0, {
|
|
279
282
|
F: __dxlog_file2,
|
|
280
|
-
L:
|
|
283
|
+
L: 159
|
|
281
284
|
});
|
|
282
285
|
scheduleTaskInterval(this._keepaliveCtx, async () => {
|
|
283
286
|
this._ws?.send("__ping__");
|
|
@@ -312,7 +315,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
312
315
|
err
|
|
313
316
|
}, {
|
|
314
317
|
F: __dxlog_file2,
|
|
315
|
-
L:
|
|
318
|
+
L: 195,
|
|
316
319
|
S: this,
|
|
317
320
|
C: (f, a) => f(...a)
|
|
318
321
|
});
|
|
@@ -326,7 +329,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
326
329
|
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
327
330
|
log2("waiting for websocket to become ready", void 0, {
|
|
328
331
|
F: __dxlog_file2,
|
|
329
|
-
L:
|
|
332
|
+
L: 205,
|
|
330
333
|
S: this,
|
|
331
334
|
C: (f, a) => f(...a)
|
|
332
335
|
});
|
|
@@ -345,7 +348,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
345
348
|
payload: protocol.getPayloadType(message)
|
|
346
349
|
}, {
|
|
347
350
|
F: __dxlog_file2,
|
|
348
|
-
L:
|
|
351
|
+
L: 218,
|
|
349
352
|
S: this,
|
|
350
353
|
C: (f, a) => f(...a)
|
|
351
354
|
});
|
|
@@ -358,7 +361,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
358
361
|
void this._heartBeatContext?.dispose();
|
|
359
362
|
this._heartBeatContext = new Context(void 0, {
|
|
360
363
|
F: __dxlog_file2,
|
|
361
|
-
L:
|
|
364
|
+
L: 227
|
|
362
365
|
});
|
|
363
366
|
scheduleTask(this._heartBeatContext, () => {
|
|
364
367
|
this._persistentLifecycle.scheduleRestart();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index.ts", "../../../src/edge-client.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';\nexport * from './errors';\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 { 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 { EdgeConnectionClosedError, EdgeIdentityChangedError } 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 connected: Event;\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 readonly reconnect = new Event();\n public readonly connected = 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 _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 }\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 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 this.connected.emit();\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(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());\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 log('waiting for websocket to become ready');\n await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n if (!this._ws) {\n throw new EdgeConnectionClosedError();\n }\n if (\n message.source &&\n (message.source.peerKey !== this._peerKey || message.source.identityKey !== this.identityKey)\n ) {\n throw new EdgeIdentityChangedError();\n }\n\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\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\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,cAAc;;;ACAd,OAAOA,eAAe;AAEtB,SAASC,SAASC,OAAOC,sBAAsBC,cAAcC,oBAAoB;AACjF,SAASC,SAASC,kBAAAA,iBAAgBC,YAAAA,iBAAgC;AAClE,SAASC,OAAAA,YAAW;AACpB,SAASC,WAAW;AACpB,SAAuBC,qBAAqB;;;ACNrC,IAAMC,4BAAN,cAAwCC,MAAAA;EAC7CC,cAAc;AACZ,UAAM,yBAAA;EACR;AACF;AAEO,IAAMC,2BAAN,cAAuCF,MAAAA;EAC5CC,cAAc;AACZ,UAAM,wBAAA;EACR;AACF;;;ACVA,SAASE,cAAcC,OAAOC,oBAAoB;AAClD,SAASC,mBAAmBC,gBAAgBC,gBAAgB;AAC5D,SAASC,wBAAwB;AACjC,SAASC,WAAW;;;;;;;;AAEpB,IAAMC,qBAAqB;AAC3B,IAAMC,4BAA4B;AA8B3B,IAAMC,sBAAN,cAAkCL,SAAAA;EASvCM,YAAY,EAAEC,OAAOC,MAAMC,WAAWC,kBAAkBN,0BAAyB,GAA+B;AAC9G,UAAK;AAJCO,wBAA8BC;AAC9BC,yBAAgB;AAItB,SAAKC,SAASP;AACd,SAAKQ,QAAQP;AACb,SAAKQ,aAAaP;AAClB,SAAKQ,mBAAmBP;EAC1B;EAEA,MACyBQ,QAAQ;AAC/B,SAAKP,eAAe,IAAIhB,aAAa,KAAKwB,MAAM,YAAA;AAC9C,UAAI;AACF,cAAM,KAAKC,SAAQ;MACrB,SAASC,KAAK;AACZnB,YAAIoB,KAAK,kBAAkB;UAAED;QAAI,GAAA;;;;;;AACjC,aAAKV,cAAcY,SAAAA;MACrB;IACF,CAAA;AACA,UAAM,KAAKT,OAAM,EAAGU,MAAM,CAACH,QAAAA;AACzBnB,UAAIoB,KAAK,gBAAgB;QAAED;MAAI,GAAA;;;;;;AAC/B,WAAKV,cAAcY,SAAAA;IACrB,CAAA;EACF;EAEA,MAAyBE,SAAS;AAChC,UAAM,KAAKd,cAAce,KAAAA;AACzB,UAAM,KAAKX,MAAK;AAChB,SAAKJ,eAAeC;EACtB;EAEA,MAAcQ,WAAW;AACvBlB,QAAI,iBAAiB,KAAKW,aAAa,MAAM;MAAEc,OAAO,KAAKC;IAAgB,GAAA;;;;;;AAC3E,UAAM,KAAKb,MAAK;AAChB,QAAI,KAAKa,oBAAoB7B,eAAe8B,MAAM;AAChD;IACF;AACA,UAAM/B,kBAAkB,KAAKqB,MAAOvB,MAAM,KAAKiB,aAAa,CAAA;AAC5D,SAAKA,gBAAgBiB,KAAKC,IAAID,KAAKE,IAAI,KAAKnB,gBAAgB,GAAGV,kBAAAA,GAAqB,KAAKc,gBAAgB;AAGzG,UAAMhB,iBAAiB,KAAO,2CAA2C,MAAM,KAAKa,OAAM,CAAA;AAE1F,SAAKD,gBAAgB;AACrB,UAAM,KAAKG,aAAU;EACvB;;;;EAMAiB,kBAAkB;AAChB,QAAI,KAAKL,oBAAoB7B,eAAe8B,MAAM;AAChD;IACF;AACA,SAAKlB,aAAcY,SAAQ;EAC7B;AACF;;EAhDG1B;GAjBUQ,oBAAAA,WAAAA,SAAAA,IAAAA;;EA0DVR;GA1DUQ,oBAAAA,WAAAA,mBAAAA,IAAAA;;;;AFvBb,IAAM6B,kBAAkB;AACxB,IAAMC,4BAA4B;
|
|
6
|
-
"names": ["WebSocket", "Trigger", "Event", "scheduleTaskInterval", "scheduleTask", "TriggerState", "Context", "LifecycleState", "Resource", "log", "buf", "MessageSchema", "EdgeConnectionClosedError", "Error", "constructor", "EdgeIdentityChangedError", "DeferredTask", "sleep", "synchronized", "cancelWithContext", "LifecycleState", "Resource", "warnAfterTimeout", "log", "INIT_RESTART_DELAY", "DEFAULT_MAX_RESTART_DELAY", "PersistentLifecycle", "constructor", "start", "stop", "onRestart", "maxRestartDelay", "_restartTask", "undefined", "_restartAfter", "_start", "_stop", "_onRestart", "_maxRestartDelay", "_open", "_ctx", "_restart", "err", "warn", "schedule", "catch", "_close", "join", "state", "_lifecycleState", "OPEN", "Math", "min", "max", "scheduleRestart", "DEFAULT_TIMEOUT", "SIGNAL_KEEPALIVE_INTERVAL", "EdgeClient", "Resource", "constructor", "_identityKey", "_peerKey", "_config", "reconnect", "Event", "connected", "_persistentLifecycle", "PersistentLifecycle", "start", "_openWebSocket", "stop", "_closeWebSocket", "onRestart", "emit", "_listeners", "Set", "_ready", "Trigger", "_ws", "undefined", "_keepaliveCtx", "_heartBeatContext", "info", "open", "isOpen", "identity", "device", "identityKey", "peerKey", "setIdentity", "scheduleRestart", "addListener", "listener", "add", "delete", "_open", "log", "catch", "err", "warn", "_close", "close", "url", "URL", "socketEndpoint", "WebSocket", "onopen", "wake", "onclose", "onerror", "event", "error", "message", "onmessage", "data", "_onHeartbeat", "toUint8Array", "buf", "fromBinary", "MessageSchema", "payload", "protocol", "getPayloadType", "wait", "timeout", "Context", "scheduleTaskInterval", "send", "throw", "EdgeIdentityChangedError", "EdgeConnectionClosedError", "reset", "dispose", "Error", "includes", "
|
|
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';\nexport * from './errors';\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 { 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 { EdgeConnectionClosedError, EdgeIdentityChangedError } 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 connected: Event;\n reconnect: Event;\n\n get info(): any;\n get identityKey(): string;\n get peerKey(): string;\n get isOpen(): boolean;\n get isConnected(): 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 readonly reconnect = new Event();\n public readonly connected = 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 _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 }\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 isConnected() {\n return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;\n }\n\n get identityKey() {\n return this._identityKey;\n }\n\n get peerKey() {\n return this._peerKey;\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 this.connected.emit();\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(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());\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 log('waiting for websocket to become ready');\n await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n if (!this._ws) {\n throw new EdgeConnectionClosedError();\n }\n if (\n message.source &&\n (message.source.peerKey !== this._peerKey || message.source.identityKey !== this.identityKey)\n ) {\n throw new EdgeIdentityChangedError();\n }\n\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\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\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": "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",
|
|
6
|
+
"names": ["WebSocket", "Trigger", "Event", "scheduleTaskInterval", "scheduleTask", "TriggerState", "Context", "LifecycleState", "Resource", "log", "buf", "MessageSchema", "EdgeConnectionClosedError", "Error", "constructor", "EdgeIdentityChangedError", "DeferredTask", "sleep", "synchronized", "cancelWithContext", "LifecycleState", "Resource", "warnAfterTimeout", "log", "INIT_RESTART_DELAY", "DEFAULT_MAX_RESTART_DELAY", "PersistentLifecycle", "constructor", "start", "stop", "onRestart", "maxRestartDelay", "_restartTask", "undefined", "_restartAfter", "_start", "_stop", "_onRestart", "_maxRestartDelay", "_open", "_ctx", "_restart", "err", "warn", "schedule", "catch", "_close", "join", "state", "_lifecycleState", "OPEN", "Math", "min", "max", "scheduleRestart", "DEFAULT_TIMEOUT", "SIGNAL_KEEPALIVE_INTERVAL", "EdgeClient", "Resource", "constructor", "_identityKey", "_peerKey", "_config", "reconnect", "Event", "connected", "_persistentLifecycle", "PersistentLifecycle", "start", "_openWebSocket", "stop", "_closeWebSocket", "onRestart", "emit", "_listeners", "Set", "_ready", "Trigger", "_ws", "undefined", "_keepaliveCtx", "_heartBeatContext", "info", "open", "isOpen", "identity", "device", "isConnected", "Boolean", "state", "TriggerState", "RESOLVED", "identityKey", "peerKey", "setIdentity", "scheduleRestart", "addListener", "listener", "add", "delete", "_open", "log", "catch", "err", "warn", "_close", "close", "url", "URL", "socketEndpoint", "WebSocket", "onopen", "wake", "onclose", "onerror", "event", "error", "message", "onmessage", "data", "_onHeartbeat", "toUint8Array", "buf", "fromBinary", "MessageSchema", "payload", "protocol", "getPayloadType", "wait", "timeout", "Context", "scheduleTaskInterval", "send", "throw", "EdgeIdentityChangedError", "EdgeConnectionClosedError", "reset", "dispose", "Error", "includes", "source", "toBinary", "_lifecycleState", "LifecycleState", "OPEN", "scheduleTask"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10334,"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":1607,"imports":[{"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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/errors.ts":{"bytes":1265,"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":
|
|
1
|
+
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10334,"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":1607,"imports":[{"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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/errors.ts":{"bytes":1265,"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":27560,"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/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":935,"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"},{"path":"packages/core/mesh/edge-client/src/errors.ts","kind":"import-statement","original":"./errors"}],"format":"esm"},"packages/core/mesh/edge-client/src/testing/test-utils.ts":{"bytes":12455,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","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/testing/index.ts":{"bytes":516,"imports":[{"path":"packages/core/mesh/edge-client/src/testing/test-utils.ts","kind":"import-statement","original":"./test-utils"}],"format":"esm"}},"outputs":{"packages/core/mesh/edge-client/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18464},"packages/core/mesh/edge-client/dist/lib/browser/index.mjs":{"imports":[{"path":"packages/core/mesh/edge-client/dist/lib/browser/chunk-ZWJXA37R.mjs","kind":"import-statement"},{"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/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/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","EdgeConnectionClosedError","EdgeIdentityChangedError","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":7046},"packages/core/mesh/edge-client/src/errors.ts":{"bytesInOutput":232},"packages/core/mesh/edge-client/src/persistent-lifecycle.ts":{"bytesInOutput":3015}},"bytes":10887},"packages/core/mesh/edge-client/dist/lib/browser/testing/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":6341},"packages/core/mesh/edge-client/dist/lib/browser/testing/index.mjs":{"imports":[{"path":"packages/core/mesh/edge-client/dist/lib/browser/chunk-ZWJXA37R.mjs","kind":"import-statement"},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","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}],"exports":["DEFAULT_PORT","createTestEdgeWsServer"],"entryPoint":"packages/core/mesh/edge-client/src/testing/index.ts","inputs":{"packages/core/mesh/edge-client/src/testing/test-utils.ts":{"bytesInOutput":3276},"packages/core/mesh/edge-client/src/testing/index.ts":{"bytesInOutput":0}},"bytes":3493},"packages/core/mesh/edge-client/dist/lib/browser/chunk-ZWJXA37R.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":5676},"packages/core/mesh/edge-client/dist/lib/browser/chunk-ZWJXA37R.mjs":{"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},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true}],"exports":["Protocol","getTypename","protocol","toUint8Array"],"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytesInOutput":2600},"packages/core/mesh/edge-client/src/defs.ts":{"bytesInOutput":298}},"bytes":3106}}}
|
package/dist/lib/node/index.cjs
CHANGED
|
@@ -177,6 +177,9 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
177
177
|
device: this._peerKey
|
|
178
178
|
};
|
|
179
179
|
}
|
|
180
|
+
get isConnected() {
|
|
181
|
+
return Boolean(this._ws) && this._ready.state === import_async.TriggerState.RESOLVED;
|
|
182
|
+
}
|
|
180
183
|
get identityKey() {
|
|
181
184
|
return this._identityKey;
|
|
182
185
|
}
|
|
@@ -200,7 +203,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
200
203
|
info: this.info
|
|
201
204
|
}, {
|
|
202
205
|
F: __dxlog_file2,
|
|
203
|
-
L:
|
|
206
|
+
L: 105,
|
|
204
207
|
S: this,
|
|
205
208
|
C: (f, a) => f(...a)
|
|
206
209
|
});
|
|
@@ -209,7 +212,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
209
212
|
err
|
|
210
213
|
}, {
|
|
211
214
|
F: __dxlog_file2,
|
|
212
|
-
L:
|
|
215
|
+
L: 107,
|
|
213
216
|
S: this,
|
|
214
217
|
C: (f, a) => f(...a)
|
|
215
218
|
});
|
|
@@ -223,7 +226,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
223
226
|
peerKey: this._peerKey
|
|
224
227
|
}, {
|
|
225
228
|
F: __dxlog_file2,
|
|
226
|
-
L:
|
|
229
|
+
L: 115,
|
|
227
230
|
S: this,
|
|
228
231
|
C: (f, a) => f(...a)
|
|
229
232
|
});
|
|
@@ -235,7 +238,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
235
238
|
this._ws.onopen = () => {
|
|
236
239
|
(0, import_log.log)("opened", this.info, {
|
|
237
240
|
F: __dxlog_file2,
|
|
238
|
-
L:
|
|
241
|
+
L: 124,
|
|
239
242
|
S: this,
|
|
240
243
|
C: (f, a) => f(...a)
|
|
241
244
|
});
|
|
@@ -245,7 +248,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
245
248
|
this._ws.onclose = () => {
|
|
246
249
|
(0, import_log.log)("closed", this.info, {
|
|
247
250
|
F: __dxlog_file2,
|
|
248
|
-
L:
|
|
251
|
+
L: 129,
|
|
249
252
|
S: this,
|
|
250
253
|
C: (f, a) => f(...a)
|
|
251
254
|
});
|
|
@@ -257,7 +260,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
257
260
|
info: event.message
|
|
258
261
|
}, {
|
|
259
262
|
F: __dxlog_file2,
|
|
260
|
-
L:
|
|
263
|
+
L: 133,
|
|
261
264
|
S: this,
|
|
262
265
|
C: (f, a) => f(...a)
|
|
263
266
|
});
|
|
@@ -275,7 +278,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
275
278
|
payload: import_chunk_ANV2HBEH.protocol.getPayloadType(message)
|
|
276
279
|
}, {
|
|
277
280
|
F: __dxlog_file2,
|
|
278
|
-
L:
|
|
281
|
+
L: 146,
|
|
279
282
|
S: this,
|
|
280
283
|
C: (f, a) => f(...a)
|
|
281
284
|
});
|
|
@@ -289,7 +292,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
289
292
|
payload: import_chunk_ANV2HBEH.protocol.getPayloadType(message)
|
|
290
293
|
}, {
|
|
291
294
|
F: __dxlog_file2,
|
|
292
|
-
L:
|
|
295
|
+
L: 152,
|
|
293
296
|
S: this,
|
|
294
297
|
C: (f, a) => f(...a)
|
|
295
298
|
});
|
|
@@ -302,7 +305,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
302
305
|
});
|
|
303
306
|
this._keepaliveCtx = new import_context.Context(void 0, {
|
|
304
307
|
F: __dxlog_file2,
|
|
305
|
-
L:
|
|
308
|
+
L: 159
|
|
306
309
|
});
|
|
307
310
|
(0, import_async.scheduleTaskInterval)(this._keepaliveCtx, async () => {
|
|
308
311
|
this._ws?.send("__ping__");
|
|
@@ -337,7 +340,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
337
340
|
err
|
|
338
341
|
}, {
|
|
339
342
|
F: __dxlog_file2,
|
|
340
|
-
L:
|
|
343
|
+
L: 195,
|
|
341
344
|
S: this,
|
|
342
345
|
C: (f, a) => f(...a)
|
|
343
346
|
});
|
|
@@ -351,7 +354,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
351
354
|
if (this._ready.state !== import_async.TriggerState.RESOLVED) {
|
|
352
355
|
(0, import_log.log)("waiting for websocket to become ready", void 0, {
|
|
353
356
|
F: __dxlog_file2,
|
|
354
|
-
L:
|
|
357
|
+
L: 205,
|
|
355
358
|
S: this,
|
|
356
359
|
C: (f, a) => f(...a)
|
|
357
360
|
});
|
|
@@ -370,7 +373,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
370
373
|
payload: import_chunk_ANV2HBEH.protocol.getPayloadType(message)
|
|
371
374
|
}, {
|
|
372
375
|
F: __dxlog_file2,
|
|
373
|
-
L:
|
|
376
|
+
L: 218,
|
|
374
377
|
S: this,
|
|
375
378
|
C: (f, a) => f(...a)
|
|
376
379
|
});
|
|
@@ -383,7 +386,7 @@ var EdgeClient = class extends import_context.Resource {
|
|
|
383
386
|
void this._heartBeatContext?.dispose();
|
|
384
387
|
this._heartBeatContext = new import_context.Context(void 0, {
|
|
385
388
|
F: __dxlog_file2,
|
|
386
|
-
L:
|
|
389
|
+
L: 227
|
|
387
390
|
});
|
|
388
391
|
(0, import_async.scheduleTask)(this._heartBeatContext, () => {
|
|
389
392
|
this._persistentLifecycle.scheduleRestart();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index.ts", "../../../src/edge-client.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';\nexport * from './errors';\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 { 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 { EdgeConnectionClosedError, EdgeIdentityChangedError } 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 connected: Event;\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 readonly reconnect = new Event();\n public readonly connected = 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 _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 }\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 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 this.connected.emit();\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(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());\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 log('waiting for websocket to become ready');\n await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n if (!this._ws) {\n throw new EdgeConnectionClosedError();\n }\n if (\n message.source &&\n (message.source.peerKey !== this._peerKey || message.source.identityKey !== this.identityKey)\n ) {\n throw new EdgeIdentityChangedError();\n }\n\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\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\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,iBAAoB;AACpB,iBAAoB;AACpB,0BAA4C;AEN5C,IAAAA,gBAAkD;AAClD,IAAAC,kBAA4D;AAC5D,mBAAiC;AACjC,IAAAC,cAAoB;ADHb,IAAMC,4BAAN,cAAwCC,MAAAA;EAC7CC,cAAc;AACZ,UAAM,yBAAA;EACR;AACF;AAEO,IAAMC,2BAAN,cAAuCF,MAAAA;EAC5CC,cAAc;AACZ,UAAM,wBAAA;EACR;AACF;;;;;;;;ACLA,IAAME,qBAAqB;AAC3B,IAAMC,4BAA4B;AA8B3B,IAAMC,sBAAN,cAAkCC,yBAAAA;EASvCL,YAAY,EAAEM,OAAOC,MAAMC,WAAWC,kBAAkBN,0BAAyB,GAA+B;AAC9G,UAAK;AAJCO,SAAAA,eAA8BC;AAC9BC,SAAAA,gBAAgB;AAItB,SAAKC,SAASP;AACd,SAAKQ,QAAQP;AACb,SAAKQ,aAAaP;AAClB,SAAKQ,mBAAmBP;EAC1B;EAEA,MACyBQ,QAAQ;AAC/B,SAAKP,eAAe,IAAIQ,2BAAa,KAAKC,MAAM,YAAA;AAC9C,UAAI;AACF,cAAM,KAAKC,SAAQ;MACrB,SAASC,KAAK;AACZC,wBAAIC,KAAK,kBAAkB;UAAEF;QAAI,GAAA;;;;;;AACjC,aAAKX,cAAcc,SAAAA;MACrB;IACF,CAAA;AACA,UAAM,KAAKX,OAAM,EAAGY,MAAM,CAACJ,QAAAA;AACzBC,sBAAIC,KAAK,gBAAgB;QAAEF;MAAI,GAAA;;;;;;AAC/B,WAAKX,cAAcc,SAAAA;IACrB,CAAA;EACF;EAEA,MAAyBE,SAAS;AAChC,UAAM,KAAKhB,cAAciB,KAAAA;AACzB,UAAM,KAAKb,MAAK;AAChB,SAAKJ,eAAeC;EACtB;EAEA,MAAcS,WAAW;AACvBE,yBAAI,iBAAiB,KAAKV,aAAa,MAAM;MAAEgB,OAAO,KAAKC;IAAgB,GAAA;;;;;;AAC3E,UAAM,KAAKf,MAAK;AAChB,QAAI,KAAKe,oBAAoBC,+BAAeC,MAAM;AAChD;IACF;AACA,cAAMC,mCAAkB,KAAKb,UAAOc,qBAAM,KAAKrB,aAAa,CAAA;AAC5D,SAAKA,gBAAgBsB,KAAKC,IAAID,KAAKE,IAAI,KAAKxB,gBAAgB,GAAGV,kBAAAA,GAAqB,KAAKc,gBAAgB;AAGzG,cAAMqB,+BAAiB,KAAO,2CAA2C,MAAM,KAAKxB,OAAM,CAAA;AAE1F,SAAKD,gBAAgB;AACrB,UAAM,KAAKG,aAAU;EACvB;;;;EAMAuB,kBAAkB;AAChB,QAAI,KAAKT,oBAAoBC,+BAAeC,MAAM;AAChD;IACF;AACA,SAAKrB,aAAcc,SAAQ;EAC7B;AACF;;EAhDGe;GAjBUnC,oBAAAA,WAAAA,SAAAA,IAAAA;;EA0DVmC;GA1DUnC,oBAAAA,WAAAA,mBAAAA,IAAAA;;AFvBb,IAAMoC,kBAAkB;AACxB,IAAMC,4BAA4B;
|
|
6
|
-
"names": ["import_async", "import_context", "import_log", "EdgeConnectionClosedError", "Error", "constructor", "EdgeIdentityChangedError", "INIT_RESTART_DELAY", "DEFAULT_MAX_RESTART_DELAY", "PersistentLifecycle", "Resource", "start", "stop", "onRestart", "maxRestartDelay", "_restartTask", "undefined", "_restartAfter", "_start", "_stop", "_onRestart", "_maxRestartDelay", "_open", "DeferredTask", "_ctx", "_restart", "err", "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", "connected", "_persistentLifecycle", "_openWebSocket", "_closeWebSocket", "emit", "_listeners", "Set", "_ready", "Trigger", "_ws", "_keepaliveCtx", "_heartBeatContext", "info", "open", "isOpen", "identity", "device", "identityKey", "peerKey", "setIdentity", "addListener", "listener", "add", "delete", "close", "url", "URL", "socketEndpoint", "WebSocket", "onopen", "wake", "onclose", "onerror", "event", "error", "message", "onmessage", "data", "_onHeartbeat", "toUint8Array", "buf", "fromBinary", "MessageSchema", "payload", "protocol", "getPayloadType", "wait", "timeout", "Context", "scheduleTaskInterval", "send", "throw", "reset", "dispose", "includes", "
|
|
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';\nexport * from './errors';\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 { 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 { EdgeConnectionClosedError, EdgeIdentityChangedError } 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 connected: Event;\n reconnect: Event;\n\n get info(): any;\n get identityKey(): string;\n get peerKey(): string;\n get isOpen(): boolean;\n get isConnected(): 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 readonly reconnect = new Event();\n public readonly connected = 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 _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 }\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 isConnected() {\n return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;\n }\n\n get identityKey() {\n return this._identityKey;\n }\n\n get peerKey() {\n return this._peerKey;\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 this.connected.emit();\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(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());\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 log('waiting for websocket to become ready');\n await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n if (!this._ws) {\n throw new EdgeConnectionClosedError();\n }\n if (\n message.source &&\n (message.source.peerKey !== this._peerKey || message.source.identityKey !== this.identityKey)\n ) {\n throw new EdgeIdentityChangedError();\n }\n\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\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\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": "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",
|
|
6
|
+
"names": ["import_async", "import_context", "import_log", "EdgeConnectionClosedError", "Error", "constructor", "EdgeIdentityChangedError", "INIT_RESTART_DELAY", "DEFAULT_MAX_RESTART_DELAY", "PersistentLifecycle", "Resource", "start", "stop", "onRestart", "maxRestartDelay", "_restartTask", "undefined", "_restartAfter", "_start", "_stop", "_onRestart", "_maxRestartDelay", "_open", "DeferredTask", "_ctx", "_restart", "err", "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", "connected", "_persistentLifecycle", "_openWebSocket", "_closeWebSocket", "emit", "_listeners", "Set", "_ready", "Trigger", "_ws", "_keepaliveCtx", "_heartBeatContext", "info", "open", "isOpen", "identity", "device", "isConnected", "Boolean", "TriggerState", "RESOLVED", "identityKey", "peerKey", "setIdentity", "addListener", "listener", "add", "delete", "close", "url", "URL", "socketEndpoint", "WebSocket", "onopen", "wake", "onclose", "onerror", "event", "error", "message", "onmessage", "data", "_onHeartbeat", "toUint8Array", "buf", "fromBinary", "MessageSchema", "payload", "protocol", "getPayloadType", "wait", "timeout", "Context", "scheduleTaskInterval", "send", "throw", "reset", "dispose", "includes", "source", "toBinary", "scheduleTask"]
|
|
7
7
|
}
|
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10334,"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":1607,"imports":[{"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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/errors.ts":{"bytes":1265,"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":
|
|
1
|
+
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10334,"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":1607,"imports":[{"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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/errors.ts":{"bytes":1265,"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":27560,"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/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":935,"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"},{"path":"packages/core/mesh/edge-client/src/errors.ts","kind":"import-statement","original":"./errors"}],"format":"esm"},"packages/core/mesh/edge-client/src/testing/test-utils.ts":{"bytes":12455,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","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/testing/index.ts":{"bytes":516,"imports":[{"path":"packages/core/mesh/edge-client/src/testing/test-utils.ts","kind":"import-statement","original":"./test-utils"}],"format":"esm"}},"outputs":{"packages/core/mesh/edge-client/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18464},"packages/core/mesh/edge-client/dist/lib/node/index.cjs":{"imports":[{"path":"packages/core/mesh/edge-client/dist/lib/node/chunk-ANV2HBEH.cjs","kind":"import-statement"},{"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/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/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","EdgeConnectionClosedError","EdgeIdentityChangedError","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":7046},"packages/core/mesh/edge-client/src/errors.ts":{"bytesInOutput":232},"packages/core/mesh/edge-client/src/persistent-lifecycle.ts":{"bytesInOutput":3015}},"bytes":10887},"packages/core/mesh/edge-client/dist/lib/node/testing/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":6341},"packages/core/mesh/edge-client/dist/lib/node/testing/index.cjs":{"imports":[{"path":"packages/core/mesh/edge-client/dist/lib/node/chunk-ANV2HBEH.cjs","kind":"import-statement"},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","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}],"exports":["DEFAULT_PORT","createTestEdgeWsServer"],"entryPoint":"packages/core/mesh/edge-client/src/testing/index.ts","inputs":{"packages/core/mesh/edge-client/src/testing/test-utils.ts":{"bytesInOutput":3276},"packages/core/mesh/edge-client/src/testing/index.ts":{"bytesInOutput":0}},"bytes":3493},"packages/core/mesh/edge-client/dist/lib/node/chunk-ANV2HBEH.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":5676},"packages/core/mesh/edge-client/dist/lib/node/chunk-ANV2HBEH.cjs":{"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},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true}],"exports":["Protocol","getTypename","protocol","toUint8Array"],"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytesInOutput":2600},"packages/core/mesh/edge-client/src/defs.ts":{"bytesInOutput":298}},"bytes":3106}}}
|
|
@@ -153,6 +153,9 @@ var EdgeClient = class extends Resource2 {
|
|
|
153
153
|
device: this._peerKey
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
|
+
get isConnected() {
|
|
157
|
+
return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;
|
|
158
|
+
}
|
|
156
159
|
get identityKey() {
|
|
157
160
|
return this._identityKey;
|
|
158
161
|
}
|
|
@@ -176,7 +179,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
176
179
|
info: this.info
|
|
177
180
|
}, {
|
|
178
181
|
F: __dxlog_file2,
|
|
179
|
-
L:
|
|
182
|
+
L: 105,
|
|
180
183
|
S: this,
|
|
181
184
|
C: (f, a) => f(...a)
|
|
182
185
|
});
|
|
@@ -185,7 +188,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
185
188
|
err
|
|
186
189
|
}, {
|
|
187
190
|
F: __dxlog_file2,
|
|
188
|
-
L:
|
|
191
|
+
L: 107,
|
|
189
192
|
S: this,
|
|
190
193
|
C: (f, a) => f(...a)
|
|
191
194
|
});
|
|
@@ -199,7 +202,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
199
202
|
peerKey: this._peerKey
|
|
200
203
|
}, {
|
|
201
204
|
F: __dxlog_file2,
|
|
202
|
-
L:
|
|
205
|
+
L: 115,
|
|
203
206
|
S: this,
|
|
204
207
|
C: (f, a) => f(...a)
|
|
205
208
|
});
|
|
@@ -211,7 +214,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
211
214
|
this._ws.onopen = () => {
|
|
212
215
|
log2("opened", this.info, {
|
|
213
216
|
F: __dxlog_file2,
|
|
214
|
-
L:
|
|
217
|
+
L: 124,
|
|
215
218
|
S: this,
|
|
216
219
|
C: (f, a) => f(...a)
|
|
217
220
|
});
|
|
@@ -221,7 +224,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
221
224
|
this._ws.onclose = () => {
|
|
222
225
|
log2("closed", this.info, {
|
|
223
226
|
F: __dxlog_file2,
|
|
224
|
-
L:
|
|
227
|
+
L: 129,
|
|
225
228
|
S: this,
|
|
226
229
|
C: (f, a) => f(...a)
|
|
227
230
|
});
|
|
@@ -233,7 +236,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
233
236
|
info: event.message
|
|
234
237
|
}, {
|
|
235
238
|
F: __dxlog_file2,
|
|
236
|
-
L:
|
|
239
|
+
L: 133,
|
|
237
240
|
S: this,
|
|
238
241
|
C: (f, a) => f(...a)
|
|
239
242
|
});
|
|
@@ -251,7 +254,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
251
254
|
payload: protocol.getPayloadType(message)
|
|
252
255
|
}, {
|
|
253
256
|
F: __dxlog_file2,
|
|
254
|
-
L:
|
|
257
|
+
L: 146,
|
|
255
258
|
S: this,
|
|
256
259
|
C: (f, a) => f(...a)
|
|
257
260
|
});
|
|
@@ -265,7 +268,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
265
268
|
payload: protocol.getPayloadType(message)
|
|
266
269
|
}, {
|
|
267
270
|
F: __dxlog_file2,
|
|
268
|
-
L:
|
|
271
|
+
L: 152,
|
|
269
272
|
S: this,
|
|
270
273
|
C: (f, a) => f(...a)
|
|
271
274
|
});
|
|
@@ -278,7 +281,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
278
281
|
});
|
|
279
282
|
this._keepaliveCtx = new Context(void 0, {
|
|
280
283
|
F: __dxlog_file2,
|
|
281
|
-
L:
|
|
284
|
+
L: 159
|
|
282
285
|
});
|
|
283
286
|
scheduleTaskInterval(this._keepaliveCtx, async () => {
|
|
284
287
|
this._ws?.send("__ping__");
|
|
@@ -313,7 +316,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
313
316
|
err
|
|
314
317
|
}, {
|
|
315
318
|
F: __dxlog_file2,
|
|
316
|
-
L:
|
|
319
|
+
L: 195,
|
|
317
320
|
S: this,
|
|
318
321
|
C: (f, a) => f(...a)
|
|
319
322
|
});
|
|
@@ -327,7 +330,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
327
330
|
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
328
331
|
log2("waiting for websocket to become ready", void 0, {
|
|
329
332
|
F: __dxlog_file2,
|
|
330
|
-
L:
|
|
333
|
+
L: 205,
|
|
331
334
|
S: this,
|
|
332
335
|
C: (f, a) => f(...a)
|
|
333
336
|
});
|
|
@@ -346,7 +349,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
346
349
|
payload: protocol.getPayloadType(message)
|
|
347
350
|
}, {
|
|
348
351
|
F: __dxlog_file2,
|
|
349
|
-
L:
|
|
352
|
+
L: 218,
|
|
350
353
|
S: this,
|
|
351
354
|
C: (f, a) => f(...a)
|
|
352
355
|
});
|
|
@@ -359,7 +362,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
359
362
|
void this._heartBeatContext?.dispose();
|
|
360
363
|
this._heartBeatContext = new Context(void 0, {
|
|
361
364
|
F: __dxlog_file2,
|
|
362
|
-
L:
|
|
365
|
+
L: 227
|
|
363
366
|
});
|
|
364
367
|
scheduleTask(this._heartBeatContext, () => {
|
|
365
368
|
this._persistentLifecycle.scheduleRestart();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index.ts", "../../../src/edge-client.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';\nexport * from './errors';\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 { 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 { EdgeConnectionClosedError, EdgeIdentityChangedError } 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 connected: Event;\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 readonly reconnect = new Event();\n public readonly connected = 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 _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 }\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 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 this.connected.emit();\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(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());\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 log('waiting for websocket to become ready');\n await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n if (!this._ws) {\n throw new EdgeConnectionClosedError();\n }\n if (\n message.source &&\n (message.source.peerKey !== this._peerKey || message.source.identityKey !== this.identityKey)\n ) {\n throw new EdgeIdentityChangedError();\n }\n\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\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\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,cAAc;;;ACAd,OAAOA,eAAe;AAEtB,SAASC,SAASC,OAAOC,sBAAsBC,cAAcC,oBAAoB;AACjF,SAASC,SAASC,kBAAAA,iBAAgBC,YAAAA,iBAAgC;AAClE,SAASC,OAAAA,YAAW;AACpB,SAASC,WAAW;AACpB,SAAuBC,qBAAqB;;;ACNrC,IAAMC,4BAAN,cAAwCC,MAAAA;EAC7CC,cAAc;AACZ,UAAM,yBAAA;EACR;AACF;AAEO,IAAMC,2BAAN,cAAuCF,MAAAA;EAC5CC,cAAc;AACZ,UAAM,wBAAA;EACR;AACF;;;ACVA,SAASE,cAAcC,OAAOC,oBAAoB;AAClD,SAASC,mBAAmBC,gBAAgBC,gBAAgB;AAC5D,SAASC,wBAAwB;AACjC,SAASC,WAAW;;;;;;;;AAEpB,IAAMC,qBAAqB;AAC3B,IAAMC,4BAA4B;AA8B3B,IAAMC,sBAAN,cAAkCL,SAAAA;EASvCM,YAAY,EAAEC,OAAOC,MAAMC,WAAWC,kBAAkBN,0BAAyB,GAA+B;AAC9G,UAAK;AAJCO,wBAA8BC;AAC9BC,yBAAgB;AAItB,SAAKC,SAASP;AACd,SAAKQ,QAAQP;AACb,SAAKQ,aAAaP;AAClB,SAAKQ,mBAAmBP;EAC1B;EAEA,MACyBQ,QAAQ;AAC/B,SAAKP,eAAe,IAAIhB,aAAa,KAAKwB,MAAM,YAAA;AAC9C,UAAI;AACF,cAAM,KAAKC,SAAQ;MACrB,SAASC,KAAK;AACZnB,YAAIoB,KAAK,kBAAkB;UAAED;QAAI,GAAA;;;;;;AACjC,aAAKV,cAAcY,SAAAA;MACrB;IACF,CAAA;AACA,UAAM,KAAKT,OAAM,EAAGU,MAAM,CAACH,QAAAA;AACzBnB,UAAIoB,KAAK,gBAAgB;QAAED;MAAI,GAAA;;;;;;AAC/B,WAAKV,cAAcY,SAAAA;IACrB,CAAA;EACF;EAEA,MAAyBE,SAAS;AAChC,UAAM,KAAKd,cAAce,KAAAA;AACzB,UAAM,KAAKX,MAAK;AAChB,SAAKJ,eAAeC;EACtB;EAEA,MAAcQ,WAAW;AACvBlB,QAAI,iBAAiB,KAAKW,aAAa,MAAM;MAAEc,OAAO,KAAKC;IAAgB,GAAA;;;;;;AAC3E,UAAM,KAAKb,MAAK;AAChB,QAAI,KAAKa,oBAAoB7B,eAAe8B,MAAM;AAChD;IACF;AACA,UAAM/B,kBAAkB,KAAKqB,MAAOvB,MAAM,KAAKiB,aAAa,CAAA;AAC5D,SAAKA,gBAAgBiB,KAAKC,IAAID,KAAKE,IAAI,KAAKnB,gBAAgB,GAAGV,kBAAAA,GAAqB,KAAKc,gBAAgB;AAGzG,UAAMhB,iBAAiB,KAAO,2CAA2C,MAAM,KAAKa,OAAM,CAAA;AAE1F,SAAKD,gBAAgB;AACrB,UAAM,KAAKG,aAAU;EACvB;;;;EAMAiB,kBAAkB;AAChB,QAAI,KAAKL,oBAAoB7B,eAAe8B,MAAM;AAChD;IACF;AACA,SAAKlB,aAAcY,SAAQ;EAC7B;AACF;;EAhDG1B;GAjBUQ,oBAAAA,WAAAA,SAAAA,IAAAA;;EA0DVR;GA1DUQ,oBAAAA,WAAAA,mBAAAA,IAAAA;;;;AFvBb,IAAM6B,kBAAkB;AACxB,IAAMC,4BAA4B;
|
|
6
|
-
"names": ["WebSocket", "Trigger", "Event", "scheduleTaskInterval", "scheduleTask", "TriggerState", "Context", "LifecycleState", "Resource", "log", "buf", "MessageSchema", "EdgeConnectionClosedError", "Error", "constructor", "EdgeIdentityChangedError", "DeferredTask", "sleep", "synchronized", "cancelWithContext", "LifecycleState", "Resource", "warnAfterTimeout", "log", "INIT_RESTART_DELAY", "DEFAULT_MAX_RESTART_DELAY", "PersistentLifecycle", "constructor", "start", "stop", "onRestart", "maxRestartDelay", "_restartTask", "undefined", "_restartAfter", "_start", "_stop", "_onRestart", "_maxRestartDelay", "_open", "_ctx", "_restart", "err", "warn", "schedule", "catch", "_close", "join", "state", "_lifecycleState", "OPEN", "Math", "min", "max", "scheduleRestart", "DEFAULT_TIMEOUT", "SIGNAL_KEEPALIVE_INTERVAL", "EdgeClient", "Resource", "constructor", "_identityKey", "_peerKey", "_config", "reconnect", "Event", "connected", "_persistentLifecycle", "PersistentLifecycle", "start", "_openWebSocket", "stop", "_closeWebSocket", "onRestart", "emit", "_listeners", "Set", "_ready", "Trigger", "_ws", "undefined", "_keepaliveCtx", "_heartBeatContext", "info", "open", "isOpen", "identity", "device", "identityKey", "peerKey", "setIdentity", "scheduleRestart", "addListener", "listener", "add", "delete", "_open", "log", "catch", "err", "warn", "_close", "close", "url", "URL", "socketEndpoint", "WebSocket", "onopen", "wake", "onclose", "onerror", "event", "error", "message", "onmessage", "data", "_onHeartbeat", "toUint8Array", "buf", "fromBinary", "MessageSchema", "payload", "protocol", "getPayloadType", "wait", "timeout", "Context", "scheduleTaskInterval", "send", "throw", "EdgeIdentityChangedError", "EdgeConnectionClosedError", "reset", "dispose", "Error", "includes", "
|
|
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';\nexport * from './errors';\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 { 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 { EdgeConnectionClosedError, EdgeIdentityChangedError } 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 connected: Event;\n reconnect: Event;\n\n get info(): any;\n get identityKey(): string;\n get peerKey(): string;\n get isOpen(): boolean;\n get isConnected(): 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 readonly reconnect = new Event();\n public readonly connected = 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 _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 }\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 isConnected() {\n return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;\n }\n\n get identityKey() {\n return this._identityKey;\n }\n\n get peerKey() {\n return this._peerKey;\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 this.connected.emit();\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(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());\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 log('waiting for websocket to become ready');\n await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });\n }\n if (!this._ws) {\n throw new EdgeConnectionClosedError();\n }\n if (\n message.source &&\n (message.source.peerKey !== this._peerKey || message.source.identityKey !== this.identityKey)\n ) {\n throw new EdgeIdentityChangedError();\n }\n\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\nexport class EdgeConnectionClosedError extends Error {\n constructor() {\n super('Edge connection closed.');\n }\n}\n\nexport class EdgeIdentityChangedError extends Error {\n constructor() {\n super('Edge identity changed.');\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": "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",
|
|
6
|
+
"names": ["WebSocket", "Trigger", "Event", "scheduleTaskInterval", "scheduleTask", "TriggerState", "Context", "LifecycleState", "Resource", "log", "buf", "MessageSchema", "EdgeConnectionClosedError", "Error", "constructor", "EdgeIdentityChangedError", "DeferredTask", "sleep", "synchronized", "cancelWithContext", "LifecycleState", "Resource", "warnAfterTimeout", "log", "INIT_RESTART_DELAY", "DEFAULT_MAX_RESTART_DELAY", "PersistentLifecycle", "constructor", "start", "stop", "onRestart", "maxRestartDelay", "_restartTask", "undefined", "_restartAfter", "_start", "_stop", "_onRestart", "_maxRestartDelay", "_open", "_ctx", "_restart", "err", "warn", "schedule", "catch", "_close", "join", "state", "_lifecycleState", "OPEN", "Math", "min", "max", "scheduleRestart", "DEFAULT_TIMEOUT", "SIGNAL_KEEPALIVE_INTERVAL", "EdgeClient", "Resource", "constructor", "_identityKey", "_peerKey", "_config", "reconnect", "Event", "connected", "_persistentLifecycle", "PersistentLifecycle", "start", "_openWebSocket", "stop", "_closeWebSocket", "onRestart", "emit", "_listeners", "Set", "_ready", "Trigger", "_ws", "undefined", "_keepaliveCtx", "_heartBeatContext", "info", "open", "isOpen", "identity", "device", "isConnected", "Boolean", "state", "TriggerState", "RESOLVED", "identityKey", "peerKey", "setIdentity", "scheduleRestart", "addListener", "listener", "add", "delete", "_open", "log", "catch", "err", "warn", "_close", "close", "url", "URL", "socketEndpoint", "WebSocket", "onopen", "wake", "onclose", "onerror", "event", "error", "message", "onmessage", "data", "_onHeartbeat", "toUint8Array", "buf", "fromBinary", "MessageSchema", "payload", "protocol", "getPayloadType", "wait", "timeout", "Context", "scheduleTaskInterval", "send", "throw", "EdgeIdentityChangedError", "EdgeConnectionClosedError", "reset", "dispose", "Error", "includes", "source", "toBinary", "_lifecycleState", "LifecycleState", "OPEN", "scheduleTask"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10334,"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":1607,"imports":[{"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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/errors.ts":{"bytes":1265,"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":
|
|
1
|
+
{"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytes":10334,"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":1607,"imports":[{"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/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"packages/core/mesh/edge-client/src/errors.ts":{"bytes":1265,"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":27560,"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/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":935,"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"},{"path":"packages/core/mesh/edge-client/src/errors.ts","kind":"import-statement","original":"./errors"}],"format":"esm"},"packages/core/mesh/edge-client/src/testing/test-utils.ts":{"bytes":12455,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","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/testing/index.ts":{"bytes":516,"imports":[{"path":"packages/core/mesh/edge-client/src/testing/test-utils.ts","kind":"import-statement","original":"./test-utils"}],"format":"esm"}},"outputs":{"packages/core/mesh/edge-client/dist/lib/node-esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18465},"packages/core/mesh/edge-client/dist/lib/node-esm/index.mjs":{"imports":[{"path":"packages/core/mesh/edge-client/dist/lib/node-esm/chunk-HNVT57AU.mjs","kind":"import-statement"},{"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/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/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","EdgeConnectionClosedError","EdgeIdentityChangedError","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":7046},"packages/core/mesh/edge-client/src/errors.ts":{"bytesInOutput":232},"packages/core/mesh/edge-client/src/persistent-lifecycle.ts":{"bytesInOutput":3015}},"bytes":10979},"packages/core/mesh/edge-client/dist/lib/node-esm/testing/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":6342},"packages/core/mesh/edge-client/dist/lib/node-esm/testing/index.mjs":{"imports":[{"path":"packages/core/mesh/edge-client/dist/lib/node-esm/chunk-HNVT57AU.mjs","kind":"import-statement"},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","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}],"exports":["DEFAULT_PORT","createTestEdgeWsServer"],"entryPoint":"packages/core/mesh/edge-client/src/testing/index.ts","inputs":{"packages/core/mesh/edge-client/src/testing/test-utils.ts":{"bytesInOutput":3276},"packages/core/mesh/edge-client/src/testing/index.ts":{"bytesInOutput":0}},"bytes":3585},"packages/core/mesh/edge-client/dist/lib/node-esm/chunk-HNVT57AU.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":5678},"packages/core/mesh/edge-client/dist/lib/node-esm/chunk-HNVT57AU.mjs":{"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},{"path":"@dxos/protocols/buf","kind":"import-statement","external":true},{"path":"@dxos/protocols/buf/dxos/edge/messenger_pb","kind":"import-statement","external":true}],"exports":["Protocol","getTypename","protocol","toUint8Array"],"inputs":{"packages/core/mesh/edge-client/src/protocol.ts":{"bytesInOutput":2600},"packages/core/mesh/edge-client/src/defs.ts":{"bytesInOutput":298}},"bytes":3199}}}
|
|
@@ -10,6 +10,7 @@ export interface EdgeConnection extends Required<Lifecycle> {
|
|
|
10
10
|
get identityKey(): string;
|
|
11
11
|
get peerKey(): string;
|
|
12
12
|
get isOpen(): boolean;
|
|
13
|
+
get isConnected(): boolean;
|
|
13
14
|
setIdentity(params: {
|
|
14
15
|
peerKey: string;
|
|
15
16
|
identityKey: string;
|
|
@@ -43,6 +44,7 @@ export declare class EdgeClient extends Resource implements EdgeConnection {
|
|
|
43
44
|
identity: string;
|
|
44
45
|
device: string;
|
|
45
46
|
};
|
|
47
|
+
get isConnected(): boolean;
|
|
46
48
|
get identityKey(): string;
|
|
47
49
|
get peerKey(): string;
|
|
48
50
|
setIdentity({ peerKey, identityKey }: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-client.d.ts","sourceRoot":"","sources":["../../../src/edge-client.ts"],"names":[],"mappings":"AAMA,OAAO,EAAW,KAAK,EAAoD,MAAM,aAAa,CAAC;AAC/F,OAAO,EAA2B,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlF,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,4CAA4C,CAAC;AAKzF,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAKzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAAC,SAAS,CAAC;IACzD,SAAS,EAAE,KAAK,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;IAEjB,IAAI,IAAI,IAAI,GAAG,CAAC;IAChB,IAAI,WAAW,IAAI,MAAM,CAAC;IAC1B,IAAI,OAAO,IAAI,MAAM,CAAC;IACtB,IAAI,MAAM,IAAI,OAAO,CAAC;IACtB,WAAW,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI,CAAC;IACnD,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;AACH,qBAAa,UAAW,SAAQ,QAAS,YAAW,cAAc;IAgB9D,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAjB1B,SAAgB,SAAS,cAAe;IACxC,SAAgB,SAAS,cAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAIlC;IAEH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8B;IACzD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,GAAG,CAAC,CAAwB;IACpC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;gBAGtC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EACP,OAAO,EAAE,eAAe;IAM3C,IAAW,IAAI;;;;MAMd;IAED,IAAI,WAAW,WAEd;IAED,IAAI,OAAO,WAEV;IAED,WAAW,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;IAMvE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IAKzD;;OAEG;cACsB,KAAK;IAO9B;;OAEG;cACsB,MAAM;YAKjB,cAAc;YAsDd,eAAe;IA0B7B;;;OAGG;IACU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,OAAO,CAAC,YAAY;CAcrB"}
|
|
1
|
+
{"version":3,"file":"edge-client.d.ts","sourceRoot":"","sources":["../../../src/edge-client.ts"],"names":[],"mappings":"AAMA,OAAO,EAAW,KAAK,EAAoD,MAAM,aAAa,CAAC;AAC/F,OAAO,EAA2B,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlF,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,4CAA4C,CAAC;AAKzF,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAKzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAAC,SAAS,CAAC;IACzD,SAAS,EAAE,KAAK,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;IAEjB,IAAI,IAAI,IAAI,GAAG,CAAC;IAChB,IAAI,WAAW,IAAI,MAAM,CAAC;IAC1B,IAAI,OAAO,IAAI,MAAM,CAAC;IACtB,IAAI,MAAM,IAAI,OAAO,CAAC;IACtB,IAAI,WAAW,IAAI,OAAO,CAAC;IAC3B,WAAW,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI,CAAC;IACnD,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;AACH,qBAAa,UAAW,SAAQ,QAAS,YAAW,cAAc;IAgB9D,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAjB1B,SAAgB,SAAS,cAAe;IACxC,SAAgB,SAAS,cAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAIlC;IAEH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8B;IACzD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,GAAG,CAAC,CAAwB;IACpC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;gBAGtC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EACP,OAAO,EAAE,eAAe;IAM3C,IAAW,IAAI;;;;MAMd;IAED,IAAI,WAAW,YAEd;IAED,IAAI,WAAW,WAEd;IAED,IAAI,OAAO,WAEV;IAED,WAAW,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;IAMvE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IAKzD;;OAEG;cACsB,KAAK;IAO9B;;OAEG;cACsB,MAAM;YAKjB,cAAc;YAsDd,eAAe;IA0B7B;;;OAGG;IACU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,OAAO,CAAC,YAAY;CAcrB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/edge-client",
|
|
3
|
-
"version": "0.6.12-main.
|
|
3
|
+
"version": "0.6.12-main.f9d0246",
|
|
4
4
|
"description": "EDGE Client",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -40,18 +40,18 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"isomorphic-ws": "^5.0.0",
|
|
42
42
|
"ws": "^8.14.2",
|
|
43
|
-
"@dxos/async": "0.6.12-main.
|
|
44
|
-
"@dxos/context": "0.6.12-main.
|
|
45
|
-
"@dxos/debug": "0.6.12-main.
|
|
46
|
-
"@dxos/invariant": "0.6.12-main.
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/protocols": "0.6.12-main.
|
|
50
|
-
"@dxos/util": "0.6.12-main.
|
|
43
|
+
"@dxos/async": "0.6.12-main.f9d0246",
|
|
44
|
+
"@dxos/context": "0.6.12-main.f9d0246",
|
|
45
|
+
"@dxos/debug": "0.6.12-main.f9d0246",
|
|
46
|
+
"@dxos/invariant": "0.6.12-main.f9d0246",
|
|
47
|
+
"@dxos/node-std": "0.6.12-main.f9d0246",
|
|
48
|
+
"@dxos/log": "0.6.12-main.f9d0246",
|
|
49
|
+
"@dxos/protocols": "0.6.12-main.f9d0246",
|
|
50
|
+
"@dxos/util": "0.6.12-main.f9d0246"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
53
|
+
"@dxos/test-utils": "0.6.12-main.f9d0246",
|
|
54
|
+
"@dxos/keys": "0.6.12-main.f9d0246"
|
|
55
55
|
},
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|
package/src/edge-client.test.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { test, expect, describe, onTestFinished } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { Trigger } from '@dxos/async';
|
|
7
8
|
import { PublicKey } from '@dxos/keys';
|
|
8
9
|
import { TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
9
10
|
import { openAndClose } from '@dxos/test-utils';
|
|
@@ -31,6 +32,28 @@ describe('EdgeClient', () => {
|
|
|
31
32
|
await expect(client.send(textMessage('Hello world 2'))).resolves.not.toThrow();
|
|
32
33
|
});
|
|
33
34
|
|
|
35
|
+
test('isConnected', async () => {
|
|
36
|
+
const admitConnection = new Trigger();
|
|
37
|
+
const { closeConnection, endpoint, cleanup } = await createTestEdgeWsServer(8001, { admitConnection });
|
|
38
|
+
onTestFinished(cleanup);
|
|
39
|
+
|
|
40
|
+
const id = PublicKey.random().toHex();
|
|
41
|
+
const client = new EdgeClient(id, id, { socketEndpoint: endpoint });
|
|
42
|
+
await openAndClose(client);
|
|
43
|
+
|
|
44
|
+
expect(client.isConnected).toBeFalsy();
|
|
45
|
+
admitConnection.wake();
|
|
46
|
+
await expect.poll(() => client.isConnected).toBeTruthy();
|
|
47
|
+
|
|
48
|
+
admitConnection.reset();
|
|
49
|
+
await closeConnection();
|
|
50
|
+
expect(client.isOpen).is.true;
|
|
51
|
+
await expect.poll(() => client.isConnected).toBeFalsy();
|
|
52
|
+
|
|
53
|
+
admitConnection.wake();
|
|
54
|
+
await expect.poll(() => client.isConnected).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
|
|
34
57
|
test('set identity reconnects', async () => {
|
|
35
58
|
const { endpoint, cleanup } = await createTestEdgeWsServer(8002);
|
|
36
59
|
onTestFinished(cleanup);
|
package/src/edge-client.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface EdgeConnection extends Required<Lifecycle> {
|
|
|
28
28
|
get identityKey(): string;
|
|
29
29
|
get peerKey(): string;
|
|
30
30
|
get isOpen(): boolean;
|
|
31
|
+
get isConnected(): boolean;
|
|
31
32
|
setIdentity(params: { peerKey: string; identityKey: string }): void;
|
|
32
33
|
addListener(listener: MessageListener): () => void;
|
|
33
34
|
send(message: Message): Promise<void>;
|
|
@@ -74,6 +75,10 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
get isConnected() {
|
|
79
|
+
return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
get identityKey() {
|
|
78
83
|
return this._identityKey;
|
|
79
84
|
}
|