@naylence/runtime 0.3.5-test.941 → 0.3.5-test.943

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 (33) hide show
  1. package/dist/browser/index.cjs +390 -8
  2. package/dist/browser/index.mjs +390 -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/inpage-connector-factory.js +12 -0
  6. package/dist/cjs/naylence/fame/connector/inpage-connector.js +159 -1
  7. package/dist/cjs/naylence/fame/connector/transport-frame.js +101 -0
  8. package/dist/cjs/naylence/fame/grants/broadcast-channel-connection-grant.js +28 -0
  9. package/dist/cjs/naylence/fame/grants/inpage-connection-grant.js +28 -0
  10. package/dist/cjs/naylence/fame/node/upstream-session-manager.js +2 -2
  11. package/dist/cjs/version.js +2 -2
  12. package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +12 -0
  13. package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +69 -1
  14. package/dist/esm/naylence/fame/connector/inpage-connector-factory.js +12 -0
  15. package/dist/esm/naylence/fame/connector/inpage-connector.js +159 -1
  16. package/dist/esm/naylence/fame/connector/transport-frame.js +94 -0
  17. package/dist/esm/naylence/fame/grants/broadcast-channel-connection-grant.js +28 -0
  18. package/dist/esm/naylence/fame/grants/inpage-connection-grant.js +28 -0
  19. package/dist/esm/naylence/fame/node/upstream-session-manager.js +2 -2
  20. package/dist/esm/version.js +2 -2
  21. package/dist/node/index.cjs +390 -8
  22. package/dist/node/index.mjs +390 -8
  23. package/dist/node/node.cjs +406 -8
  24. package/dist/node/node.mjs +406 -8
  25. package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +2 -0
  26. package/dist/types/naylence/fame/connector/broadcast-channel-connector.browser.d.ts +4 -0
  27. package/dist/types/naylence/fame/connector/inpage-connector-factory.d.ts +2 -0
  28. package/dist/types/naylence/fame/connector/inpage-connector.d.ts +11 -1
  29. package/dist/types/naylence/fame/connector/transport-frame.d.ts +58 -0
  30. package/dist/types/naylence/fame/grants/broadcast-channel-connection-grant.d.ts +6 -0
  31. package/dist/types/naylence/fame/grants/inpage-connection-grant.d.ts +8 -0
  32. package/dist/types/version.d.ts +1 -1
  33. package/package.json +1 -1
@@ -14,12 +14,12 @@ var fastify = require('fastify');
14
14
  var websocketPlugin = require('@fastify/websocket');
15
15
 
16
16
  // This file is auto-generated during build - do not edit manually
17
- // Generated from package.json version: 0.3.5-test.941
17
+ // Generated from package.json version: 0.3.5-test.943
18
18
  /**
19
19
  * The package version, injected at build time.
20
20
  * @internal
21
21
  */
22
- const VERSION = '0.3.5-test.941';
22
+ const VERSION = '0.3.5-test.943';
23
23
 
24
24
  /**
25
25
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9734,6 +9734,85 @@ class BoundedAsyncQueue {
9734
9734
  }
9735
9735
  }
9736
9736
 
9737
+ /**
9738
+ * Transport frame layer for multiplexing logical links on physical channels.
9739
+ *
9740
+ * This lightweight framing layer wraps raw FAME payloads to enable multiple
9741
+ * logical connections over a single physical channel (BroadcastChannel or InPage bus).
9742
+ *
9743
+ * The transport frame does NOT modify FAME envelopes - it only wraps the raw
9744
+ * Uint8Array payload at the connector level.
9745
+ */
9746
+ /**
9747
+ * Transport frame version for future compatibility
9748
+ */
9749
+ const TRANSPORT_FRAME_VERSION = 1;
9750
+ /**
9751
+ * Wrap a raw payload in a transport frame
9752
+ *
9753
+ * @param payload - Raw FAME envelope bytes
9754
+ * @param srcNodeId - Local node ID (this connector)
9755
+ * @param dstNodeId - Remote node ID (target connector)
9756
+ * @returns Transport frame ready for transmission
9757
+ */
9758
+ function wrapTransportFrame(payload, srcNodeId, dstNodeId) {
9759
+ return {
9760
+ v: TRANSPORT_FRAME_VERSION,
9761
+ src: srcNodeId,
9762
+ dst: dstNodeId,
9763
+ payload,
9764
+ };
9765
+ }
9766
+ /**
9767
+ * Serialize a transport frame for transmission over the bus
9768
+ *
9769
+ * @param frame - Transport frame to serialize
9770
+ * @returns Serialized frame data ready for postMessage/dispatchEvent
9771
+ */
9772
+ function serializeTransportFrame(frame) {
9773
+ // Convert Uint8Array to regular array for JSON serialization
9774
+ const serializable = {
9775
+ v: frame.v,
9776
+ src: frame.src,
9777
+ dst: frame.dst,
9778
+ payload: Array.from(frame.payload),
9779
+ };
9780
+ return serializable;
9781
+ }
9782
+ /**
9783
+ * Unwrap a transport frame, validating source and destination
9784
+ *
9785
+ * @param raw - Raw data from the bus
9786
+ * @param localNodeId - This connector's node ID
9787
+ * @param remoteNodeId - Expected remote node ID
9788
+ * @returns Unwrapped payload if frame is valid and addressed to us, null otherwise
9789
+ */
9790
+ function unwrapTransportFrame(raw, localNodeId, remoteNodeId) {
9791
+ // Validate basic structure
9792
+ if (!raw || typeof raw !== 'object') {
9793
+ return null;
9794
+ }
9795
+ const frame = raw;
9796
+ // Check version
9797
+ if (frame.v !== TRANSPORT_FRAME_VERSION) {
9798
+ return null;
9799
+ }
9800
+ // Check src and dst
9801
+ if (typeof frame.src !== 'string' || typeof frame.dst !== 'string') {
9802
+ return null;
9803
+ }
9804
+ // Only accept frames addressed to us from the expected remote
9805
+ if (frame.dst !== localNodeId || frame.src !== remoteNodeId) {
9806
+ return null;
9807
+ }
9808
+ // Extract payload
9809
+ if (!frame.payload || !Array.isArray(frame.payload)) {
9810
+ return null;
9811
+ }
9812
+ // Convert array back to Uint8Array
9813
+ return Uint8Array.from(frame.payload);
9814
+ }
9815
+
9737
9816
  const logger$_ = getLogger('naylence.fame.connector.broadcast_channel_connector');
9738
9817
  const BROADCAST_CHANNEL_CONNECTOR_TYPE = 'broadcast-channel-connector';
9739
9818
  const DEFAULT_CHANNEL$7 = 'naylence-fabric';
@@ -9799,9 +9878,20 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9799
9878
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9800
9879
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9801
9880
  this.channel = new BroadcastChannel(this.channelName);
9881
+ // Set local and remote node IDs (defaults to connector ID for backwards compatibility)
9882
+ this.localNodeId =
9883
+ typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
9884
+ ? config.localNodeId.trim()
9885
+ : this.connectorId;
9886
+ this.remoteNodeId =
9887
+ typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
9888
+ ? config.remoteNodeId.trim()
9889
+ : '*'; // Accept from any remote if not specified
9802
9890
  logger$_.debug('broadcast_channel_connector_created', {
9803
9891
  channel: this.channelName,
9804
9892
  connector_id: this.connectorId,
9893
+ local_node_id: this.localNodeId,
9894
+ remote_node_id: this.remoteNodeId,
9805
9895
  inbox_capacity: preferredCapacity,
9806
9896
  timestamp: new Date().toISOString(),
9807
9897
  });
@@ -9834,6 +9924,46 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9834
9924
  if (busMessage.senderId === this.connectorId) {
9835
9925
  return;
9836
9926
  }
9927
+ // Try to unwrap as transport frame
9928
+ const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
9929
+ if (unwrapped) {
9930
+ // Successfully unwrapped transport frame
9931
+ logger$_.debug('broadcast_channel_transport_frame_received', {
9932
+ channel: this.channelName,
9933
+ sender_id: busMessage.senderId,
9934
+ connector_id: this.connectorId,
9935
+ local_node_id: this.localNodeId,
9936
+ remote_node_id: this.remoteNodeId,
9937
+ payload_length: unwrapped.byteLength,
9938
+ });
9939
+ if (this._shouldSkipDuplicateAck(busMessage.senderId, unwrapped)) {
9940
+ return;
9941
+ }
9942
+ try {
9943
+ if (typeof this.inbox.tryEnqueue === 'function') {
9944
+ const accepted = this.inbox.tryEnqueue(unwrapped);
9945
+ if (accepted) {
9946
+ return;
9947
+ }
9948
+ }
9949
+ this.inbox.enqueue(unwrapped);
9950
+ }
9951
+ catch (error) {
9952
+ if (error instanceof QueueFullError) {
9953
+ logger$_.warning('broadcast_channel_receive_queue_full', {
9954
+ channel: this.channelName,
9955
+ });
9956
+ }
9957
+ else {
9958
+ logger$_.error('broadcast_channel_receive_error', {
9959
+ channel: this.channelName,
9960
+ error: error instanceof Error ? error.message : String(error),
9961
+ });
9962
+ }
9963
+ }
9964
+ return;
9965
+ }
9966
+ // Fall back to legacy format (no transport frame)
9837
9967
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
9838
9968
  if (!payload) {
9839
9969
  logger$_.debug('broadcast_channel_payload_rejected', {
@@ -9972,10 +10102,26 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9972
10102
  logger$_.debug('broadcast_channel_message_sending', {
9973
10103
  channel: this.channelName,
9974
10104
  sender_id: this.connectorId,
9975
- });
10105
+ local_node_id: this.localNodeId,
10106
+ remote_node_id: this.remoteNodeId,
10107
+ });
10108
+ // Only use transport framing if both localNodeId and remoteNodeId are explicitly set
10109
+ // (not using default values). This ensures backwards compatibility.
10110
+ const useTransportFrame = this.localNodeId !== this.connectorId ||
10111
+ this.remoteNodeId !== '*';
10112
+ let payload;
10113
+ if (useTransportFrame) {
10114
+ // Wrap payload in transport frame
10115
+ const frame = wrapTransportFrame(data, this.localNodeId, this.remoteNodeId);
10116
+ payload = serializeTransportFrame(frame);
10117
+ }
10118
+ else {
10119
+ // Legacy format: send raw payload
10120
+ payload = data;
10121
+ }
9976
10122
  this.channel.postMessage({
9977
10123
  senderId: this.connectorId,
9978
- payload: data,
10124
+ payload,
9979
10125
  });
9980
10126
  }
9981
10127
  async _transportReceive() {
@@ -10268,6 +10414,14 @@ function isBroadcastChannelConnectionGrant(candidate) {
10268
10414
  record.inboxCapacity <= 0)) {
10269
10415
  return false;
10270
10416
  }
10417
+ if (record.localNodeId !== undefined &&
10418
+ (typeof record.localNodeId !== 'string' || record.localNodeId.length === 0)) {
10419
+ return false;
10420
+ }
10421
+ if (record.remoteNodeId !== undefined &&
10422
+ (typeof record.remoteNodeId !== 'string' || record.remoteNodeId.length === 0)) {
10423
+ return false;
10424
+ }
10271
10425
  return true;
10272
10426
  }
10273
10427
  function normalizeBroadcastChannelConnectionGrant(candidate) {
@@ -10301,6 +10455,20 @@ function normalizeBroadcastChannelConnectionGrant(candidate) {
10301
10455
  }
10302
10456
  result.inboxCapacity = Math.floor(inboxValue);
10303
10457
  }
10458
+ const localNodeIdValue = candidate.localNodeId ?? candidate['local_node_id'];
10459
+ if (localNodeIdValue !== undefined) {
10460
+ if (typeof localNodeIdValue !== 'string' || localNodeIdValue.trim().length === 0) {
10461
+ throw new TypeError('BroadcastChannelConnectionGrant "localNodeId" must be a non-empty string when provided');
10462
+ }
10463
+ result.localNodeId = localNodeIdValue.trim();
10464
+ }
10465
+ const remoteNodeIdValue = candidate.remoteNodeId ?? candidate['remote_node_id'];
10466
+ if (remoteNodeIdValue !== undefined) {
10467
+ if (typeof remoteNodeIdValue !== 'string' || remoteNodeIdValue.trim().length === 0) {
10468
+ throw new TypeError('BroadcastChannelConnectionGrant "remoteNodeId" must be a non-empty string when provided');
10469
+ }
10470
+ result.remoteNodeId = remoteNodeIdValue.trim();
10471
+ }
10304
10472
  return result;
10305
10473
  }
10306
10474
  function broadcastChannelGrantToConnectorConfig(grant) {
@@ -10314,6 +10482,12 @@ function broadcastChannelGrantToConnectorConfig(grant) {
10314
10482
  if (normalized.inboxCapacity !== undefined) {
10315
10483
  config.inboxCapacity = normalized.inboxCapacity;
10316
10484
  }
10485
+ if (normalized.localNodeId) {
10486
+ config.localNodeId = normalized.localNodeId;
10487
+ }
10488
+ if (normalized.remoteNodeId) {
10489
+ config.remoteNodeId = normalized.remoteNodeId;
10490
+ }
10317
10491
  return config;
10318
10492
  }
10319
10493
 
@@ -10693,7 +10867,7 @@ class UpstreamSessionManager extends TaskSpawner {
10693
10867
  this.currentStopSubtasks = null;
10694
10868
  await Promise.allSettled(tasks.map((task) => task.promise));
10695
10869
  if (this.connector) {
10696
- logger$Z.info('upstream_stopping_old_connector', {
10870
+ logger$Z.debug('upstream_stopping_old_connector', {
10697
10871
  connect_epoch: this.connectEpoch,
10698
10872
  target_system_id: this.targetSystemId,
10699
10873
  timestamp: new Date().toISOString(),
@@ -10704,7 +10878,7 @@ class UpstreamSessionManager extends TaskSpawner {
10704
10878
  error: err instanceof Error ? err.message : String(err),
10705
10879
  });
10706
10880
  });
10707
- logger$Z.info('upstream_old_connector_stopped', {
10881
+ logger$Z.debug('upstream_old_connector_stopped', {
10708
10882
  connect_epoch: this.connectEpoch,
10709
10883
  target_system_id: this.targetSystemId,
10710
10884
  timestamp: new Date().toISOString(),
@@ -20248,6 +20422,7 @@ class InPageConnector extends BaseAsyncConnector {
20248
20422
  ensureBrowserEnvironment$2();
20249
20423
  super(baseConfig);
20250
20424
  this.listenerRegistered = false;
20425
+ this.visibilityChangeListenerRegistered = false;
20251
20426
  this.channelName =
20252
20427
  typeof config.channelName === 'string' && config.channelName.trim().length > 0
20253
20428
  ? config.channelName.trim()
@@ -20259,9 +20434,20 @@ class InPageConnector extends BaseAsyncConnector {
20259
20434
  : DEFAULT_INBOX_CAPACITY$6;
20260
20435
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
20261
20436
  this.connectorId = InPageConnector.generateConnectorId();
20437
+ // Set local and remote node IDs (defaults to connector ID for backwards compatibility)
20438
+ this.localNodeId =
20439
+ typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
20440
+ ? config.localNodeId.trim()
20441
+ : this.connectorId;
20442
+ this.remoteNodeId =
20443
+ typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
20444
+ ? config.remoteNodeId.trim()
20445
+ : '*'; // Accept from any remote if not specified
20262
20446
  logger$G.debug('inpage_connector_initialized', {
20263
20447
  channel: this.channelName,
20264
20448
  connector_id: this.connectorId,
20449
+ local_node_id: this.localNodeId,
20450
+ remote_node_id: this.remoteNodeId,
20265
20451
  });
20266
20452
  this.onMsg = (event) => {
20267
20453
  const messageEvent = event;
@@ -20295,6 +20481,43 @@ class InPageConnector extends BaseAsyncConnector {
20295
20481
  if (busMessage.senderId === this.connectorId) {
20296
20482
  return;
20297
20483
  }
20484
+ // Try to unwrap as transport frame
20485
+ const unwrapped = unwrapTransportFrame(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
20486
+ if (unwrapped) {
20487
+ // Successfully unwrapped transport frame
20488
+ logger$G.debug('inpage_transport_frame_received', {
20489
+ channel: this.channelName,
20490
+ sender_id: busMessage.senderId,
20491
+ connector_id: this.connectorId,
20492
+ local_node_id: this.localNodeId,
20493
+ remote_node_id: this.remoteNodeId,
20494
+ payload_length: unwrapped.byteLength,
20495
+ });
20496
+ try {
20497
+ if (typeof this.inbox.tryEnqueue === 'function') {
20498
+ const accepted = this.inbox.tryEnqueue(unwrapped);
20499
+ if (accepted) {
20500
+ return;
20501
+ }
20502
+ }
20503
+ this.inbox.enqueue(unwrapped);
20504
+ }
20505
+ catch (error) {
20506
+ if (error instanceof QueueFullError) {
20507
+ logger$G.warning('inpage_receive_queue_full', {
20508
+ channel: this.channelName,
20509
+ });
20510
+ }
20511
+ else {
20512
+ logger$G.error('inpage_receive_error', {
20513
+ channel: this.channelName,
20514
+ error: error instanceof Error ? error.message : String(error),
20515
+ });
20516
+ }
20517
+ }
20518
+ return;
20519
+ }
20520
+ // Fall back to legacy format (no transport frame)
20298
20521
  const payload = InPageConnector.coercePayload(busMessage.payload);
20299
20522
  if (!payload) {
20300
20523
  logger$G.debug('inpage_payload_rejected', {
@@ -20335,6 +20558,92 @@ class InPageConnector extends BaseAsyncConnector {
20335
20558
  };
20336
20559
  getSharedBus$1().addEventListener(this.channelName, this.onMsg);
20337
20560
  this.listenerRegistered = true;
20561
+ // Setup visibility change monitoring
20562
+ this.visibilityChangeHandler = () => {
20563
+ const isHidden = document.hidden;
20564
+ logger$G.debug('inpage_visibility_changed', {
20565
+ channel: this.channelName,
20566
+ connector_id: this.connectorId,
20567
+ visibility: isHidden ? 'hidden' : 'visible',
20568
+ timestamp: new Date().toISOString(),
20569
+ });
20570
+ // Pause/resume connector based on visibility
20571
+ if (isHidden && this.state === core.ConnectorState.STARTED) {
20572
+ this.pause().catch((err) => {
20573
+ logger$G.warning('inpage_pause_failed', {
20574
+ channel: this.channelName,
20575
+ connector_id: this.connectorId,
20576
+ error: err instanceof Error ? err.message : String(err),
20577
+ });
20578
+ });
20579
+ }
20580
+ else if (!isHidden && this.state === core.ConnectorState.PAUSED) {
20581
+ this.resume().catch((err) => {
20582
+ logger$G.warning('inpage_resume_failed', {
20583
+ channel: this.channelName,
20584
+ connector_id: this.connectorId,
20585
+ error: err instanceof Error ? err.message : String(err),
20586
+ });
20587
+ });
20588
+ }
20589
+ };
20590
+ if (typeof document !== 'undefined') {
20591
+ document.addEventListener('visibilitychange', this.visibilityChangeHandler);
20592
+ this.visibilityChangeListenerRegistered = true;
20593
+ // Track page lifecycle events to detect browser unload/discard
20594
+ if (typeof window !== 'undefined') {
20595
+ const lifecycleLogger = (event) => {
20596
+ logger$G.info('inpage_page_lifecycle', {
20597
+ channel: this.channelName,
20598
+ connector_id: this.connectorId,
20599
+ event_type: event.type,
20600
+ visibility_state: document.visibilityState,
20601
+ timestamp: new Date().toISOString(),
20602
+ });
20603
+ };
20604
+ window.addEventListener('beforeunload', lifecycleLogger);
20605
+ window.addEventListener('unload', lifecycleLogger);
20606
+ window.addEventListener('pagehide', lifecycleLogger);
20607
+ window.addEventListener('pageshow', lifecycleLogger);
20608
+ document.addEventListener('freeze', lifecycleLogger);
20609
+ document.addEventListener('resume', lifecycleLogger);
20610
+ }
20611
+ // Log initial state with detailed visibility info
20612
+ logger$G.debug('inpage_initial_visibility', {
20613
+ channel: this.channelName,
20614
+ connector_id: this.connectorId,
20615
+ visibility: document.hidden ? 'hidden' : 'visible',
20616
+ document_hidden: document.hidden,
20617
+ visibility_state: document.visibilityState,
20618
+ has_focus: document.hasFocus(),
20619
+ timestamp: new Date().toISOString(),
20620
+ });
20621
+ }
20622
+ }
20623
+ /**
20624
+ * Override start() to check initial visibility state
20625
+ */
20626
+ async start(inboundHandler) {
20627
+ await super.start(inboundHandler);
20628
+ // After transitioning to STARTED, check if tab is already hidden
20629
+ if (typeof document !== 'undefined' && document.hidden) {
20630
+ logger$G.debug('inpage_start_in_hidden_tab', {
20631
+ channel: this.channelName,
20632
+ connector_id: this.connectorId,
20633
+ document_hidden: document.hidden,
20634
+ visibility_state: document.visibilityState,
20635
+ has_focus: document.hasFocus(),
20636
+ timestamp: new Date().toISOString(),
20637
+ });
20638
+ // Immediately pause if tab is hidden at start time
20639
+ await this.pause().catch((err) => {
20640
+ logger$G.warning('inpage_initial_pause_failed', {
20641
+ channel: this.channelName,
20642
+ connector_id: this.connectorId,
20643
+ error: err instanceof Error ? err.message : String(err),
20644
+ });
20645
+ });
20646
+ }
20338
20647
  }
20339
20648
  // Allow listeners to feed envelopes directly into the in-page receive queue.
20340
20649
  async pushToReceive(rawOrEnvelope) {
@@ -20367,11 +20676,27 @@ class InPageConnector extends BaseAsyncConnector {
20367
20676
  logger$G.debug('inpage_message_sending', {
20368
20677
  channel: this.channelName,
20369
20678
  sender_id: this.connectorId,
20370
- });
20679
+ local_node_id: this.localNodeId,
20680
+ remote_node_id: this.remoteNodeId,
20681
+ });
20682
+ // Only use transport framing if both localNodeId and remoteNodeId are explicitly set
20683
+ // (not using default values). This ensures backwards compatibility.
20684
+ const useTransportFrame = this.localNodeId !== this.connectorId ||
20685
+ this.remoteNodeId !== '*';
20686
+ let payload;
20687
+ if (useTransportFrame) {
20688
+ // Wrap payload in transport frame
20689
+ const frame = wrapTransportFrame(data, this.localNodeId, this.remoteNodeId);
20690
+ payload = serializeTransportFrame(frame);
20691
+ }
20692
+ else {
20693
+ // Legacy format: send raw payload
20694
+ payload = data;
20695
+ }
20371
20696
  const event = new MessageEvent(this.channelName, {
20372
20697
  data: {
20373
20698
  senderId: this.connectorId,
20374
- payload: data,
20699
+ payload,
20375
20700
  },
20376
20701
  });
20377
20702
  getSharedBus$1().dispatchEvent(event);
@@ -20384,6 +20709,11 @@ class InPageConnector extends BaseAsyncConnector {
20384
20709
  getSharedBus$1().removeEventListener(this.channelName, this.onMsg);
20385
20710
  this.listenerRegistered = false;
20386
20711
  }
20712
+ if (this.visibilityChangeListenerRegistered && this.visibilityChangeHandler && typeof document !== 'undefined') {
20713
+ document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
20714
+ this.visibilityChangeListenerRegistered = false;
20715
+ this.visibilityChangeHandler = undefined;
20716
+ }
20387
20717
  const closeCode = typeof code === 'number' ? code : 1000;
20388
20718
  const closeReason = typeof reason === 'string' && reason.length > 0 ? reason : 'closed';
20389
20719
  const shutdownError = new FameTransportClose(closeReason, closeCode);
@@ -27731,6 +28061,14 @@ function isInPageConnectionGrant(candidate) {
27731
28061
  record.inboxCapacity <= 0)) {
27732
28062
  return false;
27733
28063
  }
28064
+ if (record.localNodeId !== undefined &&
28065
+ (typeof record.localNodeId !== 'string' || record.localNodeId.length === 0)) {
28066
+ return false;
28067
+ }
28068
+ if (record.remoteNodeId !== undefined &&
28069
+ (typeof record.remoteNodeId !== 'string' || record.remoteNodeId.length === 0)) {
28070
+ return false;
28071
+ }
27734
28072
  return true;
27735
28073
  }
27736
28074
  function normalizeInPageConnectionGrant(candidate) {
@@ -27764,6 +28102,20 @@ function normalizeInPageConnectionGrant(candidate) {
27764
28102
  }
27765
28103
  result.inboxCapacity = Math.floor(inboxValue);
27766
28104
  }
28105
+ const localNodeIdValue = candidate.localNodeId ?? candidate['local_node_id'];
28106
+ if (localNodeIdValue !== undefined) {
28107
+ if (typeof localNodeIdValue !== 'string' || localNodeIdValue.trim().length === 0) {
28108
+ throw new TypeError('InPageConnectionGrant "localNodeId" must be a non-empty string when provided');
28109
+ }
28110
+ result.localNodeId = localNodeIdValue.trim();
28111
+ }
28112
+ const remoteNodeIdValue = candidate.remoteNodeId ?? candidate['remote_node_id'];
28113
+ if (remoteNodeIdValue !== undefined) {
28114
+ if (typeof remoteNodeIdValue !== 'string' || remoteNodeIdValue.trim().length === 0) {
28115
+ throw new TypeError('InPageConnectionGrant "remoteNodeId" must be a non-empty string when provided');
28116
+ }
28117
+ result.remoteNodeId = remoteNodeIdValue.trim();
28118
+ }
27767
28119
  return result;
27768
28120
  }
27769
28121
  function inPageGrantToConnectorConfig(grant) {
@@ -27777,6 +28129,12 @@ function inPageGrantToConnectorConfig(grant) {
27777
28129
  if (normalized.inboxCapacity !== undefined) {
27778
28130
  config.inboxCapacity = normalized.inboxCapacity;
27779
28131
  }
28132
+ if (normalized.localNodeId) {
28133
+ config.localNodeId = normalized.localNodeId;
28134
+ }
28135
+ if (normalized.remoteNodeId) {
28136
+ config.remoteNodeId = normalized.remoteNodeId;
28137
+ }
27780
28138
  return config;
27781
28139
  }
27782
28140
 
@@ -28403,6 +28761,8 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28403
28761
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
28404
28762
  channelName,
28405
28763
  inboxCapacity,
28764
+ localNodeId: normalized.localNodeId,
28765
+ remoteNodeId: normalized.remoteNodeId,
28406
28766
  };
28407
28767
  const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
28408
28768
  if (options.authorization) {
@@ -28464,6 +28824,16 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28464
28824
  if (candidate.authorizationContext !== undefined) {
28465
28825
  normalized.authorizationContext = candidate.authorizationContext;
28466
28826
  }
28827
+ // Handle localNodeId
28828
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
28829
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
28830
+ normalized.localNodeId = localNodeId.trim();
28831
+ }
28832
+ // Handle remoteNodeId
28833
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
28834
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
28835
+ normalized.remoteNodeId = remoteNodeId.trim();
28836
+ }
28467
28837
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL$5;
28468
28838
  normalized.inboxCapacity =
28469
28839
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$5;
@@ -29027,6 +29397,8 @@ class InPageConnectorFactory extends ConnectorFactory {
29027
29397
  type: INPAGE_CONNECTOR_TYPE,
29028
29398
  channelName,
29029
29399
  inboxCapacity,
29400
+ localNodeId: normalized.localNodeId,
29401
+ remoteNodeId: normalized.remoteNodeId,
29030
29402
  };
29031
29403
  const connector = new InPageConnector(connectorConfig, baseConfig);
29032
29404
  if (options.authorization) {
@@ -29095,6 +29467,16 @@ class InPageConnectorFactory extends ConnectorFactory {
29095
29467
  if (candidate.authorizationContext !== undefined) {
29096
29468
  normalized.authorizationContext = candidate.authorizationContext;
29097
29469
  }
29470
+ // Handle localNodeId
29471
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
29472
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
29473
+ normalized.localNodeId = localNodeId.trim();
29474
+ }
29475
+ // Handle remoteNodeId
29476
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
29477
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
29478
+ normalized.remoteNodeId = remoteNodeId.trim();
29479
+ }
29098
29480
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL$3;
29099
29481
  normalized.inboxCapacity =
29100
29482
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$3;