@naylence/runtime 0.3.5-test.960 → 0.3.5-test.962

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.
@@ -49,6 +49,26 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
49
49
  }
50
50
  return null;
51
51
  }
52
+ static normalizeNodeId(value) {
53
+ if (typeof value !== 'string') {
54
+ return null;
55
+ }
56
+ const trimmed = value.trim();
57
+ return trimmed.length > 0 ? trimmed : null;
58
+ }
59
+ static normalizeTargetNodeId(value) {
60
+ if (typeof value !== 'string') {
61
+ return undefined;
62
+ }
63
+ const trimmed = value.trim();
64
+ if (trimmed.length === 0) {
65
+ return undefined;
66
+ }
67
+ if (trimmed === '*') {
68
+ return '*';
69
+ }
70
+ return trimmed;
71
+ }
52
72
  constructor(config, baseConfig = {}) {
53
73
  ensureBroadcastEnvironment();
54
74
  super(baseConfig);
@@ -71,10 +91,18 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
71
91
  this.inbox = new bounded_async_queue_js_1.BoundedAsyncQueue(preferredCapacity);
72
92
  this.inboxCapacity = preferredCapacity;
73
93
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
94
+ const normalizedLocalNodeId = BroadcastChannelConnector.normalizeNodeId(config.localNodeId);
95
+ if (!normalizedLocalNodeId) {
96
+ throw new Error('BroadcastChannelConnector requires a non-empty localNodeId');
97
+ }
98
+ this.localNodeId = normalizedLocalNodeId;
99
+ this.targetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(config.initialTargetNodeId);
74
100
  this.channel = new BroadcastChannel(this.channelName);
75
101
  logger.debug('broadcast_channel_connector_created', {
76
102
  channel: this.channelName,
77
103
  connector_id: this.connectorId,
104
+ local_node_id: this.localNodeId,
105
+ target_node_id: this.targetNodeId ?? null,
78
106
  inbox_capacity: preferredCapacity,
79
107
  timestamp: new Date().toISOString(),
80
108
  });
@@ -96,15 +124,32 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
96
124
  ? message.constructor?.name ?? typeof message
97
125
  : typeof message,
98
126
  has_sender_id: Boolean(message?.senderId),
127
+ has_sender_node_id: Boolean(message?.senderNodeId),
99
128
  });
100
129
  if (!message || typeof message !== 'object') {
101
130
  return;
102
131
  }
103
132
  const busMessage = message;
104
- if (typeof busMessage.senderId !== 'string' || busMessage.senderId.length === 0) {
133
+ const senderNodeId = BroadcastChannelConnector.normalizeNodeId(busMessage.senderNodeId);
134
+ if (!senderNodeId) {
135
+ logger.debug('broadcast_channel_message_rejected', {
136
+ channel: this.channelName,
137
+ connector_id: this.connectorId,
138
+ reason: 'missing_sender_node_id',
139
+ });
140
+ return;
141
+ }
142
+ if (senderNodeId === this.localNodeId) {
143
+ logger.debug('broadcast_channel_message_rejected', {
144
+ channel: this.channelName,
145
+ connector_id: this.connectorId,
146
+ reason: 'self_echo',
147
+ sender_node_id: senderNodeId,
148
+ });
105
149
  return;
106
150
  }
107
- if (busMessage.senderId === this.connectorId) {
151
+ const incomingTargetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(busMessage.targetNodeId);
152
+ if (!this._shouldAcceptMessageFromBus(senderNodeId, incomingTargetNodeId)) {
108
153
  return;
109
154
  }
110
155
  const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
@@ -118,11 +163,13 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
118
163
  }
119
164
  logger.debug('broadcast_channel_message_received', {
120
165
  channel: this.channelName,
121
- sender_id: busMessage.senderId,
166
+ sender_id: message?.senderId,
167
+ sender_node_id: senderNodeId,
168
+ target_node_id: incomingTargetNodeId ?? null,
122
169
  connector_id: this.connectorId,
123
170
  payload_length: payload.byteLength,
124
171
  });
125
- if (this._shouldSkipDuplicateAck(busMessage.senderId, payload)) {
172
+ if (this._shouldSkipDuplicateAck(senderNodeId, payload)) {
126
173
  return;
127
174
  }
128
175
  try {
@@ -266,12 +313,17 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
266
313
  }
267
314
  async _transportSendBytes(data) {
268
315
  ensureBroadcastEnvironment();
316
+ const targetNodeId = this.targetNodeId ?? '*';
269
317
  logger.debug('broadcast_channel_message_sending', {
270
318
  channel: this.channelName,
271
319
  sender_id: this.connectorId,
320
+ sender_node_id: this.localNodeId,
321
+ target_node_id: targetNodeId,
272
322
  });
273
323
  this.channel.postMessage({
274
324
  senderId: this.connectorId,
325
+ senderNodeId: this.localNodeId,
326
+ targetNodeId,
275
327
  payload: data,
276
328
  });
277
329
  }
@@ -334,6 +386,51 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
334
386
  }
335
387
  return rawOrEnvelope;
336
388
  }
389
+ _isWildcardTarget() {
390
+ return this.targetNodeId === '*' || typeof this.targetNodeId === 'undefined';
391
+ }
392
+ _shouldAcceptMessageFromBus(senderNodeId, targetNodeId) {
393
+ if (this._isWildcardTarget()) {
394
+ if (targetNodeId && targetNodeId !== '*') {
395
+ logger.debug('broadcast_channel_message_rejected', {
396
+ channel: this.channelName,
397
+ connector_id: this.connectorId,
398
+ reason: 'wildcard_target_mismatch',
399
+ sender_node_id: senderNodeId,
400
+ target_node_id: targetNodeId,
401
+ local_node_id: this.localNodeId,
402
+ });
403
+ return false;
404
+ }
405
+ return true;
406
+ }
407
+ const expectedSender = this.targetNodeId;
408
+ if (expectedSender && expectedSender !== '*' && senderNodeId !== expectedSender) {
409
+ logger.debug('broadcast_channel_message_rejected', {
410
+ channel: this.channelName,
411
+ connector_id: this.connectorId,
412
+ reason: 'unexpected_sender',
413
+ expected_sender_node_id: expectedSender,
414
+ sender_node_id: senderNodeId,
415
+ local_node_id: this.localNodeId,
416
+ });
417
+ return false;
418
+ }
419
+ if (targetNodeId &&
420
+ targetNodeId !== '*' &&
421
+ targetNodeId !== this.localNodeId) {
422
+ logger.debug('broadcast_channel_message_rejected', {
423
+ channel: this.channelName,
424
+ connector_id: this.connectorId,
425
+ reason: 'unexpected_target',
426
+ sender_node_id: senderNodeId,
427
+ target_node_id: targetNodeId,
428
+ local_node_id: this.localNodeId,
429
+ });
430
+ return false;
431
+ }
432
+ return true;
433
+ }
337
434
  _describeInboxItem(item) {
338
435
  if (item instanceof Uint8Array) {
339
436
  return 'bytes';
@@ -364,6 +461,16 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
364
461
  const normalizedSenderId = typeof senderId === 'string' && senderId.length > 0
365
462
  ? senderId
366
463
  : undefined;
464
+ if (normalizedSenderId && normalizedSenderId !== this.localNodeId) {
465
+ logger.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
466
+ channel: this.channelName,
467
+ connector_id: this.connectorId,
468
+ sender_id: normalizedSenderId,
469
+ dedup_key: dedupKey,
470
+ source: 'listener',
471
+ });
472
+ return false;
473
+ }
367
474
  logger.debug('broadcast_channel_duplicate_ack_check', {
368
475
  channel: this.channelName,
369
476
  connector_id: this.connectorId,
@@ -394,6 +501,16 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
394
501
  return false;
395
502
  }
396
503
  const senderId = this._extractSenderIdFromInboxItem(item);
504
+ if (senderId && senderId !== this.localNodeId) {
505
+ logger.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
506
+ channel: this.channelName,
507
+ connector_id: this.connectorId,
508
+ sender_id: senderId,
509
+ dedup_key: dedupKey,
510
+ source: 'inbox_item',
511
+ });
512
+ return false;
513
+ }
397
514
  logger.debug('broadcast_channel_duplicate_ack_check', {
398
515
  channel: this.channelName,
399
516
  connector_id: this.connectorId,
@@ -480,6 +597,34 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
480
597
  });
481
598
  }
482
599
  }
600
+ setTargetNodeId(nodeId) {
601
+ const normalized = BroadcastChannelConnector.normalizeNodeId(nodeId);
602
+ if (!normalized) {
603
+ throw new Error('BroadcastChannelConnector target node id must be a non-empty string');
604
+ }
605
+ if (normalized === '*') {
606
+ this.setWildcardTarget();
607
+ return;
608
+ }
609
+ this.targetNodeId = normalized;
610
+ logger.debug('broadcast_channel_target_updated', {
611
+ channel: this.channelName,
612
+ connector_id: this.connectorId,
613
+ local_node_id: this.localNodeId,
614
+ target_node_id: this.targetNodeId,
615
+ target_mode: 'direct',
616
+ });
617
+ }
618
+ setWildcardTarget() {
619
+ this.targetNodeId = '*';
620
+ logger.debug('broadcast_channel_target_updated', {
621
+ channel: this.channelName,
622
+ connector_id: this.connectorId,
623
+ local_node_id: this.localNodeId,
624
+ target_node_id: this.targetNodeId,
625
+ target_mode: 'wildcard',
626
+ });
627
+ }
483
628
  _trimSeenAcks(now) {
484
629
  while (this.seenAckOrder.length > 0) {
485
630
  const candidate = this.seenAckOrder[0];
@@ -328,7 +328,7 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
328
328
  node: routingNode,
329
329
  });
330
330
  const selection = grant_selection_policy_js_1.defaultGrantSelectionPolicy.selectCallbackGrant(selectionContext);
331
- connectorConfig = this._grantToConnectorConfig(selection.grant);
331
+ connectorConfig = this._grantToConnectorConfig(selection.grant, systemId);
332
332
  }
333
333
  catch (error) {
334
334
  logger.debug('broadcast_channel_listener_grant_selection_failed', {
@@ -337,13 +337,20 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
337
337
  error: error instanceof Error ? error.message : String(error),
338
338
  });
339
339
  connectorConfig =
340
- this._extractBroadcastConnectorConfig(frame) ??
341
- {
340
+ this._extractBroadcastConnectorConfig(frame, systemId) ??
341
+ this._buildConnectorConfigForSystem(systemId, {
342
342
  type: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
343
343
  channelName: this._channelName,
344
344
  inboxCapacity: this._inboxCapacity,
345
345
  passive: true,
346
- };
346
+ });
347
+ }
348
+ if (!connectorConfig) {
349
+ logger.error('broadcast_channel_listener_missing_connector_config', {
350
+ sender_id: params.senderId,
351
+ system_id: systemId,
352
+ });
353
+ return null;
347
354
  }
348
355
  try {
349
356
  const connector = await routingNode.createOriginConnector({
@@ -369,7 +376,7 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
369
376
  return null;
370
377
  }
371
378
  }
372
- _extractBroadcastConnectorConfig(frame) {
379
+ _extractBroadcastConnectorConfig(frame, systemId) {
373
380
  const rawGrants = frame.callbackGrants;
374
381
  if (!Array.isArray(rawGrants)) {
375
382
  return null;
@@ -380,7 +387,10 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
380
387
  (grant.type === broadcast_channel_connection_grant_js_1.BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE ||
381
388
  grant.type === broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE)) {
382
389
  try {
383
- return this._grantToConnectorConfig(grant);
390
+ if (grant.type === broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE) {
391
+ return this._buildConnectorConfigForSystem(systemId, grant);
392
+ }
393
+ return this._buildConnectorConfigForSystem(systemId, (0, broadcast_channel_connection_grant_js_1.broadcastChannelGrantToConnectorConfig)(grant));
384
394
  }
385
395
  catch (error) {
386
396
  logger.debug('broadcast_channel_listener_grant_normalization_failed', {
@@ -391,31 +401,87 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
391
401
  }
392
402
  return null;
393
403
  }
394
- _grantToConnectorConfig(grant) {
395
- if (grant.type !== broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE) {
396
- if (grant.type === broadcast_channel_connection_grant_js_1.BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
397
- return (0, broadcast_channel_connection_grant_js_1.broadcastChannelGrantToConnectorConfig)(grant);
404
+ _grantToConnectorConfig(grant, systemId) {
405
+ if (grant.type === broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE) {
406
+ return this._buildConnectorConfigForSystem(systemId, grant);
407
+ }
408
+ if (grant.type === broadcast_channel_connection_grant_js_1.BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
409
+ return this._buildConnectorConfigForSystem(systemId, (0, broadcast_channel_connection_grant_js_1.broadcastChannelGrantToConnectorConfig)(grant));
410
+ }
411
+ if ('toConnectorConfig' in grant &&
412
+ typeof grant.toConnectorConfig ===
413
+ 'function') {
414
+ const normalized = grant.toConnectorConfig();
415
+ if (normalized.type !== broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE) {
416
+ throw new Error(`Unsupported grant connector type: ${normalized.type}`);
398
417
  }
399
- throw new Error(`Unsupported grant type: ${grant.type}`);
418
+ return this._buildConnectorConfigForSystem(systemId, normalized);
400
419
  }
401
- const candidate = grant;
402
- const config = {
420
+ throw new Error(`Unsupported grant type: ${grant.type}`);
421
+ }
422
+ _buildConnectorConfigForSystem(systemId, baseConfig) {
423
+ const localNodeId = this._requireLocalNodeId();
424
+ const targetSystemId = this._normalizeNodeId(systemId);
425
+ if (!targetSystemId) {
426
+ throw new Error('BroadcastChannelListener requires a valid system id');
427
+ }
428
+ const candidate = baseConfig ?? null;
429
+ const channelCandidate = candidate && 'channelName' in candidate
430
+ ? candidate.channelName
431
+ : undefined;
432
+ const inboxCandidate = candidate && 'inboxCapacity' in candidate
433
+ ? candidate.inboxCapacity
434
+ : undefined;
435
+ const initialWindowCandidate = candidate && 'initialWindow' in candidate
436
+ ? candidate.initialWindow
437
+ : undefined;
438
+ const passiveCandidate = candidate && 'passive' in candidate
439
+ ? candidate.passive
440
+ : undefined;
441
+ const targetCandidate = candidate && 'initialTargetNodeId' in candidate
442
+ ? candidate.initialTargetNodeId
443
+ : undefined;
444
+ const channelName = typeof channelCandidate === 'string' && channelCandidate.trim().length > 0
445
+ ? channelCandidate.trim()
446
+ : this._channelName;
447
+ const inboxCapacity = typeof inboxCandidate === 'number' &&
448
+ Number.isFinite(inboxCandidate) &&
449
+ inboxCandidate > 0
450
+ ? Math.floor(inboxCandidate)
451
+ : this._inboxCapacity;
452
+ const initialWindow = typeof initialWindowCandidate === 'number' &&
453
+ Number.isFinite(initialWindowCandidate) &&
454
+ initialWindowCandidate > 0
455
+ ? Math.floor(initialWindowCandidate)
456
+ : undefined;
457
+ const initialTargetNodeId = this._normalizeNodeId(targetCandidate) ?? targetSystemId;
458
+ return {
403
459
  type: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
404
- channelName: this._channelName,
405
- inboxCapacity: this._inboxCapacity,
406
- passive: true,
460
+ channelName,
461
+ inboxCapacity,
462
+ passive: typeof passiveCandidate === 'boolean' ? passiveCandidate : true,
463
+ initialWindow,
464
+ localNodeId,
465
+ initialTargetNodeId,
407
466
  };
408
- const channelCandidate = candidate.channelName ?? candidate['channel_name'];
409
- if (typeof channelCandidate === 'string' && channelCandidate.trim().length > 0) {
410
- config.channelName = channelCandidate.trim();
467
+ }
468
+ _requireLocalNodeId() {
469
+ if (!this._routingNode) {
470
+ throw new Error('BroadcastChannelListener requires routing node context');
411
471
  }
412
- const inboxCandidate = candidate.inboxCapacity ?? candidate['inbox_capacity'];
413
- if (typeof inboxCandidate === 'number' &&
414
- Number.isFinite(inboxCandidate) &&
415
- inboxCandidate > 0) {
416
- config.inboxCapacity = Math.floor(inboxCandidate);
472
+ const normalized = this._normalizeNodeId(this._routingNode.sid) ??
473
+ this._normalizeNodeId(this._routingNode.id);
474
+ if (!normalized) {
475
+ throw new Error('BroadcastChannelListener requires routing node with a stable identifier');
476
+ }
477
+ return normalized;
478
+ }
479
+ _normalizeNodeId(value) {
480
+ if (typeof value !== 'string') {
481
+ return null;
417
482
  }
418
- return config;
483
+ const trimmed = value.trim();
484
+ return trimmed.length > 0 ? trimmed : null;
419
485
  }
420
486
  _monitorConnectorLifecycle(senderId, systemId, connector) {
421
487
  const maybeClosable = connector;
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  // This file is auto-generated during build - do not edit manually
3
- // Generated from package.json version: 0.3.5-test.960
3
+ // Generated from package.json version: 0.3.5-test.962
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.VERSION = void 0;
6
6
  /**
7
7
  * The package version, injected at build time.
8
8
  * @internal
9
9
  */
10
- exports.VERSION = '0.3.5-test.960';
10
+ exports.VERSION = '0.3.5-test.962';
@@ -72,8 +72,13 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
72
72
  }
73
73
  const normalized = this._normalizeConfig(config);
74
74
  const options = (factoryArgs[0] ?? {});
75
+ const localNodeId = this._normalizeNodeId(options.localNodeId);
76
+ if (!localNodeId) {
77
+ throw new Error('BroadcastChannelConnectorFactory requires a localNodeId in create() options');
78
+ }
75
79
  const channelName = normalized.channelName ?? DEFAULT_CHANNEL;
76
80
  const inboxCapacity = normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
81
+ const resolvedTarget = this._normalizeTargetNodeId(options.initialTargetNodeId ?? normalized.initialTargetNodeId);
77
82
  const baseConfig = {
78
83
  drainTimeout: normalized.drainTimeout,
79
84
  flowControl: normalized.flowControl,
@@ -88,6 +93,8 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
88
93
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
89
94
  channelName,
90
95
  inboxCapacity,
96
+ localNodeId,
97
+ initialTargetNodeId: resolvedTarget,
91
98
  };
92
99
  const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
93
100
  if (options.authorization) {
@@ -111,6 +118,13 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
111
118
  normalized.channelName = channel.trim();
112
119
  }
113
120
  const capacity = candidate.inboxCapacity ?? candidate['inbox_capacity'];
121
+ const initialTargetNodeId = candidate.initialTargetNodeId ?? candidate['initial_target_node_id'];
122
+ if (typeof initialTargetNodeId === 'string' && initialTargetNodeId.trim().length > 0) {
123
+ normalized.initialTargetNodeId = initialTargetNodeId.trim();
124
+ }
125
+ else if (initialTargetNodeId === '*') {
126
+ normalized.initialTargetNodeId = '*';
127
+ }
114
128
  if (typeof capacity === 'number' &&
115
129
  Number.isFinite(capacity) &&
116
130
  capacity > 0) {
@@ -154,5 +168,18 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
154
168
  normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
155
169
  return normalized;
156
170
  }
171
+ _normalizeNodeId(value) {
172
+ if (typeof value !== 'string') {
173
+ return null;
174
+ }
175
+ const trimmed = value.trim();
176
+ return trimmed.length > 0 ? trimmed : null;
177
+ }
178
+ _normalizeTargetNodeId(value) {
179
+ if (value === '*') {
180
+ return '*';
181
+ }
182
+ return this._normalizeNodeId(value) ?? '*';
183
+ }
157
184
  }
158
185
  export default BroadcastChannelConnectorFactory;