@dxos/edge-client 0.6.14-main.f49f251 → 0.6.14-staging.54a8bab
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 +433 -226
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +7 -4
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +430 -225
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +7 -4
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node-esm/index.mjs +433 -226
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +7 -4
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/edge-client.d.ts +21 -18
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts +30 -0
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -0
- package/dist/types/src/persistent-lifecycle.d.ts +7 -5
- package/dist/types/src/persistent-lifecycle.d.ts.map +1 -1
- package/dist/types/src/testing/test-utils.d.ts +1 -0
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/edge-client.test.ts +101 -14
- package/src/edge-client.ts +128 -126
- package/src/edge-http-client.ts +4 -1
- package/src/edge-ws-connection.ts +148 -0
- package/src/persistent-lifecycle.ts +26 -11
- package/src/testing/test-utils.ts +3 -0
|
@@ -9,12 +9,9 @@ import {
|
|
|
9
9
|
export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
10
10
|
|
|
11
11
|
// packages/core/mesh/edge-client/src/edge-client.ts
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { log as log2 } from "@dxos/log";
|
|
16
|
-
import { buf } from "@dxos/protocols/buf";
|
|
17
|
-
import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
12
|
+
import { Trigger, scheduleMicroTask, TriggerState } from "@dxos/async";
|
|
13
|
+
import { Resource as Resource3 } from "@dxos/context";
|
|
14
|
+
import { log as log3, logInfo as logInfo2 } from "@dxos/log";
|
|
18
15
|
|
|
19
16
|
// packages/core/mesh/edge-client/src/edge-identity.ts
|
|
20
17
|
import { invariant } from "@dxos/invariant";
|
|
@@ -56,6 +53,204 @@ var handleAuthChallenge = async (failedResponse, identity) => {
|
|
|
56
53
|
return schema.getCodecForType("dxos.halo.credentials.Presentation").encode(presentation);
|
|
57
54
|
};
|
|
58
55
|
|
|
56
|
+
// packages/core/mesh/edge-client/src/edge-ws-connection.ts
|
|
57
|
+
import WebSocket from "isomorphic-ws";
|
|
58
|
+
import { scheduleTask, scheduleTaskInterval } from "@dxos/async";
|
|
59
|
+
import { Context, Resource } from "@dxos/context";
|
|
60
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
61
|
+
import { log, logInfo } from "@dxos/log";
|
|
62
|
+
import { buf } from "@dxos/protocols/buf";
|
|
63
|
+
import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
64
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
65
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
66
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
67
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
68
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
69
|
+
}
|
|
70
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-ws-connection.ts";
|
|
71
|
+
var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
|
|
72
|
+
var EdgeWsConnection = class extends Resource {
|
|
73
|
+
constructor(_identity, _connectionInfo, _callbacks) {
|
|
74
|
+
super();
|
|
75
|
+
this._identity = _identity;
|
|
76
|
+
this._connectionInfo = _connectionInfo;
|
|
77
|
+
this._callbacks = _callbacks;
|
|
78
|
+
}
|
|
79
|
+
get info() {
|
|
80
|
+
return {
|
|
81
|
+
open: this.isOpen,
|
|
82
|
+
identity: this._identity.identityKey,
|
|
83
|
+
device: this._identity.peerKey
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
send(message) {
|
|
87
|
+
invariant2(this._ws, void 0, {
|
|
88
|
+
F: __dxlog_file2,
|
|
89
|
+
L: 48,
|
|
90
|
+
S: this,
|
|
91
|
+
A: [
|
|
92
|
+
"this._ws",
|
|
93
|
+
""
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
log("sending...", {
|
|
97
|
+
peerKey: this._identity.peerKey,
|
|
98
|
+
payload: protocol.getPayloadType(message)
|
|
99
|
+
}, {
|
|
100
|
+
F: __dxlog_file2,
|
|
101
|
+
L: 49,
|
|
102
|
+
S: this,
|
|
103
|
+
C: (f, a) => f(...a)
|
|
104
|
+
});
|
|
105
|
+
this._ws.send(buf.toBinary(MessageSchema, message));
|
|
106
|
+
}
|
|
107
|
+
async _open() {
|
|
108
|
+
this._ws = new WebSocket(this._connectionInfo.url, this._connectionInfo.protocolHeader ? [
|
|
109
|
+
this._connectionInfo.protocolHeader
|
|
110
|
+
] : []);
|
|
111
|
+
this._ws.onopen = () => {
|
|
112
|
+
if (this.isOpen) {
|
|
113
|
+
log("connected", void 0, {
|
|
114
|
+
F: __dxlog_file2,
|
|
115
|
+
L: 61,
|
|
116
|
+
S: this,
|
|
117
|
+
C: (f, a) => f(...a)
|
|
118
|
+
});
|
|
119
|
+
this._callbacks.onConnected();
|
|
120
|
+
this._scheduleHeartbeats();
|
|
121
|
+
} else {
|
|
122
|
+
log.verbose("connected after becoming inactive", {
|
|
123
|
+
currentIdentity: this._identity
|
|
124
|
+
}, {
|
|
125
|
+
F: __dxlog_file2,
|
|
126
|
+
L: 65,
|
|
127
|
+
S: this,
|
|
128
|
+
C: (f, a) => f(...a)
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
this._ws.onclose = () => {
|
|
133
|
+
if (this.isOpen) {
|
|
134
|
+
log("disconnected while being open", void 0, {
|
|
135
|
+
F: __dxlog_file2,
|
|
136
|
+
L: 70,
|
|
137
|
+
S: this,
|
|
138
|
+
C: (f, a) => f(...a)
|
|
139
|
+
});
|
|
140
|
+
this._callbacks.onRestartRequired();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
this._ws.onerror = (event) => {
|
|
144
|
+
if (this.isOpen) {
|
|
145
|
+
log.warn("edge connection socket error", {
|
|
146
|
+
error: event.error,
|
|
147
|
+
info: event.message
|
|
148
|
+
}, {
|
|
149
|
+
F: __dxlog_file2,
|
|
150
|
+
L: 76,
|
|
151
|
+
S: this,
|
|
152
|
+
C: (f, a) => f(...a)
|
|
153
|
+
});
|
|
154
|
+
this._callbacks.onRestartRequired();
|
|
155
|
+
} else {
|
|
156
|
+
log.verbose("error ignored on closed connection", {
|
|
157
|
+
error: event.error
|
|
158
|
+
}, {
|
|
159
|
+
F: __dxlog_file2,
|
|
160
|
+
L: 79,
|
|
161
|
+
S: this,
|
|
162
|
+
C: (f, a) => f(...a)
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
this._ws.onmessage = async (event) => {
|
|
167
|
+
if (!this.isOpen) {
|
|
168
|
+
log.verbose("message ignored on closed connection", {
|
|
169
|
+
event: event.type
|
|
170
|
+
}, {
|
|
171
|
+
F: __dxlog_file2,
|
|
172
|
+
L: 87,
|
|
173
|
+
S: this,
|
|
174
|
+
C: (f, a) => f(...a)
|
|
175
|
+
});
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (event.data === "__pong__") {
|
|
179
|
+
this._rescheduleHeartbeatTimeout();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const data = await toUint8Array(event.data);
|
|
183
|
+
if (this.isOpen) {
|
|
184
|
+
const message = buf.fromBinary(MessageSchema, data);
|
|
185
|
+
log("received", {
|
|
186
|
+
from: message.source,
|
|
187
|
+
payload: protocol.getPayloadType(message)
|
|
188
|
+
}, {
|
|
189
|
+
F: __dxlog_file2,
|
|
190
|
+
L: 97,
|
|
191
|
+
S: this,
|
|
192
|
+
C: (f, a) => f(...a)
|
|
193
|
+
});
|
|
194
|
+
this._callbacks.onMessage(message);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
async _close() {
|
|
199
|
+
void this._inactivityTimeoutCtx?.dispose().catch(() => {
|
|
200
|
+
});
|
|
201
|
+
try {
|
|
202
|
+
this._ws?.close();
|
|
203
|
+
this._ws = void 0;
|
|
204
|
+
} catch (err) {
|
|
205
|
+
if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
log.warn("Error closing websocket", {
|
|
209
|
+
err
|
|
210
|
+
}, {
|
|
211
|
+
F: __dxlog_file2,
|
|
212
|
+
L: 113,
|
|
213
|
+
S: this,
|
|
214
|
+
C: (f, a) => f(...a)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
_scheduleHeartbeats() {
|
|
219
|
+
invariant2(this._ws, void 0, {
|
|
220
|
+
F: __dxlog_file2,
|
|
221
|
+
L: 118,
|
|
222
|
+
S: this,
|
|
223
|
+
A: [
|
|
224
|
+
"this._ws",
|
|
225
|
+
""
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
scheduleTaskInterval(this._ctx, async () => {
|
|
229
|
+
this._ws?.send("__ping__");
|
|
230
|
+
}, SIGNAL_KEEPALIVE_INTERVAL);
|
|
231
|
+
this._ws.send("__ping__");
|
|
232
|
+
this._rescheduleHeartbeatTimeout();
|
|
233
|
+
}
|
|
234
|
+
_rescheduleHeartbeatTimeout() {
|
|
235
|
+
if (!this.isOpen) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
void this._inactivityTimeoutCtx?.dispose();
|
|
239
|
+
this._inactivityTimeoutCtx = new Context(void 0, {
|
|
240
|
+
F: __dxlog_file2,
|
|
241
|
+
L: 137
|
|
242
|
+
});
|
|
243
|
+
scheduleTask(this._inactivityTimeoutCtx, () => {
|
|
244
|
+
if (this.isOpen) {
|
|
245
|
+
this._callbacks.onRestartRequired();
|
|
246
|
+
}
|
|
247
|
+
}, 2 * SIGNAL_KEEPALIVE_INTERVAL);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
_ts_decorate([
|
|
251
|
+
logInfo
|
|
252
|
+
], EdgeWsConnection.prototype, "info", null);
|
|
253
|
+
|
|
59
254
|
// packages/core/mesh/edge-client/src/errors.ts
|
|
60
255
|
var EdgeConnectionClosedError = class extends Error {
|
|
61
256
|
constructor() {
|
|
@@ -70,21 +265,22 @@ var EdgeIdentityChangedError = class extends Error {
|
|
|
70
265
|
|
|
71
266
|
// packages/core/mesh/edge-client/src/persistent-lifecycle.ts
|
|
72
267
|
import { DeferredTask, sleep, synchronized } from "@dxos/async";
|
|
73
|
-
import { cancelWithContext, LifecycleState, Resource } from "@dxos/context";
|
|
268
|
+
import { cancelWithContext, LifecycleState, Resource as Resource2 } from "@dxos/context";
|
|
74
269
|
import { warnAfterTimeout } from "@dxos/debug";
|
|
75
|
-
import { log } from "@dxos/log";
|
|
76
|
-
function
|
|
270
|
+
import { log as log2 } from "@dxos/log";
|
|
271
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
77
272
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
78
273
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
79
274
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
80
275
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
81
276
|
}
|
|
82
|
-
var
|
|
277
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/persistent-lifecycle.ts";
|
|
83
278
|
var INIT_RESTART_DELAY = 100;
|
|
84
279
|
var DEFAULT_MAX_RESTART_DELAY = 5e3;
|
|
85
|
-
var PersistentLifecycle = class extends
|
|
280
|
+
var PersistentLifecycle = class extends Resource2 {
|
|
86
281
|
constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
|
|
87
282
|
super();
|
|
283
|
+
this._currentContext = void 0;
|
|
88
284
|
this._restartTask = void 0;
|
|
89
285
|
this._restartAfter = 0;
|
|
90
286
|
this._start = start;
|
|
@@ -97,53 +293,71 @@ var PersistentLifecycle = class extends Resource {
|
|
|
97
293
|
try {
|
|
98
294
|
await this._restart();
|
|
99
295
|
} catch (err) {
|
|
100
|
-
|
|
296
|
+
log2.warn("Restart failed", {
|
|
101
297
|
err
|
|
102
298
|
}, {
|
|
103
|
-
F:
|
|
104
|
-
L:
|
|
299
|
+
F: __dxlog_file3,
|
|
300
|
+
L: 65,
|
|
105
301
|
S: this,
|
|
106
302
|
C: (f, a) => f(...a)
|
|
107
303
|
});
|
|
108
304
|
this._restartTask?.schedule();
|
|
109
305
|
}
|
|
110
306
|
});
|
|
111
|
-
await this._start().catch((err) => {
|
|
112
|
-
|
|
307
|
+
this._currentContext = await this._start().catch((err) => {
|
|
308
|
+
log2.warn("Start failed", {
|
|
113
309
|
err
|
|
114
310
|
}, {
|
|
115
|
-
F:
|
|
116
|
-
L:
|
|
311
|
+
F: __dxlog_file3,
|
|
312
|
+
L: 70,
|
|
117
313
|
S: this,
|
|
118
314
|
C: (f, a) => f(...a)
|
|
119
315
|
});
|
|
120
316
|
this._restartTask?.schedule();
|
|
317
|
+
return void 0;
|
|
121
318
|
});
|
|
122
319
|
}
|
|
123
320
|
async _close() {
|
|
124
321
|
await this._restartTask?.join();
|
|
125
|
-
await this.
|
|
322
|
+
await this._stopCurrentContext();
|
|
126
323
|
this._restartTask = void 0;
|
|
127
324
|
}
|
|
128
325
|
async _restart() {
|
|
129
|
-
|
|
326
|
+
log2(`restarting in ${this._restartAfter}ms`, {
|
|
130
327
|
state: this._lifecycleState
|
|
131
328
|
}, {
|
|
132
|
-
F:
|
|
133
|
-
L:
|
|
329
|
+
F: __dxlog_file3,
|
|
330
|
+
L: 83,
|
|
134
331
|
S: this,
|
|
135
332
|
C: (f, a) => f(...a)
|
|
136
333
|
});
|
|
137
|
-
await this.
|
|
334
|
+
await this._stopCurrentContext();
|
|
138
335
|
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
139
336
|
return;
|
|
140
337
|
}
|
|
141
338
|
await cancelWithContext(this._ctx, sleep(this._restartAfter));
|
|
142
339
|
this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
|
|
143
|
-
await warnAfterTimeout(5e3, "Connection establishment takes too long", () =>
|
|
340
|
+
await warnAfterTimeout(5e3, "Connection establishment takes too long", async () => {
|
|
341
|
+
this._currentContext = await this._start();
|
|
342
|
+
});
|
|
144
343
|
this._restartAfter = 0;
|
|
145
344
|
await this._onRestart?.();
|
|
146
345
|
}
|
|
346
|
+
async _stopCurrentContext() {
|
|
347
|
+
if (this._currentContext) {
|
|
348
|
+
try {
|
|
349
|
+
await this._stop(this._currentContext);
|
|
350
|
+
} catch (err) {
|
|
351
|
+
log2.catch(err, void 0, {
|
|
352
|
+
F: __dxlog_file3,
|
|
353
|
+
L: 105,
|
|
354
|
+
S: this,
|
|
355
|
+
C: (f, a) => f(...a)
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
this._currentContext = void 0;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
147
361
|
/**
|
|
148
362
|
* Scheduling restart should be done from outside.
|
|
149
363
|
*/
|
|
@@ -154,10 +368,10 @@ var PersistentLifecycle = class extends Resource {
|
|
|
154
368
|
this._restartTask.schedule();
|
|
155
369
|
}
|
|
156
370
|
};
|
|
157
|
-
|
|
371
|
+
_ts_decorate2([
|
|
158
372
|
synchronized
|
|
159
373
|
], PersistentLifecycle.prototype, "_open", null);
|
|
160
|
-
|
|
374
|
+
_ts_decorate2([
|
|
161
375
|
synchronized
|
|
162
376
|
], PersistentLifecycle.prototype, "scheduleRestart", null);
|
|
163
377
|
|
|
@@ -170,30 +384,31 @@ var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
|
|
|
170
384
|
};
|
|
171
385
|
|
|
172
386
|
// packages/core/mesh/edge-client/src/edge-client.ts
|
|
173
|
-
|
|
387
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
388
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
389
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
390
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
391
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
392
|
+
}
|
|
393
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
|
|
174
394
|
var DEFAULT_TIMEOUT = 1e4;
|
|
175
|
-
var
|
|
176
|
-
var EdgeClient = class extends Resource2 {
|
|
395
|
+
var EdgeClient = class extends Resource3 {
|
|
177
396
|
constructor(_identity, _config) {
|
|
178
397
|
super();
|
|
179
398
|
this._identity = _identity;
|
|
180
399
|
this._config = _config;
|
|
181
|
-
this.reconnect = new Event();
|
|
182
|
-
this.connected = new Event();
|
|
183
400
|
this._persistentLifecycle = new PersistentLifecycle({
|
|
184
|
-
start: async () => this.
|
|
185
|
-
stop: async () => this.
|
|
186
|
-
onRestart: async () => this.reconnect.emit()
|
|
401
|
+
start: async () => this._connect(),
|
|
402
|
+
stop: async (state) => this._disconnect(state)
|
|
187
403
|
});
|
|
188
|
-
this.
|
|
404
|
+
this._messageListeners = /* @__PURE__ */ new Set();
|
|
405
|
+
this._reconnectListeners = /* @__PURE__ */ new Set();
|
|
406
|
+
this._currentConnection = void 0;
|
|
189
407
|
this._ready = new Trigger();
|
|
190
|
-
this.
|
|
191
|
-
this._keepaliveCtx = void 0;
|
|
192
|
-
this._heartBeatContext = void 0;
|
|
408
|
+
this._isActive = (connection) => connection === this._currentConnection;
|
|
193
409
|
this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
|
|
194
410
|
this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
|
|
195
411
|
}
|
|
196
|
-
// TODO(burdon): Attach logging.
|
|
197
412
|
get info() {
|
|
198
413
|
return {
|
|
199
414
|
open: this.isOpen,
|
|
@@ -202,7 +417,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
202
417
|
};
|
|
203
418
|
}
|
|
204
419
|
get isConnected() {
|
|
205
|
-
return Boolean(this.
|
|
420
|
+
return Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED;
|
|
206
421
|
}
|
|
207
422
|
get identityKey() {
|
|
208
423
|
return this._identity.identityKey;
|
|
@@ -212,41 +427,62 @@ var EdgeClient = class extends Resource2 {
|
|
|
212
427
|
}
|
|
213
428
|
setIdentity(identity) {
|
|
214
429
|
if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
|
|
215
|
-
|
|
430
|
+
log3("Edge identity changed", {
|
|
216
431
|
identity,
|
|
217
432
|
oldIdentity: this._identity
|
|
218
433
|
}, {
|
|
219
|
-
F:
|
|
220
|
-
L:
|
|
434
|
+
F: __dxlog_file4,
|
|
435
|
+
L: 95,
|
|
221
436
|
S: this,
|
|
222
437
|
C: (f, a) => f(...a)
|
|
223
438
|
});
|
|
224
439
|
this._identity = identity;
|
|
440
|
+
this._closeCurrentConnection(new EdgeIdentityChangedError());
|
|
225
441
|
this._persistentLifecycle.scheduleRestart();
|
|
226
442
|
}
|
|
227
443
|
}
|
|
228
|
-
|
|
229
|
-
this.
|
|
230
|
-
return () => this.
|
|
444
|
+
onMessage(listener) {
|
|
445
|
+
this._messageListeners.add(listener);
|
|
446
|
+
return () => this._messageListeners.delete(listener);
|
|
447
|
+
}
|
|
448
|
+
onReconnected(listener) {
|
|
449
|
+
this._reconnectListeners.add(listener);
|
|
450
|
+
if (this._ready.state === TriggerState.RESOLVED) {
|
|
451
|
+
scheduleMicroTask(this._ctx, () => {
|
|
452
|
+
if (this._reconnectListeners.has(listener)) {
|
|
453
|
+
try {
|
|
454
|
+
listener();
|
|
455
|
+
} catch (error) {
|
|
456
|
+
log3.catch(error, void 0, {
|
|
457
|
+
F: __dxlog_file4,
|
|
458
|
+
L: 117,
|
|
459
|
+
S: this,
|
|
460
|
+
C: (f, a) => f(...a)
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
return () => this._reconnectListeners.delete(listener);
|
|
231
467
|
}
|
|
232
468
|
/**
|
|
233
469
|
* Open connection to messaging service.
|
|
234
470
|
*/
|
|
235
471
|
async _open() {
|
|
236
|
-
|
|
472
|
+
log3("opening...", {
|
|
237
473
|
info: this.info
|
|
238
474
|
}, {
|
|
239
|
-
F:
|
|
240
|
-
L:
|
|
475
|
+
F: __dxlog_file4,
|
|
476
|
+
L: 129,
|
|
241
477
|
S: this,
|
|
242
478
|
C: (f, a) => f(...a)
|
|
243
479
|
});
|
|
244
480
|
this._persistentLifecycle.open().catch((err) => {
|
|
245
|
-
|
|
481
|
+
log3.warn("Error while opening connection", {
|
|
246
482
|
err
|
|
247
483
|
}, {
|
|
248
|
-
F:
|
|
249
|
-
L:
|
|
484
|
+
F: __dxlog_file4,
|
|
485
|
+
L: 131,
|
|
250
486
|
S: this,
|
|
251
487
|
C: (f, a) => f(...a)
|
|
252
488
|
});
|
|
@@ -256,153 +492,140 @@ var EdgeClient = class extends Resource2 {
|
|
|
256
492
|
* Close connection and free resources.
|
|
257
493
|
*/
|
|
258
494
|
async _close() {
|
|
259
|
-
|
|
495
|
+
log3("closing...", {
|
|
260
496
|
peerKey: this._identity.peerKey
|
|
261
497
|
}, {
|
|
262
|
-
F:
|
|
263
|
-
L:
|
|
498
|
+
F: __dxlog_file4,
|
|
499
|
+
L: 139,
|
|
264
500
|
S: this,
|
|
265
501
|
C: (f, a) => f(...a)
|
|
266
502
|
});
|
|
503
|
+
this._closeCurrentConnection();
|
|
267
504
|
await this._persistentLifecycle.close();
|
|
268
505
|
}
|
|
269
|
-
async
|
|
506
|
+
async _connect() {
|
|
270
507
|
if (this._ctx.disposed) {
|
|
271
|
-
return;
|
|
508
|
+
return void 0;
|
|
272
509
|
}
|
|
273
|
-
const
|
|
510
|
+
const identity = this._identity;
|
|
511
|
+
const path = `/ws/${identity.identityKey}/${identity.peerKey}`;
|
|
274
512
|
const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
|
|
513
|
+
if (this._identity !== identity) {
|
|
514
|
+
log3("identity changed during auth header request", void 0, {
|
|
515
|
+
F: __dxlog_file4,
|
|
516
|
+
L: 153,
|
|
517
|
+
S: this,
|
|
518
|
+
C: (f, a) => f(...a)
|
|
519
|
+
});
|
|
520
|
+
return void 0;
|
|
521
|
+
}
|
|
522
|
+
const restartRequired = new Trigger();
|
|
275
523
|
const url = new URL(path, this._baseWsUrl);
|
|
276
|
-
|
|
524
|
+
log3("Opening websocket", {
|
|
277
525
|
url: url.toString(),
|
|
278
526
|
protocolHeader
|
|
279
527
|
}, {
|
|
280
|
-
F:
|
|
281
|
-
L:
|
|
528
|
+
F: __dxlog_file4,
|
|
529
|
+
L: 159,
|
|
282
530
|
S: this,
|
|
283
531
|
C: (f, a) => f(...a)
|
|
284
532
|
});
|
|
285
|
-
|
|
533
|
+
const connection = new EdgeWsConnection(identity, {
|
|
534
|
+
url,
|
|
286
535
|
protocolHeader
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
payload: protocol.getPayloadType(message)
|
|
329
|
-
}, {
|
|
330
|
-
F: __dxlog_file3,
|
|
331
|
-
L: 162,
|
|
332
|
-
S: this,
|
|
333
|
-
C: (f, a) => f(...a)
|
|
334
|
-
});
|
|
335
|
-
if (message) {
|
|
336
|
-
for (const listener of this._listeners) {
|
|
337
|
-
try {
|
|
338
|
-
await listener(message);
|
|
339
|
-
} catch (err) {
|
|
340
|
-
log2.error("processing", {
|
|
341
|
-
err,
|
|
342
|
-
payload: protocol.getPayloadType(message)
|
|
343
|
-
}, {
|
|
344
|
-
F: __dxlog_file3,
|
|
345
|
-
L: 168,
|
|
346
|
-
S: this,
|
|
347
|
-
C: (f, a) => f(...a)
|
|
348
|
-
});
|
|
349
|
-
}
|
|
536
|
+
}, {
|
|
537
|
+
onConnected: () => {
|
|
538
|
+
if (this._isActive(connection)) {
|
|
539
|
+
this._ready.wake();
|
|
540
|
+
this._notifyReconnected();
|
|
541
|
+
} else {
|
|
542
|
+
log3.verbose("connected callback ignored, because connection is not active", void 0, {
|
|
543
|
+
F: __dxlog_file4,
|
|
544
|
+
L: 169,
|
|
545
|
+
S: this,
|
|
546
|
+
C: (f, a) => f(...a)
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
onRestartRequired: () => {
|
|
551
|
+
if (this._isActive(connection)) {
|
|
552
|
+
this._closeCurrentConnection();
|
|
553
|
+
this._persistentLifecycle.scheduleRestart();
|
|
554
|
+
} else {
|
|
555
|
+
log3.verbose("restart requested by inactive connection", void 0, {
|
|
556
|
+
F: __dxlog_file4,
|
|
557
|
+
L: 177,
|
|
558
|
+
S: this,
|
|
559
|
+
C: (f, a) => f(...a)
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
restartRequired.wake();
|
|
563
|
+
},
|
|
564
|
+
onMessage: (message) => {
|
|
565
|
+
if (this._isActive(connection)) {
|
|
566
|
+
this._notifyMessageReceived(message);
|
|
567
|
+
} else {
|
|
568
|
+
log3.verbose("ignored a message on inactive connection", {
|
|
569
|
+
from: message.source,
|
|
570
|
+
type: message.payload?.typeUrl
|
|
571
|
+
}, {
|
|
572
|
+
F: __dxlog_file4,
|
|
573
|
+
L: 185,
|
|
574
|
+
S: this,
|
|
575
|
+
C: (f, a) => f(...a)
|
|
576
|
+
});
|
|
350
577
|
}
|
|
351
578
|
}
|
|
352
|
-
};
|
|
353
|
-
await this._ready.wait({
|
|
354
|
-
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
355
579
|
});
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
this._keepaliveCtx = new Context(void 0, {
|
|
366
|
-
F: __dxlog_file3,
|
|
367
|
-
L: 179
|
|
368
|
-
});
|
|
369
|
-
scheduleTaskInterval(this._keepaliveCtx, async () => {
|
|
370
|
-
this._ws?.send("__ping__");
|
|
371
|
-
}, SIGNAL_KEEPALIVE_INTERVAL);
|
|
372
|
-
this._ws.send("__ping__");
|
|
373
|
-
this._onHeartbeat();
|
|
580
|
+
this._currentConnection = connection;
|
|
581
|
+
await connection.open();
|
|
582
|
+
await Promise.race([
|
|
583
|
+
this._ready.wait({
|
|
584
|
+
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
585
|
+
}),
|
|
586
|
+
restartRequired
|
|
587
|
+
]);
|
|
588
|
+
return connection;
|
|
374
589
|
}
|
|
375
|
-
async
|
|
376
|
-
|
|
377
|
-
|
|
590
|
+
async _disconnect(state) {
|
|
591
|
+
await state.close();
|
|
592
|
+
}
|
|
593
|
+
_closeCurrentConnection(error = new EdgeConnectionClosedError()) {
|
|
594
|
+
this._currentConnection = void 0;
|
|
595
|
+
this._ready.throw(error);
|
|
596
|
+
this._ready.reset();
|
|
597
|
+
}
|
|
598
|
+
_notifyReconnected() {
|
|
599
|
+
for (const listener of this._reconnectListeners) {
|
|
600
|
+
try {
|
|
601
|
+
listener();
|
|
602
|
+
} catch (err) {
|
|
603
|
+
log3.error("ws reconnect listener failed", {
|
|
604
|
+
err
|
|
605
|
+
}, {
|
|
606
|
+
F: __dxlog_file4,
|
|
607
|
+
L: 218,
|
|
608
|
+
S: this,
|
|
609
|
+
C: (f, a) => f(...a)
|
|
610
|
+
});
|
|
611
|
+
}
|
|
378
612
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
} catch (err) {
|
|
395
|
-
if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
|
|
396
|
-
return;
|
|
613
|
+
}
|
|
614
|
+
_notifyMessageReceived(message) {
|
|
615
|
+
for (const listener of this._messageListeners) {
|
|
616
|
+
try {
|
|
617
|
+
listener(message);
|
|
618
|
+
} catch (err) {
|
|
619
|
+
log3.error("ws incoming message processing failed", {
|
|
620
|
+
err,
|
|
621
|
+
payload: protocol.getPayloadType(message)
|
|
622
|
+
}, {
|
|
623
|
+
F: __dxlog_file4,
|
|
624
|
+
L: 228,
|
|
625
|
+
S: this,
|
|
626
|
+
C: (f, a) => f(...a)
|
|
627
|
+
});
|
|
397
628
|
}
|
|
398
|
-
log2.warn("Error closing websocket", {
|
|
399
|
-
err
|
|
400
|
-
}, {
|
|
401
|
-
F: __dxlog_file3,
|
|
402
|
-
L: 215,
|
|
403
|
-
S: this,
|
|
404
|
-
C: (f, a) => f(...a)
|
|
405
|
-
});
|
|
406
629
|
}
|
|
407
630
|
}
|
|
408
631
|
/**
|
|
@@ -411,9 +634,9 @@ var EdgeClient = class extends Resource2 {
|
|
|
411
634
|
*/
|
|
412
635
|
async send(message) {
|
|
413
636
|
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
414
|
-
|
|
415
|
-
F:
|
|
416
|
-
L:
|
|
637
|
+
log3("waiting for websocket to become ready", void 0, {
|
|
638
|
+
F: __dxlog_file4,
|
|
639
|
+
L: 239,
|
|
417
640
|
S: this,
|
|
418
641
|
C: (f, a) => f(...a)
|
|
419
642
|
});
|
|
@@ -421,35 +644,13 @@ var EdgeClient = class extends Resource2 {
|
|
|
421
644
|
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
422
645
|
});
|
|
423
646
|
}
|
|
424
|
-
if (!this.
|
|
647
|
+
if (!this._currentConnection) {
|
|
425
648
|
throw new EdgeConnectionClosedError();
|
|
426
649
|
}
|
|
427
650
|
if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
|
|
428
651
|
throw new EdgeIdentityChangedError();
|
|
429
652
|
}
|
|
430
|
-
|
|
431
|
-
peerKey: this._identity.peerKey,
|
|
432
|
-
payload: protocol.getPayloadType(message)
|
|
433
|
-
}, {
|
|
434
|
-
F: __dxlog_file3,
|
|
435
|
-
L: 238,
|
|
436
|
-
S: this,
|
|
437
|
-
C: (f, a) => f(...a)
|
|
438
|
-
});
|
|
439
|
-
this._ws.send(buf.toBinary(MessageSchema, message));
|
|
440
|
-
}
|
|
441
|
-
_onHeartbeat() {
|
|
442
|
-
if (this._lifecycleState !== LifecycleState2.OPEN) {
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
void this._heartBeatContext?.dispose();
|
|
446
|
-
this._heartBeatContext = new Context(void 0, {
|
|
447
|
-
F: __dxlog_file3,
|
|
448
|
-
L: 247
|
|
449
|
-
});
|
|
450
|
-
scheduleTask(this._heartBeatContext, () => {
|
|
451
|
-
this._persistentLifecycle.scheduleRestart();
|
|
452
|
-
}, 2 * SIGNAL_KEEPALIVE_INTERVAL);
|
|
653
|
+
this._currentConnection.send(message);
|
|
453
654
|
}
|
|
454
655
|
async _createAuthHeader(path) {
|
|
455
656
|
const httpUrl = new URL(path, this._baseHttpUrl);
|
|
@@ -460,11 +661,11 @@ var EdgeClient = class extends Resource2 {
|
|
|
460
661
|
if (response.status === 401) {
|
|
461
662
|
return encodePresentationWsAuthHeader(await handleAuthChallenge(response, this._identity));
|
|
462
663
|
} else {
|
|
463
|
-
|
|
664
|
+
log3.warn("no auth challenge from edge", {
|
|
464
665
|
status: response.status,
|
|
465
666
|
statusText: response.statusText
|
|
466
667
|
}, {
|
|
467
|
-
F:
|
|
668
|
+
F: __dxlog_file4,
|
|
468
669
|
L: 264,
|
|
469
670
|
S: this,
|
|
470
671
|
C: (f, a) => f(...a)
|
|
@@ -473,6 +674,9 @@ var EdgeClient = class extends Resource2 {
|
|
|
473
674
|
}
|
|
474
675
|
}
|
|
475
676
|
};
|
|
677
|
+
_ts_decorate3([
|
|
678
|
+
logInfo2
|
|
679
|
+
], EdgeClient.prototype, "info", null);
|
|
476
680
|
var encodePresentationWsAuthHeader = (encodedPresentation) => {
|
|
477
681
|
const encodedToken = Buffer.from(encodedPresentation).toString("base64").replace(/=*$/, "").replaceAll("/", "|");
|
|
478
682
|
return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
|
|
@@ -581,26 +785,29 @@ var createStubEdgeIdentity = () => {
|
|
|
581
785
|
// packages/core/mesh/edge-client/src/edge-http-client.ts
|
|
582
786
|
import { sleep as sleep2 } from "@dxos/async";
|
|
583
787
|
import { Context as Context2 } from "@dxos/context";
|
|
584
|
-
import { log as
|
|
788
|
+
import { log as log4 } from "@dxos/log";
|
|
585
789
|
import { EdgeCallFailedError, EdgeAuthChallengeError } from "@dxos/protocols";
|
|
586
|
-
var
|
|
790
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
|
|
587
791
|
var DEFAULT_RETRY_TIMEOUT = 1500;
|
|
588
792
|
var DEFAULT_RETRY_JITTER = 500;
|
|
589
793
|
var DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
590
794
|
var EdgeHttpClient = class {
|
|
591
795
|
constructor(baseUrl) {
|
|
592
796
|
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
|
|
593
|
-
|
|
797
|
+
log4("created", {
|
|
594
798
|
url: this._baseUrl
|
|
595
799
|
}, {
|
|
596
|
-
F:
|
|
800
|
+
F: __dxlog_file5,
|
|
597
801
|
L: 42,
|
|
598
802
|
S: this,
|
|
599
803
|
C: (f, a) => f(...a)
|
|
600
804
|
});
|
|
601
805
|
}
|
|
602
806
|
setIdentity(identity) {
|
|
603
|
-
this._edgeIdentity
|
|
807
|
+
if (this._edgeIdentity?.identityKey !== identity.identityKey || this._edgeIdentity?.peerKey !== identity.peerKey) {
|
|
808
|
+
this._edgeIdentity = identity;
|
|
809
|
+
this._authHeader = void 0;
|
|
810
|
+
}
|
|
604
811
|
}
|
|
605
812
|
createAgent(body, args) {
|
|
606
813
|
return this._call("/agents/create", {
|
|
@@ -644,18 +851,18 @@ var EdgeHttpClient = class {
|
|
|
644
851
|
}
|
|
645
852
|
async _call(path, args) {
|
|
646
853
|
const requestContext = args.context ?? new Context2(void 0, {
|
|
647
|
-
F:
|
|
648
|
-
L:
|
|
854
|
+
F: __dxlog_file5,
|
|
855
|
+
L: 91
|
|
649
856
|
});
|
|
650
857
|
const shouldRetry = createRetryHandler(args);
|
|
651
858
|
const url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
|
|
652
|
-
|
|
859
|
+
log4.info("call", {
|
|
653
860
|
method: args.method,
|
|
654
861
|
path,
|
|
655
862
|
request: args.body
|
|
656
863
|
}, {
|
|
657
|
-
F:
|
|
658
|
-
L:
|
|
864
|
+
F: __dxlog_file5,
|
|
865
|
+
L: 95,
|
|
659
866
|
S: this,
|
|
660
867
|
C: (f, a) => f(...a)
|
|
661
868
|
});
|
|
@@ -673,12 +880,12 @@ var EdgeHttpClient = class {
|
|
|
673
880
|
if (body.success) {
|
|
674
881
|
return body.data;
|
|
675
882
|
}
|
|
676
|
-
|
|
883
|
+
log4.info("unsuccessful edge response", {
|
|
677
884
|
path,
|
|
678
885
|
body
|
|
679
886
|
}, {
|
|
680
|
-
F:
|
|
681
|
-
L:
|
|
887
|
+
F: __dxlog_file5,
|
|
888
|
+
L: 114,
|
|
682
889
|
S: this,
|
|
683
890
|
C: (f, a) => f(...a)
|
|
684
891
|
});
|
|
@@ -698,12 +905,12 @@ var EdgeHttpClient = class {
|
|
|
698
905
|
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
699
906
|
}
|
|
700
907
|
if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
|
|
701
|
-
|
|
908
|
+
log4.info("retrying edge request", {
|
|
702
909
|
path,
|
|
703
910
|
processingError
|
|
704
911
|
}, {
|
|
705
|
-
F:
|
|
706
|
-
L:
|
|
912
|
+
F: __dxlog_file5,
|
|
913
|
+
L: 133,
|
|
707
914
|
S: this,
|
|
708
915
|
C: (f, a) => f(...a)
|
|
709
916
|
});
|
|
@@ -714,9 +921,9 @@ var EdgeHttpClient = class {
|
|
|
714
921
|
}
|
|
715
922
|
async _handleUnauthorized(response) {
|
|
716
923
|
if (!this._edgeIdentity) {
|
|
717
|
-
|
|
718
|
-
F:
|
|
719
|
-
L:
|
|
924
|
+
log4.warn("edge unauthorized response received before identity was set", void 0, {
|
|
925
|
+
F: __dxlog_file5,
|
|
926
|
+
L: 142,
|
|
720
927
|
S: this,
|
|
721
928
|
C: (f, a) => f(...a)
|
|
722
929
|
});
|
|
@@ -724,9 +931,9 @@ var EdgeHttpClient = class {
|
|
|
724
931
|
}
|
|
725
932
|
const challenge = await handleAuthChallenge(response, this._edgeIdentity);
|
|
726
933
|
this._authHeader = encodeAuthHeader(challenge);
|
|
727
|
-
|
|
728
|
-
F:
|
|
729
|
-
L:
|
|
934
|
+
log4("auth header updated", void 0, {
|
|
935
|
+
F: __dxlog_file5,
|
|
936
|
+
L: 147,
|
|
730
937
|
S: this,
|
|
731
938
|
C: (f, a) => f(...a)
|
|
732
939
|
});
|