@dxos/edge-client 0.8.3 → 0.8.4-main.1068cf700f
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-VHS3XEIX.mjs → neutral/chunk-VESGVCLQ.mjs} +15 -11
- package/dist/lib/{browser/chunk-VHS3XEIX.mjs.map → neutral/chunk-VESGVCLQ.mjs.map} +3 -3
- package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs +1 -1
- package/dist/lib/{browser → neutral}/index.mjs +694 -354
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/{browser → neutral}/testing/index.mjs +61 -16
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/edge-client.d.ts +15 -15
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts +59 -31
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.test.d.ts +2 -0
- package/dist/types/src/edge-http-client.test.d.ts.map +1 -0
- package/dist/types/src/edge-ws-connection.d.ts +20 -0
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
- package/dist/types/src/http-client.d.ts +25 -0
- package/dist/types/src/http-client.d.ts.map +1 -0
- package/dist/types/src/http-client.test.d.ts +2 -0
- package/dist/types/src/http-client.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-server.d.ts +9 -0
- package/dist/types/src/testing/test-server.d.ts.map +1 -0
- package/dist/types/src/testing/test-utils.d.ts +3 -3
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +30 -21
- package/src/edge-client.test.ts +4 -4
- package/src/edge-client.ts +73 -42
- package/src/edge-http-client.test.ts +22 -0
- package/src/edge-http-client.ts +368 -152
- package/src/edge-ws-connection.ts +129 -8
- package/src/edge-ws-muxer.ts +1 -1
- package/src/http-client.test.ts +58 -0
- package/src/http-client.ts +77 -0
- package/src/index.ts +4 -3
- package/src/testing/index.ts +1 -0
- package/src/testing/test-server.ts +45 -0
- package/src/testing/test-utils.ts +9 -9
- package/src/websocket.test.ts +1 -1
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node/chunk-XNHBUTNB.cjs +0 -317
- package/dist/lib/node/chunk-XNHBUTNB.cjs.map +0 -7
- package/dist/lib/node/edge-ws-muxer.cjs +0 -33
- package/dist/lib/node/edge-ws-muxer.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -1060
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/testing/index.cjs +0 -169
- package/dist/lib/node/testing/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-HGQUUFIJ.mjs +0 -299
- package/dist/lib/node-esm/chunk-HGQUUFIJ.mjs.map +0 -7
- package/dist/lib/node-esm/edge-ws-muxer.mjs +0 -12
- package/dist/lib/node-esm/edge-ws-muxer.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -1035
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -141
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- /package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs.map +0 -0
package/package.json
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/edge-client",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4-main.1068cf700f",
|
|
4
4
|
"description": "EDGE Client",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
7
11
|
"license": "MIT",
|
|
8
12
|
"author": "DXOS.org",
|
|
9
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
10
14
|
"type": "module",
|
|
11
15
|
"exports": {
|
|
12
16
|
".": {
|
|
17
|
+
"source": "./src/index.ts",
|
|
13
18
|
"types": "./dist/types/src/index.d.ts",
|
|
14
|
-
"
|
|
15
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
19
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
16
20
|
},
|
|
17
21
|
"./muxer": {
|
|
22
|
+
"source": "./src/edge-ws-muxer.ts",
|
|
18
23
|
"types": "./dist/types/src/edge-ws-muxer.d.ts",
|
|
19
|
-
"
|
|
20
|
-
"node": "./dist/lib/node-esm/edge-ws-muxer.mjs"
|
|
24
|
+
"default": "./dist/lib/neutral/edge-ws-muxer.mjs"
|
|
21
25
|
},
|
|
22
26
|
"./testing": {
|
|
27
|
+
"source": "./src/testing/index.ts",
|
|
23
28
|
"types": "./dist/types/src/testing/index.d.ts",
|
|
24
|
-
"
|
|
25
|
-
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
29
|
+
"default": "./dist/lib/neutral/testing/index.mjs"
|
|
26
30
|
}
|
|
27
31
|
},
|
|
28
32
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -39,23 +43,28 @@
|
|
|
39
43
|
"README.md"
|
|
40
44
|
],
|
|
41
45
|
"dependencies": {
|
|
46
|
+
"@effect/platform": "0.94.4",
|
|
42
47
|
"isomorphic-ws": "^5.0.0",
|
|
43
48
|
"ws": "^8.14.2",
|
|
44
|
-
"@dxos/async": "0.8.
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/credentials": "0.8.
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
49
|
+
"@dxos/async": "0.8.4-main.1068cf700f",
|
|
50
|
+
"@dxos/debug": "0.8.4-main.1068cf700f",
|
|
51
|
+
"@dxos/credentials": "0.8.4-main.1068cf700f",
|
|
52
|
+
"@dxos/context": "0.8.4-main.1068cf700f",
|
|
53
|
+
"@dxos/crypto": "0.8.4-main.1068cf700f",
|
|
54
|
+
"@dxos/keyring": "0.8.4-main.1068cf700f",
|
|
55
|
+
"@dxos/effect": "0.8.4-main.1068cf700f",
|
|
56
|
+
"@dxos/invariant": "0.8.4-main.1068cf700f",
|
|
57
|
+
"@dxos/keys": "0.8.4-main.1068cf700f",
|
|
58
|
+
"@dxos/log": "0.8.4-main.1068cf700f",
|
|
59
|
+
"@dxos/node-std": "0.8.4-main.1068cf700f",
|
|
60
|
+
"@dxos/protocols": "0.8.4-main.1068cf700f",
|
|
61
|
+
"@dxos/util": "0.8.4-main.1068cf700f"
|
|
56
62
|
},
|
|
57
63
|
"devDependencies": {
|
|
58
|
-
"@dxos/test-utils": "0.8.
|
|
64
|
+
"@dxos/test-utils": "0.8.4-main.1068cf700f"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"effect": "3.19.16"
|
|
59
68
|
},
|
|
60
69
|
"publishConfig": {
|
|
61
70
|
"access": "public"
|
package/src/edge-client.test.ts
CHANGED
|
@@ -41,17 +41,17 @@ describe('EdgeClient', () => {
|
|
|
41
41
|
|
|
42
42
|
const { client } = await openNewClient(endpoint);
|
|
43
43
|
|
|
44
|
-
expect(client.status).toBe(EdgeStatus.NOT_CONNECTED);
|
|
44
|
+
expect(client.status.state).toBe(EdgeStatus.ConnectionState.NOT_CONNECTED);
|
|
45
45
|
admitConnection.wake();
|
|
46
|
-
await expect.poll(() => client.status).toBe(EdgeStatus.CONNECTED);
|
|
46
|
+
await expect.poll(() => client.status.state).toBe(EdgeStatus.ConnectionState.CONNECTED);
|
|
47
47
|
|
|
48
48
|
admitConnection.reset();
|
|
49
49
|
await closeConnection();
|
|
50
50
|
expect(client.isOpen).is.true;
|
|
51
|
-
await expect.poll(() => client.status).toBe(EdgeStatus.NOT_CONNECTED);
|
|
51
|
+
await expect.poll(() => client.status.state).toBe(EdgeStatus.ConnectionState.NOT_CONNECTED);
|
|
52
52
|
|
|
53
53
|
admitConnection.wake();
|
|
54
|
-
await expect.poll(() => client.status).toBe(EdgeStatus.CONNECTED);
|
|
54
|
+
await expect.poll(() => client.status.state).toBe(EdgeStatus.ConnectionState.CONNECTED);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
test('set identity reconnects', async () => {
|
package/src/edge-client.ts
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
Event,
|
|
7
|
+
PersistentLifecycle,
|
|
8
|
+
Trigger,
|
|
9
|
+
TriggerState,
|
|
10
|
+
scheduleMicroTask,
|
|
11
|
+
scheduleTaskInterval,
|
|
12
|
+
} from '@dxos/async';
|
|
13
|
+
import { type Lifecycle, Resource } from '@dxos/context';
|
|
7
14
|
import { log, logInfo } from '@dxos/log';
|
|
8
15
|
import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
9
16
|
import { EdgeStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
@@ -17,9 +24,19 @@ import { getEdgeUrlWithProtocol } from './utils';
|
|
|
17
24
|
|
|
18
25
|
const DEFAULT_TIMEOUT = 10_000;
|
|
19
26
|
|
|
27
|
+
// Refresh status every second: rtt, rate counters.
|
|
28
|
+
const STATUS_REFRESH_INTERVAL = 1000;
|
|
29
|
+
|
|
20
30
|
export type MessageListener = (message: Message) => void;
|
|
21
31
|
export type ReconnectListener = () => void;
|
|
22
32
|
|
|
33
|
+
export type MessengerConfig = {
|
|
34
|
+
socketEndpoint: string;
|
|
35
|
+
timeout?: number;
|
|
36
|
+
protocol?: Protocol;
|
|
37
|
+
disableAuth?: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
23
40
|
export interface EdgeConnection extends Required<Lifecycle> {
|
|
24
41
|
statusChanged: Event<EdgeStatus>;
|
|
25
42
|
get info(): any;
|
|
@@ -28,18 +45,11 @@ export interface EdgeConnection extends Required<Lifecycle> {
|
|
|
28
45
|
get isOpen(): boolean;
|
|
29
46
|
get status(): EdgeStatus;
|
|
30
47
|
setIdentity(identity: EdgeIdentity): void;
|
|
48
|
+
send(message: Message): Promise<void>;
|
|
31
49
|
onMessage(listener: MessageListener): () => void;
|
|
32
50
|
onReconnected(listener: ReconnectListener): () => void;
|
|
33
|
-
send(message: Message): Promise<void>;
|
|
34
51
|
}
|
|
35
52
|
|
|
36
|
-
export type MessengerConfig = {
|
|
37
|
-
socketEndpoint: string;
|
|
38
|
-
timeout?: number;
|
|
39
|
-
protocol?: Protocol;
|
|
40
|
-
disableAuth?: boolean;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
53
|
/**
|
|
44
54
|
* Messenger client for EDGE:
|
|
45
55
|
* - While open, uses PersistentLifecycle to keep an open EdgeWsConnection, reconnecting on failures.
|
|
@@ -81,9 +91,18 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
get status(): EdgeStatus {
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
return {
|
|
95
|
+
state:
|
|
96
|
+
Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED
|
|
97
|
+
? EdgeStatus.ConnectionState.CONNECTED
|
|
98
|
+
: EdgeStatus.ConnectionState.NOT_CONNECTED,
|
|
99
|
+
uptime: this._currentConnection?.uptime ?? 0,
|
|
100
|
+
rtt: this._currentConnection?.rtt ?? 0,
|
|
101
|
+
rateBytesUp: this._currentConnection?.uploadRate ?? 0,
|
|
102
|
+
rateBytesDown: this._currentConnection?.downloadRate ?? 0,
|
|
103
|
+
messagesSent: this._currentConnection?.messagesSent ?? 0,
|
|
104
|
+
messagesReceived: this._currentConnection?.messagesReceived ?? 0,
|
|
105
|
+
};
|
|
87
106
|
}
|
|
88
107
|
|
|
89
108
|
get identityKey() {
|
|
@@ -94,7 +113,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
94
113
|
return this._identity.peerKey;
|
|
95
114
|
}
|
|
96
115
|
|
|
97
|
-
setIdentity(identity: EdgeIdentity)
|
|
116
|
+
setIdentity(identity: EdgeIdentity) {
|
|
98
117
|
if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
|
|
99
118
|
log('Edge identity changed', { identity, oldIdentity: this._identity });
|
|
100
119
|
this._identity = identity;
|
|
@@ -103,12 +122,36 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
103
122
|
}
|
|
104
123
|
}
|
|
105
124
|
|
|
106
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Send message.
|
|
127
|
+
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
128
|
+
*/
|
|
129
|
+
public async send(message: Message) {
|
|
130
|
+
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
131
|
+
log('waiting for websocket');
|
|
132
|
+
await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!this._currentConnection) {
|
|
136
|
+
throw new EdgeConnectionClosedError();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (
|
|
140
|
+
message.source &&
|
|
141
|
+
(message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)
|
|
142
|
+
) {
|
|
143
|
+
throw new EdgeIdentityChangedError();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this._currentConnection.send(message);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public onMessage(listener: MessageListener) {
|
|
107
150
|
this._messageListeners.add(listener);
|
|
108
151
|
return () => this._messageListeners.delete(listener);
|
|
109
152
|
}
|
|
110
153
|
|
|
111
|
-
public onReconnected(listener: () => void)
|
|
154
|
+
public onReconnected(listener: () => void) {
|
|
112
155
|
this._reconnectListeners.add(listener);
|
|
113
156
|
if (this._ready.state === TriggerState.RESOLVED) {
|
|
114
157
|
// Microtask so that listener is always called asynchronously, no matter the state of the ready trigger
|
|
@@ -123,6 +166,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
123
166
|
}
|
|
124
167
|
});
|
|
125
168
|
}
|
|
169
|
+
|
|
126
170
|
return () => this._reconnectListeners.delete(listener);
|
|
127
171
|
}
|
|
128
172
|
|
|
@@ -134,6 +178,18 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
134
178
|
this._persistentLifecycle.open().catch((err) => {
|
|
135
179
|
log.warn('Error while opening connection', { err });
|
|
136
180
|
});
|
|
181
|
+
|
|
182
|
+
// Notify about status changes (rtt, rate counters).
|
|
183
|
+
scheduleTaskInterval(
|
|
184
|
+
this._ctx,
|
|
185
|
+
async () => {
|
|
186
|
+
if (!this._currentConnection) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.statusChanged.emit(this.status);
|
|
190
|
+
},
|
|
191
|
+
STATUS_REFRESH_INTERVAL,
|
|
192
|
+
);
|
|
137
193
|
}
|
|
138
194
|
|
|
139
195
|
/**
|
|
@@ -200,7 +256,6 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
200
256
|
// Race with restartRequired so that restart is not blocked by _connect execution.
|
|
201
257
|
// Wait on ready to attempt a reconnect if it times out.
|
|
202
258
|
await Promise.race([this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT }), restartRequired]);
|
|
203
|
-
|
|
204
259
|
return connection;
|
|
205
260
|
}
|
|
206
261
|
|
|
@@ -237,30 +292,6 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
237
292
|
}
|
|
238
293
|
}
|
|
239
294
|
|
|
240
|
-
/**
|
|
241
|
-
* Send message.
|
|
242
|
-
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
243
|
-
*/
|
|
244
|
-
public async send(message: Message): Promise<void> {
|
|
245
|
-
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
246
|
-
log('waiting for websocket to become ready');
|
|
247
|
-
await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (!this._currentConnection) {
|
|
251
|
-
throw new EdgeConnectionClosedError();
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
message.source &&
|
|
256
|
-
(message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)
|
|
257
|
-
) {
|
|
258
|
-
throw new EdgeIdentityChangedError();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
this._currentConnection.send(message);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
295
|
private async _createAuthHeader(path: string): Promise<string | undefined> {
|
|
265
296
|
const httpUrl = new URL(path, this._baseHttpUrl);
|
|
266
297
|
httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), 'http');
|
|
@@ -277,7 +308,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
277
308
|
}
|
|
278
309
|
|
|
279
310
|
const encodePresentationWsAuthHeader = (encodedPresentation: Uint8Array): string => {
|
|
280
|
-
// = and / characters are not allowed in the WebSocket subprotocol header.
|
|
311
|
+
// '=' and '/' characters are not allowed in the WebSocket subprotocol header.
|
|
281
312
|
const encodedToken = Buffer.from(encodedPresentation).toString('base64').replace(/=*$/, '').replaceAll('/', '|');
|
|
282
313
|
return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
|
|
283
314
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { createEphemeralEdgeIdentity } from './auth';
|
|
8
|
+
import { EdgeHttpClient } from './edge-http-client';
|
|
9
|
+
|
|
10
|
+
// TODO(burdon): Factor out config.
|
|
11
|
+
const DEV_SERVER = 'https://edge.dxos.workers.dev';
|
|
12
|
+
|
|
13
|
+
describe.skipIf(process.env.CI)('EdgeHttpClient', () => {
|
|
14
|
+
it.skip('should get status', async ({ expect }) => {
|
|
15
|
+
const client = new EdgeHttpClient(DEV_SERVER);
|
|
16
|
+
const identity = await createEphemeralEdgeIdentity();
|
|
17
|
+
client.setIdentity(identity);
|
|
18
|
+
|
|
19
|
+
const result = await client.getStatus();
|
|
20
|
+
expect(result).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
});
|