@salesforce/lds-runtime-aura 1.428.0-dev11 → 1.428.0-dev13
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 +447 -85
- package/dist/types/fetch-service-descriptors/jwt-authorized-fetch-service.d.ts +3 -0
- package/dist/types/network-sfap.d.ts +1 -0
- 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/sfap-jwt-mint-params.d.ts +69 -0
- package/package.json +42 -41
package/dist/ldsEngineCreator.js
CHANGED
|
@@ -1590,9 +1590,12 @@ const _FetchNetworkCommand = class _FetchNetworkCommand extends NetworkCommand {
|
|
|
1590
1590
|
this.services = services;
|
|
1591
1591
|
this.additionalNullResponses = [];
|
|
1592
1592
|
}
|
|
1593
|
-
fetch() {
|
|
1593
|
+
fetch(contextSeed) {
|
|
1594
1594
|
try {
|
|
1595
|
-
|
|
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);
|
|
1596
1599
|
} catch (reason) {
|
|
1597
1600
|
return resolvedPromiseLike$2(err$1(toError(reason)));
|
|
1598
1601
|
}
|
|
@@ -2760,7 +2763,7 @@ function buildServiceDescriptor$d(luvio) {
|
|
|
2760
2763
|
},
|
|
2761
2764
|
};
|
|
2762
2765
|
}
|
|
2763
|
-
// version: 1.428.0-
|
|
2766
|
+
// version: 1.428.0-dev13-ab893a1bbc
|
|
2764
2767
|
|
|
2765
2768
|
/*!
|
|
2766
2769
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -3113,7 +3116,7 @@ function buildServiceDescriptor$9(notifyRecordUpdateAvailable, getNormalizedLuvi
|
|
|
3113
3116
|
},
|
|
3114
3117
|
};
|
|
3115
3118
|
}
|
|
3116
|
-
// version: 1.428.0-
|
|
3119
|
+
// version: 1.428.0-dev13-ab893a1bbc
|
|
3117
3120
|
|
|
3118
3121
|
/*!
|
|
3119
3122
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -4201,11 +4204,13 @@ class JwtToken {
|
|
|
4201
4204
|
* @param _token - The JWT string.
|
|
4202
4205
|
* @param _decodedInfo - The decoded information from the JWT.
|
|
4203
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.
|
|
4204
4208
|
*/
|
|
4205
|
-
constructor(_token, _decodedInfo, _extraInfo) {
|
|
4209
|
+
constructor(_token, _decodedInfo, _extraInfo, _mintParams) {
|
|
4206
4210
|
this._token = _token;
|
|
4207
4211
|
this._decodedInfo = _decodedInfo;
|
|
4208
4212
|
this._extraInfo = _extraInfo;
|
|
4213
|
+
this._mintParams = _mintParams;
|
|
4209
4214
|
}
|
|
4210
4215
|
/**
|
|
4211
4216
|
* Get the JWT string.
|
|
@@ -4231,6 +4236,14 @@ class JwtToken {
|
|
|
4231
4236
|
get decodedInfo() {
|
|
4232
4237
|
return this._decodedInfo;
|
|
4233
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
|
+
}
|
|
4234
4247
|
/**
|
|
4235
4248
|
* Get the remaining time in seconds until the JWT expires.
|
|
4236
4249
|
*
|
|
@@ -4248,6 +4261,12 @@ class JwtToken {
|
|
|
4248
4261
|
return this.tokenRemainingSeconds <= 0;
|
|
4249
4262
|
}
|
|
4250
4263
|
}
|
|
4264
|
+
function cacheKeyFor(params) {
|
|
4265
|
+
if (params === void 0) {
|
|
4266
|
+
return "jwt:";
|
|
4267
|
+
}
|
|
4268
|
+
return `jwt:${stableJSONStringify$2(params) ?? ""}`;
|
|
4269
|
+
}
|
|
4251
4270
|
let defaultLogger = {
|
|
4252
4271
|
trace: () => {
|
|
4253
4272
|
},
|
|
@@ -4281,85 +4300,146 @@ class JwtRepository {
|
|
|
4281
4300
|
this.limitInSeconds = limitInSeconds;
|
|
4282
4301
|
this.defaultTokenTTLInSeconds = defaultTokenTTLInSeconds;
|
|
4283
4302
|
this.logger = logger;
|
|
4303
|
+
this._tokens = /* @__PURE__ */ new Map();
|
|
4304
|
+
this.timeoutHandlers = /* @__PURE__ */ new Map();
|
|
4284
4305
|
this.observers = [];
|
|
4285
4306
|
}
|
|
4286
4307
|
/**
|
|
4287
|
-
* 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.
|
|
4288
4311
|
*/
|
|
4289
4312
|
get token() {
|
|
4290
|
-
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));
|
|
4291
4322
|
}
|
|
4292
4323
|
/**
|
|
4293
|
-
* Set the
|
|
4324
|
+
* Set the cached token for the given mint params.
|
|
4294
4325
|
*
|
|
4295
4326
|
* @param token - JWT token as a string.
|
|
4296
4327
|
* @param extraInfo - Optional extra information.
|
|
4328
|
+
* @param params - The mint params identifying the token. Omit for the legacy global token.
|
|
4297
4329
|
*/
|
|
4298
|
-
setToken(token, extraInfo) {
|
|
4330
|
+
setToken(token, extraInfo, params) {
|
|
4299
4331
|
const decodedInfo = computeDecodedInfo(
|
|
4300
4332
|
token,
|
|
4301
4333
|
this.defaultTokenTTLInSeconds,
|
|
4302
4334
|
this.logger
|
|
4303
4335
|
);
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
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);
|
|
4307
4353
|
}
|
|
4308
4354
|
/**
|
|
4309
|
-
* 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.
|
|
4310
4358
|
*/
|
|
4311
|
-
|
|
4312
|
-
this.
|
|
4313
|
-
|
|
4359
|
+
clearAllTokens() {
|
|
4360
|
+
for (const key of Array.from(this.timeoutHandlers.keys())) {
|
|
4361
|
+
this.clearTimeoutHandler(key);
|
|
4362
|
+
}
|
|
4363
|
+
this._tokens.clear();
|
|
4314
4364
|
}
|
|
4315
4365
|
/**
|
|
4316
|
-
* 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.
|
|
4317
4370
|
*
|
|
4318
|
-
*
|
|
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.
|
|
4319
4376
|
*/
|
|
4320
4377
|
subscribeToTokenNearExpiration(cb) {
|
|
4321
4378
|
this.observers.push(cb);
|
|
4322
|
-
this.
|
|
4379
|
+
for (const key of this._tokens.keys()) {
|
|
4380
|
+
this.observeTokenExpiration(key);
|
|
4381
|
+
}
|
|
4323
4382
|
return () => {
|
|
4324
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
|
+
}
|
|
4325
4389
|
};
|
|
4326
4390
|
}
|
|
4327
4391
|
/**
|
|
4328
|
-
*
|
|
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.
|
|
4329
4395
|
*/
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4396
|
+
get size() {
|
|
4397
|
+
return this._tokens.size;
|
|
4398
|
+
}
|
|
4399
|
+
/**
|
|
4400
|
+
* Clear the timeout handler for a specific cache key.
|
|
4401
|
+
*/
|
|
4402
|
+
clearTimeoutHandler(key) {
|
|
4403
|
+
const handler = this.timeoutHandlers.get(key);
|
|
4404
|
+
if (handler !== void 0) {
|
|
4405
|
+
clearTimeout(handler);
|
|
4406
|
+
this.timeoutHandlers.delete(key);
|
|
4333
4407
|
}
|
|
4334
4408
|
}
|
|
4335
4409
|
/**
|
|
4336
|
-
* Observe and handle token
|
|
4410
|
+
* Observe and handle expiration of the token at the given cache key.
|
|
4337
4411
|
*/
|
|
4338
|
-
observeTokenExpiration() {
|
|
4339
|
-
this.clearTimeoutHandler();
|
|
4340
|
-
|
|
4412
|
+
observeTokenExpiration(key) {
|
|
4413
|
+
this.clearTimeoutHandler(key);
|
|
4414
|
+
const token = this._tokens.get(key);
|
|
4415
|
+
if (this.observers.length === 0 || token === void 0) {
|
|
4341
4416
|
return;
|
|
4342
4417
|
}
|
|
4343
|
-
|
|
4344
|
-
() => this.notifyTokenIsExpiring(),
|
|
4345
|
-
this.computeTimeoutTimeInMs()
|
|
4418
|
+
const handler = setTimeout(
|
|
4419
|
+
() => this.notifyTokenIsExpiring(key),
|
|
4420
|
+
this.computeTimeoutTimeInMs(token)
|
|
4346
4421
|
);
|
|
4422
|
+
this.timeoutHandlers.set(key, handler);
|
|
4347
4423
|
}
|
|
4348
4424
|
/**
|
|
4349
|
-
* Compute the timeout time in milliseconds.
|
|
4425
|
+
* Compute the timeout time in milliseconds for the given token.
|
|
4350
4426
|
*/
|
|
4351
|
-
computeTimeoutTimeInMs() {
|
|
4352
|
-
const remainingSeconds =
|
|
4353
|
-
|
|
4427
|
+
computeTimeoutTimeInMs(token) {
|
|
4428
|
+
const remainingSeconds = token.tokenRemainingSeconds;
|
|
4429
|
+
const timeoutTimeInSeconds = remainingSeconds - this.limitInSeconds;
|
|
4354
4430
|
return timeoutTimeInSeconds < 0 ? 0 : timeoutTimeInSeconds * 1e3;
|
|
4355
4431
|
}
|
|
4356
4432
|
/**
|
|
4357
|
-
* Notify all observers that the token is expiring.
|
|
4433
|
+
* Notify all observers that the token at the given cache key is expiring.
|
|
4358
4434
|
*/
|
|
4359
|
-
notifyTokenIsExpiring() {
|
|
4435
|
+
notifyTokenIsExpiring(key) {
|
|
4436
|
+
const token = this._tokens.get(key);
|
|
4437
|
+
if (token === void 0) {
|
|
4438
|
+
return;
|
|
4439
|
+
}
|
|
4360
4440
|
this.observers.forEach((cb) => {
|
|
4361
4441
|
try {
|
|
4362
|
-
cb.call(void 0,
|
|
4442
|
+
cb.call(void 0, token);
|
|
4363
4443
|
} catch (e2) {
|
|
4364
4444
|
this.logger.error(e2.message);
|
|
4365
4445
|
}
|
|
@@ -4377,57 +4457,68 @@ class JwtManager {
|
|
|
4377
4457
|
constructor(jwtRepository, resolver, options) {
|
|
4378
4458
|
this.jwtRepository = jwtRepository;
|
|
4379
4459
|
this.resolver = resolver;
|
|
4460
|
+
this.inflightPromises = /* @__PURE__ */ new Map();
|
|
4380
4461
|
if (options == null ? void 0 : options.keepTokenUpdated) {
|
|
4381
|
-
jwtRepository.subscribeToTokenNearExpiration(
|
|
4462
|
+
jwtRepository.subscribeToTokenNearExpiration(
|
|
4463
|
+
(token) => this.refreshToken(token.mintParams)
|
|
4464
|
+
);
|
|
4382
4465
|
}
|
|
4383
4466
|
}
|
|
4384
4467
|
/**
|
|
4385
|
-
* Method to get a JWT token.
|
|
4386
|
-
* If there's a token request in progress, it will return the Promise of this request.
|
|
4387
|
-
* If the current token is undefined or expired, it will initiate a token refresh.
|
|
4388
|
-
* Otherwise, it will return the current token.
|
|
4468
|
+
* Method to get a JWT token for the given mint params.
|
|
4389
4469
|
*
|
|
4390
|
-
*
|
|
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.
|
|
4391
4476
|
*/
|
|
4392
|
-
getJwt() {
|
|
4393
|
-
|
|
4394
|
-
|
|
4477
|
+
getJwt(params) {
|
|
4478
|
+
const key = cacheKeyFor(params);
|
|
4479
|
+
const inflight = this.inflightPromises.get(key);
|
|
4480
|
+
if (inflight) {
|
|
4481
|
+
return inflight;
|
|
4395
4482
|
}
|
|
4396
|
-
const token = this.jwtRepository.
|
|
4483
|
+
const token = this.jwtRepository.getToken(params);
|
|
4397
4484
|
if (token === void 0 || token.isExpired) {
|
|
4398
|
-
return this.refreshToken();
|
|
4485
|
+
return this.refreshToken(params);
|
|
4399
4486
|
}
|
|
4400
4487
|
return token;
|
|
4401
4488
|
}
|
|
4402
4489
|
/**
|
|
4403
|
-
* Method to refresh a JWT token.
|
|
4404
|
-
* If a refresh request is already in progress, it will return the Promise of this request.
|
|
4405
|
-
* Otherwise, it will start a new refresh request and return its Promise.
|
|
4490
|
+
* Method to refresh a JWT token for the given mint params.
|
|
4406
4491
|
*
|
|
4407
|
-
* @
|
|
4492
|
+
* @param params - Optional mint parameters. Omit for the legacy parameterless token.
|
|
4493
|
+
* @returns Promise of the refreshed token.
|
|
4408
4494
|
*/
|
|
4409
|
-
refreshToken() {
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
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);
|
|
4420
4510
|
});
|
|
4421
|
-
}
|
|
4422
|
-
|
|
4511
|
+
});
|
|
4512
|
+
this.inflightPromises.set(key, promise);
|
|
4513
|
+
return promise;
|
|
4423
4514
|
}
|
|
4424
4515
|
/**
|
|
4425
|
-
* Method to check if
|
|
4516
|
+
* Method to check if any token refresh is in progress.
|
|
4426
4517
|
*
|
|
4427
|
-
* @returns {boolean} true if
|
|
4518
|
+
* @returns {boolean} true if at least one refresh is in flight, false otherwise.
|
|
4428
4519
|
*/
|
|
4429
4520
|
get isRefreshingToken() {
|
|
4430
|
-
return this.
|
|
4521
|
+
return this.inflightPromises.size > 0;
|
|
4431
4522
|
}
|
|
4432
4523
|
}
|
|
4433
4524
|
|
|
@@ -4445,9 +4536,18 @@ function buildServiceDescriptor$2(interceptors = {
|
|
|
4445
4536
|
return {
|
|
4446
4537
|
type: "fetch",
|
|
4447
4538
|
version: "1.0",
|
|
4448
|
-
service: function(
|
|
4539
|
+
service: function(input, init) {
|
|
4449
4540
|
var _a;
|
|
4450
|
-
|
|
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 };
|
|
4451
4551
|
const {
|
|
4452
4552
|
request: requestInterceptors = [],
|
|
4453
4553
|
retry: retryInterceptor = void 0,
|
|
@@ -4455,17 +4555,17 @@ function buildServiceDescriptor$2(interceptors = {
|
|
|
4455
4555
|
finally: finallyInterceptors = []
|
|
4456
4556
|
} = interceptors;
|
|
4457
4557
|
const pending = requestInterceptors.reduce(
|
|
4458
|
-
(previousPromise, interceptor) => previousPromise.then((
|
|
4459
|
-
resolvedPromiseLike$2(
|
|
4558
|
+
(previousPromise, interceptor) => previousPromise.then((args) => interceptor(args, context)),
|
|
4559
|
+
resolvedPromiseLike$2(fetchArgs)
|
|
4460
4560
|
);
|
|
4461
|
-
return Promise.resolve(pending).then((
|
|
4561
|
+
return Promise.resolve(pending).then((args) => {
|
|
4462
4562
|
if (retryInterceptor) {
|
|
4463
|
-
return retryInterceptor(
|
|
4563
|
+
return retryInterceptor(args, retryService, context);
|
|
4464
4564
|
} else {
|
|
4465
4565
|
if (retryService) {
|
|
4466
|
-
return retryService.applyRetry(() => fetch(...
|
|
4566
|
+
return retryService.applyRetry(() => fetch(...args));
|
|
4467
4567
|
}
|
|
4468
|
-
return fetch(...
|
|
4568
|
+
return fetch(...args);
|
|
4469
4569
|
}
|
|
4470
4570
|
}).then((response) => {
|
|
4471
4571
|
return responseInterceptors.reduce(
|
|
@@ -4780,6 +4880,10 @@ const SALESFORCE_API_BASE_URI_FLAG = 'api.salesforce.com';
|
|
|
4780
4880
|
const X_REQUEST_ID_HEADER = 'x-request-id';
|
|
4781
4881
|
const SFAPController = 'SalesforceApiPlatformController';
|
|
4782
4882
|
const SFAPJwtMethod = 'getSFAPLightningJwtService';
|
|
4883
|
+
// Parameterized mint: the POST signature's auto-generated Aura method
|
|
4884
|
+
// (`@ConnectSignature(..., generateAuraMethod = true)` on the SFAP Lightning JWT
|
|
4885
|
+
// Service Connect resource). Takes a single `requestBody` named param.
|
|
4886
|
+
const SFAPJwtPostMethod = 'postSFAPLightningJwtService';
|
|
4783
4887
|
/**
|
|
4784
4888
|
* We expect jwt info and baseUri to be present in the response.
|
|
4785
4889
|
*
|
|
@@ -4926,6 +5030,144 @@ function generateRequestId$1() {
|
|
|
4926
5030
|
}
|
|
4927
5031
|
}
|
|
4928
5032
|
|
|
5033
|
+
/**
|
|
5034
|
+
* Normalize SFAP-shaped params into the opaque `JwtMintParams` bag that callers
|
|
5035
|
+
* hand to `JwtManager.getJwt(params)`. Scopes are sorted because they are
|
|
5036
|
+
* semantically a set — without this, `['a', 'b']` and `['b', 'a']` would cache
|
|
5037
|
+
* as separate entries (OneStore treats arrays as ordered under stable-JSON
|
|
5038
|
+
* serialization; see ADR "JWT Parameterization" §2).
|
|
5039
|
+
*
|
|
5040
|
+
* MANDATORY CONSUMER ENTRY POINT. Every consumer that mints a parameterized
|
|
5041
|
+
* SFAP JWT MUST assemble its params through this function before calling the
|
|
5042
|
+
* manager. The cache key is derived by `JwtManager` from the caller's params
|
|
5043
|
+
* (`cacheKeyFor(params)`) *before* the resolver runs — so the resolver cannot
|
|
5044
|
+
* normalize after the fact. Set-stable caching therefore depends on the caller
|
|
5045
|
+
* routing params through here. Do NOT sort inside the resolver: that would only
|
|
5046
|
+
* reorder the wire body, not the cache key, and mutating the caller's params
|
|
5047
|
+
* mid-call would desync the in-flight-dedup key from the stored-token key.
|
|
5048
|
+
*/
|
|
5049
|
+
/**
|
|
5050
|
+
* Validate and narrow an opaque `JwtMintParams` bag to the SFAP-shaped subset the
|
|
5051
|
+
* resolver forwards. Returns `{ error }` (surfaced as a resolver rejection) for a
|
|
5052
|
+
* malformed bag rather than throwing.
|
|
5053
|
+
*/
|
|
5054
|
+
function coerceToSfapParams(params) {
|
|
5055
|
+
const scopes = params.scopes;
|
|
5056
|
+
const dynamicParams = params.dynamicParams;
|
|
5057
|
+
if (scopes !== undefined && !isStringArray(scopes)) {
|
|
5058
|
+
return { error: 'SFAP JWT params.scopes must be a string[] when provided.' };
|
|
5059
|
+
}
|
|
5060
|
+
if (dynamicParams !== undefined && !isStringRecord(dynamicParams)) {
|
|
5061
|
+
return {
|
|
5062
|
+
error: 'SFAP JWT params.dynamicParams must be a Record<string, string> when provided.',
|
|
5063
|
+
};
|
|
5064
|
+
}
|
|
5065
|
+
return { params: { scopes, dynamicParams } };
|
|
5066
|
+
}
|
|
5067
|
+
function isStringArray(value) {
|
|
5068
|
+
return Array.isArray(value) && value.every((s) => typeof s === 'string');
|
|
5069
|
+
}
|
|
5070
|
+
function isStringRecord(value) {
|
|
5071
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
5072
|
+
return false;
|
|
5073
|
+
}
|
|
5074
|
+
return Object.values(value).every((v) => typeof v === 'string');
|
|
5075
|
+
}
|
|
5076
|
+
/**
|
|
5077
|
+
* Build the SFAP mint request body from the coerced params: `scopes` joined into a
|
|
5078
|
+
* single space-delimited string, `dynamicParams` mapped to `dynamicParameters.items`.
|
|
5079
|
+
* Empty scopes / dynamic params are omitted.
|
|
5080
|
+
*/
|
|
5081
|
+
function buildRequestBody(params) {
|
|
5082
|
+
const body = {};
|
|
5083
|
+
if (params.scopes && params.scopes.length > 0) {
|
|
5084
|
+
body.scopes = params.scopes.join(' ');
|
|
5085
|
+
}
|
|
5086
|
+
if (params.dynamicParams) {
|
|
5087
|
+
const items = Object.entries(params.dynamicParams).map(([name, value]) => ({ name, value }));
|
|
5088
|
+
if (items.length > 0) {
|
|
5089
|
+
body.dynamicParameters = { items };
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
return body;
|
|
5093
|
+
}
|
|
5094
|
+
|
|
5095
|
+
/**
|
|
5096
|
+
* Parameterized SFAP JWT resolver that mints over **Aura transport** instead of a
|
|
5097
|
+
* direct HTTP `fetch`.
|
|
5098
|
+
*
|
|
5099
|
+
* It dispatches the SFAP Lightning JWT Service's parameterized POST via its
|
|
5100
|
+
* auto-generated Aura controller method
|
|
5101
|
+
* `SalesforceApiPlatformController.postSFAPLightningJwtService` (generated by
|
|
5102
|
+
* `@ConnectSignature(..., generateAuraMethod = true)` on the Connect resource).
|
|
5103
|
+
* The mint inputs ride as the single `requestBody` named param, in the same
|
|
5104
|
+
* `{ scopes, dynamicParameters: { items } }` shape the HTTP resolver POSTs — built
|
|
5105
|
+
* by the shared {@link buildRequestBody}, so the two transports stay in lockstep.
|
|
5106
|
+
*
|
|
5107
|
+
* Why Aura (not the HTTP resolver): the mint endpoint is a same-origin core
|
|
5108
|
+
* resource, and routing it over Aura keeps session/CSRF handling inside the Aura
|
|
5109
|
+
* stack rather than issuing a credentialed cross-cutting `fetch` from the client.
|
|
5110
|
+
* This mirrors the legacy parameterless `platformSfapJwtResolver` in
|
|
5111
|
+
* `network-sfap.ts`, which already mints over Aura via the `getSFAPLightningJwtService`
|
|
5112
|
+
* generated method — this is the parameterized sibling of that call.
|
|
5113
|
+
*
|
|
5114
|
+
* A `JwtResolver` is invoked directly by `JwtManager` (not through Luvio's
|
|
5115
|
+
* `appRouter`/`ResourceRequest` pipeline), so the correct mechanism is a direct
|
|
5116
|
+
* named-controller `dispatchAuraAction`, not the `auraNetworkAdapter`/connect-route
|
|
5117
|
+
* table. The SFAP JWT endpoint is not registered as a connect-over-Aura route, and
|
|
5118
|
+
* a resolver has no `ResourceRequest` for the router to look up.
|
|
5119
|
+
*/
|
|
5120
|
+
class ParameterizedSfapJwtAuraResolver {
|
|
5121
|
+
getJwt(params) {
|
|
5122
|
+
return new Promise((resolve, reject) => {
|
|
5123
|
+
if (params === undefined) {
|
|
5124
|
+
// Misuse: the dispatching resolver routes parameterless calls to the
|
|
5125
|
+
// legacy resolver. Reject so production fails loudly here.
|
|
5126
|
+
reject('ParameterizedSfapJwtAuraResolver requires JwtMintParams. The legacy parameterless path should be served by platformSfapJwtResolver.');
|
|
5127
|
+
return;
|
|
5128
|
+
}
|
|
5129
|
+
const coerced = coerceToSfapParams(params);
|
|
5130
|
+
if ('error' in coerced) {
|
|
5131
|
+
reject(coerced.error);
|
|
5132
|
+
return;
|
|
5133
|
+
}
|
|
5134
|
+
// The mint inputs become the single `requestBody` named param of the
|
|
5135
|
+
// generated Aura method (Aura binds top-level keys to the controller
|
|
5136
|
+
// method's named args). Reuses the HTTP resolver's body builder so both
|
|
5137
|
+
// transports send an identical shape.
|
|
5138
|
+
const requestBody = buildRequestBody(coerced.params);
|
|
5139
|
+
// No fetchImpl / requestInterceptor / CSRF / credentials handling here —
|
|
5140
|
+
// the Aura stack owns session + CSRF. Matches the legacy resolver's call.
|
|
5141
|
+
dispatchAuraAction(`${SFAPController}.${SFAPJwtPostMethod}`, { requestBody }, defaultActionConfig)
|
|
5142
|
+
.then((response) => {
|
|
5143
|
+
const body = response.body;
|
|
5144
|
+
if (!body || typeof body.jwt !== 'string' || typeof body.baseUri !== 'string') {
|
|
5145
|
+
// Never serialize the body into the error — it may carry a JWT.
|
|
5146
|
+
reject('SFAP JWT response missing required fields (jwt, baseUri)');
|
|
5147
|
+
return;
|
|
5148
|
+
}
|
|
5149
|
+
resolve({ jwt: body.jwt, extraInfo: { baseUri: body.baseUri } });
|
|
5150
|
+
})
|
|
5151
|
+
.catch((error) => {
|
|
5152
|
+
// Error mapping ported from the legacy platformSfapJwtResolver
|
|
5153
|
+
// (network-sfap.ts): plain Errors carry a message; non-500
|
|
5154
|
+
// AuraFetchResponses are ConnectInJava errors with a typed body;
|
|
5155
|
+
// 500s carry an { error } body.
|
|
5156
|
+
if (error instanceof Error) {
|
|
5157
|
+
reject(error.message);
|
|
5158
|
+
return;
|
|
5159
|
+
}
|
|
5160
|
+
const { status } = error;
|
|
5161
|
+
if (status !== HttpStatusCode$2.ServerError) {
|
|
5162
|
+
reject(error.body.message);
|
|
5163
|
+
return;
|
|
5164
|
+
}
|
|
5165
|
+
reject(error.body.error);
|
|
5166
|
+
});
|
|
5167
|
+
});
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
|
|
4929
5171
|
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";
|
|
4930
5172
|
|
|
4931
5173
|
/**
|
|
@@ -5742,7 +5984,7 @@ function getEnvironmentSetting(name) {
|
|
|
5742
5984
|
}
|
|
5743
5985
|
return undefined;
|
|
5744
5986
|
}
|
|
5745
|
-
// version: 1.428.0-
|
|
5987
|
+
// version: 1.428.0-dev13-ab893a1bbc
|
|
5746
5988
|
|
|
5747
5989
|
const environmentHasAura = typeof window !== 'undefined' && typeof window.$A !== 'undefined';
|
|
5748
5990
|
const defaultConfig = {
|
|
@@ -7016,9 +7258,106 @@ function buildCsrfRetryInterceptor() {
|
|
|
7016
7258
|
};
|
|
7017
7259
|
}
|
|
7018
7260
|
|
|
7261
|
+
/**
|
|
7262
|
+
* The context-seed key under which a custom command forwards its JWT mint
|
|
7263
|
+
* parameters. The framework's `buildServiceDescriptor` merges the request init's
|
|
7264
|
+
* `__contextSeed` onto the per-request interceptor context, so this interceptor
|
|
7265
|
+
* reads the params off `context[JWT_MINT_PARAMS_SEED_KEY]`.
|
|
7266
|
+
*
|
|
7267
|
+
* This is a cross-team contract: the custom command writes this key, this
|
|
7268
|
+
* interceptor reads it. It is intentionally an opaque key — OneStore does not
|
|
7269
|
+
* define it and never inspects the param shape; the typed shape lives in the
|
|
7270
|
+
* resolver.
|
|
7271
|
+
*/
|
|
7272
|
+
const JWT_MINT_PARAMS_SEED_KEY = 'jwtMintParams';
|
|
7273
|
+
/**
|
|
7274
|
+
* Returns `true` if the fetch arguments already carry an `Authorization` header,
|
|
7275
|
+
* across the three shapes the framework's `setHeader` handles: a `Request`
|
|
7276
|
+
* resource's own headers, an `options.headers` `Headers` instance, or a plain
|
|
7277
|
+
* record. The descriptor's guarded legacy interceptor uses this to skip when the
|
|
7278
|
+
* parameterized interceptor has already authorized the request — avoiding a second
|
|
7279
|
+
* mint and the throw `setHeaderAuthorization` raises on an existing header.
|
|
7280
|
+
*/
|
|
7281
|
+
function hasAuthorizationHeader([resource, options]) {
|
|
7282
|
+
if (resource instanceof Request && resource.headers.has('Authorization')) {
|
|
7283
|
+
return true;
|
|
7284
|
+
}
|
|
7285
|
+
const headers = options?.headers;
|
|
7286
|
+
if (headers === undefined) {
|
|
7287
|
+
return false;
|
|
7288
|
+
}
|
|
7289
|
+
if (headers instanceof Headers) {
|
|
7290
|
+
return headers.has('Authorization');
|
|
7291
|
+
}
|
|
7292
|
+
if (Array.isArray(headers)) {
|
|
7293
|
+
return headers.some(([name]) => name.toLowerCase() === 'authorization');
|
|
7294
|
+
}
|
|
7295
|
+
return Reflect.has(headers, 'Authorization');
|
|
7296
|
+
}
|
|
7297
|
+
/**
|
|
7298
|
+
* Builds the request interceptor that bridges the per-request context seed to the
|
|
7299
|
+
* dispatching SFAP JwtManager for the **parameterized** mint path.
|
|
7300
|
+
*
|
|
7301
|
+
* For a parameterized SFAP command, the context carries `jwtMintParams`. This
|
|
7302
|
+
* interceptor reads them, calls `jwtManager.getJwt(params)` (which the dispatching
|
|
7303
|
+
* resolver routes to the parameterized resolver), attaches the `Authorization`
|
|
7304
|
+
* header, and rewrites the request URL via the minted `baseUri`. The presence of
|
|
7305
|
+
* that `Authorization` header is the signal the descriptor's guarded legacy
|
|
7306
|
+
* interceptor uses to skip — so the legacy path does not mint a second time.
|
|
7307
|
+
*
|
|
7308
|
+
* For a legacy parameterless command the context carries no `jwtMintParams`, so
|
|
7309
|
+
* this interceptor **early-returns on its first line** and the request flows
|
|
7310
|
+
* unchanged to the legacy `buildJwtRequestHeaderInterceptor` — guaranteeing zero
|
|
7311
|
+
* behavior change for non-opted-in adapters.
|
|
7312
|
+
*
|
|
7313
|
+
* Lives in `lds-lightning-platform` (not OneStore) beside the existing SFAP/CSRF
|
|
7314
|
+
* interceptors, per the JWT-parameterization ADR §5: OneStore provides only the
|
|
7315
|
+
* generic interceptor mechanism and the opaque context-seed channel; the service-
|
|
7316
|
+
* specific bridge is a runtime-layer concern.
|
|
7317
|
+
*
|
|
7318
|
+
* @param jwtManager - the dispatching SFAP JwtManager
|
|
7319
|
+
* @param jwtRequestModifier - applies the minted `extraInfo.baseUri` to the request URL
|
|
7320
|
+
*/
|
|
7321
|
+
function buildJwtParameterizationInterceptor(jwtManager, jwtRequestModifier = (_extraInfo, fetchArgs) => fetchArgs) {
|
|
7322
|
+
return (fetchArgs, context) => {
|
|
7323
|
+
const jwtContext = context;
|
|
7324
|
+
const mintParams = jwtContext?.[JWT_MINT_PARAMS_SEED_KEY];
|
|
7325
|
+
// No mint params → not a parameterized request. Do nothing; the legacy
|
|
7326
|
+
// interceptor handles it. This MUST be the first statement so non-opted-in
|
|
7327
|
+
// SFAP commands observe no work at all.
|
|
7328
|
+
if (mintParams === undefined) {
|
|
7329
|
+
return resolvedPromiseLike$2(fetchArgs);
|
|
7330
|
+
}
|
|
7331
|
+
return resolvedPromiseLike$2(jwtManager.getJwt(mintParams)).then((token) => {
|
|
7332
|
+
const fetchArgsWithAuthorization = setHeaderAuthorization(token, fetchArgs);
|
|
7333
|
+
return token.extraInfo
|
|
7334
|
+
? jwtRequestModifier(token.extraInfo, fetchArgsWithAuthorization)
|
|
7335
|
+
: fetchArgsWithAuthorization;
|
|
7336
|
+
});
|
|
7337
|
+
};
|
|
7338
|
+
}
|
|
7339
|
+
|
|
7019
7340
|
const SFAP_BASE_URL = 'api.salesforce.com';
|
|
7341
|
+
function buildDispatchingSfapJwtResolver(legacyResolver, parameterizedResolver) {
|
|
7342
|
+
return {
|
|
7343
|
+
getJwt(params) {
|
|
7344
|
+
if (params === undefined) {
|
|
7345
|
+
return legacyResolver.getJwt();
|
|
7346
|
+
}
|
|
7347
|
+
return parameterizedResolver.getJwt(params);
|
|
7348
|
+
},
|
|
7349
|
+
};
|
|
7350
|
+
}
|
|
7351
|
+
// The parameterized mint is dispatched over **Aura transport**:
|
|
7352
|
+
// the resolver calls the SFAP Lightning JWT Service's auto-generated Aura method
|
|
7353
|
+
// `SalesforceApiPlatformController.postSFAPLightningJwtService`, so session + CSRF
|
|
7354
|
+
// are handled inside the Aura stack. This mirrors the legacy parameterless
|
|
7355
|
+
// `platformSfapJwtResolver`, which already mints over Aura. The minted JWT is then
|
|
7356
|
+
// attached as a Bearer token on the downstream SFAP API request (which stays HTTP).
|
|
7357
|
+
const parameterizedSfapJwtResolver = new ParameterizedSfapJwtAuraResolver();
|
|
7358
|
+
const sfapJwtResolver = buildDispatchingSfapJwtResolver(platformSfapJwtResolver, parameterizedSfapJwtResolver);
|
|
7020
7359
|
const sfapJwtRepository = new JwtRepository();
|
|
7021
|
-
const sfapJwtManager = new JwtManager(sfapJwtRepository,
|
|
7360
|
+
const sfapJwtManager = new JwtManager(sfapJwtRepository, sfapJwtResolver);
|
|
7022
7361
|
function prefetchSfapJwt() {
|
|
7023
7362
|
const maybePromise = sfapJwtManager.getJwt();
|
|
7024
7363
|
if ('then' in maybePromise) {
|
|
@@ -7029,8 +7368,12 @@ function prefetchSfapJwt() {
|
|
|
7029
7368
|
function buildJwtAuthorizedSfapFetchServiceDescriptor(logger) {
|
|
7030
7369
|
const jwtAuthorizedFetchService = buildServiceDescriptor$2({
|
|
7031
7370
|
createContext: createInstrumentationIdContext(),
|
|
7032
|
-
request: [
|
|
7033
|
-
|
|
7371
|
+
request: [
|
|
7372
|
+
buildThirdPartyTrackerRegisterInterceptor(),
|
|
7373
|
+
buildJwtParameterizationInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger)),
|
|
7374
|
+
// Guarded so it is skipped once the parameterized interceptor handled the request.
|
|
7375
|
+
buildGuardedLegacyJwtRequestInterceptor(logger),
|
|
7376
|
+
],
|
|
7034
7377
|
finally: [buildThirdPartyTrackerFinishInterceptor()],
|
|
7035
7378
|
});
|
|
7036
7379
|
return {
|
|
@@ -7110,8 +7453,13 @@ function buildUnauthorizedFetchServiceDescriptor() {
|
|
|
7110
7453
|
tags: { authenticationScopes: '' },
|
|
7111
7454
|
};
|
|
7112
7455
|
}
|
|
7113
|
-
|
|
7114
|
-
|
|
7456
|
+
/**
|
|
7457
|
+
* The `JwtRequestModifier` shared by both the legacy and parameterized SFAP
|
|
7458
|
+
* interceptors: it rewrites the request URL's host/protocol to the minted
|
|
7459
|
+
* `extraInfo.baseUri` (only for `api.salesforce.com` resources).
|
|
7460
|
+
*/
|
|
7461
|
+
function buildSfapJwtRequestModifier(logger) {
|
|
7462
|
+
return ({ baseUri }, [resource, request]) => {
|
|
7115
7463
|
if (typeof resource !== 'string' && !(resource instanceof URL)) {
|
|
7116
7464
|
// istanbul ignore else: this will not be tested in NODE_ENV = production for test coverage
|
|
7117
7465
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -7129,8 +7477,22 @@ function buildJwtRequestInterceptor(logger) {
|
|
|
7129
7477
|
url.protocol = overrideUrl.protocol;
|
|
7130
7478
|
return [url, request];
|
|
7131
7479
|
};
|
|
7132
|
-
|
|
7133
|
-
|
|
7480
|
+
}
|
|
7481
|
+
function buildJwtRequestInterceptor(logger) {
|
|
7482
|
+
return buildJwtRequestHeaderInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger));
|
|
7483
|
+
}
|
|
7484
|
+
/**
|
|
7485
|
+
* Wraps the legacy `buildJwtRequestHeaderInterceptor` with a pass-through guard:
|
|
7486
|
+
* when the parameterized interceptor has already authorized the request (an
|
|
7487
|
+
* `Authorization` header is present), this returns `args` untouched so the legacy
|
|
7488
|
+
* interceptor does not mint a second time or attempt to set a duplicate
|
|
7489
|
+
* Authorization header (which `setHeaderAuthorization` throws on). For every legacy
|
|
7490
|
+
* (non-parameterized) request no Authorization header is present yet, so the legacy
|
|
7491
|
+
* interceptor runs exactly as before — a strict pass-through.
|
|
7492
|
+
*/
|
|
7493
|
+
function buildGuardedLegacyJwtRequestInterceptor(logger) {
|
|
7494
|
+
const legacyInterceptor = buildJwtRequestInterceptor(logger);
|
|
7495
|
+
return (args) => hasAuthorizationHeader(args) ? resolvedPromiseLike$2(args) : legacyInterceptor(args);
|
|
7134
7496
|
}
|
|
7135
7497
|
|
|
7136
7498
|
const PDL_EXECUTE_ASYNC_OPTIONS = {
|
|
@@ -10580,4 +10942,4 @@ function ldsEngineCreator() {
|
|
|
10580
10942
|
}
|
|
10581
10943
|
|
|
10582
10944
|
export { LexRequestStrategy, PdlPrefetcherEventType, PdlRequestPriority, buildPredictorForContext, configService, ldsEngineCreator as default, initializeLDS, initializeOneStore, notifyUpdateAvailableFactory, registerRequestStrategy, saveRequestAsPrediction, subscribeToPrefetcherEvents, unregisterRequestStrategy, whenPredictionsReady };
|
|
10583
|
-
// version: 1.428.0-
|
|
10945
|
+
// version: 1.428.0-dev13-42815c056e
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { type JwtResolver } from '@conduit-client/jwt-manager';
|
|
1
2
|
import { type FetchServiceDescriptor } from '@conduit-client/service-fetch-network/v1';
|
|
3
|
+
import { type ExtraInfo } from '../network-sfap';
|
|
2
4
|
import { type LoggerService } from '@conduit-client/utils';
|
|
5
|
+
export declare function buildDispatchingSfapJwtResolver(legacyResolver: JwtResolver<ExtraInfo>, parameterizedResolver: JwtResolver<ExtraInfo>): JwtResolver<ExtraInfo>;
|
|
3
6
|
export declare function prefetchSfapJwt(): Promise<undefined>;
|
|
4
7
|
export declare function buildJwtAuthorizedSfapFetchServiceDescriptor(logger: LoggerService): FetchServiceDescriptor;
|
|
5
8
|
/**
|
|
@@ -2,6 +2,7 @@ import type { FetchResponse, ResourceRequest, ResourceRequestContext } from '@lu
|
|
|
2
2
|
import type { JwtResolver } from '@conduit-client/jwt-manager';
|
|
3
3
|
export declare const SFAPController = "SalesforceApiPlatformController";
|
|
4
4
|
export declare const SFAPJwtMethod = "getSFAPLightningJwtService";
|
|
5
|
+
export declare const SFAPJwtPostMethod = "postSFAPLightningJwtService";
|
|
5
6
|
export type ExtraInfo = {
|
|
6
7
|
baseUri: string;
|
|
7
8
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { JwtMintParams, JwtResolver } from '@conduit-client/jwt-manager';
|
|
2
|
+
import { type ParameterizedSfapExtraInfo } from './sfap-jwt-mint-params';
|
|
3
|
+
/**
|
|
4
|
+
* Parameterized SFAP JWT resolver that mints over **Aura transport** instead of a
|
|
5
|
+
* direct HTTP `fetch`.
|
|
6
|
+
*
|
|
7
|
+
* It dispatches the SFAP Lightning JWT Service's parameterized POST via its
|
|
8
|
+
* auto-generated Aura controller method
|
|
9
|
+
* `SalesforceApiPlatformController.postSFAPLightningJwtService` (generated by
|
|
10
|
+
* `@ConnectSignature(..., generateAuraMethod = true)` on the Connect resource).
|
|
11
|
+
* The mint inputs ride as the single `requestBody` named param, in the same
|
|
12
|
+
* `{ scopes, dynamicParameters: { items } }` shape the HTTP resolver POSTs — built
|
|
13
|
+
* by the shared {@link buildRequestBody}, so the two transports stay in lockstep.
|
|
14
|
+
*
|
|
15
|
+
* Why Aura (not the HTTP resolver): the mint endpoint is a same-origin core
|
|
16
|
+
* resource, and routing it over Aura keeps session/CSRF handling inside the Aura
|
|
17
|
+
* stack rather than issuing a credentialed cross-cutting `fetch` from the client.
|
|
18
|
+
* This mirrors the legacy parameterless `platformSfapJwtResolver` in
|
|
19
|
+
* `network-sfap.ts`, which already mints over Aura via the `getSFAPLightningJwtService`
|
|
20
|
+
* generated method — this is the parameterized sibling of that call.
|
|
21
|
+
*
|
|
22
|
+
* A `JwtResolver` is invoked directly by `JwtManager` (not through Luvio's
|
|
23
|
+
* `appRouter`/`ResourceRequest` pipeline), so the correct mechanism is a direct
|
|
24
|
+
* named-controller `dispatchAuraAction`, not the `auraNetworkAdapter`/connect-route
|
|
25
|
+
* table. The SFAP JWT endpoint is not registered as a connect-over-Aura route, and
|
|
26
|
+
* a resolver has no `ResourceRequest` for the router to look up.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ParameterizedSfapJwtAuraResolver implements JwtResolver<ParameterizedSfapExtraInfo> {
|
|
29
|
+
getJwt(params?: JwtMintParams): Promise<{
|
|
30
|
+
jwt: string;
|
|
31
|
+
extraInfo: ParameterizedSfapExtraInfo;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export declare function buildParameterizedSfapJwtAuraResolver(): ParameterizedSfapJwtAuraResolver;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FetchParameters, RequestInterceptor } from '@conduit-client/service-fetch-network/v1';
|
|
2
|
+
import { type JwtRequestModifier } from '@conduit-client/service-fetch-network/v1';
|
|
3
|
+
import type { JwtManager } from '@conduit-client/jwt-manager';
|
|
4
|
+
import type { ExtraInfo } from '../network-sfap';
|
|
5
|
+
/**
|
|
6
|
+
* The context-seed key under which a custom command forwards its JWT mint
|
|
7
|
+
* parameters. The framework's `buildServiceDescriptor` merges the request init's
|
|
8
|
+
* `__contextSeed` onto the per-request interceptor context, so this interceptor
|
|
9
|
+
* reads the params off `context[JWT_MINT_PARAMS_SEED_KEY]`.
|
|
10
|
+
*
|
|
11
|
+
* This is a cross-team contract: the custom command writes this key, this
|
|
12
|
+
* interceptor reads it. It is intentionally an opaque key — OneStore does not
|
|
13
|
+
* define it and never inspects the param shape; the typed shape lives in the
|
|
14
|
+
* resolver.
|
|
15
|
+
*/
|
|
16
|
+
export declare const JWT_MINT_PARAMS_SEED_KEY = "jwtMintParams";
|
|
17
|
+
/**
|
|
18
|
+
* Returns `true` if the fetch arguments already carry an `Authorization` header,
|
|
19
|
+
* across the three shapes the framework's `setHeader` handles: a `Request`
|
|
20
|
+
* resource's own headers, an `options.headers` `Headers` instance, or a plain
|
|
21
|
+
* record. The descriptor's guarded legacy interceptor uses this to skip when the
|
|
22
|
+
* parameterized interceptor has already authorized the request — avoiding a second
|
|
23
|
+
* mint and the throw `setHeaderAuthorization` raises on an existing header.
|
|
24
|
+
*/
|
|
25
|
+
export declare function hasAuthorizationHeader([resource, options]: FetchParameters): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Builds the request interceptor that bridges the per-request context seed to the
|
|
28
|
+
* dispatching SFAP JwtManager for the **parameterized** mint path.
|
|
29
|
+
*
|
|
30
|
+
* For a parameterized SFAP command, the context carries `jwtMintParams`. This
|
|
31
|
+
* interceptor reads them, calls `jwtManager.getJwt(params)` (which the dispatching
|
|
32
|
+
* resolver routes to the parameterized resolver), attaches the `Authorization`
|
|
33
|
+
* header, and rewrites the request URL via the minted `baseUri`. The presence of
|
|
34
|
+
* that `Authorization` header is the signal the descriptor's guarded legacy
|
|
35
|
+
* interceptor uses to skip — so the legacy path does not mint a second time.
|
|
36
|
+
*
|
|
37
|
+
* For a legacy parameterless command the context carries no `jwtMintParams`, so
|
|
38
|
+
* this interceptor **early-returns on its first line** and the request flows
|
|
39
|
+
* unchanged to the legacy `buildJwtRequestHeaderInterceptor` — guaranteeing zero
|
|
40
|
+
* behavior change for non-opted-in adapters.
|
|
41
|
+
*
|
|
42
|
+
* Lives in `lds-lightning-platform` (not OneStore) beside the existing SFAP/CSRF
|
|
43
|
+
* interceptors, per the JWT-parameterization ADR §5: OneStore provides only the
|
|
44
|
+
* generic interceptor mechanism and the opaque context-seed channel; the service-
|
|
45
|
+
* specific bridge is a runtime-layer concern.
|
|
46
|
+
*
|
|
47
|
+
* @param jwtManager - the dispatching SFAP JwtManager
|
|
48
|
+
* @param jwtRequestModifier - applies the minted `extraInfo.baseUri` to the request URL
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildJwtParameterizationInterceptor(jwtManager: JwtManager<unknown, ExtraInfo>, jwtRequestModifier?: JwtRequestModifier): RequestInterceptor;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { JwtMintParams } from '@conduit-client/jwt-manager';
|
|
2
|
+
/**
|
|
3
|
+
* Typed shape of the params the SFAP Lightning JWT Service understands.
|
|
4
|
+
* Lives in the resolver layer because OneStore is param-shape-agnostic; it
|
|
5
|
+
* sees only an opaque `JwtMintParams` bag and stable-JSON-stringifies it for
|
|
6
|
+
* cache keying.
|
|
7
|
+
*/
|
|
8
|
+
export type SfapJwtMintParams = {
|
|
9
|
+
scopes?: string[];
|
|
10
|
+
dynamicParams?: Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The `extraInfo` the SFAP JWT resolver returns alongside the minted token: the
|
|
14
|
+
* per-tenant base URI the downstream SFAP API request is rewritten to.
|
|
15
|
+
*/
|
|
16
|
+
export type ParameterizedSfapExtraInfo = {
|
|
17
|
+
baseUri: string;
|
|
18
|
+
};
|
|
19
|
+
type DynamicParameterItem = {
|
|
20
|
+
name: string;
|
|
21
|
+
value: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The SFAP Lightning JWT Service mint request body. Sent as the `requestBody`
|
|
25
|
+
* named param of the `postSFAPLightningJwtService` Aura controller method.
|
|
26
|
+
* Matches the server's `SFAPLightningJwtServiceInputRepresentation`: `scopes` is
|
|
27
|
+
* a single space-delimited string; `dynamicParameters` is `{ items: [{ name, value }] }`.
|
|
28
|
+
*/
|
|
29
|
+
export type SfapJwtRequestBody = {
|
|
30
|
+
scopes?: string;
|
|
31
|
+
dynamicParameters?: {
|
|
32
|
+
items: DynamicParameterItem[];
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Normalize SFAP-shaped params into the opaque `JwtMintParams` bag that callers
|
|
37
|
+
* hand to `JwtManager.getJwt(params)`. Scopes are sorted because they are
|
|
38
|
+
* semantically a set — without this, `['a', 'b']` and `['b', 'a']` would cache
|
|
39
|
+
* as separate entries (OneStore treats arrays as ordered under stable-JSON
|
|
40
|
+
* serialization; see ADR "JWT Parameterization" §2).
|
|
41
|
+
*
|
|
42
|
+
* MANDATORY CONSUMER ENTRY POINT. Every consumer that mints a parameterized
|
|
43
|
+
* SFAP JWT MUST assemble its params through this function before calling the
|
|
44
|
+
* manager. The cache key is derived by `JwtManager` from the caller's params
|
|
45
|
+
* (`cacheKeyFor(params)`) *before* the resolver runs — so the resolver cannot
|
|
46
|
+
* normalize after the fact. Set-stable caching therefore depends on the caller
|
|
47
|
+
* routing params through here. Do NOT sort inside the resolver: that would only
|
|
48
|
+
* reorder the wire body, not the cache key, and mutating the caller's params
|
|
49
|
+
* mid-call would desync the in-flight-dedup key from the stored-token key.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildSfapJwtMintParams(params: SfapJwtMintParams): JwtMintParams;
|
|
52
|
+
export type SfapParamsResult = {
|
|
53
|
+
params: SfapJwtMintParams;
|
|
54
|
+
} | {
|
|
55
|
+
error: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Validate and narrow an opaque `JwtMintParams` bag to the SFAP-shaped subset the
|
|
59
|
+
* resolver forwards. Returns `{ error }` (surfaced as a resolver rejection) for a
|
|
60
|
+
* malformed bag rather than throwing.
|
|
61
|
+
*/
|
|
62
|
+
export declare function coerceToSfapParams(params: JwtMintParams): SfapParamsResult;
|
|
63
|
+
/**
|
|
64
|
+
* Build the SFAP mint request body from the coerced params: `scopes` joined into a
|
|
65
|
+
* single space-delimited string, `dynamicParams` mapped to `dynamicParameters.items`.
|
|
66
|
+
* Empty scopes / dynamic params are omitted.
|
|
67
|
+
*/
|
|
68
|
+
export declare function buildRequestBody(params: SfapJwtMintParams): SfapJwtRequestBody;
|
|
69
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-runtime-aura",
|
|
3
|
-
"version": "1.428.0-
|
|
3
|
+
"version": "1.428.0-dev13",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "LDS engine for Aura runtime",
|
|
6
6
|
"main": "dist/ldsEngineCreator.js",
|
|
@@ -34,59 +34,60 @@
|
|
|
34
34
|
"release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-runtime-aura"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@conduit-client/service-provisioner": "3.
|
|
38
|
-
"@conduit-client/tools-core": "3.
|
|
39
|
-
"@salesforce/lds-adapters-apex": "^1.428.0-
|
|
40
|
-
"@salesforce/lds-adapters-uiapi": "^1.428.0-
|
|
41
|
-
"@salesforce/lds-ads-bridge": "^1.428.0-
|
|
42
|
-
"@salesforce/lds-aura-storage": "^1.428.0-
|
|
43
|
-
"@salesforce/lds-bindings": "^1.428.0-
|
|
44
|
-
"@salesforce/lds-instrumentation": "^1.428.0-
|
|
45
|
-
"@salesforce/lds-network-aura": "^1.428.0-
|
|
46
|
-
"@salesforce/lds-network-fetch": "^1.428.0-
|
|
37
|
+
"@conduit-client/service-provisioner": "3.19.0-dev3",
|
|
38
|
+
"@conduit-client/tools-core": "3.19.0-dev3",
|
|
39
|
+
"@salesforce/lds-adapters-apex": "^1.428.0-dev13",
|
|
40
|
+
"@salesforce/lds-adapters-uiapi": "^1.428.0-dev13",
|
|
41
|
+
"@salesforce/lds-ads-bridge": "^1.428.0-dev13",
|
|
42
|
+
"@salesforce/lds-aura-storage": "^1.428.0-dev13",
|
|
43
|
+
"@salesforce/lds-bindings": "^1.428.0-dev13",
|
|
44
|
+
"@salesforce/lds-instrumentation": "^1.428.0-dev13",
|
|
45
|
+
"@salesforce/lds-network-aura": "^1.428.0-dev13",
|
|
46
|
+
"@salesforce/lds-network-fetch": "^1.428.0-dev13",
|
|
47
47
|
"jwt-encode": "1.0.1"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@conduit-client/command-aura-graphql-normalized-cache-control": "3.
|
|
51
|
-
"@conduit-client/command-aura-network": "3.
|
|
52
|
-
"@conduit-client/command-aura-normalized-cache-control": "3.
|
|
53
|
-
"@conduit-client/command-aura-resource-cache-control": "3.
|
|
54
|
-
"@conduit-client/command-fetch-network": "3.
|
|
55
|
-
"@conduit-client/command-http-graphql-normalized-cache-control": "3.
|
|
56
|
-
"@conduit-client/command-http-normalized-cache-control": "3.
|
|
57
|
-
"@conduit-client/command-ndjson": "3.
|
|
58
|
-
"@conduit-client/command-network": "3.
|
|
59
|
-
"@conduit-client/command-sse": "3.
|
|
60
|
-
"@conduit-client/command-streaming": "3.
|
|
61
|
-
"@conduit-client/
|
|
62
|
-
"@conduit-client/service-
|
|
63
|
-
"@conduit-client/service-bindings-
|
|
64
|
-
"@conduit-client/service-
|
|
65
|
-
"@conduit-client/service-cache
|
|
66
|
-
"@conduit-client/service-cache-
|
|
67
|
-
"@conduit-client/service-
|
|
68
|
-
"@conduit-client/service-
|
|
69
|
-
"@conduit-client/service-
|
|
70
|
-
"@conduit-client/service-
|
|
71
|
-
"@conduit-client/service-
|
|
72
|
-
"@conduit-client/service-
|
|
73
|
-
"@conduit-client/
|
|
50
|
+
"@conduit-client/command-aura-graphql-normalized-cache-control": "3.19.0-dev3",
|
|
51
|
+
"@conduit-client/command-aura-network": "3.19.0-dev3",
|
|
52
|
+
"@conduit-client/command-aura-normalized-cache-control": "3.19.0-dev3",
|
|
53
|
+
"@conduit-client/command-aura-resource-cache-control": "3.19.0-dev3",
|
|
54
|
+
"@conduit-client/command-fetch-network": "3.19.0-dev3",
|
|
55
|
+
"@conduit-client/command-http-graphql-normalized-cache-control": "3.19.0-dev3",
|
|
56
|
+
"@conduit-client/command-http-normalized-cache-control": "3.19.0-dev3",
|
|
57
|
+
"@conduit-client/command-ndjson": "3.19.0-dev3",
|
|
58
|
+
"@conduit-client/command-network": "3.19.0-dev3",
|
|
59
|
+
"@conduit-client/command-sse": "3.19.0-dev3",
|
|
60
|
+
"@conduit-client/command-streaming": "3.19.0-dev3",
|
|
61
|
+
"@conduit-client/jwt-manager": "3.19.0-dev3",
|
|
62
|
+
"@conduit-client/service-aura-network": "3.19.0-dev3",
|
|
63
|
+
"@conduit-client/service-bindings-imperative": "3.19.0-dev3",
|
|
64
|
+
"@conduit-client/service-bindings-lwc": "3.19.0-dev3",
|
|
65
|
+
"@conduit-client/service-cache": "3.19.0-dev3",
|
|
66
|
+
"@conduit-client/service-cache-control": "3.19.0-dev3",
|
|
67
|
+
"@conduit-client/service-cache-inclusion-policy": "3.19.0-dev3",
|
|
68
|
+
"@conduit-client/service-config": "3.19.0-dev3",
|
|
69
|
+
"@conduit-client/service-feature-flags": "3.19.0-dev3",
|
|
70
|
+
"@conduit-client/service-fetch-network": "3.19.0-dev3",
|
|
71
|
+
"@conduit-client/service-instrument-command": "3.19.0-dev3",
|
|
72
|
+
"@conduit-client/service-pubsub": "3.19.0-dev3",
|
|
73
|
+
"@conduit-client/service-store": "3.19.0-dev3",
|
|
74
|
+
"@conduit-client/utils": "3.19.0-dev3",
|
|
74
75
|
"@luvio/network-adapter-composable": "0.160.3",
|
|
75
76
|
"@luvio/network-adapter-fetch": "0.160.3",
|
|
76
77
|
"@lwc/state": "^0.29.0",
|
|
77
|
-
"@salesforce/lds-adapters-onestore-graphql": "^1.428.0-
|
|
78
|
+
"@salesforce/lds-adapters-onestore-graphql": "^1.428.0-dev13",
|
|
78
79
|
"@salesforce/lds-adapters-uiapi-lex": "^1.415.0",
|
|
79
|
-
"@salesforce/lds-durable-storage": "^1.428.0-
|
|
80
|
-
"@salesforce/lds-luvio-service": "^1.428.0-
|
|
81
|
-
"@salesforce/lds-luvio-uiapi-records-service": "^1.428.0-
|
|
80
|
+
"@salesforce/lds-durable-storage": "^1.428.0-dev13",
|
|
81
|
+
"@salesforce/lds-luvio-service": "^1.428.0-dev13",
|
|
82
|
+
"@salesforce/lds-luvio-uiapi-records-service": "^1.428.0-dev13"
|
|
82
83
|
},
|
|
83
84
|
"luvioBundlesize": [
|
|
84
85
|
{
|
|
85
86
|
"path": "./dist/ldsEngineCreator.js",
|
|
86
87
|
"maxSize": {
|
|
87
|
-
"none": "
|
|
88
|
+
"none": "410 kB",
|
|
88
89
|
"min": "190 kB",
|
|
89
|
-
"compressed": "
|
|
90
|
+
"compressed": "71 kB"
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
],
|