@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.
- package/dist/browser/index.cjs +290 -33
- package/dist/browser/index.mjs +290 -33
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector-factory.js +36 -0
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +131 -6
- package/dist/cjs/naylence/fame/connector/broadcast-channel-listener.js +91 -25
- package/dist/cjs/naylence/fame/node/upstream-session-manager.js +30 -0
- package/dist/cjs/version.js +2 -2
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +36 -0
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +131 -6
- package/dist/esm/naylence/fame/connector/broadcast-channel-listener.js +91 -25
- package/dist/esm/naylence/fame/node/upstream-session-manager.js +30 -0
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +290 -33
- package/dist/node/index.mjs +290 -33
- package/dist/node/node.cjs +290 -33
- package/dist/node/node.mjs +290 -33
- package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +6 -0
- package/dist/types/naylence/fame/connector/broadcast-channel-connector.browser.d.ts +10 -0
- package/dist/types/naylence/fame/connector/broadcast-channel-connector.node.d.ts +1 -6
- package/dist/types/naylence/fame/connector/broadcast-channel-listener.d.ts +3 -0
- package/dist/types/naylence/fame/grants/broadcast-channel-connection-grant.d.ts +1 -1
- package/dist/types/naylence/fame/node/upstream-session-manager.d.ts +2 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -46,6 +46,26 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
46
46
|
}
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
|
+
static normalizeNodeId(value) {
|
|
50
|
+
if (typeof value !== 'string') {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const trimmed = value.trim();
|
|
54
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
55
|
+
}
|
|
56
|
+
static normalizeTargetNodeId(value) {
|
|
57
|
+
if (typeof value !== 'string') {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const trimmed = value.trim();
|
|
61
|
+
if (trimmed.length === 0) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
if (trimmed === '*') {
|
|
65
|
+
return '*';
|
|
66
|
+
}
|
|
67
|
+
return trimmed;
|
|
68
|
+
}
|
|
49
69
|
constructor(config, baseConfig = {}) {
|
|
50
70
|
ensureBroadcastEnvironment();
|
|
51
71
|
super(baseConfig);
|
|
@@ -68,10 +88,18 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
68
88
|
this.inbox = new BoundedAsyncQueue(preferredCapacity);
|
|
69
89
|
this.inboxCapacity = preferredCapacity;
|
|
70
90
|
this.connectorId = BroadcastChannelConnector.generateConnectorId();
|
|
91
|
+
const normalizedLocalNodeId = BroadcastChannelConnector.normalizeNodeId(config.localNodeId);
|
|
92
|
+
if (!normalizedLocalNodeId) {
|
|
93
|
+
throw new Error('BroadcastChannelConnector requires a non-empty localNodeId');
|
|
94
|
+
}
|
|
95
|
+
this.localNodeId = normalizedLocalNodeId;
|
|
96
|
+
this.targetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(config.initialTargetNodeId);
|
|
71
97
|
this.channel = new BroadcastChannel(this.channelName);
|
|
72
98
|
logger.debug('broadcast_channel_connector_created', {
|
|
73
99
|
channel: this.channelName,
|
|
74
100
|
connector_id: this.connectorId,
|
|
101
|
+
local_node_id: this.localNodeId,
|
|
102
|
+
target_node_id: this.targetNodeId ?? null,
|
|
75
103
|
inbox_capacity: preferredCapacity,
|
|
76
104
|
timestamp: new Date().toISOString(),
|
|
77
105
|
});
|
|
@@ -93,15 +121,32 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
93
121
|
? message.constructor?.name ?? typeof message
|
|
94
122
|
: typeof message,
|
|
95
123
|
has_sender_id: Boolean(message?.senderId),
|
|
124
|
+
has_sender_node_id: Boolean(message?.senderNodeId),
|
|
96
125
|
});
|
|
97
126
|
if (!message || typeof message !== 'object') {
|
|
98
127
|
return;
|
|
99
128
|
}
|
|
100
129
|
const busMessage = message;
|
|
101
|
-
|
|
130
|
+
const senderNodeId = BroadcastChannelConnector.normalizeNodeId(busMessage.senderNodeId);
|
|
131
|
+
if (!senderNodeId) {
|
|
132
|
+
logger.debug('broadcast_channel_message_rejected', {
|
|
133
|
+
channel: this.channelName,
|
|
134
|
+
connector_id: this.connectorId,
|
|
135
|
+
reason: 'missing_sender_node_id',
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (senderNodeId === this.localNodeId) {
|
|
140
|
+
logger.debug('broadcast_channel_message_rejected', {
|
|
141
|
+
channel: this.channelName,
|
|
142
|
+
connector_id: this.connectorId,
|
|
143
|
+
reason: 'self_echo',
|
|
144
|
+
sender_node_id: senderNodeId,
|
|
145
|
+
});
|
|
102
146
|
return;
|
|
103
147
|
}
|
|
104
|
-
|
|
148
|
+
const incomingTargetNodeId = BroadcastChannelConnector.normalizeTargetNodeId(busMessage.targetNodeId);
|
|
149
|
+
if (!this._shouldAcceptMessageFromBus(senderNodeId, incomingTargetNodeId)) {
|
|
105
150
|
return;
|
|
106
151
|
}
|
|
107
152
|
const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
|
|
@@ -115,11 +160,13 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
115
160
|
}
|
|
116
161
|
logger.debug('broadcast_channel_message_received', {
|
|
117
162
|
channel: this.channelName,
|
|
118
|
-
sender_id:
|
|
163
|
+
sender_id: message?.senderId,
|
|
164
|
+
sender_node_id: senderNodeId,
|
|
165
|
+
target_node_id: incomingTargetNodeId ?? null,
|
|
119
166
|
connector_id: this.connectorId,
|
|
120
167
|
payload_length: payload.byteLength,
|
|
121
168
|
});
|
|
122
|
-
if (this._shouldSkipDuplicateAck(
|
|
169
|
+
if (this._shouldSkipDuplicateAck(senderNodeId, payload)) {
|
|
123
170
|
return;
|
|
124
171
|
}
|
|
125
172
|
try {
|
|
@@ -263,12 +310,17 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
263
310
|
}
|
|
264
311
|
async _transportSendBytes(data) {
|
|
265
312
|
ensureBroadcastEnvironment();
|
|
313
|
+
const targetNodeId = this.targetNodeId ?? '*';
|
|
266
314
|
logger.debug('broadcast_channel_message_sending', {
|
|
267
315
|
channel: this.channelName,
|
|
268
316
|
sender_id: this.connectorId,
|
|
317
|
+
sender_node_id: this.localNodeId,
|
|
318
|
+
target_node_id: targetNodeId,
|
|
269
319
|
});
|
|
270
320
|
this.channel.postMessage({
|
|
271
321
|
senderId: this.connectorId,
|
|
322
|
+
senderNodeId: this.localNodeId,
|
|
323
|
+
targetNodeId,
|
|
272
324
|
payload: data,
|
|
273
325
|
});
|
|
274
326
|
}
|
|
@@ -331,6 +383,51 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
331
383
|
}
|
|
332
384
|
return rawOrEnvelope;
|
|
333
385
|
}
|
|
386
|
+
_isWildcardTarget() {
|
|
387
|
+
return this.targetNodeId === '*' || typeof this.targetNodeId === 'undefined';
|
|
388
|
+
}
|
|
389
|
+
_shouldAcceptMessageFromBus(senderNodeId, targetNodeId) {
|
|
390
|
+
if (this._isWildcardTarget()) {
|
|
391
|
+
if (targetNodeId && targetNodeId !== '*') {
|
|
392
|
+
logger.debug('broadcast_channel_message_rejected', {
|
|
393
|
+
channel: this.channelName,
|
|
394
|
+
connector_id: this.connectorId,
|
|
395
|
+
reason: 'wildcard_target_mismatch',
|
|
396
|
+
sender_node_id: senderNodeId,
|
|
397
|
+
target_node_id: targetNodeId,
|
|
398
|
+
local_node_id: this.localNodeId,
|
|
399
|
+
});
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
const expectedSender = this.targetNodeId;
|
|
405
|
+
if (expectedSender && expectedSender !== '*' && senderNodeId !== expectedSender) {
|
|
406
|
+
logger.debug('broadcast_channel_message_rejected', {
|
|
407
|
+
channel: this.channelName,
|
|
408
|
+
connector_id: this.connectorId,
|
|
409
|
+
reason: 'unexpected_sender',
|
|
410
|
+
expected_sender_node_id: expectedSender,
|
|
411
|
+
sender_node_id: senderNodeId,
|
|
412
|
+
local_node_id: this.localNodeId,
|
|
413
|
+
});
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
if (targetNodeId &&
|
|
417
|
+
targetNodeId !== '*' &&
|
|
418
|
+
targetNodeId !== this.localNodeId) {
|
|
419
|
+
logger.debug('broadcast_channel_message_rejected', {
|
|
420
|
+
channel: this.channelName,
|
|
421
|
+
connector_id: this.connectorId,
|
|
422
|
+
reason: 'unexpected_target',
|
|
423
|
+
sender_node_id: senderNodeId,
|
|
424
|
+
target_node_id: targetNodeId,
|
|
425
|
+
local_node_id: this.localNodeId,
|
|
426
|
+
});
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
334
431
|
_describeInboxItem(item) {
|
|
335
432
|
if (item instanceof Uint8Array) {
|
|
336
433
|
return 'bytes';
|
|
@@ -361,7 +458,7 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
361
458
|
const normalizedSenderId = typeof senderId === 'string' && senderId.length > 0
|
|
362
459
|
? senderId
|
|
363
460
|
: undefined;
|
|
364
|
-
if (normalizedSenderId && normalizedSenderId !== this.
|
|
461
|
+
if (normalizedSenderId && normalizedSenderId !== this.localNodeId) {
|
|
365
462
|
logger.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
|
|
366
463
|
channel: this.channelName,
|
|
367
464
|
connector_id: this.connectorId,
|
|
@@ -401,7 +498,7 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
401
498
|
return false;
|
|
402
499
|
}
|
|
403
500
|
const senderId = this._extractSenderIdFromInboxItem(item);
|
|
404
|
-
if (senderId && senderId !== this.
|
|
501
|
+
if (senderId && senderId !== this.localNodeId) {
|
|
405
502
|
logger.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
|
|
406
503
|
channel: this.channelName,
|
|
407
504
|
connector_id: this.connectorId,
|
|
@@ -497,6 +594,34 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
497
594
|
});
|
|
498
595
|
}
|
|
499
596
|
}
|
|
597
|
+
setTargetNodeId(nodeId) {
|
|
598
|
+
const normalized = BroadcastChannelConnector.normalizeNodeId(nodeId);
|
|
599
|
+
if (!normalized) {
|
|
600
|
+
throw new Error('BroadcastChannelConnector target node id must be a non-empty string');
|
|
601
|
+
}
|
|
602
|
+
if (normalized === '*') {
|
|
603
|
+
this.setWildcardTarget();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
this.targetNodeId = normalized;
|
|
607
|
+
logger.debug('broadcast_channel_target_updated', {
|
|
608
|
+
channel: this.channelName,
|
|
609
|
+
connector_id: this.connectorId,
|
|
610
|
+
local_node_id: this.localNodeId,
|
|
611
|
+
target_node_id: this.targetNodeId,
|
|
612
|
+
target_mode: 'direct',
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
setWildcardTarget() {
|
|
616
|
+
this.targetNodeId = '*';
|
|
617
|
+
logger.debug('broadcast_channel_target_updated', {
|
|
618
|
+
channel: this.channelName,
|
|
619
|
+
connector_id: this.connectorId,
|
|
620
|
+
local_node_id: this.localNodeId,
|
|
621
|
+
target_node_id: this.targetNodeId,
|
|
622
|
+
target_mode: 'wildcard',
|
|
623
|
+
});
|
|
624
|
+
}
|
|
500
625
|
_trimSeenAcks(now) {
|
|
501
626
|
while (this.seenAckOrder.length > 0) {
|
|
502
627
|
const candidate = this.seenAckOrder[0];
|
|
@@ -324,7 +324,7 @@ export class BroadcastChannelListener extends TransportListener {
|
|
|
324
324
|
node: routingNode,
|
|
325
325
|
});
|
|
326
326
|
const selection = defaultGrantSelectionPolicy.selectCallbackGrant(selectionContext);
|
|
327
|
-
connectorConfig = this._grantToConnectorConfig(selection.grant);
|
|
327
|
+
connectorConfig = this._grantToConnectorConfig(selection.grant, systemId);
|
|
328
328
|
}
|
|
329
329
|
catch (error) {
|
|
330
330
|
logger.debug('broadcast_channel_listener_grant_selection_failed', {
|
|
@@ -333,13 +333,20 @@ export class BroadcastChannelListener extends TransportListener {
|
|
|
333
333
|
error: error instanceof Error ? error.message : String(error),
|
|
334
334
|
});
|
|
335
335
|
connectorConfig =
|
|
336
|
-
this._extractBroadcastConnectorConfig(frame) ??
|
|
337
|
-
{
|
|
336
|
+
this._extractBroadcastConnectorConfig(frame, systemId) ??
|
|
337
|
+
this._buildConnectorConfigForSystem(systemId, {
|
|
338
338
|
type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
|
|
339
339
|
channelName: this._channelName,
|
|
340
340
|
inboxCapacity: this._inboxCapacity,
|
|
341
341
|
passive: true,
|
|
342
|
-
};
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
if (!connectorConfig) {
|
|
345
|
+
logger.error('broadcast_channel_listener_missing_connector_config', {
|
|
346
|
+
sender_id: params.senderId,
|
|
347
|
+
system_id: systemId,
|
|
348
|
+
});
|
|
349
|
+
return null;
|
|
343
350
|
}
|
|
344
351
|
try {
|
|
345
352
|
const connector = await routingNode.createOriginConnector({
|
|
@@ -365,7 +372,7 @@ export class BroadcastChannelListener extends TransportListener {
|
|
|
365
372
|
return null;
|
|
366
373
|
}
|
|
367
374
|
}
|
|
368
|
-
_extractBroadcastConnectorConfig(frame) {
|
|
375
|
+
_extractBroadcastConnectorConfig(frame, systemId) {
|
|
369
376
|
const rawGrants = frame.callbackGrants;
|
|
370
377
|
if (!Array.isArray(rawGrants)) {
|
|
371
378
|
return null;
|
|
@@ -376,7 +383,10 @@ export class BroadcastChannelListener extends TransportListener {
|
|
|
376
383
|
(grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE ||
|
|
377
384
|
grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE)) {
|
|
378
385
|
try {
|
|
379
|
-
|
|
386
|
+
if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
|
|
387
|
+
return this._buildConnectorConfigForSystem(systemId, grant);
|
|
388
|
+
}
|
|
389
|
+
return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
|
|
380
390
|
}
|
|
381
391
|
catch (error) {
|
|
382
392
|
logger.debug('broadcast_channel_listener_grant_normalization_failed', {
|
|
@@ -387,31 +397,87 @@ export class BroadcastChannelListener extends TransportListener {
|
|
|
387
397
|
}
|
|
388
398
|
return null;
|
|
389
399
|
}
|
|
390
|
-
_grantToConnectorConfig(grant) {
|
|
391
|
-
if (grant.type
|
|
392
|
-
|
|
393
|
-
|
|
400
|
+
_grantToConnectorConfig(grant, systemId) {
|
|
401
|
+
if (grant.type === BROADCAST_CHANNEL_CONNECTOR_TYPE) {
|
|
402
|
+
return this._buildConnectorConfigForSystem(systemId, grant);
|
|
403
|
+
}
|
|
404
|
+
if (grant.type === BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE) {
|
|
405
|
+
return this._buildConnectorConfigForSystem(systemId, broadcastChannelGrantToConnectorConfig(grant));
|
|
406
|
+
}
|
|
407
|
+
if ('toConnectorConfig' in grant &&
|
|
408
|
+
typeof grant.toConnectorConfig ===
|
|
409
|
+
'function') {
|
|
410
|
+
const normalized = grant.toConnectorConfig();
|
|
411
|
+
if (normalized.type !== BROADCAST_CHANNEL_CONNECTOR_TYPE) {
|
|
412
|
+
throw new Error(`Unsupported grant connector type: ${normalized.type}`);
|
|
394
413
|
}
|
|
395
|
-
|
|
414
|
+
return this._buildConnectorConfigForSystem(systemId, normalized);
|
|
396
415
|
}
|
|
397
|
-
|
|
398
|
-
|
|
416
|
+
throw new Error(`Unsupported grant type: ${grant.type}`);
|
|
417
|
+
}
|
|
418
|
+
_buildConnectorConfigForSystem(systemId, baseConfig) {
|
|
419
|
+
const localNodeId = this._requireLocalNodeId();
|
|
420
|
+
const targetSystemId = this._normalizeNodeId(systemId);
|
|
421
|
+
if (!targetSystemId) {
|
|
422
|
+
throw new Error('BroadcastChannelListener requires a valid system id');
|
|
423
|
+
}
|
|
424
|
+
const candidate = baseConfig ?? null;
|
|
425
|
+
const channelCandidate = candidate && 'channelName' in candidate
|
|
426
|
+
? candidate.channelName
|
|
427
|
+
: undefined;
|
|
428
|
+
const inboxCandidate = candidate && 'inboxCapacity' in candidate
|
|
429
|
+
? candidate.inboxCapacity
|
|
430
|
+
: undefined;
|
|
431
|
+
const initialWindowCandidate = candidate && 'initialWindow' in candidate
|
|
432
|
+
? candidate.initialWindow
|
|
433
|
+
: undefined;
|
|
434
|
+
const passiveCandidate = candidate && 'passive' in candidate
|
|
435
|
+
? candidate.passive
|
|
436
|
+
: undefined;
|
|
437
|
+
const targetCandidate = candidate && 'initialTargetNodeId' in candidate
|
|
438
|
+
? candidate.initialTargetNodeId
|
|
439
|
+
: undefined;
|
|
440
|
+
const channelName = typeof channelCandidate === 'string' && channelCandidate.trim().length > 0
|
|
441
|
+
? channelCandidate.trim()
|
|
442
|
+
: this._channelName;
|
|
443
|
+
const inboxCapacity = typeof inboxCandidate === 'number' &&
|
|
444
|
+
Number.isFinite(inboxCandidate) &&
|
|
445
|
+
inboxCandidate > 0
|
|
446
|
+
? Math.floor(inboxCandidate)
|
|
447
|
+
: this._inboxCapacity;
|
|
448
|
+
const initialWindow = typeof initialWindowCandidate === 'number' &&
|
|
449
|
+
Number.isFinite(initialWindowCandidate) &&
|
|
450
|
+
initialWindowCandidate > 0
|
|
451
|
+
? Math.floor(initialWindowCandidate)
|
|
452
|
+
: undefined;
|
|
453
|
+
const initialTargetNodeId = this._normalizeNodeId(targetCandidate) ?? targetSystemId;
|
|
454
|
+
return {
|
|
399
455
|
type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
|
|
400
|
-
channelName
|
|
401
|
-
inboxCapacity
|
|
402
|
-
passive: true,
|
|
456
|
+
channelName,
|
|
457
|
+
inboxCapacity,
|
|
458
|
+
passive: typeof passiveCandidate === 'boolean' ? passiveCandidate : true,
|
|
459
|
+
initialWindow,
|
|
460
|
+
localNodeId,
|
|
461
|
+
initialTargetNodeId,
|
|
403
462
|
};
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
463
|
+
}
|
|
464
|
+
_requireLocalNodeId() {
|
|
465
|
+
if (!this._routingNode) {
|
|
466
|
+
throw new Error('BroadcastChannelListener requires routing node context');
|
|
407
467
|
}
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
468
|
+
const normalized = this._normalizeNodeId(this._routingNode.sid) ??
|
|
469
|
+
this._normalizeNodeId(this._routingNode.id);
|
|
470
|
+
if (!normalized) {
|
|
471
|
+
throw new Error('BroadcastChannelListener requires routing node with a stable identifier');
|
|
472
|
+
}
|
|
473
|
+
return normalized;
|
|
474
|
+
}
|
|
475
|
+
_normalizeNodeId(value) {
|
|
476
|
+
if (typeof value !== 'string') {
|
|
477
|
+
return null;
|
|
413
478
|
}
|
|
414
|
-
|
|
479
|
+
const trimmed = value.trim();
|
|
480
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
415
481
|
}
|
|
416
482
|
_monitorConnectorLifecycle(senderId, systemId, connector) {
|
|
417
483
|
const maybeClosable = connector;
|
|
@@ -277,6 +277,20 @@ export class UpstreamSessionManager extends TaskSpawner {
|
|
|
277
277
|
waitEvent(event, signal) {
|
|
278
278
|
return signal ? event.wait({ signal }) : event.wait();
|
|
279
279
|
}
|
|
280
|
+
_getLocalNodeId() {
|
|
281
|
+
const normalized = this._normalizeNodeId(this.node.id);
|
|
282
|
+
if (!normalized) {
|
|
283
|
+
throw new Error('UpstreamSessionManager requires node with a stable identifier');
|
|
284
|
+
}
|
|
285
|
+
return normalized;
|
|
286
|
+
}
|
|
287
|
+
_normalizeNodeId(value) {
|
|
288
|
+
if (typeof value !== 'string') {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
const trimmed = value.trim();
|
|
292
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
293
|
+
}
|
|
280
294
|
async connectCycle() {
|
|
281
295
|
if (!this.admissionClient) {
|
|
282
296
|
throw new FameConnectError('Admission client is required to attach upstream');
|
|
@@ -298,6 +312,8 @@ export class UpstreamSessionManager extends TaskSpawner {
|
|
|
298
312
|
await this.onWelcome(welcome.frame);
|
|
299
313
|
const connector = await ConnectorFactory.createConnector(grant, {
|
|
300
314
|
systemId: welcome.frame.systemId,
|
|
315
|
+
localNodeId: this._getLocalNodeId(),
|
|
316
|
+
initialTargetNodeId: '*',
|
|
301
317
|
});
|
|
302
318
|
await connector.start(this.wrappedHandler);
|
|
303
319
|
this.connector = connector;
|
|
@@ -323,6 +339,20 @@ export class UpstreamSessionManager extends TaskSpawner {
|
|
|
323
339
|
}
|
|
324
340
|
const attachInfo = await this.attachClient.attach(this.node, this.outboundOriginType, connector, welcome.frame, this.wrappedHandler, this.getKeys() ?? undefined, callbackGrants);
|
|
325
341
|
this.targetSystemId = attachInfo.targetSystemId ?? null;
|
|
342
|
+
if (this.targetSystemId) {
|
|
343
|
+
const targetAware = connector;
|
|
344
|
+
if (typeof targetAware.setTargetNodeId === 'function') {
|
|
345
|
+
try {
|
|
346
|
+
targetAware.setTargetNodeId(this.targetSystemId);
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
logger.warning('broadcast_channel_target_apply_failed', {
|
|
350
|
+
error: error instanceof Error ? error.message : String(error),
|
|
351
|
+
target_node_id: this.targetSystemId,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
326
356
|
await this.onAttach(attachInfo, connector);
|
|
327
357
|
// Close the admission client immediately after attach completes
|
|
328
358
|
// This releases HTTP keep-alive connections (Node.js fetch/undici requires explicit cleanup)
|
package/dist/esm/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This file is auto-generated during build - do not edit manually
|
|
2
|
-
// Generated from package.json version: 0.3.5-test.
|
|
2
|
+
// Generated from package.json version: 0.3.5-test.963
|
|
3
3
|
/**
|
|
4
4
|
* The package version, injected at build time.
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
|
-
export const VERSION = '0.3.5-test.
|
|
7
|
+
export const VERSION = '0.3.5-test.963';
|