@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/dist/index.d.cts CHANGED
@@ -5,6 +5,7 @@ interface SecureEnvelope<TData = unknown> {
5
5
  event: string;
6
6
  data: TData;
7
7
  }
8
+ type SecureBinaryPayload = Buffer | Uint8Array | Blob;
8
9
  interface SecureAckOptions {
9
10
  timeoutMs?: number;
10
11
  }
@@ -199,4 +200,4 @@ declare class SecureClient {
199
200
  private flushPendingPayloadQueue;
200
201
  }
201
202
 
202
- export { type SecureAckCallback, type SecureAckOptions, SecureClient, type SecureClientConnectHandler, type SecureClientDisconnectHandler, type SecureClientEventHandler, type SecureClientEventMap, type SecureClientLifecycleEvent, type SecureClientOptions, type SecureClientReadyHandler, type SecureClientReconnectOptions, type SecureEnvelope, type SecureErrorHandler, SecureServer, type SecureServerClient, type SecureServerConnectionHandler, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerOptions, type SecureServerReadyHandler, type SecureServerRoomOperator };
203
+ export { type SecureAckCallback, type SecureAckOptions, type SecureBinaryPayload, SecureClient, type SecureClientConnectHandler, type SecureClientDisconnectHandler, type SecureClientEventHandler, type SecureClientEventMap, type SecureClientLifecycleEvent, type SecureClientOptions, type SecureClientReadyHandler, type SecureClientReconnectOptions, type SecureEnvelope, type SecureErrorHandler, SecureServer, type SecureServerClient, type SecureServerConnectionHandler, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerOptions, type SecureServerReadyHandler, type SecureServerRoomOperator };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ interface SecureEnvelope<TData = unknown> {
5
5
  event: string;
6
6
  data: TData;
7
7
  }
8
+ type SecureBinaryPayload = Buffer | Uint8Array | Blob;
8
9
  interface SecureAckOptions {
9
10
  timeoutMs?: number;
10
11
  }
@@ -199,4 +200,4 @@ declare class SecureClient {
199
200
  private flushPendingPayloadQueue;
200
201
  }
201
202
 
202
- export { type SecureAckCallback, type SecureAckOptions, SecureClient, type SecureClientConnectHandler, type SecureClientDisconnectHandler, type SecureClientEventHandler, type SecureClientEventMap, type SecureClientLifecycleEvent, type SecureClientOptions, type SecureClientReadyHandler, type SecureClientReconnectOptions, type SecureEnvelope, type SecureErrorHandler, SecureServer, type SecureServerClient, type SecureServerConnectionHandler, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerOptions, type SecureServerReadyHandler, type SecureServerRoomOperator };
203
+ export { type SecureAckCallback, type SecureAckOptions, type SecureBinaryPayload, SecureClient, type SecureClientConnectHandler, type SecureClientDisconnectHandler, type SecureClientEventHandler, type SecureClientEventMap, type SecureClientLifecycleEvent, type SecureClientOptions, type SecureClientReadyHandler, type SecureClientReconnectOptions, type SecureEnvelope, type SecureErrorHandler, SecureServer, type SecureServerClient, type SecureServerConnectionHandler, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerOptions, type SecureServerReadyHandler, type SecureServerRoomOperator };
package/dist/index.js CHANGED
@@ -15,6 +15,8 @@ var GCM_AUTH_TAG_LENGTH = 16;
15
15
  var ENCRYPTION_KEY_LENGTH = 32;
16
16
  var ENCRYPTED_PACKET_VERSION = 1;
17
17
  var ENCRYPTED_PACKET_PREFIX_LENGTH = 1 + GCM_IV_LENGTH + GCM_AUTH_TAG_LENGTH;
18
+ var BINARY_PAYLOAD_MARKER = "__afxBinaryPayload";
19
+ var BINARY_PAYLOAD_VERSION = 1;
18
20
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
19
21
  var DEFAULT_HEARTBEAT_TIMEOUT_MS = 15e3;
20
22
  var DEFAULT_RECONNECT_INITIAL_DELAY_MS = 250;
@@ -55,7 +57,103 @@ function rawDataToBuffer(rawData) {
55
57
  }
56
58
  return Buffer.from(rawData);
57
59
  }
58
- function serializeEnvelope(event, data) {
60
+ function isBlobValue(value) {
61
+ return typeof Blob !== "undefined" && value instanceof Blob;
62
+ }
63
+ function isPlainObject(value) {
64
+ if (typeof value !== "object" || value === null) {
65
+ return false;
66
+ }
67
+ const prototype = Object.getPrototypeOf(value);
68
+ return prototype === Object.prototype || prototype === null;
69
+ }
70
+ function encodeBinaryPayload(kind, payloadBuffer, mimeType) {
71
+ const encodedPayload = {
72
+ [BINARY_PAYLOAD_MARKER]: BINARY_PAYLOAD_VERSION,
73
+ kind,
74
+ base64: payloadBuffer.toString("base64")
75
+ };
76
+ if (mimeType !== void 0 && mimeType.length > 0) {
77
+ encodedPayload.mimeType = mimeType;
78
+ }
79
+ return encodedPayload;
80
+ }
81
+ async function encodeEnvelopeData(value) {
82
+ if (Buffer.isBuffer(value)) {
83
+ return encodeBinaryPayload("buffer", value);
84
+ }
85
+ if (value instanceof Uint8Array) {
86
+ const typedArrayBuffer = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
87
+ return encodeBinaryPayload("uint8array", typedArrayBuffer);
88
+ }
89
+ if (isBlobValue(value)) {
90
+ const blobBuffer = Buffer.from(await value.arrayBuffer());
91
+ return encodeBinaryPayload("blob", blobBuffer, value.type);
92
+ }
93
+ if (Array.isArray(value)) {
94
+ return Promise.all(value.map((item) => encodeEnvelopeData(item)));
95
+ }
96
+ if (isPlainObject(value)) {
97
+ const encodedEntries = await Promise.all(
98
+ Object.entries(value).map(async ([key, entryValue]) => {
99
+ return [key, await encodeEnvelopeData(entryValue)];
100
+ })
101
+ );
102
+ return Object.fromEntries(encodedEntries);
103
+ }
104
+ return value;
105
+ }
106
+ function isEncodedBinaryPayload(value) {
107
+ if (!isPlainObject(value)) {
108
+ return false;
109
+ }
110
+ if (value[BINARY_PAYLOAD_MARKER] !== BINARY_PAYLOAD_VERSION) {
111
+ return false;
112
+ }
113
+ if (value.kind !== "buffer" && value.kind !== "uint8array" && value.kind !== "blob") {
114
+ return false;
115
+ }
116
+ if (typeof value.base64 !== "string") {
117
+ return false;
118
+ }
119
+ if (value.mimeType !== void 0 && typeof value.mimeType !== "string") {
120
+ return false;
121
+ }
122
+ return true;
123
+ }
124
+ function decodeEnvelopeData(value) {
125
+ if (Array.isArray(value)) {
126
+ return value.map((item) => decodeEnvelopeData(item));
127
+ }
128
+ if (isEncodedBinaryPayload(value)) {
129
+ const binaryBuffer = Buffer.from(value.base64, "base64");
130
+ if (value.kind === "buffer") {
131
+ return binaryBuffer;
132
+ }
133
+ if (value.kind === "uint8array") {
134
+ return Uint8Array.from(binaryBuffer);
135
+ }
136
+ if (typeof Blob === "undefined") {
137
+ return binaryBuffer;
138
+ }
139
+ return new Blob([binaryBuffer], {
140
+ type: value.mimeType ?? ""
141
+ });
142
+ }
143
+ if (isPlainObject(value)) {
144
+ const decodedEntries = Object.entries(value).map(([key, entryValue]) => {
145
+ return [key, decodeEnvelopeData(entryValue)];
146
+ });
147
+ return Object.fromEntries(decodedEntries);
148
+ }
149
+ return value;
150
+ }
151
+ async function serializeEnvelope(event, data) {
152
+ const encodedData = await encodeEnvelopeData(data);
153
+ const envelope = { event, data: encodedData };
154
+ return JSON.stringify(envelope);
155
+ }
156
+ function serializePlainEnvelope(event, data) {
59
157
  const envelope = { event, data };
60
158
  return JSON.stringify(envelope);
61
159
  }
@@ -67,7 +165,7 @@ function parseEnvelope(rawData) {
67
165
  }
68
166
  return {
69
167
  event: parsed.event,
70
- data: parsed.data
168
+ data: decodeEnvelopeData(parsed.data)
71
169
  };
72
170
  }
73
171
  function parseEnvelopeFromText(decodedPayload) {
@@ -77,7 +175,7 @@ function parseEnvelopeFromText(decodedPayload) {
77
175
  }
78
176
  return {
79
177
  event: parsed.event,
80
- data: parsed.data
178
+ data: decodeEnvelopeData(parsed.data)
81
179
  };
82
180
  }
83
181
  function decodeCloseReason(reason) {
@@ -355,7 +453,9 @@ var SecureServer = class {
355
453
  }
356
454
  const envelope = { event, data };
357
455
  for (const client of this.clientsById.values()) {
358
- this.sendOrQueuePayload(client.socket, envelope);
456
+ void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
457
+ return void 0;
458
+ });
359
459
  }
360
460
  } catch (error) {
361
461
  this.notifyError(normalizeToError(error, "Failed to emit server event."));
@@ -373,7 +473,9 @@ var SecureServer = class {
373
473
  throw new Error(`Client with id ${clientId} was not found.`);
374
474
  }
375
475
  if (!ackArgs.expectsAck) {
376
- this.sendOrQueuePayload(client.socket, { event, data });
476
+ void this.sendOrQueuePayload(client.socket, { event, data }).catch(() => {
477
+ return void 0;
478
+ });
377
479
  return true;
378
480
  }
379
481
  const ackPromise = this.sendRpcRequest(
@@ -686,22 +788,24 @@ var SecureServer = class {
686
788
  this.notifyError(normalizeToError(error, "Failed to send server payload."));
687
789
  }
688
790
  }
689
- sendEncryptedEnvelope(socket, envelope) {
791
+ async sendEncryptedEnvelope(socket, envelope) {
792
+ if (socket.readyState !== WebSocket.OPEN) {
793
+ return;
794
+ }
795
+ const encryptionKey = this.encryptionKeyBySocket.get(socket);
796
+ if (!encryptionKey) {
797
+ const missingKeyError = new Error("Missing encryption key for connected socket.");
798
+ this.notifyError(missingKeyError);
799
+ throw missingKeyError;
800
+ }
690
801
  try {
691
- if (socket.readyState !== WebSocket.OPEN) {
692
- return;
693
- }
694
- const encryptionKey = this.encryptionKeyBySocket.get(socket);
695
- if (!encryptionKey) {
696
- throw new Error("Missing encryption key for connected socket.");
697
- }
698
- const encryptedPayload = encryptSerializedEnvelope(
699
- serializeEnvelope(envelope.event, envelope.data),
700
- encryptionKey
701
- );
802
+ const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
803
+ const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
702
804
  socket.send(encryptedPayload);
703
805
  } catch (error) {
704
- this.notifyError(normalizeToError(error, "Failed to send encrypted server payload."));
806
+ const normalizedError = normalizeToError(error, "Failed to send encrypted server payload.");
807
+ this.notifyError(normalizedError);
808
+ throw normalizedError;
705
809
  }
706
810
  }
707
811
  sendRpcRequest(socket, event, data, timeoutMs) {
@@ -722,13 +826,19 @@ var SecureServer = class {
722
826
  reject,
723
827
  timeoutHandle
724
828
  });
725
- this.sendOrQueuePayload(socket, {
829
+ void this.sendOrQueuePayload(socket, {
726
830
  event: INTERNAL_RPC_REQUEST_EVENT,
727
831
  data: {
728
832
  id: requestId,
729
833
  event,
730
834
  data
731
835
  }
836
+ }).catch((error) => {
837
+ clearTimeout(timeoutHandle);
838
+ pendingRequests.delete(requestId);
839
+ reject(
840
+ normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
841
+ );
732
842
  });
733
843
  });
734
844
  }
@@ -770,7 +880,7 @@ var SecureServer = class {
770
880
  rpcRequestPayload.data,
771
881
  client
772
882
  );
773
- this.sendEncryptedEnvelope(client.socket, {
883
+ await this.sendEncryptedEnvelope(client.socket, {
774
884
  event: INTERNAL_RPC_RESPONSE_EVENT,
775
885
  data: {
776
886
  id: rpcRequestPayload.id,
@@ -780,7 +890,7 @@ var SecureServer = class {
780
890
  });
781
891
  } catch (error) {
782
892
  const normalizedError = normalizeToError(error, "Server ACK request handler failed.");
783
- this.sendEncryptedEnvelope(client.socket, {
893
+ await this.sendEncryptedEnvelope(client.socket, {
784
894
  event: INTERNAL_RPC_RESPONSE_EVENT,
785
895
  data: {
786
896
  id: rpcRequestPayload.id,
@@ -854,7 +964,7 @@ var SecureServer = class {
854
964
  sendInternalHandshake(socket, localPublicKey) {
855
965
  this.sendRaw(
856
966
  socket,
857
- serializeEnvelope(INTERNAL_HANDSHAKE_EVENT, {
967
+ serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
858
968
  publicKey: localPublicKey
859
969
  })
860
970
  );
@@ -887,23 +997,23 @@ var SecureServer = class {
887
997
  sendOrQueuePayload(socket, envelope) {
888
998
  if (!this.isClientHandshakeReady(socket)) {
889
999
  this.queuePayload(socket, envelope);
890
- return;
1000
+ return Promise.resolve();
891
1001
  }
892
- this.sendEncryptedEnvelope(socket, envelope);
1002
+ return this.sendEncryptedEnvelope(socket, envelope);
893
1003
  }
894
1004
  queuePayload(socket, envelope) {
895
1005
  const pendingPayloads = this.pendingPayloadsBySocket.get(socket) ?? [];
896
1006
  pendingPayloads.push(envelope);
897
1007
  this.pendingPayloadsBySocket.set(socket, pendingPayloads);
898
1008
  }
899
- flushQueuedPayloads(socket) {
1009
+ async flushQueuedPayloads(socket) {
900
1010
  const pendingPayloads = this.pendingPayloadsBySocket.get(socket);
901
1011
  if (!pendingPayloads || pendingPayloads.length === 0) {
902
1012
  return;
903
1013
  }
904
1014
  this.pendingPayloadsBySocket.delete(socket);
905
1015
  for (const envelope of pendingPayloads) {
906
- this.sendEncryptedEnvelope(socket, envelope);
1016
+ await this.sendEncryptedEnvelope(socket, envelope);
907
1017
  }
908
1018
  }
909
1019
  createSecureServerClient(clientId, socket, request) {
@@ -1013,7 +1123,9 @@ var SecureServer = class {
1013
1123
  if (!client) {
1014
1124
  continue;
1015
1125
  }
1016
- this.sendOrQueuePayload(client.socket, envelope);
1126
+ void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
1127
+ return void 0;
1128
+ });
1017
1129
  }
1018
1130
  }
1019
1131
  };
@@ -1177,7 +1289,9 @@ var SecureClient = class {
1177
1289
  this.pendingPayloadQueue.push(envelope);
1178
1290
  return true;
1179
1291
  }
1180
- this.sendEncryptedEnvelope(envelope);
1292
+ void this.sendEncryptedEnvelope(envelope).catch(() => {
1293
+ return void 0;
1294
+ });
1181
1295
  return true;
1182
1296
  } catch (error) {
1183
1297
  const normalizedError = normalizeToError(error, "Failed to emit client event.");
@@ -1423,22 +1537,26 @@ var SecureClient = class {
1423
1537
  }
1424
1538
  }
1425
1539
  }
1426
- sendEncryptedEnvelope(envelope) {
1540
+ async sendEncryptedEnvelope(envelope) {
1541
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
1542
+ const socketStateError = new Error("Client socket is not connected.");
1543
+ this.notifyError(socketStateError);
1544
+ throw socketStateError;
1545
+ }
1546
+ const encryptionKey = this.handshakeState?.encryptionKey;
1547
+ if (!encryptionKey) {
1548
+ const missingKeyError = new Error("Missing encryption key for client payload encryption.");
1549
+ this.notifyError(missingKeyError);
1550
+ throw missingKeyError;
1551
+ }
1427
1552
  try {
1428
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
1429
- throw new Error("Client socket is not connected.");
1430
- }
1431
- const encryptionKey = this.handshakeState?.encryptionKey;
1432
- if (!encryptionKey) {
1433
- throw new Error("Missing encryption key for client payload encryption.");
1434
- }
1435
- const encryptedPayload = encryptSerializedEnvelope(
1436
- serializeEnvelope(envelope.event, envelope.data),
1437
- encryptionKey
1438
- );
1553
+ const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
1554
+ const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
1439
1555
  this.socket.send(encryptedPayload);
1440
1556
  } catch (error) {
1441
- this.notifyError(normalizeToError(error, "Failed to send encrypted client payload."));
1557
+ const normalizedError = normalizeToError(error, "Failed to send encrypted client payload.");
1558
+ this.notifyError(normalizedError);
1559
+ throw normalizedError;
1442
1560
  }
1443
1561
  }
1444
1562
  sendRpcRequest(event, data, timeoutMs) {
@@ -1469,7 +1587,13 @@ var SecureClient = class {
1469
1587
  this.pendingPayloadQueue.push(rpcRequestEnvelope);
1470
1588
  return;
1471
1589
  }
1472
- this.sendEncryptedEnvelope(rpcRequestEnvelope);
1590
+ void this.sendEncryptedEnvelope(rpcRequestEnvelope).catch((error) => {
1591
+ clearTimeout(timeoutHandle);
1592
+ this.pendingRpcRequests.delete(requestId);
1593
+ reject(
1594
+ normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
1595
+ );
1596
+ });
1473
1597
  });
1474
1598
  }
1475
1599
  handleRpcResponse(data) {
@@ -1505,7 +1629,7 @@ var SecureClient = class {
1505
1629
  rpcRequestPayload.event,
1506
1630
  rpcRequestPayload.data
1507
1631
  );
1508
- this.sendEncryptedEnvelope({
1632
+ await this.sendEncryptedEnvelope({
1509
1633
  event: INTERNAL_RPC_RESPONSE_EVENT,
1510
1634
  data: {
1511
1635
  id: rpcRequestPayload.id,
@@ -1515,7 +1639,7 @@ var SecureClient = class {
1515
1639
  });
1516
1640
  } catch (error) {
1517
1641
  const normalizedError = normalizeToError(error, "Client ACK request handler failed.");
1518
- this.sendEncryptedEnvelope({
1642
+ await this.sendEncryptedEnvelope({
1519
1643
  event: INTERNAL_RPC_RESPONSE_EVENT,
1520
1644
  data: {
1521
1645
  id: rpcRequestPayload.id,
@@ -1560,7 +1684,7 @@ var SecureClient = class {
1560
1684
  throw new Error("Missing client handshake state.");
1561
1685
  }
1562
1686
  this.socket.send(
1563
- serializeEnvelope(INTERNAL_HANDSHAKE_EVENT, {
1687
+ serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
1564
1688
  publicKey: this.handshakeState.localPublicKey
1565
1689
  })
1566
1690
  );
@@ -1582,7 +1706,7 @@ var SecureClient = class {
1582
1706
  this.handshakeState.sharedSecret = sharedSecret;
1583
1707
  this.handshakeState.encryptionKey = deriveEncryptionKey(sharedSecret);
1584
1708
  this.handshakeState.isReady = true;
1585
- this.flushPendingPayloadQueue();
1709
+ void this.flushPendingPayloadQueue();
1586
1710
  this.notifyReady();
1587
1711
  } catch (error) {
1588
1712
  this.notifyError(normalizeToError(error, "Failed to complete client handshake."));
@@ -1591,14 +1715,14 @@ var SecureClient = class {
1591
1715
  isHandshakeReady() {
1592
1716
  return this.handshakeState?.isReady ?? false;
1593
1717
  }
1594
- flushPendingPayloadQueue() {
1718
+ async flushPendingPayloadQueue() {
1595
1719
  if (!this.socket || this.socket.readyState !== WebSocket.OPEN || !this.isHandshakeReady()) {
1596
1720
  return;
1597
1721
  }
1598
1722
  const pendingPayloads = this.pendingPayloadQueue;
1599
1723
  this.pendingPayloadQueue = [];
1600
1724
  for (const envelope of pendingPayloads) {
1601
- this.sendEncryptedEnvelope(envelope);
1725
+ await this.sendEncryptedEnvelope(envelope);
1602
1726
  }
1603
1727
  }
1604
1728
  };