@naylence/runtime 0.3.6 → 0.3.7
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 +220 -172
- package/dist/browser/index.mjs +220 -173
- package/dist/cjs/_env-shim.js +2 -1
- package/dist/cjs/naylence/fame/config/extended-fame-config.js +2 -1
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector-factory.js +12 -4
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +14 -6
- package/dist/cjs/naylence/fame/connector/broadcast-channel-listener-factory.js +3 -1
- package/dist/cjs/naylence/fame/connector/broadcast-channel-listener.js +11 -7
- package/dist/cjs/naylence/fame/connector/index.js +2 -1
- package/dist/cjs/naylence/fame/connector/inpage-connector-factory.js +2 -1
- package/dist/cjs/naylence/fame/connector/inpage-connector.js +13 -6
- package/dist/cjs/naylence/fame/connector/inpage-listener-factory.js +4 -4
- package/dist/cjs/naylence/fame/connector/inpage-listener.js +5 -2
- package/dist/cjs/naylence/fame/delivery/default-delivery-tracker.js +4 -62
- package/dist/cjs/naylence/fame/grants/broadcast-channel-connection-grant.js +6 -3
- package/dist/cjs/naylence/fame/grants/inpage-connection-grant.js +4 -2
- package/dist/cjs/naylence/fame/http/oauth2-token-router.js +9 -9
- package/dist/cjs/naylence/fame/node/admission/default-node-attach-client.js +34 -5
- package/dist/cjs/naylence/fame/node/upstream-session-manager.js +40 -8
- package/dist/cjs/naylence/fame/security/auth/oauth2-pkce-token-provider-factory.js +10 -6
- package/dist/cjs/naylence/fame/security/auth/oauth2-pkce-token-provider.js +1 -2
- package/dist/cjs/naylence/fame/security/default-security-manager.js +2 -1
- package/dist/cjs/naylence/fame/security/trust-store/noop-trust-store-provider-factory.js +2 -2
- package/dist/cjs/naylence/fame/security/trust-store/trust-store-provider-factory.js +2 -2
- package/dist/cjs/naylence/fame/util/index.js +3 -1
- package/dist/cjs/node.js +4 -1
- package/dist/cjs/version.js +2 -2
- package/dist/esm/_env-shim.js +2 -1
- package/dist/esm/browser.js +2 -2
- package/dist/esm/naylence/fame/config/extended-fame-config.js +2 -1
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector-factory.js +12 -4
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +14 -6
- package/dist/esm/naylence/fame/connector/broadcast-channel-listener-factory.js +3 -1
- package/dist/esm/naylence/fame/connector/broadcast-channel-listener.js +11 -7
- package/dist/esm/naylence/fame/connector/index.js +2 -2
- package/dist/esm/naylence/fame/connector/inpage-connector-factory.js +2 -1
- package/dist/esm/naylence/fame/connector/inpage-connector.js +13 -6
- package/dist/esm/naylence/fame/connector/inpage-listener-factory.js +4 -4
- package/dist/esm/naylence/fame/connector/inpage-listener.js +5 -2
- package/dist/esm/naylence/fame/delivery/default-delivery-tracker.js +4 -62
- package/dist/esm/naylence/fame/grants/broadcast-channel-connection-grant.js +6 -3
- package/dist/esm/naylence/fame/grants/inpage-connection-grant.js +4 -2
- package/dist/esm/naylence/fame/http/oauth2-token-router.js +9 -9
- package/dist/esm/naylence/fame/node/admission/default-node-attach-client.js +34 -5
- package/dist/esm/naylence/fame/node/upstream-session-manager.js +40 -8
- package/dist/esm/naylence/fame/security/auth/oauth2-pkce-token-provider-factory.js +10 -6
- package/dist/esm/naylence/fame/security/auth/oauth2-pkce-token-provider.js +1 -2
- package/dist/esm/naylence/fame/security/default-security-manager.js +2 -1
- package/dist/esm/naylence/fame/security/index.js +1 -1
- package/dist/esm/naylence/fame/security/trust-store/noop-trust-store-provider-factory.js +3 -3
- package/dist/esm/naylence/fame/security/trust-store/trust-store-provider-factory.js +3 -3
- package/dist/esm/naylence/fame/util/index.js +1 -0
- package/dist/esm/node.js +2 -1
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +218 -171
- package/dist/node/index.mjs +218 -172
- package/dist/node/node.cjs +184 -136
- package/dist/node/node.mjs +183 -137
- package/dist/types/browser.d.ts +2 -2
- package/dist/types/naylence/fame/connector/broadcast-channel-connector-factory.d.ts +1 -0
- package/dist/types/naylence/fame/connector/index.d.ts +3 -3
- package/dist/types/naylence/fame/delivery/default-delivery-tracker.d.ts +0 -6
- package/dist/types/naylence/fame/node/admission/default-node-attach-client.d.ts +1 -0
- package/dist/types/naylence/fame/security/index.d.ts +1 -1
- package/dist/types/naylence/fame/security/trust-store/noop-trust-store-provider-factory.d.ts +3 -3
- package/dist/types/naylence/fame/security/trust-store/trust-store-provider-factory.d.ts +4 -4
- package/dist/types/naylence/fame/security/trust-store/trust-store-provider.d.ts +5 -5
- package/dist/types/naylence/fame/util/index.d.ts +1 -0
- package/dist/types/node.d.ts +2 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -146,9 +146,6 @@ export class DefaultDeliveryTracker extends TaskSpawner {
|
|
|
146
146
|
this.ackDoneSince = new Map();
|
|
147
147
|
this.replyDoneSince = new Map();
|
|
148
148
|
this.pendingAckDispatches = new Set();
|
|
149
|
-
this.recentlyHandled = new Map();
|
|
150
|
-
this.recentlyHandledOrder = [];
|
|
151
|
-
this.recentlyHandledTtlMs = 60000;
|
|
152
149
|
this.isPreparingToStop = false;
|
|
153
150
|
this.shutdownRequestedAtMs = null;
|
|
154
151
|
this.shutdownRetryGraceMs = 1000;
|
|
@@ -384,22 +381,6 @@ export class DefaultDeliveryTracker extends TaskSpawner {
|
|
|
384
381
|
}
|
|
385
382
|
}
|
|
386
383
|
else {
|
|
387
|
-
const wasRecentlyHandled = await this.lock.runExclusive(async () => this.wasRecentlyHandled(envelope.id));
|
|
388
|
-
if (wasRecentlyHandled) {
|
|
389
|
-
logger.debug('tracker_duplicate_envelope_recently_handled', {
|
|
390
|
-
envp_id: envelope.id,
|
|
391
|
-
});
|
|
392
|
-
return new TrackedEnvelope({
|
|
393
|
-
timeoutAtMs: 0,
|
|
394
|
-
overallTimeoutAtMs: 0,
|
|
395
|
-
expectedResponseType: envelope.rtype ?? FameResponseType.NONE,
|
|
396
|
-
createdAtMs: Date.now(),
|
|
397
|
-
status: EnvelopeStatus.HANDLED,
|
|
398
|
-
mailboxType: MailboxType.INBOX,
|
|
399
|
-
originalEnvelope: envelope,
|
|
400
|
-
serviceName: inboxName,
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
384
|
tracked = new TrackedEnvelope({
|
|
404
385
|
timeoutAtMs: 0,
|
|
405
386
|
overallTimeoutAtMs: 0,
|
|
@@ -421,12 +402,8 @@ export class DefaultDeliveryTracker extends TaskSpawner {
|
|
|
421
402
|
async onEnvelopeHandled(envelope) {
|
|
422
403
|
const inbox = this.ensureInbox();
|
|
423
404
|
envelope.status = EnvelopeStatus.HANDLED;
|
|
405
|
+
// Delete the envelope from inbox to prevent growth
|
|
424
406
|
await inbox.delete(envelope.originalEnvelope.id);
|
|
425
|
-
await this.lock.runExclusive(async () => {
|
|
426
|
-
this.markRecentlyHandled(envelope.originalEnvelope.id);
|
|
427
|
-
});
|
|
428
|
-
// Preserve handled envelope to prevent duplicate redelivery during shutdown drains.
|
|
429
|
-
// await inbox.set(envelope.originalEnvelope.id, envelope);
|
|
430
407
|
}
|
|
431
408
|
async onEnvelopeHandleFailed(inboxName, envelope, context, error, isFinalFailure = false) {
|
|
432
409
|
void context;
|
|
@@ -645,9 +622,9 @@ export class DefaultDeliveryTracker extends TaskSpawner {
|
|
|
645
622
|
});
|
|
646
623
|
await this.markDoneSince(this.replyFutures, trackedEnvelope.originalEnvelope.id, this.replyDoneSince);
|
|
647
624
|
await this.markDoneSince(this.ackFutures, trackedEnvelope.originalEnvelope.id, this.ackDoneSince);
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
625
|
+
// Note: ACK is already sent in onCorrelatedMessage (lines 655-657)
|
|
626
|
+
// when the reply envelope is first delivered. No need to send it again here.
|
|
627
|
+
// Removing this duplicate sendAck call fixes the duplicate DeliveryAck bug.
|
|
651
628
|
for (const handler of this.eventHandlers) {
|
|
652
629
|
await handler.onEnvelopeReplied?.(trackedEnvelope, envelope);
|
|
653
630
|
}
|
|
@@ -797,8 +774,6 @@ export class DefaultDeliveryTracker extends TaskSpawner {
|
|
|
797
774
|
}
|
|
798
775
|
this.streamDone.clear();
|
|
799
776
|
this.correlationToEnvelope.clear();
|
|
800
|
-
this.recentlyHandled.clear();
|
|
801
|
-
this.recentlyHandledOrder.length = 0;
|
|
802
777
|
return values;
|
|
803
778
|
});
|
|
804
779
|
for (const timer of timers) {
|
|
@@ -1380,39 +1355,6 @@ export class DefaultDeliveryTracker extends TaskSpawner {
|
|
|
1380
1355
|
this.pendingAckDispatches.delete(ackDispatch);
|
|
1381
1356
|
}
|
|
1382
1357
|
}
|
|
1383
|
-
markRecentlyHandled(envelopeId) {
|
|
1384
|
-
const now = Date.now();
|
|
1385
|
-
this.recentlyHandled.set(envelopeId, now);
|
|
1386
|
-
this.recentlyHandledOrder.push(envelopeId);
|
|
1387
|
-
this.trimRecentlyHandled(now);
|
|
1388
|
-
}
|
|
1389
|
-
wasRecentlyHandled(envelopeId) {
|
|
1390
|
-
const now = Date.now();
|
|
1391
|
-
const timestamp = this.recentlyHandled.get(envelopeId);
|
|
1392
|
-
if (timestamp === undefined) {
|
|
1393
|
-
return false;
|
|
1394
|
-
}
|
|
1395
|
-
if (now - timestamp > this.recentlyHandledTtlMs) {
|
|
1396
|
-
this.recentlyHandled.delete(envelopeId);
|
|
1397
|
-
return false;
|
|
1398
|
-
}
|
|
1399
|
-
return true;
|
|
1400
|
-
}
|
|
1401
|
-
trimRecentlyHandled(now) {
|
|
1402
|
-
while (this.recentlyHandledOrder.length > 0) {
|
|
1403
|
-
const candidate = this.recentlyHandledOrder[0];
|
|
1404
|
-
const timestamp = this.recentlyHandled.get(candidate);
|
|
1405
|
-
if (timestamp === undefined) {
|
|
1406
|
-
this.recentlyHandledOrder.shift();
|
|
1407
|
-
continue;
|
|
1408
|
-
}
|
|
1409
|
-
if (now - timestamp <= this.recentlyHandledTtlMs) {
|
|
1410
|
-
break;
|
|
1411
|
-
}
|
|
1412
|
-
this.recentlyHandled.delete(candidate);
|
|
1413
|
-
this.recentlyHandledOrder.shift();
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
1358
|
getShutdownRetryDeferDelay(nowMs) {
|
|
1417
1359
|
if (!this.isPreparingToStop || this.shutdownRequestedAtMs === null) {
|
|
1418
1360
|
return null;
|
|
@@ -41,14 +41,16 @@ export function normalizeBroadcastChannelConnectionGrant(candidate) {
|
|
|
41
41
|
type,
|
|
42
42
|
purpose,
|
|
43
43
|
};
|
|
44
|
-
const channelValue = candidate.channelName ??
|
|
44
|
+
const channelValue = candidate.channelName ??
|
|
45
|
+
candidate['channel_name'];
|
|
45
46
|
if (channelValue !== undefined) {
|
|
46
47
|
if (typeof channelValue !== 'string' || channelValue.trim().length === 0) {
|
|
47
48
|
throw new TypeError('BroadcastChannelConnectionGrant "channelName" must be a non-empty string when provided');
|
|
48
49
|
}
|
|
49
50
|
result.channelName = channelValue.trim();
|
|
50
51
|
}
|
|
51
|
-
const inboxValue = candidate.inboxCapacity ??
|
|
52
|
+
const inboxValue = candidate.inboxCapacity ??
|
|
53
|
+
candidate['inbox_capacity'];
|
|
52
54
|
if (inboxValue !== undefined) {
|
|
53
55
|
if (typeof inboxValue !== 'number' ||
|
|
54
56
|
!Number.isFinite(inboxValue) ||
|
|
@@ -57,7 +59,8 @@ export function normalizeBroadcastChannelConnectionGrant(candidate) {
|
|
|
57
59
|
}
|
|
58
60
|
result.inboxCapacity = Math.floor(inboxValue);
|
|
59
61
|
}
|
|
60
|
-
const windowValue = candidate.initialWindow ??
|
|
62
|
+
const windowValue = candidate.initialWindow ??
|
|
63
|
+
candidate['initial_window'];
|
|
61
64
|
if (windowValue !== undefined) {
|
|
62
65
|
if (typeof windowValue !== 'number' ||
|
|
63
66
|
!Number.isFinite(windowValue) ||
|
|
@@ -36,14 +36,16 @@ export function normalizeInPageConnectionGrant(candidate) {
|
|
|
36
36
|
type,
|
|
37
37
|
purpose,
|
|
38
38
|
};
|
|
39
|
-
const channelValue = candidate.channelName ??
|
|
39
|
+
const channelValue = candidate.channelName ??
|
|
40
|
+
candidate['channel_name'];
|
|
40
41
|
if (channelValue !== undefined) {
|
|
41
42
|
if (typeof channelValue !== 'string' || channelValue.trim().length === 0) {
|
|
42
43
|
throw new TypeError('InPageConnectionGrant "channelName" must be a non-empty string when provided');
|
|
43
44
|
}
|
|
44
45
|
result.channelName = channelValue.trim();
|
|
45
46
|
}
|
|
46
|
-
const inboxValue = candidate.inboxCapacity ??
|
|
47
|
+
const inboxValue = candidate.inboxCapacity ??
|
|
48
|
+
candidate['inbox_capacity'];
|
|
47
49
|
if (inboxValue !== undefined) {
|
|
48
50
|
if (typeof inboxValue !== 'number' ||
|
|
49
51
|
!Number.isFinite(inboxValue) ||
|
|
@@ -419,7 +419,9 @@ function parseCookies(cookieHeader) {
|
|
|
419
419
|
if (!cookieHeader) {
|
|
420
420
|
return {};
|
|
421
421
|
}
|
|
422
|
-
return cookieHeader
|
|
422
|
+
return cookieHeader
|
|
423
|
+
.split(';')
|
|
424
|
+
.reduce((acc, entry) => {
|
|
423
425
|
const [rawName, ...rawValueParts] = entry.split('=');
|
|
424
426
|
const name = rawName?.trim();
|
|
425
427
|
if (!name) {
|
|
@@ -567,10 +569,7 @@ function setNoCacheHeaders(res) {
|
|
|
567
569
|
res.set('Pragma', 'no-cache');
|
|
568
570
|
}
|
|
569
571
|
function respondInvalidClient(res) {
|
|
570
|
-
res
|
|
571
|
-
.status(401)
|
|
572
|
-
.set('WWW-Authenticate', 'Basic')
|
|
573
|
-
.json({
|
|
572
|
+
res.status(401).set('WWW-Authenticate', 'Basic').json({
|
|
574
573
|
error: 'invalid_client',
|
|
575
574
|
error_description: 'Invalid client credentials',
|
|
576
575
|
});
|
|
@@ -607,10 +606,10 @@ export function createOAuth2TokenRouter(options) {
|
|
|
607
606
|
DEFAULT_JWT_ALGORITHM;
|
|
608
607
|
const allowedScopes = getAllowedScopes(configAllowedScopes);
|
|
609
608
|
const resolvedTokenTtlSec = tokenTtlSec ?? 3600;
|
|
610
|
-
const enablePkce = coerceBoolean(process.env[ENV_VAR_ENABLE_PKCE]) ??
|
|
611
|
-
(configEnablePkce ?? true);
|
|
609
|
+
const enablePkce = coerceBoolean(process.env[ENV_VAR_ENABLE_PKCE]) ?? configEnablePkce ?? true;
|
|
612
610
|
const allowPublicClients = coerceBoolean(process.env[ENV_VAR_ALLOW_PUBLIC_CLIENTS]) ??
|
|
613
|
-
|
|
611
|
+
configAllowPublicClients ??
|
|
612
|
+
true;
|
|
614
613
|
const authorizationCodeTtlSec = ensurePositiveInteger(coerceNumber(process.env[ENV_VAR_AUTHORIZATION_CODE_TTL]) ??
|
|
615
614
|
configAuthorizationCodeTtlSec) ?? DEFAULT_AUTHORIZATION_CODE_TTL_SEC;
|
|
616
615
|
const devLoginExplicitlyEnabled = coerceBoolean(process.env[ENV_VAR_ENABLE_DEV_LOGIN]) ??
|
|
@@ -625,7 +624,8 @@ export function createOAuth2TokenRouter(options) {
|
|
|
625
624
|
configDevLoginCookieName ??
|
|
626
625
|
DEFAULT_SESSION_COOKIE_NAME;
|
|
627
626
|
const devLoginSecureCookie = coerceBoolean(process.env[ENV_VAR_SESSION_SECURE_COOKIE]) ??
|
|
628
|
-
|
|
627
|
+
configDevLoginSecureCookie ??
|
|
628
|
+
false;
|
|
629
629
|
const devLoginTitle = coerceString(process.env[ENV_VAR_LOGIN_TITLE]) ??
|
|
630
630
|
configDevLoginTitle ??
|
|
631
631
|
DEFAULT_LOGIN_TITLE;
|
|
@@ -8,15 +8,30 @@ export class DefaultNodeAttachClient {
|
|
|
8
8
|
constructor(options = {}) {
|
|
9
9
|
this.buffer = [];
|
|
10
10
|
this.inHandshake = false;
|
|
11
|
+
this.expectedSystemId = null;
|
|
11
12
|
this.timeoutMs = options.timeoutMs ?? 10000;
|
|
12
13
|
this.attachmentKeyValidator = options.attachmentKeyValidator;
|
|
13
14
|
this.replicaStickinessManager = options.replicaStickinessManager ?? null;
|
|
14
15
|
}
|
|
15
16
|
async attach(node, originType, connector, welcomeFrame, finalHandler, keys, callbackGrants) {
|
|
16
17
|
this.inHandshake = true;
|
|
18
|
+
this.expectedSystemId = welcomeFrame.systemId;
|
|
17
19
|
const interimHandler = async (envelope, context) => {
|
|
18
20
|
if (this.inHandshake) {
|
|
19
|
-
|
|
21
|
+
// Filter: only buffer frames related to our systemId or frames without systemId info
|
|
22
|
+
const frameSystemId = envelope.frame
|
|
23
|
+
?.systemId;
|
|
24
|
+
if (!frameSystemId || frameSystemId === this.expectedSystemId) {
|
|
25
|
+
this.buffer.push(envelope);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Silently ignore frames from other agents during concurrent handshakes
|
|
29
|
+
logger.debug('handshake_ignoring_frame_from_different_system', {
|
|
30
|
+
frame_type: envelope.frame.type,
|
|
31
|
+
frame_system_id: frameSystemId,
|
|
32
|
+
expected_system_id: this.expectedSystemId,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
20
35
|
return null;
|
|
21
36
|
}
|
|
22
37
|
return finalHandler(envelope, context);
|
|
@@ -143,6 +158,7 @@ export class DefaultNodeAttachClient {
|
|
|
143
158
|
parent_id: ackFrame.targetSystemId,
|
|
144
159
|
});
|
|
145
160
|
this.inHandshake = false;
|
|
161
|
+
this.expectedSystemId = null;
|
|
146
162
|
await connector.replaceHandler(finalHandler);
|
|
147
163
|
while (this.buffer.length > 0) {
|
|
148
164
|
const bufferedEnvelope = this.buffer.shift();
|
|
@@ -200,7 +216,8 @@ export class DefaultNodeAttachClient {
|
|
|
200
216
|
const deadline = Date.now() + this.timeoutMs;
|
|
201
217
|
while (Date.now() < deadline) {
|
|
202
218
|
// Allow both STARTED and PAUSED states (PAUSED = tab hidden but connection alive)
|
|
203
|
-
if (connector.state !== ConnectorState.STARTED &&
|
|
219
|
+
if (connector.state !== ConnectorState.STARTED &&
|
|
220
|
+
connector.state !== ConnectorState.PAUSED) {
|
|
204
221
|
let errorMessage = 'Connector closed while waiting for NodeAttachAck';
|
|
205
222
|
if (connector.closeCode !== undefined) {
|
|
206
223
|
errorMessage += ` (code=${connector.closeCode}`;
|
|
@@ -219,9 +236,21 @@ export class DefaultNodeAttachClient {
|
|
|
219
236
|
if (envelope.frame.type === 'NodeAttachAck') {
|
|
220
237
|
return envelope;
|
|
221
238
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
239
|
+
// NodeAttach frames during handshake are expected in multi-agent scenarios
|
|
240
|
+
// where multiple agents attach concurrently to the same channel
|
|
241
|
+
if (envelope.frame.type === 'NodeAttach') {
|
|
242
|
+
logger.debug('handshake_ignoring_concurrent_attach', {
|
|
243
|
+
frame_type: envelope.frame.type,
|
|
244
|
+
frame_system_id: envelope.frame?.systemId ??
|
|
245
|
+
'unknown',
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
// Other unexpected frames are still logged as errors
|
|
250
|
+
logger.error('unexpected_frame_during_handshake', {
|
|
251
|
+
frame_type: envelope.frame.type,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
225
254
|
}
|
|
226
255
|
await delay(HANDSHAKE_POLL_INTERVAL_MS);
|
|
227
256
|
}
|
|
@@ -318,25 +318,56 @@ export class UpstreamSessionManager extends TaskSpawner {
|
|
|
318
318
|
await connector.start(this.wrappedHandler);
|
|
319
319
|
this.connector = connector;
|
|
320
320
|
const callbackGrants = this.node.gatherSupportedCallbackGrants();
|
|
321
|
+
logger.debug('callback_grants_before_augmentation', {
|
|
322
|
+
count: callbackGrants.length,
|
|
323
|
+
types: callbackGrants.map((g) => g.type),
|
|
324
|
+
});
|
|
325
|
+
// Check if we should create a broadcast callback grant before processing connection grants
|
|
326
|
+
// This prevents adding duplicate broadcast grants
|
|
327
|
+
const shouldAddBroadcastGrant = this.shouldAdvertiseBroadcastGrant(grant, callbackGrants);
|
|
328
|
+
const broadcastCallbackGrant = shouldAddBroadcastGrant
|
|
329
|
+
? this.createBroadcastCallbackGrant(grant)
|
|
330
|
+
: null;
|
|
331
|
+
logger.debug('broadcast_callback_grant_check', {
|
|
332
|
+
should_add: shouldAddBroadcastGrant,
|
|
333
|
+
grant_created: !!broadcastCallbackGrant,
|
|
334
|
+
});
|
|
321
335
|
// Include admission client's connection grants as callback grants
|
|
322
336
|
// This ensures DirectAdmissionClient grants are available for grant selection
|
|
323
|
-
if (welcome.frame.connectionGrants &&
|
|
337
|
+
if (welcome.frame.connectionGrants &&
|
|
338
|
+
Array.isArray(welcome.frame.connectionGrants)) {
|
|
324
339
|
for (const grant of welcome.frame.connectionGrants) {
|
|
325
340
|
if (grant && typeof grant === 'object') {
|
|
326
341
|
// Avoid duplicates by checking if grant already exists
|
|
327
|
-
const isDuplicate = callbackGrants.some(existing => JSON.stringify(existing) === JSON.stringify(grant));
|
|
342
|
+
const isDuplicate = callbackGrants.some((existing) => JSON.stringify(existing) === JSON.stringify(grant));
|
|
328
343
|
if (!isDuplicate) {
|
|
329
344
|
callbackGrants.push(grant);
|
|
345
|
+
logger.debug('added_connection_grant_as_callback', {
|
|
346
|
+
type: grant.type,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
logger.debug('skipped_duplicate_connection_grant', {
|
|
351
|
+
type: grant.type,
|
|
352
|
+
});
|
|
330
353
|
}
|
|
331
354
|
}
|
|
332
355
|
}
|
|
333
356
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
357
|
+
// Add broadcast grant after connection grants to ensure we don't duplicate
|
|
358
|
+
// any broadcast grants that may have been in connectionGrants
|
|
359
|
+
if (broadcastCallbackGrant &&
|
|
360
|
+
this.shouldAdvertiseBroadcastGrant(grant, callbackGrants)) {
|
|
361
|
+
callbackGrants.push(broadcastCallbackGrant);
|
|
362
|
+
logger.debug('added_broadcast_callback_grant');
|
|
363
|
+
}
|
|
364
|
+
else if (broadcastCallbackGrant) {
|
|
365
|
+
logger.debug('skipped_duplicate_broadcast_callback_grant');
|
|
339
366
|
}
|
|
367
|
+
logger.debug('callback_grants_after_augmentation', {
|
|
368
|
+
count: callbackGrants.length,
|
|
369
|
+
types: callbackGrants.map((g) => g.type),
|
|
370
|
+
});
|
|
340
371
|
const attachInfo = await this.attachClient.attach(this.node, this.outboundOriginType, connector, welcome.frame, this.wrappedHandler, this.getKeys() ?? undefined, callbackGrants);
|
|
341
372
|
this.targetSystemId = attachInfo.targetSystemId ?? null;
|
|
342
373
|
if (this.targetSystemId) {
|
|
@@ -577,7 +608,8 @@ export class UpstreamSessionManager extends TaskSpawner {
|
|
|
577
608
|
continue;
|
|
578
609
|
}
|
|
579
610
|
// Reset ack time if just resumed from pause (prevents immediate timeout)
|
|
580
|
-
if (previousState === ConnectorState.PAUSED &&
|
|
611
|
+
if (previousState === ConnectorState.PAUSED &&
|
|
612
|
+
currentState === ConnectorState.STARTED) {
|
|
581
613
|
logger.debug('connector_just_resumed_resetting_ack_time', {
|
|
582
614
|
previous_state: previousState,
|
|
583
615
|
current_state: currentState,
|
|
@@ -58,19 +58,23 @@ function normalizeConfig(config) {
|
|
|
58
58
|
normalized.clientSecretConfig = normalizeSecretSource(clientSecretSource);
|
|
59
59
|
}
|
|
60
60
|
const audienceCandidate = candidate.audience ?? candidate.aud;
|
|
61
|
-
if (typeof audienceCandidate === 'string' &&
|
|
61
|
+
if (typeof audienceCandidate === 'string' &&
|
|
62
|
+
audienceCandidate.trim().length > 0) {
|
|
62
63
|
normalized.audience = audienceCandidate.trim();
|
|
63
64
|
}
|
|
64
65
|
const codeChallengeMethod = candidate.codeChallengeMethod ?? candidate.code_challenge_method;
|
|
65
|
-
if (typeof codeChallengeMethod === 'string' &&
|
|
66
|
+
if (typeof codeChallengeMethod === 'string' &&
|
|
67
|
+
codeChallengeMethod.trim().length > 0) {
|
|
66
68
|
normalized.codeChallengeMethod = codeChallengeMethod.trim();
|
|
67
69
|
}
|
|
68
70
|
const codeVerifierLength = candidate.codeVerifierLength ?? candidate.code_verifier_length;
|
|
69
|
-
if (typeof codeVerifierLength === 'number' &&
|
|
71
|
+
if (typeof codeVerifierLength === 'number' &&
|
|
72
|
+
Number.isFinite(codeVerifierLength)) {
|
|
70
73
|
normalized.codeVerifierLength = codeVerifierLength;
|
|
71
74
|
}
|
|
72
75
|
const clockSkewSeconds = candidate.clockSkewSeconds ?? candidate.clock_skew_seconds;
|
|
73
|
-
if (typeof clockSkewSeconds === 'number' &&
|
|
76
|
+
if (typeof clockSkewSeconds === 'number' &&
|
|
77
|
+
Number.isFinite(clockSkewSeconds)) {
|
|
74
78
|
normalized.clockSkewSeconds = clockSkewSeconds;
|
|
75
79
|
}
|
|
76
80
|
const loginHintParam = candidate.loginHintParam ?? candidate.login_hint_param;
|
|
@@ -115,8 +119,8 @@ export class OAuth2PkceTokenProviderFactory extends TokenProviderFactory {
|
|
|
115
119
|
options.audience = normalized.audience;
|
|
116
120
|
}
|
|
117
121
|
if (normalized.codeChallengeMethod) {
|
|
118
|
-
options.codeChallengeMethod =
|
|
119
|
-
.toUpperCase();
|
|
122
|
+
options.codeChallengeMethod =
|
|
123
|
+
normalized.codeChallengeMethod.toUpperCase();
|
|
120
124
|
}
|
|
121
125
|
if (normalized.codeVerifierLength) {
|
|
122
126
|
options.codeVerifierLength = normalized.codeVerifierLength;
|
|
@@ -64,8 +64,7 @@ function normalizeOptions(raw) {
|
|
|
64
64
|
const scopes = normalizeScopes(camel.scopes) ??
|
|
65
65
|
normalizeScopes(snake.scopes ?? snake.scope) ??
|
|
66
66
|
DEFAULT_SCOPES.slice();
|
|
67
|
-
const audience = coerceString(camel.audience) ??
|
|
68
|
-
coerceString(snake.audience ?? snake.aud);
|
|
67
|
+
const audience = coerceString(camel.audience) ?? coerceString(snake.audience ?? snake.aud);
|
|
69
68
|
const fetchImpl = (camel.fetchImpl ?? snake.fetch_impl);
|
|
70
69
|
const clockSkewSeconds = coerceNumber(camel.clockSkewSeconds) ??
|
|
71
70
|
coerceNumber(snake.clock_skew_seconds) ??
|
|
@@ -504,7 +504,8 @@ export class DefaultSecurityManager {
|
|
|
504
504
|
const hasSignature = Boolean(envelope.sec?.sig);
|
|
505
505
|
if (!hasSignature) {
|
|
506
506
|
const nodeSid = node.sid;
|
|
507
|
-
const envelopeSid = envelope
|
|
507
|
+
const envelopeSid = envelope
|
|
508
|
+
.sid;
|
|
508
509
|
const isLocalUnsignedSelfEnvelope = localContext.originType === DeliveryOriginType.LOCAL &&
|
|
509
510
|
typeof nodeSid === 'string' &&
|
|
510
511
|
nodeSid.length > 0 &&
|
|
@@ -47,7 +47,7 @@ export * from './signing/envelope-signer.js';
|
|
|
47
47
|
export * from './signing/envelope-verifier.js';
|
|
48
48
|
export { SigningConfig as SigningConfigClass, } from './signing/signing-config.js';
|
|
49
49
|
export { canonicalJson, decodeBase64Url, frameDigest, immutableHeaders, } from './signing/eddsa-signer-verifier.js';
|
|
50
|
-
export { encodeUtf8
|
|
50
|
+
export { encodeUtf8 } from './signing/eddsa-utils.js';
|
|
51
51
|
export { EdDSAEnvelopeSigner, } from './signing/eddsa-envelope-signer.js';
|
|
52
52
|
export * from './crypto/providers/crypto-provider.js';
|
|
53
53
|
export * from './crypto/providers/default-crypto-provider.js';
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { NoopTrustStoreProvider, TrustStoreProviderFactory, TRUST_STORE_PROVIDER_FACTORY_BASE_TYPE, } from
|
|
1
|
+
import { NoopTrustStoreProvider, TrustStoreProviderFactory, TRUST_STORE_PROVIDER_FACTORY_BASE_TYPE, } from './trust-store-provider-factory.js';
|
|
2
2
|
export const FACTORY_META = {
|
|
3
3
|
base: TRUST_STORE_PROVIDER_FACTORY_BASE_TYPE,
|
|
4
|
-
key:
|
|
4
|
+
key: 'NoopTrustStoreProvider',
|
|
5
5
|
isDefault: true,
|
|
6
6
|
priority: 10,
|
|
7
7
|
};
|
|
8
8
|
export class NoopTrustStoreProviderFactory extends TrustStoreProviderFactory {
|
|
9
9
|
constructor() {
|
|
10
10
|
super(...arguments);
|
|
11
|
-
this.type =
|
|
11
|
+
this.type = 'NoopTrustStoreProvider';
|
|
12
12
|
this.isDefault = true;
|
|
13
13
|
this.priority = 10;
|
|
14
14
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AbstractResourceFactory, createDefaultResource, createResource, } from
|
|
2
|
-
const DEFAULT_UNCONFIGURED_MESSAGE =
|
|
3
|
-
export const TRUST_STORE_PROVIDER_FACTORY_BASE_TYPE =
|
|
1
|
+
import { AbstractResourceFactory, createDefaultResource, createResource, } from '@naylence/factory';
|
|
2
|
+
const DEFAULT_UNCONFIGURED_MESSAGE = 'Trust store is not configured. Set FAME_CA_CERTS to a PEM value, a file path, a data URI, or an HTTPS bundle URL.';
|
|
3
|
+
export const TRUST_STORE_PROVIDER_FACTORY_BASE_TYPE = 'TrustStoreProviderFactory';
|
|
4
4
|
export class TrustStoreProviderFactory extends AbstractResourceFactory {
|
|
5
5
|
createUnconfiguredProvider(reason) {
|
|
6
6
|
return new NoopTrustStoreProvider(reason ?? DEFAULT_UNCONFIGURED_MESSAGE);
|
package/dist/esm/node.js
CHANGED
|
@@ -15,7 +15,8 @@ export * from './naylence/fame/placement/node-placement-strategy-factory.js';
|
|
|
15
15
|
export * from './naylence/fame/transport/transport-provisioner.js';
|
|
16
16
|
export * from './naylence/fame/welcome/index.js';
|
|
17
17
|
export * from './naylence/fame/sentinel/index.js';
|
|
18
|
-
export { HttpStatelessConnector, QueueFullError, DefaultHttpServer, getWebsocketListenerInstance, TransportListener, TRANSPORT_LISTENER_FACTORY_BASE_TYPE, WebSocketListener, HttpListener, getHttpListenerInstance, InPageListener, getInPageListenerInstance, } from './naylence/fame/connector/index.js';
|
|
18
|
+
export { HttpStatelessConnector, QueueFullError, DefaultHttpServer, getWebsocketListenerInstance, TransportListener, TransportListenerFactory, TRANSPORT_LISTENER_FACTORY_BASE_TYPE, WebSocketListener, HttpListener, getHttpListenerInstance, InPageListener, getInPageListenerInstance, } from './naylence/fame/connector/index.js';
|
|
19
|
+
export { safeImport } from './naylence/fame/util/lazy-import.js';
|
|
19
20
|
export { InProcessFameFabric, InProcessFameFabricFactory, FAME_FABRIC_FACTORY_BASE_TYPE, } from './naylence/fame/fabric/index.js';
|
|
20
21
|
export { normalizeExtendedFameConfig, } from './naylence/fame/config/index.js';
|
|
21
22
|
export { createJwksRouter, } from './naylence/fame/http/jwks-api-router.js';
|
package/dist/esm/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This file is auto-generated during build - do not edit manually
|
|
2
|
-
// Generated from package.json version: 0.3.
|
|
2
|
+
// Generated from package.json version: 0.3.7
|
|
3
3
|
/**
|
|
4
4
|
* The package version, injected at build time.
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
|
-
export const VERSION = '0.3.
|
|
7
|
+
export const VERSION = '0.3.7';
|