@naylence/runtime 0.3.5-test.942 → 0.3.5-test.944

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 (36) hide show
  1. package/dist/browser/index.cjs +380 -8
  2. package/dist/browser/index.mjs +380 -8
  3. package/dist/cjs/naylence/fame/connector/broadcast-channel-connector-factory.js +12 -0
  4. package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +69 -1
  5. package/dist/cjs/naylence/fame/connector/broadcast-channel-listener.js +35 -0
  6. package/dist/cjs/naylence/fame/connector/inpage-connector-factory.js +12 -0
  7. package/dist/cjs/naylence/fame/connector/inpage-connector.js +66 -1
  8. package/dist/cjs/naylence/fame/connector/inpage-listener.js +49 -2
  9. package/dist/cjs/naylence/fame/connector/transport-frame.js +101 -0
  10. package/dist/cjs/naylence/fame/grants/broadcast-channel-connection-grant.js +28 -0
  11. package/dist/cjs/naylence/fame/grants/inpage-connection-grant.js +28 -0
  12. package/dist/cjs/version.js +2 -2
  13. package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +12 -0
  14. package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +69 -1
  15. package/dist/esm/naylence/fame/connector/broadcast-channel-listener.js +35 -0
  16. package/dist/esm/naylence/fame/connector/inpage-connector-factory.js +12 -0
  17. package/dist/esm/naylence/fame/connector/inpage-connector.js +66 -1
  18. package/dist/esm/naylence/fame/connector/inpage-listener.js +49 -2
  19. package/dist/esm/naylence/fame/connector/transport-frame.js +94 -0
  20. package/dist/esm/naylence/fame/grants/broadcast-channel-connection-grant.js +28 -0
  21. package/dist/esm/naylence/fame/grants/inpage-connection-grant.js +28 -0
  22. package/dist/esm/version.js +2 -2
  23. package/dist/node/index.cjs +380 -8
  24. package/dist/node/index.mjs +380 -8
  25. package/dist/node/node.cjs +396 -8
  26. package/dist/node/node.mjs +396 -8
  27. package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +2 -0
  28. package/dist/types/naylence/fame/connector/broadcast-channel-connector.browser.d.ts +4 -0
  29. package/dist/types/naylence/fame/connector/inpage-connector-factory.d.ts +2 -0
  30. package/dist/types/naylence/fame/connector/inpage-connector.d.ts +4 -0
  31. package/dist/types/naylence/fame/connector/inpage-listener.d.ts +1 -0
  32. package/dist/types/naylence/fame/connector/transport-frame.d.ts +58 -0
  33. package/dist/types/naylence/fame/grants/broadcast-channel-connection-grant.d.ts +6 -0
  34. package/dist/types/naylence/fame/grants/inpage-connection-grant.d.ts +8 -0
  35. package/dist/types/version.d.ts +1 -1
  36. package/package.json +1 -1
@@ -96,12 +96,12 @@ installProcessEnvShim();
96
96
  // --- END ENV SHIM ---
97
97
 
98
98
  // This file is auto-generated during build - do not edit manually
99
- // Generated from package.json version: 0.3.5-test.942
99
+ // Generated from package.json version: 0.3.5-test.944
100
100
  /**
101
101
  * The package version, injected at build time.
102
102
  * @internal
103
103
  */
104
- const VERSION = '0.3.5-test.942';
104
+ const VERSION = '0.3.5-test.944';
105
105
 
106
106
  /**
107
107
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9816,6 +9816,85 @@ class BoundedAsyncQueue {
9816
9816
  }
9817
9817
  }
9818
9818
 
9819
+ /**
9820
+ * Transport frame layer for multiplexing logical links on physical channels.
9821
+ *
9822
+ * This lightweight framing layer wraps raw FAME payloads to enable multiple
9823
+ * logical connections over a single physical channel (BroadcastChannel or InPage bus).
9824
+ *
9825
+ * The transport frame does NOT modify FAME envelopes - it only wraps the raw
9826
+ * Uint8Array payload at the connector level.
9827
+ */
9828
+ /**
9829
+ * Transport frame version for future compatibility
9830
+ */
9831
+ const TRANSPORT_FRAME_VERSION = 1;
9832
+ /**
9833
+ * Wrap a raw payload in a transport frame
9834
+ *
9835
+ * @param payload - Raw FAME envelope bytes
9836
+ * @param srcNodeId - Local node ID (this connector)
9837
+ * @param dstNodeId - Remote node ID (target connector)
9838
+ * @returns Transport frame ready for transmission
9839
+ */
9840
+ function wrapTransportFrame(payload, srcNodeId, dstNodeId) {
9841
+ return {
9842
+ v: TRANSPORT_FRAME_VERSION,
9843
+ src: srcNodeId,
9844
+ dst: dstNodeId,
9845
+ payload,
9846
+ };
9847
+ }
9848
+ /**
9849
+ * Serialize a transport frame for transmission over the bus
9850
+ *
9851
+ * @param frame - Transport frame to serialize
9852
+ * @returns Serialized frame data ready for postMessage/dispatchEvent
9853
+ */
9854
+ function serializeTransportFrame(frame) {
9855
+ // Convert Uint8Array to regular array for JSON serialization
9856
+ const serializable = {
9857
+ v: frame.v,
9858
+ src: frame.src,
9859
+ dst: frame.dst,
9860
+ payload: Array.from(frame.payload),
9861
+ };
9862
+ return serializable;
9863
+ }
9864
+ /**
9865
+ * Unwrap a transport frame, validating source and destination
9866
+ *
9867
+ * @param raw - Raw data from the bus
9868
+ * @param localNodeId - This connector's node ID
9869
+ * @param remoteNodeId - Expected remote node ID
9870
+ * @returns Unwrapped payload if frame is valid and addressed to us, null otherwise
9871
+ */
9872
+ function unwrapTransportFrame(raw, localNodeId, remoteNodeId) {
9873
+ // Validate basic structure
9874
+ if (!raw || typeof raw !== 'object') {
9875
+ return null;
9876
+ }
9877
+ const frame = raw;
9878
+ // Check version
9879
+ if (frame.v !== TRANSPORT_FRAME_VERSION) {
9880
+ return null;
9881
+ }
9882
+ // Check src and dst
9883
+ if (typeof frame.src !== 'string' || typeof frame.dst !== 'string') {
9884
+ return null;
9885
+ }
9886
+ // Only accept frames addressed to us from the expected remote
9887
+ if (frame.dst !== localNodeId || frame.src !== remoteNodeId) {
9888
+ return null;
9889
+ }
9890
+ // Extract payload
9891
+ if (!frame.payload || !Array.isArray(frame.payload)) {
9892
+ return null;
9893
+ }
9894
+ // Convert array back to Uint8Array
9895
+ return Uint8Array.from(frame.payload);
9896
+ }
9897
+
9819
9898
  const logger$_ = getLogger('naylence.fame.connector.broadcast_channel_connector');
9820
9899
  const BROADCAST_CHANNEL_CONNECTOR_TYPE = 'broadcast-channel-connector';
9821
9900
  const DEFAULT_CHANNEL$7 = 'naylence-fabric';
@@ -9881,9 +9960,20 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9881
9960
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9882
9961
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9883
9962
  this.channel = new BroadcastChannel(this.channelName);
9963
+ // Set local and remote node IDs (defaults to connector ID for backwards compatibility)
9964
+ this.localNodeId =
9965
+ typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
9966
+ ? config.localNodeId.trim()
9967
+ : this.connectorId;
9968
+ this.remoteNodeId =
9969
+ typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
9970
+ ? config.remoteNodeId.trim()
9971
+ : '*'; // Accept from any remote if not specified
9884
9972
  logger$_.debug('broadcast_channel_connector_created', {
9885
9973
  channel: this.channelName,
9886
9974
  connector_id: this.connectorId,
9975
+ local_node_id: this.localNodeId,
9976
+ remote_node_id: this.remoteNodeId,
9887
9977
  inbox_capacity: preferredCapacity,
9888
9978
  timestamp: new Date().toISOString(),
9889
9979
  });
@@ -9916,6 +10006,46 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9916
10006
  if (busMessage.senderId === this.connectorId) {
9917
10007
  return;
9918
10008
  }
10009
+ // Try to unwrap as transport frame
10010
+ const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
10011
+ if (unwrapped) {
10012
+ // Successfully unwrapped transport frame
10013
+ logger$_.debug('broadcast_channel_transport_frame_received', {
10014
+ channel: this.channelName,
10015
+ sender_id: busMessage.senderId,
10016
+ connector_id: this.connectorId,
10017
+ local_node_id: this.localNodeId,
10018
+ remote_node_id: this.remoteNodeId,
10019
+ payload_length: unwrapped.byteLength,
10020
+ });
10021
+ if (this._shouldSkipDuplicateAck(busMessage.senderId, unwrapped)) {
10022
+ return;
10023
+ }
10024
+ try {
10025
+ if (typeof this.inbox.tryEnqueue === 'function') {
10026
+ const accepted = this.inbox.tryEnqueue(unwrapped);
10027
+ if (accepted) {
10028
+ return;
10029
+ }
10030
+ }
10031
+ this.inbox.enqueue(unwrapped);
10032
+ }
10033
+ catch (error) {
10034
+ if (error instanceof QueueFullError) {
10035
+ logger$_.warning('broadcast_channel_receive_queue_full', {
10036
+ channel: this.channelName,
10037
+ });
10038
+ }
10039
+ else {
10040
+ logger$_.error('broadcast_channel_receive_error', {
10041
+ channel: this.channelName,
10042
+ error: error instanceof Error ? error.message : String(error),
10043
+ });
10044
+ }
10045
+ }
10046
+ return;
10047
+ }
10048
+ // Fall back to legacy format (no transport frame)
9919
10049
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
9920
10050
  if (!payload) {
9921
10051
  logger$_.debug('broadcast_channel_payload_rejected', {
@@ -10054,10 +10184,26 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10054
10184
  logger$_.debug('broadcast_channel_message_sending', {
10055
10185
  channel: this.channelName,
10056
10186
  sender_id: this.connectorId,
10057
- });
10187
+ local_node_id: this.localNodeId,
10188
+ remote_node_id: this.remoteNodeId,
10189
+ });
10190
+ // Only use transport framing if both localNodeId and remoteNodeId are explicitly set
10191
+ // (not using default values). This ensures backwards compatibility.
10192
+ const useTransportFrame = this.localNodeId !== this.connectorId ||
10193
+ this.remoteNodeId !== '*';
10194
+ let payload;
10195
+ if (useTransportFrame) {
10196
+ // Wrap payload in transport frame
10197
+ const frame = wrapTransportFrame(data, this.localNodeId, this.remoteNodeId);
10198
+ payload = serializeTransportFrame(frame);
10199
+ }
10200
+ else {
10201
+ // Legacy format: send raw payload
10202
+ payload = data;
10203
+ }
10058
10204
  this.channel.postMessage({
10059
10205
  senderId: this.connectorId,
10060
- payload: data,
10206
+ payload,
10061
10207
  });
10062
10208
  }
10063
10209
  async _transportReceive() {
@@ -10350,6 +10496,14 @@ function isBroadcastChannelConnectionGrant(candidate) {
10350
10496
  record.inboxCapacity <= 0)) {
10351
10497
  return false;
10352
10498
  }
10499
+ if (record.localNodeId !== undefined &&
10500
+ (typeof record.localNodeId !== 'string' || record.localNodeId.length === 0)) {
10501
+ return false;
10502
+ }
10503
+ if (record.remoteNodeId !== undefined &&
10504
+ (typeof record.remoteNodeId !== 'string' || record.remoteNodeId.length === 0)) {
10505
+ return false;
10506
+ }
10353
10507
  return true;
10354
10508
  }
10355
10509
  function normalizeBroadcastChannelConnectionGrant(candidate) {
@@ -10383,6 +10537,20 @@ function normalizeBroadcastChannelConnectionGrant(candidate) {
10383
10537
  }
10384
10538
  result.inboxCapacity = Math.floor(inboxValue);
10385
10539
  }
10540
+ const localNodeIdValue = candidate.localNodeId ?? candidate['local_node_id'];
10541
+ if (localNodeIdValue !== undefined) {
10542
+ if (typeof localNodeIdValue !== 'string' || localNodeIdValue.trim().length === 0) {
10543
+ throw new TypeError('BroadcastChannelConnectionGrant "localNodeId" must be a non-empty string when provided');
10544
+ }
10545
+ result.localNodeId = localNodeIdValue.trim();
10546
+ }
10547
+ const remoteNodeIdValue = candidate.remoteNodeId ?? candidate['remote_node_id'];
10548
+ if (remoteNodeIdValue !== undefined) {
10549
+ if (typeof remoteNodeIdValue !== 'string' || remoteNodeIdValue.trim().length === 0) {
10550
+ throw new TypeError('BroadcastChannelConnectionGrant "remoteNodeId" must be a non-empty string when provided');
10551
+ }
10552
+ result.remoteNodeId = remoteNodeIdValue.trim();
10553
+ }
10386
10554
  return result;
10387
10555
  }
10388
10556
  function broadcastChannelGrantToConnectorConfig(grant) {
@@ -10396,6 +10564,12 @@ function broadcastChannelGrantToConnectorConfig(grant) {
10396
10564
  if (normalized.inboxCapacity !== undefined) {
10397
10565
  config.inboxCapacity = normalized.inboxCapacity;
10398
10566
  }
10567
+ if (normalized.localNodeId) {
10568
+ config.localNodeId = normalized.localNodeId;
10569
+ }
10570
+ if (normalized.remoteNodeId) {
10571
+ config.remoteNodeId = normalized.remoteNodeId;
10572
+ }
10399
10573
  return config;
10400
10574
  }
10401
10575
 
@@ -20342,9 +20516,20 @@ class InPageConnector extends BaseAsyncConnector {
20342
20516
  : DEFAULT_INBOX_CAPACITY$6;
20343
20517
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
20344
20518
  this.connectorId = InPageConnector.generateConnectorId();
20519
+ // Set local and remote node IDs (defaults to connector ID for backwards compatibility)
20520
+ this.localNodeId =
20521
+ typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
20522
+ ? config.localNodeId.trim()
20523
+ : this.connectorId;
20524
+ this.remoteNodeId =
20525
+ typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
20526
+ ? config.remoteNodeId.trim()
20527
+ : '*'; // Accept from any remote if not specified
20345
20528
  logger$G.debug('inpage_connector_initialized', {
20346
20529
  channel: this.channelName,
20347
20530
  connector_id: this.connectorId,
20531
+ local_node_id: this.localNodeId,
20532
+ remote_node_id: this.remoteNodeId,
20348
20533
  });
20349
20534
  this.onMsg = (event) => {
20350
20535
  const messageEvent = event;
@@ -20378,6 +20563,43 @@ class InPageConnector extends BaseAsyncConnector {
20378
20563
  if (busMessage.senderId === this.connectorId) {
20379
20564
  return;
20380
20565
  }
20566
+ // Try to unwrap as transport frame
20567
+ const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
20568
+ if (unwrapped) {
20569
+ // Successfully unwrapped transport frame
20570
+ logger$G.debug('inpage_transport_frame_received', {
20571
+ channel: this.channelName,
20572
+ sender_id: busMessage.senderId,
20573
+ connector_id: this.connectorId,
20574
+ local_node_id: this.localNodeId,
20575
+ remote_node_id: this.remoteNodeId,
20576
+ payload_length: unwrapped.byteLength,
20577
+ });
20578
+ try {
20579
+ if (typeof this.inbox.tryEnqueue === 'function') {
20580
+ const accepted = this.inbox.tryEnqueue(unwrapped);
20581
+ if (accepted) {
20582
+ return;
20583
+ }
20584
+ }
20585
+ this.inbox.enqueue(unwrapped);
20586
+ }
20587
+ catch (error) {
20588
+ if (error instanceof QueueFullError) {
20589
+ logger$G.warning('inpage_receive_queue_full', {
20590
+ channel: this.channelName,
20591
+ });
20592
+ }
20593
+ else {
20594
+ logger$G.error('inpage_receive_error', {
20595
+ channel: this.channelName,
20596
+ error: error instanceof Error ? error.message : String(error),
20597
+ });
20598
+ }
20599
+ }
20600
+ return;
20601
+ }
20602
+ // Fall back to legacy format (no transport frame)
20381
20603
  const payload = InPageConnector.coercePayload(busMessage.payload);
20382
20604
  if (!payload) {
20383
20605
  logger$G.debug('inpage_payload_rejected', {
@@ -20536,11 +20758,27 @@ class InPageConnector extends BaseAsyncConnector {
20536
20758
  logger$G.debug('inpage_message_sending', {
20537
20759
  channel: this.channelName,
20538
20760
  sender_id: this.connectorId,
20539
- });
20761
+ local_node_id: this.localNodeId,
20762
+ remote_node_id: this.remoteNodeId,
20763
+ });
20764
+ // Only use transport framing if both localNodeId and remoteNodeId are explicitly set
20765
+ // (not using default values). This ensures backwards compatibility.
20766
+ const useTransportFrame = this.localNodeId !== this.connectorId ||
20767
+ this.remoteNodeId !== '*';
20768
+ let payload;
20769
+ if (useTransportFrame) {
20770
+ // Wrap payload in transport frame
20771
+ const frame = wrapTransportFrame(data, this.localNodeId, this.remoteNodeId);
20772
+ payload = serializeTransportFrame(frame);
20773
+ }
20774
+ else {
20775
+ // Legacy format: send raw payload
20776
+ payload = data;
20777
+ }
20540
20778
  const event = new MessageEvent(this.channelName, {
20541
20779
  data: {
20542
20780
  senderId: this.connectorId,
20543
- payload: data,
20781
+ payload,
20544
20782
  },
20545
20783
  });
20546
20784
  getSharedBus$1().dispatchEvent(event);
@@ -27905,6 +28143,14 @@ function isInPageConnectionGrant(candidate) {
27905
28143
  record.inboxCapacity <= 0)) {
27906
28144
  return false;
27907
28145
  }
28146
+ if (record.localNodeId !== undefined &&
28147
+ (typeof record.localNodeId !== 'string' || record.localNodeId.length === 0)) {
28148
+ return false;
28149
+ }
28150
+ if (record.remoteNodeId !== undefined &&
28151
+ (typeof record.remoteNodeId !== 'string' || record.remoteNodeId.length === 0)) {
28152
+ return false;
28153
+ }
27908
28154
  return true;
27909
28155
  }
27910
28156
  function normalizeInPageConnectionGrant(candidate) {
@@ -27938,6 +28184,20 @@ function normalizeInPageConnectionGrant(candidate) {
27938
28184
  }
27939
28185
  result.inboxCapacity = Math.floor(inboxValue);
27940
28186
  }
28187
+ const localNodeIdValue = candidate.localNodeId ?? candidate['local_node_id'];
28188
+ if (localNodeIdValue !== undefined) {
28189
+ if (typeof localNodeIdValue !== 'string' || localNodeIdValue.trim().length === 0) {
28190
+ throw new TypeError('InPageConnectionGrant "localNodeId" must be a non-empty string when provided');
28191
+ }
28192
+ result.localNodeId = localNodeIdValue.trim();
28193
+ }
28194
+ const remoteNodeIdValue = candidate.remoteNodeId ?? candidate['remote_node_id'];
28195
+ if (remoteNodeIdValue !== undefined) {
28196
+ if (typeof remoteNodeIdValue !== 'string' || remoteNodeIdValue.trim().length === 0) {
28197
+ throw new TypeError('InPageConnectionGrant "remoteNodeId" must be a non-empty string when provided');
28198
+ }
28199
+ result.remoteNodeId = remoteNodeIdValue.trim();
28200
+ }
27941
28201
  return result;
27942
28202
  }
27943
28203
  function inPageGrantToConnectorConfig(grant) {
@@ -27951,6 +28211,12 @@ function inPageGrantToConnectorConfig(grant) {
27951
28211
  if (normalized.inboxCapacity !== undefined) {
27952
28212
  config.inboxCapacity = normalized.inboxCapacity;
27953
28213
  }
28214
+ if (normalized.localNodeId) {
28215
+ config.localNodeId = normalized.localNodeId;
28216
+ }
28217
+ if (normalized.remoteNodeId) {
28218
+ config.remoteNodeId = normalized.remoteNodeId;
28219
+ }
27954
28220
  return config;
27955
28221
  }
27956
28222
 
@@ -28572,6 +28838,8 @@ class InPageConnectorFactory extends ConnectorFactory {
28572
28838
  type: INPAGE_CONNECTOR_TYPE,
28573
28839
  channelName,
28574
28840
  inboxCapacity,
28841
+ localNodeId: normalized.localNodeId,
28842
+ remoteNodeId: normalized.remoteNodeId,
28575
28843
  };
28576
28844
  const connector = new InPageConnector(connectorConfig, baseConfig);
28577
28845
  if (options.authorization) {
@@ -28640,6 +28908,16 @@ class InPageConnectorFactory extends ConnectorFactory {
28640
28908
  if (candidate.authorizationContext !== undefined) {
28641
28909
  normalized.authorizationContext = candidate.authorizationContext;
28642
28910
  }
28911
+ // Handle localNodeId
28912
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
28913
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
28914
+ normalized.localNodeId = localNodeId.trim();
28915
+ }
28916
+ // Handle remoteNodeId
28917
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
28918
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
28919
+ normalized.remoteNodeId = remoteNodeId.trim();
28920
+ }
28643
28921
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL$5;
28644
28922
  normalized.inboxCapacity =
28645
28923
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$5;
@@ -28739,6 +29017,8 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28739
29017
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
28740
29018
  channelName,
28741
29019
  inboxCapacity,
29020
+ localNodeId: normalized.localNodeId,
29021
+ remoteNodeId: normalized.remoteNodeId,
28742
29022
  };
28743
29023
  const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
28744
29024
  if (options.authorization) {
@@ -28800,6 +29080,16 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28800
29080
  if (candidate.authorizationContext !== undefined) {
28801
29081
  normalized.authorizationContext = candidate.authorizationContext;
28802
29082
  }
29083
+ // Handle localNodeId
29084
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
29085
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
29086
+ normalized.localNodeId = localNodeId.trim();
29087
+ }
29088
+ // Handle remoteNodeId
29089
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
29090
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
29091
+ normalized.remoteNodeId = remoteNodeId.trim();
29092
+ }
28803
29093
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL$4;
28804
29094
  normalized.inboxCapacity =
28805
29095
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$4;
@@ -29168,6 +29458,7 @@ class InPageListener extends TransportListener {
29168
29458
  this._busHandler = null;
29169
29459
  this._senderRegistry = new Map();
29170
29460
  this._systemToSender = new Map();
29461
+ this._flowIdToSender = new Map();
29171
29462
  this._pendingAttachments = new Map();
29172
29463
  ensureBrowserEnvironment$1();
29173
29464
  const channelCandidate = options?.channelName;
@@ -29234,6 +29525,7 @@ class InPageListener extends TransportListener {
29234
29525
  this._unregisterBusListener();
29235
29526
  this._senderRegistry.clear();
29236
29527
  this._systemToSender.clear();
29528
+ this._flowIdToSender.clear();
29237
29529
  this._pendingAttachments.clear();
29238
29530
  logger$p.debug('inpage_listener_stopped', {
29239
29531
  channel: this._channelName,
@@ -29287,10 +29579,25 @@ class InPageListener extends TransportListener {
29287
29579
  await this._handleAttachFrame(senderId, envelope);
29288
29580
  return;
29289
29581
  }
29290
- const entry = this._senderRegistry.get(senderId);
29582
+ // Try to find connector by sender ID first
29583
+ let entry = this._senderRegistry.get(senderId);
29584
+ // If not found and we have a flowId, try to route based on flow
29585
+ if (!entry && envelope.flowId) {
29586
+ const originalSenderId = this._flowIdToSender.get(envelope.flowId);
29587
+ if (originalSenderId) {
29588
+ entry = this._senderRegistry.get(originalSenderId);
29589
+ logger$p.debug('inpage_listener_routed_by_flow_id', {
29590
+ sender_id: senderId,
29591
+ original_sender_id: originalSenderId,
29592
+ flow_id: envelope.flowId,
29593
+ frame_type: envelope.frame?.type ?? 'unknown',
29594
+ });
29595
+ }
29596
+ }
29291
29597
  if (!entry) {
29292
29598
  logger$p.debug('inpage_listener_no_connector_for_sender', {
29293
29599
  sender_id: senderId,
29600
+ flow_id: envelope.flowId,
29294
29601
  frame_type: envelope.frame?.type ?? 'unknown',
29295
29602
  });
29296
29603
  return;
@@ -29367,6 +29674,15 @@ class InPageListener extends TransportListener {
29367
29674
  }
29368
29675
  this._senderRegistry.set(senderId, entry);
29369
29676
  this._systemToSender.set(entry.systemId, senderId);
29677
+ // Track the flowId if present so we can route responses back
29678
+ if (envelope.flowId) {
29679
+ this._flowIdToSender.set(envelope.flowId, senderId);
29680
+ logger$p.debug('inpage_listener_registered_flow_id', {
29681
+ sender_id: senderId,
29682
+ system_id: entry.systemId,
29683
+ flow_id: envelope.flowId,
29684
+ });
29685
+ }
29370
29686
  await this._deliverEnvelope(entry, envelope);
29371
29687
  }
29372
29688
  async _createConnectorForAttach(params) {
@@ -29414,7 +29730,7 @@ class InPageListener extends TransportListener {
29414
29730
  origin_type: originType,
29415
29731
  connector_type: connector.constructor?.name ?? 'unknown',
29416
29732
  });
29417
- return { connector, systemId, originType };
29733
+ return { connector, systemId, originType, senderId: params.senderId };
29418
29734
  }
29419
29735
  catch (error) {
29420
29736
  logger$p.error('inpage_listener_connector_creation_failed', {
@@ -29468,6 +29784,12 @@ class InPageListener extends TransportListener {
29468
29784
  if (this._systemToSender.get(systemId) === senderId) {
29469
29785
  this._systemToSender.delete(systemId);
29470
29786
  }
29787
+ // Clean up flowId mappings for this sender
29788
+ for (const [flowId, sid] of this._flowIdToSender.entries()) {
29789
+ if (sid === senderId) {
29790
+ this._flowIdToSender.delete(flowId);
29791
+ }
29792
+ }
29471
29793
  })
29472
29794
  .catch((error) => {
29473
29795
  logger$p.debug('inpage_listener_wait_until_closed_failed', {
@@ -29479,9 +29801,24 @@ class InPageListener extends TransportListener {
29479
29801
  if (this._systemToSender.get(systemId) === senderId) {
29480
29802
  this._systemToSender.delete(systemId);
29481
29803
  }
29804
+ // Clean up flowId mappings for this sender
29805
+ for (const [flowId, sid] of this._flowIdToSender.entries()) {
29806
+ if (sid === senderId) {
29807
+ this._flowIdToSender.delete(flowId);
29808
+ }
29809
+ }
29482
29810
  });
29483
29811
  }
29484
29812
  async _deliverEnvelope(entry, envelope) {
29813
+ // Track flowId for routing responses back
29814
+ if (envelope.flowId && !this._flowIdToSender.has(envelope.flowId)) {
29815
+ this._flowIdToSender.set(envelope.flowId, entry.senderId);
29816
+ logger$p.debug('inpage_listener_registered_flow_id_on_delivery', {
29817
+ sender_id: entry.senderId,
29818
+ system_id: entry.systemId,
29819
+ flow_id: envelope.flowId,
29820
+ });
29821
+ }
29485
29822
  const message = this._buildChannelMessage({
29486
29823
  envelope,
29487
29824
  connector: entry.connector,
@@ -29924,6 +30261,26 @@ class BroadcastChannelListener extends TransportListener {
29924
30261
  inboxCapacity: this._inboxCapacity,
29925
30262
  };
29926
30263
  }
30264
+ // Automatically configure transport frame multiplexing:
30265
+ // Set remoteNodeId to the incoming senderId to target responses back to the specific client/agent
30266
+ const broadcastConfig = connectorConfig;
30267
+ if (!broadcastConfig.remoteNodeId) {
30268
+ broadcastConfig.remoteNodeId = params.senderId;
30269
+ logger$o.debug('broadcast_channel_listener_auto_configured_remote_node_id', {
30270
+ sender_id: params.senderId,
30271
+ system_id: systemId,
30272
+ remote_node_id: params.senderId,
30273
+ local_node_id: broadcastConfig.localNodeId ?? '<not set>',
30274
+ });
30275
+ }
30276
+ else {
30277
+ logger$o.debug('broadcast_channel_listener_using_provided_remote_node_id', {
30278
+ sender_id: params.senderId,
30279
+ system_id: systemId,
30280
+ remote_node_id: broadcastConfig.remoteNodeId,
30281
+ local_node_id: broadcastConfig.localNodeId ?? '<not set>',
30282
+ });
30283
+ }
29927
30284
  try {
29928
30285
  const connector = await routingNode.createOriginConnector({
29929
30286
  originType,
@@ -29993,6 +30350,21 @@ class BroadcastChannelListener extends TransportListener {
29993
30350
  inboxCandidate > 0) {
29994
30351
  config.inboxCapacity = Math.floor(inboxCandidate);
29995
30352
  }
30353
+ // Extract transport frame multiplexing node IDs
30354
+ const localNodeIdCandidate = candidate.localNodeId ?? candidate['local_node_id'];
30355
+ if (typeof localNodeIdCandidate === 'string' && localNodeIdCandidate.trim().length > 0) {
30356
+ config.localNodeId = localNodeIdCandidate.trim();
30357
+ logger$o.debug('broadcast_channel_listener_extracted_local_node_id', {
30358
+ local_node_id: config.localNodeId,
30359
+ });
30360
+ }
30361
+ const remoteNodeIdCandidate = candidate.remoteNodeId ?? candidate['remote_node_id'];
30362
+ if (typeof remoteNodeIdCandidate === 'string' && remoteNodeIdCandidate.trim().length > 0) {
30363
+ config.remoteNodeId = remoteNodeIdCandidate.trim();
30364
+ logger$o.debug('broadcast_channel_listener_extracted_remote_node_id', {
30365
+ remote_node_id: config.remoteNodeId,
30366
+ });
30367
+ }
29996
30368
  return config;
29997
30369
  }
29998
30370
  _monitorConnectorLifecycle(senderId, systemId, connector) {
@@ -89,6 +89,8 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
89
89
  type: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
90
90
  channelName,
91
91
  inboxCapacity,
92
+ localNodeId: normalized.localNodeId,
93
+ remoteNodeId: normalized.remoteNodeId,
92
94
  };
93
95
  const connector = new broadcast_channel_connector_js_1.BroadcastChannelConnector(connectorConfig, baseConfig);
94
96
  if (options.authorization) {
@@ -150,6 +152,16 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
150
152
  if (candidate.authorizationContext !== undefined) {
151
153
  normalized.authorizationContext = candidate.authorizationContext;
152
154
  }
155
+ // Handle localNodeId
156
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
157
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
158
+ normalized.localNodeId = localNodeId.trim();
159
+ }
160
+ // Handle remoteNodeId
161
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
162
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
163
+ normalized.remoteNodeId = remoteNodeId.trim();
164
+ }
153
165
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL;
154
166
  normalized.inboxCapacity =
155
167
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;