@naylence/runtime 0.3.5-test.942 → 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 (31) hide show
  1. package/dist/browser/index.cjs +296 -6
  2. package/dist/browser/index.mjs +296 -6
  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 +66 -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/version.js +2 -2
  11. package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +12 -0
  12. package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +69 -1
  13. package/dist/esm/naylence/fame/connector/inpage-connector-factory.js +12 -0
  14. package/dist/esm/naylence/fame/connector/inpage-connector.js +66 -1
  15. package/dist/esm/naylence/fame/connector/transport-frame.js +94 -0
  16. package/dist/esm/naylence/fame/grants/broadcast-channel-connection-grant.js +28 -0
  17. package/dist/esm/naylence/fame/grants/inpage-connection-grant.js +28 -0
  18. package/dist/esm/version.js +2 -2
  19. package/dist/node/index.cjs +296 -6
  20. package/dist/node/index.mjs +296 -6
  21. package/dist/node/node.cjs +312 -6
  22. package/dist/node/node.mjs +312 -6
  23. package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +2 -0
  24. package/dist/types/naylence/fame/connector/broadcast-channel-connector.browser.d.ts +4 -0
  25. package/dist/types/naylence/fame/connector/inpage-connector-factory.d.ts +2 -0
  26. package/dist/types/naylence/fame/connector/inpage-connector.d.ts +4 -0
  27. package/dist/types/naylence/fame/connector/transport-frame.d.ts +58 -0
  28. package/dist/types/naylence/fame/grants/broadcast-channel-connection-grant.d.ts +6 -0
  29. package/dist/types/naylence/fame/grants/inpage-connection-grant.d.ts +8 -0
  30. package/dist/types/version.d.ts +1 -1
  31. 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.943
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.943';
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;
@@ -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;
@@ -6,6 +6,7 @@ const errors_js_1 = require("../errors/errors.js");
6
6
  const logging_js_1 = require("../util/logging.js");
7
7
  const bounded_async_queue_js_1 = require("../util/bounded-async-queue.js");
8
8
  const core_1 = require("@naylence/core");
9
+ const transport_frame_js_1 = require("./transport-frame.js");
9
10
  const logger = (0, logging_js_1.getLogger)('naylence.fame.connector.broadcast_channel_connector');
10
11
  exports.BROADCAST_CHANNEL_CONNECTOR_TYPE = 'broadcast-channel-connector';
11
12
  const DEFAULT_CHANNEL = 'naylence-fabric';
@@ -71,9 +72,20 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
71
72
  this.inbox = new bounded_async_queue_js_1.BoundedAsyncQueue(preferredCapacity);
72
73
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
73
74
  this.channel = new BroadcastChannel(this.channelName);
75
+ // Set local and remote node IDs (defaults to connector ID for backwards compatibility)
76
+ this.localNodeId =
77
+ typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
78
+ ? config.localNodeId.trim()
79
+ : this.connectorId;
80
+ this.remoteNodeId =
81
+ typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
82
+ ? config.remoteNodeId.trim()
83
+ : '*'; // Accept from any remote if not specified
74
84
  logger.debug('broadcast_channel_connector_created', {
75
85
  channel: this.channelName,
76
86
  connector_id: this.connectorId,
87
+ local_node_id: this.localNodeId,
88
+ remote_node_id: this.remoteNodeId,
77
89
  inbox_capacity: preferredCapacity,
78
90
  timestamp: new Date().toISOString(),
79
91
  });
@@ -106,6 +118,46 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
106
118
  if (busMessage.senderId === this.connectorId) {
107
119
  return;
108
120
  }
121
+ // Try to unwrap as transport frame
122
+ const unwrapped = (0, transport_frame_js_1.unwrapTransportFrame)(busMessage.payload, this.localNodeId, this.remoteNodeId === '*' ? busMessage.senderId : this.remoteNodeId);
123
+ if (unwrapped) {
124
+ // Successfully unwrapped transport frame
125
+ logger.debug('broadcast_channel_transport_frame_received', {
126
+ channel: this.channelName,
127
+ sender_id: busMessage.senderId,
128
+ connector_id: this.connectorId,
129
+ local_node_id: this.localNodeId,
130
+ remote_node_id: this.remoteNodeId,
131
+ payload_length: unwrapped.byteLength,
132
+ });
133
+ if (this._shouldSkipDuplicateAck(busMessage.senderId, unwrapped)) {
134
+ return;
135
+ }
136
+ try {
137
+ if (typeof this.inbox.tryEnqueue === 'function') {
138
+ const accepted = this.inbox.tryEnqueue(unwrapped);
139
+ if (accepted) {
140
+ return;
141
+ }
142
+ }
143
+ this.inbox.enqueue(unwrapped);
144
+ }
145
+ catch (error) {
146
+ if (error instanceof bounded_async_queue_js_1.QueueFullError) {
147
+ logger.warning('broadcast_channel_receive_queue_full', {
148
+ channel: this.channelName,
149
+ });
150
+ }
151
+ else {
152
+ logger.error('broadcast_channel_receive_error', {
153
+ channel: this.channelName,
154
+ error: error instanceof Error ? error.message : String(error),
155
+ });
156
+ }
157
+ }
158
+ return;
159
+ }
160
+ // Fall back to legacy format (no transport frame)
109
161
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
110
162
  if (!payload) {
111
163
  logger.debug('broadcast_channel_payload_rejected', {
@@ -244,10 +296,26 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
244
296
  logger.debug('broadcast_channel_message_sending', {
245
297
  channel: this.channelName,
246
298
  sender_id: this.connectorId,
299
+ local_node_id: this.localNodeId,
300
+ remote_node_id: this.remoteNodeId,
247
301
  });
302
+ // Only use transport framing if both localNodeId and remoteNodeId are explicitly set
303
+ // (not using default values). This ensures backwards compatibility.
304
+ const useTransportFrame = this.localNodeId !== this.connectorId ||
305
+ this.remoteNodeId !== '*';
306
+ let payload;
307
+ if (useTransportFrame) {
308
+ // Wrap payload in transport frame
309
+ const frame = (0, transport_frame_js_1.wrapTransportFrame)(data, this.localNodeId, this.remoteNodeId);
310
+ payload = (0, transport_frame_js_1.serializeTransportFrame)(frame);
311
+ }
312
+ else {
313
+ // Legacy format: send raw payload
314
+ payload = data;
315
+ }
248
316
  this.channel.postMessage({
249
317
  senderId: this.connectorId,
250
- payload: data,
318
+ payload,
251
319
  });
252
320
  }
253
321
  async _transportReceive() {
@@ -84,6 +84,8 @@ class InPageConnectorFactory extends connector_factory_js_1.ConnectorFactory {
84
84
  type: inpage_connector_js_1.INPAGE_CONNECTOR_TYPE,
85
85
  channelName,
86
86
  inboxCapacity,
87
+ localNodeId: normalized.localNodeId,
88
+ remoteNodeId: normalized.remoteNodeId,
87
89
  };
88
90
  const connector = new inpage_connector_js_1.InPageConnector(connectorConfig, baseConfig);
89
91
  if (options.authorization) {
@@ -152,6 +154,16 @@ class InPageConnectorFactory extends connector_factory_js_1.ConnectorFactory {
152
154
  if (candidate.authorizationContext !== undefined) {
153
155
  normalized.authorizationContext = candidate.authorizationContext;
154
156
  }
157
+ // Handle localNodeId
158
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
159
+ if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
160
+ normalized.localNodeId = localNodeId.trim();
161
+ }
162
+ // Handle remoteNodeId
163
+ const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
164
+ if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
165
+ normalized.remoteNodeId = remoteNodeId.trim();
166
+ }
155
167
  normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL;
156
168
  normalized.inboxCapacity =
157
169
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;