@naylence/runtime 0.3.5-test.953 → 0.3.5-test.955
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 +133 -530
- package/dist/browser/index.mjs +133 -530
- package/dist/cjs/naylence/fame/channel/flow-controller.js +38 -1
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector-factory.js +2 -14
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +51 -109
- package/dist/cjs/naylence/fame/connector/broadcast-channel-listener.js +10 -76
- package/dist/cjs/naylence/fame/connector/inpage-connector-factory.js +0 -12
- package/dist/cjs/naylence/fame/connector/inpage-connector.js +1 -105
- package/dist/cjs/naylence/fame/connector/inpage-listener.js +2 -49
- package/dist/cjs/naylence/fame/grants/broadcast-channel-connection-grant.js +12 -23
- package/dist/cjs/naylence/fame/grants/inpage-connection-grant.js +0 -28
- package/dist/cjs/naylence/fame/node/admission/default-node-attach-client.js +0 -14
- package/dist/cjs/naylence/fame/node/upstream-session-manager.js +0 -6
- package/dist/cjs/version.js +2 -2
- package/dist/esm/naylence/fame/channel/flow-controller.js +38 -1
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +2 -14
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +51 -109
- package/dist/esm/naylence/fame/connector/broadcast-channel-listener.js +10 -76
- package/dist/esm/naylence/fame/connector/inpage-connector-factory.js +0 -12
- package/dist/esm/naylence/fame/connector/inpage-connector.js +1 -105
- package/dist/esm/naylence/fame/connector/inpage-listener.js +2 -49
- package/dist/esm/naylence/fame/grants/broadcast-channel-connection-grant.js +12 -23
- package/dist/esm/naylence/fame/grants/inpage-connection-grant.js +0 -28
- package/dist/esm/naylence/fame/node/admission/default-node-attach-client.js +0 -14
- package/dist/esm/naylence/fame/node/upstream-session-manager.js +0 -6
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +133 -530
- package/dist/node/index.mjs +133 -530
- package/dist/node/node.cjs +133 -546
- package/dist/node/node.mjs +133 -546
- package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +0 -2
- package/dist/types/naylence/fame/connector/broadcast-channel-connector.browser.d.ts +4 -9
- package/dist/types/naylence/fame/connector/inpage-connector-factory.d.ts +0 -2
- package/dist/types/naylence/fame/connector/inpage-connector.d.ts +0 -9
- package/dist/types/naylence/fame/connector/inpage-listener.d.ts +0 -1
- package/dist/types/naylence/fame/grants/broadcast-channel-connection-grant.d.ts +3 -6
- package/dist/types/naylence/fame/grants/inpage-connection-grant.d.ts +0 -8
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/cjs/naylence/fame/connector/transport-frame.js +0 -100
- package/dist/esm/naylence/fame/connector/transport-frame.js +0 -93
- package/dist/types/naylence/fame/connector/transport-frame.d.ts +0 -56
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.FlowController = void 0;
|
|
27
27
|
const core_1 = require("@naylence/core");
|
|
28
|
+
const logging_js_1 = require("../util/logging.js");
|
|
29
|
+
const logger = (0, logging_js_1.getLogger)('naylence.fame.flow.flow_controller');
|
|
28
30
|
/**
|
|
29
31
|
* Simple condition variable implementation for TypeScript/Node.js
|
|
30
32
|
* Similar to Python's asyncio.Condition
|
|
@@ -150,8 +152,17 @@ class FlowController {
|
|
|
150
152
|
// clamp into [0, initialWindow]
|
|
151
153
|
const newBalance = Math.max(0, Math.min(this.initialWindow, prev + delta));
|
|
152
154
|
this.credits.set(flowId, newBalance);
|
|
155
|
+
const crossedZero = prev <= 0 && newBalance > 0;
|
|
156
|
+
logger.debug('flow_controller_add_credits', {
|
|
157
|
+
flow_id: flowId,
|
|
158
|
+
delta,
|
|
159
|
+
prev_balance: prev,
|
|
160
|
+
new_balance: newBalance,
|
|
161
|
+
initial_window: this.initialWindow,
|
|
162
|
+
crossed_zero: crossedZero,
|
|
163
|
+
});
|
|
153
164
|
// wake waiters only if we crossed the zero boundary
|
|
154
|
-
if (
|
|
165
|
+
if (crossedZero) {
|
|
155
166
|
this.wakeWaiters(flowId);
|
|
156
167
|
}
|
|
157
168
|
return newBalance;
|
|
@@ -162,11 +173,27 @@ class FlowController {
|
|
|
162
173
|
async acquire(flowId) {
|
|
163
174
|
this.ensureFlow(flowId);
|
|
164
175
|
const condition = this.conditions.get(flowId);
|
|
176
|
+
logger.debug('flow_controller_acquire_attempt', {
|
|
177
|
+
flow_id: flowId,
|
|
178
|
+
current_balance: this.credits.get(flowId),
|
|
179
|
+
});
|
|
165
180
|
while (this.credits.get(flowId) <= 0) {
|
|
181
|
+
logger.debug('flow_controller_waiting_for_credit', {
|
|
182
|
+
flow_id: flowId,
|
|
183
|
+
});
|
|
166
184
|
await condition.wait();
|
|
185
|
+
logger.debug('flow_controller_woke_with_credit', {
|
|
186
|
+
flow_id: flowId,
|
|
187
|
+
balance_after_wake: this.credits.get(flowId),
|
|
188
|
+
});
|
|
167
189
|
}
|
|
168
190
|
const current = this.credits.get(flowId);
|
|
169
191
|
this.credits.set(flowId, current - 1);
|
|
192
|
+
logger.debug('flow_controller_credit_consumed', {
|
|
193
|
+
flow_id: flowId,
|
|
194
|
+
prev_balance: current,
|
|
195
|
+
remaining_balance: current - 1,
|
|
196
|
+
});
|
|
170
197
|
}
|
|
171
198
|
/**
|
|
172
199
|
* Consume *credits* immediately (non-blocking).
|
|
@@ -186,6 +213,12 @@ class FlowController {
|
|
|
186
213
|
const current = this.credits.get(flowId);
|
|
187
214
|
const remaining = Math.max(current - credits, 0);
|
|
188
215
|
this.credits.set(flowId, remaining);
|
|
216
|
+
logger.debug('flow_controller_consume', {
|
|
217
|
+
flow_id: flowId,
|
|
218
|
+
requested: credits,
|
|
219
|
+
prev_balance: current,
|
|
220
|
+
remaining_balance: remaining,
|
|
221
|
+
});
|
|
189
222
|
return remaining;
|
|
190
223
|
}
|
|
191
224
|
/**
|
|
@@ -206,6 +239,10 @@ class FlowController {
|
|
|
206
239
|
this.windowIds.delete(flowId);
|
|
207
240
|
this.credits.set(flowId, this.initialWindow);
|
|
208
241
|
this.wakeWaiters(flowId);
|
|
242
|
+
logger.debug('flow_controller_flow_reset', {
|
|
243
|
+
flow_id: flowId,
|
|
244
|
+
reset_balance: this.initialWindow,
|
|
245
|
+
});
|
|
209
246
|
}
|
|
210
247
|
/**
|
|
211
248
|
* Return `[windowId, flags]` for the next outbound envelope.
|
|
@@ -40,8 +40,7 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
|
|
|
40
40
|
type: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
|
|
41
41
|
channelName: connectorConfig.channelName,
|
|
42
42
|
inboxCapacity: connectorConfig.inboxCapacity,
|
|
43
|
-
|
|
44
|
-
remoteNodeId: connectorConfig.remoteNodeId,
|
|
43
|
+
initialWindow: connectorConfig.initialWindow,
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
46
|
const config = {
|
|
@@ -66,6 +65,7 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
|
|
|
66
65
|
purpose: 'connection',
|
|
67
66
|
channelName: normalizedConfig.channelName,
|
|
68
67
|
inboxCapacity: normalizedConfig.inboxCapacity,
|
|
68
|
+
initialWindow: normalizedConfig.initialWindow,
|
|
69
69
|
});
|
|
70
70
|
return grant;
|
|
71
71
|
}
|
|
@@ -91,8 +91,6 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
|
|
|
91
91
|
type: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
|
|
92
92
|
channelName,
|
|
93
93
|
inboxCapacity,
|
|
94
|
-
localNodeId: normalized.localNodeId,
|
|
95
|
-
remoteNodeId: normalized.remoteNodeId,
|
|
96
94
|
};
|
|
97
95
|
const connector = new broadcast_channel_connector_js_1.BroadcastChannelConnector(connectorConfig, baseConfig);
|
|
98
96
|
if (options.authorization) {
|
|
@@ -154,16 +152,6 @@ class BroadcastChannelConnectorFactory extends connector_factory_js_1.ConnectorF
|
|
|
154
152
|
if (candidate.authorizationContext !== undefined) {
|
|
155
153
|
normalized.authorizationContext = candidate.authorizationContext;
|
|
156
154
|
}
|
|
157
|
-
// Handle localNodeId
|
|
158
|
-
const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
|
|
159
|
-
if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
|
|
160
|
-
normalized.localNodeId = localNodeId.trim();
|
|
161
|
-
}
|
|
162
|
-
// Handle remoteNodeId
|
|
163
|
-
const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
|
|
164
|
-
if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
|
|
165
|
-
normalized.remoteNodeId = remoteNodeId.trim();
|
|
166
|
-
}
|
|
167
155
|
normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL;
|
|
168
156
|
normalized.inboxCapacity =
|
|
169
157
|
normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
|
|
@@ -6,7 +6,6 @@ const errors_js_1 = require("../errors/errors.js");
|
|
|
6
6
|
const logging_js_1 = require("../util/logging.js");
|
|
7
7
|
const bounded_async_queue_js_1 = require("../util/bounded-async-queue.js");
|
|
8
8
|
const core_1 = require("@naylence/core");
|
|
9
|
-
const transport_frame_js_1 = require("./transport-frame.js");
|
|
10
9
|
const logger = (0, logging_js_1.getLogger)('naylence.fame.connector.broadcast_channel_connector');
|
|
11
10
|
exports.BROADCAST_CHANNEL_CONNECTOR_TYPE = 'broadcast-channel-connector';
|
|
12
11
|
const DEFAULT_CHANNEL = 'naylence-fabric';
|
|
@@ -70,22 +69,12 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
70
69
|
? Math.floor(config.inboxCapacity)
|
|
71
70
|
: DEFAULT_INBOX_CAPACITY;
|
|
72
71
|
this.inbox = new bounded_async_queue_js_1.BoundedAsyncQueue(preferredCapacity);
|
|
72
|
+
this.inboxCapacity = preferredCapacity;
|
|
73
73
|
this.connectorId = BroadcastChannelConnector.generateConnectorId();
|
|
74
74
|
this.channel = new BroadcastChannel(this.channelName);
|
|
75
|
-
// Set local and remote node IDs (defaults to connector ID for backwards compatibility)
|
|
76
|
-
this.localNodeId =
|
|
77
|
-
typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
|
|
78
|
-
? config.localNodeId.trim()
|
|
79
|
-
: this.connectorId;
|
|
80
|
-
this.remoteNodeId =
|
|
81
|
-
typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
|
|
82
|
-
? config.remoteNodeId.trim()
|
|
83
|
-
: '*'; // Accept from any remote if not specified
|
|
84
75
|
logger.debug('broadcast_channel_connector_created', {
|
|
85
76
|
channel: this.channelName,
|
|
86
77
|
connector_id: this.connectorId,
|
|
87
|
-
local_node_id: this.localNodeId,
|
|
88
|
-
remote_node_id: this.remoteNodeId,
|
|
89
78
|
inbox_capacity: preferredCapacity,
|
|
90
79
|
timestamp: new Date().toISOString(),
|
|
91
80
|
});
|
|
@@ -118,67 +107,6 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
118
107
|
if (busMessage.senderId === this.connectorId) {
|
|
119
108
|
return;
|
|
120
109
|
}
|
|
121
|
-
// Try to unwrap as transport frame
|
|
122
|
-
const frame = (0, transport_frame_js_1.unwrapTransportFrame)(busMessage.payload);
|
|
123
|
-
if (frame) {
|
|
124
|
-
// Apply connector's filtering policy: strict dst check, src accepts wildcard
|
|
125
|
-
const srcMatches = this.remoteNodeId === '*' || frame.src === this.remoteNodeId;
|
|
126
|
-
const dstMatches = frame.dst === this.localNodeId;
|
|
127
|
-
if (dstMatches && srcMatches) {
|
|
128
|
-
// Successfully received and filtered transport frame
|
|
129
|
-
logger.debug('broadcast_channel_transport_frame_received', {
|
|
130
|
-
channel: this.channelName,
|
|
131
|
-
sender_id: busMessage.senderId,
|
|
132
|
-
connector_id: this.connectorId,
|
|
133
|
-
local_node_id: this.localNodeId,
|
|
134
|
-
remote_node_id: this.remoteNodeId,
|
|
135
|
-
frame_src: frame.src,
|
|
136
|
-
frame_dst: frame.dst,
|
|
137
|
-
payload_length: frame.payload.byteLength,
|
|
138
|
-
});
|
|
139
|
-
const unwrapped = frame.payload;
|
|
140
|
-
if (this._shouldSkipDuplicateAck(busMessage.senderId, unwrapped)) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
if (typeof this.inbox.tryEnqueue === 'function') {
|
|
145
|
-
const accepted = this.inbox.tryEnqueue(unwrapped);
|
|
146
|
-
if (accepted) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
this.inbox.enqueue(unwrapped);
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
if (error instanceof bounded_async_queue_js_1.QueueFullError) {
|
|
154
|
-
logger.warning('broadcast_channel_receive_queue_full', {
|
|
155
|
-
channel: this.channelName,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
logger.error('broadcast_channel_receive_error', {
|
|
160
|
-
channel: this.channelName,
|
|
161
|
-
error: error instanceof Error ? error.message : String(error),
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
// Frame filtered out by addressing rules
|
|
169
|
-
logger.debug('broadcast_channel_transport_frame_filtered', {
|
|
170
|
-
channel: this.channelName,
|
|
171
|
-
connector_id: this.connectorId,
|
|
172
|
-
local_node_id: this.localNodeId,
|
|
173
|
-
remote_node_id: this.remoteNodeId,
|
|
174
|
-
frame_src: frame.src,
|
|
175
|
-
frame_dst: frame.dst,
|
|
176
|
-
reason: !dstMatches ? 'wrong_destination' : 'wrong_source',
|
|
177
|
-
});
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// Fall back to legacy format (no transport frame)
|
|
182
110
|
const payload = BroadcastChannelConnector.coercePayload(busMessage.payload);
|
|
183
111
|
if (!payload) {
|
|
184
112
|
logger.debug('broadcast_channel_payload_rejected', {
|
|
@@ -201,15 +129,27 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
201
129
|
if (typeof this.inbox.tryEnqueue === 'function') {
|
|
202
130
|
const accepted = this.inbox.tryEnqueue(payload);
|
|
203
131
|
if (accepted) {
|
|
132
|
+
this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
|
|
133
|
+
source: 'listener',
|
|
134
|
+
enqueue_strategy: 'try',
|
|
135
|
+
payload_length: payload.byteLength,
|
|
136
|
+
});
|
|
204
137
|
return;
|
|
205
138
|
}
|
|
206
139
|
}
|
|
207
140
|
this.inbox.enqueue(payload);
|
|
141
|
+
this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
|
|
142
|
+
source: 'listener',
|
|
143
|
+
enqueue_strategy: 'enqueue',
|
|
144
|
+
payload_length: payload.byteLength,
|
|
145
|
+
});
|
|
208
146
|
}
|
|
209
147
|
catch (error) {
|
|
210
148
|
if (error instanceof bounded_async_queue_js_1.QueueFullError) {
|
|
211
149
|
logger.warning('broadcast_channel_receive_queue_full', {
|
|
212
150
|
channel: this.channelName,
|
|
151
|
+
inbox_capacity: this.inboxCapacity,
|
|
152
|
+
inbox_remaining_capacity: this.inbox.remainingCapacity,
|
|
213
153
|
});
|
|
214
154
|
}
|
|
215
155
|
else {
|
|
@@ -293,15 +233,25 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
293
233
|
if (typeof this.inbox.tryEnqueue === 'function') {
|
|
294
234
|
const accepted = this.inbox.tryEnqueue(item);
|
|
295
235
|
if (accepted) {
|
|
236
|
+
this.logInboxSnapshot('broadcast_channel_push_enqueued', {
|
|
237
|
+
enqueue_strategy: 'try',
|
|
238
|
+
item_type: this._describeInboxItem(item),
|
|
239
|
+
});
|
|
296
240
|
return;
|
|
297
241
|
}
|
|
298
242
|
}
|
|
299
243
|
this.inbox.enqueue(item);
|
|
244
|
+
this.logInboxSnapshot('broadcast_channel_push_enqueued', {
|
|
245
|
+
enqueue_strategy: 'enqueue',
|
|
246
|
+
item_type: this._describeInboxItem(item),
|
|
247
|
+
});
|
|
300
248
|
}
|
|
301
249
|
catch (error) {
|
|
302
250
|
if (error instanceof bounded_async_queue_js_1.QueueFullError) {
|
|
303
251
|
logger.warning('broadcast_channel_push_queue_full', {
|
|
304
252
|
channel: this.channelName,
|
|
253
|
+
inbox_capacity: this.inboxCapacity,
|
|
254
|
+
inbox_remaining_capacity: this.inbox.remainingCapacity,
|
|
305
255
|
});
|
|
306
256
|
throw error;
|
|
307
257
|
}
|
|
@@ -317,30 +267,18 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
317
267
|
logger.debug('broadcast_channel_message_sending', {
|
|
318
268
|
channel: this.channelName,
|
|
319
269
|
sender_id: this.connectorId,
|
|
320
|
-
local_node_id: this.localNodeId,
|
|
321
|
-
remote_node_id: this.remoteNodeId,
|
|
322
270
|
});
|
|
323
|
-
// Only use transport framing if both localNodeId and remoteNodeId are explicitly set
|
|
324
|
-
// (not using default values). This ensures backwards compatibility.
|
|
325
|
-
const useTransportFrame = this.localNodeId !== this.connectorId ||
|
|
326
|
-
this.remoteNodeId !== '*';
|
|
327
|
-
let payload;
|
|
328
|
-
if (useTransportFrame) {
|
|
329
|
-
// Wrap payload in transport frame
|
|
330
|
-
const frame = (0, transport_frame_js_1.wrapTransportFrame)(data, this.localNodeId, this.remoteNodeId);
|
|
331
|
-
payload = (0, transport_frame_js_1.serializeTransportFrame)(frame);
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
// Legacy format: send raw payload
|
|
335
|
-
payload = data;
|
|
336
|
-
}
|
|
337
271
|
this.channel.postMessage({
|
|
338
272
|
senderId: this.connectorId,
|
|
339
|
-
payload,
|
|
273
|
+
payload: data,
|
|
340
274
|
});
|
|
341
275
|
}
|
|
342
276
|
async _transportReceive() {
|
|
343
|
-
|
|
277
|
+
const item = await this.inbox.dequeue();
|
|
278
|
+
this.logInboxSnapshot('broadcast_channel_inbox_dequeued', {
|
|
279
|
+
item_type: this._describeInboxItem(item),
|
|
280
|
+
});
|
|
281
|
+
return item;
|
|
344
282
|
}
|
|
345
283
|
async _transportClose(code, reason) {
|
|
346
284
|
logger.debug('broadcast_channel_transport_closing', {
|
|
@@ -394,6 +332,28 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
394
332
|
}
|
|
395
333
|
return rawOrEnvelope;
|
|
396
334
|
}
|
|
335
|
+
_describeInboxItem(item) {
|
|
336
|
+
if (item instanceof Uint8Array) {
|
|
337
|
+
return 'bytes';
|
|
338
|
+
}
|
|
339
|
+
if (item.envelope) {
|
|
340
|
+
return 'channel_message';
|
|
341
|
+
}
|
|
342
|
+
if (item.frame) {
|
|
343
|
+
return 'envelope';
|
|
344
|
+
}
|
|
345
|
+
return 'unknown';
|
|
346
|
+
}
|
|
347
|
+
logInboxSnapshot(event, extra = {}) {
|
|
348
|
+
logger.debug(event, {
|
|
349
|
+
channel: this.channelName,
|
|
350
|
+
connector_id: this.connectorId,
|
|
351
|
+
connector_state: this.state,
|
|
352
|
+
inbox_capacity: this.inboxCapacity,
|
|
353
|
+
inbox_remaining_capacity: this.inbox.remainingCapacity,
|
|
354
|
+
...extra,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
397
357
|
_shouldSkipDuplicateAck(senderId, payload) {
|
|
398
358
|
const dedupKey = this._extractAckDedupKey(payload);
|
|
399
359
|
if (!dedupKey) {
|
|
@@ -492,24 +452,6 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
|
|
|
492
452
|
});
|
|
493
453
|
}
|
|
494
454
|
}
|
|
495
|
-
/**
|
|
496
|
-
* Update the remote node ID after learning it from NodeAttachAck
|
|
497
|
-
* This allows upstream connectors to switch from wildcard to specific addressing
|
|
498
|
-
*/
|
|
499
|
-
updateRemoteNodeId(newRemoteNodeId) {
|
|
500
|
-
if (typeof newRemoteNodeId !== 'string' || newRemoteNodeId.trim().length === 0) {
|
|
501
|
-
throw new Error('Invalid remote node ID');
|
|
502
|
-
}
|
|
503
|
-
const oldValue = this.remoteNodeId;
|
|
504
|
-
this.remoteNodeId = newRemoteNodeId.trim();
|
|
505
|
-
logger.debug('broadcast_channel_connector_remote_node_id_updated', {
|
|
506
|
-
channel: this.channelName,
|
|
507
|
-
connector_id: this.connectorId,
|
|
508
|
-
local_node_id: this.localNodeId,
|
|
509
|
-
old_remote_node_id: oldValue,
|
|
510
|
-
new_remote_node_id: this.remoteNodeId,
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
455
|
_trimSeenAcks(now) {
|
|
514
456
|
while (this.seenAckOrder.length > 0) {
|
|
515
457
|
const candidate = this.seenAckOrder[0];
|
|
@@ -11,7 +11,6 @@ const broadcast_channel_connector_js_1 = require("./broadcast-channel-connector.
|
|
|
11
11
|
const grant_selection_policy_js_1 = require("./grant-selection-policy.js");
|
|
12
12
|
const bounded_async_queue_js_1 = require("../util/bounded-async-queue.js");
|
|
13
13
|
const broadcast_channel_connection_grant_js_1 = require("../grants/broadcast-channel-connection-grant.js");
|
|
14
|
-
const transport_frame_js_1 = require("./transport-frame.js");
|
|
15
14
|
const logger = (0, logging_js_1.getLogger)('naylence.fame.connector.broadcast_channel_listener');
|
|
16
15
|
const DEFAULT_CHANNEL = 'naylence-fabric';
|
|
17
16
|
const DEFAULT_INBOX_CAPACITY = 2048;
|
|
@@ -143,18 +142,13 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
|
|
|
143
142
|
});
|
|
144
143
|
}
|
|
145
144
|
asCallbackGrant() {
|
|
146
|
-
|
|
145
|
+
return this.withLegacySnakeCaseKeys({
|
|
147
146
|
type: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
|
|
148
147
|
connectorType: broadcast_channel_connector_js_1.BROADCAST_CHANNEL_CONNECTOR_TYPE,
|
|
149
148
|
connectionGrantType: broadcast_channel_connection_grant_js_1.BROADCAST_CHANNEL_CONNECTION_GRANT_TYPE,
|
|
150
149
|
channelName: this._channelName,
|
|
151
150
|
inboxCapacity: this._inboxCapacity,
|
|
152
|
-
};
|
|
153
|
-
// Include localNodeId for transport frame multiplexing if node is available
|
|
154
|
-
if (this._routingNode) {
|
|
155
|
-
grant.localNodeId = this._routingNode.id;
|
|
156
|
-
}
|
|
157
|
-
return this.withLegacySnakeCaseKeys(grant);
|
|
151
|
+
});
|
|
158
152
|
}
|
|
159
153
|
_registerChannelListener() {
|
|
160
154
|
if (this._channelHandler) {
|
|
@@ -214,54 +208,23 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
|
|
|
214
208
|
if (typeof senderId !== 'string' || senderId.length === 0) {
|
|
215
209
|
return null;
|
|
216
210
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
// Apply listener's filtering policy: accept frames addressed to us OR with wildcard destination
|
|
224
|
-
// Wildcard is needed because downstream nodes don't know the sentinel's ID during initial attach
|
|
225
|
-
const isAddressedToUs = frame.dst === this._routingNode.id || frame.dst === '*';
|
|
226
|
-
if (isAddressedToUs) {
|
|
227
|
-
envelopePayload = frame.payload;
|
|
228
|
-
logger.debug('broadcast_channel_listener_unwrapped_transport_frame', {
|
|
229
|
-
sender_id: senderId,
|
|
230
|
-
src: frame.src,
|
|
231
|
-
dst: frame.dst,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
// Frame addressed to a different node, ignore it
|
|
236
|
-
logger.debug('broadcast_channel_listener_ignored_frame_wrong_destination', {
|
|
237
|
-
sender_id: senderId,
|
|
238
|
-
dst: frame.dst,
|
|
239
|
-
expected: this._routingNode.id,
|
|
240
|
-
});
|
|
241
|
-
return null;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
// If not a transport frame, try to coerce as legacy format
|
|
246
|
-
if (!envelopePayload) {
|
|
247
|
-
envelopePayload = coercePayload(record.payload);
|
|
248
|
-
if (!envelopePayload) {
|
|
249
|
-
logger.debug('broadcast_channel_listener_ignored_event_without_payload', {
|
|
250
|
-
sender_id: senderId,
|
|
251
|
-
});
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
211
|
+
const payload = coercePayload(record.payload);
|
|
212
|
+
if (!payload) {
|
|
213
|
+
logger.debug('broadcast_channel_listener_ignored_event_without_payload', {
|
|
214
|
+
sender_id: senderId,
|
|
215
|
+
});
|
|
216
|
+
return null;
|
|
254
217
|
}
|
|
255
218
|
let envelope;
|
|
256
219
|
try {
|
|
257
|
-
const decoded = new TextDecoder().decode(
|
|
220
|
+
const decoded = new TextDecoder().decode(payload);
|
|
258
221
|
const parsed = JSON.parse(decoded);
|
|
259
222
|
envelope = (0, core_1.deserializeEnvelope)(parsed);
|
|
260
223
|
}
|
|
261
224
|
catch (error) {
|
|
262
225
|
const decoded = (() => {
|
|
263
226
|
try {
|
|
264
|
-
return new TextDecoder().decode(
|
|
227
|
+
return new TextDecoder().decode(payload);
|
|
265
228
|
}
|
|
266
229
|
catch {
|
|
267
230
|
return null;
|
|
@@ -381,20 +344,6 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
|
|
|
381
344
|
inboxCapacity: this._inboxCapacity,
|
|
382
345
|
};
|
|
383
346
|
}
|
|
384
|
-
// Automatically configure transport frame multiplexing:
|
|
385
|
-
// Use node IDs (not connector IDs) for node-to-node targeting
|
|
386
|
-
const broadcastConfig = connectorConfig;
|
|
387
|
-
// Always force localNodeId to be this listener's node ID
|
|
388
|
-
// This ensures the sentinel sets localNodeId=sentinel, not the child's ID
|
|
389
|
-
broadcastConfig.localNodeId = routingNode.id;
|
|
390
|
-
// Always force remoteNodeId to be the attaching child's system ID
|
|
391
|
-
broadcastConfig.remoteNodeId = systemId;
|
|
392
|
-
logger.debug('broadcast_channel_listener_configured_node_ids', {
|
|
393
|
-
sender_id: params.senderId,
|
|
394
|
-
system_id: systemId,
|
|
395
|
-
local_node_id: broadcastConfig.localNodeId,
|
|
396
|
-
remote_node_id: broadcastConfig.remoteNodeId,
|
|
397
|
-
});
|
|
398
347
|
try {
|
|
399
348
|
const connector = await routingNode.createOriginConnector({
|
|
400
349
|
originType,
|
|
@@ -464,21 +413,6 @@ class BroadcastChannelListener extends transport_listener_js_1.TransportListener
|
|
|
464
413
|
inboxCandidate > 0) {
|
|
465
414
|
config.inboxCapacity = Math.floor(inboxCandidate);
|
|
466
415
|
}
|
|
467
|
-
// Extract transport frame multiplexing node IDs
|
|
468
|
-
const localNodeIdCandidate = candidate.localNodeId ?? candidate['local_node_id'];
|
|
469
|
-
if (typeof localNodeIdCandidate === 'string' && localNodeIdCandidate.trim().length > 0) {
|
|
470
|
-
config.localNodeId = localNodeIdCandidate.trim();
|
|
471
|
-
logger.debug('broadcast_channel_listener_extracted_local_node_id', {
|
|
472
|
-
local_node_id: config.localNodeId,
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
const remoteNodeIdCandidate = candidate.remoteNodeId ?? candidate['remote_node_id'];
|
|
476
|
-
if (typeof remoteNodeIdCandidate === 'string' && remoteNodeIdCandidate.trim().length > 0) {
|
|
477
|
-
config.remoteNodeId = remoteNodeIdCandidate.trim();
|
|
478
|
-
logger.debug('broadcast_channel_listener_extracted_remote_node_id', {
|
|
479
|
-
remote_node_id: config.remoteNodeId,
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
416
|
return config;
|
|
483
417
|
}
|
|
484
418
|
_monitorConnectorLifecycle(senderId, systemId, connector) {
|
|
@@ -84,8 +84,6 @@ class InPageConnectorFactory extends connector_factory_js_1.ConnectorFactory {
|
|
|
84
84
|
type: inpage_connector_js_1.INPAGE_CONNECTOR_TYPE,
|
|
85
85
|
channelName,
|
|
86
86
|
inboxCapacity,
|
|
87
|
-
localNodeId: normalized.localNodeId,
|
|
88
|
-
remoteNodeId: normalized.remoteNodeId,
|
|
89
87
|
};
|
|
90
88
|
const connector = new inpage_connector_js_1.InPageConnector(connectorConfig, baseConfig);
|
|
91
89
|
if (options.authorization) {
|
|
@@ -154,16 +152,6 @@ class InPageConnectorFactory extends connector_factory_js_1.ConnectorFactory {
|
|
|
154
152
|
if (candidate.authorizationContext !== undefined) {
|
|
155
153
|
normalized.authorizationContext = candidate.authorizationContext;
|
|
156
154
|
}
|
|
157
|
-
// Handle localNodeId
|
|
158
|
-
const localNodeId = candidate.localNodeId ?? candidate['local_node_id'];
|
|
159
|
-
if (typeof localNodeId === 'string' && localNodeId.trim().length > 0) {
|
|
160
|
-
normalized.localNodeId = localNodeId.trim();
|
|
161
|
-
}
|
|
162
|
-
// Handle remoteNodeId
|
|
163
|
-
const remoteNodeId = candidate.remoteNodeId ?? candidate['remote_node_id'];
|
|
164
|
-
if (typeof remoteNodeId === 'string' && remoteNodeId.trim().length > 0) {
|
|
165
|
-
normalized.remoteNodeId = remoteNodeId.trim();
|
|
166
|
-
}
|
|
167
155
|
normalized.channelName = normalized.channelName ?? DEFAULT_CHANNEL;
|
|
168
156
|
normalized.inboxCapacity =
|
|
169
157
|
normalized.inboxCapacity ?? DEFAULT_INBOX_CAPACITY;
|
|
@@ -10,7 +10,6 @@ const errors_js_1 = require("../errors/errors.js");
|
|
|
10
10
|
const logging_js_1 = require("../util/logging.js");
|
|
11
11
|
const bounded_async_queue_js_1 = require("../util/bounded-async-queue.js");
|
|
12
12
|
const core_1 = require("@naylence/core");
|
|
13
|
-
const transport_frame_js_1 = require("./transport-frame.js");
|
|
14
13
|
const logger = (0, logging_js_1.getLogger)('naylence.fame.connector.inpage_connector');
|
|
15
14
|
exports.INPAGE_CONNECTOR_TYPE = 'inpage-connector';
|
|
16
15
|
const DEFAULT_CHANNEL = 'naylence-fabric';
|
|
@@ -81,20 +80,9 @@ class InPageConnector extends base_async_connector_js_1.BaseAsyncConnector {
|
|
|
81
80
|
: DEFAULT_INBOX_CAPACITY;
|
|
82
81
|
this.inbox = new bounded_async_queue_js_1.BoundedAsyncQueue(preferredCapacity);
|
|
83
82
|
this.connectorId = InPageConnector.generateConnectorId();
|
|
84
|
-
// Set local and remote node IDs (defaults to connector ID for backwards compatibility)
|
|
85
|
-
this.localNodeId =
|
|
86
|
-
typeof config.localNodeId === 'string' && config.localNodeId.trim().length > 0
|
|
87
|
-
? config.localNodeId.trim()
|
|
88
|
-
: this.connectorId;
|
|
89
|
-
this.remoteNodeId =
|
|
90
|
-
typeof config.remoteNodeId === 'string' && config.remoteNodeId.trim().length > 0
|
|
91
|
-
? config.remoteNodeId.trim()
|
|
92
|
-
: '*'; // Accept from any remote if not specified
|
|
93
83
|
logger.debug('inpage_connector_initialized', {
|
|
94
84
|
channel: this.channelName,
|
|
95
85
|
connector_id: this.connectorId,
|
|
96
|
-
local_node_id: this.localNodeId,
|
|
97
|
-
remote_node_id: this.remoteNodeId,
|
|
98
86
|
});
|
|
99
87
|
this.onMsg = (event) => {
|
|
100
88
|
const messageEvent = event;
|
|
@@ -128,64 +116,6 @@ class InPageConnector extends base_async_connector_js_1.BaseAsyncConnector {
|
|
|
128
116
|
if (busMessage.senderId === this.connectorId) {
|
|
129
117
|
return;
|
|
130
118
|
}
|
|
131
|
-
// Try to unwrap as transport frame
|
|
132
|
-
const frame = (0, transport_frame_js_1.unwrapTransportFrame)(busMessage.payload);
|
|
133
|
-
if (frame) {
|
|
134
|
-
// Apply connector's filtering policy: strict dst check, src accepts wildcard
|
|
135
|
-
const srcMatches = this.remoteNodeId === '*' || frame.src === this.remoteNodeId;
|
|
136
|
-
const dstMatches = frame.dst === this.localNodeId;
|
|
137
|
-
if (dstMatches && srcMatches) {
|
|
138
|
-
// Successfully received and filtered transport frame
|
|
139
|
-
logger.debug('inpage_transport_frame_received', {
|
|
140
|
-
channel: this.channelName,
|
|
141
|
-
sender_id: busMessage.senderId,
|
|
142
|
-
connector_id: this.connectorId,
|
|
143
|
-
local_node_id: this.localNodeId,
|
|
144
|
-
remote_node_id: this.remoteNodeId,
|
|
145
|
-
frame_src: frame.src,
|
|
146
|
-
frame_dst: frame.dst,
|
|
147
|
-
payload_length: frame.payload.byteLength,
|
|
148
|
-
});
|
|
149
|
-
const unwrapped = frame.payload;
|
|
150
|
-
try {
|
|
151
|
-
if (typeof this.inbox.tryEnqueue === 'function') {
|
|
152
|
-
const accepted = this.inbox.tryEnqueue(unwrapped);
|
|
153
|
-
if (accepted) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
this.inbox.enqueue(unwrapped);
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
if (error instanceof bounded_async_queue_js_1.QueueFullError) {
|
|
161
|
-
logger.warning('inpage_receive_queue_full', {
|
|
162
|
-
channel: this.channelName,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
logger.error('inpage_receive_error', {
|
|
167
|
-
channel: this.channelName,
|
|
168
|
-
error: error instanceof Error ? error.message : String(error),
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
// Frame filtered out by addressing rules
|
|
176
|
-
logger.debug('inpage_transport_frame_filtered', {
|
|
177
|
-
channel: this.channelName,
|
|
178
|
-
connector_id: this.connectorId,
|
|
179
|
-
local_node_id: this.localNodeId,
|
|
180
|
-
remote_node_id: this.remoteNodeId,
|
|
181
|
-
frame_src: frame.src,
|
|
182
|
-
frame_dst: frame.dst,
|
|
183
|
-
reason: !dstMatches ? 'wrong_destination' : 'wrong_source',
|
|
184
|
-
});
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// Fall back to legacy format (no transport frame)
|
|
189
119
|
const payload = InPageConnector.coercePayload(busMessage.payload);
|
|
190
120
|
if (!payload) {
|
|
191
121
|
logger.debug('inpage_payload_rejected', {
|
|
@@ -344,27 +274,11 @@ class InPageConnector extends base_async_connector_js_1.BaseAsyncConnector {
|
|
|
344
274
|
logger.debug('inpage_message_sending', {
|
|
345
275
|
channel: this.channelName,
|
|
346
276
|
sender_id: this.connectorId,
|
|
347
|
-
local_node_id: this.localNodeId,
|
|
348
|
-
remote_node_id: this.remoteNodeId,
|
|
349
277
|
});
|
|
350
|
-
// Only use transport framing if both localNodeId and remoteNodeId are explicitly set
|
|
351
|
-
// (not using default values). This ensures backwards compatibility.
|
|
352
|
-
const useTransportFrame = this.localNodeId !== this.connectorId ||
|
|
353
|
-
this.remoteNodeId !== '*';
|
|
354
|
-
let payload;
|
|
355
|
-
if (useTransportFrame) {
|
|
356
|
-
// Wrap payload in transport frame
|
|
357
|
-
const frame = (0, transport_frame_js_1.wrapTransportFrame)(data, this.localNodeId, this.remoteNodeId);
|
|
358
|
-
payload = (0, transport_frame_js_1.serializeTransportFrame)(frame);
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
// Legacy format: send raw payload
|
|
362
|
-
payload = data;
|
|
363
|
-
}
|
|
364
278
|
const event = new MessageEvent(this.channelName, {
|
|
365
279
|
data: {
|
|
366
280
|
senderId: this.connectorId,
|
|
367
|
-
payload,
|
|
281
|
+
payload: data,
|
|
368
282
|
},
|
|
369
283
|
});
|
|
370
284
|
getSharedBus().dispatchEvent(event);
|
|
@@ -393,23 +307,5 @@ class InPageConnector extends base_async_connector_js_1.BaseAsyncConnector {
|
|
|
393
307
|
}
|
|
394
308
|
return rawOrEnvelope;
|
|
395
309
|
}
|
|
396
|
-
/**
|
|
397
|
-
* Update the remote node ID after learning it from NodeAttachAck
|
|
398
|
-
* This allows upstream connectors to switch from wildcard to specific addressing
|
|
399
|
-
*/
|
|
400
|
-
updateRemoteNodeId(newRemoteNodeId) {
|
|
401
|
-
if (typeof newRemoteNodeId !== 'string' || newRemoteNodeId.trim().length === 0) {
|
|
402
|
-
throw new Error('Invalid remote node ID');
|
|
403
|
-
}
|
|
404
|
-
const oldValue = this.remoteNodeId;
|
|
405
|
-
this.remoteNodeId = newRemoteNodeId.trim();
|
|
406
|
-
logger.debug('inpage_connector_remote_node_id_updated', {
|
|
407
|
-
channel: this.channelName,
|
|
408
|
-
connector_id: this.connectorId,
|
|
409
|
-
local_node_id: this.localNodeId,
|
|
410
|
-
old_remote_node_id: oldValue,
|
|
411
|
-
new_remote_node_id: this.remoteNodeId,
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
310
|
}
|
|
415
311
|
exports.InPageConnector = InPageConnector;
|