@aegis-fluxion/core 0.7.1 → 0.7.2

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
@@ -1,6 +1,7 @@
1
1
  import { IncomingMessage } from 'node:http';
2
2
  import WebSocket, { ClientOptions, ServerOptions } from 'ws';
3
3
 
4
+ declare const SECURE_SERVER_ADAPTER_MESSAGE_VERSION = 1;
4
5
  interface SecureEnvelope<TData = unknown> {
5
6
  event: string;
6
7
  data: TData;
@@ -28,9 +29,25 @@ interface SecureServerRateLimitOptions {
28
29
  disconnectCode?: number;
29
30
  disconnectReason?: string;
30
31
  }
32
+ type SecureServerAdapterMessageScope = "broadcast" | "room";
33
+ interface SecureServerAdapterMessage {
34
+ version: typeof SECURE_SERVER_ADAPTER_MESSAGE_VERSION;
35
+ originServerId: string;
36
+ scope: SecureServerAdapterMessageScope;
37
+ event: string;
38
+ data: unknown;
39
+ emittedAt: number;
40
+ room?: string;
41
+ }
42
+ interface SecureServerAdapter {
43
+ attach: (server: SecureServer) => void | Promise<void>;
44
+ publish: (message: SecureServerAdapterMessage) => void | Promise<void>;
45
+ detach?: (server: SecureServer) => void | Promise<void>;
46
+ }
31
47
  interface SecureServerOptions extends ServerOptions {
32
48
  heartbeat?: SecureServerHeartbeatOptions;
33
49
  rateLimit?: SecureServerRateLimitOptions;
50
+ adapter?: SecureServerAdapter;
34
51
  }
35
52
  interface SecureClientReconnectOptions {
36
53
  enabled?: boolean;
@@ -98,8 +115,11 @@ interface SecureServerMessageMiddlewareContext {
98
115
  type SecureServerMiddlewareContext = SecureServerConnectionMiddlewareContext | SecureServerMessageMiddlewareContext;
99
116
  type SecureServerMiddlewareNext = () => Promise<void>;
100
117
  type SecureServerMiddleware = (context: SecureServerMiddlewareContext, next: SecureServerMiddlewareNext) => void | Promise<void>;
118
+ declare function normalizeSecureServerAdapterMessage(value: unknown): SecureServerAdapterMessage;
101
119
  declare class SecureServer {
120
+ private readonly instanceId;
102
121
  private readonly socketServer;
122
+ private adapter;
103
123
  private readonly heartbeatConfig;
104
124
  private readonly rateLimitConfig;
105
125
  private heartbeatIntervalHandle;
@@ -125,7 +145,10 @@ declare class SecureServer {
125
145
  private readonly rateLimitBucketsByIp;
126
146
  constructor(options: SecureServerOptions);
127
147
  get clientCount(): number;
148
+ get serverId(): string;
128
149
  get clients(): ReadonlyMap<string, SecureServerClient>;
150
+ setAdapter(adapter: SecureServerAdapter | null): Promise<void>;
151
+ handleAdapterMessage(message: unknown): Promise<void>;
129
152
  on(event: "connection", handler: SecureServerConnectionHandler): this;
130
153
  on(event: "disconnect", handler: SecureServerDisconnectHandler): this;
131
154
  on(event: "ready", handler: SecureServerReadyHandler): this;
@@ -185,6 +208,8 @@ declare class SecureServer {
185
208
  private queuePayload;
186
209
  private flushQueuedPayloads;
187
210
  private createSecureServerClient;
211
+ private emitLocally;
212
+ private publishAdapterMessage;
188
213
  private normalizeRoomName;
189
214
  private joinClientToRoom;
190
215
  private leaveClientFromRoom;
@@ -251,4 +276,4 @@ declare class SecureClient {
251
276
  private flushPendingPayloadQueue;
252
277
  }
253
278
 
254
- 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 SecureServerConnectionMiddlewareContext, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerMessageMiddlewareContext, type SecureServerMiddleware, type SecureServerMiddlewareContext, type SecureServerMiddlewareNext, type SecureServerOptions, type SecureServerRateLimitAction, type SecureServerRateLimitOptions, type SecureServerReadyHandler, type SecureServerRoomOperator };
279
+ 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 SecureServerAdapter, type SecureServerAdapterMessage, type SecureServerAdapterMessageScope, type SecureServerClient, type SecureServerConnectionHandler, type SecureServerConnectionMiddlewareContext, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerMessageMiddlewareContext, type SecureServerMiddleware, type SecureServerMiddlewareContext, type SecureServerMiddlewareNext, type SecureServerOptions, type SecureServerRateLimitAction, type SecureServerRateLimitOptions, type SecureServerReadyHandler, type SecureServerRoomOperator, normalizeSecureServerAdapterMessage };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { IncomingMessage } from 'node:http';
2
2
  import WebSocket, { ClientOptions, ServerOptions } from 'ws';
3
3
 
4
+ declare const SECURE_SERVER_ADAPTER_MESSAGE_VERSION = 1;
4
5
  interface SecureEnvelope<TData = unknown> {
5
6
  event: string;
6
7
  data: TData;
@@ -28,9 +29,25 @@ interface SecureServerRateLimitOptions {
28
29
  disconnectCode?: number;
29
30
  disconnectReason?: string;
30
31
  }
32
+ type SecureServerAdapterMessageScope = "broadcast" | "room";
33
+ interface SecureServerAdapterMessage {
34
+ version: typeof SECURE_SERVER_ADAPTER_MESSAGE_VERSION;
35
+ originServerId: string;
36
+ scope: SecureServerAdapterMessageScope;
37
+ event: string;
38
+ data: unknown;
39
+ emittedAt: number;
40
+ room?: string;
41
+ }
42
+ interface SecureServerAdapter {
43
+ attach: (server: SecureServer) => void | Promise<void>;
44
+ publish: (message: SecureServerAdapterMessage) => void | Promise<void>;
45
+ detach?: (server: SecureServer) => void | Promise<void>;
46
+ }
31
47
  interface SecureServerOptions extends ServerOptions {
32
48
  heartbeat?: SecureServerHeartbeatOptions;
33
49
  rateLimit?: SecureServerRateLimitOptions;
50
+ adapter?: SecureServerAdapter;
34
51
  }
35
52
  interface SecureClientReconnectOptions {
36
53
  enabled?: boolean;
@@ -98,8 +115,11 @@ interface SecureServerMessageMiddlewareContext {
98
115
  type SecureServerMiddlewareContext = SecureServerConnectionMiddlewareContext | SecureServerMessageMiddlewareContext;
99
116
  type SecureServerMiddlewareNext = () => Promise<void>;
100
117
  type SecureServerMiddleware = (context: SecureServerMiddlewareContext, next: SecureServerMiddlewareNext) => void | Promise<void>;
118
+ declare function normalizeSecureServerAdapterMessage(value: unknown): SecureServerAdapterMessage;
101
119
  declare class SecureServer {
120
+ private readonly instanceId;
102
121
  private readonly socketServer;
122
+ private adapter;
103
123
  private readonly heartbeatConfig;
104
124
  private readonly rateLimitConfig;
105
125
  private heartbeatIntervalHandle;
@@ -125,7 +145,10 @@ declare class SecureServer {
125
145
  private readonly rateLimitBucketsByIp;
126
146
  constructor(options: SecureServerOptions);
127
147
  get clientCount(): number;
148
+ get serverId(): string;
128
149
  get clients(): ReadonlyMap<string, SecureServerClient>;
150
+ setAdapter(adapter: SecureServerAdapter | null): Promise<void>;
151
+ handleAdapterMessage(message: unknown): Promise<void>;
129
152
  on(event: "connection", handler: SecureServerConnectionHandler): this;
130
153
  on(event: "disconnect", handler: SecureServerDisconnectHandler): this;
131
154
  on(event: "ready", handler: SecureServerReadyHandler): this;
@@ -185,6 +208,8 @@ declare class SecureServer {
185
208
  private queuePayload;
186
209
  private flushQueuedPayloads;
187
210
  private createSecureServerClient;
211
+ private emitLocally;
212
+ private publishAdapterMessage;
188
213
  private normalizeRoomName;
189
214
  private joinClientToRoom;
190
215
  private leaveClientFromRoom;
@@ -251,4 +276,4 @@ declare class SecureClient {
251
276
  private flushPendingPayloadQueue;
252
277
  }
253
278
 
254
- 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 SecureServerConnectionMiddlewareContext, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerMessageMiddlewareContext, type SecureServerMiddleware, type SecureServerMiddlewareContext, type SecureServerMiddlewareNext, type SecureServerOptions, type SecureServerRateLimitAction, type SecureServerRateLimitOptions, type SecureServerReadyHandler, type SecureServerRoomOperator };
279
+ 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 SecureServerAdapter, type SecureServerAdapterMessage, type SecureServerAdapterMessageScope, type SecureServerClient, type SecureServerConnectionHandler, type SecureServerConnectionMiddlewareContext, type SecureServerDisconnectHandler, type SecureServerEventHandler, type SecureServerEventMap, type SecureServerHeartbeatOptions, type SecureServerLifecycleEvent, type SecureServerMessageMiddlewareContext, type SecureServerMiddleware, type SecureServerMiddlewareContext, type SecureServerMiddlewareNext, type SecureServerOptions, type SecureServerRateLimitAction, type SecureServerRateLimitOptions, type SecureServerReadyHandler, type SecureServerRoomOperator, normalizeSecureServerAdapterMessage };
package/dist/index.js CHANGED
@@ -34,6 +34,51 @@ var DEFAULT_RATE_LIMIT_MAX_THROTTLE_MS = 2e3;
34
34
  var DEFAULT_RATE_LIMIT_DISCONNECT_AFTER_VIOLATIONS = 4;
35
35
  var DEFAULT_RATE_LIMIT_CLOSE_CODE = 1013;
36
36
  var DEFAULT_RATE_LIMIT_CLOSE_REASON = "Rate limit exceeded. Please retry later.";
37
+ var SECURE_SERVER_ADAPTER_MESSAGE_VERSION = 1;
38
+ function normalizeSecureServerAdapterMessage(value) {
39
+ if (!isPlainObject(value)) {
40
+ throw new Error("SecureServer adapter message must be a plain object.");
41
+ }
42
+ if (value.version !== SECURE_SERVER_ADAPTER_MESSAGE_VERSION) {
43
+ throw new Error(
44
+ `Unsupported SecureServer adapter message version: ${String(value.version)}.`
45
+ );
46
+ }
47
+ if (typeof value.originServerId !== "string" || value.originServerId.trim().length === 0) {
48
+ throw new Error("SecureServer adapter message originServerId must be a non-empty string.");
49
+ }
50
+ if (value.scope !== "broadcast" && value.scope !== "room") {
51
+ throw new Error('SecureServer adapter message scope must be either "broadcast" or "room".');
52
+ }
53
+ if (typeof value.event !== "string" || value.event.trim().length === 0) {
54
+ throw new Error("SecureServer adapter message event must be a non-empty string.");
55
+ }
56
+ if (typeof value.emittedAt !== "number" || !Number.isFinite(value.emittedAt)) {
57
+ throw new Error("SecureServer adapter message emittedAt must be a finite number.");
58
+ }
59
+ if (value.scope === "room") {
60
+ if (typeof value.room !== "string" || value.room.trim().length === 0) {
61
+ throw new Error("SecureServer adapter message room must be a non-empty string.");
62
+ }
63
+ return {
64
+ version: SECURE_SERVER_ADAPTER_MESSAGE_VERSION,
65
+ originServerId: value.originServerId,
66
+ scope: value.scope,
67
+ event: value.event,
68
+ data: value.data,
69
+ emittedAt: value.emittedAt,
70
+ room: value.room.trim()
71
+ };
72
+ }
73
+ return {
74
+ version: SECURE_SERVER_ADAPTER_MESSAGE_VERSION,
75
+ originServerId: value.originServerId,
76
+ scope: value.scope,
77
+ event: value.event,
78
+ data: value.data,
79
+ emittedAt: value.emittedAt
80
+ };
81
+ }
37
82
  function normalizeToError(error, fallbackMessage) {
38
83
  if (error instanceof Error) {
39
84
  return error;
@@ -362,7 +407,9 @@ function decryptSerializedEnvelope(rawData, encryptionKey) {
362
407
  return plaintext.toString("utf8");
363
408
  }
364
409
  var SecureServer = class {
410
+ instanceId = randomUUID();
365
411
  socketServer;
412
+ adapter = null;
366
413
  heartbeatConfig;
367
414
  rateLimitConfig;
368
415
  heartbeatIntervalHandle = null;
@@ -387,19 +434,76 @@ var SecureServer = class {
387
434
  rateLimitBucketsByClientId = /* @__PURE__ */ new Map();
388
435
  rateLimitBucketsByIp = /* @__PURE__ */ new Map();
389
436
  constructor(options) {
390
- const { heartbeat, rateLimit, ...socketServerOptions } = options;
437
+ const { heartbeat, rateLimit, adapter, ...socketServerOptions } = options;
391
438
  this.heartbeatConfig = this.resolveHeartbeatConfig(heartbeat);
392
439
  this.rateLimitConfig = this.resolveRateLimitConfig(rateLimit);
393
440
  this.socketServer = new WebSocketServer(socketServerOptions);
394
441
  this.bindSocketServerEvents();
395
442
  this.startHeartbeatLoop();
443
+ if (adapter) {
444
+ void this.setAdapter(adapter).catch(() => {
445
+ return void 0;
446
+ });
447
+ }
396
448
  }
397
449
  get clientCount() {
398
450
  return this.clientsById.size;
399
451
  }
452
+ get serverId() {
453
+ return this.instanceId;
454
+ }
400
455
  get clients() {
401
456
  return this.clientsById;
402
457
  }
458
+ async setAdapter(adapter) {
459
+ const previousAdapter = this.adapter;
460
+ if (previousAdapter === adapter) {
461
+ return;
462
+ }
463
+ try {
464
+ if (previousAdapter?.detach) {
465
+ await Promise.resolve(previousAdapter.detach(this));
466
+ }
467
+ this.adapter = null;
468
+ if (!adapter) {
469
+ return;
470
+ }
471
+ await Promise.resolve(adapter.attach(this));
472
+ this.adapter = adapter;
473
+ } catch (error) {
474
+ const normalizedError = normalizeToError(
475
+ error,
476
+ "Failed to set SecureServer adapter."
477
+ );
478
+ this.notifyError(normalizedError);
479
+ throw normalizedError;
480
+ }
481
+ }
482
+ async handleAdapterMessage(message) {
483
+ try {
484
+ const normalizedMessage = normalizeSecureServerAdapterMessage(message);
485
+ if (normalizedMessage.originServerId === this.instanceId) {
486
+ return;
487
+ }
488
+ if (normalizedMessage.scope === "broadcast") {
489
+ this.emitLocally(normalizedMessage.event, normalizedMessage.data);
490
+ return;
491
+ }
492
+ if (!normalizedMessage.room) {
493
+ return;
494
+ }
495
+ this.emitToRoom(
496
+ normalizedMessage.room,
497
+ normalizedMessage.event,
498
+ normalizedMessage.data,
499
+ false
500
+ );
501
+ } catch (error) {
502
+ this.notifyError(
503
+ normalizeToError(error, "Failed to process SecureServer adapter message.")
504
+ );
505
+ }
506
+ }
403
507
  on(event, handler) {
404
508
  try {
405
509
  if (event === "connection") {
@@ -486,12 +590,12 @@ var SecureServer = class {
486
590
  if (isReservedEmitEvent(event)) {
487
591
  throw new Error(`The event "${event}" is reserved and cannot be emitted manually.`);
488
592
  }
489
- const envelope = { event, data };
490
- for (const client of this.clientsById.values()) {
491
- void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
492
- return void 0;
493
- });
494
- }
593
+ this.emitLocally(event, data);
594
+ this.publishAdapterMessage({
595
+ scope: "broadcast",
596
+ event,
597
+ data
598
+ });
495
599
  } catch (error) {
496
600
  this.notifyError(normalizeToError(error, "Failed to emit server event."));
497
601
  }
@@ -548,7 +652,7 @@ var SecureServer = class {
548
652
  return {
549
653
  emit: (event, data) => {
550
654
  try {
551
- this.emitToRoom(normalizedRoom, event, data);
655
+ this.emitToRoom(normalizedRoom, event, data, true);
552
656
  } catch (error) {
553
657
  this.notifyError(
554
658
  normalizeToError(error, `Failed to emit event to room ${normalizedRoom}.`)
@@ -561,6 +665,15 @@ var SecureServer = class {
561
665
  close(code = DEFAULT_CLOSE_CODE, reason = DEFAULT_CLOSE_REASON) {
562
666
  try {
563
667
  this.stopHeartbeatLoop();
668
+ const activeAdapter = this.adapter;
669
+ this.adapter = null;
670
+ if (activeAdapter?.detach) {
671
+ void Promise.resolve(activeAdapter.detach(this)).catch((error) => {
672
+ this.notifyError(
673
+ normalizeToError(error, "Failed to detach SecureServer adapter during close.")
674
+ );
675
+ });
676
+ }
564
677
  for (const client of this.clientsById.values()) {
565
678
  this.rejectPendingRpcRequests(
566
679
  client.socket,
@@ -1429,6 +1542,48 @@ var SecureServer = class {
1429
1542
  leaveAll: () => this.leaveClientFromAllRooms(clientId)
1430
1543
  };
1431
1544
  }
1545
+ emitLocally(event, data) {
1546
+ const envelope = { event, data };
1547
+ for (const client of this.clientsById.values()) {
1548
+ void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
1549
+ return void 0;
1550
+ });
1551
+ }
1552
+ }
1553
+ publishAdapterMessage(message) {
1554
+ if (!this.adapter) {
1555
+ return;
1556
+ }
1557
+ let adapterMessage;
1558
+ if (message.scope === "room") {
1559
+ if (!message.room) {
1560
+ return;
1561
+ }
1562
+ adapterMessage = {
1563
+ version: SECURE_SERVER_ADAPTER_MESSAGE_VERSION,
1564
+ originServerId: this.instanceId,
1565
+ scope: "room",
1566
+ event: message.event,
1567
+ data: message.data,
1568
+ emittedAt: Date.now(),
1569
+ room: message.room
1570
+ };
1571
+ } else {
1572
+ adapterMessage = {
1573
+ version: SECURE_SERVER_ADAPTER_MESSAGE_VERSION,
1574
+ originServerId: this.instanceId,
1575
+ scope: "broadcast",
1576
+ event: message.event,
1577
+ data: message.data,
1578
+ emittedAt: Date.now()
1579
+ };
1580
+ }
1581
+ void Promise.resolve(this.adapter.publish(adapterMessage)).catch((error) => {
1582
+ this.notifyError(
1583
+ normalizeToError(error, "Failed to publish SecureServer adapter message.")
1584
+ );
1585
+ });
1586
+ }
1432
1587
  normalizeRoomName(room) {
1433
1588
  if (typeof room !== "string") {
1434
1589
  throw new Error("Room name must be a string.");
@@ -1494,22 +1649,29 @@ var SecureServer = class {
1494
1649
  }
1495
1650
  return roomNames.length;
1496
1651
  }
1497
- emitToRoom(room, event, data) {
1652
+ emitToRoom(room, event, data, replicate) {
1498
1653
  if (isReservedEmitEvent(event)) {
1499
1654
  throw new Error(`The event "${event}" is reserved and cannot be emitted manually.`);
1500
1655
  }
1501
1656
  const roomMembers = this.roomMembersByName.get(room);
1502
- if (!roomMembers || roomMembers.size === 0) {
1503
- return;
1504
- }
1505
- const envelope = { event, data };
1506
- for (const clientId of roomMembers) {
1507
- const client = this.clientsById.get(clientId);
1508
- if (!client) {
1509
- continue;
1657
+ if (roomMembers && roomMembers.size > 0) {
1658
+ const envelope = { event, data };
1659
+ for (const clientId of roomMembers) {
1660
+ const client = this.clientsById.get(clientId);
1661
+ if (!client) {
1662
+ continue;
1663
+ }
1664
+ void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
1665
+ return void 0;
1666
+ });
1510
1667
  }
1511
- void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
1512
- return void 0;
1668
+ }
1669
+ if (replicate) {
1670
+ this.publishAdapterMessage({
1671
+ scope: "room",
1672
+ room,
1673
+ event,
1674
+ data
1513
1675
  });
1514
1676
  }
1515
1677
  }
@@ -2112,6 +2274,6 @@ var SecureClient = class {
2112
2274
  }
2113
2275
  };
2114
2276
 
2115
- export { SecureClient, SecureServer };
2277
+ export { SecureClient, SecureServer, normalizeSecureServerAdapterMessage };
2116
2278
  //# sourceMappingURL=index.js.map
2117
2279
  //# sourceMappingURL=index.js.map