@dxos/edge-client 0.8.1 → 0.8.2-main.2f9c567
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-TKYUZ5ZK.mjs +302 -0
- package/dist/lib/browser/chunk-TKYUZ5ZK.mjs.map +7 -0
- package/dist/lib/browser/edge-ws-muxer.mjs +11 -0
- package/dist/lib/browser/edge-ws-muxer.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +93 -49
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +32 -20
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/chunk-ZOL3YSDR.cjs +322 -0
- package/dist/lib/node/chunk-ZOL3YSDR.cjs.map +7 -0
- package/dist/lib/node/edge-ws-muxer.cjs +33 -0
- package/dist/lib/node/edge-ws-muxer.cjs.map +7 -0
- package/dist/lib/node/index.cjs +105 -61
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +32 -21
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node-esm/chunk-25HGRGNZ.mjs +304 -0
- package/dist/lib/node-esm/chunk-25HGRGNZ.mjs.map +7 -0
- package/dist/lib/node-esm/edge-ws-muxer.mjs +12 -0
- package/dist/lib/node-esm/edge-ws-muxer.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +93 -49
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +32 -20
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/edge-client.d.ts +7 -2
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts +1 -0
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/src/edge-ws-muxer.d.ts +35 -0
- package/dist/types/src/edge-ws-muxer.d.ts.map +1 -0
- package/dist/types/src/edge-ws-muxer.test.d.ts +2 -0
- package/dist/types/src/edge-ws-muxer.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-utils.d.ts +6 -2
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/package.json +19 -14
- package/src/edge-client.test.ts +5 -4
- package/src/edge-client.ts +16 -8
- package/src/edge-ws-connection.ts +36 -18
- package/src/edge-ws-muxer.test.ts +55 -0
- package/src/edge-ws-muxer.ts +217 -0
- package/src/index.ts +1 -0
- package/src/testing/test-utils.ts +33 -26
- package/dist/lib/browser/chunk-ZWJXA37R.mjs +0 -113
- package/dist/lib/browser/chunk-ZWJXA37R.mjs.map +0 -7
- package/dist/lib/node/chunk-ANV2HBEH.cjs +0 -136
- package/dist/lib/node/chunk-ANV2HBEH.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-HNVT57AU.mjs +0 -115
- package/dist/lib/node-esm/chunk-HNVT57AU.mjs.map +0 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-client.d.ts","sourceRoot":"","sources":["../../../src/edge-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"edge-client.d.ts","sourceRoot":"","sources":["../../../src/edge-client.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EAAE,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAEzD,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAC;AAGxE,OAAO,EAAE,KAAK,YAAY,EAAuB,MAAM,iBAAiB,CAAC;AAGzE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AACzD,MAAM,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC;AAE3C,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAAC,SAAS,CAAC;IACzD,aAAa,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,IAAI,IAAI,GAAG,CAAC;IAChB,IAAI,WAAW,IAAI,MAAM,CAAC;IAC1B,IAAI,OAAO,IAAI,MAAM,CAAC;IACtB,IAAI,MAAM,IAAI,OAAO,CAAC;IACtB,IAAI,MAAM,IAAI,UAAU,CAAC;IACzB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1C,SAAS,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI,CAAC;IACjD,aAAa,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,QAAS,YAAW,cAAc;IAgB9D,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhB1B,SAAgB,aAAa,oBAA2B;IAExD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAGlC;IAEH,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA8B;IAChE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgC;IACpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,kBAAkB,CAAC,CAA+B;IAC1D,OAAO,CAAC,MAAM,CAAiB;gBAGrB,SAAS,EAAE,YAAY,EACd,OAAO,EAAE,eAAe;IAO3C,IACW,IAAI;;;;;MAOd;IAED,IAAI,MAAM,IAAI,UAAU,CAIvB;IAED,IAAI,WAAW,WAEd;IAED,IAAI,OAAO,WAEV;IAED,WAAW,CAAC,QAAQ,EAAE,YAAY;IAS3B,SAAS,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IAKhD,aAAa,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAkBtD;;OAEG;cACsB,KAAK;IAO9B;;OAEG;cACsB,MAAM;YAMjB,QAAQ;YA2DR,WAAW;IAKzB,OAAO,CAAC,uBAAuB;IAO/B,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,sBAAsB;IAU9B;;;OAGG;IACU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;YAoBpC,iBAAiB;IAY/B,OAAO,CAAC,SAAS,CAA4E;CAC9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-ws-connection.d.ts","sourceRoot":"","sources":["../../../src/edge-ws-connection.ts"],"names":[],"mappings":"AAOA,OAAO,EAAW,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"edge-ws-connection.d.ts","sourceRoot":"","sources":["../../../src/edge-ws-connection.ts"],"names":[],"mappings":"AAOA,OAAO,EAAW,QAAQ,EAAE,MAAM,eAAe,CAAC;AAKlD,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,4CAA4C,CAAC;AAGzF,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAOpD,MAAM,MAAM,yBAAyB,GAAG;IACtC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,iBAAiB,EAAE,MAAM,IAAI,CAAC;CAC/B,CAAC;AAEF,qBAAa,gBAAiB,SAAQ,QAAQ;IAM1C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAP7B,OAAO,CAAC,qBAAqB,CAAsB;IACnD,OAAO,CAAC,GAAG,CAAwB;IACnC,OAAO,CAAC,QAAQ,CAA6B;gBAG1B,SAAS,EAAE,YAAY,EACvB,eAAe,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,EACtD,UAAU,EAAE,yBAAyB;IAKxD,IACW,IAAI;;;;MAMd;IAEM,IAAI,CAAC,OAAO,EAAE,OAAO;cAoBH,KAAK;cA+DL,MAAM;IAgB/B,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,2BAA2B;CAiBpC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
2
|
+
/**
|
|
3
|
+
* https://developers.cloudflare.com/durable-objects/platform/limits/
|
|
4
|
+
*/
|
|
5
|
+
export declare const CLOUDFLARE_MESSAGE_MAX_BYTES: number;
|
|
6
|
+
export declare const CLOUDFLARE_RPC_MAX_BYTES: number;
|
|
7
|
+
export declare class WebSocketMuxer {
|
|
8
|
+
private readonly _ws;
|
|
9
|
+
private readonly _inMessageAccumulator;
|
|
10
|
+
private readonly _outMessageChunks;
|
|
11
|
+
private readonly _outMessageChannelByService;
|
|
12
|
+
private _sendTimeout;
|
|
13
|
+
private readonly _maxChunkLength;
|
|
14
|
+
constructor(_ws: WebSocketCompat, config?: {
|
|
15
|
+
maxChunkLength: number;
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Resolves when all the message chunks get enqueued for sending.
|
|
19
|
+
*/
|
|
20
|
+
send(message: Message): Promise<void>;
|
|
21
|
+
receiveData(data: Uint8Array): Message | undefined;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
private _sendChunkedMessages;
|
|
24
|
+
private _resolveChannel;
|
|
25
|
+
}
|
|
26
|
+
type WebSocketCompat = {
|
|
27
|
+
readonly readyState: number;
|
|
28
|
+
/**
|
|
29
|
+
* Not available in workerd.
|
|
30
|
+
*/
|
|
31
|
+
bufferedAmount?: number;
|
|
32
|
+
send(message: (ArrayBuffer | ArrayBufferView) | string): void;
|
|
33
|
+
};
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=edge-ws-muxer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-ws-muxer.d.ts","sourceRoot":"","sources":["../../../src/edge-ws-muxer.ts"],"names":[],"mappings":"AAOA,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,4CAA4C,CAAC;AAkBzF;;GAEG;AACH,eAAO,MAAM,4BAA4B,QAAc,CAAC;AACxD,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AAMzD,qBAAa,cAAc;IAUvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IATtB,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA+B;IACrE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqC;IACvE,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAA6B;IAEzE,OAAO,CAAC,YAAY,CAAkB;IAEtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAGtB,GAAG,EAAE,eAAe,EACrC,MAAM,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE;IAKrC;;OAEG;IACU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAgD3C,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,SAAS;IAuBlD,OAAO;IAad,OAAO,CAAC,oBAAoB;IA0C5B,OAAO,CAAC,eAAe;CAWxB;AAED,KAAK,eAAe,GAAG;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,OAAO,EAAE,CAAC,WAAW,GAAG,eAAe,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;CAC/D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-ws-muxer.test.d.ts","sourceRoot":"","sources":["../../../src/edge-ws-muxer.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,4CAA4C,CAAC;AAE3D,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,4CAA4C,CAAC;AAE3D,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import WebSocket from 'isomorphic-ws';
|
|
2
2
|
import { Trigger } from '@dxos/async';
|
|
3
3
|
import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
4
|
+
import { WebSocketMuxer } from '../edge-ws-muxer';
|
|
4
5
|
export declare const DEFAULT_PORT = 8080;
|
|
5
6
|
type TestEdgeWsServerParams = {
|
|
6
7
|
admitConnection?: Trigger;
|
|
@@ -13,9 +14,12 @@ export declare const createTestEdgeWsServer: (port?: number, params?: TestEdgeWs
|
|
|
13
14
|
messageSourceLog: any[];
|
|
14
15
|
endpoint: string;
|
|
15
16
|
cleanup: () => void;
|
|
16
|
-
currentConnection: () =>
|
|
17
|
+
currentConnection: () => {
|
|
18
|
+
ws: WebSocket;
|
|
19
|
+
muxer: WebSocketMuxer;
|
|
20
|
+
} | undefined;
|
|
17
21
|
sendResponseMessage: (request: Message, responsePayload: Uint8Array) => void;
|
|
18
|
-
sendMessage: (msg: Message) => void
|
|
22
|
+
sendMessage: (msg: Message) => Promise<void>;
|
|
19
23
|
closeConnection: () => Promise<void>;
|
|
20
24
|
}>;
|
|
21
25
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../../src/testing/test-utils.ts"],"names":[],"mappings":"AAIA,OAAO,SAAS,MAAM,eAAe,CAAC;AAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../../src/testing/test-utils.ts"],"names":[],"mappings":"AAIA,OAAO,SAAS,MAAM,eAAe,CAAC;AAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAItC,OAAO,EAAoC,KAAK,OAAO,EAAE,MAAM,4CAA4C,CAAC;AAG5G,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC,KAAK,sBAAsB,GAAG;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,GAAG,CAAC;IAC9C,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;CACpE,CAAC;AAEF,eAAO,MAAM,sBAAsB,2BAAwC,sBAAsB;;;;;;;YAOzE,SAAS;eAAS,cAAc;;mCAwErC,OAAO,mBAAmB,UAAU;uBA1BhC,OAAO;;EAS7B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/edge-client",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2-main.2f9c567",
|
|
4
4
|
"description": "EDGE Client",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"browser": "./dist/lib/browser/index.mjs",
|
|
15
15
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
16
16
|
},
|
|
17
|
+
"./muxer": {
|
|
18
|
+
"types": "./dist/types/src/edge-ws-muxer.d.ts",
|
|
19
|
+
"browser": "./dist/lib/browser/edge-ws-muxer.mjs",
|
|
20
|
+
"node": "./dist/lib/node-esm/edge-ws-muxer.mjs"
|
|
21
|
+
},
|
|
17
22
|
"./testing": {
|
|
18
23
|
"types": "./dist/types/src/testing/index.d.ts",
|
|
19
24
|
"browser": "./dist/lib/browser/testing/index.mjs",
|
|
@@ -36,21 +41,21 @@
|
|
|
36
41
|
"dependencies": {
|
|
37
42
|
"isomorphic-ws": "^5.0.0",
|
|
38
43
|
"ws": "^8.14.2",
|
|
39
|
-
"@dxos/async": "0.8.
|
|
40
|
-
"@dxos/context": "0.8.
|
|
41
|
-
"@dxos/credentials": "0.8.
|
|
42
|
-
"@dxos/crypto": "0.8.
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/node-std": "0.8.
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
44
|
+
"@dxos/async": "0.8.2-main.2f9c567",
|
|
45
|
+
"@dxos/context": "0.8.2-main.2f9c567",
|
|
46
|
+
"@dxos/credentials": "0.8.2-main.2f9c567",
|
|
47
|
+
"@dxos/crypto": "0.8.2-main.2f9c567",
|
|
48
|
+
"@dxos/invariant": "0.8.2-main.2f9c567",
|
|
49
|
+
"@dxos/debug": "0.8.2-main.2f9c567",
|
|
50
|
+
"@dxos/keyring": "0.8.2-main.2f9c567",
|
|
51
|
+
"@dxos/keys": "0.8.2-main.2f9c567",
|
|
52
|
+
"@dxos/node-std": "0.8.2-main.2f9c567",
|
|
53
|
+
"@dxos/protocols": "0.8.2-main.2f9c567",
|
|
54
|
+
"@dxos/util": "0.8.2-main.2f9c567",
|
|
55
|
+
"@dxos/log": "0.8.2-main.2f9c567"
|
|
51
56
|
},
|
|
52
57
|
"devDependencies": {
|
|
53
|
-
"@dxos/test-utils": "0.8.
|
|
58
|
+
"@dxos/test-utils": "0.8.2-main.2f9c567"
|
|
54
59
|
},
|
|
55
60
|
"publishConfig": {
|
|
56
61
|
"access": "public"
|
package/src/edge-client.test.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { describe, expect, onTestFinished, test } from 'vitest';
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
8
|
import { Keyring } from '@dxos/keyring';
|
|
9
9
|
import { TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
10
|
+
import { EdgeStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
11
|
import { openAndClose } from '@dxos/test-utils';
|
|
11
12
|
|
|
12
13
|
import { createEphemeralEdgeIdentity, createTestHaloEdgeIdentity } from './auth';
|
|
@@ -40,17 +41,17 @@ describe('EdgeClient', () => {
|
|
|
40
41
|
|
|
41
42
|
const { client } = await openNewClient(endpoint);
|
|
42
43
|
|
|
43
|
-
expect(client.
|
|
44
|
+
expect(client.status).toBe(EdgeStatus.NOT_CONNECTED);
|
|
44
45
|
admitConnection.wake();
|
|
45
|
-
await expect.poll(() => client.
|
|
46
|
+
await expect.poll(() => client.status).toBe(EdgeStatus.CONNECTED);
|
|
46
47
|
|
|
47
48
|
admitConnection.reset();
|
|
48
49
|
await closeConnection();
|
|
49
50
|
expect(client.isOpen).is.true;
|
|
50
|
-
await expect.poll(() => client.
|
|
51
|
+
await expect.poll(() => client.status).toBe(EdgeStatus.NOT_CONNECTED);
|
|
51
52
|
|
|
52
53
|
admitConnection.wake();
|
|
53
|
-
await expect.poll(() => client.
|
|
54
|
+
await expect.poll(() => client.status).toBe(EdgeStatus.CONNECTED);
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
test('set identity reconnects', async () => {
|
package/src/edge-client.ts
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Trigger, scheduleMicroTask, TriggerState, PersistentLifecycle } from '@dxos/async';
|
|
5
|
+
import { Trigger, scheduleMicroTask, TriggerState, PersistentLifecycle, Event } from '@dxos/async';
|
|
6
6
|
import { Resource, type Lifecycle } from '@dxos/context';
|
|
7
7
|
import { log, logInfo } from '@dxos/log';
|
|
8
8
|
import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
9
|
+
import { EdgeStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
9
10
|
|
|
10
11
|
import { protocol } from './defs';
|
|
11
12
|
import { type EdgeIdentity, handleAuthChallenge } from './edge-identity';
|
|
@@ -20,11 +21,12 @@ export type MessageListener = (message: Message) => void;
|
|
|
20
21
|
export type ReconnectListener = () => void;
|
|
21
22
|
|
|
22
23
|
export interface EdgeConnection extends Required<Lifecycle> {
|
|
24
|
+
statusChanged: Event<EdgeStatus>;
|
|
23
25
|
get info(): any;
|
|
24
26
|
get identityKey(): string;
|
|
25
27
|
get peerKey(): string;
|
|
26
28
|
get isOpen(): boolean;
|
|
27
|
-
get
|
|
29
|
+
get status(): EdgeStatus;
|
|
28
30
|
setIdentity(identity: EdgeIdentity): void;
|
|
29
31
|
onMessage(listener: MessageListener): () => void;
|
|
30
32
|
onReconnected(listener: ReconnectListener): () => void;
|
|
@@ -45,6 +47,8 @@ export type MessengerConfig = {
|
|
|
45
47
|
* - Dispatches connection state and message notifications.
|
|
46
48
|
*/
|
|
47
49
|
export class EdgeClient extends Resource implements EdgeConnection {
|
|
50
|
+
public readonly statusChanged = new Event<EdgeStatus>();
|
|
51
|
+
|
|
48
52
|
private readonly _persistentLifecycle = new PersistentLifecycle<EdgeWsConnection>({
|
|
49
53
|
start: async () => this._connect(),
|
|
50
54
|
stop: async (state: EdgeWsConnection) => this._disconnect(state),
|
|
@@ -52,10 +56,8 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
52
56
|
|
|
53
57
|
private readonly _messageListeners = new Set<MessageListener>();
|
|
54
58
|
private readonly _reconnectListeners = new Set<ReconnectListener>();
|
|
55
|
-
|
|
56
59
|
private readonly _baseWsUrl: string;
|
|
57
60
|
private readonly _baseHttpUrl: string;
|
|
58
|
-
|
|
59
61
|
private _currentConnection?: EdgeWsConnection = undefined;
|
|
60
62
|
private _ready = new Trigger();
|
|
61
63
|
|
|
@@ -72,13 +74,16 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
72
74
|
public get info() {
|
|
73
75
|
return {
|
|
74
76
|
open: this.isOpen,
|
|
77
|
+
status: this.status,
|
|
75
78
|
identity: this._identity.identityKey,
|
|
76
79
|
device: this._identity.peerKey,
|
|
77
80
|
};
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
get
|
|
81
|
-
return Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED
|
|
83
|
+
get status(): EdgeStatus {
|
|
84
|
+
return Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED
|
|
85
|
+
? EdgeStatus.CONNECTED
|
|
86
|
+
: EdgeStatus.NOT_CONNECTED;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
get identityKey() {
|
|
@@ -94,7 +99,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
94
99
|
log('Edge identity changed', { identity, oldIdentity: this._identity });
|
|
95
100
|
this._identity = identity;
|
|
96
101
|
this._closeCurrentConnection(new EdgeIdentityChangedError());
|
|
97
|
-
this._persistentLifecycle.scheduleRestart();
|
|
102
|
+
void this._persistentLifecycle.scheduleRestart();
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
105
|
|
|
@@ -171,7 +176,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
171
176
|
onRestartRequired: () => {
|
|
172
177
|
if (this._isActive(connection)) {
|
|
173
178
|
this._closeCurrentConnection();
|
|
174
|
-
this._persistentLifecycle.scheduleRestart();
|
|
179
|
+
void this._persistentLifecycle.scheduleRestart();
|
|
175
180
|
} else {
|
|
176
181
|
log.verbose('restart requested by inactive connection');
|
|
177
182
|
}
|
|
@@ -201,15 +206,18 @@ export class EdgeClient extends Resource implements EdgeConnection {
|
|
|
201
206
|
|
|
202
207
|
private async _disconnect(state: EdgeWsConnection) {
|
|
203
208
|
await state.close();
|
|
209
|
+
this.statusChanged.emit(this.status);
|
|
204
210
|
}
|
|
205
211
|
|
|
206
212
|
private _closeCurrentConnection(error: Error = new EdgeConnectionClosedError()) {
|
|
207
213
|
this._currentConnection = undefined;
|
|
208
214
|
this._ready.throw(error);
|
|
209
215
|
this._ready.reset();
|
|
216
|
+
this.statusChanged.emit(this.status);
|
|
210
217
|
}
|
|
211
218
|
|
|
212
219
|
private _notifyReconnected() {
|
|
220
|
+
this.statusChanged.emit(this.status);
|
|
213
221
|
for (const listener of this._reconnectListeners) {
|
|
214
222
|
try {
|
|
215
223
|
listener();
|
|
@@ -8,21 +8,18 @@ import { scheduleTask, scheduleTaskInterval } from '@dxos/async';
|
|
|
8
8
|
import { Context, Resource } from '@dxos/context';
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
10
10
|
import { log, logInfo } from '@dxos/log';
|
|
11
|
+
import { EdgeWebsocketProtocol } from '@dxos/protocols';
|
|
11
12
|
import { buf } from '@dxos/protocols/buf';
|
|
12
13
|
import { MessageSchema, type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
13
14
|
|
|
14
15
|
import { protocol } from './defs';
|
|
15
16
|
import { type EdgeIdentity } from './edge-identity';
|
|
17
|
+
import { CLOUDFLARE_MESSAGE_MAX_BYTES, WebSocketMuxer } from './edge-ws-muxer';
|
|
16
18
|
import { toUint8Array } from './protocol';
|
|
17
19
|
|
|
18
20
|
const SIGNAL_KEEPALIVE_INTERVAL = 4_000;
|
|
19
21
|
const SIGNAL_KEEPALIVE_TIMEOUT = 12_000;
|
|
20
22
|
|
|
21
|
-
/**
|
|
22
|
-
* 1MB websocket message limit: https://developers.cloudflare.com/durable-objects/platform/limits/
|
|
23
|
-
*/
|
|
24
|
-
const CLOUDFLARE_MESSAGE_LENGTH_LIMIT = 1024 * 1024;
|
|
25
|
-
|
|
26
23
|
export type EdgeWsConnectionCallbacks = {
|
|
27
24
|
onConnected: () => void;
|
|
28
25
|
onMessage: (message: Message) => void;
|
|
@@ -32,6 +29,7 @@ export type EdgeWsConnectionCallbacks = {
|
|
|
32
29
|
export class EdgeWsConnection extends Resource {
|
|
33
30
|
private _inactivityTimeoutCtx: Context | undefined;
|
|
34
31
|
private _ws: WebSocket | undefined;
|
|
32
|
+
private _wsMuxer: WebSocketMuxer | undefined;
|
|
35
33
|
|
|
36
34
|
constructor(
|
|
37
35
|
private readonly _identity: EdgeIdentity,
|
|
@@ -52,24 +50,34 @@ export class EdgeWsConnection extends Resource {
|
|
|
52
50
|
|
|
53
51
|
public send(message: Message) {
|
|
54
52
|
invariant(this._ws);
|
|
53
|
+
invariant(this._wsMuxer);
|
|
55
54
|
log('sending...', { peerKey: this._identity.peerKey, payload: protocol.getPayloadType(message) });
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
if (this._ws?.protocol.includes(EdgeWebsocketProtocol.V0)) {
|
|
56
|
+
const binary = buf.toBinary(MessageSchema, message);
|
|
57
|
+
if (binary.length > CLOUDFLARE_MESSAGE_MAX_BYTES) {
|
|
58
|
+
log.error('Message dropped because it was too large (>1MB).', {
|
|
59
|
+
byteLength: binary.byteLength,
|
|
60
|
+
serviceId: message.serviceId,
|
|
61
|
+
payload: protocol.getPayloadType(message),
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this._ws.send(binary);
|
|
66
|
+
} else {
|
|
67
|
+
this._wsMuxer.send(message).catch((e) => log.catch(e));
|
|
64
68
|
}
|
|
65
|
-
this._ws.send(encoded);
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
protected override async _open() {
|
|
72
|
+
const baseProtocols = [...Object.values(EdgeWebsocketProtocol)];
|
|
69
73
|
this._ws = new WebSocket(
|
|
70
74
|
this._connectionInfo.url.toString(),
|
|
71
|
-
this._connectionInfo.protocolHeader
|
|
75
|
+
this._connectionInfo.protocolHeader
|
|
76
|
+
? [...baseProtocols, this._connectionInfo.protocolHeader]
|
|
77
|
+
: [...baseProtocols],
|
|
72
78
|
);
|
|
79
|
+
const muxer = new WebSocketMuxer(this._ws);
|
|
80
|
+
this._wsMuxer = muxer;
|
|
73
81
|
|
|
74
82
|
this._ws.onopen = () => {
|
|
75
83
|
if (this.isOpen) {
|
|
@@ -84,6 +92,7 @@ export class EdgeWsConnection extends Resource {
|
|
|
84
92
|
if (this.isOpen) {
|
|
85
93
|
log.warn('disconnected while being open', { code: event.code, reason: event.reason });
|
|
86
94
|
this._callbacks.onRestartRequired();
|
|
95
|
+
muxer.destroy();
|
|
87
96
|
}
|
|
88
97
|
};
|
|
89
98
|
this._ws.onerror = (event) => {
|
|
@@ -106,9 +115,16 @@ export class EdgeWsConnection extends Resource {
|
|
|
106
115
|
this._rescheduleHeartbeatTimeout();
|
|
107
116
|
return;
|
|
108
117
|
}
|
|
109
|
-
const
|
|
110
|
-
if (this.isOpen) {
|
|
111
|
-
|
|
118
|
+
const bytes = await toUint8Array(event.data);
|
|
119
|
+
if (!this.isOpen) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const message = this._ws?.protocol?.includes(EdgeWebsocketProtocol.V0)
|
|
124
|
+
? buf.fromBinary(MessageSchema, bytes)
|
|
125
|
+
: muxer.receiveData(bytes);
|
|
126
|
+
|
|
127
|
+
if (message) {
|
|
112
128
|
log('received', { from: message.source, payload: protocol.getPayloadType(message) });
|
|
113
129
|
this._callbacks.onMessage(message);
|
|
114
130
|
}
|
|
@@ -121,6 +137,8 @@ export class EdgeWsConnection extends Resource {
|
|
|
121
137
|
try {
|
|
122
138
|
this._ws?.close();
|
|
123
139
|
this._ws = undefined;
|
|
140
|
+
this._wsMuxer?.destroy();
|
|
141
|
+
this._wsMuxer = undefined;
|
|
124
142
|
} catch (err) {
|
|
125
143
|
if (err instanceof Error && err.message.includes('WebSocket is closed before the connection is established.')) {
|
|
126
144
|
return;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { bufWkt } from '@dxos/protocols/buf';
|
|
8
|
+
import { TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
9
|
+
|
|
10
|
+
import { protocol } from './defs';
|
|
11
|
+
import type { EdgeIdentity } from './edge-identity';
|
|
12
|
+
import { WebSocketMuxer } from './edge-ws-muxer';
|
|
13
|
+
|
|
14
|
+
const MAX_CHUNK_LENGTH = 16;
|
|
15
|
+
|
|
16
|
+
describe('WebSocketMuxerTest', () => {
|
|
17
|
+
test('basic message reassembly', async () => {
|
|
18
|
+
const { muxer: muxer1, sentMessages } = await createMuxer();
|
|
19
|
+
const { muxer: muxer2 } = await createMuxer();
|
|
20
|
+
const content = 'A'.repeat(MAX_CHUNK_LENGTH);
|
|
21
|
+
await muxer1.send(textMessage(content));
|
|
22
|
+
|
|
23
|
+
expect(sentMessages.length).toBeGreaterThan(1);
|
|
24
|
+
for (const chunk of sentMessages.slice(0, -1)) {
|
|
25
|
+
expect(muxer2.receiveData(chunk)).toBeUndefined();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const reassembledMessage = muxer2.receiveData(sentMessages.at(-1)!)!;
|
|
29
|
+
expect(reassembledMessage).toBeDefined();
|
|
30
|
+
|
|
31
|
+
const decoded = bufWkt.anyUnpack(reassembledMessage.payload!, TextMessageSchema);
|
|
32
|
+
expect(decoded?.message).toStrictEqual(content);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const textMessage = (message: string, source?: EdgeIdentity) =>
|
|
37
|
+
protocol.createMessage(TextMessageSchema, {
|
|
38
|
+
source: source && { peerKey: source.peerKey, identityKey: source.identityKey },
|
|
39
|
+
serviceId: 'test-service',
|
|
40
|
+
payload: { message },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const createMuxer = async () => {
|
|
44
|
+
const sentMessages: Uint8Array[] = [];
|
|
45
|
+
const muxer = new WebSocketMuxer(
|
|
46
|
+
{
|
|
47
|
+
readyState: 1,
|
|
48
|
+
send: (message: string) => {
|
|
49
|
+
sentMessages.push(Buffer.from(message));
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{ maxChunkLength: MAX_CHUNK_LENGTH },
|
|
53
|
+
);
|
|
54
|
+
return { muxer, sentMessages };
|
|
55
|
+
};
|