@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
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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,7 +461,7 @@ 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;
|
|
367
|
-
if (normalizedSenderId && normalizedSenderId !== this.
|
|
464
|
+
if (normalizedSenderId && normalizedSenderId !== this.localNodeId) {
|
|
368
465
|
logger.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
|
|
369
466
|
channel: this.channelName,
|
|
370
467
|
connector_id: this.connectorId,
|
|
@@ -404,7 +501,7 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
404
501
|
return false;
|
|
405
502
|
}
|
|
406
503
|
const senderId = this._extractSenderIdFromInboxItem(item);
|
|
407
|
-
if (senderId && senderId !== this.
|
|
504
|
+
if (senderId && senderId !== this.localNodeId) {
|
|
408
505
|
logger.debug('broadcast_channel_duplicate_ack_bypass_non_self', {
|
|
409
506
|
channel: this.channelName,
|
|
410
507
|
connector_id: this.connectorId,
|
|
@@ -500,6 +597,34 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
500
597
|
});
|
|
501
598
|
}
|
|
502
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
|
+
}
|
|
503
628
|
_trimSeenAcks(now) {
|
|
504
629
|
while (this.seenAckOrder.length > 0) {
|
|
505
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
|
-
|
|
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
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
418
|
+
return this._buildConnectorConfigForSystem(systemId, normalized);
|
|
400
419
|
}
|
|
401
|
-
|
|
402
|
-
|
|
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
|
|
405
|
-
inboxCapacity
|
|
406
|
-
passive: true,
|
|
460
|
+
channelName,
|
|
461
|
+
inboxCapacity,
|
|
462
|
+
passive: typeof passiveCandidate === 'boolean' ? passiveCandidate : true,
|
|
463
|
+
initialWindow,
|
|
464
|
+
localNodeId,
|
|
465
|
+
initialTargetNodeId,
|
|
407
466
|
};
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
467
|
+
}
|
|
468
|
+
_requireLocalNodeId() {
|
|
469
|
+
if (!this._routingNode) {
|
|
470
|
+
throw new Error('BroadcastChannelListener requires routing node context');
|
|
411
471
|
}
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -280,6 +280,20 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
280
280
|
waitEvent(event, signal) {
|
|
281
281
|
return signal ? event.wait({ signal }) : event.wait();
|
|
282
282
|
}
|
|
283
|
+
_getLocalNodeId() {
|
|
284
|
+
const normalized = this._normalizeNodeId(this.node.id);
|
|
285
|
+
if (!normalized) {
|
|
286
|
+
throw new Error('UpstreamSessionManager requires node with a stable identifier');
|
|
287
|
+
}
|
|
288
|
+
return normalized;
|
|
289
|
+
}
|
|
290
|
+
_normalizeNodeId(value) {
|
|
291
|
+
if (typeof value !== 'string') {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const trimmed = value.trim();
|
|
295
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
296
|
+
}
|
|
283
297
|
async connectCycle() {
|
|
284
298
|
if (!this.admissionClient) {
|
|
285
299
|
throw new errors_js_1.FameConnectError('Admission client is required to attach upstream');
|
|
@@ -301,6 +315,8 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
301
315
|
await this.onWelcome(welcome.frame);
|
|
302
316
|
const connector = await connector_factory_js_1.ConnectorFactory.createConnector(grant, {
|
|
303
317
|
systemId: welcome.frame.systemId,
|
|
318
|
+
localNodeId: this._getLocalNodeId(),
|
|
319
|
+
initialTargetNodeId: '*',
|
|
304
320
|
});
|
|
305
321
|
await connector.start(this.wrappedHandler);
|
|
306
322
|
this.connector = connector;
|
|
@@ -326,6 +342,20 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
326
342
|
}
|
|
327
343
|
const attachInfo = await this.attachClient.attach(this.node, this.outboundOriginType, connector, welcome.frame, this.wrappedHandler, this.getKeys() ?? undefined, callbackGrants);
|
|
328
344
|
this.targetSystemId = attachInfo.targetSystemId ?? null;
|
|
345
|
+
if (this.targetSystemId) {
|
|
346
|
+
const targetAware = connector;
|
|
347
|
+
if (typeof targetAware.setTargetNodeId === 'function') {
|
|
348
|
+
try {
|
|
349
|
+
targetAware.setTargetNodeId(this.targetSystemId);
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
logger.warning('broadcast_channel_target_apply_failed', {
|
|
353
|
+
error: error instanceof Error ? error.message : String(error),
|
|
354
|
+
target_node_id: this.targetSystemId,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
329
359
|
await this.onAttach(attachInfo, connector);
|
|
330
360
|
// Close the admission client immediately after attach completes
|
|
331
361
|
// This releases HTTP keep-alive connections (Node.js fetch/undici requires explicit cleanup)
|
package/dist/cjs/version.js
CHANGED
|
@@ -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.
|
|
3
|
+
// Generated from package.json version: 0.3.5-test.963
|
|
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.
|
|
10
|
+
exports.VERSION = '0.3.5-test.963';
|
|
@@ -72,8 +72,16 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
|
|
|
72
72
|
}
|
|
73
73
|
const normalized = this._normalizeConfig(config);
|
|
74
74
|
const options = (factoryArgs[0] ?? {});
|
|
75
|
+
const normalizedLocalNodeFromConfig = this._normalizeNodeId(normalized.localNodeId);
|
|
76
|
+
const localNodeId = this._normalizeNodeId(options.localNodeId) ?? normalizedLocalNodeFromConfig;
|
|
77
|
+
if (!localNodeId) {
|
|
78
|
+
throw new Error('BroadcastChannelConnectorFactory requires a localNodeId from config or create() options');
|
|
79
|
+
}
|
|
75
80
|
const channelName = normalized.channelName ?? DEFAULT_CHANNEL;
|
|
76
81
|
const inboxCapacity = normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
|
|
82
|
+
const targetFromOptions = this._normalizeTargetNodeId(options.initialTargetNodeId);
|
|
83
|
+
const targetFromConfig = this._normalizeTargetNodeId(normalized.initialTargetNodeId);
|
|
84
|
+
const resolvedTarget = targetFromOptions ?? targetFromConfig ?? '*';
|
|
77
85
|
const baseConfig = {
|
|
78
86
|
drainTimeout: normalized.drainTimeout,
|
|
79
87
|
flowControl: normalized.flowControl,
|
|
@@ -88,6 +96,8 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
|
|
|
88
96
|
type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
|
|
89
97
|
channelName,
|
|
90
98
|
inboxCapacity,
|
|
99
|
+
localNodeId,
|
|
100
|
+
initialTargetNodeId: resolvedTarget,
|
|
91
101
|
};
|
|
92
102
|
const connector = new BroadcastChannelConnector(connectorConfig, baseConfig);
|
|
93
103
|
if (options.authorization) {
|
|
@@ -111,11 +121,21 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
|
|
|
111
121
|
normalized.channelName = channel.trim();
|
|
112
122
|
}
|
|
113
123
|
const capacity = candidate.inboxCapacity ?? candidate['inbox_capacity'];
|
|
124
|
+
const initialTargetNodeId = candidate.initialTargetNodeId ?? candidate['initial_target_node_id'];
|
|
125
|
+
const normalizedTarget = this._normalizeTargetNodeId(initialTargetNodeId);
|
|
126
|
+
if (normalizedTarget) {
|
|
127
|
+
normalized.initialTargetNodeId = normalizedTarget;
|
|
128
|
+
}
|
|
114
129
|
if (typeof capacity === 'number' &&
|
|
115
130
|
Number.isFinite(capacity) &&
|
|
116
131
|
capacity > 0) {
|
|
117
132
|
normalized.inboxCapacity = Math.floor(capacity);
|
|
118
133
|
}
|
|
134
|
+
const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
|
|
135
|
+
const normalizedLocalNodeId = this._normalizeNodeId(localNodeId);
|
|
136
|
+
if (normalizedLocalNodeId) {
|
|
137
|
+
normalized.localNodeId = normalizedLocalNodeId;
|
|
138
|
+
}
|
|
119
139
|
if (typeof candidate.flowControl === 'boolean') {
|
|
120
140
|
normalized.flowControl = candidate.flowControl;
|
|
121
141
|
}
|
|
@@ -154,5 +174,21 @@ export class BroadcastChannelConnectorFactory extends ConnectorFactory {
|
|
|
154
174
|
normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
|
|
155
175
|
return normalized;
|
|
156
176
|
}
|
|
177
|
+
_normalizeNodeId(value) {
|
|
178
|
+
if (typeof value !== 'string') {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const trimmed = value.trim();
|
|
182
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
183
|
+
}
|
|
184
|
+
_normalizeTargetNodeId(value) {
|
|
185
|
+
if (value === undefined || value === null) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
if (value === '*') {
|
|
189
|
+
return '*';
|
|
190
|
+
}
|
|
191
|
+
return this._normalizeNodeId(value) ?? undefined;
|
|
192
|
+
}
|
|
157
193
|
}
|
|
158
194
|
export default BroadcastChannelConnectorFactory;
|