@naylence/runtime 0.3.5-test.961 → 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.
- package/dist/browser/index.cjs +251 -33
- package/dist/browser/index.mjs +251 -33
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector-factory.js +27 -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/version.js +2 -2
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +27 -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/version.js +2 -2
- package/dist/node/index.cjs +251 -33
- package/dist/node/index.mjs +251 -33
- package/dist/node/node.cjs +251 -33
- package/dist/node/node.mjs +251 -33
- package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +5 -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/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;
|
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.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.
|
|
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;
|
|
@@ -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];
|