@dxos/edge-client 0.6.12-main.5cc132e → 0.6.12-main.78ddbdf

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.
Files changed (47) hide show
  1. package/dist/lib/browser/chunk-ZWJXA37R.mjs +113 -0
  2. package/dist/lib/browser/chunk-ZWJXA37R.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +57 -157
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +122 -0
  7. package/dist/lib/browser/testing/index.mjs.map +7 -0
  8. package/dist/lib/node/chunk-ANV2HBEH.cjs +136 -0
  9. package/dist/lib/node/chunk-ANV2HBEH.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +56 -154
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +152 -0
  14. package/dist/lib/node/testing/index.cjs.map +7 -0
  15. package/dist/lib/node-esm/chunk-HNVT57AU.mjs +115 -0
  16. package/dist/lib/node-esm/chunk-HNVT57AU.mjs.map +7 -0
  17. package/dist/lib/node-esm/index.mjs +58 -157
  18. package/dist/lib/node-esm/index.mjs.map +4 -4
  19. package/dist/lib/node-esm/meta.json +1 -1
  20. package/dist/lib/node-esm/testing/index.mjs +123 -0
  21. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  22. package/dist/types/src/defs.d.ts.map +1 -1
  23. package/dist/types/src/edge-client.d.ts +3 -2
  24. package/dist/types/src/edge-client.d.ts.map +1 -1
  25. package/dist/types/src/errors.d.ts +4 -1
  26. package/dist/types/src/errors.d.ts.map +1 -1
  27. package/dist/types/src/index.d.ts +1 -0
  28. package/dist/types/src/index.d.ts.map +1 -1
  29. package/dist/types/src/protocol.d.ts +2 -2
  30. package/dist/types/src/protocol.d.ts.map +1 -1
  31. package/dist/types/src/testing/index.d.ts +2 -0
  32. package/dist/types/src/testing/index.d.ts.map +1 -0
  33. package/dist/types/src/testing/test-utils.d.ts +20 -0
  34. package/dist/types/src/testing/test-utils.d.ts.map +1 -0
  35. package/package.json +24 -13
  36. package/src/defs.ts +2 -3
  37. package/src/edge-client.test.ts +8 -5
  38. package/src/edge-client.ts +17 -8
  39. package/src/errors.ts +8 -2
  40. package/src/index.ts +1 -0
  41. package/src/protocol.ts +2 -2
  42. package/src/testing/index.ts +5 -0
  43. package/src/testing/test-utils.ts +111 -0
  44. package/src/websocket.test.ts +6 -5
  45. package/dist/types/src/test-utils.d.ts +0 -11
  46. package/dist/types/src/test-utils.d.ts.map +0 -1
  47. package/src/test-utils.ts +0 -49
@@ -0,0 +1,123 @@
1
+ import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
+ import {
3
+ protocol,
4
+ toUint8Array
5
+ } from "../chunk-HNVT57AU.mjs";
6
+
7
+ // packages/core/mesh/edge-client/src/testing/test-utils.ts
8
+ import WebSocket from "isomorphic-ws";
9
+ import { Trigger } from "@dxos/async";
10
+ import { log } from "@dxos/log";
11
+ import { buf } from "@dxos/protocols/buf";
12
+ import { MessageSchema, TextMessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
13
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/testing/test-utils.ts";
14
+ var DEFAULT_PORT = 8080;
15
+ var createTestEdgeWsServer = async (port = DEFAULT_PORT, params) => {
16
+ const server = new WebSocket.Server({
17
+ port,
18
+ verifyClient: createConnectionDelayHandler(params)
19
+ });
20
+ let connection;
21
+ const messageSink = [];
22
+ const closeTrigger = new Trigger();
23
+ const sendResponseMessage = createResponseSender(() => connection);
24
+ server.on("connection", (ws) => {
25
+ connection = ws;
26
+ ws.on("error", (err) => log.catch(err, void 0, {
27
+ F: __dxlog_file,
28
+ L: 34,
29
+ S: void 0,
30
+ C: (f, a) => f(...a)
31
+ }));
32
+ ws.on("message", async (data) => {
33
+ if (String(data) === "__ping__") {
34
+ ws.send("__pong__");
35
+ return;
36
+ }
37
+ const { request, requestPayload } = await decodeRequest(params, data);
38
+ if (params?.messageHandler) {
39
+ const responsePayload = await params.messageHandler(requestPayload);
40
+ if (responsePayload && connection) {
41
+ sendResponseMessage(request, responsePayload);
42
+ }
43
+ }
44
+ log("message", {
45
+ payload: requestPayload
46
+ }, {
47
+ F: __dxlog_file,
48
+ L: 47,
49
+ S: void 0,
50
+ C: (f, a) => f(...a)
51
+ });
52
+ messageSink.push(requestPayload);
53
+ });
54
+ ws.on("close", () => {
55
+ connection = void 0;
56
+ closeTrigger.wake();
57
+ });
58
+ });
59
+ return {
60
+ server,
61
+ messageSink,
62
+ endpoint: `ws://localhost:${port}`,
63
+ cleanup: () => server.close(),
64
+ currentConnection: () => connection,
65
+ sendResponseMessage,
66
+ closeConnection: () => {
67
+ closeTrigger.reset();
68
+ connection.close(1011);
69
+ return closeTrigger.wait();
70
+ }
71
+ };
72
+ };
73
+ var createConnectionDelayHandler = (params) => {
74
+ return (_, callback) => {
75
+ if (params?.admitConnection) {
76
+ log("delaying edge connection admission", void 0, {
77
+ F: __dxlog_file,
78
+ L: 75,
79
+ S: void 0,
80
+ C: (f, a) => f(...a)
81
+ });
82
+ void params.admitConnection.wait().then(() => {
83
+ callback(true);
84
+ log("edge connection admitted", void 0, {
85
+ F: __dxlog_file,
86
+ L: 78,
87
+ S: void 0,
88
+ C: (f, a) => f(...a)
89
+ });
90
+ });
91
+ } else {
92
+ callback(true);
93
+ }
94
+ };
95
+ };
96
+ var createResponseSender = (connection) => {
97
+ return (request, responsePayload) => {
98
+ const recipient = request.source;
99
+ connection().send(buf.toBinary(MessageSchema, buf.create(MessageSchema, {
100
+ source: {
101
+ identityKey: recipient.identityKey,
102
+ peerKey: recipient.peerKey
103
+ },
104
+ serviceId: request.serviceId,
105
+ payload: {
106
+ value: responsePayload
107
+ }
108
+ })));
109
+ };
110
+ };
111
+ var decodeRequest = async (params, data) => {
112
+ const request = buf.fromBinary(MessageSchema, await toUint8Array(data));
113
+ const requestPayload = params?.payloadDecoder ? params.payloadDecoder(request.payload.value) : protocol.getPayload(request, TextMessageSchema);
114
+ return {
115
+ request,
116
+ requestPayload
117
+ };
118
+ };
119
+ export {
120
+ DEFAULT_PORT,
121
+ createTestEdgeWsServer
122
+ };
123
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/testing/test-utils.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Trigger } from '@dxos/async';\nimport { log } from '@dxos/log';\nimport { buf } from '@dxos/protocols/buf';\nimport { MessageSchema, TextMessageSchema, type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';\n\nimport { protocol } from '../defs';\nimport { toUint8Array } from '../protocol';\n\nexport const DEFAULT_PORT = 8080;\n\ntype TestEdgeWsServerParams = {\n admitConnection?: Trigger;\n payloadDecoder?: (payload: Uint8Array) => any;\n messageHandler?: (payload: any) => Promise<Uint8Array | undefined>;\n};\n\nexport const createTestEdgeWsServer = async (port = DEFAULT_PORT, params?: TestEdgeWsServerParams) => {\n const server = new WebSocket.Server({ port, verifyClient: createConnectionDelayHandler(params) });\n\n let connection: WebSocket | undefined;\n\n const messageSink: any[] = [];\n const closeTrigger = new Trigger();\n const sendResponseMessage = createResponseSender(() => connection!);\n\n server.on('connection', (ws) => {\n connection = ws;\n ws.on('error', (err) => log.catch(err));\n ws.on('message', async (data) => {\n if (String(data) === '__ping__') {\n ws.send('__pong__');\n return;\n }\n const { request, requestPayload } = await decodeRequest(params, data);\n if (params?.messageHandler) {\n const responsePayload = await params.messageHandler(requestPayload);\n if (responsePayload && connection) {\n sendResponseMessage(request, responsePayload);\n }\n }\n log('message', { payload: requestPayload });\n messageSink.push(requestPayload);\n });\n\n ws.on('close', () => {\n connection = undefined;\n closeTrigger.wake();\n });\n });\n\n return {\n server,\n messageSink,\n endpoint: `ws://localhost:${port}`,\n cleanup: () => server.close(),\n currentConnection: () => connection,\n sendResponseMessage,\n closeConnection: () => {\n closeTrigger.reset();\n connection!.close(1011);\n return closeTrigger.wait();\n },\n };\n};\n\nconst createConnectionDelayHandler = (params: TestEdgeWsServerParams | undefined) => {\n return (_: any, callback: (admit: boolean) => void) => {\n if (params?.admitConnection) {\n log('delaying edge connection admission');\n void params.admitConnection.wait().then(() => {\n callback(true);\n log('edge connection admitted');\n });\n } else {\n callback(true);\n }\n };\n};\n\nconst createResponseSender = (connection: () => WebSocket) => {\n return (request: Message, responsePayload: Uint8Array) => {\n const recipient = request.source!;\n connection().send(\n buf.toBinary(\n MessageSchema,\n buf.create(MessageSchema, {\n source: {\n identityKey: recipient.identityKey,\n peerKey: recipient.peerKey,\n },\n serviceId: request.serviceId!,\n payload: { value: responsePayload },\n }),\n ),\n );\n };\n};\n\nconst decodeRequest = async (params: TestEdgeWsServerParams | undefined, data: any) => {\n const request = buf.fromBinary(MessageSchema, await toUint8Array(data));\n const requestPayload = params?.payloadDecoder\n ? params.payloadDecoder(request.payload!.value!)\n : protocol.getPayload(request, TextMessageSchema);\n return { request, requestPayload };\n};\n"],
5
+ "mappings": ";;;;;;;AAIA,OAAOA,eAAe;AAEtB,SAASC,eAAe;AACxB,SAASC,WAAW;AACpB,SAASC,WAAW;AACpB,SAASC,eAAeC,yBAAuC;;AAKxD,IAAMC,eAAe;AAQrB,IAAMC,yBAAyB,OAAOC,OAAOF,cAAcG,WAAAA;AAChE,QAAMC,SAAS,IAAIC,UAAUC,OAAO;IAAEJ;IAAMK,cAAcC,6BAA6BL,MAAAA;EAAQ,CAAA;AAE/F,MAAIM;AAEJ,QAAMC,cAAqB,CAAA;AAC3B,QAAMC,eAAe,IAAIC,QAAAA;AACzB,QAAMC,sBAAsBC,qBAAqB,MAAML,UAAAA;AAEvDL,SAAOW,GAAG,cAAc,CAACC,OAAAA;AACvBP,iBAAaO;AACbA,OAAGD,GAAG,SAAS,CAACE,QAAQC,IAAIC,MAAMF,KAAAA,QAAAA;;;;;;AAClCD,OAAGD,GAAG,WAAW,OAAOK,SAAAA;AACtB,UAAIC,OAAOD,IAAAA,MAAU,YAAY;AAC/BJ,WAAGM,KAAK,UAAA;AACR;MACF;AACA,YAAM,EAAEC,SAASC,eAAc,IAAK,MAAMC,cAActB,QAAQiB,IAAAA;AAChE,UAAIjB,QAAQuB,gBAAgB;AAC1B,cAAMC,kBAAkB,MAAMxB,OAAOuB,eAAeF,cAAAA;AACpD,YAAIG,mBAAmBlB,YAAY;AACjCI,8BAAoBU,SAASI,eAAAA;QAC/B;MACF;AACAT,UAAI,WAAW;QAAEU,SAASJ;MAAe,GAAA;;;;;;AACzCd,kBAAYmB,KAAKL,cAAAA;IACnB,CAAA;AAEAR,OAAGD,GAAG,SAAS,MAAA;AACbN,mBAAaqB;AACbnB,mBAAaoB,KAAI;IACnB,CAAA;EACF,CAAA;AAEA,SAAO;IACL3B;IACAM;IACAsB,UAAU,kBAAkB9B,IAAAA;IAC5B+B,SAAS,MAAM7B,OAAO8B,MAAK;IAC3BC,mBAAmB,MAAM1B;IACzBI;IACAuB,iBAAiB,MAAA;AACfzB,mBAAa0B,MAAK;AAClB5B,iBAAYyB,MAAM,IAAA;AAClB,aAAOvB,aAAa2B,KAAI;IAC1B;EACF;AACF;AAEA,IAAM9B,+BAA+B,CAACL,WAAAA;AACpC,SAAO,CAACoC,GAAQC,aAAAA;AACd,QAAIrC,QAAQsC,iBAAiB;AAC3BvB,UAAI,sCAAA,QAAA;;;;;;AACJ,WAAKf,OAAOsC,gBAAgBH,KAAI,EAAGI,KAAK,MAAA;AACtCF,iBAAS,IAAA;AACTtB,YAAI,4BAAA,QAAA;;;;;;MACN,CAAA;IACF,OAAO;AACLsB,eAAS,IAAA;IACX;EACF;AACF;AAEA,IAAM1B,uBAAuB,CAACL,eAAAA;AAC5B,SAAO,CAACc,SAAkBI,oBAAAA;AACxB,UAAMgB,YAAYpB,QAAQqB;AAC1BnC,eAAAA,EAAaa,KACXuB,IAAIC,SACFC,eACAF,IAAIG,OAAOD,eAAe;MACxBH,QAAQ;QACNK,aAAaN,UAAUM;QACvBC,SAASP,UAAUO;MACrB;MACAC,WAAW5B,QAAQ4B;MACnBvB,SAAS;QAAEwB,OAAOzB;MAAgB;IACpC,CAAA,CAAA,CAAA;EAGN;AACF;AAEA,IAAMF,gBAAgB,OAAOtB,QAA4CiB,SAAAA;AACvE,QAAMG,UAAUsB,IAAIQ,WAAWN,eAAe,MAAMO,aAAalC,IAAAA,CAAAA;AACjE,QAAMI,iBAAiBrB,QAAQoD,iBAC3BpD,OAAOoD,eAAehC,QAAQK,QAASwB,KAAK,IAC5CI,SAASC,WAAWlC,SAASmC,iBAAAA;AACjC,SAAO;IAAEnC;IAASC;EAAe;AACnC;",
6
+ "names": ["WebSocket", "Trigger", "log", "buf", "MessageSchema", "TextMessageSchema", "DEFAULT_PORT", "createTestEdgeWsServer", "port", "params", "server", "WebSocket", "Server", "verifyClient", "createConnectionDelayHandler", "connection", "messageSink", "closeTrigger", "Trigger", "sendResponseMessage", "createResponseSender", "on", "ws", "err", "log", "catch", "data", "String", "send", "request", "requestPayload", "decodeRequest", "messageHandler", "responsePayload", "payload", "push", "undefined", "wake", "endpoint", "cleanup", "close", "currentConnection", "closeConnection", "reset", "wait", "_", "callback", "admitConnection", "then", "recipient", "source", "buf", "toBinary", "MessageSchema", "create", "identityKey", "peerKey", "serviceId", "value", "fromBinary", "toUint8Array", "payloadDecoder", "protocol", "getPayload", "TextMessageSchema"]
7
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,eAAO,MAAM,QAAQ,UAAwF,CAAC"}
1
+ {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,eAAO,MAAM,QAAQ,UAA+F,CAAC"}
@@ -4,6 +4,7 @@ import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
4
4
  import { type Protocol } from './protocol';
5
5
  export type MessageListener = (message: Message) => void | Promise<void>;
6
6
  export interface EdgeConnection extends Required<Lifecycle> {
7
+ connected: Event;
7
8
  reconnect: Event;
8
9
  get info(): any;
9
10
  get identityKey(): string;
@@ -28,10 +29,10 @@ export declare class EdgeClient extends Resource implements EdgeConnection {
28
29
  private _identityKey;
29
30
  private _peerKey;
30
31
  private readonly _config;
31
- reconnect: Event<void>;
32
+ readonly reconnect: Event<void>;
33
+ readonly connected: Event<void>;
32
34
  private readonly _persistentLifecycle;
33
35
  private readonly _listeners;
34
- private readonly _protocol;
35
36
  private _ready;
36
37
  private _ws?;
37
38
  private _keepaliveCtx?;
@@ -1 +1 @@
1
- {"version":3,"file":"edge-client.d.ts","sourceRoot":"","sources":["../../../src/edge-client.ts"],"names":[],"mappings":"AAMA,OAAO,EAAW,KAAK,EAAoD,MAAM,aAAa,CAAC;AAC/F,OAAO,EAA2B,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAIlF,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,4CAA4C,CAAC;AAKzF,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAKzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAAC,SAAS,CAAC;IACzD,SAAS,EAAE,KAAK,CAAC;IAEjB,IAAI,IAAI,IAAI,GAAG,CAAC;IAChB,IAAI,WAAW,IAAI,MAAM,CAAC;IAC1B,IAAI,OAAO,IAAI,MAAM,CAAC;IACtB,IAAI,MAAM,IAAI,OAAO,CAAC;IACtB,WAAW,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI,CAAC;IACnD,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,qBAAa,UAAW,SAAQ,QAAS,YAAW,cAAc;IAgB9D,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAjBnB,SAAS,cAAe;IAC/B,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAIlC;IAEH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8B;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,GAAG,CAAC,CAAwB;IACpC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;gBAGtC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EACP,OAAO,EAAE,eAAe;IAO3C,IAAW,IAAI;;;;MAMd;IAED,IAAI,WAAW,WAEd;IAED,IAAI,OAAO,WAEV;IAED,WAAW,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;IAMvE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IAKzD;;OAEG;cACsB,KAAK;IAO9B;;OAEG;cACsB,MAAM;YAKjB,cAAc;YAqDd,eAAe;IA0B7B;;;OAGG;IACU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlD,OAAO,CAAC,YAAY;CAcrB"}
1
+ {"version":3,"file":"edge-client.d.ts","sourceRoot":"","sources":["../../../src/edge-client.ts"],"names":[],"mappings":"AAMA,OAAO,EAAW,KAAK,EAAoD,MAAM,aAAa,CAAC;AAC/F,OAAO,EAA2B,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlF,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,4CAA4C,CAAC;AAKzF,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAKzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAAC,SAAS,CAAC;IACzD,SAAS,EAAE,KAAK,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;IAEjB,IAAI,IAAI,IAAI,GAAG,CAAC;IAChB,IAAI,WAAW,IAAI,MAAM,CAAC;IAC1B,IAAI,OAAO,IAAI,MAAM,CAAC;IACtB,IAAI,MAAM,IAAI,OAAO,CAAC;IACtB,WAAW,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI,CAAC;IACnD,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,qBAAa,UAAW,SAAQ,QAAS,YAAW,cAAc;IAgB9D,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAjB1B,SAAgB,SAAS,cAAe;IACxC,SAAgB,SAAS,cAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAIlC;IAEH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8B;IACzD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,GAAG,CAAC,CAAwB;IACpC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;gBAGtC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EACP,OAAO,EAAE,eAAe;IAM3C,IAAW,IAAI;;;;MAMd;IAED,IAAI,WAAW,WAEd;IAED,IAAI,OAAO,WAEV;IAED,WAAW,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;IAMvE,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,IAAI;IAKzD;;OAEG;cACsB,KAAK;IAO9B;;OAEG;cACsB,MAAM;YAKjB,cAAc;YAsDd,eAAe;IA0B7B;;;OAGG;IACU,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,OAAO,CAAC,YAAY;CAcrB"}
@@ -1,4 +1,7 @@
1
- export declare class WebsocketClosedError extends Error {
1
+ export declare class EdgeConnectionClosedError extends Error {
2
+ constructor();
3
+ }
4
+ export declare class EdgeIdentityChangedError extends Error {
2
5
  constructor();
3
6
  }
4
7
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/errors.ts"],"names":[],"mappings":"AAIA,qBAAa,oBAAqB,SAAQ,KAAK;;CAI9C"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/errors.ts"],"names":[],"mappings":"AAIA,qBAAa,yBAA0B,SAAQ,KAAK;;CAInD;AAED,qBAAa,wBAAyB,SAAQ,KAAK;;CAIlD"}
@@ -2,4 +2,5 @@ export * from '@dxos/protocols/buf/dxos/edge/messenger_pb';
2
2
  export * from './edge-client';
3
3
  export * from './defs';
4
4
  export * from './protocol';
5
+ export * from './errors';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
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"}
@@ -1,6 +1,6 @@
1
1
  import { buf } from '@dxos/protocols/buf';
2
- import { type Message, type Peer as PeerProto } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
3
- export type PeerData = Partial<PeerProto>;
2
+ import { type Message, type PeerSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
3
+ export type PeerData = buf.MessageInitShape<typeof PeerSchema>;
4
4
  export declare const getTypename: (typeName: string) => string;
5
5
  /**
6
6
  * NOTE: The type registry should be extended with all message types.
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/protocol.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,GAAG,EAAU,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,KAAK,OAAO,EAAiB,KAAK,IAAI,IAAI,SAAS,EAAE,MAAM,4CAA4C,CAAC;AAGjH,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAE1C,eAAO,MAAM,WAAW,aAAc,MAAM,WAAsC,CAAC;AAEnF;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;gBAEjC,KAAK,EAAE,GAAG,CAAC,WAAW,EAAE;IAIpC,IAAI,YAAY,IAAI,GAAG,CAAC,QAAQ,CAE/B;IAED,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,GAAG;IAQ7B;;OAEG;IACH,UAAU,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;IAa9F;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;IASpD;;OAEG;IACH,aAAa,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,EACxC,IAAI,EAAE,IAAI,EACV,EACE,MAAM,EACN,MAAM,EACN,OAAO,EACP,SAAS,GACV,EAAE;QACD,MAAM,CAAC,EAAE,QAAQ,CAAC;QAClB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;QACpB,OAAO,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;CAUJ;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,SAAgB,GAAG,KAAG,OAAO,CAAC,UAAU,CAYhE,CAAC"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/protocol.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,GAAG,EAAU,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,KAAK,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,4CAA4C,CAAC;AAG1G,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC,OAAO,UAAU,CAAC,CAAC;AAE/D,eAAO,MAAM,WAAW,aAAc,MAAM,WAAsC,CAAC;AAEnF;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;gBAEjC,KAAK,EAAE,GAAG,CAAC,WAAW,EAAE;IAIpC,IAAI,YAAY,IAAI,GAAG,CAAC,QAAQ,CAE/B;IAED,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,GAAG;IAQ7B;;OAEG;IACH,UAAU,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;IAa9F;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;IASpD;;OAEG;IACH,aAAa,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,EACxC,IAAI,EAAE,IAAI,EACV,EACE,MAAM,EACN,MAAM,EACN,OAAO,EACP,SAAS,GACV,EAAE;QACD,MAAM,CAAC,EAAE,QAAQ,CAAC;QAClB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;QACpB,OAAO,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;CAUJ;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,SAAgB,GAAG,KAAG,OAAO,CAAC,UAAU,CAYhE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './test-utils';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/testing/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC"}
@@ -0,0 +1,20 @@
1
+ import WebSocket from 'isomorphic-ws';
2
+ import { Trigger } from '@dxos/async';
3
+ import { type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
4
+ export declare const DEFAULT_PORT = 8080;
5
+ type TestEdgeWsServerParams = {
6
+ admitConnection?: Trigger;
7
+ payloadDecoder?: (payload: Uint8Array) => any;
8
+ messageHandler?: (payload: any) => Promise<Uint8Array | undefined>;
9
+ };
10
+ export declare const createTestEdgeWsServer: (port?: number, params?: TestEdgeWsServerParams) => Promise<{
11
+ server: WebSocket.Server;
12
+ messageSink: any[];
13
+ endpoint: string;
14
+ cleanup: () => void;
15
+ currentConnection: () => WebSocket | undefined;
16
+ sendResponseMessage: (request: Message, responsePayload: Uint8Array) => void;
17
+ closeConnection: () => Promise<void>;
18
+ }>;
19
+ export {};
20
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +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;AAGtC,OAAO,EAAoC,KAAK,OAAO,EAAE,MAAM,4CAA4C,CAAC;AAK5G,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;;;;;;mCAgE9E,OAAO,mBAAmB,UAAU;;EAjBtD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/edge-client",
3
- "version": "0.6.12-main.5cc132e",
3
+ "version": "0.6.12-main.78ddbdf",
4
4
  "description": "EDGE Client",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -14,11 +14,23 @@
14
14
  "default": "./dist/lib/node-esm/index.mjs"
15
15
  },
16
16
  "types": "./dist/types/src/index.d.ts"
17
+ },
18
+ "./testing": {
19
+ "browser": "./dist/lib/browser/testing/index.mjs",
20
+ "node": {
21
+ "require": "./dist/lib/node/testing/index.cjs",
22
+ "default": "./dist/lib/node-esm/testing/index.mjs"
23
+ },
24
+ "types": "./dist/types/src/testing/index.d.ts"
17
25
  }
18
26
  },
19
27
  "types": "dist/types/src/index.d.ts",
20
28
  "typesVersions": {
21
- "*": {}
29
+ "*": {
30
+ "testing": [
31
+ "dist/types/src/testing/index.d.ts"
32
+ ]
33
+ }
22
34
  },
23
35
  "files": [
24
36
  "dist",
@@ -26,21 +38,20 @@
26
38
  "README.md"
27
39
  ],
28
40
  "dependencies": {
29
- "@bufbuild/protobuf": "^2.0.0",
30
41
  "isomorphic-ws": "^5.0.0",
31
42
  "ws": "^8.14.2",
32
- "@dxos/async": "0.6.12-main.5cc132e",
33
- "@dxos/context": "0.6.12-main.5cc132e",
34
- "@dxos/debug": "0.6.12-main.5cc132e",
35
- "@dxos/invariant": "0.6.12-main.5cc132e",
36
- "@dxos/log": "0.6.12-main.5cc132e",
37
- "@dxos/node-std": "0.6.12-main.5cc132e",
38
- "@dxos/util": "0.6.12-main.5cc132e",
39
- "@dxos/protocols": "0.6.12-main.5cc132e"
43
+ "@dxos/async": "0.6.12-main.78ddbdf",
44
+ "@dxos/context": "0.6.12-main.78ddbdf",
45
+ "@dxos/debug": "0.6.12-main.78ddbdf",
46
+ "@dxos/invariant": "0.6.12-main.78ddbdf",
47
+ "@dxos/log": "0.6.12-main.78ddbdf",
48
+ "@dxos/protocols": "0.6.12-main.78ddbdf",
49
+ "@dxos/node-std": "0.6.12-main.78ddbdf",
50
+ "@dxos/util": "0.6.12-main.78ddbdf"
40
51
  },
41
52
  "devDependencies": {
42
- "@dxos/keys": "0.6.12-main.5cc132e",
43
- "@dxos/test-utils": "0.6.12-main.5cc132e"
53
+ "@dxos/test-utils": "0.6.12-main.78ddbdf",
54
+ "@dxos/keys": "0.6.12-main.78ddbdf"
44
55
  },
45
56
  "publishConfig": {
46
57
  "access": "public"
package/src/defs.ts CHANGED
@@ -2,10 +2,9 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { AnySchema } from '@bufbuild/protobuf/wkt';
6
-
5
+ import { bufWkt } from '@dxos/protocols/buf';
7
6
  import { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
8
7
 
9
8
  import { Protocol } from './protocol';
10
9
 
11
- export const protocol = new Protocol([SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema, AnySchema]);
10
+ export const protocol = new Protocol([SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema, bufWkt.AnySchema]);
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { test, expect, describe } from 'vitest';
5
+ import { test, expect, describe, onTestFinished } from 'vitest';
6
6
 
7
7
  import { PublicKey } from '@dxos/keys';
8
8
  import { TextMessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
@@ -10,13 +10,15 @@ import { openAndClose } from '@dxos/test-utils';
10
10
 
11
11
  import { protocol } from './defs';
12
12
  import { EdgeClient } from './edge-client';
13
- import { createTestWsServer } from './test-utils';
13
+ import { createTestEdgeWsServer } from './testing';
14
14
 
15
15
  describe('EdgeClient', () => {
16
16
  const textMessage = (message: string) => protocol.createMessage(TextMessageSchema, { payload: { message } });
17
17
 
18
18
  test('reconnects on error', async () => {
19
- const { error: serverError, endpoint } = await createTestWsServer(8001);
19
+ const { closeConnection, endpoint, cleanup } = await createTestEdgeWsServer(8001);
20
+ onTestFinished(cleanup);
21
+
20
22
  const id = PublicKey.random().toHex();
21
23
  const client = new EdgeClient(id, id, { socketEndpoint: endpoint });
22
24
  await openAndClose(client);
@@ -24,13 +26,14 @@ describe('EdgeClient', () => {
24
26
  expect(client.isOpen).is.true;
25
27
 
26
28
  const reconnected = client.reconnect.waitForCount(1);
27
- await serverError();
29
+ await closeConnection();
28
30
  await reconnected;
29
31
  await expect(client.send(textMessage('Hello world 2'))).resolves.not.toThrow();
30
32
  });
31
33
 
32
34
  test('set identity reconnects', async () => {
33
- const { endpoint } = await createTestWsServer(8002);
35
+ const { endpoint, cleanup } = await createTestEdgeWsServer(8002);
36
+ onTestFinished(cleanup);
34
37
 
35
38
  const id = PublicKey.random().toHex();
36
39
  const client = new EdgeClient(id, id, { socketEndpoint: endpoint });
@@ -6,13 +6,12 @@ import WebSocket from 'isomorphic-ws';
6
6
 
7
7
  import { Trigger, Event, scheduleTaskInterval, scheduleTask, TriggerState } from '@dxos/async';
8
8
  import { Context, LifecycleState, Resource, type Lifecycle } from '@dxos/context';
9
- import { invariant } from '@dxos/invariant';
10
9
  import { log } from '@dxos/log';
11
10
  import { buf } from '@dxos/protocols/buf';
12
11
  import { type Message, MessageSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
13
12
 
14
13
  import { protocol } from './defs';
15
- import { WebsocketClosedError } from './errors';
14
+ import { EdgeConnectionClosedError, EdgeIdentityChangedError } from './errors';
16
15
  import { PersistentLifecycle } from './persistent-lifecycle';
17
16
  import { type Protocol, toUint8Array } from './protocol';
18
17
 
@@ -22,6 +21,7 @@ const SIGNAL_KEEPALIVE_INTERVAL = 5_000;
22
21
  export type MessageListener = (message: Message) => void | Promise<void>;
23
22
 
24
23
  export interface EdgeConnection extends Required<Lifecycle> {
24
+ connected: Event;
25
25
  reconnect: Event;
26
26
 
27
27
  get info(): any;
@@ -43,7 +43,8 @@ export type MessengerConfig = {
43
43
  * Messenger client.
44
44
  */
45
45
  export class EdgeClient extends Resource implements EdgeConnection {
46
- public reconnect = new Event();
46
+ public readonly reconnect = new Event();
47
+ public readonly connected = new Event();
47
48
  private readonly _persistentLifecycle = new PersistentLifecycle({
48
49
  start: async () => this._openWebSocket(),
49
50
  stop: async () => this._closeWebSocket(),
@@ -51,7 +52,6 @@ export class EdgeClient extends Resource implements EdgeConnection {
51
52
  });
52
53
 
53
54
  private readonly _listeners = new Set<MessageListener>();
54
- private readonly _protocol: Protocol;
55
55
  private _ready = new Trigger();
56
56
  private _ws?: WebSocket = undefined;
57
57
  private _keepaliveCtx?: Context = undefined;
@@ -63,7 +63,6 @@ export class EdgeClient extends Resource implements EdgeConnection {
63
63
  private readonly _config: MessengerConfig,
64
64
  ) {
65
65
  super();
66
- this._protocol = this._config.protocol ?? protocol;
67
66
  }
68
67
 
69
68
  // TODO(burdon): Attach logging.
@@ -119,6 +118,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
119
118
  this._ws.onopen = () => {
120
119
  log('opened', this.info);
121
120
  this._ready.wake();
121
+ this.connected.emit();
122
122
  };
123
123
  this._ws.onclose = () => {
124
124
  log('closed', this.info);
@@ -170,7 +170,7 @@ export class EdgeClient extends Resource implements EdgeConnection {
170
170
  return;
171
171
  }
172
172
  try {
173
- this._ready.throw(new WebsocketClosedError());
173
+ this._ready.throw(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());
174
174
  this._ready.reset();
175
175
  void this._keepaliveCtx?.dispose();
176
176
  this._keepaliveCtx = undefined;
@@ -197,10 +197,19 @@ export class EdgeClient extends Resource implements EdgeConnection {
197
197
  */
198
198
  public async send(message: Message): Promise<void> {
199
199
  if (this._ready.state !== TriggerState.RESOLVED) {
200
+ log('waiting for websocket to become ready');
200
201
  await this._ready.wait({ timeout: this._config.timeout ?? DEFAULT_TIMEOUT });
201
202
  }
202
- invariant(this._ws);
203
- invariant(!message.source || message.source.peerKey === this._peerKey);
203
+ if (!this._ws) {
204
+ throw new EdgeConnectionClosedError();
205
+ }
206
+ if (
207
+ message.source &&
208
+ (message.source.peerKey !== this._peerKey || message.source.identityKey !== this.identityKey)
209
+ ) {
210
+ throw new EdgeIdentityChangedError();
211
+ }
212
+
204
213
  log('sending...', { peerKey: this._peerKey, payload: protocol.getPayloadType(message) });
205
214
  this._ws.send(buf.toBinary(MessageSchema, message));
206
215
  }
package/src/errors.ts CHANGED
@@ -2,8 +2,14 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export class WebsocketClosedError extends Error {
5
+ export class EdgeConnectionClosedError extends Error {
6
6
  constructor() {
7
- super('WebSocket connection closed');
7
+ super('Edge connection closed.');
8
+ }
9
+ }
10
+
11
+ export class EdgeIdentityChangedError extends Error {
12
+ constructor() {
13
+ super('Edge identity changed.');
8
14
  }
9
15
  }
package/src/index.ts CHANGED
@@ -7,3 +7,4 @@ export * from '@dxos/protocols/buf/dxos/edge/messenger_pb';
7
7
  export * from './edge-client';
8
8
  export * from './defs';
9
9
  export * from './protocol';
10
+ export * from './errors';
package/src/protocol.ts CHANGED
@@ -4,10 +4,10 @@
4
4
 
5
5
  import { invariant } from '@dxos/invariant';
6
6
  import { buf, bufWkt } from '@dxos/protocols/buf';
7
- import { type Message, MessageSchema, type Peer as PeerProto } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
7
+ import { type Message, MessageSchema, type PeerSchema } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
8
8
  import { bufferToArray } from '@dxos/util';
9
9
 
10
- export type PeerData = Partial<PeerProto>;
10
+ export type PeerData = buf.MessageInitShape<typeof PeerSchema>;
11
11
 
12
12
  export const getTypename = (typeName: string) => `type.googleapis.com/${typeName}`;
13
13
 
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './test-utils';
@@ -0,0 +1,111 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import WebSocket from 'isomorphic-ws';
6
+
7
+ import { Trigger } from '@dxos/async';
8
+ import { log } from '@dxos/log';
9
+ import { buf } from '@dxos/protocols/buf';
10
+ import { MessageSchema, TextMessageSchema, type Message } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
11
+
12
+ import { protocol } from '../defs';
13
+ import { toUint8Array } from '../protocol';
14
+
15
+ export const DEFAULT_PORT = 8080;
16
+
17
+ type TestEdgeWsServerParams = {
18
+ admitConnection?: Trigger;
19
+ payloadDecoder?: (payload: Uint8Array) => any;
20
+ messageHandler?: (payload: any) => Promise<Uint8Array | undefined>;
21
+ };
22
+
23
+ export const createTestEdgeWsServer = async (port = DEFAULT_PORT, params?: TestEdgeWsServerParams) => {
24
+ const server = new WebSocket.Server({ port, verifyClient: createConnectionDelayHandler(params) });
25
+
26
+ let connection: WebSocket | undefined;
27
+
28
+ const messageSink: any[] = [];
29
+ const closeTrigger = new Trigger();
30
+ const sendResponseMessage = createResponseSender(() => connection!);
31
+
32
+ server.on('connection', (ws) => {
33
+ connection = ws;
34
+ ws.on('error', (err) => log.catch(err));
35
+ ws.on('message', async (data) => {
36
+ if (String(data) === '__ping__') {
37
+ ws.send('__pong__');
38
+ return;
39
+ }
40
+ const { request, requestPayload } = await decodeRequest(params, data);
41
+ if (params?.messageHandler) {
42
+ const responsePayload = await params.messageHandler(requestPayload);
43
+ if (responsePayload && connection) {
44
+ sendResponseMessage(request, responsePayload);
45
+ }
46
+ }
47
+ log('message', { payload: requestPayload });
48
+ messageSink.push(requestPayload);
49
+ });
50
+
51
+ ws.on('close', () => {
52
+ connection = undefined;
53
+ closeTrigger.wake();
54
+ });
55
+ });
56
+
57
+ return {
58
+ server,
59
+ messageSink,
60
+ endpoint: `ws://localhost:${port}`,
61
+ cleanup: () => server.close(),
62
+ currentConnection: () => connection,
63
+ sendResponseMessage,
64
+ closeConnection: () => {
65
+ closeTrigger.reset();
66
+ connection!.close(1011);
67
+ return closeTrigger.wait();
68
+ },
69
+ };
70
+ };
71
+
72
+ const createConnectionDelayHandler = (params: TestEdgeWsServerParams | undefined) => {
73
+ return (_: any, callback: (admit: boolean) => void) => {
74
+ if (params?.admitConnection) {
75
+ log('delaying edge connection admission');
76
+ void params.admitConnection.wait().then(() => {
77
+ callback(true);
78
+ log('edge connection admitted');
79
+ });
80
+ } else {
81
+ callback(true);
82
+ }
83
+ };
84
+ };
85
+
86
+ const createResponseSender = (connection: () => WebSocket) => {
87
+ return (request: Message, responsePayload: Uint8Array) => {
88
+ const recipient = request.source!;
89
+ connection().send(
90
+ buf.toBinary(
91
+ MessageSchema,
92
+ buf.create(MessageSchema, {
93
+ source: {
94
+ identityKey: recipient.identityKey,
95
+ peerKey: recipient.peerKey,
96
+ },
97
+ serviceId: request.serviceId!,
98
+ payload: { value: responsePayload },
99
+ }),
100
+ ),
101
+ );
102
+ };
103
+ };
104
+
105
+ const decodeRequest = async (params: TestEdgeWsServerParams | undefined, data: any) => {
106
+ const request = buf.fromBinary(MessageSchema, await toUint8Array(data));
107
+ const requestPayload = params?.payloadDecoder
108
+ ? params.payloadDecoder(request.payload!.value!)
109
+ : protocol.getPayload(request, TextMessageSchema);
110
+ return { request, requestPayload };
111
+ };
@@ -3,16 +3,17 @@
3
3
  //
4
4
 
5
5
  import WebSocket from 'isomorphic-ws';
6
- import { describe, expect, test } from 'vitest';
6
+ import { describe, expect, test, onTestFinished } from 'vitest';
7
7
 
8
8
  import { Trigger, TriggerState } from '@dxos/async';
9
9
 
10
- import { createTestWsServer } from './test-utils';
10
+ import { createTestEdgeWsServer } from './testing';
11
11
 
12
- // TODO(wittjosiah): Doesn't work in vitest.
13
- describe.skip('WebSocket', () => {
12
+ describe('WebSocket', () => {
14
13
  test('swap `onclose` handler ', async () => {
15
- const { endpoint } = await createTestWsServer(8003);
14
+ const { endpoint, cleanup } = await createTestEdgeWsServer(8003);
15
+ onTestFinished(cleanup);
16
+
16
17
  const ws = new WebSocket(endpoint);
17
18
  const opened = new Trigger();
18
19
  ws.onopen = () => {