@salesforce/lds-runtime-aura 1.428.0-dev2 → 1.428.0-dev20

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.
@@ -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
- return response2.json().then(
1379
- (json) => {
1380
- return this.processFetchReturnValue(json);
1381
- },
1382
- (reason) => err$1(toError(reason))
1383
- ).finally(() => {
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
- return this.convertFetchResponseToData(this.services.fetch(...this.fetchParams));
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
- return response2.json().then(
1583
- (json) => ok$2(json),
1584
- (reason) => err$1(toError(reason))
1585
- ).finally(() => {
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-dev2-2d5e0a6e5f
2766
+ // version: 1.428.0-dev20-d72ac06681
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-dev2-2d5e0a6e5f
3119
+ // version: 1.428.0-dev20-d72ac06681
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 current token.
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._token;
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 current token.
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
- this._token = new JwtToken(token, decodedInfo, extraInfo);
4257
- this.observeTokenExpiration();
4258
- return this._token;
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;
4259
4341
  }
4260
4342
  /**
4261
- * Remove the current token.
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").
4262
4348
  */
4263
- removeToken() {
4264
- this._token = void 0;
4265
- this.clearTimeoutHandler();
4349
+ removeToken(params) {
4350
+ const key = cacheKeyFor(params);
4351
+ this._tokens.delete(key);
4352
+ this.clearTimeoutHandler(key);
4266
4353
  }
4267
4354
  /**
4268
- * Subscribe to the token nearing its expiration.
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.
4358
+ */
4359
+ clearAllTokens() {
4360
+ for (const key of Array.from(this.timeoutHandlers.keys())) {
4361
+ this.clearTimeoutHandler(key);
4362
+ }
4363
+ this._tokens.clear();
4364
+ }
4365
+ /**
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
- * @param cb - Callback function to execute when token is nearing expiration.
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.observeTokenExpiration();
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
- * Clear the timeout handler.
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.
4281
4395
  */
4282
- clearTimeoutHandler() {
4283
- if (this.timeoutHandler !== void 0) {
4284
- clearTimeout(this.timeoutHandler);
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);
4285
4407
  }
4286
4408
  }
4287
4409
  /**
4288
- * Observe and handle token expiration.
4410
+ * Observe and handle expiration of the token at the given cache key.
4289
4411
  */
4290
- observeTokenExpiration() {
4291
- this.clearTimeoutHandler();
4292
- if (this.observers.length === 0 || this.token === void 0) {
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
- this.timeoutHandler = setTimeout(
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 = this.token.tokenRemainingSeconds;
4305
- let timeoutTimeInSeconds = remainingSeconds - this.limitInSeconds;
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, this.token);
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(() => this.refreshToken());
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.
4469
+ *
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.
4341
4473
  *
4342
- * @returns {JwtToken<T, ExtraInfo> | Promise<JwtToken<T, ExtraInfo>>} The current token or the Promise of a token request.
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
- if (this.inflightPromise) {
4346
- return this.inflightPromise;
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.token;
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
- * @returns {Promise<JwtToken<T, ExtraInfo>>} Promise of the refreshed token.
4492
+ * @param params - Optional mint parameters. Omit for the legacy parameterless token.
4493
+ * @returns Promise of the refreshed token.
4360
4494
  */
4361
- refreshToken() {
4362
- if (this.inflightPromise === void 0) {
4363
- this.inflightPromise = new Promise((resolve, reject) => {
4364
- this.resolver.getJwt().then(({ jwt, extraInfo }) => {
4365
- this.inflightPromise = void 0;
4366
- const token = this.jwtRepository.setToken(jwt, extraInfo);
4367
- resolve(token);
4368
- }).catch((reason) => {
4369
- this.inflightPromise = void 0;
4370
- reject(reason);
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
- return this.inflightPromise;
4511
+ });
4512
+ this.inflightPromises.set(key, promise);
4513
+ return promise;
4375
4514
  }
4376
4515
  /**
4377
- * Method to check if a token refresh is in progress.
4516
+ * Method to check if any token refresh is in progress.
4378
4517
  *
4379
- * @returns {boolean} true if a token refresh is in progress, false otherwise.
4518
+ * @returns {boolean} true if at least one refresh is in flight, false otherwise.
4380
4519
  */
4381
4520
  get isRefreshingToken() {
4382
- return this.inflightPromise !== void 0;
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(...args) {
4539
+ service: function(input, init) {
4401
4540
  var _a;
4402
- const context = (_a = interceptors.createContext) == null ? void 0 : _a.call(interceptors);
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((args2) => interceptor(args2, context)),
4411
- resolvedPromiseLike$2(args)
4558
+ (previousPromise, interceptor) => previousPromise.then((args) => interceptor(args, context)),
4559
+ resolvedPromiseLike$2(fetchArgs)
4412
4560
  );
4413
- return Promise.resolve(pending).then((args2) => {
4561
+ return Promise.resolve(pending).then((args) => {
4414
4562
  if (retryInterceptor) {
4415
- return retryInterceptor(args2, retryService, context);
4563
+ return retryInterceptor(args, retryService, context);
4416
4564
  } else {
4417
4565
  if (retryService) {
4418
- return retryService.applyRetry(() => fetch(...args2));
4566
+ return retryService.applyRetry(() => fetch(...args));
4419
4567
  }
4420
- return fetch(...args2);
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.3-354dc58b
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 queryStrings = [];
4862
+ const searchParams = new URLSearchParams();
4715
4863
  for (const key of keys$1(params)) {
4716
- queryStrings.push(`${key}=${params[key]}`);
4864
+ searchParams.append(key, String(params[key]));
4717
4865
  }
4718
- if (queryStrings.length > 0) {
4719
- return `?${queryStrings.join('&')}`;
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-dev2-2d5e0a6e5f
5995
+ // version: 1.428.0-dev20-d72ac06681
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.errorCode === 'INVALID_SESSION_ID') {
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.errorCode === 'INVALID_SESSION_ID') {
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
- // Only retry on 400 status
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, bad requests, etc.)
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,122 @@ 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
+ function buildDispatchingSfapJwtResolver(legacyResolver, parameterizedResolver) {
7358
+ return {
7359
+ getJwt(params) {
7360
+ if (params === undefined) {
7361
+ return legacyResolver.getJwt();
7362
+ }
7363
+ return parameterizedResolver.getJwt(params);
7364
+ },
7365
+ };
7366
+ }
7367
+ // The parameterized mint is dispatched over **Aura transport**:
7368
+ // the resolver calls the SFAP Lightning JWT Service's auto-generated Aura method
7369
+ // `SalesforceApiPlatformController.postSFAPLightningJwtService`, so session + CSRF
7370
+ // are handled inside the Aura stack. This mirrors the legacy parameterless
7371
+ // `platformSfapJwtResolver`, which already mints over Aura. The minted JWT is then
7372
+ // attached as a Bearer token on the downstream SFAP API request (which stays HTTP).
7373
+ const parameterizedSfapJwtResolver = new ParameterizedSfapJwtAuraResolver();
7374
+ const sfapJwtResolver = buildDispatchingSfapJwtResolver(platformSfapJwtResolver, parameterizedSfapJwtResolver);
6979
7375
  const sfapJwtRepository = new JwtRepository();
6980
- const sfapJwtManager = new JwtManager(sfapJwtRepository, platformSfapJwtResolver);
7376
+ const sfapJwtManager = new JwtManager(sfapJwtRepository, sfapJwtResolver);
7377
+ function prefetchSfapJwt() {
7378
+ const maybePromise = sfapJwtManager.getJwt();
7379
+ if ('then' in maybePromise) {
7380
+ return maybePromise.then(() => undefined).catch(() => undefined);
7381
+ }
7382
+ return Promise.resolve(undefined);
7383
+ }
6981
7384
  function buildJwtAuthorizedSfapFetchServiceDescriptor(logger) {
6982
7385
  const jwtAuthorizedFetchService = buildServiceDescriptor$2({
6983
7386
  createContext: createInstrumentationIdContext(),
6984
- request: [buildThirdPartyTrackerRegisterInterceptor(), buildJwtRequestInterceptor(logger)],
6985
- retry: buildCsrfRetryInterceptor(),
7387
+ request: [
7388
+ buildThirdPartyTrackerRegisterInterceptor(),
7389
+ buildJwtParameterizationInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger)),
7390
+ // Guarded so it is skipped once the parameterized interceptor handled the request.
7391
+ buildGuardedLegacyJwtRequestInterceptor(logger),
7392
+ ],
6986
7393
  finally: [buildThirdPartyTrackerFinishInterceptor()],
6987
7394
  });
6988
7395
  return {
@@ -7062,8 +7469,13 @@ function buildUnauthorizedFetchServiceDescriptor() {
7062
7469
  tags: { authenticationScopes: '' },
7063
7470
  };
7064
7471
  }
7065
- function buildJwtRequestInterceptor(logger) {
7066
- const jwtRequestModifier = ({ baseUri }, [resource, request]) => {
7472
+ /**
7473
+ * The `JwtRequestModifier` shared by both the legacy and parameterized SFAP
7474
+ * interceptors: it rewrites the request URL's host/protocol to the minted
7475
+ * `extraInfo.baseUri` (only for `api.salesforce.com` resources).
7476
+ */
7477
+ function buildSfapJwtRequestModifier(logger) {
7478
+ return ({ baseUri }, [resource, request]) => {
7067
7479
  if (typeof resource !== 'string' && !(resource instanceof URL)) {
7068
7480
  // istanbul ignore else: this will not be tested in NODE_ENV = production for test coverage
7069
7481
  if (process.env.NODE_ENV !== 'production') {
@@ -7081,8 +7493,22 @@ function buildJwtRequestInterceptor(logger) {
7081
7493
  url.protocol = overrideUrl.protocol;
7082
7494
  return [url, request];
7083
7495
  };
7084
- const jwtRequestHeaderInterceptor = buildJwtRequestHeaderInterceptor(sfapJwtManager, jwtRequestModifier);
7085
- return jwtRequestHeaderInterceptor;
7496
+ }
7497
+ function buildJwtRequestInterceptor(logger) {
7498
+ return buildJwtRequestHeaderInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger));
7499
+ }
7500
+ /**
7501
+ * Wraps the legacy `buildJwtRequestHeaderInterceptor` with a pass-through guard:
7502
+ * when the parameterized interceptor has already authorized the request (an
7503
+ * `Authorization` header is present), this returns `args` untouched so the legacy
7504
+ * interceptor does not mint a second time or attempt to set a duplicate
7505
+ * Authorization header (which `setHeaderAuthorization` throws on). For every legacy
7506
+ * (non-parameterized) request no Authorization header is present yet, so the legacy
7507
+ * interceptor runs exactly as before — a strict pass-through.
7508
+ */
7509
+ function buildGuardedLegacyJwtRequestInterceptor(logger) {
7510
+ const legacyInterceptor = buildJwtRequestInterceptor(logger);
7511
+ return (args) => hasAuthorizationHeader(args) ? resolvedPromiseLike$2(args) : legacyInterceptor(args);
7086
7512
  }
7087
7513
 
7088
7514
  const PDL_EXECUTE_ASYNC_OPTIONS = {
@@ -10532,4 +10958,4 @@ function ldsEngineCreator() {
10532
10958
  }
10533
10959
 
10534
10960
  export { LexRequestStrategy, PdlPrefetcherEventType, PdlRequestPriority, buildPredictorForContext, configService, ldsEngineCreator as default, initializeLDS, initializeOneStore, notifyUpdateAvailableFactory, registerRequestStrategy, saveRequestAsPrediction, subscribeToPrefetcherEvents, unregisterRequestStrategy, whenPredictionsReady };
10535
- // version: 1.428.0-dev2-ad2109c7a1
10961
+ // version: 1.428.0-dev20-1b319a0432
@@ -1,5 +1,9 @@
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>;
6
+ export declare function prefetchSfapJwt(): Promise<undefined>;
3
7
  export declare function buildJwtAuthorizedSfapFetchServiceDescriptor(logger: LoggerService): FetchServiceDescriptor;
4
8
  /**
5
9
  * Returns a service descriptor for a fetch service that includes one-off copilot
@@ -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
  };
@@ -10,7 +11,6 @@ export type ExtraInfo = {
10
11
  * {@link JwtResolver} for platform SFAP
11
12
  */
12
13
  export declare const platformSfapJwtResolver: JwtResolver<ExtraInfo>;
13
- export declare function prefetchSfapJwt(): Promise<undefined>;
14
14
  declare const composedNetworkAdapter: {
15
15
  shouldHandleRequest(resourceRequest: ResourceRequest): boolean;
16
16
  adapter: (resourceRequest: ResourceRequest, resourceRequestContext: ResourceRequestContext) => Promise<FetchResponse<any>>;
@@ -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;
@@ -1,4 +1,5 @@
1
- import { RetryPolicy, RetryContext } from '@conduit-client/service-retry/v1';
1
+ import type { RetryContext } from '@conduit-client/service-retry/v1';
2
+ import { RetryPolicy } from '@conduit-client/service-retry/v1';
2
3
  import type { FetchParameters } from '@conduit-client/service-fetch-network/v1';
3
4
  type CsrfTokenRetryPolicyConfig = {
4
5
  /**
@@ -0,0 +1,80 @@
1
+ import type { JwtMintParams } from '@conduit-client/jwt-manager';
2
+ /**
3
+ * JSON-primitive value a dynamic mint parameter may take.
4
+ *
5
+ * Mirrors the SFAP Lightning JWT Service's own `validateValue`
6
+ * (`SFAPLightningJwtServiceResource.java`), which accepts native `Boolean`,
7
+ * `Number`, or `String` and lets the minted claim's type follow the JSON type.
8
+ * The resolver therefore forwards these primitives verbatim — it must NOT
9
+ * stringify booleans/numbers, because the `data_cloud_user_claims` handler gates
10
+ * on native `Boolean.TRUE.equals(...)` and silently drops a stringified `"true"`.
11
+ */
12
+ export type SfapDynamicParamValue = string | boolean | number;
13
+ /**
14
+ * Typed shape of the params the SFAP Lightning JWT Service understands.
15
+ * Lives in the resolver layer because OneStore is param-shape-agnostic; it
16
+ * sees only an opaque `JwtMintParams` bag and stable-JSON-stringifies it for
17
+ * cache keying.
18
+ */
19
+ export type SfapJwtMintParams = {
20
+ scopes?: string[];
21
+ dynamicParams?: Record<string, SfapDynamicParamValue>;
22
+ };
23
+ /**
24
+ * The `extraInfo` the SFAP JWT resolver returns alongside the minted token: the
25
+ * per-tenant base URI the downstream SFAP API request is rewritten to.
26
+ */
27
+ export type ParameterizedSfapExtraInfo = {
28
+ baseUri: string;
29
+ };
30
+ type DynamicParameterItem = {
31
+ name: string;
32
+ value: SfapDynamicParamValue;
33
+ };
34
+ /**
35
+ * The SFAP Lightning JWT Service mint request body. Sent as the `requestBody`
36
+ * named param of the `postSFAPLightningJwtService` Aura controller method.
37
+ * Matches the server's `SFAPLightningJwtServiceInputRepresentation`: `scopes` is
38
+ * a single space-delimited string; `dynamicParameters` is `{ items: [{ name, value }] }`.
39
+ */
40
+ export type SfapJwtRequestBody = {
41
+ scopes?: string;
42
+ dynamicParameters?: {
43
+ items: DynamicParameterItem[];
44
+ };
45
+ };
46
+ /**
47
+ * Normalize SFAP-shaped params into the opaque `JwtMintParams` bag that callers
48
+ * hand to `JwtManager.getJwt(params)`. Scopes are sorted because they are
49
+ * semantically a set — without this, `['a', 'b']` and `['b', 'a']` would cache
50
+ * as separate entries (OneStore treats arrays as ordered under stable-JSON
51
+ * serialization; see ADR "JWT Parameterization" §2).
52
+ *
53
+ * MANDATORY CONSUMER ENTRY POINT. Every consumer that mints a parameterized
54
+ * SFAP JWT MUST assemble its params through this function before calling the
55
+ * manager. The cache key is derived by `JwtManager` from the caller's params
56
+ * (`cacheKeyFor(params)`) *before* the resolver runs — so the resolver cannot
57
+ * normalize after the fact. Set-stable caching therefore depends on the caller
58
+ * routing params through here. Do NOT sort inside the resolver: that would only
59
+ * reorder the wire body, not the cache key, and mutating the caller's params
60
+ * mid-call would desync the in-flight-dedup key from the stored-token key.
61
+ */
62
+ export declare function buildSfapJwtMintParams(params: SfapJwtMintParams): JwtMintParams;
63
+ export type SfapParamsResult = {
64
+ params: SfapJwtMintParams;
65
+ } | {
66
+ error: string;
67
+ };
68
+ /**
69
+ * Validate and narrow an opaque `JwtMintParams` bag to the SFAP-shaped subset the
70
+ * resolver forwards. Returns `{ error }` (surfaced as a resolver rejection) for a
71
+ * malformed bag rather than throwing.
72
+ */
73
+ export declare function coerceToSfapParams(params: JwtMintParams): SfapParamsResult;
74
+ /**
75
+ * Build the SFAP mint request body from the coerced params: `scopes` joined into a
76
+ * single space-delimited string, `dynamicParams` mapped to `dynamicParameters.items`.
77
+ * Empty scopes / dynamic params are omitted.
78
+ */
79
+ export declare function buildRequestBody(params: SfapJwtMintParams): SfapJwtRequestBody;
80
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-aura",
3
- "version": "1.428.0-dev2",
3
+ "version": "1.428.0-dev20",
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.18.0",
38
- "@conduit-client/tools-core": "3.18.0",
39
- "@salesforce/lds-adapters-apex": "^1.428.0-dev2",
40
- "@salesforce/lds-adapters-uiapi": "^1.428.0-dev2",
41
- "@salesforce/lds-ads-bridge": "^1.428.0-dev2",
42
- "@salesforce/lds-aura-storage": "^1.428.0-dev2",
43
- "@salesforce/lds-bindings": "^1.428.0-dev2",
44
- "@salesforce/lds-instrumentation": "^1.428.0-dev2",
45
- "@salesforce/lds-network-aura": "^1.428.0-dev2",
46
- "@salesforce/lds-network-fetch": "^1.428.0-dev2",
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-dev20",
40
+ "@salesforce/lds-adapters-uiapi": "^1.428.0-dev20",
41
+ "@salesforce/lds-ads-bridge": "^1.428.0-dev20",
42
+ "@salesforce/lds-aura-storage": "^1.428.0-dev20",
43
+ "@salesforce/lds-bindings": "^1.428.0-dev20",
44
+ "@salesforce/lds-instrumentation": "^1.428.0-dev20",
45
+ "@salesforce/lds-network-aura": "^1.428.0-dev20",
46
+ "@salesforce/lds-network-fetch": "^1.428.0-dev20",
47
47
  "jwt-encode": "1.0.1"
48
48
  },
49
49
  "dependencies": {
50
- "@conduit-client/command-aura-graphql-normalized-cache-control": "3.18.0",
51
- "@conduit-client/command-aura-network": "3.18.0",
52
- "@conduit-client/command-aura-normalized-cache-control": "3.18.0",
53
- "@conduit-client/command-aura-resource-cache-control": "3.18.0",
54
- "@conduit-client/command-fetch-network": "3.18.0",
55
- "@conduit-client/command-http-graphql-normalized-cache-control": "3.18.0",
56
- "@conduit-client/command-http-normalized-cache-control": "3.18.0",
57
- "@conduit-client/command-ndjson": "3.18.0",
58
- "@conduit-client/command-network": "3.18.0",
59
- "@conduit-client/command-sse": "3.18.0",
60
- "@conduit-client/command-streaming": "3.18.0",
61
- "@conduit-client/service-aura-network": "3.18.0",
62
- "@conduit-client/service-bindings-imperative": "3.18.0",
63
- "@conduit-client/service-bindings-lwc": "3.18.0",
64
- "@conduit-client/service-cache": "3.18.0",
65
- "@conduit-client/service-cache-control": "3.18.0",
66
- "@conduit-client/service-cache-inclusion-policy": "3.18.0",
67
- "@conduit-client/service-config": "3.18.0",
68
- "@conduit-client/service-feature-flags": "3.18.0",
69
- "@conduit-client/service-fetch-network": "3.18.0",
70
- "@conduit-client/service-instrument-command": "3.18.0",
71
- "@conduit-client/service-pubsub": "3.18.0",
72
- "@conduit-client/service-store": "3.18.0",
73
- "@conduit-client/utils": "3.18.0",
74
- "@luvio/network-adapter-composable": "0.160.3",
75
- "@luvio/network-adapter-fetch": "0.160.3",
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",
75
+ "@luvio/network-adapter-composable": "0.160.4-dev1",
76
+ "@luvio/network-adapter-fetch": "0.160.4-dev1",
76
77
  "@lwc/state": "^0.29.0",
77
- "@salesforce/lds-adapters-onestore-graphql": "^1.428.0-dev2",
78
+ "@salesforce/lds-adapters-onestore-graphql": "^1.428.0-dev20",
78
79
  "@salesforce/lds-adapters-uiapi-lex": "^1.415.0",
79
- "@salesforce/lds-durable-storage": "^1.428.0-dev2",
80
- "@salesforce/lds-luvio-service": "^1.428.0-dev2",
81
- "@salesforce/lds-luvio-uiapi-records-service": "^1.428.0-dev2"
80
+ "@salesforce/lds-durable-storage": "^1.428.0-dev20",
81
+ "@salesforce/lds-luvio-service": "^1.428.0-dev20",
82
+ "@salesforce/lds-luvio-uiapi-records-service": "^1.428.0-dev20"
82
83
  },
83
84
  "luvioBundlesize": [
84
85
  {
85
86
  "path": "./dist/ldsEngineCreator.js",
86
87
  "maxSize": {
87
- "none": "370 kB",
88
+ "none": "410 kB",
88
89
  "min": "190 kB",
89
- "compressed": "61.9 kB"
90
+ "compressed": "71 kB"
90
91
  }
91
92
  }
92
93
  ],