@dxos/edge-client 0.6.13 → 0.6.14-main.1366248
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/chunk-ZWJXA37R.mjs +113 -0
- package/dist/lib/browser/chunk-ZWJXA37R.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +802 -286
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +128 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/chunk-ANV2HBEH.cjs +136 -0
- package/dist/lib/node/chunk-ANV2HBEH.cjs.map +7 -0
- package/dist/lib/node/index.cjs +798 -283
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +158 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-HNVT57AU.mjs +115 -0
- package/dist/lib/node-esm/chunk-HNVT57AU.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +994 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +129 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/auth.d.ts +22 -0
- package/dist/types/src/auth.d.ts.map +1 -0
- package/dist/types/src/defs.d.ts.map +1 -1
- package/dist/types/src/edge-client.d.ts +30 -26
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts +48 -0
- package/dist/types/src/edge-http-client.d.ts.map +1 -0
- package/dist/types/src/edge-identity.d.ts +15 -0
- package/dist/types/src/edge-identity.d.ts.map +1 -0
- 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/errors.d.ts +4 -1
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -1
- 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/protocol.d.ts +2 -2
- package/dist/types/src/protocol.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/test-utils.d.ts +22 -0
- package/dist/types/src/testing/test-utils.d.ts.map +1 -0
- package/dist/types/src/utils.d.ts +2 -0
- package/dist/types/src/utils.d.ts.map +1 -0
- package/package.json +27 -17
- package/src/auth.ts +135 -0
- package/src/defs.ts +2 -3
- package/src/edge-client.test.ts +144 -25
- package/src/edge-client.ts +181 -127
- package/src/edge-http-client.ts +213 -0
- package/src/edge-identity.ts +31 -0
- package/src/edge-ws-connection.ts +148 -0
- package/src/errors.ts +8 -2
- package/src/index.ts +4 -0
- package/src/persistent-lifecycle.test.ts +2 -2
- package/src/persistent-lifecycle.ts +26 -11
- package/src/protocol.test.ts +1 -2
- package/src/protocol.ts +2 -2
- package/src/testing/index.ts +5 -0
- package/src/testing/test-utils.ts +117 -0
- package/src/utils.ts +10 -0
- package/src/websocket.test.ts +5 -4
- package/dist/types/src/test-utils.d.ts +0 -11
- package/dist/types/src/test-utils.d.ts.map +0 -1
- package/src/test-utils.ts +0 -49
|
@@ -1,147 +1,286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Protocol,
|
|
3
|
+
getTypename,
|
|
4
|
+
protocol,
|
|
5
|
+
toUint8Array
|
|
6
|
+
} from "./chunk-ZWJXA37R.mjs";
|
|
7
|
+
|
|
1
8
|
// packages/core/mesh/edge-client/src/index.ts
|
|
2
9
|
export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
3
10
|
|
|
4
11
|
// packages/core/mesh/edge-client/src/edge-client.ts
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
9
|
-
import { log as log2 } from "@dxos/log";
|
|
10
|
-
import { buf as buf2 } from "@dxos/protocols/buf";
|
|
11
|
-
import { MessageSchema as MessageSchema2 } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
12
|
-
|
|
13
|
-
// packages/core/mesh/edge-client/src/defs.ts
|
|
14
|
-
import { AnySchema } from "@bufbuild/protobuf/wkt";
|
|
15
|
-
import { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } 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";
|
|
16
15
|
|
|
17
|
-
// packages/core/mesh/edge-client/src/
|
|
16
|
+
// packages/core/mesh/edge-client/src/edge-identity.ts
|
|
18
17
|
import { invariant } from "@dxos/invariant";
|
|
19
|
-
import {
|
|
18
|
+
import { schema } from "@dxos/protocols/proto";
|
|
19
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-identity.ts";
|
|
20
|
+
var handleAuthChallenge = async (failedResponse, identity) => {
|
|
21
|
+
invariant(failedResponse.status === 401, void 0, {
|
|
22
|
+
F: __dxlog_file,
|
|
23
|
+
L: 21,
|
|
24
|
+
S: void 0,
|
|
25
|
+
A: [
|
|
26
|
+
"failedResponse.status === 401",
|
|
27
|
+
""
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
const headerValue = failedResponse.headers.get("Www-Authenticate");
|
|
31
|
+
invariant(headerValue?.startsWith("VerifiablePresentation challenge="), void 0, {
|
|
32
|
+
F: __dxlog_file,
|
|
33
|
+
L: 24,
|
|
34
|
+
S: void 0,
|
|
35
|
+
A: [
|
|
36
|
+
"headerValue?.startsWith('VerifiablePresentation challenge=')",
|
|
37
|
+
""
|
|
38
|
+
]
|
|
39
|
+
});
|
|
40
|
+
const challenge = headerValue?.slice("VerifiablePresentation challenge=".length);
|
|
41
|
+
invariant(challenge, void 0, {
|
|
42
|
+
F: __dxlog_file,
|
|
43
|
+
L: 27,
|
|
44
|
+
S: void 0,
|
|
45
|
+
A: [
|
|
46
|
+
"challenge",
|
|
47
|
+
""
|
|
48
|
+
]
|
|
49
|
+
});
|
|
50
|
+
const presentation = await identity.presentCredentials({
|
|
51
|
+
challenge: Buffer.from(challenge, "base64")
|
|
52
|
+
});
|
|
53
|
+
return schema.getCodecForType("dxos.halo.credentials.Presentation").encode(presentation);
|
|
54
|
+
};
|
|
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";
|
|
20
63
|
import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
21
|
-
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
var
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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;
|
|
30
78
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
type: this.getPayloadType(message)
|
|
39
|
-
};
|
|
40
|
-
}
|
|
79
|
+
get info() {
|
|
80
|
+
return {
|
|
81
|
+
open: this.isOpen,
|
|
82
|
+
identity: this._identity.identityKey,
|
|
83
|
+
device: this._identity.peerKey
|
|
84
|
+
};
|
|
41
85
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
invariant(message.payload, void 0, {
|
|
47
|
-
F: __dxlog_file,
|
|
48
|
-
L: 40,
|
|
86
|
+
send(message) {
|
|
87
|
+
invariant2(this._ws, void 0, {
|
|
88
|
+
F: __dxlog_file2,
|
|
89
|
+
L: 48,
|
|
49
90
|
S: this,
|
|
50
91
|
A: [
|
|
51
|
-
"
|
|
92
|
+
"this._ws",
|
|
52
93
|
""
|
|
53
94
|
]
|
|
54
95
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
L: 46,
|
|
96
|
+
log("sending...", {
|
|
97
|
+
peerKey: this._identity.peerKey,
|
|
98
|
+
payload: protocol.getPayloadType(message)
|
|
99
|
+
}, {
|
|
100
|
+
F: __dxlog_file2,
|
|
101
|
+
L: 49,
|
|
62
102
|
S: this,
|
|
63
|
-
|
|
64
|
-
"bufWkt.anyIs(message.payload, type)",
|
|
65
|
-
"`Unexpected payload type: ${payloadTypename}}`"
|
|
66
|
-
]
|
|
103
|
+
C: (f, a) => f(...a)
|
|
67
104
|
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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,
|
|
72
222
|
S: this,
|
|
73
223
|
A: [
|
|
74
|
-
"
|
|
75
|
-
"
|
|
224
|
+
"this._ws",
|
|
225
|
+
""
|
|
76
226
|
]
|
|
77
227
|
});
|
|
78
|
-
|
|
228
|
+
scheduleTaskInterval(this._ctx, async () => {
|
|
229
|
+
this._ws?.send("__ping__");
|
|
230
|
+
}, SIGNAL_KEEPALIVE_INTERVAL);
|
|
231
|
+
this._ws.send("__ping__");
|
|
232
|
+
this._rescheduleHeartbeatTimeout();
|
|
79
233
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
getPayloadType(message) {
|
|
84
|
-
if (!message.payload) {
|
|
85
|
-
return void 0;
|
|
234
|
+
_rescheduleHeartbeatTimeout() {
|
|
235
|
+
if (!this.isOpen) {
|
|
236
|
+
return;
|
|
86
237
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
* Create a packed message.
|
|
92
|
-
*/
|
|
93
|
-
createMessage(type, { source, target, payload, serviceId }) {
|
|
94
|
-
return buf.create(MessageSchema, {
|
|
95
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
96
|
-
source,
|
|
97
|
-
target,
|
|
98
|
-
serviceId,
|
|
99
|
-
payload: payload ? bufWkt.anyPack(type, buf.create(type, payload)) : void 0
|
|
238
|
+
void this._inactivityTimeoutCtx?.dispose();
|
|
239
|
+
this._inactivityTimeoutCtx = new Context(void 0, {
|
|
240
|
+
F: __dxlog_file2,
|
|
241
|
+
L: 137
|
|
100
242
|
});
|
|
243
|
+
scheduleTask(this._inactivityTimeoutCtx, () => {
|
|
244
|
+
if (this.isOpen) {
|
|
245
|
+
this._callbacks.onRestartRequired();
|
|
246
|
+
}
|
|
247
|
+
}, 2 * SIGNAL_KEEPALIVE_INTERVAL);
|
|
101
248
|
}
|
|
102
249
|
};
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
if (data instanceof Blob) {
|
|
108
|
-
return new Uint8Array(await data.arrayBuffer());
|
|
109
|
-
}
|
|
110
|
-
throw new Error(`Unexpected datatype: ${data}`);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// packages/core/mesh/edge-client/src/defs.ts
|
|
114
|
-
var protocol = new Protocol([
|
|
115
|
-
SwarmRequestSchema,
|
|
116
|
-
SwarmResponseSchema,
|
|
117
|
-
TextMessageSchema,
|
|
118
|
-
AnySchema
|
|
119
|
-
]);
|
|
250
|
+
_ts_decorate([
|
|
251
|
+
logInfo
|
|
252
|
+
], EdgeWsConnection.prototype, "info", null);
|
|
120
253
|
|
|
121
254
|
// packages/core/mesh/edge-client/src/errors.ts
|
|
122
|
-
var
|
|
255
|
+
var EdgeConnectionClosedError = class extends Error {
|
|
123
256
|
constructor() {
|
|
124
|
-
super("
|
|
257
|
+
super("Edge connection closed.");
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var EdgeIdentityChangedError = class extends Error {
|
|
261
|
+
constructor() {
|
|
262
|
+
super("Edge identity changed.");
|
|
125
263
|
}
|
|
126
264
|
};
|
|
127
265
|
|
|
128
266
|
// packages/core/mesh/edge-client/src/persistent-lifecycle.ts
|
|
129
267
|
import { DeferredTask, sleep, synchronized } from "@dxos/async";
|
|
130
|
-
import { cancelWithContext, LifecycleState, Resource } from "@dxos/context";
|
|
268
|
+
import { cancelWithContext, LifecycleState, Resource as Resource2 } from "@dxos/context";
|
|
131
269
|
import { warnAfterTimeout } from "@dxos/debug";
|
|
132
|
-
import { log } from "@dxos/log";
|
|
133
|
-
function
|
|
270
|
+
import { log as log2 } from "@dxos/log";
|
|
271
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
134
272
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
135
273
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
136
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;
|
|
137
275
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
138
276
|
}
|
|
139
|
-
var
|
|
277
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/persistent-lifecycle.ts";
|
|
140
278
|
var INIT_RESTART_DELAY = 100;
|
|
141
279
|
var DEFAULT_MAX_RESTART_DELAY = 5e3;
|
|
142
|
-
var PersistentLifecycle = class extends
|
|
280
|
+
var PersistentLifecycle = class extends Resource2 {
|
|
143
281
|
constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
|
|
144
282
|
super();
|
|
283
|
+
this._currentContext = void 0;
|
|
145
284
|
this._restartTask = void 0;
|
|
146
285
|
this._restartAfter = 0;
|
|
147
286
|
this._start = start;
|
|
@@ -154,53 +293,71 @@ var PersistentLifecycle = class extends Resource {
|
|
|
154
293
|
try {
|
|
155
294
|
await this._restart();
|
|
156
295
|
} catch (err) {
|
|
157
|
-
|
|
296
|
+
log2.warn("Restart failed", {
|
|
158
297
|
err
|
|
159
298
|
}, {
|
|
160
|
-
F:
|
|
161
|
-
L:
|
|
299
|
+
F: __dxlog_file3,
|
|
300
|
+
L: 65,
|
|
162
301
|
S: this,
|
|
163
302
|
C: (f, a) => f(...a)
|
|
164
303
|
});
|
|
165
304
|
this._restartTask?.schedule();
|
|
166
305
|
}
|
|
167
306
|
});
|
|
168
|
-
await this._start().catch((err) => {
|
|
169
|
-
|
|
307
|
+
this._currentContext = await this._start().catch((err) => {
|
|
308
|
+
log2.warn("Start failed", {
|
|
170
309
|
err
|
|
171
310
|
}, {
|
|
172
|
-
F:
|
|
173
|
-
L:
|
|
311
|
+
F: __dxlog_file3,
|
|
312
|
+
L: 70,
|
|
174
313
|
S: this,
|
|
175
314
|
C: (f, a) => f(...a)
|
|
176
315
|
});
|
|
177
316
|
this._restartTask?.schedule();
|
|
317
|
+
return void 0;
|
|
178
318
|
});
|
|
179
319
|
}
|
|
180
320
|
async _close() {
|
|
181
321
|
await this._restartTask?.join();
|
|
182
|
-
await this.
|
|
322
|
+
await this._stopCurrentContext();
|
|
183
323
|
this._restartTask = void 0;
|
|
184
324
|
}
|
|
185
325
|
async _restart() {
|
|
186
|
-
|
|
326
|
+
log2(`restarting in ${this._restartAfter}ms`, {
|
|
187
327
|
state: this._lifecycleState
|
|
188
328
|
}, {
|
|
189
|
-
F:
|
|
190
|
-
L:
|
|
329
|
+
F: __dxlog_file3,
|
|
330
|
+
L: 83,
|
|
191
331
|
S: this,
|
|
192
332
|
C: (f, a) => f(...a)
|
|
193
333
|
});
|
|
194
|
-
await this.
|
|
334
|
+
await this._stopCurrentContext();
|
|
195
335
|
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
196
336
|
return;
|
|
197
337
|
}
|
|
198
338
|
await cancelWithContext(this._ctx, sleep(this._restartAfter));
|
|
199
339
|
this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
|
|
200
|
-
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
|
+
});
|
|
201
343
|
this._restartAfter = 0;
|
|
202
344
|
await this._onRestart?.();
|
|
203
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
|
+
}
|
|
204
361
|
/**
|
|
205
362
|
* Scheduling restart should be done from outside.
|
|
206
363
|
*/
|
|
@@ -211,77 +368,121 @@ var PersistentLifecycle = class extends Resource {
|
|
|
211
368
|
this._restartTask.schedule();
|
|
212
369
|
}
|
|
213
370
|
};
|
|
214
|
-
|
|
371
|
+
_ts_decorate2([
|
|
215
372
|
synchronized
|
|
216
373
|
], PersistentLifecycle.prototype, "_open", null);
|
|
217
|
-
|
|
374
|
+
_ts_decorate2([
|
|
218
375
|
synchronized
|
|
219
376
|
], PersistentLifecycle.prototype, "scheduleRestart", null);
|
|
220
377
|
|
|
378
|
+
// packages/core/mesh/edge-client/src/utils.ts
|
|
379
|
+
var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
|
|
380
|
+
const isSecure = baseUrl.startsWith("https") || baseUrl.startsWith("wss");
|
|
381
|
+
const url = new URL(baseUrl);
|
|
382
|
+
url.protocol = protocol2 + (isSecure ? "s" : "");
|
|
383
|
+
return url.toString();
|
|
384
|
+
};
|
|
385
|
+
|
|
221
386
|
// packages/core/mesh/edge-client/src/edge-client.ts
|
|
222
|
-
|
|
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";
|
|
223
394
|
var DEFAULT_TIMEOUT = 1e4;
|
|
224
|
-
var
|
|
225
|
-
|
|
226
|
-
constructor(_identityKey, _peerKey, _config) {
|
|
395
|
+
var EdgeClient = class extends Resource3 {
|
|
396
|
+
constructor(_identity, _config) {
|
|
227
397
|
super();
|
|
228
|
-
this.
|
|
229
|
-
this._peerKey = _peerKey;
|
|
398
|
+
this._identity = _identity;
|
|
230
399
|
this._config = _config;
|
|
231
|
-
this.reconnect = new Event();
|
|
232
400
|
this._persistentLifecycle = new PersistentLifecycle({
|
|
233
|
-
start: async () => this.
|
|
234
|
-
stop: async () => this.
|
|
235
|
-
onRestart: async () => this.reconnect.emit()
|
|
401
|
+
start: async () => this._connect(),
|
|
402
|
+
stop: async (state) => this._disconnect(state)
|
|
236
403
|
});
|
|
237
|
-
this.
|
|
404
|
+
this._messageListeners = /* @__PURE__ */ new Set();
|
|
405
|
+
this._reconnectListeners = /* @__PURE__ */ new Set();
|
|
406
|
+
this._currentConnection = void 0;
|
|
238
407
|
this._ready = new Trigger();
|
|
239
|
-
this.
|
|
240
|
-
this.
|
|
241
|
-
this.
|
|
242
|
-
this._protocol = this._config.protocol ?? protocol;
|
|
408
|
+
this._isActive = (connection) => connection === this._currentConnection;
|
|
409
|
+
this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
|
|
410
|
+
this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
|
|
243
411
|
}
|
|
244
|
-
// TODO(burdon): Attach logging.
|
|
245
412
|
get info() {
|
|
246
413
|
return {
|
|
247
414
|
open: this.isOpen,
|
|
248
|
-
identity: this.
|
|
249
|
-
device: this.
|
|
415
|
+
identity: this._identity.identityKey,
|
|
416
|
+
device: this._identity.peerKey
|
|
250
417
|
};
|
|
251
418
|
}
|
|
419
|
+
get isConnected() {
|
|
420
|
+
return Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED;
|
|
421
|
+
}
|
|
252
422
|
get identityKey() {
|
|
253
|
-
return this.
|
|
423
|
+
return this._identity.identityKey;
|
|
254
424
|
}
|
|
255
425
|
get peerKey() {
|
|
256
|
-
return this.
|
|
426
|
+
return this._identity.peerKey;
|
|
257
427
|
}
|
|
258
|
-
setIdentity(
|
|
259
|
-
this.
|
|
260
|
-
|
|
261
|
-
|
|
428
|
+
setIdentity(identity) {
|
|
429
|
+
if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
|
|
430
|
+
log3("Edge identity changed", {
|
|
431
|
+
identity,
|
|
432
|
+
oldIdentity: this._identity
|
|
433
|
+
}, {
|
|
434
|
+
F: __dxlog_file4,
|
|
435
|
+
L: 95,
|
|
436
|
+
S: this,
|
|
437
|
+
C: (f, a) => f(...a)
|
|
438
|
+
});
|
|
439
|
+
this._identity = identity;
|
|
440
|
+
this._closeCurrentConnection(new EdgeIdentityChangedError());
|
|
441
|
+
this._persistentLifecycle.scheduleRestart();
|
|
442
|
+
}
|
|
262
443
|
}
|
|
263
|
-
|
|
264
|
-
this.
|
|
265
|
-
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);
|
|
266
467
|
}
|
|
267
468
|
/**
|
|
268
469
|
* Open connection to messaging service.
|
|
269
470
|
*/
|
|
270
471
|
async _open() {
|
|
271
|
-
|
|
472
|
+
log3("opening...", {
|
|
272
473
|
info: this.info
|
|
273
474
|
}, {
|
|
274
|
-
F:
|
|
275
|
-
L:
|
|
475
|
+
F: __dxlog_file4,
|
|
476
|
+
L: 129,
|
|
276
477
|
S: this,
|
|
277
478
|
C: (f, a) => f(...a)
|
|
278
479
|
});
|
|
279
480
|
this._persistentLifecycle.open().catch((err) => {
|
|
280
|
-
|
|
481
|
+
log3.warn("Error while opening connection", {
|
|
281
482
|
err
|
|
282
483
|
}, {
|
|
283
|
-
F:
|
|
284
|
-
L:
|
|
484
|
+
F: __dxlog_file4,
|
|
485
|
+
L: 131,
|
|
285
486
|
S: this,
|
|
286
487
|
C: (f, a) => f(...a)
|
|
287
488
|
});
|
|
@@ -291,127 +492,140 @@ var EdgeClient = class extends Resource2 {
|
|
|
291
492
|
* Close connection and free resources.
|
|
292
493
|
*/
|
|
293
494
|
async _close() {
|
|
294
|
-
|
|
295
|
-
peerKey: this.
|
|
495
|
+
log3("closing...", {
|
|
496
|
+
peerKey: this._identity.peerKey
|
|
296
497
|
}, {
|
|
297
|
-
F:
|
|
298
|
-
L:
|
|
498
|
+
F: __dxlog_file4,
|
|
499
|
+
L: 139,
|
|
299
500
|
S: this,
|
|
300
501
|
C: (f, a) => f(...a)
|
|
301
502
|
});
|
|
503
|
+
this._closeCurrentConnection();
|
|
302
504
|
await this._persistentLifecycle.close();
|
|
303
505
|
}
|
|
304
|
-
async
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
};
|
|
316
|
-
this._ws.onclose = () => {
|
|
317
|
-
log2("closed", this.info, {
|
|
318
|
-
F: __dxlog_file3,
|
|
319
|
-
L: 124,
|
|
320
|
-
S: this,
|
|
321
|
-
C: (f, a) => f(...a)
|
|
322
|
-
});
|
|
323
|
-
this._persistentLifecycle.scheduleRestart();
|
|
324
|
-
};
|
|
325
|
-
this._ws.onerror = (event) => {
|
|
326
|
-
log2.warn("EdgeClient socket error", {
|
|
327
|
-
error: event.error,
|
|
328
|
-
info: event.message
|
|
329
|
-
}, {
|
|
330
|
-
F: __dxlog_file3,
|
|
331
|
-
L: 128,
|
|
332
|
-
S: this,
|
|
333
|
-
C: (f, a) => f(...a)
|
|
334
|
-
});
|
|
335
|
-
this._persistentLifecycle.scheduleRestart();
|
|
336
|
-
};
|
|
337
|
-
this._ws.onmessage = async (event) => {
|
|
338
|
-
if (event.data === "__pong__") {
|
|
339
|
-
this._onHeartbeat();
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
const data = await toUint8Array(event.data);
|
|
343
|
-
const message = buf2.fromBinary(MessageSchema2, data);
|
|
344
|
-
log2("received", {
|
|
345
|
-
peerKey: this._peerKey,
|
|
346
|
-
payload: protocol.getPayloadType(message)
|
|
347
|
-
}, {
|
|
348
|
-
F: __dxlog_file3,
|
|
349
|
-
L: 141,
|
|
506
|
+
async _connect() {
|
|
507
|
+
if (this._ctx.disposed) {
|
|
508
|
+
return void 0;
|
|
509
|
+
}
|
|
510
|
+
const identity = this._identity;
|
|
511
|
+
const path = `/ws/${identity.identityKey}/${identity.peerKey}`;
|
|
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,
|
|
350
517
|
S: this,
|
|
351
518
|
C: (f, a) => f(...a)
|
|
352
519
|
});
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
520
|
+
return void 0;
|
|
521
|
+
}
|
|
522
|
+
const restartRequired = new Trigger();
|
|
523
|
+
const url = new URL(path, this._baseWsUrl);
|
|
524
|
+
log3("Opening websocket", {
|
|
525
|
+
url: url.toString(),
|
|
526
|
+
protocolHeader
|
|
527
|
+
}, {
|
|
528
|
+
F: __dxlog_file4,
|
|
529
|
+
L: 159,
|
|
530
|
+
S: this,
|
|
531
|
+
C: (f, a) => f(...a)
|
|
532
|
+
});
|
|
533
|
+
const connection = new EdgeWsConnection(identity, {
|
|
534
|
+
url,
|
|
535
|
+
protocolHeader
|
|
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
|
+
});
|
|
368
577
|
}
|
|
369
578
|
}
|
|
370
|
-
};
|
|
371
|
-
await this._ready.wait({
|
|
372
|
-
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
373
579
|
});
|
|
374
|
-
this.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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;
|
|
383
589
|
}
|
|
384
|
-
async
|
|
385
|
-
|
|
386
|
-
|
|
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
|
+
}
|
|
387
612
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
} catch (err) {
|
|
404
|
-
if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
|
|
405
|
-
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
|
+
});
|
|
406
628
|
}
|
|
407
|
-
log2.warn("Error closing websocket", {
|
|
408
|
-
err
|
|
409
|
-
}, {
|
|
410
|
-
F: __dxlog_file3,
|
|
411
|
-
L: 190,
|
|
412
|
-
S: this,
|
|
413
|
-
C: (f, a) => f(...a)
|
|
414
|
-
});
|
|
415
629
|
}
|
|
416
630
|
}
|
|
417
631
|
/**
|
|
@@ -420,57 +634,359 @@ var EdgeClient = class extends Resource2 {
|
|
|
420
634
|
*/
|
|
421
635
|
async send(message) {
|
|
422
636
|
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
637
|
+
log3("waiting for websocket to become ready", void 0, {
|
|
638
|
+
F: __dxlog_file4,
|
|
639
|
+
L: 239,
|
|
640
|
+
S: this,
|
|
641
|
+
C: (f, a) => f(...a)
|
|
642
|
+
});
|
|
423
643
|
await this._ready.wait({
|
|
424
644
|
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
425
645
|
});
|
|
426
646
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
647
|
+
if (!this._currentConnection) {
|
|
648
|
+
throw new EdgeConnectionClosedError();
|
|
649
|
+
}
|
|
650
|
+
if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
|
|
651
|
+
throw new EdgeIdentityChangedError();
|
|
652
|
+
}
|
|
653
|
+
this._currentConnection.send(message);
|
|
654
|
+
}
|
|
655
|
+
async _createAuthHeader(path) {
|
|
656
|
+
const httpUrl = new URL(path, this._baseHttpUrl);
|
|
657
|
+
httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
|
|
658
|
+
const response = await fetch(httpUrl, {
|
|
659
|
+
method: "GET"
|
|
435
660
|
});
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
661
|
+
if (response.status === 401) {
|
|
662
|
+
return encodePresentationWsAuthHeader(await handleAuthChallenge(response, this._identity));
|
|
663
|
+
} else {
|
|
664
|
+
log3.warn("no auth challenge from edge", {
|
|
665
|
+
status: response.status,
|
|
666
|
+
statusText: response.statusText
|
|
667
|
+
}, {
|
|
668
|
+
F: __dxlog_file4,
|
|
669
|
+
L: 264,
|
|
670
|
+
S: this,
|
|
671
|
+
C: (f, a) => f(...a)
|
|
672
|
+
});
|
|
673
|
+
return void 0;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
_ts_decorate3([
|
|
678
|
+
logInfo2
|
|
679
|
+
], EdgeClient.prototype, "info", null);
|
|
680
|
+
var encodePresentationWsAuthHeader = (encodedPresentation) => {
|
|
681
|
+
const encodedToken = Buffer.from(encodedPresentation).toString("base64").replace(/=*$/, "").replaceAll("/", "|");
|
|
682
|
+
return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// packages/core/mesh/edge-client/src/auth.ts
|
|
686
|
+
import { createCredential, signPresentation } from "@dxos/credentials";
|
|
687
|
+
import { Keyring } from "@dxos/keyring";
|
|
688
|
+
import { PublicKey } from "@dxos/keys";
|
|
689
|
+
var createDeviceEdgeIdentity = async (signer, key) => {
|
|
690
|
+
return {
|
|
691
|
+
identityKey: key.toHex(),
|
|
692
|
+
peerKey: key.toHex(),
|
|
693
|
+
presentCredentials: async ({ challenge }) => {
|
|
694
|
+
return signPresentation({
|
|
695
|
+
presentation: {
|
|
696
|
+
credentials: [
|
|
697
|
+
// Verifier requires at least one credential in the presentation to establish the subject.
|
|
698
|
+
await createCredential({
|
|
699
|
+
assertion: {
|
|
700
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
701
|
+
},
|
|
702
|
+
issuer: key,
|
|
703
|
+
subject: key,
|
|
704
|
+
signer
|
|
705
|
+
})
|
|
706
|
+
]
|
|
707
|
+
},
|
|
708
|
+
signer,
|
|
709
|
+
signerKey: key,
|
|
710
|
+
nonce: challenge
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
};
|
|
715
|
+
var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
|
|
716
|
+
const credentialsToSign = credentials.length > 0 ? credentials : [
|
|
717
|
+
await createCredential({
|
|
718
|
+
assertion: {
|
|
719
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
720
|
+
},
|
|
721
|
+
issuer: identityKey,
|
|
722
|
+
subject: identityKey,
|
|
723
|
+
signer,
|
|
724
|
+
chain,
|
|
725
|
+
signingKey: peerKey
|
|
726
|
+
})
|
|
727
|
+
];
|
|
728
|
+
return {
|
|
729
|
+
identityKey: identityKey.toHex(),
|
|
730
|
+
peerKey: peerKey.toHex(),
|
|
731
|
+
presentCredentials: async ({ challenge }) => {
|
|
732
|
+
return signPresentation({
|
|
733
|
+
presentation: {
|
|
734
|
+
credentials: credentialsToSign
|
|
735
|
+
},
|
|
736
|
+
signer,
|
|
737
|
+
nonce: challenge,
|
|
738
|
+
signerKey: peerKey,
|
|
739
|
+
chain
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
};
|
|
744
|
+
var createEphemeralEdgeIdentity = async () => {
|
|
745
|
+
const keyring = new Keyring();
|
|
746
|
+
const key = await keyring.createKey();
|
|
747
|
+
return createDeviceEdgeIdentity(keyring, key);
|
|
748
|
+
};
|
|
749
|
+
var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
|
|
750
|
+
const deviceAdmission = await createCredential({
|
|
751
|
+
assertion: {
|
|
752
|
+
"@type": "dxos.halo.credentials.AuthorizedDevice",
|
|
753
|
+
deviceKey,
|
|
754
|
+
identityKey
|
|
755
|
+
},
|
|
756
|
+
issuer: identityKey,
|
|
757
|
+
subject: deviceKey,
|
|
758
|
+
signer
|
|
759
|
+
});
|
|
760
|
+
return createChainEdgeIdentity(signer, identityKey, deviceKey, {
|
|
761
|
+
credential: deviceAdmission
|
|
762
|
+
}, [
|
|
763
|
+
await createCredential({
|
|
764
|
+
assertion: {
|
|
765
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
766
|
+
},
|
|
767
|
+
issuer: identityKey,
|
|
768
|
+
subject: identityKey,
|
|
769
|
+
signer
|
|
770
|
+
})
|
|
771
|
+
]);
|
|
772
|
+
};
|
|
773
|
+
var createStubEdgeIdentity = () => {
|
|
774
|
+
const identityKey = PublicKey.random();
|
|
775
|
+
const deviceKey = PublicKey.random();
|
|
776
|
+
return {
|
|
777
|
+
identityKey: identityKey.toHex(),
|
|
778
|
+
peerKey: deviceKey.toHex(),
|
|
779
|
+
presentCredentials: async () => {
|
|
780
|
+
throw new Error("Stub identity does not support authentication.");
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
// packages/core/mesh/edge-client/src/edge-http-client.ts
|
|
786
|
+
import { sleep as sleep2 } from "@dxos/async";
|
|
787
|
+
import { Context as Context2 } from "@dxos/context";
|
|
788
|
+
import { log as log4 } from "@dxos/log";
|
|
789
|
+
import { EdgeCallFailedError, EdgeAuthChallengeError } from "@dxos/protocols";
|
|
790
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
|
|
791
|
+
var DEFAULT_RETRY_TIMEOUT = 1500;
|
|
792
|
+
var DEFAULT_RETRY_JITTER = 500;
|
|
793
|
+
var DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
794
|
+
var EdgeHttpClient = class {
|
|
795
|
+
constructor(baseUrl) {
|
|
796
|
+
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
|
|
797
|
+
log4("created", {
|
|
798
|
+
url: this._baseUrl
|
|
799
|
+
}, {
|
|
800
|
+
F: __dxlog_file5,
|
|
801
|
+
L: 42,
|
|
439
802
|
S: this,
|
|
440
|
-
|
|
441
|
-
"!message.source || message.source.peerKey === this._peerKey",
|
|
442
|
-
""
|
|
443
|
-
]
|
|
803
|
+
C: (f, a) => f(...a)
|
|
444
804
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
805
|
+
}
|
|
806
|
+
setIdentity(identity) {
|
|
807
|
+
if (this._edgeIdentity?.identityKey !== identity.identityKey || this._edgeIdentity?.peerKey !== identity.peerKey) {
|
|
808
|
+
this._edgeIdentity = identity;
|
|
809
|
+
this._authHeader = void 0;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
createAgent(body, args) {
|
|
813
|
+
return this._call("/agents/create", {
|
|
814
|
+
...args,
|
|
815
|
+
method: "POST",
|
|
816
|
+
body
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
getAgentStatus(request, args) {
|
|
820
|
+
return this._call(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, {
|
|
821
|
+
...args,
|
|
822
|
+
method: "GET"
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
getCredentialsForNotarization(spaceId, args) {
|
|
826
|
+
return this._call(`/spaces/${spaceId}/notarization`, {
|
|
827
|
+
...args,
|
|
828
|
+
method: "GET"
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
async notarizeCredentials(spaceId, body, args) {
|
|
832
|
+
await this._call(`/spaces/${spaceId}/notarization`, {
|
|
833
|
+
...args,
|
|
834
|
+
body,
|
|
835
|
+
method: "POST"
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
async joinSpaceByInvitation(spaceId, body, args) {
|
|
839
|
+
return this._call(`/spaces/${spaceId}/join`, {
|
|
840
|
+
...args,
|
|
841
|
+
body,
|
|
842
|
+
method: "POST"
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
async recoverIdentity(body, args) {
|
|
846
|
+
return this._call("/identity/recover", {
|
|
847
|
+
...args,
|
|
848
|
+
body,
|
|
849
|
+
method: "POST"
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
async _call(path, args) {
|
|
853
|
+
const requestContext = args.context ?? new Context2(void 0, {
|
|
854
|
+
F: __dxlog_file5,
|
|
855
|
+
L: 91
|
|
856
|
+
});
|
|
857
|
+
const shouldRetry = createRetryHandler(args);
|
|
858
|
+
const url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
|
|
859
|
+
log4.info("call", {
|
|
860
|
+
method: args.method,
|
|
861
|
+
path,
|
|
862
|
+
request: args.body
|
|
448
863
|
}, {
|
|
449
|
-
F:
|
|
450
|
-
L:
|
|
864
|
+
F: __dxlog_file5,
|
|
865
|
+
L: 95,
|
|
451
866
|
S: this,
|
|
452
867
|
C: (f, a) => f(...a)
|
|
453
868
|
});
|
|
454
|
-
|
|
869
|
+
let handledAuth = false;
|
|
870
|
+
let authHeader = this._authHeader;
|
|
871
|
+
while (true) {
|
|
872
|
+
let processingError;
|
|
873
|
+
let retryAfterHeaderValue = Number.NaN;
|
|
874
|
+
try {
|
|
875
|
+
const request = createRequest(args, authHeader);
|
|
876
|
+
const response = await fetch(url, request);
|
|
877
|
+
retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
|
|
878
|
+
if (response.ok) {
|
|
879
|
+
const body = await response.json();
|
|
880
|
+
if (body.success) {
|
|
881
|
+
return body.data;
|
|
882
|
+
}
|
|
883
|
+
log4.info("unsuccessful edge response", {
|
|
884
|
+
path,
|
|
885
|
+
body
|
|
886
|
+
}, {
|
|
887
|
+
F: __dxlog_file5,
|
|
888
|
+
L: 114,
|
|
889
|
+
S: this,
|
|
890
|
+
C: (f, a) => f(...a)
|
|
891
|
+
});
|
|
892
|
+
if (body.errorData?.type === "auth_challenge" && typeof body.errorData?.challenge === "string") {
|
|
893
|
+
processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
|
|
894
|
+
} else {
|
|
895
|
+
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
896
|
+
}
|
|
897
|
+
} else if (response.status === 401 && !handledAuth) {
|
|
898
|
+
authHeader = await this._handleUnauthorized(response);
|
|
899
|
+
handledAuth = true;
|
|
900
|
+
continue;
|
|
901
|
+
} else {
|
|
902
|
+
processingError = EdgeCallFailedError.fromHttpFailure(response);
|
|
903
|
+
}
|
|
904
|
+
} catch (error) {
|
|
905
|
+
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
906
|
+
}
|
|
907
|
+
if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
|
|
908
|
+
log4.info("retrying edge request", {
|
|
909
|
+
path,
|
|
910
|
+
processingError
|
|
911
|
+
}, {
|
|
912
|
+
F: __dxlog_file5,
|
|
913
|
+
L: 133,
|
|
914
|
+
S: this,
|
|
915
|
+
C: (f, a) => f(...a)
|
|
916
|
+
});
|
|
917
|
+
} else {
|
|
918
|
+
throw processingError;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
455
921
|
}
|
|
456
|
-
|
|
457
|
-
if (this.
|
|
458
|
-
|
|
922
|
+
async _handleUnauthorized(response) {
|
|
923
|
+
if (!this._edgeIdentity) {
|
|
924
|
+
log4.warn("edge unauthorized response received before identity was set", void 0, {
|
|
925
|
+
F: __dxlog_file5,
|
|
926
|
+
L: 142,
|
|
927
|
+
S: this,
|
|
928
|
+
C: (f, a) => f(...a)
|
|
929
|
+
});
|
|
930
|
+
throw EdgeCallFailedError.fromHttpFailure(response);
|
|
459
931
|
}
|
|
460
|
-
|
|
461
|
-
this.
|
|
462
|
-
|
|
463
|
-
|
|
932
|
+
const challenge = await handleAuthChallenge(response, this._edgeIdentity);
|
|
933
|
+
this._authHeader = encodeAuthHeader(challenge);
|
|
934
|
+
log4("auth header updated", void 0, {
|
|
935
|
+
F: __dxlog_file5,
|
|
936
|
+
L: 147,
|
|
937
|
+
S: this,
|
|
938
|
+
C: (f, a) => f(...a)
|
|
464
939
|
});
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
940
|
+
return this._authHeader;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
var createRetryHandler = (args) => {
|
|
944
|
+
if (!args.retry || args.retry.count < 1) {
|
|
945
|
+
return async () => false;
|
|
468
946
|
}
|
|
947
|
+
let retries = 0;
|
|
948
|
+
const maxRetries = args.retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
|
|
949
|
+
const baseTimeout = args.retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
|
|
950
|
+
const jitter = args.retry.jitter ?? DEFAULT_RETRY_JITTER;
|
|
951
|
+
return async (ctx, retryAfter) => {
|
|
952
|
+
if (++retries > maxRetries || ctx.disposed) {
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
if (retryAfter) {
|
|
956
|
+
await sleep2(retryAfter);
|
|
957
|
+
} else {
|
|
958
|
+
const timeout = baseTimeout + Math.random() * jitter;
|
|
959
|
+
await sleep2(timeout);
|
|
960
|
+
}
|
|
961
|
+
return true;
|
|
962
|
+
};
|
|
963
|
+
};
|
|
964
|
+
var createRequest = (args, authHeader) => {
|
|
965
|
+
return {
|
|
966
|
+
method: args.method,
|
|
967
|
+
body: args.body && JSON.stringify(args.body),
|
|
968
|
+
headers: authHeader ? {
|
|
969
|
+
Authorization: authHeader
|
|
970
|
+
} : void 0
|
|
971
|
+
};
|
|
972
|
+
};
|
|
973
|
+
var encodeAuthHeader = (challenge) => {
|
|
974
|
+
const encodedChallenge = Buffer.from(challenge).toString("base64");
|
|
975
|
+
return `VerifiablePresentation pb;base64,${encodedChallenge}`;
|
|
469
976
|
};
|
|
470
977
|
export {
|
|
471
978
|
EdgeClient,
|
|
979
|
+
EdgeConnectionClosedError,
|
|
980
|
+
EdgeHttpClient,
|
|
981
|
+
EdgeIdentityChangedError,
|
|
472
982
|
Protocol,
|
|
983
|
+
createChainEdgeIdentity,
|
|
984
|
+
createDeviceEdgeIdentity,
|
|
985
|
+
createEphemeralEdgeIdentity,
|
|
986
|
+
createStubEdgeIdentity,
|
|
987
|
+
createTestHaloEdgeIdentity,
|
|
473
988
|
getTypename,
|
|
989
|
+
handleAuthChallenge,
|
|
474
990
|
protocol,
|
|
475
991
|
toUint8Array
|
|
476
992
|
};
|