@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
@@ -5478,12 +5478,12 @@ for (const [name, config] of Object.entries(SQLITE_PROFILES)) {
5478
5478
  }
5479
5479
 
5480
5480
  // This file is auto-generated during build - do not edit manually
5481
- // Generated from package.json version: 0.3.5-test.942
5481
+ // Generated from package.json version: 0.3.5-test.944
5482
5482
  /**
5483
5483
  * The package version, injected at build time.
5484
5484
  * @internal
5485
5485
  */
5486
- const VERSION = '0.3.5-test.942';
5486
+ const VERSION = '0.3.5-test.944';
5487
5487
 
5488
5488
  /**
5489
5489
  * Fame errors module - Fame protocol specific error classes
@@ -11471,6 +11471,101 @@ class BoundedAsyncQueue {
11471
11471
  }
11472
11472
  }
11473
11473
 
11474
+ /**
11475
+ * Transport frame layer for multiplexing logical links on physical channels.
11476
+ *
11477
+ * This lightweight framing layer wraps raw FAME payloads to enable multiple
11478
+ * logical connections over a single physical channel (BroadcastChannel or InPage bus).
11479
+ *
11480
+ * The transport frame does NOT modify FAME envelopes - it only wraps the raw
11481
+ * Uint8Array payload at the connector level.
11482
+ */
11483
+ /**
11484
+ * Transport frame version for future compatibility
11485
+ */
11486
+ const TRANSPORT_FRAME_VERSION = 1;
11487
+ /**
11488
+ * Wrap a raw payload in a transport frame
11489
+ *
11490
+ * @param payload - Raw FAME envelope bytes
11491
+ * @param srcNodeId - Local node ID (this connector)
11492
+ * @param dstNodeId - Remote node ID (target connector)
11493
+ * @returns Transport frame ready for transmission
11494
+ */
11495
+ function wrapTransportFrame(payload, srcNodeId, dstNodeId) {
11496
+ return {
11497
+ v: TRANSPORT_FRAME_VERSION,
11498
+ src: srcNodeId,
11499
+ dst: dstNodeId,
11500
+ payload,
11501
+ };
11502
+ }
11503
+ /**
11504
+ * Serialize a transport frame for transmission over the bus
11505
+ *
11506
+ * @param frame - Transport frame to serialize
11507
+ * @returns Serialized frame data ready for postMessage/dispatchEvent
11508
+ */
11509
+ function serializeTransportFrame(frame) {
11510
+ // Convert Uint8Array to regular array for JSON serialization
11511
+ const serializable = {
11512
+ v: frame.v,
11513
+ src: frame.src,
11514
+ dst: frame.dst,
11515
+ payload: Array.from(frame.payload),
11516
+ };
11517
+ return serializable;
11518
+ }
11519
+ /**
11520
+ * Unwrap a transport frame, validating source and destination
11521
+ *
11522
+ * @param raw - Raw data from the bus
11523
+ * @param localNodeId - This connector's node ID
11524
+ * @param remoteNodeId - Expected remote node ID
11525
+ * @returns Unwrapped payload if frame is valid and addressed to us, null otherwise
11526
+ */
11527
+ function unwrapTransportFrame(raw, localNodeId, remoteNodeId) {
11528
+ // Validate basic structure
11529
+ if (!raw || typeof raw !== 'object') {
11530
+ return null;
11531
+ }
11532
+ const frame = raw;
11533
+ // Check version
11534
+ if (frame.v !== TRANSPORT_FRAME_VERSION) {
11535
+ return null;
11536
+ }
11537
+ // Check src and dst
11538
+ if (typeof frame.src !== 'string' || typeof frame.dst !== 'string') {
11539
+ return null;
11540
+ }
11541
+ // Only accept frames addressed to us from the expected remote
11542
+ if (frame.dst !== localNodeId || frame.src !== remoteNodeId) {
11543
+ return null;
11544
+ }
11545
+ // Extract payload
11546
+ if (!frame.payload || !Array.isArray(frame.payload)) {
11547
+ return null;
11548
+ }
11549
+ // Convert array back to Uint8Array
11550
+ return Uint8Array.from(frame.payload);
11551
+ }
11552
+ /**
11553
+ * Check if raw data looks like a transport frame
11554
+ *
11555
+ * @param raw - Raw data from the bus
11556
+ * @returns True if this appears to be a transport frame
11557
+ */
11558
+ function isTransportFrame(raw) {
11559
+ if (!raw || typeof raw !== 'object') {
11560
+ return false;
11561
+ }
11562
+ const frame = raw;
11563
+ return (typeof frame.v === 'number' &&
11564
+ typeof frame.src === 'string' &&
11565
+ typeof frame.dst === 'string' &&
11566
+ Array.isArray(frame.payload));
11567
+ }
11568
+
11474
11569
  const logger$10 = getLogger('naylence.fame.connector.broadcast_channel_connector');
11475
11570
  const BROADCAST_CHANNEL_CONNECTOR_TYPE$1 = 'broadcast-channel-connector';
11476
11571
  const DEFAULT_CHANNEL$7 = 'naylence-fabric';
@@ -11536,9 +11631,20 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11536
11631
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
11537
11632
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
11538
11633
  this.channel = new BroadcastChannel(this.channelName);
11634
+ // Set local and remote node IDs (defaults to connector ID for backwards compatibility)
11635
+ this.localNodeId =
11636
+ typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
11637
+ ? config.localNodeId.trim()
11638
+ : this.connectorId;
11639
+ this.remoteNodeId =
11640
+ typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
11641
+ ? config.remoteNodeId.trim()
11642
+ : '*'; // Accept from any remote if not specified
11539
11643
  logger$10.debug('broadcast_channel_connector_created', {
11540
11644
  channel: this.channelName,
11541
11645
  connector_id: this.connectorId,
11646
+ local_node_id: this.localNodeId,
11647
+ remote_node_id: this.remoteNodeId,
11542
11648
  inbox_capacity: preferredCapacity,
11543
11649
  timestamp: new Date().toISOString(),
11544
11650
  });
@@ -11571,6 +11677,46 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11571
11677
  if (busMessage.senderId === this.connectorId) {
11572
11678
  return;
11573
11679
  }
11680
+ // Try to unwrap as transport frame
11681
+ const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
11682
+ if (unwrapped) {
11683
+ // Successfully unwrapped transport frame
11684
+ logger$10.debug('broadcast_channel_transport_frame_received', {
11685
+ channel: this.channelName,
11686
+ sender_id: busMessage.senderId,
11687
+ connector_id: this.connectorId,
11688
+ local_node_id: this.localNodeId,
11689
+ remote_node_id: this.remoteNodeId,
11690
+ payload_length: unwrapped.byteLength,
11691
+ });
11692
+ if (this._shouldSkipDuplicateAck(busMessage.senderId, unwrapped)) {
11693
+ return;
11694
+ }
11695
+ try {
11696
+ if (typeof this.inbox.tryEnqueue === 'function') {
11697
+ const accepted = this.inbox.tryEnqueue(unwrapped);
11698
+ if (accepted) {
11699
+ return;
11700
+ }
11701
+ }
11702
+ this.inbox.enqueue(unwrapped);
11703
+ }
11704
+ catch (error) {
11705
+ if (error instanceof QueueFullError) {
11706
+ logger$10.warning('broadcast_channel_receive_queue_full', {
11707
+ channel: this.channelName,
11708
+ });
11709
+ }
11710
+ else {
11711
+ logger$10.error('broadcast_channel_receive_error', {
11712
+ channel: this.channelName,
11713
+ error: error instanceof Error ? error.message : String(error),
11714
+ });
11715
+ }
11716
+ }
11717
+ return;
11718
+ }
11719
+ // Fall back to legacy format (no transport frame)
11574
11720
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
11575
11721
  if (!payload) {
11576
11722
  logger$10.debug('broadcast_channel_payload_rejected', {
@@ -11709,10 +11855,26 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11709
11855
  logger$10.debug('broadcast_channel_message_sending', {
11710
11856
  channel: this.channelName,
11711
11857
  sender_id: this.connectorId,
11712
- });
11858
+ local_node_id: this.localNodeId,
11859
+ remote_node_id: this.remoteNodeId,
11860
+ });
11861
+ // Only use transport framing if both localNodeId and remoteNodeId are explicitly set
11862
+ // (not using default values). This ensures backwards compatibility.
11863
+ const useTransportFrame = this.localNodeId !== this.connectorId ||
11864
+ this.remoteNodeId !== '*';
11865
+ let payload;
11866
+ if (useTransportFrame) {
11867
+ // Wrap payload in transport frame
11868
+ const frame = wrapTransportFrame(data, this.localNodeId, this.remoteNodeId);
11869
+ payload = serializeTransportFrame(frame);
11870
+ }
11871
+ else {
11872
+ // Legacy format: send raw payload
11873
+ payload = data;
11874
+ }
11713
11875
  this.channel.postMessage({
11714
11876
  senderId: this.connectorId,
11715
- payload: data,
11877
+ payload,
11716
11878
  });
11717
11879
  }
11718
11880
  async _transportReceive() {
@@ -11960,6 +12122,14 @@ function isBroadcastChannelConnectionGrant(candidate) {
11960
12122
  record.inboxCapacity <= 0)) {
11961
12123
  return false;
11962
12124
  }
12125
+ if (record.localNodeId !== undefined &&
12126
+ (typeof record.localNodeId !== 'string' || record.localNodeId.length === 0)) {
12127
+ return false;
12128
+ }
12129
+ if (record.remoteNodeId !== undefined &&
12130
+ (typeof record.remoteNodeId !== 'string' || record.remoteNodeId.length === 0)) {
12131
+ return false;
12132
+ }
11963
12133
  return true;
11964
12134
  }
11965
12135
  function normalizeBroadcastChannelConnectionGrant(candidate) {
@@ -11993,6 +12163,20 @@ function normalizeBroadcastChannelConnectionGrant(candidate) {
11993
12163
  }
11994
12164
  result.inboxCapacity = Math.floor(inboxValue);
11995
12165
  }
12166
+ const localNodeIdValue = candidate.localNodeId ?? candidate['local_node_id'];
12167
+ if (localNodeIdValue !== undefined) {
12168
+ if (typeof localNodeIdValue !== 'string' || localNodeIdValue.trim().length === 0) {
12169
+ throw new TypeError('BroadcastChannelConnectionGrant "localNodeId" must be a non-empty string when provided');
12170
+ }
12171
+ result.localNodeId = localNodeIdValue.trim();
12172
+ }
12173
+ const remoteNodeIdValue = candidate.remoteNodeId ?? candidate['remote_node_id'];
12174
+ if (remoteNodeIdValue !== undefined) {
12175
+ if (typeof remoteNodeIdValue !== 'string' || remoteNodeIdValue.trim().length === 0) {
12176
+ throw new TypeError('BroadcastChannelConnectionGrant "remoteNodeId" must be a non-empty string when provided');
12177
+ }
12178
+ result.remoteNodeId = remoteNodeIdValue.trim();
12179
+ }
11996
12180
  return result;
11997
12181
  }
11998
12182
  function broadcastChannelGrantToConnectorConfig(grant) {
@@ -12006,6 +12190,12 @@ function broadcastChannelGrantToConnectorConfig(grant) {
12006
12190
  if (normalized.inboxCapacity !== undefined) {
12007
12191
  config.inboxCapacity = normalized.inboxCapacity;
12008
12192
  }
12193
+ if (normalized.localNodeId) {
12194
+ config.localNodeId = normalized.localNodeId;
12195
+ }
12196
+ if (normalized.remoteNodeId) {
12197
+ config.remoteNodeId = normalized.remoteNodeId;
12198
+ }
12009
12199
  return config;
12010
12200
  }
12011
12201
 
@@ -21438,9 +21628,20 @@ class InPageConnector extends BaseAsyncConnector {
21438
21628
  : DEFAULT_INBOX_CAPACITY$6;
21439
21629
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
21440
21630
  this.connectorId = InPageConnector.generateConnectorId();
21631
+ // Set local and remote node IDs (defaults to connector ID for backwards compatibility)
21632
+ this.localNodeId =
21633
+ typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
21634
+ ? config.localNodeId.trim()
21635
+ : this.connectorId;
21636
+ this.remoteNodeId =
21637
+ typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
21638
+ ? config.remoteNodeId.trim()
21639
+ : '*'; // Accept from any remote if not specified
21441
21640
  logger$J.debug('inpage_connector_initialized', {
21442
21641
  channel: this.channelName,
21443
21642
  connector_id: this.connectorId,
21643
+ local_node_id: this.localNodeId,
21644
+ remote_node_id: this.remoteNodeId,
21444
21645
  });
21445
21646
  this.onMsg = (event) => {
21446
21647
  const messageEvent = event;
@@ -21474,6 +21675,43 @@ class InPageConnector extends BaseAsyncConnector {
21474
21675
  if (busMessage.senderId === this.connectorId) {
21475
21676
  return;
21476
21677
  }
21678
+ // Try to unwrap as transport frame
21679
+ const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
21680
+ if (unwrapped) {
21681
+ // Successfully unwrapped transport frame
21682
+ logger$J.debug('inpage_transport_frame_received', {
21683
+ channel: this.channelName,
21684
+ sender_id: busMessage.senderId,
21685
+ connector_id: this.connectorId,
21686
+ local_node_id: this.localNodeId,
21687
+ remote_node_id: this.remoteNodeId,
21688
+ payload_length: unwrapped.byteLength,
21689
+ });
21690
+ try {
21691
+ if (typeof this.inbox.tryEnqueue === 'function') {
21692
+ const accepted = this.inbox.tryEnqueue(unwrapped);
21693
+ if (accepted) {
21694
+ return;
21695
+ }
21696
+ }
21697
+ this.inbox.enqueue(unwrapped);
21698
+ }
21699
+ catch (error) {
21700
+ if (error instanceof QueueFullError) {
21701
+ logger$J.warning('inpage_receive_queue_full', {
21702
+ channel: this.channelName,
21703
+ });
21704
+ }
21705
+ else {
21706
+ logger$J.error('inpage_receive_error', {
21707
+ channel: this.channelName,
21708
+ error: error instanceof Error ? error.message : String(error),
21709
+ });
21710
+ }
21711
+ }
21712
+ return;
21713
+ }
21714
+ // Fall back to legacy format (no transport frame)
21477
21715
  const payload = InPageConnector.coercePayload(busMessage.payload);
21478
21716
  if (!payload) {
21479
21717
  logger$J.debug('inpage_payload_rejected', {
@@ -21632,11 +21870,27 @@ class InPageConnector extends BaseAsyncConnector {
21632
21870
  logger$J.debug('inpage_message_sending', {
21633
21871
  channel: this.channelName,
21634
21872
  sender_id: this.connectorId,
21635
- });
21873
+ local_node_id: this.localNodeId,
21874
+ remote_node_id: this.remoteNodeId,
21875
+ });
21876
+ // Only use transport framing if both localNodeId and remoteNodeId are explicitly set
21877
+ // (not using default values). This ensures backwards compatibility.
21878
+ const useTransportFrame = this.localNodeId !== this.connectorId ||
21879
+ this.remoteNodeId !== '*';
21880
+ let payload;
21881
+ if (useTransportFrame) {
21882
+ // Wrap payload in transport frame
21883
+ const frame = wrapTransportFrame(data, this.localNodeId, this.remoteNodeId);
21884
+ payload = serializeTransportFrame(frame);
21885
+ }
21886
+ else {
21887
+ // Legacy format: send raw payload
21888
+ payload = data;
21889
+ }
21636
21890
  const event = new MessageEvent(this.channelName, {
21637
21891
  data: {
21638
21892
  senderId: this.connectorId,
21639
- payload: data,
21893
+ payload,
21640
21894
  },
21641
21895
  });
21642
21896
  getSharedBus$1().dispatchEvent(event);
@@ -28948,6 +29202,14 @@ function isInPageConnectionGrant(candidate) {
28948
29202
  record.inboxCapacity <= 0)) {
28949
29203
  return false;
28950
29204
  }
29205
+ if (record.localNodeId !== undefined &&
29206
+ (typeof record.localNodeId !== 'string' || record.localNodeId.length === 0)) {
29207
+ return false;
29208
+ }
29209
+ if (record.remoteNodeId !== undefined &&
29210
+ (typeof record.remoteNodeId !== 'string' || record.remoteNodeId.length === 0)) {
29211
+ return false;
29212
+ }
28951
29213
  return true;
28952
29214
  }
28953
29215
  function normalizeInPageConnectionGrant(candidate) {
@@ -28981,6 +29243,20 @@ function normalizeInPageConnectionGrant(candidate) {
28981
29243
  }
28982
29244
  result.inboxCapacity = Math.floor(inboxValue);
28983
29245
  }
29246
+ const localNodeIdValue = candidate.localNodeId ?? candidate['local_node_id'];
29247
+ if (localNodeIdValue !== undefined) {
29248
+ if (typeof localNodeIdValue !== 'string' || localNodeIdValue.trim().length === 0) {
29249
+ throw new TypeError('InPageConnectionGrant "localNodeId" must be a non-empty string when provided');
29250
+ }
29251
+ result.localNodeId = localNodeIdValue.trim();
29252
+ }
29253
+ const remoteNodeIdValue = candidate.remoteNodeId ?? candidate['remote_node_id'];
29254
+ if (remoteNodeIdValue !== undefined) {
29255
+ if (typeof remoteNodeIdValue !== 'string' || remoteNodeIdValue.trim().length === 0) {
29256
+ throw new TypeError('InPageConnectionGrant "remoteNodeId" must be a non-empty string when provided');
29257
+ }
29258
+ result.remoteNodeId = remoteNodeIdValue.trim();
29259
+ }
28984
29260
  return result;
28985
29261
  }
28986
29262
  function inPageGrantToConnectorConfig(grant) {
@@ -28994,6 +29270,12 @@ function inPageGrantToConnectorConfig(grant) {
28994
29270
  if (normalized.inboxCapacity !== undefined) {
28995
29271
  config.inboxCapacity = normalized.inboxCapacity;
28996
29272
  }
29273
+ if (normalized.localNodeId) {
29274
+ config.localNodeId = normalized.localNodeId;
29275
+ }
29276
+ if (normalized.remoteNodeId) {
29277
+ config.remoteNodeId = normalized.remoteNodeId;
29278
+ }
28997
29279
  return config;
28998
29280
  }
28999
29281
 
@@ -30261,6 +30543,8 @@ class InPageConnectorFactory extends ConnectorFactory {
30261
30543
  type: INPAGE_CONNECTOR_TYPE,
30262
30544
  channelName,
30263
30545
  inboxCapacity,
30546
+ localNodeId: normalized.localNodeId,
30547
+ remoteNodeId: normalized.remoteNodeId,
30264
30548
  };
30265
30549
  const connector = new InPageConnector(connectorConfig, baseConfig);
30266
30550
  if (options.authorization) {
@@ -30329,6 +30613,16 @@ class InPageConnectorFactory extends ConnectorFactory {
30329
30613
  if (candidate.authorizationContext !== undefined) {
30330
30614
  normalized.authorizationContext = candidate.authorizationContext;
30331
30615
  }
30616
+ // Handle localNodeId
30617
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
30618
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
30619
+ normalized.localNodeId = localNodeId.trim();
30620
+ }
30621
+ // Handle remoteNodeId
30622
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
30623
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
30624
+ normalized.remoteNodeId = remoteNodeId.trim();
30625
+ }
30332
30626
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL$3;
30333
30627
  normalized.inboxCapacity =
30334
30628
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$3;
@@ -30428,6 +30722,8 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
30428
30722
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE$1,
30429
30723
  channelName,
30430
30724
  inboxCapacity,
30725
+ localNodeId: normalized.localNodeId,
30726
+ remoteNodeId: normalized.remoteNodeId,
30431
30727
  };
30432
30728
  const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
30433
30729
  if (options.authorization) {
@@ -30489,6 +30785,16 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
30489
30785
  if (candidate.authorizationContext !== undefined) {
30490
30786
  normalized.authorizationContext = candidate.authorizationContext;
30491
30787
  }
30788
+ // Handle localNodeId
30789
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
30790
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
30791
+ normalized.localNodeId = localNodeId.trim();
30792
+ }
30793
+ // Handle remoteNodeId
30794
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
30795
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
30796
+ normalized.remoteNodeId = remoteNodeId.trim();
30797
+ }
30492
30798
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL$2;
30493
30799
  normalized.inboxCapacity =
30494
30800
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$2;
@@ -31973,6 +32279,7 @@ class InPageListener extends TransportListener {
31973
32279
  this._busHandler = null;
31974
32280
  this._senderRegistry = new Map();
31975
32281
  this._systemToSender = new Map();
32282
+ this._flowIdToSender = new Map();
31976
32283
  this._pendingAttachments = new Map();
31977
32284
  ensureBrowserEnvironment$1();
31978
32285
  const channelCandidate = options?.channelName;
@@ -32039,6 +32346,7 @@ class InPageListener extends TransportListener {
32039
32346
  this._unregisterBusListener();
32040
32347
  this._senderRegistry.clear();
32041
32348
  this._systemToSender.clear();
32349
+ this._flowIdToSender.clear();
32042
32350
  this._pendingAttachments.clear();
32043
32351
  logger$o.debug('inpage_listener_stopped', {
32044
32352
  channel: this._channelName,
@@ -32092,10 +32400,25 @@ class InPageListener extends TransportListener {
32092
32400
  await this._handleAttachFrame(senderId, envelope);
32093
32401
  return;
32094
32402
  }
32095
- const entry = this._senderRegistry.get(senderId);
32403
+ // Try to find connector by sender ID first
32404
+ let entry = this._senderRegistry.get(senderId);
32405
+ // If not found and we have a flowId, try to route based on flow
32406
+ if (!entry && envelope.flowId) {
32407
+ const originalSenderId = this._flowIdToSender.get(envelope.flowId);
32408
+ if (originalSenderId) {
32409
+ entry = this._senderRegistry.get(originalSenderId);
32410
+ logger$o.debug('inpage_listener_routed_by_flow_id', {
32411
+ sender_id: senderId,
32412
+ original_sender_id: originalSenderId,
32413
+ flow_id: envelope.flowId,
32414
+ frame_type: envelope.frame?.type ?? 'unknown',
32415
+ });
32416
+ }
32417
+ }
32096
32418
  if (!entry) {
32097
32419
  logger$o.debug('inpage_listener_no_connector_for_sender', {
32098
32420
  sender_id: senderId,
32421
+ flow_id: envelope.flowId,
32099
32422
  frame_type: envelope.frame?.type ?? 'unknown',
32100
32423
  });
32101
32424
  return;
@@ -32172,6 +32495,15 @@ class InPageListener extends TransportListener {
32172
32495
  }
32173
32496
  this._senderRegistry.set(senderId, entry);
32174
32497
  this._systemToSender.set(entry.systemId, senderId);
32498
+ // Track the flowId if present so we can route responses back
32499
+ if (envelope.flowId) {
32500
+ this._flowIdToSender.set(envelope.flowId, senderId);
32501
+ logger$o.debug('inpage_listener_registered_flow_id', {
32502
+ sender_id: senderId,
32503
+ system_id: entry.systemId,
32504
+ flow_id: envelope.flowId,
32505
+ });
32506
+ }
32175
32507
  await this._deliverEnvelope(entry, envelope);
32176
32508
  }
32177
32509
  async _createConnectorForAttach(params) {
@@ -32219,7 +32551,7 @@ class InPageListener extends TransportListener {
32219
32551
  origin_type: originType,
32220
32552
  connector_type: connector.constructor?.name ?? 'unknown',
32221
32553
  });
32222
- return { connector, systemId, originType };
32554
+ return { connector, systemId, originType, senderId: params.senderId };
32223
32555
  }
32224
32556
  catch (error) {
32225
32557
  logger$o.error('inpage_listener_connector_creation_failed', {
@@ -32273,6 +32605,12 @@ class InPageListener extends TransportListener {
32273
32605
  if (this._systemToSender.get(systemId) === senderId) {
32274
32606
  this._systemToSender.delete(systemId);
32275
32607
  }
32608
+ // Clean up flowId mappings for this sender
32609
+ for (const [flowId, sid] of this._flowIdToSender.entries()) {
32610
+ if (sid === senderId) {
32611
+ this._flowIdToSender.delete(flowId);
32612
+ }
32613
+ }
32276
32614
  })
32277
32615
  .catch((error) => {
32278
32616
  logger$o.debug('inpage_listener_wait_until_closed_failed', {
@@ -32284,9 +32622,24 @@ class InPageListener extends TransportListener {
32284
32622
  if (this._systemToSender.get(systemId) === senderId) {
32285
32623
  this._systemToSender.delete(systemId);
32286
32624
  }
32625
+ // Clean up flowId mappings for this sender
32626
+ for (const [flowId, sid] of this._flowIdToSender.entries()) {
32627
+ if (sid === senderId) {
32628
+ this._flowIdToSender.delete(flowId);
32629
+ }
32630
+ }
32287
32631
  });
32288
32632
  }
32289
32633
  async _deliverEnvelope(entry, envelope) {
32634
+ // Track flowId for routing responses back
32635
+ if (envelope.flowId && !this._flowIdToSender.has(envelope.flowId)) {
32636
+ this._flowIdToSender.set(envelope.flowId, entry.senderId);
32637
+ logger$o.debug('inpage_listener_registered_flow_id_on_delivery', {
32638
+ sender_id: entry.senderId,
32639
+ system_id: entry.systemId,
32640
+ flow_id: envelope.flowId,
32641
+ });
32642
+ }
32290
32643
  const message = this._buildChannelMessage({
32291
32644
  envelope,
32292
32645
  connector: entry.connector,
@@ -32667,6 +33020,26 @@ class BroadcastChannelListener extends TransportListener {
32667
33020
  inboxCapacity: this._inboxCapacity,
32668
33021
  };
32669
33022
  }
33023
+ // Automatically configure transport frame multiplexing:
33024
+ // Set remoteNodeId to the incoming senderId to target responses back to the specific client/agent
33025
+ const broadcastConfig = connectorConfig;
33026
+ if (!broadcastConfig.remoteNodeId) {
33027
+ broadcastConfig.remoteNodeId = params.senderId;
33028
+ logger$n.debug('broadcast_channel_listener_auto_configured_remote_node_id', {
33029
+ sender_id: params.senderId,
33030
+ system_id: systemId,
33031
+ remote_node_id: params.senderId,
33032
+ local_node_id: broadcastConfig.localNodeId ?? '<not set>',
33033
+ });
33034
+ }
33035
+ else {
33036
+ logger$n.debug('broadcast_channel_listener_using_provided_remote_node_id', {
33037
+ sender_id: params.senderId,
33038
+ system_id: systemId,
33039
+ remote_node_id: broadcastConfig.remoteNodeId,
33040
+ local_node_id: broadcastConfig.localNodeId ?? '<not set>',
33041
+ });
33042
+ }
32670
33043
  try {
32671
33044
  const connector = await routingNode.createOriginConnector({
32672
33045
  originType,
@@ -32736,6 +33109,21 @@ class BroadcastChannelListener extends TransportListener {
32736
33109
  inboxCandidate > 0) {
32737
33110
  config.inboxCapacity = Math.floor(inboxCandidate);
32738
33111
  }
33112
+ // Extract transport frame multiplexing node IDs
33113
+ const localNodeIdCandidate = candidate.localNodeId ?? candidate['local_node_id'];
33114
+ if (typeof localNodeIdCandidate === 'string' && localNodeIdCandidate.trim().length > 0) {
33115
+ config.localNodeId = localNodeIdCandidate.trim();
33116
+ logger$n.debug('broadcast_channel_listener_extracted_local_node_id', {
33117
+ local_node_id: config.localNodeId,
33118
+ });
33119
+ }
33120
+ const remoteNodeIdCandidate = candidate.remoteNodeId ?? candidate['remote_node_id'];
33121
+ if (typeof remoteNodeIdCandidate === 'string' && remoteNodeIdCandidate.trim().length > 0) {
33122
+ config.remoteNodeId = remoteNodeIdCandidate.trim();
33123
+ logger$n.debug('broadcast_channel_listener_extracted_remote_node_id', {
33124
+ remote_node_id: config.remoteNodeId,
33125
+ });
33126
+ }
32739
33127
  return config;
32740
33128
  }
32741
33129
  _monitorConnectorLifecycle(senderId, systemId, connector) {