@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
@@ -98,12 +98,12 @@ installProcessEnvShim();
98
98
  // --- END ENV SHIM ---
99
99
 
100
100
  // This file is auto-generated during build - do not edit manually
101
- // Generated from package.json version: 0.3.5-test.961
101
+ // Generated from package.json version: 0.3.5-test.963
102
102
  /**
103
103
  * The package version, injected at build time.
104
104
  * @internal
105
105
  */
106
- const VERSION = '0.3.5-test.961';
106
+ const VERSION = '0.3.5-test.963';
107
107
 
108
108
  /**
109
109
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9946,6 +9946,26 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9946
9946
  }
9947
9947
  return null;
9948
9948
  }
9949
+ static normalizeNodeId(value) {
9950
+ if (typeof value !== 'string') {
9951
+ return null;
9952
+ }
9953
+ const trimmed = value.trim();
9954
+ return trimmed.length > 0 ? trimmed : null;
9955
+ }
9956
+ static normalizeTargetNodeId(value) {
9957
+ if (typeof value !== 'string') {
9958
+ return undefined;
9959
+ }
9960
+ const trimmed = value.trim();
9961
+ if (trimmed.length === 0) {
9962
+ return undefined;
9963
+ }
9964
+ if (trimmed === '*') {
9965
+ return '*';
9966
+ }
9967
+ return trimmed;
9968
+ }
9949
9969
  constructor(config, baseConfig = {}) {
9950
9970
  ensureBroadcastEnvironment();
9951
9971
  super(baseConfig);
@@ -9968,10 +9988,18 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9968
9988
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9969
9989
  this.inboxCapacity = preferredCapacity;
9970
9990
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9991
+ const normalizedLocalNodeId = BroadcastChannelConnector.normalizeNodeId(config.localNodeId);
9992
+ if (!normalizedLocalNodeId) {
9993
+ throw new Error('BroadcastChannelConnector requires a non-empty localNodeId');
9994
+ }
9995
+ this.localNodeId = normalizedLocalNodeId;
9996
+ this.targetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(config.initialTargetNodeId);
9971
9997
  this.channel = new BroadcastChannel(this.channelName);
9972
9998
  logger$_.debug('broadcast_channel_connector_created', {
9973
9999
  channel: this.channelName,
9974
10000
  connector_id: this.connectorId,
10001
+ local_node_id: this.localNodeId,
10002
+ target_node_id: this.targetNodeId ?? null,
9975
10003
  inbox_capacity: preferredCapacity,
9976
10004
  timestamp: new Date().toISOString(),
9977
10005
  });
@@ -9993,15 +10021,32 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9993
10021
  ? message.constructor?.name ?? typeof message
9994
10022
  : typeof message,
9995
10023
  has_sender_id: Boolean(message?.senderId),
10024
+ has_sender_node_id: Boolean(message?.senderNodeId),
9996
10025
  });
9997
10026
  if (!message || typeof message !== 'object') {
9998
10027
  return;
9999
10028
  }
10000
10029
  const busMessage = message;
10001
- if (typeof busMessage.senderId !== 'string' || busMessage.senderId.length === 0) {
10030
+ const senderNodeId = BroadcastChannelConnector.normalizeNodeId(busMessage.senderNodeId);
10031
+ if (!senderNodeId) {
10032
+ logger$_.debug('broadcast_channel_message_rejected', {
10033
+ channel: this.channelName,
10034
+ connector_id: this.connectorId,
10035
+ reason: 'missing_sender_node_id',
10036
+ });
10002
10037
  return;
10003
10038
  }
10004
- if (busMessage.senderId === this.connectorId) {
10039
+ if (senderNodeId === this.localNodeId) {
10040
+ logger$_.debug('broadcast_channel_message_rejected', {
10041
+ channel: this.channelName,
10042
+ connector_id: this.connectorId,
10043
+ reason: 'self_echo',
10044
+ sender_node_id: senderNodeId,
10045
+ });
10046
+ return;
10047
+ }
10048
+ const incomingTargetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(busMessage.targetNodeId);
10049
+ if (!this._shouldAcceptMessageFromBus(senderNodeId, incomingTargetNodeId)) {
10005
10050
  return;
10006
10051
  }
10007
10052
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
@@ -10015,11 +10060,13 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10015
10060
  }
10016
10061
  logger$_.debug('broadcast_channel_message_received', {
10017
10062
  channel: this.channelName,
10018
- sender_id: busMessage.senderId,
10063
+ sender_id: message?.senderId,
10064
+ sender_node_id: senderNodeId,
10065
+ target_node_id: incomingTargetNodeId ?? null,
10019
10066
  connector_id: this.connectorId,
10020
10067
  payload_length: payload.byteLength,
10021
10068
  });
10022
- if (this._shouldSkipDuplicateAck(busMessage.senderId, payload)) {
10069
+ if (this._shouldSkipDuplicateAck(senderNodeId, payload)) {
10023
10070
  return;
10024
10071
  }
10025
10072
  try {
@@ -10163,12 +10210,17 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10163
10210
  }
10164
10211
  async _transportSendBytes(data) {
10165
10212
  ensureBroadcastEnvironment();
10213
+ const targetNodeId = this.targetNodeId ?? '*';
10166
10214
  logger$_.debug('broadcast_channel_message_sending', {
10167
10215
  channel: this.channelName,
10168
10216
  sender_id: this.connectorId,
10217
+ sender_node_id: this.localNodeId,
10218
+ target_node_id: targetNodeId,
10169
10219
  });
10170
10220
  this.channel.postMessage({
10171
10221
  senderId: this.connectorId,
10222
+ senderNodeId: this.localNodeId,
10223
+ targetNodeId,
10172
10224
  payload: data,
10173
10225
  });
10174
10226
  }
@@ -10231,6 +10283,51 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10231
10283
  }
10232
10284
  return rawOrEnvelope;
10233
10285
  }
10286
+ _isWildcardTarget() {
10287
+ return this.targetNodeId === '*' || typeof this.targetNodeId === 'undefined';
10288
+ }
10289
+ _shouldAcceptMessageFromBus(senderNodeId, targetNodeId) {
10290
+ if (this._isWildcardTarget()) {
10291
+ if (targetNodeId && targetNodeId !== '*') {
10292
+ logger$_.debug('broadcast_channel_message_rejected', {
10293
+ channel: this.channelName,
10294
+ connector_id: this.connectorId,
10295
+ reason: 'wildcard_target_mismatch',
10296
+ sender_node_id: senderNodeId,
10297
+ target_node_id: targetNodeId,
10298
+ local_node_id: this.localNodeId,
10299
+ });
10300
+ return false;
10301
+ }
10302
+ return true;
10303
+ }
10304
+ const expectedSender = this.targetNodeId;
10305
+ if (expectedSender && expectedSender !== '*' && senderNodeId !== expectedSender) {
10306
+ logger$_.debug('broadcast_channel_message_rejected', {
10307
+ channel: this.channelName,
10308
+ connector_id: this.connectorId,
10309
+ reason: 'unexpected_sender',
10310
+ expected_sender_node_id: expectedSender,
10311
+ sender_node_id: senderNodeId,
10312
+ local_node_id: this.localNodeId,
10313
+ });
10314
+ return false;
10315
+ }
10316
+ if (targetNodeId &&
10317
+ targetNodeId !== '*' &&
10318
+ targetNodeId !== this.localNodeId) {
10319
+ logger$_.debug('broadcast_channel_message_rejected', {
10320
+ channel: this.channelName,
10321
+ connector_id: this.connectorId,
10322
+ reason: 'unexpected_target',
10323
+ sender_node_id: senderNodeId,
10324
+ target_node_id: targetNodeId,
10325
+ local_node_id: this.localNodeId,
10326
+ });
10327
+ return false;
10328
+ }
10329
+ return true;
10330
+ }
10234
10331
  _describeInboxItem(item) {
10235
10332
  if (item instanceof Uint8Array) {
10236
10333
  return 'bytes';
@@ -10261,7 +10358,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10261
10358
  const normalizedSenderId = typeof senderId === 'string' && senderId.length > 0
10262
10359
  ? senderId
10263
10360
  : undefined;
10264
- if (normalizedSenderId && normalizedSenderId !== this.connectorId) {
10361
+ if (normalizedSenderId && normalizedSenderId !== this.localNodeId) {
10265
10362
  logger$_.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
10266
10363
  channel: this.channelName,
10267
10364
  connector_id: this.connectorId,
@@ -10301,7 +10398,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10301
10398
  return false;
10302
10399
  }
10303
10400
  const senderId = this._extractSenderIdFromInboxItem(item);
10304
- if (senderId && senderId !== this.connectorId) {
10401
+ if (senderId && senderId !== this.localNodeId) {
10305
10402
  logger$_.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
10306
10403
  channel: this.channelName,
10307
10404
  connector_id: this.connectorId,
@@ -10397,6 +10494,34 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10397
10494
  });
10398
10495
  }
10399
10496
  }
10497
+ setTargetNodeId(nodeId) {
10498
+ const normalized = BroadcastChannelConnector.normalizeNodeId(nodeId);
10499
+ if (!normalized) {
10500
+ throw new Error('BroadcastChannelConnector target node id must be a non-empty string');
10501
+ }
10502
+ if (normalized === '*') {
10503
+ this.setWildcardTarget();
10504
+ return;
10505
+ }
10506
+ this.targetNodeId = normalized;
10507
+ logger$_.debug('broadcast_channel_target_updated', {
10508
+ channel: this.channelName,
10509
+ connector_id: this.connectorId,
10510
+ local_node_id: this.localNodeId,
10511
+ target_node_id: this.targetNodeId,
10512
+ target_mode: 'direct',
10513
+ });
10514
+ }
10515
+ setWildcardTarget() {
10516
+ this.targetNodeId = '*';
10517
+ logger$_.debug('broadcast_channel_target_updated', {
10518
+ channel: this.channelName,
10519
+ connector_id: this.connectorId,
10520
+ local_node_id: this.localNodeId,
10521
+ target_node_id: this.targetNodeId,
10522
+ target_mode: 'wildcard',
10523
+ });
10524
+ }
10400
10525
  _trimSeenAcks(now) {
10401
10526
  while (this.seenAckOrder.length > 0) {
10402
10527
  const candidate = this.seenAckOrder[0];
@@ -10868,6 +10993,20 @@ class UpstreamSessionManager extends TaskSpawner {
10868
10993
  waitEvent(event, signal) {
10869
10994
  return signal ? event.wait({ signal }) : event.wait();
10870
10995
  }
10996
+ _getLocalNodeId() {
10997
+ const normalized = this._normalizeNodeId(this.node.id);
10998
+ if (!normalized) {
10999
+ throw new Error('UpstreamSessionManager requires node with a stable identifier');
11000
+ }
11001
+ return normalized;
11002
+ }
11003
+ _normalizeNodeId(value) {
11004
+ if (typeof value !== 'string') {
11005
+ return null;
11006
+ }
11007
+ const trimmed = value.trim();
11008
+ return trimmed.length > 0 ? trimmed : null;
11009
+ }
10871
11010
  async connectCycle() {
10872
11011
  if (!this.admissionClient) {
10873
11012
  throw new FameConnectError('Admission client is required to attach upstream');
@@ -10889,6 +11028,8 @@ class UpstreamSessionManager extends TaskSpawner {
10889
11028
  await this.onWelcome(welcome.frame);
10890
11029
  const connector = await ConnectorFactory.createConnector(grant, {
10891
11030
  systemId: welcome.frame.systemId,
11031
+ localNodeId: this._getLocalNodeId(),
11032
+ initialTargetNodeId: '*',
10892
11033
  });
10893
11034
  await connector.start(this.wrappedHandler);
10894
11035
  this.connector = connector;
@@ -10914,6 +11055,20 @@ class UpstreamSessionManager extends TaskSpawner {
10914
11055
  }
10915
11056
  const attachInfo = await this.attachClient.attach(this.node, this.outboundOriginType, connector, welcome.frame, this.wrappedHandler, this.getKeys() ?? undefined, callbackGrants);
10916
11057
  this.targetSystemId = attachInfo.targetSystemId ?? null;
11058
+ if (this.targetSystemId) {
11059
+ const targetAware = connector;
11060
+ if (typeof targetAware.setTargetNodeId === 'function') {
11061
+ try {
11062
+ targetAware.setTargetNodeId(this.targetSystemId);
11063
+ }
11064
+ catch (error) {
11065
+ logger$Z.warning('broadcast_channel_target_apply_failed', {
11066
+ error: error instanceof Error ? error.message : String(error),
11067
+ target_node_id: this.targetSystemId,
11068
+ });
11069
+ }
11070
+ }
11071
+ }
10917
11072
  await this.onAttach(attachInfo, connector);
10918
11073
  // Close the admission client immediately after attach completes
10919
11074
  // This releases HTTP keep-alive connections (Node.js fetch/undici requires explicit cleanup)
@@ -28933,8 +29088,16 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28933
29088
  }
28934
29089
  const normalized = this._normalizeConfig(config);
28935
29090
  const options = (factoryArgs[0] ?? {});
29091
+ const normalizedLocalNodeFromConfig = this._normalizeNodeId(normalized.localNodeId);
29092
+ const localNodeId = this._normalizeNodeId(options.localNodeId) ?? normalizedLocalNodeFromConfig;
29093
+ if (!localNodeId) {
29094
+ throw new Error('BroadcastChannelConnectorFactory requires a localNodeId from config or create() options');
29095
+ }
28936
29096
  const channelName = normalized.channelName ?? DEFAULT_CHANNEL$4;
28937
29097
  const inboxCapacity = normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$4;
29098
+ const targetFromOptions = this._normalizeTargetNodeId(options.initialTargetNodeId);
29099
+ const targetFromConfig = this._normalizeTargetNodeId(normalized.initialTargetNodeId);
29100
+ const resolvedTarget = targetFromOptions ?? targetFromConfig ?? '*';
28938
29101
  const baseConfig = {
28939
29102
  drainTimeout: normalized.drainTimeout,
28940
29103
  flowControl: normalized.flowControl,
@@ -28949,6 +29112,8 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28949
29112
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
28950
29113
  channelName,
28951
29114
  inboxCapacity,
29115
+ localNodeId,
29116
+ initialTargetNodeId: resolvedTarget,
28952
29117
  };
28953
29118
  const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
28954
29119
  if (options.authorization) {
@@ -28972,11 +29137,21 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28972
29137
  normalized.channelName = channel.trim();
28973
29138
  }
28974
29139
  const capacity = candidate.inboxCapacity ?? candidate['inbox_capacity'];
29140
+ const initialTargetNodeId = candidate.initialTargetNodeId ?? candidate['initial_target_node_id'];
29141
+ const normalizedTarget = this._normalizeTargetNodeId(initialTargetNodeId);
29142
+ if (normalizedTarget) {
29143
+ normalized.initialTargetNodeId = normalizedTarget;
29144
+ }
28975
29145
  if (typeof capacity === 'number' &&
28976
29146
  Number.isFinite(capacity) &&
28977
29147
  capacity > 0) {
28978
29148
  normalized.inboxCapacity = Math.floor(capacity);
28979
29149
  }
29150
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
29151
+ const normalizedLocalNodeId = this._normalizeNodeId(localNodeId);
29152
+ if (normalizedLocalNodeId) {
29153
+ normalized.localNodeId = normalizedLocalNodeId;
29154
+ }
28980
29155
  if (typeof candidate.flowControl === 'boolean') {
28981
29156
  normalized.flowControl = candidate.flowControl;
28982
29157
  }
@@ -29015,6 +29190,22 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
29015
29190
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$4;
29016
29191
  return normalized;
29017
29192
  }
29193
+ _normalizeNodeId(value) {
29194
+ if (typeof value !== 'string') {
29195
+ return null;
29196
+ }
29197
+ const trimmed = value.trim();
29198
+ return trimmed.length > 0 ? trimmed : null;
29199
+ }
29200
+ _normalizeTargetNodeId(value) {
29201
+ if (value === undefined || value === null) {
29202
+ return undefined;
29203
+ }
29204
+ if (value === '*') {
29205
+ return '*';
29206
+ }
29207
+ return this._normalizeNodeId(value) ?? undefined;
29208
+ }
29018
29209
  }
29019
29210
 
29020
29211
  var broadcastChannelConnectorFactory = /*#__PURE__*/Object.freeze({
@@ -30118,7 +30309,7 @@ class BroadcastChannelListener extends TransportListener {
30118
30309
  node: routingNode,
30119
30310
  });
30120
30311
  const selection = defaultGrantSelectionPolicy.selectCallbackGrant(selectionContext);
30121
- connectorConfig = this._grantToConnectorConfig(selection.grant);
30312
+ connectorConfig = this._grantToConnectorConfig(selection.grant, systemId);
30122
30313
  }
30123
30314
  catch (error) {
30124
30315
  logger$o.debug('broadcast_channel_listener_grant_selection_failed', {
@@ -30127,13 +30318,20 @@ class BroadcastChannelListener extends TransportListener {
30127
30318
  error: error instanceof Error ? error.message : String(error),
30128
30319
  });
30129
30320
  connectorConfig =
30130
- this._extractBroadcastConnectorConfig(frame) ??
30131
- {
30321
+ this._extractBroadcastConnectorConfig(frame, systemId) ??
30322
+ this._buildConnectorConfigForSystem(systemId, {
30132
30323
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
30133
30324
  channelName: this._channelName,
30134
30325
  inboxCapacity: this._inboxCapacity,
30135
30326
  passive: true,
30136
- };
30327
+ });
30328
+ }
30329
+ if (!connectorConfig) {
30330
+ logger$o.error('broadcast_channel_listener_missing_connector_config', {
30331
+ sender_id: params.senderId,
30332
+ system_id: systemId,
30333
+ });
30334
+ return null;
30137
30335
  }
30138
30336
  try {
30139
30337
  const connector = await routingNode.createOriginConnector({
@@ -30159,7 +30357,7 @@ class BroadcastChannelListener extends TransportListener {
30159
30357
  return null;
30160
30358
  }
30161
30359
  }
30162
- _extractBroadcastConnectorConfig(frame) {
30360
+ _extractBroadcastConnectorConfig(frame, systemId) {
30163
30361
  const rawGrants = frame.callbackGrants;
30164
30362
  if (!Array.isArray(rawGrants)) {
30165
30363
  return null;
@@ -30170,7 +30368,10 @@ class BroadcastChannelListener extends TransportListener {
30170
30368
  (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE ||
30171
30369
  grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE)) {
30172
30370
  try {
30173
- return this._grantToConnectorConfig(grant);
30371
+ if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30372
+ return this._buildConnectorConfigForSystem(systemId, grant);
30373
+ }
30374
+ return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
30174
30375
  }
30175
30376
  catch (error) {
30176
30377
  logger$o.debug('broadcast_channel_listener_grant_normalization_failed', {
@@ -30181,31 +30382,87 @@ class BroadcastChannelListener extends TransportListener {
30181
30382
  }
30182
30383
  return null;
30183
30384
  }
30184
- _grantToConnectorConfig(grant) {
30185
- if (grant.type !== BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30186
- if (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
30187
- return broadcastChannelGrantToConnectorConfig(grant);
30385
+ _grantToConnectorConfig(grant, systemId) {
30386
+ if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30387
+ return this._buildConnectorConfigForSystem(systemId, grant);
30388
+ }
30389
+ if (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
30390
+ return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
30391
+ }
30392
+ if ('toConnectorConfig' in grant &&
30393
+ typeof grant.toConnectorConfig ===
30394
+ 'function') {
30395
+ const normalized = grant.toConnectorConfig();
30396
+ if (normalized.type !== BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30397
+ throw new Error(`Unsupported grant connector type: ${normalized.type}`);
30188
30398
  }
30189
- throw new Error(`Unsupported grant type: ${grant.type}`);
30399
+ return this._buildConnectorConfigForSystem(systemId, normalized);
30190
30400
  }
30191
- const candidate = grant;
30192
- const config = {
30401
+ throw new Error(`Unsupported grant type: ${grant.type}`);
30402
+ }
30403
+ _buildConnectorConfigForSystem(systemId, baseConfig) {
30404
+ const localNodeId = this._requireLocalNodeId();
30405
+ const targetSystemId = this._normalizeNodeId(systemId);
30406
+ if (!targetSystemId) {
30407
+ throw new Error('BroadcastChannelListener requires a valid system id');
30408
+ }
30409
+ const candidate = baseConfig ?? null;
30410
+ const channelCandidate = candidate && 'channelName' in candidate
30411
+ ? candidate.channelName
30412
+ : undefined;
30413
+ const inboxCandidate = candidate && 'inboxCapacity' in candidate
30414
+ ? candidate.inboxCapacity
30415
+ : undefined;
30416
+ const initialWindowCandidate = candidate && 'initialWindow' in candidate
30417
+ ? candidate.initialWindow
30418
+ : undefined;
30419
+ const passiveCandidate = candidate && 'passive' in candidate
30420
+ ? candidate.passive
30421
+ : undefined;
30422
+ const targetCandidate = candidate && 'initialTargetNodeId' in candidate
30423
+ ? candidate.initialTargetNodeId
30424
+ : undefined;
30425
+ const channelName = typeof channelCandidate === 'string' && channelCandidate.trim().length > 0
30426
+ ? channelCandidate.trim()
30427
+ : this._channelName;
30428
+ const inboxCapacity = typeof inboxCandidate === 'number' &&
30429
+ Number.isFinite(inboxCandidate) &&
30430
+ inboxCandidate > 0
30431
+ ? Math.floor(inboxCandidate)
30432
+ : this._inboxCapacity;
30433
+ const initialWindow = typeof initialWindowCandidate === 'number' &&
30434
+ Number.isFinite(initialWindowCandidate) &&
30435
+ initialWindowCandidate > 0
30436
+ ? Math.floor(initialWindowCandidate)
30437
+ : undefined;
30438
+ const initialTargetNodeId = this._normalizeNodeId(targetCandidate) ?? targetSystemId;
30439
+ return {
30193
30440
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
30194
- channelName: this._channelName,
30195
- inboxCapacity: this._inboxCapacity,
30196
- passive: true,
30441
+ channelName,
30442
+ inboxCapacity,
30443
+ passive: typeof passiveCandidate === 'boolean' ? passiveCandidate : true,
30444
+ initialWindow,
30445
+ localNodeId,
30446
+ initialTargetNodeId,
30197
30447
  };
30198
- const channelCandidate = candidate.channelName ?? candidate['channel_name'];
30199
- if (typeof channelCandidate === 'string' && channelCandidate.trim().length > 0) {
30200
- config.channelName = channelCandidate.trim();
30448
+ }
30449
+ _requireLocalNodeId() {
30450
+ if (!this._routingNode) {
30451
+ throw new Error('BroadcastChannelListener requires routing node context');
30201
30452
  }
30202
- const inboxCandidate = candidate.inboxCapacity ?? candidate['inbox_capacity'];
30203
- if (typeof inboxCandidate === 'number' &&
30204
- Number.isFinite(inboxCandidate) &&
30205
- inboxCandidate > 0) {
30206
- config.inboxCapacity = Math.floor(inboxCandidate);
30453
+ const normalized = this._normalizeNodeId(this._routingNode.sid) ??
30454
+ this._normalizeNodeId(this._routingNode.id);
30455
+ if (!normalized) {
30456
+ throw new Error('BroadcastChannelListener requires routing node with a stable identifier');
30207
30457
  }
30208
- return config;
30458
+ return normalized;
30459
+ }
30460
+ _normalizeNodeId(value) {
30461
+ if (typeof value !== 'string') {
30462
+ return null;
30463
+ }
30464
+ const trimmed = value.trim();
30465
+ return trimmed.length > 0 ? trimmed : null;
30209
30466
  }
30210
30467
  _monitorConnectorLifecycle(senderId, systemId, connector) {
30211
30468
  const maybeClosable = connector;