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