@salesforce/lds-runtime-aura 1.428.0-dev2 → 1.428.0-dev21
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/ldsEngineCreator.js +669 -112
- package/dist/types/fetch-service-descriptors/jwt-authorized-fetch-service.d.ts +25 -0
- package/dist/types/network-sfap.d.ts +1 -1
- package/dist/types/parameterized-sfap-jwt-aura-resolver.d.ts +34 -0
- package/dist/types/request-interceptors/jwt-parameterization.d.ts +50 -0
- package/dist/types/retry-policies/csrf-token-retry-policy.d.ts +2 -1
- package/dist/types/sfap-jwt-mint-params.d.ts +80 -0
- package/package.json +44 -43
package/dist/ldsEngineCreator.js
CHANGED
|
@@ -1354,6 +1354,7 @@ class HttpCacheControlCommand extends CacheControlCommand {
|
|
|
1354
1354
|
constructor(services) {
|
|
1355
1355
|
super(services);
|
|
1356
1356
|
this.services = services;
|
|
1357
|
+
this.additionalNullResponses = [];
|
|
1357
1358
|
}
|
|
1358
1359
|
requestFromNetwork() {
|
|
1359
1360
|
return this.fetch();
|
|
@@ -1365,6 +1366,15 @@ class HttpCacheControlCommand extends CacheControlCommand {
|
|
|
1365
1366
|
return resolvedPromiseLike$2(err$1(toError(reason)));
|
|
1366
1367
|
}
|
|
1367
1368
|
}
|
|
1369
|
+
isSemanticNullResponse(response) {
|
|
1370
|
+
return this.additionalNullResponses.includes(response.status);
|
|
1371
|
+
}
|
|
1372
|
+
isProtocolNoBodyStatus(status) {
|
|
1373
|
+
return status === 204 || status === 205;
|
|
1374
|
+
}
|
|
1375
|
+
isUndeclaredNoBodyResponse(response) {
|
|
1376
|
+
return this.isProtocolNoBodyStatus(response.status) && !this.isSemanticNullResponse(response);
|
|
1377
|
+
}
|
|
1368
1378
|
async coerceError(errorResponse) {
|
|
1369
1379
|
return toError(errorResponse.statusText);
|
|
1370
1380
|
}
|
|
@@ -1375,12 +1385,26 @@ class HttpCacheControlCommand extends CacheControlCommand {
|
|
|
1375
1385
|
return response.then(
|
|
1376
1386
|
(response2) => {
|
|
1377
1387
|
if (response2.ok) {
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1388
|
+
let resultPromise;
|
|
1389
|
+
if (this.isSemanticNullResponse(response2)) {
|
|
1390
|
+
resultPromise = Promise.resolve(ok$2(null));
|
|
1391
|
+
} else if (this.isUndeclaredNoBodyResponse(response2)) {
|
|
1392
|
+
resultPromise = Promise.resolve(
|
|
1393
|
+
err$1(
|
|
1394
|
+
toError(
|
|
1395
|
+
`Unexpected ${response2.status} response: no-content status was not declared in the API specification. Declare this response in your OAS without a content property.`
|
|
1396
|
+
)
|
|
1397
|
+
)
|
|
1398
|
+
);
|
|
1399
|
+
} else {
|
|
1400
|
+
resultPromise = response2.json().then(
|
|
1401
|
+
(json) => {
|
|
1402
|
+
return this.processFetchReturnValue(json);
|
|
1403
|
+
},
|
|
1404
|
+
(reason) => err$1(toError(reason))
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
return resultPromise.finally(() => {
|
|
1384
1408
|
try {
|
|
1385
1409
|
this.afterRequestHooks({ statusCode: response2.status });
|
|
1386
1410
|
} catch {
|
|
@@ -1564,14 +1588,27 @@ const _FetchNetworkCommand = class _FetchNetworkCommand extends NetworkCommand {
|
|
|
1564
1588
|
constructor(services) {
|
|
1565
1589
|
super(services);
|
|
1566
1590
|
this.services = services;
|
|
1591
|
+
this.additionalNullResponses = [];
|
|
1567
1592
|
}
|
|
1568
|
-
fetch() {
|
|
1593
|
+
fetch(contextSeed) {
|
|
1569
1594
|
try {
|
|
1570
|
-
|
|
1595
|
+
const [input, init] = this.fetchParams;
|
|
1596
|
+
const initWithSeed = contextSeed === void 0 ? init : { ...init, __contextSeed: contextSeed };
|
|
1597
|
+
const fetchCall = initWithSeed === void 0 ? this.services.fetch(input) : this.services.fetch(input, initWithSeed);
|
|
1598
|
+
return this.convertFetchResponseToData(fetchCall);
|
|
1571
1599
|
} catch (reason) {
|
|
1572
1600
|
return resolvedPromiseLike$2(err$1(toError(reason)));
|
|
1573
1601
|
}
|
|
1574
1602
|
}
|
|
1603
|
+
isSemanticNullResponse(response) {
|
|
1604
|
+
return this.additionalNullResponses.includes(response.status);
|
|
1605
|
+
}
|
|
1606
|
+
isProtocolNoBodyStatus(status) {
|
|
1607
|
+
return status === 204 || status === 205;
|
|
1608
|
+
}
|
|
1609
|
+
isUndeclaredNoBodyResponse(response) {
|
|
1610
|
+
return this.isProtocolNoBodyStatus(response.status) && !this.isSemanticNullResponse(response);
|
|
1611
|
+
}
|
|
1575
1612
|
async coerceError(errorResponse) {
|
|
1576
1613
|
return toError(errorResponse.statusText);
|
|
1577
1614
|
}
|
|
@@ -1579,10 +1616,24 @@ const _FetchNetworkCommand = class _FetchNetworkCommand extends NetworkCommand {
|
|
|
1579
1616
|
return response.then(
|
|
1580
1617
|
(response2) => {
|
|
1581
1618
|
if (response2.ok) {
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1619
|
+
let resultPromise;
|
|
1620
|
+
if (this.isSemanticNullResponse(response2)) {
|
|
1621
|
+
resultPromise = Promise.resolve(ok$2(null));
|
|
1622
|
+
} else if (this.isUndeclaredNoBodyResponse(response2)) {
|
|
1623
|
+
resultPromise = Promise.resolve(
|
|
1624
|
+
err$1(
|
|
1625
|
+
toError(
|
|
1626
|
+
`Unexpected ${response2.status} response: no-content status was not declared in the API specification. Declare this response in your OAS without a content property.`
|
|
1627
|
+
)
|
|
1628
|
+
)
|
|
1629
|
+
);
|
|
1630
|
+
} else {
|
|
1631
|
+
resultPromise = response2.json().then(
|
|
1632
|
+
(json) => ok$2(json),
|
|
1633
|
+
(reason) => err$1(toError(reason))
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
return resultPromise.finally(() => {
|
|
1586
1637
|
try {
|
|
1587
1638
|
this.afterRequestHooks({ statusCode: response2.status });
|
|
1588
1639
|
} catch {
|
|
@@ -2712,7 +2763,7 @@ function buildServiceDescriptor$d(luvio) {
|
|
|
2712
2763
|
},
|
|
2713
2764
|
};
|
|
2714
2765
|
}
|
|
2715
|
-
// version: 1.428.0-
|
|
2766
|
+
// version: 1.428.0-dev21-133a7da4f5
|
|
2716
2767
|
|
|
2717
2768
|
/*!
|
|
2718
2769
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -3065,7 +3116,7 @@ function buildServiceDescriptor$9(notifyRecordUpdateAvailable, getNormalizedLuvi
|
|
|
3065
3116
|
},
|
|
3066
3117
|
};
|
|
3067
3118
|
}
|
|
3068
|
-
// version: 1.428.0-
|
|
3119
|
+
// version: 1.428.0-dev21-133a7da4f5
|
|
3069
3120
|
|
|
3070
3121
|
/*!
|
|
3071
3122
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -4153,11 +4204,13 @@ class JwtToken {
|
|
|
4153
4204
|
* @param _token - The JWT string.
|
|
4154
4205
|
* @param _decodedInfo - The decoded information from the JWT.
|
|
4155
4206
|
* @param _extraInfo - Any additional information associated with the JWT.
|
|
4207
|
+
* @param _mintParams - The parameters used to mint this token. Undefined for legacy parameterless tokens.
|
|
4156
4208
|
*/
|
|
4157
|
-
constructor(_token, _decodedInfo, _extraInfo) {
|
|
4209
|
+
constructor(_token, _decodedInfo, _extraInfo, _mintParams) {
|
|
4158
4210
|
this._token = _token;
|
|
4159
4211
|
this._decodedInfo = _decodedInfo;
|
|
4160
4212
|
this._extraInfo = _extraInfo;
|
|
4213
|
+
this._mintParams = _mintParams;
|
|
4161
4214
|
}
|
|
4162
4215
|
/**
|
|
4163
4216
|
* Get the JWT string.
|
|
@@ -4183,6 +4236,14 @@ class JwtToken {
|
|
|
4183
4236
|
get decodedInfo() {
|
|
4184
4237
|
return this._decodedInfo;
|
|
4185
4238
|
}
|
|
4239
|
+
/**
|
|
4240
|
+
* Get the mint parameters used to produce this token.
|
|
4241
|
+
*
|
|
4242
|
+
* @returns The mint parameters, or undefined for legacy parameterless tokens.
|
|
4243
|
+
*/
|
|
4244
|
+
get mintParams() {
|
|
4245
|
+
return this._mintParams;
|
|
4246
|
+
}
|
|
4186
4247
|
/**
|
|
4187
4248
|
* Get the remaining time in seconds until the JWT expires.
|
|
4188
4249
|
*
|
|
@@ -4200,6 +4261,12 @@ class JwtToken {
|
|
|
4200
4261
|
return this.tokenRemainingSeconds <= 0;
|
|
4201
4262
|
}
|
|
4202
4263
|
}
|
|
4264
|
+
function cacheKeyFor(params) {
|
|
4265
|
+
if (params === void 0) {
|
|
4266
|
+
return "jwt:";
|
|
4267
|
+
}
|
|
4268
|
+
return `jwt:${stableJSONStringify$2(params) ?? ""}`;
|
|
4269
|
+
}
|
|
4203
4270
|
let defaultLogger = {
|
|
4204
4271
|
trace: () => {
|
|
4205
4272
|
},
|
|
@@ -4233,85 +4300,146 @@ class JwtRepository {
|
|
|
4233
4300
|
this.limitInSeconds = limitInSeconds;
|
|
4234
4301
|
this.defaultTokenTTLInSeconds = defaultTokenTTLInSeconds;
|
|
4235
4302
|
this.logger = logger;
|
|
4303
|
+
this._tokens = /* @__PURE__ */ new Map();
|
|
4304
|
+
this.timeoutHandlers = /* @__PURE__ */ new Map();
|
|
4236
4305
|
this.observers = [];
|
|
4237
4306
|
}
|
|
4238
4307
|
/**
|
|
4239
|
-
* Get the
|
|
4308
|
+
* Get the legacy (parameterless) token. Equivalent to `getToken()` with
|
|
4309
|
+
* no args. Preserved for backward compatibility with code that reads the
|
|
4310
|
+
* `token` property directly.
|
|
4240
4311
|
*/
|
|
4241
4312
|
get token() {
|
|
4242
|
-
return this.
|
|
4313
|
+
return this.getToken();
|
|
4314
|
+
}
|
|
4315
|
+
/**
|
|
4316
|
+
* Get the cached token for the given mint params.
|
|
4317
|
+
*
|
|
4318
|
+
* @param params - The mint params identifying the token. Omit for the legacy global token.
|
|
4319
|
+
*/
|
|
4320
|
+
getToken(params) {
|
|
4321
|
+
return this._tokens.get(cacheKeyFor(params));
|
|
4243
4322
|
}
|
|
4244
4323
|
/**
|
|
4245
|
-
* Set the
|
|
4324
|
+
* Set the cached token for the given mint params.
|
|
4246
4325
|
*
|
|
4247
4326
|
* @param token - JWT token as a string.
|
|
4248
4327
|
* @param extraInfo - Optional extra information.
|
|
4328
|
+
* @param params - The mint params identifying the token. Omit for the legacy global token.
|
|
4249
4329
|
*/
|
|
4250
|
-
setToken(token, extraInfo) {
|
|
4330
|
+
setToken(token, extraInfo, params) {
|
|
4251
4331
|
const decodedInfo = computeDecodedInfo(
|
|
4252
4332
|
token,
|
|
4253
4333
|
this.defaultTokenTTLInSeconds,
|
|
4254
4334
|
this.logger
|
|
4255
4335
|
);
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4336
|
+
const key = cacheKeyFor(params);
|
|
4337
|
+
const jwtToken = new JwtToken(token, decodedInfo, extraInfo, params);
|
|
4338
|
+
this._tokens.set(key, jwtToken);
|
|
4339
|
+
this.observeTokenExpiration(key);
|
|
4340
|
+
return jwtToken;
|
|
4341
|
+
}
|
|
4342
|
+
/**
|
|
4343
|
+
* Remove the cached token for the given mint params.
|
|
4344
|
+
*
|
|
4345
|
+
* @param params - The mint params identifying the token. Omit to remove
|
|
4346
|
+
* only the legacy global-key entry (matches today's behavior of "remove
|
|
4347
|
+
* the only token there is").
|
|
4348
|
+
*/
|
|
4349
|
+
removeToken(params) {
|
|
4350
|
+
const key = cacheKeyFor(params);
|
|
4351
|
+
this._tokens.delete(key);
|
|
4352
|
+
this.clearTimeoutHandler(key);
|
|
4259
4353
|
}
|
|
4260
4354
|
/**
|
|
4261
|
-
* Remove
|
|
4355
|
+
* Remove every cached token and clear every near-expiry timer. Used by
|
|
4356
|
+
* test teardown and full auth-reset paths that previously called
|
|
4357
|
+
* `removeToken()` to clear the single token.
|
|
4262
4358
|
*/
|
|
4263
|
-
|
|
4264
|
-
this.
|
|
4265
|
-
|
|
4359
|
+
clearAllTokens() {
|
|
4360
|
+
for (const key of Array.from(this.timeoutHandlers.keys())) {
|
|
4361
|
+
this.clearTimeoutHandler(key);
|
|
4362
|
+
}
|
|
4363
|
+
this._tokens.clear();
|
|
4266
4364
|
}
|
|
4267
4365
|
/**
|
|
4268
|
-
* Subscribe to
|
|
4366
|
+
* Subscribe to any cached token nearing its expiration.
|
|
4367
|
+
*
|
|
4368
|
+
* The callback receives the specific token whose timer fired. Use
|
|
4369
|
+
* `token.mintParams` to determine which token-set the expiry belongs to.
|
|
4269
4370
|
*
|
|
4270
|
-
*
|
|
4371
|
+
* Already-cached tokens are armed at subscribe time. Tokens added later
|
|
4372
|
+
* via `setToken` arm their own timers from inside `setToken`, so this
|
|
4373
|
+
* loop only needs to cover the existing entries.
|
|
4374
|
+
*
|
|
4375
|
+
* @param cb - Callback function to execute when a token is nearing expiration.
|
|
4271
4376
|
*/
|
|
4272
4377
|
subscribeToTokenNearExpiration(cb) {
|
|
4273
4378
|
this.observers.push(cb);
|
|
4274
|
-
this.
|
|
4379
|
+
for (const key of this._tokens.keys()) {
|
|
4380
|
+
this.observeTokenExpiration(key);
|
|
4381
|
+
}
|
|
4275
4382
|
return () => {
|
|
4276
4383
|
this.observers = this.observers.filter((observer) => observer !== cb);
|
|
4384
|
+
if (this.observers.length === 0) {
|
|
4385
|
+
for (const key of Array.from(this.timeoutHandlers.keys())) {
|
|
4386
|
+
this.clearTimeoutHandler(key);
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4277
4389
|
};
|
|
4278
4390
|
}
|
|
4279
4391
|
/**
|
|
4280
|
-
*
|
|
4392
|
+
* Number of currently cached tokens. Exposed for size observability so
|
|
4393
|
+
* runtime callers can emit a metric on cache growth and detect adapters
|
|
4394
|
+
* that misuse `dynamicParams` for per-request values.
|
|
4395
|
+
*/
|
|
4396
|
+
get size() {
|
|
4397
|
+
return this._tokens.size;
|
|
4398
|
+
}
|
|
4399
|
+
/**
|
|
4400
|
+
* Clear the timeout handler for a specific cache key.
|
|
4281
4401
|
*/
|
|
4282
|
-
clearTimeoutHandler() {
|
|
4283
|
-
|
|
4284
|
-
|
|
4402
|
+
clearTimeoutHandler(key) {
|
|
4403
|
+
const handler = this.timeoutHandlers.get(key);
|
|
4404
|
+
if (handler !== void 0) {
|
|
4405
|
+
clearTimeout(handler);
|
|
4406
|
+
this.timeoutHandlers.delete(key);
|
|
4285
4407
|
}
|
|
4286
4408
|
}
|
|
4287
4409
|
/**
|
|
4288
|
-
* Observe and handle token
|
|
4410
|
+
* Observe and handle expiration of the token at the given cache key.
|
|
4289
4411
|
*/
|
|
4290
|
-
observeTokenExpiration() {
|
|
4291
|
-
this.clearTimeoutHandler();
|
|
4292
|
-
|
|
4412
|
+
observeTokenExpiration(key) {
|
|
4413
|
+
this.clearTimeoutHandler(key);
|
|
4414
|
+
const token = this._tokens.get(key);
|
|
4415
|
+
if (this.observers.length === 0 || token === void 0) {
|
|
4293
4416
|
return;
|
|
4294
4417
|
}
|
|
4295
|
-
|
|
4296
|
-
() => this.notifyTokenIsExpiring(),
|
|
4297
|
-
this.computeTimeoutTimeInMs()
|
|
4418
|
+
const handler = setTimeout(
|
|
4419
|
+
() => this.notifyTokenIsExpiring(key),
|
|
4420
|
+
this.computeTimeoutTimeInMs(token)
|
|
4298
4421
|
);
|
|
4422
|
+
this.timeoutHandlers.set(key, handler);
|
|
4299
4423
|
}
|
|
4300
4424
|
/**
|
|
4301
|
-
* Compute the timeout time in milliseconds.
|
|
4425
|
+
* Compute the timeout time in milliseconds for the given token.
|
|
4302
4426
|
*/
|
|
4303
|
-
computeTimeoutTimeInMs() {
|
|
4304
|
-
const remainingSeconds =
|
|
4305
|
-
|
|
4427
|
+
computeTimeoutTimeInMs(token) {
|
|
4428
|
+
const remainingSeconds = token.tokenRemainingSeconds;
|
|
4429
|
+
const timeoutTimeInSeconds = remainingSeconds - this.limitInSeconds;
|
|
4306
4430
|
return timeoutTimeInSeconds < 0 ? 0 : timeoutTimeInSeconds * 1e3;
|
|
4307
4431
|
}
|
|
4308
4432
|
/**
|
|
4309
|
-
* Notify all observers that the token is expiring.
|
|
4433
|
+
* Notify all observers that the token at the given cache key is expiring.
|
|
4310
4434
|
*/
|
|
4311
|
-
notifyTokenIsExpiring() {
|
|
4435
|
+
notifyTokenIsExpiring(key) {
|
|
4436
|
+
const token = this._tokens.get(key);
|
|
4437
|
+
if (token === void 0) {
|
|
4438
|
+
return;
|
|
4439
|
+
}
|
|
4312
4440
|
this.observers.forEach((cb) => {
|
|
4313
4441
|
try {
|
|
4314
|
-
cb.call(void 0,
|
|
4442
|
+
cb.call(void 0, token);
|
|
4315
4443
|
} catch (e2) {
|
|
4316
4444
|
this.logger.error(e2.message);
|
|
4317
4445
|
}
|
|
@@ -4329,57 +4457,68 @@ class JwtManager {
|
|
|
4329
4457
|
constructor(jwtRepository, resolver, options) {
|
|
4330
4458
|
this.jwtRepository = jwtRepository;
|
|
4331
4459
|
this.resolver = resolver;
|
|
4460
|
+
this.inflightPromises = /* @__PURE__ */ new Map();
|
|
4332
4461
|
if (options == null ? void 0 : options.keepTokenUpdated) {
|
|
4333
|
-
jwtRepository.subscribeToTokenNearExpiration(
|
|
4462
|
+
jwtRepository.subscribeToTokenNearExpiration(
|
|
4463
|
+
(token) => this.refreshToken(token.mintParams)
|
|
4464
|
+
);
|
|
4334
4465
|
}
|
|
4335
4466
|
}
|
|
4336
4467
|
/**
|
|
4337
|
-
* Method to get a JWT token.
|
|
4338
|
-
* If there's a token request in progress, it will return the Promise of this request.
|
|
4339
|
-
* If the current token is undefined or expired, it will initiate a token refresh.
|
|
4340
|
-
* Otherwise, it will return the current token.
|
|
4468
|
+
* Method to get a JWT token for the given mint params.
|
|
4341
4469
|
*
|
|
4342
|
-
*
|
|
4470
|
+
* If a request for the same params is in flight, returns its promise. If
|
|
4471
|
+
* a cached token for those params exists and is not expired, returns it
|
|
4472
|
+
* synchronously. Otherwise initiates a refresh.
|
|
4473
|
+
*
|
|
4474
|
+
* @param params - Optional mint parameters. Omit for the legacy parameterless token.
|
|
4475
|
+
* @returns The cached token (sync) or a promise that resolves to the refreshed token.
|
|
4343
4476
|
*/
|
|
4344
|
-
getJwt() {
|
|
4345
|
-
|
|
4346
|
-
|
|
4477
|
+
getJwt(params) {
|
|
4478
|
+
const key = cacheKeyFor(params);
|
|
4479
|
+
const inflight = this.inflightPromises.get(key);
|
|
4480
|
+
if (inflight) {
|
|
4481
|
+
return inflight;
|
|
4347
4482
|
}
|
|
4348
|
-
const token = this.jwtRepository.
|
|
4483
|
+
const token = this.jwtRepository.getToken(params);
|
|
4349
4484
|
if (token === void 0 || token.isExpired) {
|
|
4350
|
-
return this.refreshToken();
|
|
4485
|
+
return this.refreshToken(params);
|
|
4351
4486
|
}
|
|
4352
4487
|
return token;
|
|
4353
4488
|
}
|
|
4354
4489
|
/**
|
|
4355
|
-
* Method to refresh a JWT token.
|
|
4356
|
-
* If a refresh request is already in progress, it will return the Promise of this request.
|
|
4357
|
-
* Otherwise, it will start a new refresh request and return its Promise.
|
|
4490
|
+
* Method to refresh a JWT token for the given mint params.
|
|
4358
4491
|
*
|
|
4359
|
-
* @
|
|
4492
|
+
* @param params - Optional mint parameters. Omit for the legacy parameterless token.
|
|
4493
|
+
* @returns Promise of the refreshed token.
|
|
4360
4494
|
*/
|
|
4361
|
-
refreshToken() {
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4495
|
+
refreshToken(params) {
|
|
4496
|
+
const key = cacheKeyFor(params);
|
|
4497
|
+
const existing = this.inflightPromises.get(key);
|
|
4498
|
+
if (existing !== void 0) {
|
|
4499
|
+
return existing;
|
|
4500
|
+
}
|
|
4501
|
+
const resolverPromise = params === void 0 ? this.resolver.getJwt() : this.resolver.getJwt(params);
|
|
4502
|
+
const promise = new Promise((resolve, reject) => {
|
|
4503
|
+
resolverPromise.then(({ jwt, extraInfo }) => {
|
|
4504
|
+
this.inflightPromises.delete(key);
|
|
4505
|
+
const token = this.jwtRepository.setToken(jwt, extraInfo, params);
|
|
4506
|
+
resolve(token);
|
|
4507
|
+
}).catch((reason) => {
|
|
4508
|
+
this.inflightPromises.delete(key);
|
|
4509
|
+
reject(reason);
|
|
4372
4510
|
});
|
|
4373
|
-
}
|
|
4374
|
-
|
|
4511
|
+
});
|
|
4512
|
+
this.inflightPromises.set(key, promise);
|
|
4513
|
+
return promise;
|
|
4375
4514
|
}
|
|
4376
4515
|
/**
|
|
4377
|
-
* Method to check if
|
|
4516
|
+
* Method to check if any token refresh is in progress.
|
|
4378
4517
|
*
|
|
4379
|
-
* @returns {boolean} true if
|
|
4518
|
+
* @returns {boolean} true if at least one refresh is in flight, false otherwise.
|
|
4380
4519
|
*/
|
|
4381
4520
|
get isRefreshingToken() {
|
|
4382
|
-
return this.
|
|
4521
|
+
return this.inflightPromises.size > 0;
|
|
4383
4522
|
}
|
|
4384
4523
|
}
|
|
4385
4524
|
|
|
@@ -4397,9 +4536,18 @@ function buildServiceDescriptor$2(interceptors = {
|
|
|
4397
4536
|
return {
|
|
4398
4537
|
type: "fetch",
|
|
4399
4538
|
version: "1.0",
|
|
4400
|
-
service: function(
|
|
4539
|
+
service: function(input, init) {
|
|
4401
4540
|
var _a;
|
|
4402
|
-
|
|
4541
|
+
let contextSeed;
|
|
4542
|
+
let cleanInit = init;
|
|
4543
|
+
if (init !== void 0 && "__contextSeed" in init) {
|
|
4544
|
+
const { __contextSeed, ...initWithoutSeed } = init;
|
|
4545
|
+
contextSeed = __contextSeed;
|
|
4546
|
+
cleanInit = Object.keys(initWithoutSeed).length === 0 ? void 0 : initWithoutSeed;
|
|
4547
|
+
}
|
|
4548
|
+
const fetchArgs = cleanInit === void 0 ? [input] : [input, cleanInit];
|
|
4549
|
+
const baseContext = (_a = interceptors.createContext) == null ? void 0 : _a.call(interceptors);
|
|
4550
|
+
const context = contextSeed === void 0 ? baseContext : { ...baseContext, ...contextSeed };
|
|
4403
4551
|
const {
|
|
4404
4552
|
request: requestInterceptors = [],
|
|
4405
4553
|
retry: retryInterceptor = void 0,
|
|
@@ -4407,17 +4555,17 @@ function buildServiceDescriptor$2(interceptors = {
|
|
|
4407
4555
|
finally: finallyInterceptors = []
|
|
4408
4556
|
} = interceptors;
|
|
4409
4557
|
const pending = requestInterceptors.reduce(
|
|
4410
|
-
(previousPromise, interceptor) => previousPromise.then((
|
|
4411
|
-
resolvedPromiseLike$2(
|
|
4558
|
+
(previousPromise, interceptor) => previousPromise.then((args) => interceptor(args, context)),
|
|
4559
|
+
resolvedPromiseLike$2(fetchArgs)
|
|
4412
4560
|
);
|
|
4413
|
-
return Promise.resolve(pending).then((
|
|
4561
|
+
return Promise.resolve(pending).then((args) => {
|
|
4414
4562
|
if (retryInterceptor) {
|
|
4415
|
-
return retryInterceptor(
|
|
4563
|
+
return retryInterceptor(args, retryService, context);
|
|
4416
4564
|
} else {
|
|
4417
4565
|
if (retryService) {
|
|
4418
|
-
return retryService.applyRetry(() => fetch(...
|
|
4566
|
+
return retryService.applyRetry(() => fetch(...args));
|
|
4419
4567
|
}
|
|
4420
|
-
return fetch(...
|
|
4568
|
+
return fetch(...args);
|
|
4421
4569
|
}
|
|
4422
4570
|
}).then((response) => {
|
|
4423
4571
|
return responseInterceptors.reduce(
|
|
@@ -4638,7 +4786,7 @@ var TypeCheckShapes;
|
|
|
4638
4786
|
TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
|
|
4639
4787
|
TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
|
|
4640
4788
|
})(TypeCheckShapes || (TypeCheckShapes = {}));
|
|
4641
|
-
// engine version: 0.160.
|
|
4789
|
+
// engine version: 0.160.4-dev1-4b808818
|
|
4642
4790
|
|
|
4643
4791
|
const { keys: keys$1 } = Object;
|
|
4644
4792
|
|
|
@@ -4711,12 +4859,13 @@ const fetchNetworkAdapter = async (resourceRequest, _resourceRequestContext) =>
|
|
|
4711
4859
|
};
|
|
4712
4860
|
};
|
|
4713
4861
|
function generateQueryString(params) {
|
|
4714
|
-
const
|
|
4862
|
+
const searchParams = new URLSearchParams();
|
|
4715
4863
|
for (const key of keys$1(params)) {
|
|
4716
|
-
|
|
4864
|
+
searchParams.append(key, String(params[key]));
|
|
4717
4865
|
}
|
|
4718
|
-
|
|
4719
|
-
|
|
4866
|
+
const queryString = searchParams.toString();
|
|
4867
|
+
if (queryString.length > 0) {
|
|
4868
|
+
return `?${queryString}`;
|
|
4720
4869
|
}
|
|
4721
4870
|
return '';
|
|
4722
4871
|
}
|
|
@@ -4732,6 +4881,10 @@ const SALESFORCE_API_BASE_URI_FLAG = 'api.salesforce.com';
|
|
|
4732
4881
|
const X_REQUEST_ID_HEADER = 'x-request-id';
|
|
4733
4882
|
const SFAPController = 'SalesforceApiPlatformController';
|
|
4734
4883
|
const SFAPJwtMethod = 'getSFAPLightningJwtService';
|
|
4884
|
+
// Parameterized mint: the POST signature's auto-generated Aura method
|
|
4885
|
+
// (`@ConnectSignature(..., generateAuraMethod = true)` on the SFAP Lightning JWT
|
|
4886
|
+
// Service Connect resource). Takes a single `requestBody` named param.
|
|
4887
|
+
const SFAPJwtPostMethod = 'postSFAPLightningJwtService';
|
|
4735
4888
|
/**
|
|
4736
4889
|
* We expect jwt info and baseUri to be present in the response.
|
|
4737
4890
|
*
|
|
@@ -4782,13 +4935,6 @@ const platformSfapJwtResolver = {
|
|
|
4782
4935
|
},
|
|
4783
4936
|
};
|
|
4784
4937
|
const jwtManager = new JwtManager(new JwtRepository(), platformSfapJwtResolver);
|
|
4785
|
-
function prefetchSfapJwt() {
|
|
4786
|
-
const maybePromise = jwtManager.getJwt();
|
|
4787
|
-
if ('then' in maybePromise) {
|
|
4788
|
-
return maybePromise.then(() => undefined).catch(() => undefined);
|
|
4789
|
-
}
|
|
4790
|
-
return Promise.resolve(undefined);
|
|
4791
|
-
}
|
|
4792
4938
|
const authenticateRequest = (resourceRequest, jwt) => {
|
|
4793
4939
|
const { token } = jwt;
|
|
4794
4940
|
const { headers } = resourceRequest;
|
|
@@ -4885,6 +5031,151 @@ function generateRequestId$1() {
|
|
|
4885
5031
|
}
|
|
4886
5032
|
}
|
|
4887
5033
|
|
|
5034
|
+
/**
|
|
5035
|
+
* Normalize SFAP-shaped params into the opaque `JwtMintParams` bag that callers
|
|
5036
|
+
* hand to `JwtManager.getJwt(params)`. Scopes are sorted because they are
|
|
5037
|
+
* semantically a set — without this, `['a', 'b']` and `['b', 'a']` would cache
|
|
5038
|
+
* as separate entries (OneStore treats arrays as ordered under stable-JSON
|
|
5039
|
+
* serialization; see ADR "JWT Parameterization" §2).
|
|
5040
|
+
*
|
|
5041
|
+
* MANDATORY CONSUMER ENTRY POINT. Every consumer that mints a parameterized
|
|
5042
|
+
* SFAP JWT MUST assemble its params through this function before calling the
|
|
5043
|
+
* manager. The cache key is derived by `JwtManager` from the caller's params
|
|
5044
|
+
* (`cacheKeyFor(params)`) *before* the resolver runs — so the resolver cannot
|
|
5045
|
+
* normalize after the fact. Set-stable caching therefore depends on the caller
|
|
5046
|
+
* routing params through here. Do NOT sort inside the resolver: that would only
|
|
5047
|
+
* reorder the wire body, not the cache key, and mutating the caller's params
|
|
5048
|
+
* mid-call would desync the in-flight-dedup key from the stored-token key.
|
|
5049
|
+
*/
|
|
5050
|
+
/**
|
|
5051
|
+
* Validate and narrow an opaque `JwtMintParams` bag to the SFAP-shaped subset the
|
|
5052
|
+
* resolver forwards. Returns `{ error }` (surfaced as a resolver rejection) for a
|
|
5053
|
+
* malformed bag rather than throwing.
|
|
5054
|
+
*/
|
|
5055
|
+
function coerceToSfapParams(params) {
|
|
5056
|
+
const scopes = params.scopes;
|
|
5057
|
+
const dynamicParams = params.dynamicParams;
|
|
5058
|
+
if (scopes !== undefined && !isStringArray(scopes)) {
|
|
5059
|
+
return { error: 'SFAP JWT params.scopes must be a string[] when provided.' };
|
|
5060
|
+
}
|
|
5061
|
+
if (dynamicParams !== undefined && !isPrimitiveRecord(dynamicParams)) {
|
|
5062
|
+
return {
|
|
5063
|
+
error: 'SFAP JWT params.dynamicParams must be a Record<string, string | boolean | number> when provided.',
|
|
5064
|
+
};
|
|
5065
|
+
}
|
|
5066
|
+
return { params: { scopes, dynamicParams } };
|
|
5067
|
+
}
|
|
5068
|
+
function isStringArray(value) {
|
|
5069
|
+
return Array.isArray(value) && value.every((s) => typeof s === 'string');
|
|
5070
|
+
}
|
|
5071
|
+
/**
|
|
5072
|
+
* A record whose values are JSON primitives (string, boolean, or number) — the
|
|
5073
|
+
* value types the SFAP Lightning JWT Service accepts for a dynamic parameter
|
|
5074
|
+
* (see {@link SfapDynamicParamValue}). Booleans/numbers are forwarded natively
|
|
5075
|
+
* (not stringified) so claim handlers that gate on a native `Boolean` see the
|
|
5076
|
+
* intended value. Nested objects/arrays, `null`, and `undefined` are rejected.
|
|
5077
|
+
*/
|
|
5078
|
+
function isPrimitiveRecord(value) {
|
|
5079
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
5080
|
+
return false;
|
|
5081
|
+
}
|
|
5082
|
+
return Object.values(value).every((v) => typeof v === 'string' || typeof v === 'boolean' || typeof v === 'number');
|
|
5083
|
+
}
|
|
5084
|
+
/**
|
|
5085
|
+
* Build the SFAP mint request body from the coerced params: `scopes` joined into a
|
|
5086
|
+
* single space-delimited string, `dynamicParams` mapped to `dynamicParameters.items`.
|
|
5087
|
+
* Empty scopes / dynamic params are omitted.
|
|
5088
|
+
*/
|
|
5089
|
+
function buildRequestBody(params) {
|
|
5090
|
+
const body = {};
|
|
5091
|
+
if (params.scopes && params.scopes.length > 0) {
|
|
5092
|
+
body.scopes = params.scopes.join(' ');
|
|
5093
|
+
}
|
|
5094
|
+
if (params.dynamicParams) {
|
|
5095
|
+
const items = Object.entries(params.dynamicParams).map(([name, value]) => ({ name, value }));
|
|
5096
|
+
if (items.length > 0) {
|
|
5097
|
+
body.dynamicParameters = { items };
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
return body;
|
|
5101
|
+
}
|
|
5102
|
+
|
|
5103
|
+
/**
|
|
5104
|
+
* Parameterized SFAP JWT resolver that mints over **Aura transport** instead of a
|
|
5105
|
+
* direct HTTP `fetch`.
|
|
5106
|
+
*
|
|
5107
|
+
* It dispatches the SFAP Lightning JWT Service's parameterized POST via its
|
|
5108
|
+
* auto-generated Aura controller method
|
|
5109
|
+
* `SalesforceApiPlatformController.postSFAPLightningJwtService` (generated by
|
|
5110
|
+
* `@ConnectSignature(..., generateAuraMethod = true)` on the Connect resource).
|
|
5111
|
+
* The mint inputs ride as the single `requestBody` named param, in the same
|
|
5112
|
+
* `{ scopes, dynamicParameters: { items } }` shape the HTTP resolver POSTs — built
|
|
5113
|
+
* by the shared {@link buildRequestBody}, so the two transports stay in lockstep.
|
|
5114
|
+
*
|
|
5115
|
+
* Why Aura (not the HTTP resolver): the mint endpoint is a same-origin core
|
|
5116
|
+
* resource, and routing it over Aura keeps session/CSRF handling inside the Aura
|
|
5117
|
+
* stack rather than issuing a credentialed cross-cutting `fetch` from the client.
|
|
5118
|
+
* This mirrors the legacy parameterless `platformSfapJwtResolver` in
|
|
5119
|
+
* `network-sfap.ts`, which already mints over Aura via the `getSFAPLightningJwtService`
|
|
5120
|
+
* generated method — this is the parameterized sibling of that call.
|
|
5121
|
+
*
|
|
5122
|
+
* A `JwtResolver` is invoked directly by `JwtManager` (not through Luvio's
|
|
5123
|
+
* `appRouter`/`ResourceRequest` pipeline), so the correct mechanism is a direct
|
|
5124
|
+
* named-controller `dispatchAuraAction`, not the `auraNetworkAdapter`/connect-route
|
|
5125
|
+
* table. The SFAP JWT endpoint is not registered as a connect-over-Aura route, and
|
|
5126
|
+
* a resolver has no `ResourceRequest` for the router to look up.
|
|
5127
|
+
*/
|
|
5128
|
+
class ParameterizedSfapJwtAuraResolver {
|
|
5129
|
+
getJwt(params) {
|
|
5130
|
+
return new Promise((resolve, reject) => {
|
|
5131
|
+
if (params === undefined) {
|
|
5132
|
+
// Misuse: the dispatching resolver routes parameterless calls to the
|
|
5133
|
+
// legacy resolver. Reject so production fails loudly here.
|
|
5134
|
+
reject('ParameterizedSfapJwtAuraResolver requires JwtMintParams. The legacy parameterless path should be served by platformSfapJwtResolver.');
|
|
5135
|
+
return;
|
|
5136
|
+
}
|
|
5137
|
+
const coerced = coerceToSfapParams(params);
|
|
5138
|
+
if ('error' in coerced) {
|
|
5139
|
+
reject(coerced.error);
|
|
5140
|
+
return;
|
|
5141
|
+
}
|
|
5142
|
+
// The mint inputs become the single `requestBody` named param of the
|
|
5143
|
+
// generated Aura method (Aura binds top-level keys to the controller
|
|
5144
|
+
// method's named args). Reuses the HTTP resolver's body builder so both
|
|
5145
|
+
// transports send an identical shape.
|
|
5146
|
+
const requestBody = buildRequestBody(coerced.params);
|
|
5147
|
+
// No fetchImpl / requestInterceptor / CSRF / credentials handling here —
|
|
5148
|
+
// the Aura stack owns session + CSRF. Matches the legacy resolver's call.
|
|
5149
|
+
dispatchAuraAction(`${SFAPController}.${SFAPJwtPostMethod}`, { requestBody }, defaultActionConfig)
|
|
5150
|
+
.then((response) => {
|
|
5151
|
+
const body = response.body;
|
|
5152
|
+
if (!body || typeof body.jwt !== 'string' || typeof body.baseUri !== 'string') {
|
|
5153
|
+
// Never serialize the body into the error — it may carry a JWT.
|
|
5154
|
+
reject('SFAP JWT response missing required fields (jwt, baseUri)');
|
|
5155
|
+
return;
|
|
5156
|
+
}
|
|
5157
|
+
resolve({ jwt: body.jwt, extraInfo: { baseUri: body.baseUri } });
|
|
5158
|
+
})
|
|
5159
|
+
.catch((error) => {
|
|
5160
|
+
// Error mapping ported from the legacy platformSfapJwtResolver
|
|
5161
|
+
// (network-sfap.ts): plain Errors carry a message; non-500
|
|
5162
|
+
// AuraFetchResponses are ConnectInJava errors with a typed body;
|
|
5163
|
+
// 500s carry an { error } body.
|
|
5164
|
+
if (error instanceof Error) {
|
|
5165
|
+
reject(error.message);
|
|
5166
|
+
return;
|
|
5167
|
+
}
|
|
5168
|
+
const { status } = error;
|
|
5169
|
+
if (status !== HttpStatusCode$2.ServerError) {
|
|
5170
|
+
reject(error.body.message);
|
|
5171
|
+
return;
|
|
5172
|
+
}
|
|
5173
|
+
reject(error.body.error);
|
|
5174
|
+
});
|
|
5175
|
+
});
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
|
|
4888
5179
|
function e(e){this.message=e;}e.prototype=new Error,e.prototype.name="InvalidCharacterError";"undefined"!=typeof window&&window.atob&&window.atob.bind(window)||function(r){var t=String(r).replace(/=+$/,"");if(t.length%4==1)throw new e("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,o,a=0,i=0,c="";o=t.charAt(i++);~o&&(n=a%4?64*n+o:o,a++%4)?c+=String.fromCharCode(255&n>>(-2*a&6)):0)o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(o);return c};function n(e){this.message=e;}n.prototype=new Error,n.prototype.name="InvalidTokenError";
|
|
4889
5180
|
|
|
4890
5181
|
/**
|
|
@@ -5701,7 +5992,7 @@ function getEnvironmentSetting(name) {
|
|
|
5701
5992
|
}
|
|
5702
5993
|
return undefined;
|
|
5703
5994
|
}
|
|
5704
|
-
// version: 1.428.0-
|
|
5995
|
+
// version: 1.428.0-dev21-133a7da4f5
|
|
5705
5996
|
|
|
5706
5997
|
const environmentHasAura = typeof window !== 'undefined' && typeof window.$A !== 'undefined';
|
|
5707
5998
|
const defaultConfig = {
|
|
@@ -5961,12 +6252,20 @@ function buildLuvioPageScopedCacheRequestInterceptor() {
|
|
|
5961
6252
|
};
|
|
5962
6253
|
}
|
|
5963
6254
|
|
|
6255
|
+
// Connect REST returns errors as an array `[{ errorCode, ... }, ...]`; the Aura
|
|
6256
|
+
// action path uses a bare object `{ errorCode, ... }`. HTTP-based adapters hit
|
|
6257
|
+
// Connect REST directly, so the body arrives array-shaped and a plain
|
|
6258
|
+
// `body.errorCode` read would miss it. Handle both shapes.
|
|
6259
|
+
function isInvalidSession(body) {
|
|
6260
|
+
const errorCode = Array.isArray(body) ? body[0] && body[0].errorCode : body && body.errorCode;
|
|
6261
|
+
return errorCode === 'INVALID_SESSION_ID';
|
|
6262
|
+
}
|
|
5964
6263
|
function buildLexRuntimeAuthExpirationRedirectResponseInterceptor(logger) {
|
|
5965
6264
|
return async (response) => {
|
|
5966
6265
|
if (response.status === 401) {
|
|
5967
6266
|
try {
|
|
5968
6267
|
const coercedResponse = (await coerceResponseToFetchResponse(response.clone()));
|
|
5969
|
-
if (coercedResponse.body
|
|
6268
|
+
if (isInvalidSession(coercedResponse.body)) {
|
|
5970
6269
|
logger.warn(`Received ${response.status} status code from LEX runtime service`);
|
|
5971
6270
|
// Fire the event asynchronously, similar to the legacy setTimeout pattern
|
|
5972
6271
|
window.setTimeout(() => {
|
|
@@ -5984,7 +6283,7 @@ function buildLexRuntimeAuthExpirationRedirectResponseInterceptor(logger) {
|
|
|
5984
6283
|
function buildLexRuntimeLuvioAuthExpirationRedirectResponseInterceptor() {
|
|
5985
6284
|
return async (response) => {
|
|
5986
6285
|
if (response.status === 401) {
|
|
5987
|
-
if (response.body
|
|
6286
|
+
if (isInvalidSession(response.body)) {
|
|
5988
6287
|
window.setTimeout(() => {
|
|
5989
6288
|
dispatchGlobalEvent('aura:invalidSession');
|
|
5990
6289
|
}, 0);
|
|
@@ -6883,12 +7182,12 @@ class CsrfTokenRetryPolicy extends RetryPolicy {
|
|
|
6883
7182
|
if (context.attempt >= this.config.maxRetries) {
|
|
6884
7183
|
return false;
|
|
6885
7184
|
}
|
|
6886
|
-
//
|
|
6887
|
-
if (result.status !== 400) {
|
|
7185
|
+
// Connect may return 401 or 400 for invalid CSRF scenarios
|
|
7186
|
+
if (result.status !== 400 && result.status !== 401) {
|
|
6888
7187
|
return false;
|
|
6889
7188
|
}
|
|
6890
7189
|
// Check if this is a CSRF error by examining the response body
|
|
6891
|
-
// This avoids retrying all 400s (validation errors,
|
|
7190
|
+
// This avoids retrying all 400s/401s (validation errors, auth errors, etc.)
|
|
6892
7191
|
return await isCsrfError(result);
|
|
6893
7192
|
}
|
|
6894
7193
|
/**
|
|
@@ -6975,14 +7274,143 @@ function buildCsrfRetryInterceptor() {
|
|
|
6975
7274
|
};
|
|
6976
7275
|
}
|
|
6977
7276
|
|
|
7277
|
+
/**
|
|
7278
|
+
* The context-seed key under which a custom command forwards its JWT mint
|
|
7279
|
+
* parameters. The framework's `buildServiceDescriptor` merges the request init's
|
|
7280
|
+
* `__contextSeed` onto the per-request interceptor context, so this interceptor
|
|
7281
|
+
* reads the params off `context[JWT_MINT_PARAMS_SEED_KEY]`.
|
|
7282
|
+
*
|
|
7283
|
+
* This is a cross-team contract: the custom command writes this key, this
|
|
7284
|
+
* interceptor reads it. It is intentionally an opaque key — OneStore does not
|
|
7285
|
+
* define it and never inspects the param shape; the typed shape lives in the
|
|
7286
|
+
* resolver.
|
|
7287
|
+
*/
|
|
7288
|
+
const JWT_MINT_PARAMS_SEED_KEY = 'jwtMintParams';
|
|
7289
|
+
/**
|
|
7290
|
+
* Returns `true` if the fetch arguments already carry an `Authorization` header,
|
|
7291
|
+
* across the three shapes the framework's `setHeader` handles: a `Request`
|
|
7292
|
+
* resource's own headers, an `options.headers` `Headers` instance, or a plain
|
|
7293
|
+
* record. The descriptor's guarded legacy interceptor uses this to skip when the
|
|
7294
|
+
* parameterized interceptor has already authorized the request — avoiding a second
|
|
7295
|
+
* mint and the throw `setHeaderAuthorization` raises on an existing header.
|
|
7296
|
+
*/
|
|
7297
|
+
function hasAuthorizationHeader([resource, options]) {
|
|
7298
|
+
if (resource instanceof Request && resource.headers.has('Authorization')) {
|
|
7299
|
+
return true;
|
|
7300
|
+
}
|
|
7301
|
+
const headers = options?.headers;
|
|
7302
|
+
if (headers === undefined) {
|
|
7303
|
+
return false;
|
|
7304
|
+
}
|
|
7305
|
+
if (headers instanceof Headers) {
|
|
7306
|
+
return headers.has('Authorization');
|
|
7307
|
+
}
|
|
7308
|
+
if (Array.isArray(headers)) {
|
|
7309
|
+
return headers.some(([name]) => name.toLowerCase() === 'authorization');
|
|
7310
|
+
}
|
|
7311
|
+
return Reflect.has(headers, 'Authorization');
|
|
7312
|
+
}
|
|
7313
|
+
/**
|
|
7314
|
+
* Builds the request interceptor that bridges the per-request context seed to the
|
|
7315
|
+
* dispatching SFAP JwtManager for the **parameterized** mint path.
|
|
7316
|
+
*
|
|
7317
|
+
* For a parameterized SFAP command, the context carries `jwtMintParams`. This
|
|
7318
|
+
* interceptor reads them, calls `jwtManager.getJwt(params)` (which the dispatching
|
|
7319
|
+
* resolver routes to the parameterized resolver), attaches the `Authorization`
|
|
7320
|
+
* header, and rewrites the request URL via the minted `baseUri`. The presence of
|
|
7321
|
+
* that `Authorization` header is the signal the descriptor's guarded legacy
|
|
7322
|
+
* interceptor uses to skip — so the legacy path does not mint a second time.
|
|
7323
|
+
*
|
|
7324
|
+
* For a legacy parameterless command the context carries no `jwtMintParams`, so
|
|
7325
|
+
* this interceptor **early-returns on its first line** and the request flows
|
|
7326
|
+
* unchanged to the legacy `buildJwtRequestHeaderInterceptor` — guaranteeing zero
|
|
7327
|
+
* behavior change for non-opted-in adapters.
|
|
7328
|
+
*
|
|
7329
|
+
* Lives in `lds-lightning-platform` (not OneStore) beside the existing SFAP/CSRF
|
|
7330
|
+
* interceptors, per the JWT-parameterization ADR §5: OneStore provides only the
|
|
7331
|
+
* generic interceptor mechanism and the opaque context-seed channel; the service-
|
|
7332
|
+
* specific bridge is a runtime-layer concern.
|
|
7333
|
+
*
|
|
7334
|
+
* @param jwtManager - the dispatching SFAP JwtManager
|
|
7335
|
+
* @param jwtRequestModifier - applies the minted `extraInfo.baseUri` to the request URL
|
|
7336
|
+
*/
|
|
7337
|
+
function buildJwtParameterizationInterceptor(jwtManager, jwtRequestModifier = (_extraInfo, fetchArgs) => fetchArgs) {
|
|
7338
|
+
return (fetchArgs, context) => {
|
|
7339
|
+
const jwtContext = context;
|
|
7340
|
+
const mintParams = jwtContext?.[JWT_MINT_PARAMS_SEED_KEY];
|
|
7341
|
+
// No mint params → not a parameterized request. Do nothing; the legacy
|
|
7342
|
+
// interceptor handles it. This MUST be the first statement so non-opted-in
|
|
7343
|
+
// SFAP commands observe no work at all.
|
|
7344
|
+
if (mintParams === undefined) {
|
|
7345
|
+
return resolvedPromiseLike$2(fetchArgs);
|
|
7346
|
+
}
|
|
7347
|
+
return resolvedPromiseLike$2(jwtManager.getJwt(mintParams)).then((token) => {
|
|
7348
|
+
const fetchArgsWithAuthorization = setHeaderAuthorization(token, fetchArgs);
|
|
7349
|
+
return token.extraInfo
|
|
7350
|
+
? jwtRequestModifier(token.extraInfo, fetchArgsWithAuthorization)
|
|
7351
|
+
: fetchArgsWithAuthorization;
|
|
7352
|
+
});
|
|
7353
|
+
};
|
|
7354
|
+
}
|
|
7355
|
+
|
|
6978
7356
|
const SFAP_BASE_URL = 'api.salesforce.com';
|
|
7357
|
+
// The fetch-service tag the Data360 (direct-to-Data Cloud) custom command binds
|
|
7358
|
+
// to. Distinct from SFAP's `sfap_api` so the Data360 host-rewrite interceptor only
|
|
7359
|
+
// ever runs for that command — zero blast radius on the SFAP fleet.
|
|
7360
|
+
//
|
|
7361
|
+
// The tag is deliberately named after the *mechanism* (the `cdp_url` host rewrite),
|
|
7362
|
+
// not a capability, because this whole descriptor is interim: it exists only while
|
|
7363
|
+
// the mint service returns the fixed SFAP `baseUri` regardless of platform. When the
|
|
7364
|
+
// service returns the correct per-platform `baseUri`, the data 360 route can be re-evaluated.
|
|
7365
|
+
// This mechanism is NOT intended to be re-used outside of this initial context.
|
|
7366
|
+
// This is a client-side routing key only.
|
|
7367
|
+
const DATA_360_CDP_URL_REWRITE_AUTH_SCOPE = 'data_360_cdp_url_rewrite';
|
|
7368
|
+
// The minted-JWT claim carrying the Data Cloud tenant-specific endpoint (TSE).
|
|
7369
|
+
// The server's `SFAPJwtClaimHandlerImpl` emits it for CDP-provisioned orgs as a
|
|
7370
|
+
// verbatim passthrough of `DataCloudTenant.getApiEndpoint()` — no normalization.
|
|
7371
|
+
// The exact FORM therefore varies and must not be assumed: it may be a bare host
|
|
7372
|
+
// (`<hash>.c360a.salesforce.com`) or a full URL with scheme
|
|
7373
|
+
// (`https://a360.cdp.<region>.aws.sfdc.cl`), and the suffix is cloud-dependent
|
|
7374
|
+
// (commercial `.salesforce.com`, substrate `.aws.sfdc.cl`, GovCloud
|
|
7375
|
+
// `.salesforce.mil`). This is why the interceptor strips any scheme before parsing
|
|
7376
|
+
// and applies no host-suffix policy — see the interceptor docblock's trust boundary.
|
|
7377
|
+
const CDP_URL_CLAIM = 'cdp_url';
|
|
7378
|
+
function buildDispatchingSfapJwtResolver(legacyResolver, parameterizedResolver) {
|
|
7379
|
+
return {
|
|
7380
|
+
getJwt(params) {
|
|
7381
|
+
if (params === undefined) {
|
|
7382
|
+
return legacyResolver.getJwt();
|
|
7383
|
+
}
|
|
7384
|
+
return parameterizedResolver.getJwt(params);
|
|
7385
|
+
},
|
|
7386
|
+
};
|
|
7387
|
+
}
|
|
7388
|
+
// The parameterized mint is dispatched over **Aura transport**:
|
|
7389
|
+
// the resolver calls the SFAP Lightning JWT Service's auto-generated Aura method
|
|
7390
|
+
// `SalesforceApiPlatformController.postSFAPLightningJwtService`, so session + CSRF
|
|
7391
|
+
// are handled inside the Aura stack. This mirrors the legacy parameterless
|
|
7392
|
+
// `platformSfapJwtResolver`, which already mints over Aura. The minted JWT is then
|
|
7393
|
+
// attached as a Bearer token on the downstream SFAP API request (which stays HTTP).
|
|
7394
|
+
const parameterizedSfapJwtResolver = new ParameterizedSfapJwtAuraResolver();
|
|
7395
|
+
const sfapJwtResolver = buildDispatchingSfapJwtResolver(platformSfapJwtResolver, parameterizedSfapJwtResolver);
|
|
6979
7396
|
const sfapJwtRepository = new JwtRepository();
|
|
6980
|
-
const sfapJwtManager = new JwtManager(sfapJwtRepository,
|
|
7397
|
+
const sfapJwtManager = new JwtManager(sfapJwtRepository, sfapJwtResolver);
|
|
7398
|
+
function prefetchSfapJwt() {
|
|
7399
|
+
const maybePromise = sfapJwtManager.getJwt();
|
|
7400
|
+
if ('then' in maybePromise) {
|
|
7401
|
+
return maybePromise.then(() => undefined).catch(() => undefined);
|
|
7402
|
+
}
|
|
7403
|
+
return Promise.resolve(undefined);
|
|
7404
|
+
}
|
|
6981
7405
|
function buildJwtAuthorizedSfapFetchServiceDescriptor(logger) {
|
|
6982
7406
|
const jwtAuthorizedFetchService = buildServiceDescriptor$2({
|
|
6983
7407
|
createContext: createInstrumentationIdContext(),
|
|
6984
|
-
request: [
|
|
6985
|
-
|
|
7408
|
+
request: [
|
|
7409
|
+
buildThirdPartyTrackerRegisterInterceptor(),
|
|
7410
|
+
buildJwtParameterizationInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger)),
|
|
7411
|
+
// Guarded so it is skipped once the parameterized interceptor handled the request.
|
|
7412
|
+
buildGuardedLegacyJwtRequestInterceptor(logger),
|
|
7413
|
+
],
|
|
6986
7414
|
finally: [buildThirdPartyTrackerFinishInterceptor()],
|
|
6987
7415
|
});
|
|
6988
7416
|
return {
|
|
@@ -6990,6 +7418,46 @@ function buildJwtAuthorizedSfapFetchServiceDescriptor(logger) {
|
|
|
6990
7418
|
tags: { authenticationScopes: 'sfap_api' },
|
|
6991
7419
|
};
|
|
6992
7420
|
}
|
|
7421
|
+
/**
|
|
7422
|
+
* Returns a service descriptor for the **direct-to-Data360** custom command
|
|
7423
|
+
* (CDP Query v3, host `*.c360a.salesforce.com`).
|
|
7424
|
+
*
|
|
7425
|
+
* Unlike SFAP, the per-tenant base host is NOT returned in the mint response's
|
|
7426
|
+
* `baseUri` (which is the fixed SFAP host); it rides on the minted JWT as the
|
|
7427
|
+
* `cdp_url` claim. The standard `JwtRequestModifier` only receives `extraInfo`,
|
|
7428
|
+
* never the decoded claims, so the host rewrite cannot be expressed as a modifier
|
|
7429
|
+
* — it must read the token directly. This descriptor therefore uses a bespoke
|
|
7430
|
+
* interceptor ({@link buildData360HostRewriteInterceptor}) that mints the
|
|
7431
|
+
* parameterized SFAP JWT, attaches the Bearer token, decodes `cdp_url`, and
|
|
7432
|
+
* rewrites the request host to that TSE.
|
|
7433
|
+
*
|
|
7434
|
+
* It reuses the module's `sfapJwtManager`: `cdp_url` is emitted on the same
|
|
7435
|
+
* `SFAP_API`-scope JWT, so the Data360 command mints the same kind of token — it
|
|
7436
|
+
* just forwards the data-cloud scopes via the context-seed `jwtMintParams`.
|
|
7437
|
+
*
|
|
7438
|
+
* Gated behind its own `data_360_cdp_url_rewrite` auth-scope tag so it binds ONLY to
|
|
7439
|
+
* the Data360 command — the SFAP fleet never flows past this interceptor.
|
|
7440
|
+
*/
|
|
7441
|
+
function buildJwtAuthorizedData360FetchServiceDescriptor(logger) {
|
|
7442
|
+
const data360FetchService = buildServiceDescriptor$2({
|
|
7443
|
+
createContext: createInstrumentationIdContext(),
|
|
7444
|
+
request: [
|
|
7445
|
+
buildThirdPartyTrackerRegisterInterceptor(),
|
|
7446
|
+
buildData360HostRewriteInterceptor(logger),
|
|
7447
|
+
// Compression runs LAST, after the host-rewrite interceptor has minted,
|
|
7448
|
+
// attached the Bearer token, and rewritten the URL — so it only ever sees
|
|
7449
|
+
// the final request body. It is a strict pass-through: bodies that are
|
|
7450
|
+
// missing, non-string, or under the 1KB threshold flow through untouched,
|
|
7451
|
+
// preserving the tuple (URL + Authorization header) the rewrite produced.
|
|
7452
|
+
buildCompressionInterceptor({ algorithm: 'gzip' }),
|
|
7453
|
+
],
|
|
7454
|
+
finally: [buildThirdPartyTrackerFinishInterceptor()],
|
|
7455
|
+
});
|
|
7456
|
+
return {
|
|
7457
|
+
...data360FetchService,
|
|
7458
|
+
tags: { authenticationScopes: DATA_360_CDP_URL_REWRITE_AUTH_SCOPE },
|
|
7459
|
+
};
|
|
7460
|
+
}
|
|
6993
7461
|
/**
|
|
6994
7462
|
* Returns a service descriptor for a fetch service that includes one-off copilot
|
|
6995
7463
|
* hacks. This fetch service is not intended for use by anything other than
|
|
@@ -7062,8 +7530,13 @@ function buildUnauthorizedFetchServiceDescriptor() {
|
|
|
7062
7530
|
tags: { authenticationScopes: '' },
|
|
7063
7531
|
};
|
|
7064
7532
|
}
|
|
7065
|
-
|
|
7066
|
-
|
|
7533
|
+
/**
|
|
7534
|
+
* The `JwtRequestModifier` shared by both the legacy and parameterized SFAP
|
|
7535
|
+
* interceptors: it rewrites the request URL's host/protocol to the minted
|
|
7536
|
+
* `extraInfo.baseUri` (only for `api.salesforce.com` resources).
|
|
7537
|
+
*/
|
|
7538
|
+
function buildSfapJwtRequestModifier(logger) {
|
|
7539
|
+
return ({ baseUri }, [resource, request]) => {
|
|
7067
7540
|
if (typeof resource !== 'string' && !(resource instanceof URL)) {
|
|
7068
7541
|
// istanbul ignore else: this will not be tested in NODE_ENV = production for test coverage
|
|
7069
7542
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -7081,8 +7554,91 @@ function buildJwtRequestInterceptor(logger) {
|
|
|
7081
7554
|
url.protocol = overrideUrl.protocol;
|
|
7082
7555
|
return [url, request];
|
|
7083
7556
|
};
|
|
7084
|
-
|
|
7085
|
-
|
|
7557
|
+
}
|
|
7558
|
+
function buildJwtRequestInterceptor(logger) {
|
|
7559
|
+
return buildJwtRequestHeaderInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger));
|
|
7560
|
+
}
|
|
7561
|
+
/**
|
|
7562
|
+
* Request interceptor for the direct-to-Data360 command. It cannot use the
|
|
7563
|
+
* standard `JwtRequestModifier` seam because the tenant host is a JWT *claim*
|
|
7564
|
+
* (`cdp_url`), and modifiers only receive `extraInfo`. So — like the copilot
|
|
7565
|
+
* interceptor that reads `decodedInfo.iss` — it holds the token directly:
|
|
7566
|
+
*
|
|
7567
|
+
* 1. Read the command's `jwtMintParams` off the per-request context seed and
|
|
7568
|
+
* mint (via the parameterized SFAP resolver, routed by the dispatching
|
|
7569
|
+
* resolver inside `sfapJwtManager`). Without mint params there is no token
|
|
7570
|
+
* to authorize with, so leave the request untouched.
|
|
7571
|
+
* 2. Decode `cdp_url` from the JWT and resolve the tenant-specific endpoint
|
|
7572
|
+
* (TSE). `cdp_url` is a bare host, so a scheme is prepended before parsing
|
|
7573
|
+
* and the protocol is forced to `https:`.
|
|
7574
|
+
* 3. Only after a usable `cdp_url` is confirmed, attach `Authorization: Bearer
|
|
7575
|
+
* <jwt>` and rewrite the request URL's host/protocol to the TSE.
|
|
7576
|
+
*
|
|
7577
|
+
* **Trust boundary.** `cdp_url` is a claim on a first-party, server-minted JWT
|
|
7578
|
+
* delivered over the same-origin Aura transport, so the interceptor does NOT police
|
|
7579
|
+
* its contents (no host-suffix or format allowlist) — the routing host is the
|
|
7580
|
+
* minting service's responsibility, and this mirrors the SFAP sibling, which
|
|
7581
|
+
* likewise trusts the server-provided `baseUri` rewrite target. The one guard that
|
|
7582
|
+
* remains is shape hygiene: if `cdp_url` is absent/empty/non-string (org not
|
|
7583
|
+
* CDP-provisioned, so the server's claim gate did not fire) the interceptor throws
|
|
7584
|
+
* a developer-facing error instead of an opaque parse failure. A minted token is
|
|
7585
|
+
* never attached until a usable host is in hand.
|
|
7586
|
+
*/
|
|
7587
|
+
function buildData360HostRewriteInterceptor(logger) {
|
|
7588
|
+
return (fetchArgs, context) => {
|
|
7589
|
+
const mintParams = context?.[JWT_MINT_PARAMS_SEED_KEY];
|
|
7590
|
+
// No mint params → not a parameterized Data360 request. Nothing to do.
|
|
7591
|
+
if (mintParams === undefined) {
|
|
7592
|
+
return resolvedPromiseLike$2(fetchArgs);
|
|
7593
|
+
}
|
|
7594
|
+
return resolvedPromiseLike$2(sfapJwtManager.getJwt(mintParams)).then((token) => {
|
|
7595
|
+
const [resource, request] = fetchArgs;
|
|
7596
|
+
if (typeof resource !== 'string' && !(resource instanceof URL)) {
|
|
7597
|
+
// istanbul ignore else: not exercised under NODE_ENV=production
|
|
7598
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
7599
|
+
throw new Error('Data360 fetch service expects a string or URL resource');
|
|
7600
|
+
}
|
|
7601
|
+
return fetchArgs;
|
|
7602
|
+
}
|
|
7603
|
+
const cdpUrl = token.decodedInfo?.[CDP_URL_CLAIM];
|
|
7604
|
+
// Require a non-empty string. This is shape hygiene, NOT a content
|
|
7605
|
+
// policy: the routing host is the minting service's responsibility, and
|
|
7606
|
+
// we deliberately do not couple to its contents (no host-suffix/format
|
|
7607
|
+
// check — the SFAP sibling likewise trusts its server-provided rewrite
|
|
7608
|
+
// target). We only confirm we were handed a usable host string; a
|
|
7609
|
+
// missing/empty/non-string claim (org not Data Cloud provisioned, so the
|
|
7610
|
+
// server's claim gate did not fire) fails with a clear error rather than
|
|
7611
|
+
// an opaque `new URL` TypeError. The `typeof` check also narrows `cdpUrl`
|
|
7612
|
+
// to `string` for the `.replace` below.
|
|
7613
|
+
if (typeof cdpUrl !== 'string' || cdpUrl.length === 0) {
|
|
7614
|
+
logger.warn(`Data360 fetch service: minted JWT has no usable "${CDP_URL_CLAIM}" claim. The org may not be Data Cloud provisioned.`);
|
|
7615
|
+
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
7616
|
+
throw new Error(`Data360 fetch service: minted JWT has no usable "${CDP_URL_CLAIM}" claim; cannot route the request to a Data Cloud tenant endpoint.`);
|
|
7617
|
+
}
|
|
7618
|
+
// `cdp_url` is a bare host (no scheme); prepend https so `new URL`
|
|
7619
|
+
// parses it as an origin. Force https regardless of any scheme in the
|
|
7620
|
+
// claim — the TSE is always TLS.
|
|
7621
|
+
const tse = new URL(`https://${cdpUrl.replace(/^[a-z]+:\/\//i, '')}`);
|
|
7622
|
+
const authorizedArgs = setHeaderAuthorization(token, [resource, request]);
|
|
7623
|
+
const url = typeof resource === 'string' ? new URL(resource) : new URL(resource.toString());
|
|
7624
|
+
url.host = tse.host;
|
|
7625
|
+
url.protocol = 'https:';
|
|
7626
|
+
return [url, authorizedArgs[1]];
|
|
7627
|
+
});
|
|
7628
|
+
};
|
|
7629
|
+
}
|
|
7630
|
+
/**
|
|
7631
|
+
* Wraps the legacy `buildJwtRequestHeaderInterceptor` with a pass-through guard:
|
|
7632
|
+
* when the parameterized interceptor has already authorized the request (an
|
|
7633
|
+
* `Authorization` header is present), this returns `args` untouched so the legacy
|
|
7634
|
+
* interceptor does not mint a second time or attempt to set a duplicate
|
|
7635
|
+
* Authorization header (which `setHeaderAuthorization` throws on). For every legacy
|
|
7636
|
+
* (non-parameterized) request no Authorization header is present yet, so the legacy
|
|
7637
|
+
* interceptor runs exactly as before — a strict pass-through.
|
|
7638
|
+
*/
|
|
7639
|
+
function buildGuardedLegacyJwtRequestInterceptor(logger) {
|
|
7640
|
+
const legacyInterceptor = buildJwtRequestInterceptor(logger);
|
|
7641
|
+
return (args) => hasAuthorizationHeader(args) ? resolvedPromiseLike$2(args) : legacyInterceptor(args);
|
|
7086
7642
|
}
|
|
7087
7643
|
|
|
7088
7644
|
const PDL_EXECUTE_ASYNC_OPTIONS = {
|
|
@@ -10478,6 +11034,7 @@ function initializeOneStore(luvio) {
|
|
|
10478
11034
|
buildLexRuntimeDefaultFetchServiceDescriptor(loggerService, retryService),
|
|
10479
11035
|
buildUnauthorizedFetchServiceDescriptor(),
|
|
10480
11036
|
buildJwtAuthorizedSfapFetchServiceDescriptor(loggerService),
|
|
11037
|
+
buildJwtAuthorizedData360FetchServiceDescriptor(loggerService),
|
|
10481
11038
|
buildCopilotFetchServiceDescriptor(loggerService),
|
|
10482
11039
|
buildAuraNetworkService(),
|
|
10483
11040
|
buildServiceDescriptor$j(instrumentationServiceDescriptor.service),
|
|
@@ -10532,4 +11089,4 @@ function ldsEngineCreator() {
|
|
|
10532
11089
|
}
|
|
10533
11090
|
|
|
10534
11091
|
export { LexRequestStrategy, PdlPrefetcherEventType, PdlRequestPriority, buildPredictorForContext, configService, ldsEngineCreator as default, initializeLDS, initializeOneStore, notifyUpdateAvailableFactory, registerRequestStrategy, saveRequestAsPrediction, subscribeToPrefetcherEvents, unregisterRequestStrategy, whenPredictionsReady };
|
|
10535
|
-
// version: 1.428.0-
|
|
11092
|
+
// version: 1.428.0-dev21-c9faba50c9
|