@aegis-fluxion/core 0.4.0 → 0.5.0

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/README.md CHANGED
@@ -1,20 +1,22 @@
1
1
  # @aegis-fluxion/core
2
2
 
3
- Core E2E-encrypted WebSocket primitives for `aegis-fluxion`.
3
+ Low-level E2E-encrypted WebSocket primitives for the `aegis-fluxion` ecosystem.
4
4
 
5
- Version: **0.4.0**
5
+ If you prefer a single user-facing package, use [`aegis-fluxion`](../aegis-fluxion/README.md).
6
+
7
+ Version: **0.5.0**
6
8
 
7
9
  ---
8
10
 
9
- ## Features
11
+ ## Highlights
10
12
 
11
- - ECDH handshake (`prime256v1`) with ephemeral key exchange
12
- - AES-256-GCM encrypted message envelopes
13
- - Server/client lifecycle events (`connect`, `ready`, `disconnect`, `error`)
14
- - Secure room routing (`join`, `leave`, `to(room).emit`)
15
- - Heartbeat-based zombie cleanup
16
- - Auto-reconnect with exponential backoff
17
- - **RPC-style ACK request/response with timeout support**
13
+ - Ephemeral ECDH handshake (`prime256v1`)
14
+ - AES-256-GCM encrypted envelopes
15
+ - Encrypted ACK request/response (`Promise` and callback)
16
+ - Secure room routing (`join`, `leave`, `leaveAll`, `to(room).emit`)
17
+ - Heartbeat and zombie socket cleanup
18
+ - Auto-reconnect with fresh re-handshake
19
+ - **Binary payload support**: `Buffer`, `Uint8Array`, `Blob`
18
20
 
19
21
  ---
20
22
 
@@ -26,7 +28,88 @@ npm install @aegis-fluxion/core ws
26
28
 
27
29
  ---
28
30
 
29
- ## API at a Glance
31
+ ## Binary Data Support
32
+
33
+ `@aegis-fluxion/core` supports encrypted binary payload transfer while preserving type fidelity.
34
+
35
+ Supported send/receive types:
36
+
37
+ - `Buffer`
38
+ - `Uint8Array`
39
+ - `Blob`
40
+
41
+ Binary values can be nested in regular objects and arrays.
42
+
43
+ ---
44
+
45
+ ## Example: Encrypted Binary Event
46
+
47
+ ```ts
48
+ import { SecureClient, SecureServer } from "@aegis-fluxion/core";
49
+
50
+ const server = new SecureServer({ host: "127.0.0.1", port: 8080 });
51
+ const client = new SecureClient("ws://127.0.0.1:8080");
52
+
53
+ server.on("image:chunk", (data, socket) => {
54
+ const chunk = data as Buffer;
55
+
56
+ if (!Buffer.isBuffer(chunk)) {
57
+ throw new Error("Expected Buffer payload.");
58
+ }
59
+
60
+ socket.emit("image:chunk:ack", chunk);
61
+ });
62
+
63
+ client.on("ready", () => {
64
+ const imageChunk = Buffer.from("89504e470d0a", "hex");
65
+ client.emit("image:chunk", imageChunk);
66
+ });
67
+
68
+ client.on("image:chunk:ack", (payload) => {
69
+ const echoedChunk = payload as Buffer;
70
+ console.log("Echoed bytes:", echoedChunk.byteLength);
71
+ });
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Example: ACK Roundtrip with Mixed Binary Types
77
+
78
+ ```ts
79
+ server.on("binary:inspect", async (payload) => {
80
+ const { file, bytes, blob } = payload as {
81
+ file: Buffer;
82
+ bytes: Uint8Array;
83
+ blob: Blob;
84
+ };
85
+
86
+ return {
87
+ fileBytes: file.byteLength,
88
+ bytesBytes: bytes.byteLength,
89
+ blobBytes: blob.size
90
+ };
91
+ });
92
+
93
+ client.on("ready", async () => {
94
+ const result = await client.emit(
95
+ "binary:inspect",
96
+ {
97
+ file: Buffer.from("file-binary"),
98
+ bytes: Uint8Array.from([1, 2, 3, 4]),
99
+ blob: new Blob([Buffer.from("blob-binary")], {
100
+ type: "application/octet-stream"
101
+ })
102
+ },
103
+ { timeoutMs: 1500 }
104
+ );
105
+
106
+ console.log(result);
107
+ });
108
+ ```
109
+
110
+ ---
111
+
112
+ ## API Snapshot
30
113
 
31
114
  ### `SecureServer`
32
115
 
@@ -44,10 +127,10 @@ npm install @aegis-fluxion/core ws
44
127
 
45
128
  - `id: string`
46
129
  - `socket: WebSocket`
130
+ - `emit(event, data, ...ackArgs): boolean | Promise<unknown>`
47
131
  - `join(room): boolean`
48
132
  - `leave(room): boolean`
49
133
  - `leaveAll(): number`
50
- - `emit(event, data, ...ackArgs): boolean | Promise<unknown>`
51
134
 
52
135
  ### `SecureClient`
53
136
 
@@ -64,97 +147,18 @@ npm install @aegis-fluxion/core ws
64
147
 
65
148
  ---
66
149
 
67
- ## ACK (Request-Response) Usage
68
-
69
- ### 1) Client -> Server (Promise ACK)
70
-
71
- ```ts
72
- import { SecureClient, SecureServer } from "@aegis-fluxion/core";
73
-
74
- const server = new SecureServer({ port: 8080, host: "127.0.0.1" });
75
-
76
- server.on("math:add", ({ a, b }) => {
77
- return { total: Number(a) + Number(b) };
78
- });
79
-
80
- const client = new SecureClient("ws://127.0.0.1:8080");
81
-
82
- client.on("ready", async () => {
83
- const response = await client.emit(
84
- "math:add",
85
- { a: 2, b: 3 },
86
- { timeoutMs: 1000 }
87
- );
88
-
89
- console.log(response); // { total: 5 }
90
- });
91
- ```
92
-
93
- ### 2) Client -> Server (Callback ACK)
94
-
95
- ```ts
96
- client.emit(
97
- "math:add",
98
- { a: 4, b: 6 },
99
- { timeoutMs: 1000 },
100
- (error, response) => {
101
- if (error) {
102
- console.error("ACK error:", error.message);
103
- return;
104
- }
105
-
106
- console.log(response); // { total: 10 }
107
- }
108
- );
109
- ```
110
-
111
- ### 3) Server -> Client (Promise ACK)
112
-
113
- ```ts
114
- server.on("ready", async (clientSocket) => {
115
- const response = await clientSocket.emit(
116
- "agent:health",
117
- { verbose: true },
118
- { timeoutMs: 1200 }
119
- );
120
-
121
- console.log(response);
122
- });
123
-
124
- client.on("agent:health", () => {
125
- return { ok: true, uptime: process.uptime() };
126
- });
127
- ```
128
-
129
- ### 4) ACK Timeout Behavior
130
-
131
- When no response arrives before `timeoutMs`, ACK request fails:
132
-
133
- - Promise form -> rejects with timeout error
134
- - Callback form -> callback receives `Error`
135
-
136
- ```ts
137
- try {
138
- await client.emit("never:respond", { ping: true }, { timeoutMs: 300 });
139
- } catch (error) {
140
- console.error((error as Error).message);
141
- // ACK response timed out after 300ms for event "never:respond".
142
- }
143
- ```
144
-
145
- ---
146
-
147
150
  ## Security Notes
148
151
 
149
- - ACK request and ACK response frames are encrypted with the same AES-GCM tunnel as normal events.
150
- - Internal handshake/RPC transport events are reserved and cannot be emitted manually.
151
- - On disconnect/heartbeat timeout, pending ACK promises are rejected and memory state is cleaned.
152
+ - All payloads (including binary) are encrypted end-to-end with AES-256-GCM.
153
+ - Authentication tags are verified on every packet (tampered packets are dropped).
154
+ - Internal transport events are reserved (`__handshake`, `__rpc:req`, `__rpc:res`).
155
+ - Pending ACK requests are rejected on timeout/disconnect.
152
156
 
153
157
  ---
154
158
 
155
159
  ## Development
156
160
 
157
- From monorepo root:
161
+ From repository root:
158
162
 
159
163
  ```bash
160
164
  npm run typecheck -w @aegis-fluxion/core
@@ -164,16 +168,6 @@ npm run build -w @aegis-fluxion/core
164
168
 
165
169
  ---
166
170
 
167
- ## Publish
168
-
169
- From monorepo root:
170
-
171
- ```bash
172
- npm publish -w @aegis-fluxion/core --access public
173
- ```
174
-
175
- ---
176
-
177
171
  ## License
178
172
 
179
173
  MIT
package/dist/index.cjs CHANGED
@@ -21,6 +21,8 @@ var GCM_AUTH_TAG_LENGTH = 16;
21
21
  var ENCRYPTION_KEY_LENGTH = 32;
22
22
  var ENCRYPTED_PACKET_VERSION = 1;
23
23
  var ENCRYPTED_PACKET_PREFIX_LENGTH = 1 + GCM_IV_LENGTH + GCM_AUTH_TAG_LENGTH;
24
+ var BINARY_PAYLOAD_MARKER = "__afxBinaryPayload";
25
+ var BINARY_PAYLOAD_VERSION = 1;
24
26
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
25
27
  var DEFAULT_HEARTBEAT_TIMEOUT_MS = 15e3;
26
28
  var DEFAULT_RECONNECT_INITIAL_DELAY_MS = 250;
@@ -61,7 +63,103 @@ function rawDataToBuffer(rawData) {
61
63
  }
62
64
  return Buffer.from(rawData);
63
65
  }
64
- function serializeEnvelope(event, data) {
66
+ function isBlobValue(value) {
67
+ return typeof Blob !== "undefined" && value instanceof Blob;
68
+ }
69
+ function isPlainObject(value) {
70
+ if (typeof value !== "object" || value === null) {
71
+ return false;
72
+ }
73
+ const prototype = Object.getPrototypeOf(value);
74
+ return prototype === Object.prototype || prototype === null;
75
+ }
76
+ function encodeBinaryPayload(kind, payloadBuffer, mimeType) {
77
+ const encodedPayload = {
78
+ [BINARY_PAYLOAD_MARKER]: BINARY_PAYLOAD_VERSION,
79
+ kind,
80
+ base64: payloadBuffer.toString("base64")
81
+ };
82
+ if (mimeType !== void 0 && mimeType.length > 0) {
83
+ encodedPayload.mimeType = mimeType;
84
+ }
85
+ return encodedPayload;
86
+ }
87
+ async function encodeEnvelopeData(value) {
88
+ if (Buffer.isBuffer(value)) {
89
+ return encodeBinaryPayload("buffer", value);
90
+ }
91
+ if (value instanceof Uint8Array) {
92
+ const typedArrayBuffer = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
93
+ return encodeBinaryPayload("uint8array", typedArrayBuffer);
94
+ }
95
+ if (isBlobValue(value)) {
96
+ const blobBuffer = Buffer.from(await value.arrayBuffer());
97
+ return encodeBinaryPayload("blob", blobBuffer, value.type);
98
+ }
99
+ if (Array.isArray(value)) {
100
+ return Promise.all(value.map((item) => encodeEnvelopeData(item)));
101
+ }
102
+ if (isPlainObject(value)) {
103
+ const encodedEntries = await Promise.all(
104
+ Object.entries(value).map(async ([key, entryValue]) => {
105
+ return [key, await encodeEnvelopeData(entryValue)];
106
+ })
107
+ );
108
+ return Object.fromEntries(encodedEntries);
109
+ }
110
+ return value;
111
+ }
112
+ function isEncodedBinaryPayload(value) {
113
+ if (!isPlainObject(value)) {
114
+ return false;
115
+ }
116
+ if (value[BINARY_PAYLOAD_MARKER] !== BINARY_PAYLOAD_VERSION) {
117
+ return false;
118
+ }
119
+ if (value.kind !== "buffer" && value.kind !== "uint8array" && value.kind !== "blob") {
120
+ return false;
121
+ }
122
+ if (typeof value.base64 !== "string") {
123
+ return false;
124
+ }
125
+ if (value.mimeType !== void 0 && typeof value.mimeType !== "string") {
126
+ return false;
127
+ }
128
+ return true;
129
+ }
130
+ function decodeEnvelopeData(value) {
131
+ if (Array.isArray(value)) {
132
+ return value.map((item) => decodeEnvelopeData(item));
133
+ }
134
+ if (isEncodedBinaryPayload(value)) {
135
+ const binaryBuffer = Buffer.from(value.base64, "base64");
136
+ if (value.kind === "buffer") {
137
+ return binaryBuffer;
138
+ }
139
+ if (value.kind === "uint8array") {
140
+ return Uint8Array.from(binaryBuffer);
141
+ }
142
+ if (typeof Blob === "undefined") {
143
+ return binaryBuffer;
144
+ }
145
+ return new Blob([binaryBuffer], {
146
+ type: value.mimeType ?? ""
147
+ });
148
+ }
149
+ if (isPlainObject(value)) {
150
+ const decodedEntries = Object.entries(value).map(([key, entryValue]) => {
151
+ return [key, decodeEnvelopeData(entryValue)];
152
+ });
153
+ return Object.fromEntries(decodedEntries);
154
+ }
155
+ return value;
156
+ }
157
+ async function serializeEnvelope(event, data) {
158
+ const encodedData = await encodeEnvelopeData(data);
159
+ const envelope = { event, data: encodedData };
160
+ return JSON.stringify(envelope);
161
+ }
162
+ function serializePlainEnvelope(event, data) {
65
163
  const envelope = { event, data };
66
164
  return JSON.stringify(envelope);
67
165
  }
@@ -73,7 +171,7 @@ function parseEnvelope(rawData) {
73
171
  }
74
172
  return {
75
173
  event: parsed.event,
76
- data: parsed.data
174
+ data: decodeEnvelopeData(parsed.data)
77
175
  };
78
176
  }
79
177
  function parseEnvelopeFromText(decodedPayload) {
@@ -83,7 +181,7 @@ function parseEnvelopeFromText(decodedPayload) {
83
181
  }
84
182
  return {
85
183
  event: parsed.event,
86
- data: parsed.data
184
+ data: decodeEnvelopeData(parsed.data)
87
185
  };
88
186
  }
89
187
  function decodeCloseReason(reason) {
@@ -361,7 +459,9 @@ var SecureServer = class {
361
459
  }
362
460
  const envelope = { event, data };
363
461
  for (const client of this.clientsById.values()) {
364
- this.sendOrQueuePayload(client.socket, envelope);
462
+ void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
463
+ return void 0;
464
+ });
365
465
  }
366
466
  } catch (error) {
367
467
  this.notifyError(normalizeToError(error, "Failed to emit server event."));
@@ -379,7 +479,9 @@ var SecureServer = class {
379
479
  throw new Error(`Client with id ${clientId} was not found.`);
380
480
  }
381
481
  if (!ackArgs.expectsAck) {
382
- this.sendOrQueuePayload(client.socket, { event, data });
482
+ void this.sendOrQueuePayload(client.socket, { event, data }).catch(() => {
483
+ return void 0;
484
+ });
383
485
  return true;
384
486
  }
385
487
  const ackPromise = this.sendRpcRequest(
@@ -692,22 +794,24 @@ var SecureServer = class {
692
794
  this.notifyError(normalizeToError(error, "Failed to send server payload."));
693
795
  }
694
796
  }
695
- sendEncryptedEnvelope(socket, envelope) {
797
+ async sendEncryptedEnvelope(socket, envelope) {
798
+ if (socket.readyState !== WebSocket__default.default.OPEN) {
799
+ return;
800
+ }
801
+ const encryptionKey = this.encryptionKeyBySocket.get(socket);
802
+ if (!encryptionKey) {
803
+ const missingKeyError = new Error("Missing encryption key for connected socket.");
804
+ this.notifyError(missingKeyError);
805
+ throw missingKeyError;
806
+ }
696
807
  try {
697
- if (socket.readyState !== WebSocket__default.default.OPEN) {
698
- return;
699
- }
700
- const encryptionKey = this.encryptionKeyBySocket.get(socket);
701
- if (!encryptionKey) {
702
- throw new Error("Missing encryption key for connected socket.");
703
- }
704
- const encryptedPayload = encryptSerializedEnvelope(
705
- serializeEnvelope(envelope.event, envelope.data),
706
- encryptionKey
707
- );
808
+ const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
809
+ const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
708
810
  socket.send(encryptedPayload);
709
811
  } catch (error) {
710
- this.notifyError(normalizeToError(error, "Failed to send encrypted server payload."));
812
+ const normalizedError = normalizeToError(error, "Failed to send encrypted server payload.");
813
+ this.notifyError(normalizedError);
814
+ throw normalizedError;
711
815
  }
712
816
  }
713
817
  sendRpcRequest(socket, event, data, timeoutMs) {
@@ -728,13 +832,19 @@ var SecureServer = class {
728
832
  reject,
729
833
  timeoutHandle
730
834
  });
731
- this.sendOrQueuePayload(socket, {
835
+ void this.sendOrQueuePayload(socket, {
732
836
  event: INTERNAL_RPC_REQUEST_EVENT,
733
837
  data: {
734
838
  id: requestId,
735
839
  event,
736
840
  data
737
841
  }
842
+ }).catch((error) => {
843
+ clearTimeout(timeoutHandle);
844
+ pendingRequests.delete(requestId);
845
+ reject(
846
+ normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
847
+ );
738
848
  });
739
849
  });
740
850
  }
@@ -776,7 +886,7 @@ var SecureServer = class {
776
886
  rpcRequestPayload.data,
777
887
  client
778
888
  );
779
- this.sendEncryptedEnvelope(client.socket, {
889
+ await this.sendEncryptedEnvelope(client.socket, {
780
890
  event: INTERNAL_RPC_RESPONSE_EVENT,
781
891
  data: {
782
892
  id: rpcRequestPayload.id,
@@ -786,7 +896,7 @@ var SecureServer = class {
786
896
  });
787
897
  } catch (error) {
788
898
  const normalizedError = normalizeToError(error, "Server ACK request handler failed.");
789
- this.sendEncryptedEnvelope(client.socket, {
899
+ await this.sendEncryptedEnvelope(client.socket, {
790
900
  event: INTERNAL_RPC_RESPONSE_EVENT,
791
901
  data: {
792
902
  id: rpcRequestPayload.id,
@@ -860,7 +970,7 @@ var SecureServer = class {
860
970
  sendInternalHandshake(socket, localPublicKey) {
861
971
  this.sendRaw(
862
972
  socket,
863
- serializeEnvelope(INTERNAL_HANDSHAKE_EVENT, {
973
+ serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
864
974
  publicKey: localPublicKey
865
975
  })
866
976
  );
@@ -893,23 +1003,23 @@ var SecureServer = class {
893
1003
  sendOrQueuePayload(socket, envelope) {
894
1004
  if (!this.isClientHandshakeReady(socket)) {
895
1005
  this.queuePayload(socket, envelope);
896
- return;
1006
+ return Promise.resolve();
897
1007
  }
898
- this.sendEncryptedEnvelope(socket, envelope);
1008
+ return this.sendEncryptedEnvelope(socket, envelope);
899
1009
  }
900
1010
  queuePayload(socket, envelope) {
901
1011
  const pendingPayloads = this.pendingPayloadsBySocket.get(socket) ?? [];
902
1012
  pendingPayloads.push(envelope);
903
1013
  this.pendingPayloadsBySocket.set(socket, pendingPayloads);
904
1014
  }
905
- flushQueuedPayloads(socket) {
1015
+ async flushQueuedPayloads(socket) {
906
1016
  const pendingPayloads = this.pendingPayloadsBySocket.get(socket);
907
1017
  if (!pendingPayloads || pendingPayloads.length === 0) {
908
1018
  return;
909
1019
  }
910
1020
  this.pendingPayloadsBySocket.delete(socket);
911
1021
  for (const envelope of pendingPayloads) {
912
- this.sendEncryptedEnvelope(socket, envelope);
1022
+ await this.sendEncryptedEnvelope(socket, envelope);
913
1023
  }
914
1024
  }
915
1025
  createSecureServerClient(clientId, socket, request) {
@@ -1019,7 +1129,9 @@ var SecureServer = class {
1019
1129
  if (!client) {
1020
1130
  continue;
1021
1131
  }
1022
- this.sendOrQueuePayload(client.socket, envelope);
1132
+ void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
1133
+ return void 0;
1134
+ });
1023
1135
  }
1024
1136
  }
1025
1137
  };
@@ -1183,7 +1295,9 @@ var SecureClient = class {
1183
1295
  this.pendingPayloadQueue.push(envelope);
1184
1296
  return true;
1185
1297
  }
1186
- this.sendEncryptedEnvelope(envelope);
1298
+ void this.sendEncryptedEnvelope(envelope).catch(() => {
1299
+ return void 0;
1300
+ });
1187
1301
  return true;
1188
1302
  } catch (error) {
1189
1303
  const normalizedError = normalizeToError(error, "Failed to emit client event.");
@@ -1429,22 +1543,26 @@ var SecureClient = class {
1429
1543
  }
1430
1544
  }
1431
1545
  }
1432
- sendEncryptedEnvelope(envelope) {
1546
+ async sendEncryptedEnvelope(envelope) {
1547
+ if (!this.socket || this.socket.readyState !== WebSocket__default.default.OPEN) {
1548
+ const socketStateError = new Error("Client socket is not connected.");
1549
+ this.notifyError(socketStateError);
1550
+ throw socketStateError;
1551
+ }
1552
+ const encryptionKey = this.handshakeState?.encryptionKey;
1553
+ if (!encryptionKey) {
1554
+ const missingKeyError = new Error("Missing encryption key for client payload encryption.");
1555
+ this.notifyError(missingKeyError);
1556
+ throw missingKeyError;
1557
+ }
1433
1558
  try {
1434
- if (!this.socket || this.socket.readyState !== WebSocket__default.default.OPEN) {
1435
- throw new Error("Client socket is not connected.");
1436
- }
1437
- const encryptionKey = this.handshakeState?.encryptionKey;
1438
- if (!encryptionKey) {
1439
- throw new Error("Missing encryption key for client payload encryption.");
1440
- }
1441
- const encryptedPayload = encryptSerializedEnvelope(
1442
- serializeEnvelope(envelope.event, envelope.data),
1443
- encryptionKey
1444
- );
1559
+ const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
1560
+ const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
1445
1561
  this.socket.send(encryptedPayload);
1446
1562
  } catch (error) {
1447
- this.notifyError(normalizeToError(error, "Failed to send encrypted client payload."));
1563
+ const normalizedError = normalizeToError(error, "Failed to send encrypted client payload.");
1564
+ this.notifyError(normalizedError);
1565
+ throw normalizedError;
1448
1566
  }
1449
1567
  }
1450
1568
  sendRpcRequest(event, data, timeoutMs) {
@@ -1475,7 +1593,13 @@ var SecureClient = class {
1475
1593
  this.pendingPayloadQueue.push(rpcRequestEnvelope);
1476
1594
  return;
1477
1595
  }
1478
- this.sendEncryptedEnvelope(rpcRequestEnvelope);
1596
+ void this.sendEncryptedEnvelope(rpcRequestEnvelope).catch((error) => {
1597
+ clearTimeout(timeoutHandle);
1598
+ this.pendingRpcRequests.delete(requestId);
1599
+ reject(
1600
+ normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
1601
+ );
1602
+ });
1479
1603
  });
1480
1604
  }
1481
1605
  handleRpcResponse(data) {
@@ -1511,7 +1635,7 @@ var SecureClient = class {
1511
1635
  rpcRequestPayload.event,
1512
1636
  rpcRequestPayload.data
1513
1637
  );
1514
- this.sendEncryptedEnvelope({
1638
+ await this.sendEncryptedEnvelope({
1515
1639
  event: INTERNAL_RPC_RESPONSE_EVENT,
1516
1640
  data: {
1517
1641
  id: rpcRequestPayload.id,
@@ -1521,7 +1645,7 @@ var SecureClient = class {
1521
1645
  });
1522
1646
  } catch (error) {
1523
1647
  const normalizedError = normalizeToError(error, "Client ACK request handler failed.");
1524
- this.sendEncryptedEnvelope({
1648
+ await this.sendEncryptedEnvelope({
1525
1649
  event: INTERNAL_RPC_RESPONSE_EVENT,
1526
1650
  data: {
1527
1651
  id: rpcRequestPayload.id,
@@ -1566,7 +1690,7 @@ var SecureClient = class {
1566
1690
  throw new Error("Missing client handshake state.");
1567
1691
  }
1568
1692
  this.socket.send(
1569
- serializeEnvelope(INTERNAL_HANDSHAKE_EVENT, {
1693
+ serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
1570
1694
  publicKey: this.handshakeState.localPublicKey
1571
1695
  })
1572
1696
  );
@@ -1588,7 +1712,7 @@ var SecureClient = class {
1588
1712
  this.handshakeState.sharedSecret = sharedSecret;
1589
1713
  this.handshakeState.encryptionKey = deriveEncryptionKey(sharedSecret);
1590
1714
  this.handshakeState.isReady = true;
1591
- this.flushPendingPayloadQueue();
1715
+ void this.flushPendingPayloadQueue();
1592
1716
  this.notifyReady();
1593
1717
  } catch (error) {
1594
1718
  this.notifyError(normalizeToError(error, "Failed to complete client handshake."));
@@ -1597,14 +1721,14 @@ var SecureClient = class {
1597
1721
  isHandshakeReady() {
1598
1722
  return this.handshakeState?.isReady ?? false;
1599
1723
  }
1600
- flushPendingPayloadQueue() {
1724
+ async flushPendingPayloadQueue() {
1601
1725
  if (!this.socket || this.socket.readyState !== WebSocket__default.default.OPEN || !this.isHandshakeReady()) {
1602
1726
  return;
1603
1727
  }
1604
1728
  const pendingPayloads = this.pendingPayloadQueue;
1605
1729
  this.pendingPayloadQueue = [];
1606
1730
  for (const envelope of pendingPayloads) {
1607
- this.sendEncryptedEnvelope(envelope);
1731
+ await this.sendEncryptedEnvelope(envelope);
1608
1732
  }
1609
1733
  }
1610
1734
  };