@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
@@ -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.961
99
+ // Generated from package.json version: 0.3.5-test.963
100
100
  /**
101
101
  * The package version, injected at build time.
102
102
  * @internal
103
103
  */
104
- const VERSION = '0.3.5-test.961';
104
+ const VERSION = '0.3.5-test.963';
105
105
 
106
106
  /**
107
107
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -9944,6 +9944,26 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9944
9944
  }
9945
9945
  return null;
9946
9946
  }
9947
+ static normalizeNodeId(value) {
9948
+ if (typeof value !== 'string') {
9949
+ return null;
9950
+ }
9951
+ const trimmed = value.trim();
9952
+ return trimmed.length > 0 ? trimmed : null;
9953
+ }
9954
+ static normalizeTargetNodeId(value) {
9955
+ if (typeof value !== 'string') {
9956
+ return undefined;
9957
+ }
9958
+ const trimmed = value.trim();
9959
+ if (trimmed.length === 0) {
9960
+ return undefined;
9961
+ }
9962
+ if (trimmed === '*') {
9963
+ return '*';
9964
+ }
9965
+ return trimmed;
9966
+ }
9947
9967
  constructor(config, baseConfig = {}) {
9948
9968
  ensureBroadcastEnvironment();
9949
9969
  super(baseConfig);
@@ -9966,10 +9986,18 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9966
9986
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9967
9987
  this.inboxCapacity = preferredCapacity;
9968
9988
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9989
+ const normalizedLocalNodeId = BroadcastChannelConnector.normalizeNodeId(config.localNodeId);
9990
+ if (!normalizedLocalNodeId) {
9991
+ throw new Error('BroadcastChannelConnector requires a non-empty localNodeId');
9992
+ }
9993
+ this.localNodeId = normalizedLocalNodeId;
9994
+ this.targetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(config.initialTargetNodeId);
9969
9995
  this.channel = new BroadcastChannel(this.channelName);
9970
9996
  logger$_.debug('broadcast_channel_connector_created', {
9971
9997
  channel: this.channelName,
9972
9998
  connector_id: this.connectorId,
9999
+ local_node_id: this.localNodeId,
10000
+ target_node_id: this.targetNodeId ?? null,
9973
10001
  inbox_capacity: preferredCapacity,
9974
10002
  timestamp: new Date().toISOString(),
9975
10003
  });
@@ -9991,15 +10019,32 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9991
10019
  ? message.constructor?.name ?? typeof message
9992
10020
  : typeof message,
9993
10021
  has_sender_id: Boolean(message?.senderId),
10022
+ has_sender_node_id: Boolean(message?.senderNodeId),
9994
10023
  });
9995
10024
  if (!message || typeof message !== 'object') {
9996
10025
  return;
9997
10026
  }
9998
10027
  const busMessage = message;
9999
- if (typeof busMessage.senderId !== 'string' || busMessage.senderId.length === 0) {
10028
+ const senderNodeId = BroadcastChannelConnector.normalizeNodeId(busMessage.senderNodeId);
10029
+ if (!senderNodeId) {
10030
+ logger$_.debug('broadcast_channel_message_rejected', {
10031
+ channel: this.channelName,
10032
+ connector_id: this.connectorId,
10033
+ reason: 'missing_sender_node_id',
10034
+ });
10000
10035
  return;
10001
10036
  }
10002
- if (busMessage.senderId === this.connectorId) {
10037
+ if (senderNodeId === this.localNodeId) {
10038
+ logger$_.debug('broadcast_channel_message_rejected', {
10039
+ channel: this.channelName,
10040
+ connector_id: this.connectorId,
10041
+ reason: 'self_echo',
10042
+ sender_node_id: senderNodeId,
10043
+ });
10044
+ return;
10045
+ }
10046
+ const incomingTargetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(busMessage.targetNodeId);
10047
+ if (!this._shouldAcceptMessageFromBus(senderNodeId, incomingTargetNodeId)) {
10003
10048
  return;
10004
10049
  }
10005
10050
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
@@ -10013,11 +10058,13 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10013
10058
  }
10014
10059
  logger$_.debug('broadcast_channel_message_received', {
10015
10060
  channel: this.channelName,
10016
- sender_id: busMessage.senderId,
10061
+ sender_id: message?.senderId,
10062
+ sender_node_id: senderNodeId,
10063
+ target_node_id: incomingTargetNodeId ?? null,
10017
10064
  connector_id: this.connectorId,
10018
10065
  payload_length: payload.byteLength,
10019
10066
  });
10020
- if (this._shouldSkipDuplicateAck(busMessage.senderId, payload)) {
10067
+ if (this._shouldSkipDuplicateAck(senderNodeId, payload)) {
10021
10068
  return;
10022
10069
  }
10023
10070
  try {
@@ -10161,12 +10208,17 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10161
10208
  }
10162
10209
  async _transportSendBytes(data) {
10163
10210
  ensureBroadcastEnvironment();
10211
+ const targetNodeId = this.targetNodeId ?? '*';
10164
10212
  logger$_.debug('broadcast_channel_message_sending', {
10165
10213
  channel: this.channelName,
10166
10214
  sender_id: this.connectorId,
10215
+ sender_node_id: this.localNodeId,
10216
+ target_node_id: targetNodeId,
10167
10217
  });
10168
10218
  this.channel.postMessage({
10169
10219
  senderId: this.connectorId,
10220
+ senderNodeId: this.localNodeId,
10221
+ targetNodeId,
10170
10222
  payload: data,
10171
10223
  });
10172
10224
  }
@@ -10229,6 +10281,51 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10229
10281
  }
10230
10282
  return rawOrEnvelope;
10231
10283
  }
10284
+ _isWildcardTarget() {
10285
+ return this.targetNodeId === '*' || typeof this.targetNodeId === 'undefined';
10286
+ }
10287
+ _shouldAcceptMessageFromBus(senderNodeId, targetNodeId) {
10288
+ if (this._isWildcardTarget()) {
10289
+ if (targetNodeId && targetNodeId !== '*') {
10290
+ logger$_.debug('broadcast_channel_message_rejected', {
10291
+ channel: this.channelName,
10292
+ connector_id: this.connectorId,
10293
+ reason: 'wildcard_target_mismatch',
10294
+ sender_node_id: senderNodeId,
10295
+ target_node_id: targetNodeId,
10296
+ local_node_id: this.localNodeId,
10297
+ });
10298
+ return false;
10299
+ }
10300
+ return true;
10301
+ }
10302
+ const expectedSender = this.targetNodeId;
10303
+ if (expectedSender && expectedSender !== '*' && senderNodeId !== expectedSender) {
10304
+ logger$_.debug('broadcast_channel_message_rejected', {
10305
+ channel: this.channelName,
10306
+ connector_id: this.connectorId,
10307
+ reason: 'unexpected_sender',
10308
+ expected_sender_node_id: expectedSender,
10309
+ sender_node_id: senderNodeId,
10310
+ local_node_id: this.localNodeId,
10311
+ });
10312
+ return false;
10313
+ }
10314
+ if (targetNodeId &&
10315
+ targetNodeId !== '*' &&
10316
+ targetNodeId !== this.localNodeId) {
10317
+ logger$_.debug('broadcast_channel_message_rejected', {
10318
+ channel: this.channelName,
10319
+ connector_id: this.connectorId,
10320
+ reason: 'unexpected_target',
10321
+ sender_node_id: senderNodeId,
10322
+ target_node_id: targetNodeId,
10323
+ local_node_id: this.localNodeId,
10324
+ });
10325
+ return false;
10326
+ }
10327
+ return true;
10328
+ }
10232
10329
  _describeInboxItem(item) {
10233
10330
  if (item instanceof Uint8Array) {
10234
10331
  return 'bytes';
@@ -10259,7 +10356,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10259
10356
  const normalizedSenderId = typeof senderId === 'string' && senderId.length > 0
10260
10357
  ? senderId
10261
10358
  : undefined;
10262
- if (normalizedSenderId && normalizedSenderId !== this.connectorId) {
10359
+ if (normalizedSenderId && normalizedSenderId !== this.localNodeId) {
10263
10360
  logger$_.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
10264
10361
  channel: this.channelName,
10265
10362
  connector_id: this.connectorId,
@@ -10299,7 +10396,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10299
10396
  return false;
10300
10397
  }
10301
10398
  const senderId = this._extractSenderIdFromInboxItem(item);
10302
- if (senderId && senderId !== this.connectorId) {
10399
+ if (senderId && senderId !== this.localNodeId) {
10303
10400
  logger$_.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
10304
10401
  channel: this.channelName,
10305
10402
  connector_id: this.connectorId,
@@ -10395,6 +10492,34 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10395
10492
  });
10396
10493
  }
10397
10494
  }
10495
+ setTargetNodeId(nodeId) {
10496
+ const normalized = BroadcastChannelConnector.normalizeNodeId(nodeId);
10497
+ if (!normalized) {
10498
+ throw new Error('BroadcastChannelConnector target node id must be a non-empty string');
10499
+ }
10500
+ if (normalized === '*') {
10501
+ this.setWildcardTarget();
10502
+ return;
10503
+ }
10504
+ this.targetNodeId = normalized;
10505
+ logger$_.debug('broadcast_channel_target_updated', {
10506
+ channel: this.channelName,
10507
+ connector_id: this.connectorId,
10508
+ local_node_id: this.localNodeId,
10509
+ target_node_id: this.targetNodeId,
10510
+ target_mode: 'direct',
10511
+ });
10512
+ }
10513
+ setWildcardTarget() {
10514
+ this.targetNodeId = '*';
10515
+ logger$_.debug('broadcast_channel_target_updated', {
10516
+ channel: this.channelName,
10517
+ connector_id: this.connectorId,
10518
+ local_node_id: this.localNodeId,
10519
+ target_node_id: this.targetNodeId,
10520
+ target_mode: 'wildcard',
10521
+ });
10522
+ }
10398
10523
  _trimSeenAcks(now) {
10399
10524
  while (this.seenAckOrder.length > 0) {
10400
10525
  const candidate = this.seenAckOrder[0];
@@ -10866,6 +10991,20 @@ class UpstreamSessionManager extends TaskSpawner {
10866
10991
  waitEvent(event, signal) {
10867
10992
  return signal ? event.wait({ signal }) : event.wait();
10868
10993
  }
10994
+ _getLocalNodeId() {
10995
+ const normalized = this._normalizeNodeId(this.node.id);
10996
+ if (!normalized) {
10997
+ throw new Error('UpstreamSessionManager requires node with a stable identifier');
10998
+ }
10999
+ return normalized;
11000
+ }
11001
+ _normalizeNodeId(value) {
11002
+ if (typeof value !== 'string') {
11003
+ return null;
11004
+ }
11005
+ const trimmed = value.trim();
11006
+ return trimmed.length > 0 ? trimmed : null;
11007
+ }
10869
11008
  async connectCycle() {
10870
11009
  if (!this.admissionClient) {
10871
11010
  throw new FameConnectError('Admission client is required to attach upstream');
@@ -10887,6 +11026,8 @@ class UpstreamSessionManager extends TaskSpawner {
10887
11026
  await this.onWelcome(welcome.frame);
10888
11027
  const connector = await ConnectorFactory.createConnector(grant, {
10889
11028
  systemId: welcome.frame.systemId,
11029
+ localNodeId: this._getLocalNodeId(),
11030
+ initialTargetNodeId: '*',
10890
11031
  });
10891
11032
  await connector.start(this.wrappedHandler);
10892
11033
  this.connector = connector;
@@ -10912,6 +11053,20 @@ class UpstreamSessionManager extends TaskSpawner {
10912
11053
  }
10913
11054
  const attachInfo = await this.attachClient.attach(this.node, this.outboundOriginType, connector, welcome.frame, this.wrappedHandler, this.getKeys() ?? undefined, callbackGrants);
10914
11055
  this.targetSystemId = attachInfo.targetSystemId ?? null;
11056
+ if (this.targetSystemId) {
11057
+ const targetAware = connector;
11058
+ if (typeof targetAware.setTargetNodeId === 'function') {
11059
+ try {
11060
+ targetAware.setTargetNodeId(this.targetSystemId);
11061
+ }
11062
+ catch (error) {
11063
+ logger$Z.warning('broadcast_channel_target_apply_failed', {
11064
+ error: error instanceof Error ? error.message : String(error),
11065
+ target_node_id: this.targetSystemId,
11066
+ });
11067
+ }
11068
+ }
11069
+ }
10915
11070
  await this.onAttach(attachInfo, connector);
10916
11071
  // Close the admission client immediately after attach completes
10917
11072
  // This releases HTTP keep-alive connections (Node.js fetch/undici requires explicit cleanup)
@@ -28931,8 +29086,16 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28931
29086
  }
28932
29087
  const normalized = this._normalizeConfig(config);
28933
29088
  const options = (factoryArgs[0] ?? {});
29089
+ const normalizedLocalNodeFromConfig = this._normalizeNodeId(normalized.localNodeId);
29090
+ const localNodeId = this._normalizeNodeId(options.localNodeId) ?? normalizedLocalNodeFromConfig;
29091
+ if (!localNodeId) {
29092
+ throw new Error('BroadcastChannelConnectorFactory requires a localNodeId from config or create() options');
29093
+ }
28934
29094
  const channelName = normalized.channelName ?? DEFAULT_CHANNEL$4;
28935
29095
  const inboxCapacity = normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$4;
29096
+ const targetFromOptions = this._normalizeTargetNodeId(options.initialTargetNodeId);
29097
+ const targetFromConfig = this._normalizeTargetNodeId(normalized.initialTargetNodeId);
29098
+ const resolvedTarget = targetFromOptions ?? targetFromConfig ?? '*';
28936
29099
  const baseConfig = {
28937
29100
  drainTimeout: normalized.drainTimeout,
28938
29101
  flowControl: normalized.flowControl,
@@ -28947,6 +29110,8 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28947
29110
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
28948
29111
  channelName,
28949
29112
  inboxCapacity,
29113
+ localNodeId,
29114
+ initialTargetNodeId: resolvedTarget,
28950
29115
  };
28951
29116
  const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
28952
29117
  if (options.authorization) {
@@ -28970,11 +29135,21 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
28970
29135
  normalized.channelName = channel.trim();
28971
29136
  }
28972
29137
  const capacity = candidate.inboxCapacity ?? candidate['inbox_capacity'];
29138
+ const initialTargetNodeId = candidate.initialTargetNodeId ?? candidate['initial_target_node_id'];
29139
+ const normalizedTarget = this._normalizeTargetNodeId(initialTargetNodeId);
29140
+ if (normalizedTarget) {
29141
+ normalized.initialTargetNodeId = normalizedTarget;
29142
+ }
28973
29143
  if (typeof capacity === 'number' &&
28974
29144
  Number.isFinite(capacity) &&
28975
29145
  capacity > 0) {
28976
29146
  normalized.inboxCapacity = Math.floor(capacity);
28977
29147
  }
29148
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
29149
+ const normalizedLocalNodeId = this._normalizeNodeId(localNodeId);
29150
+ if (normalizedLocalNodeId) {
29151
+ normalized.localNodeId = normalizedLocalNodeId;
29152
+ }
28978
29153
  if (typeof candidate.flowControl === 'boolean') {
28979
29154
  normalized.flowControl = candidate.flowControl;
28980
29155
  }
@@ -29013,6 +29188,22 @@ class BroadcastChannelConnectorFactory extends ConnectorFactory {
29013
29188
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY$4;
29014
29189
  return normalized;
29015
29190
  }
29191
+ _normalizeNodeId(value) {
29192
+ if (typeof value !== 'string') {
29193
+ return null;
29194
+ }
29195
+ const trimmed = value.trim();
29196
+ return trimmed.length > 0 ? trimmed : null;
29197
+ }
29198
+ _normalizeTargetNodeId(value) {
29199
+ if (value === undefined || value === null) {
29200
+ return undefined;
29201
+ }
29202
+ if (value === '*') {
29203
+ return '*';
29204
+ }
29205
+ return this._normalizeNodeId(value) ?? undefined;
29206
+ }
29016
29207
  }
29017
29208
 
29018
29209
  var broadcastChannelConnectorFactory = /*#__PURE__*/Object.freeze({
@@ -30116,7 +30307,7 @@ class BroadcastChannelListener extends TransportListener {
30116
30307
  node: routingNode,
30117
30308
  });
30118
30309
  const selection = defaultGrantSelectionPolicy.selectCallbackGrant(selectionContext);
30119
- connectorConfig = this._grantToConnectorConfig(selection.grant);
30310
+ connectorConfig = this._grantToConnectorConfig(selection.grant, systemId);
30120
30311
  }
30121
30312
  catch (error) {
30122
30313
  logger$o.debug('broadcast_channel_listener_grant_selection_failed', {
@@ -30125,13 +30316,20 @@ class BroadcastChannelListener extends TransportListener {
30125
30316
  error: error instanceof Error ? error.message : String(error),
30126
30317
  });
30127
30318
  connectorConfig =
30128
- this._extractBroadcastConnectorConfig(frame) ??
30129
- {
30319
+ this._extractBroadcastConnectorConfig(frame, systemId) ??
30320
+ this._buildConnectorConfigForSystem(systemId, {
30130
30321
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
30131
30322
  channelName: this._channelName,
30132
30323
  inboxCapacity: this._inboxCapacity,
30133
30324
  passive: true,
30134
- };
30325
+ });
30326
+ }
30327
+ if (!connectorConfig) {
30328
+ logger$o.error('broadcast_channel_listener_missing_connector_config', {
30329
+ sender_id: params.senderId,
30330
+ system_id: systemId,
30331
+ });
30332
+ return null;
30135
30333
  }
30136
30334
  try {
30137
30335
  const connector = await routingNode.createOriginConnector({
@@ -30157,7 +30355,7 @@ class BroadcastChannelListener extends TransportListener {
30157
30355
  return null;
30158
30356
  }
30159
30357
  }
30160
- _extractBroadcastConnectorConfig(frame) {
30358
+ _extractBroadcastConnectorConfig(frame, systemId) {
30161
30359
  const rawGrants = frame.callbackGrants;
30162
30360
  if (!Array.isArray(rawGrants)) {
30163
30361
  return null;
@@ -30168,7 +30366,10 @@ class BroadcastChannelListener extends TransportListener {
30168
30366
  (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE ||
30169
30367
  grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE)) {
30170
30368
  try {
30171
- return this._grantToConnectorConfig(grant);
30369
+ if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30370
+ return this._buildConnectorConfigForSystem(systemId, grant);
30371
+ }
30372
+ return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
30172
30373
  }
30173
30374
  catch (error) {
30174
30375
  logger$o.debug('broadcast_channel_listener_grant_normalization_failed', {
@@ -30179,31 +30380,87 @@ class BroadcastChannelListener extends TransportListener {
30179
30380
  }
30180
30381
  return null;
30181
30382
  }
30182
- _grantToConnectorConfig(grant) {
30183
- if (grant.type !== BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30184
- if (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
30185
- return broadcastChannelGrantToConnectorConfig(grant);
30383
+ _grantToConnectorConfig(grant, systemId) {
30384
+ if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30385
+ return this._buildConnectorConfigForSystem(systemId, grant);
30386
+ }
30387
+ if (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
30388
+ return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
30389
+ }
30390
+ if ('toConnectorConfig' in grant &&
30391
+ typeof grant.toConnectorConfig ===
30392
+ 'function') {
30393
+ const normalized = grant.toConnectorConfig();
30394
+ if (normalized.type !== BROADCAST_CHANNEL_CONNECTOR_TYPE) {
30395
+ throw new Error(`Unsupported grant connector type: ${normalized.type}`);
30186
30396
  }
30187
- throw new Error(`Unsupported grant type: ${grant.type}`);
30397
+ return this._buildConnectorConfigForSystem(systemId, normalized);
30188
30398
  }
30189
- const candidate = grant;
30190
- const config = {
30399
+ throw new Error(`Unsupported grant type: ${grant.type}`);
30400
+ }
30401
+ _buildConnectorConfigForSystem(systemId, baseConfig) {
30402
+ const localNodeId = this._requireLocalNodeId();
30403
+ const targetSystemId = this._normalizeNodeId(systemId);
30404
+ if (!targetSystemId) {
30405
+ throw new Error('BroadcastChannelListener requires a valid system id');
30406
+ }
30407
+ const candidate = baseConfig ?? null;
30408
+ const channelCandidate = candidate && 'channelName' in candidate
30409
+ ? candidate.channelName
30410
+ : undefined;
30411
+ const inboxCandidate = candidate && 'inboxCapacity' in candidate
30412
+ ? candidate.inboxCapacity
30413
+ : undefined;
30414
+ const initialWindowCandidate = candidate && 'initialWindow' in candidate
30415
+ ? candidate.initialWindow
30416
+ : undefined;
30417
+ const passiveCandidate = candidate && 'passive' in candidate
30418
+ ? candidate.passive
30419
+ : undefined;
30420
+ const targetCandidate = candidate && 'initialTargetNodeId' in candidate
30421
+ ? candidate.initialTargetNodeId
30422
+ : undefined;
30423
+ const channelName = typeof channelCandidate === 'string' && channelCandidate.trim().length > 0
30424
+ ? channelCandidate.trim()
30425
+ : this._channelName;
30426
+ const inboxCapacity = typeof inboxCandidate === 'number' &&
30427
+ Number.isFinite(inboxCandidate) &&
30428
+ inboxCandidate > 0
30429
+ ? Math.floor(inboxCandidate)
30430
+ : this._inboxCapacity;
30431
+ const initialWindow = typeof initialWindowCandidate === 'number' &&
30432
+ Number.isFinite(initialWindowCandidate) &&
30433
+ initialWindowCandidate > 0
30434
+ ? Math.floor(initialWindowCandidate)
30435
+ : undefined;
30436
+ const initialTargetNodeId = this._normalizeNodeId(targetCandidate) ?? targetSystemId;
30437
+ return {
30191
30438
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
30192
- channelName: this._channelName,
30193
- inboxCapacity: this._inboxCapacity,
30194
- passive: true,
30439
+ channelName,
30440
+ inboxCapacity,
30441
+ passive: typeof passiveCandidate === 'boolean' ? passiveCandidate : true,
30442
+ initialWindow,
30443
+ localNodeId,
30444
+ initialTargetNodeId,
30195
30445
  };
30196
- const channelCandidate = candidate.channelName ?? candidate['channel_name'];
30197
- if (typeof channelCandidate === 'string' && channelCandidate.trim().length > 0) {
30198
- config.channelName = channelCandidate.trim();
30446
+ }
30447
+ _requireLocalNodeId() {
30448
+ if (!this._routingNode) {
30449
+ throw new Error('BroadcastChannelListener requires routing node context');
30199
30450
  }
30200
- const inboxCandidate = candidate.inboxCapacity ?? candidate['inbox_capacity'];
30201
- if (typeof inboxCandidate === 'number' &&
30202
- Number.isFinite(inboxCandidate) &&
30203
- inboxCandidate > 0) {
30204
- config.inboxCapacity = Math.floor(inboxCandidate);
30451
+ const normalized = this._normalizeNodeId(this._routingNode.sid) ??
30452
+ this._normalizeNodeId(this._routingNode.id);
30453
+ if (!normalized) {
30454
+ throw new Error('BroadcastChannelListener requires routing node with a stable identifier');
30205
30455
  }
30206
- return config;
30456
+ return normalized;
30457
+ }
30458
+ _normalizeNodeId(value) {
30459
+ if (typeof value !== 'string') {
30460
+ return null;
30461
+ }
30462
+ const trimmed = value.trim();
30463
+ return trimmed.length > 0 ? trimmed : null;
30207
30464
  }
30208
30465
  _monitorConnectorLifecycle(senderId, systemId, connector) {
30209
30466
  const maybeClosable = connector;
@@ -75,8 +75,16 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
75
75
  }
76
76
  const normalized = this._normalizeConfig(config);
77
77
  const options = (factoryArgs[0] ?? {});
78
+ const normalizedLocalNodeFromConfig = this._normalizeNodeId(normalized.localNodeId);
79
+ const localNodeId = this._normalizeNodeId(options.localNodeId) ?? normalizedLocalNodeFromConfig;
80
+ if (!localNodeId) {
81
+ throw new Error('BroadcastChannelConnectorFactory requires a localNodeId from config or create() options');
82
+ }
78
83
  const channelName = normalized.channelName ?? DEFAULT_CHANNEL;
79
84
  const inboxCapacity = normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
85
+ const targetFromOptions = this._normalizeTargetNodeId(options.initialTargetNodeId);
86
+ const targetFromConfig = this._normalizeTargetNodeId(normalized.initialTargetNodeId);
87
+ const resolvedTarget = targetFromOptions ?? targetFromConfig ?? '*';
80
88
  const baseConfig = {
81
89
  drainTimeout: normalized.drainTimeout,
82
90
  flowControl: normalized.flowControl,
@@ -91,6 +99,8 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
91
99
  type: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
92
100
  channelName,
93
101
  inboxCapacity,
102
+ localNodeId,
103
+ initialTargetNodeId: resolvedTarget,
94
104
  };
95
105
  const connector = new broadcast_channel_connector_js_1.BroadcastChannelConnector(connectorConfig, baseConfig);
96
106
  if (options.authorization) {
@@ -114,11 +124,21 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
114
124
  normalized.channelName = channel.trim();
115
125
  }
116
126
  const capacity = candidate.inboxCapacity ?? candidate['inbox_capacity'];
127
+ const initialTargetNodeId = candidate.initialTargetNodeId ?? candidate['initial_target_node_id'];
128
+ const normalizedTarget = this._normalizeTargetNodeId(initialTargetNodeId);
129
+ if (normalizedTarget) {
130
+ normalized.initialTargetNodeId = normalizedTarget;
131
+ }
117
132
  if (typeof capacity === 'number' &&
118
133
  Number.isFinite(capacity) &&
119
134
  capacity > 0) {
120
135
  normalized.inboxCapacity = Math.floor(capacity);
121
136
  }
137
+ const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
138
+ const normalizedLocalNodeId = this._normalizeNodeId(localNodeId);
139
+ if (normalizedLocalNodeId) {
140
+ normalized.localNodeId = normalizedLocalNodeId;
141
+ }
122
142
  if (typeof candidate.flowControl === 'boolean') {
123
143
  normalized.flowControl = candidate.flowControl;
124
144
  }
@@ -157,6 +177,22 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
157
177
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
158
178
  return normalized;
159
179
  }
180
+ _normalizeNodeId(value) {
181
+ if (typeof value !== 'string') {
182
+ return null;
183
+ }
184
+ const trimmed = value.trim();
185
+ return trimmed.length > 0 ? trimmed : null;
186
+ }
187
+ _normalizeTargetNodeId(value) {
188
+ if (value === undefined || value === null) {
189
+ return undefined;
190
+ }
191
+ if (value === '*') {
192
+ return '*';
193
+ }
194
+ return this._normalizeNodeId(value) ?? undefined;
195
+ }
160
196
  }
161
197
  exports.BroadcastChannelConnectorFactory = BroadcastChannelConnectorFactory;
162
198
  exports.default = BroadcastChannelConnectorFactory;