@naylence/runtime 0.3.5-test.961 → 0.3.5-test.963

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 (24) hide show
  1. package/dist/browser/index.cjs +290 -33
  2. package/dist/browser/index.mjs +290 -33
  3. package/dist/cjs/naylence/fame/connector/broadcast-channel-connector-factory.js +36 -0
  4. package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +131 -6
  5. package/dist/cjs/naylence/fame/connector/broadcast-channel-listener.js +91 -25
  6. package/dist/cjs/naylence/fame/node/upstream-session-manager.js +30 -0
  7. package/dist/cjs/version.js +2 -2
  8. package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +36 -0
  9. package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +131 -6
  10. package/dist/esm/naylence/fame/connector/broadcast-channel-listener.js +91 -25
  11. package/dist/esm/naylence/fame/node/upstream-session-manager.js +30 -0
  12. package/dist/esm/version.js +2 -2
  13. package/dist/node/index.cjs +290 -33
  14. package/dist/node/index.mjs +290 -33
  15. package/dist/node/node.cjs +290 -33
  16. package/dist/node/node.mjs +290 -33
  17. package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +6 -0
  18. package/dist/types/naylence/fame/connector/broadcast-channel-connector.browser.d.ts +10 -0
  19. package/dist/types/naylence/fame/connector/broadcast-channel-connector.node.d.ts +1 -6
  20. package/dist/types/naylence/fame/connector/broadcast-channel-listener.d.ts +3 -0
  21. package/dist/types/naylence/fame/grants/broadcast-channel-connection-grant.d.ts +1 -1
  22. package/dist/types/naylence/fame/node/upstream-session-manager.d.ts +2 -0
  23. package/dist/types/version.d.ts +1 -1
  24. 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.961
17
+ // Generated from package.json version: 0.3.5-test.963
18
18
  /**
19
19
  * The package version, injected at build time.
20
20
  * @internal
21
21
  */
22
- const VERSION = '0.3.5-test.961';
22
+ const VERSION = '0.3.5-test.963';
23
23
 
24
24
  /**
25
25
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9862,6 +9862,26 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9862
9862
  }
9863
9863
  return null;
9864
9864
  }
9865
+ static normalizeNodeId(value) {
9866
+ if (typeof value !== 'string') {
9867
+ return null;
9868
+ }
9869
+ const trimmed = value.trim();
9870
+ return trimmed.length > 0 ? trimmed : null;
9871
+ }
9872
+ static normalizeTargetNodeId(value) {
9873
+ if (typeof value !== 'string') {
9874
+ return undefined;
9875
+ }
9876
+ const trimmed = value.trim();
9877
+ if (trimmed.length === 0) {
9878
+ return undefined;
9879
+ }
9880
+ if (trimmed === '*') {
9881
+ return '*';
9882
+ }
9883
+ return trimmed;
9884
+ }
9865
9885
  constructor(config, baseConfig = {}) {
9866
9886
  ensureBroadcastEnvironment();
9867
9887
  super(baseConfig);
@@ -9884,10 +9904,18 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9884
9904
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9885
9905
  this.inboxCapacity = preferredCapacity;
9886
9906
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9907
+ const normalizedLocalNodeId = BroadcastChannelConnector.normalizeNodeId(config.localNodeId);
9908
+ if (!normalizedLocalNodeId) {
9909
+ throw new Error('BroadcastChannelConnector requires a non-empty localNodeId');
9910
+ }
9911
+ this.localNodeId = normalizedLocalNodeId;
9912
+ this.targetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(config.initialTargetNodeId);
9887
9913
  this.channel = new BroadcastChannel(this.channelName);
9888
9914
  logger$_.debug('broadcast_channel_connector_created', {
9889
9915
  channel: this.channelName,
9890
9916
  connector_id: this.connectorId,
9917
+ local_node_id: this.localNodeId,
9918
+ target_node_id: this.targetNodeId ?? null,
9891
9919
  inbox_capacity: preferredCapacity,
9892
9920
  timestamp: new Date().toISOString(),
9893
9921
  });
@@ -9909,15 +9937,32 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9909
9937
  ? message.constructor?.name ?? typeof message
9910
9938
  : typeof message,
9911
9939
  has_sender_id: Boolean(message?.senderId),
9940
+ has_sender_node_id: Boolean(message?.senderNodeId),
9912
9941
  });
9913
9942
  if (!message || typeof message !== 'object') {
9914
9943
  return;
9915
9944
  }
9916
9945
  const busMessage = message;
9917
- if (typeof busMessage.senderId !== 'string' || busMessage.senderId.length === 0) {
9946
+ const senderNodeId = BroadcastChannelConnector.normalizeNodeId(busMessage.senderNodeId);
9947
+ if (!senderNodeId) {
9948
+ logger$_.debug('broadcast_channel_message_rejected', {
9949
+ channel: this.channelName,
9950
+ connector_id: this.connectorId,
9951
+ reason: 'missing_sender_node_id',
9952
+ });
9918
9953
  return;
9919
9954
  }
9920
- if (busMessage.senderId === this.connectorId) {
9955
+ if (senderNodeId === this.localNodeId) {
9956
+ logger$_.debug('broadcast_channel_message_rejected', {
9957
+ channel: this.channelName,
9958
+ connector_id: this.connectorId,
9959
+ reason: 'self_echo',
9960
+ sender_node_id: senderNodeId,
9961
+ });
9962
+ return;
9963
+ }
9964
+ const incomingTargetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(busMessage.targetNodeId);
9965
+ if (!this._shouldAcceptMessageFromBus(senderNodeId, incomingTargetNodeId)) {
9921
9966
  return;
9922
9967
  }
9923
9968
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
@@ -9931,11 +9976,13 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9931
9976
  }
9932
9977
  logger$_.debug('broadcast_channel_message_received', {
9933
9978
  channel: this.channelName,
9934
- sender_id: busMessage.senderId,
9979
+ sender_id: message?.senderId,
9980
+ sender_node_id: senderNodeId,
9981
+ target_node_id: incomingTargetNodeId ?? null,
9935
9982
  connector_id: this.connectorId,
9936
9983
  payload_length: payload.byteLength,
9937
9984
  });
9938
- if (this._shouldSkipDuplicateAck(busMessage.senderId, payload)) {
9985
+ if (this._shouldSkipDuplicateAck(senderNodeId, payload)) {
9939
9986
  return;
9940
9987
  }
9941
9988
  try {
@@ -10079,12 +10126,17 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10079
10126
  }
10080
10127
  async _transportSendBytes(data) {
10081
10128
  ensureBroadcastEnvironment();
10129
+ const targetNodeId = this.targetNodeId ?? '*';
10082
10130
  logger$_.debug('broadcast_channel_message_sending', {
10083
10131
  channel: this.channelName,
10084
10132
  sender_id: this.connectorId,
10133
+ sender_node_id: this.localNodeId,
10134
+ target_node_id: targetNodeId,
10085
10135
  });
10086
10136
  this.channel.postMessage({
10087
10137
  senderId: this.connectorId,
10138
+ senderNodeId: this.localNodeId,
10139
+ targetNodeId,
10088
10140
  payload: data,
10089
10141
  });
10090
10142
  }
@@ -10147,6 +10199,51 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10147
10199
  }
10148
10200
  return rawOrEnvelope;
10149
10201
  }
10202
+ _isWildcardTarget() {
10203
+ return this.targetNodeId === '*' || typeof this.targetNodeId === 'undefined';
10204
+ }
10205
+ _shouldAcceptMessageFromBus(senderNodeId, targetNodeId) {
10206
+ if (this._isWildcardTarget()) {
10207
+ if (targetNodeId && targetNodeId !== '*') {
10208
+ logger$_.debug('broadcast_channel_message_rejected', {
10209
+ channel: this.channelName,
10210
+ connector_id: this.connectorId,
10211
+ reason: 'wildcard_target_mismatch',
10212
+ sender_node_id: senderNodeId,
10213
+ target_node_id: targetNodeId,
10214
+ local_node_id: this.localNodeId,
10215
+ });
10216
+ return false;
10217
+ }
10218
+ return true;
10219
+ }
10220
+ const expectedSender = this.targetNodeId;
10221
+ if (expectedSender && expectedSender !== '*' && senderNodeId !== expectedSender) {
10222
+ logger$_.debug('broadcast_channel_message_rejected', {
10223
+ channel: this.channelName,
10224
+ connector_id: this.connectorId,
10225
+ reason: 'unexpected_sender',
10226
+ expected_sender_node_id: expectedSender,
10227
+ sender_node_id: senderNodeId,
10228
+ local_node_id: this.localNodeId,
10229
+ });
10230
+ return false;
10231
+ }
10232
+ if (targetNodeId &&
10233
+ targetNodeId !== '*' &&
10234
+ targetNodeId !== this.localNodeId) {
10235
+ logger$_.debug('broadcast_channel_message_rejected', {
10236
+ channel: this.channelName,
10237
+ connector_id: this.connectorId,
10238
+ reason: 'unexpected_target',
10239
+ sender_node_id: senderNodeId,
10240
+ target_node_id: targetNodeId,
10241
+ local_node_id: this.localNodeId,
10242
+ });
10243
+ return false;
10244
+ }
10245
+ return true;
10246
+ }
10150
10247
  _describeInboxItem(item) {
10151
10248
  if (item instanceof Uint8Array) {
10152
10249
  return 'bytes';
@@ -10177,7 +10274,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10177
10274
  const normalizedSenderId = typeof senderId === 'string' && senderId.length > 0
10178
10275
  ? senderId
10179
10276
  : undefined;
10180
- if (normalizedSenderId && normalizedSenderId !== this.connectorId) {
10277
+ if (normalizedSenderId && normalizedSenderId !== this.localNodeId) {
10181
10278
  logger$_.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
10182
10279
  channel: this.channelName,
10183
10280
  connector_id: this.connectorId,
@@ -10217,7 +10314,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10217
10314
  return false;
10218
10315
  }
10219
10316
  const senderId = this._extractSenderIdFromInboxItem(item);
10220
- if (senderId && senderId !== this.connectorId) {
10317
+ if (senderId && senderId !== this.localNodeId) {
10221
10318
  logger$_.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
10222
10319
  channel: this.channelName,
10223
10320
  connector_id: this.connectorId,
@@ -10313,6 +10410,34 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10313
10410
  });
10314
10411
  }
10315
10412
  }
10413
+ setTargetNodeId(nodeId) {
10414
+ const normalized = BroadcastChannelConnector.normalizeNodeId(nodeId);
10415
+ if (!normalized) {
10416
+ throw new Error('BroadcastChannelConnector target node id must be a non-empty string');
10417
+ }
10418
+ if (normalized === '*') {
10419
+ this.setWildcardTarget();
10420
+ return;
10421
+ }
10422
+ this.targetNodeId = normalized;
10423
+ logger$_.debug('broadcast_channel_target_updated', {
10424
+ channel: this.channelName,
10425
+ connector_id: this.connectorId,
10426
+ local_node_id: this.localNodeId,
10427
+ target_node_id: this.targetNodeId,
10428
+ target_mode: 'direct',
10429
+ });
10430
+ }
10431
+ setWildcardTarget() {
10432
+ this.targetNodeId = '*';
10433
+ logger$_.debug('broadcast_channel_target_updated', {
10434
+ channel: this.channelName,
10435
+ connector_id: this.connectorId,
10436
+ local_node_id: this.localNodeId,
10437
+ target_node_id: this.targetNodeId,
10438
+ target_mode: 'wildcard',
10439
+ });
10440
+ }
10316
10441
  _trimSeenAcks(now) {
10317
10442
  while (this.seenAckOrder.length > 0) {
10318
10443
  const candidate = this.seenAckOrder[0];
@@ -10784,6 +10909,20 @@ class UpstreamSessionManager extends TaskSpawner {
10784
10909
  waitEvent(event, signal) {
10785
10910
  return signal ? event.wait({ signal }) : event.wait();
10786
10911
  }
10912
+ _getLocalNodeId() {
10913
+ const normalized = this._normalizeNodeId(this.node.id);
10914
+ if (!normalized) {
10915
+ throw new Error('UpstreamSessionManager requires node with a stable identifier');
10916
+ }
10917
+ return normalized;
10918
+ }
10919
+ _normalizeNodeId(value) {
10920
+ if (typeof value !== 'string') {
10921
+ return null;
10922
+ }
10923
+ const trimmed = value.trim();
10924
+ return trimmed.length > 0 ? trimmed : null;
10925
+ }
10787
10926
  async connectCycle() {
10788
10927
  if (!this.admissionClient) {
10789
10928
  throw new FameConnectError('Admission client is required to attach upstream');
@@ -10805,6 +10944,8 @@ class UpstreamSessionManager extends TaskSpawner {
10805
10944
  await this.onWelcome(welcome.frame);
10806
10945
  const connector = await ConnectorFactory.createConnector(grant, {
10807
10946
  systemId: welcome.frame.systemId,
10947
+ localNodeId: this._getLocalNodeId(),
10948
+ initialTargetNodeId: '*',
10808
10949
  });
10809
10950
  await connector.start(this.wrappedHandler);
10810
10951
  this.connector = connector;
@@ -10830,6 +10971,20 @@ class UpstreamSessionManager extends TaskSpawner {
10830
10971
  }
10831
10972
  const attachInfo = await this.attachClient.attach(this.node, this.outboundOriginType, connector, welcome.frame, this.wrappedHandler, this.getKeys() ?? undefined, callbackGrants);
10832
10973
  this.targetSystemId = attachInfo.targetSystemId ?? null;
10974
+ if (this.targetSystemId) {
10975
+ const targetAware = connector;
10976
+ if (typeof targetAware.setTargetNodeId === 'function') {
10977
+ try {
10978
+ targetAware.setTargetNodeId(this.targetSystemId);
10979
+ }
10980
+ catch (error) {
10981
+ logger$Z.warning('broadcast_channel_target_apply_failed', {
10982
+ error: error instanceof Error ? error.message : String(error),
10983
+ target_node_id: this.targetSystemId,
10984
+ });
10985
+ }
10986
+ }
10987
+ }
10833
10988
  await this.onAttach(attachInfo, connector);
10834
10989
  // Close the admission client immediately after attach completes
10835
10990
  // This releases HTTP keep-alive connections (Node.js fetch/undici requires explicit cleanup)
@@ -28687,8 +28842,16 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28687
28842
  }
28688
28843
  const normalized = this._normalizeConfig(config);
28689
28844
  const options = (factoryArgs[0] ?? {});
28845
+ const normalizedLocalNodeFromConfig = this._normalizeNodeId(normalized.localNodeId);
28846
+ const localNodeId = this._normalizeNodeId(options.localNodeId) ?? normalizedLocalNodeFromConfig;
28847
+ if (!localNodeId) {
28848
+ throw new Error('BroadcastChannelConnectorFactory requires a localNodeId from config or create() options');
28849
+ }
28690
28850
  const channelName = normalized.channelName ?? DEFAULT_CHANNEL$5;
28691
28851
  const inboxCapacity = normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$5;
28852
+ const targetFromOptions = this._normalizeTargetNodeId(options.initialTargetNodeId);
28853
+ const targetFromConfig = this._normalizeTargetNodeId(normalized.initialTargetNodeId);
28854
+ const resolvedTarget = targetFromOptions ?? targetFromConfig ?? '*';
28692
28855
  const baseConfig = {
28693
28856
  drainTimeout: normalized.drainTimeout,
28694
28857
  flowControl: normalized.flowControl,
@@ -28703,6 +28866,8 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28703
28866
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
28704
28867
  channelName,
28705
28868
  inboxCapacity,
28869
+ localNodeId,
28870
+ initialTargetNodeId: resolvedTarget,
28706
28871
  };
28707
28872
  const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
28708
28873
  if (options.authorization) {
@@ -28726,11 +28891,21 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28726
28891
  normalized.channelName = channel.trim();
28727
28892
  }
28728
28893
  const capacity = candidate.inboxCapacity ?? candidate['inbox_capacity'];
28894
+ const initialTargetNodeId = candidate.initialTargetNodeId ?? candidate['initial_target_node_id'];
28895
+ const normalizedTarget = this._normalizeTargetNodeId(initialTargetNodeId);
28896
+ if (normalizedTarget) {
28897
+ normalized.initialTargetNodeId = normalizedTarget;
28898
+ }
28729
28899
  if (typeof capacity === 'number' &&
28730
28900
  Number.isFinite(capacity) &&
28731
28901
  capacity > 0) {
28732
28902
  normalized.inboxCapacity = Math.floor(capacity);
28733
28903
  }
28904
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
28905
+ const normalizedLocalNodeId = this._normalizeNodeId(localNodeId);
28906
+ if (normalizedLocalNodeId) {
28907
+ normalized.localNodeId = normalizedLocalNodeId;
28908
+ }
28734
28909
  if (typeof candidate.flowControl === 'boolean') {
28735
28910
  normalized.flowControl = candidate.flowControl;
28736
28911
  }
@@ -28769,6 +28944,22 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28769
28944
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$5;
28770
28945
  return normalized;
28771
28946
  }
28947
+ _normalizeNodeId(value) {
28948
+ if (typeof value !== 'string') {
28949
+ return null;
28950
+ }
28951
+ const trimmed = value.trim();
28952
+ return trimmed.length > 0 ? trimmed : null;
28953
+ }
28954
+ _normalizeTargetNodeId(value) {
28955
+ if (value === undefined || value === null) {
28956
+ return undefined;
28957
+ }
28958
+ if (value === '*') {
28959
+ return '*';
28960
+ }
28961
+ return this._normalizeNodeId(value) ?? undefined;
28962
+ }
28772
28963
  }
28773
28964
 
28774
28965
  var broadcastChannelConnectorFactory = /*#__PURE__*/Object.freeze({
@@ -36346,7 +36537,7 @@ class BroadcastChannelListener extends TransportListener {
36346
36537
  node: routingNode,
36347
36538
  });
36348
36539
  const selection = defaultGrantSelectionPolicy.selectCallbackGrant(selectionContext);
36349
- connectorConfig = this._grantToConnectorConfig(selection.grant);
36540
+ connectorConfig = this._grantToConnectorConfig(selection.grant, systemId);
36350
36541
  }
36351
36542
  catch (error) {
36352
36543
  logger$a.debug('broadcast_channel_listener_grant_selection_failed', {
@@ -36355,13 +36546,20 @@ class BroadcastChannelListener extends TransportListener {
36355
36546
  error: error instanceof Error ? error.message : String(error),
36356
36547
  });
36357
36548
  connectorConfig =
36358
- this._extractBroadcastConnectorConfig(frame) ??
36359
- {
36549
+ this._extractBroadcastConnectorConfig(frame, systemId) ??
36550
+ this._buildConnectorConfigForSystem(systemId, {
36360
36551
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
36361
36552
  channelName: this._channelName,
36362
36553
  inboxCapacity: this._inboxCapacity,
36363
36554
  passive: true,
36364
- };
36555
+ });
36556
+ }
36557
+ if (!connectorConfig) {
36558
+ logger$a.error('broadcast_channel_listener_missing_connector_config', {
36559
+ sender_id: params.senderId,
36560
+ system_id: systemId,
36561
+ });
36562
+ return null;
36365
36563
  }
36366
36564
  try {
36367
36565
  const connector = await routingNode.createOriginConnector({
@@ -36387,7 +36585,7 @@ class BroadcastChannelListener extends TransportListener {
36387
36585
  return null;
36388
36586
  }
36389
36587
  }
36390
- _extractBroadcastConnectorConfig(frame) {
36588
+ _extractBroadcastConnectorConfig(frame, systemId) {
36391
36589
  const rawGrants = frame.callbackGrants;
36392
36590
  if (!Array.isArray(rawGrants)) {
36393
36591
  return null;
@@ -36398,7 +36596,10 @@ class BroadcastChannelListener extends TransportListener {
36398
36596
  (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE ||
36399
36597
  grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE)) {
36400
36598
  try {
36401
- return this._grantToConnectorConfig(grant);
36599
+ if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
36600
+ return this._buildConnectorConfigForSystem(systemId, grant);
36601
+ }
36602
+ return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
36402
36603
  }
36403
36604
  catch (error) {
36404
36605
  logger$a.debug('broadcast_channel_listener_grant_normalization_failed', {
@@ -36409,31 +36610,87 @@ class BroadcastChannelListener extends TransportListener {
36409
36610
  }
36410
36611
  return null;
36411
36612
  }
36412
- _grantToConnectorConfig(grant) {
36413
- if (grant.type !== BROADCAST_CHANNEL_CONNECTOR_TYPE) {
36414
- if (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
36415
- return broadcastChannelGrantToConnectorConfig(grant);
36613
+ _grantToConnectorConfig(grant, systemId) {
36614
+ if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
36615
+ return this._buildConnectorConfigForSystem(systemId, grant);
36616
+ }
36617
+ if (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
36618
+ return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
36619
+ }
36620
+ if ('toConnectorConfig' in grant &&
36621
+ typeof grant.toConnectorConfig ===
36622
+ 'function') {
36623
+ const normalized = grant.toConnectorConfig();
36624
+ if (normalized.type !== BROADCAST_CHANNEL_CONNECTOR_TYPE) {
36625
+ throw new Error(`Unsupported grant connector type: ${normalized.type}`);
36416
36626
  }
36417
- throw new Error(`Unsupported grant type: ${grant.type}`);
36627
+ return this._buildConnectorConfigForSystem(systemId, normalized);
36418
36628
  }
36419
- const candidate = grant;
36420
- const config = {
36629
+ throw new Error(`Unsupported grant type: ${grant.type}`);
36630
+ }
36631
+ _buildConnectorConfigForSystem(systemId, baseConfig) {
36632
+ const localNodeId = this._requireLocalNodeId();
36633
+ const targetSystemId = this._normalizeNodeId(systemId);
36634
+ if (!targetSystemId) {
36635
+ throw new Error('BroadcastChannelListener requires a valid system id');
36636
+ }
36637
+ const candidate = baseConfig ?? null;
36638
+ const channelCandidate = candidate && 'channelName' in candidate
36639
+ ? candidate.channelName
36640
+ : undefined;
36641
+ const inboxCandidate = candidate && 'inboxCapacity' in candidate
36642
+ ? candidate.inboxCapacity
36643
+ : undefined;
36644
+ const initialWindowCandidate = candidate && 'initialWindow' in candidate
36645
+ ? candidate.initialWindow
36646
+ : undefined;
36647
+ const passiveCandidate = candidate && 'passive' in candidate
36648
+ ? candidate.passive
36649
+ : undefined;
36650
+ const targetCandidate = candidate && 'initialTargetNodeId' in candidate
36651
+ ? candidate.initialTargetNodeId
36652
+ : undefined;
36653
+ const channelName = typeof channelCandidate === 'string' && channelCandidate.trim().length > 0
36654
+ ? channelCandidate.trim()
36655
+ : this._channelName;
36656
+ const inboxCapacity = typeof inboxCandidate === 'number' &&
36657
+ Number.isFinite(inboxCandidate) &&
36658
+ inboxCandidate > 0
36659
+ ? Math.floor(inboxCandidate)
36660
+ : this._inboxCapacity;
36661
+ const initialWindow = typeof initialWindowCandidate === 'number' &&
36662
+ Number.isFinite(initialWindowCandidate) &&
36663
+ initialWindowCandidate > 0
36664
+ ? Math.floor(initialWindowCandidate)
36665
+ : undefined;
36666
+ const initialTargetNodeId = this._normalizeNodeId(targetCandidate) ?? targetSystemId;
36667
+ return {
36421
36668
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
36422
- channelName: this._channelName,
36423
- inboxCapacity: this._inboxCapacity,
36424
- passive: true,
36669
+ channelName,
36670
+ inboxCapacity,
36671
+ passive: typeof passiveCandidate === 'boolean' ? passiveCandidate : true,
36672
+ initialWindow,
36673
+ localNodeId,
36674
+ initialTargetNodeId,
36425
36675
  };
36426
- const channelCandidate = candidate.channelName ?? candidate['channel_name'];
36427
- if (typeof channelCandidate === 'string' && channelCandidate.trim().length > 0) {
36428
- config.channelName = channelCandidate.trim();
36676
+ }
36677
+ _requireLocalNodeId() {
36678
+ if (!this._routingNode) {
36679
+ throw new Error('BroadcastChannelListener requires routing node context');
36429
36680
  }
36430
- const inboxCandidate = candidate.inboxCapacity ?? candidate['inbox_capacity'];
36431
- if (typeof inboxCandidate === 'number' &&
36432
- Number.isFinite(inboxCandidate) &&
36433
- inboxCandidate > 0) {
36434
- config.inboxCapacity = Math.floor(inboxCandidate);
36681
+ const normalized = this._normalizeNodeId(this._routingNode.sid) ??
36682
+ this._normalizeNodeId(this._routingNode.id);
36683
+ if (!normalized) {
36684
+ throw new Error('BroadcastChannelListener requires routing node with a stable identifier');
36435
36685
  }
36436
- return config;
36686
+ return normalized;
36687
+ }
36688
+ _normalizeNodeId(value) {
36689
+ if (typeof value !== 'string') {
36690
+ return null;
36691
+ }
36692
+ const trimmed = value.trim();
36693
+ return trimmed.length > 0 ? trimmed : null;
36437
36694
  }
36438
36695
  _monitorConnectorLifecycle(senderId, systemId, connector) {
36439
36696
  const maybeClosable = connector;