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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-dev21-133a7da4f5
2716
2767
 
2717
2768
  /*!
2718
2769
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -3065,7 +3116,7 @@ function buildServiceDescriptor$9(notifyRecordUpdateAvailable, getNormalizedLuvi
3065
3116
  },
3066
3117
  };
3067
3118
  }
3068
- // version: 1.428.0-dev2-2d5e0a6e5f
3119
+ // version: 1.428.0-dev21-133a7da4f5
3069
3120
 
3070
3121
  /*!
3071
3122
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -4153,11 +4204,13 @@ class JwtToken {
4153
4204
  * @param _token - The JWT string.
4154
4205
  * @param _decodedInfo - The decoded information from the JWT.
4155
4206
  * @param _extraInfo - Any additional information associated with the JWT.
4207
+ * @param _mintParams - The parameters used to mint this token. Undefined for legacy parameterless tokens.
4156
4208
  */
4157
- constructor(_token, _decodedInfo, _extraInfo) {
4209
+ constructor(_token, _decodedInfo, _extraInfo, _mintParams) {
4158
4210
  this._token = _token;
4159
4211
  this._decodedInfo = _decodedInfo;
4160
4212
  this._extraInfo = _extraInfo;
4213
+ this._mintParams = _mintParams;
4161
4214
  }
4162
4215
  /**
4163
4216
  * Get the JWT string.
@@ -4183,6 +4236,14 @@ class JwtToken {
4183
4236
  get decodedInfo() {
4184
4237
  return this._decodedInfo;
4185
4238
  }
4239
+ /**
4240
+ * Get the mint parameters used to produce this token.
4241
+ *
4242
+ * @returns The mint parameters, or undefined for legacy parameterless tokens.
4243
+ */
4244
+ get mintParams() {
4245
+ return this._mintParams;
4246
+ }
4186
4247
  /**
4187
4248
  * Get the remaining time in seconds until the JWT expires.
4188
4249
  *
@@ -4200,6 +4261,12 @@ class JwtToken {
4200
4261
  return this.tokenRemainingSeconds <= 0;
4201
4262
  }
4202
4263
  }
4264
+ function cacheKeyFor(params) {
4265
+ if (params === void 0) {
4266
+ return "jwt:";
4267
+ }
4268
+ return `jwt:${stableJSONStringify$2(params) ?? ""}`;
4269
+ }
4203
4270
  let defaultLogger = {
4204
4271
  trace: () => {
4205
4272
  },
@@ -4233,85 +4300,146 @@ class JwtRepository {
4233
4300
  this.limitInSeconds = limitInSeconds;
4234
4301
  this.defaultTokenTTLInSeconds = defaultTokenTTLInSeconds;
4235
4302
  this.logger = logger;
4303
+ this._tokens = /* @__PURE__ */ new Map();
4304
+ this.timeoutHandlers = /* @__PURE__ */ new Map();
4236
4305
  this.observers = [];
4237
4306
  }
4238
4307
  /**
4239
- * Get the 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;
4341
+ }
4342
+ /**
4343
+ * Remove the cached token for the given mint params.
4344
+ *
4345
+ * @param params - The mint params identifying the token. Omit to remove
4346
+ * only the legacy global-key entry (matches today's behavior of "remove
4347
+ * the only token there is").
4348
+ */
4349
+ removeToken(params) {
4350
+ const key = cacheKeyFor(params);
4351
+ this._tokens.delete(key);
4352
+ this.clearTimeoutHandler(key);
4259
4353
  }
4260
4354
  /**
4261
- * Remove the current token.
4355
+ * Remove every cached token and clear every near-expiry timer. Used by
4356
+ * test teardown and full auth-reset paths that previously called
4357
+ * `removeToken()` to clear the single token.
4262
4358
  */
4263
- removeToken() {
4264
- this._token = void 0;
4265
- this.clearTimeoutHandler();
4359
+ clearAllTokens() {
4360
+ for (const key of Array.from(this.timeoutHandlers.keys())) {
4361
+ this.clearTimeoutHandler(key);
4362
+ }
4363
+ this._tokens.clear();
4266
4364
  }
4267
4365
  /**
4268
- * Subscribe to the token nearing its expiration.
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.
4395
+ */
4396
+ get size() {
4397
+ return this._tokens.size;
4398
+ }
4399
+ /**
4400
+ * Clear the timeout handler for a specific cache key.
4281
4401
  */
4282
- clearTimeoutHandler() {
4283
- if (this.timeoutHandler !== void 0) {
4284
- clearTimeout(this.timeoutHandler);
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.
4341
4469
  *
4342
- * @returns {JwtToken<T, ExtraInfo> | Promise<JwtToken<T, ExtraInfo>>} The current token or the Promise of a token request.
4470
+ * If a request for the same params is in flight, returns its promise. If
4471
+ * a cached token for those params exists and is not expired, returns it
4472
+ * synchronously. Otherwise initiates a refresh.
4473
+ *
4474
+ * @param params - Optional mint parameters. Omit for the legacy parameterless token.
4475
+ * @returns The cached token (sync) or a promise that resolves to the refreshed token.
4343
4476
  */
4344
- getJwt() {
4345
- 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-dev21-133a7da4f5
5705
5996
 
5706
5997
  const environmentHasAura = typeof window !== 'undefined' && typeof window.$A !== 'undefined';
5707
5998
  const defaultConfig = {
@@ -5961,12 +6252,20 @@ function buildLuvioPageScopedCacheRequestInterceptor() {
5961
6252
  };
5962
6253
  }
5963
6254
 
6255
+ // Connect REST returns errors as an array `[{ errorCode, ... }, ...]`; the Aura
6256
+ // action path uses a bare object `{ errorCode, ... }`. HTTP-based adapters hit
6257
+ // Connect REST directly, so the body arrives array-shaped and a plain
6258
+ // `body.errorCode` read would miss it. Handle both shapes.
6259
+ function isInvalidSession(body) {
6260
+ const errorCode = Array.isArray(body) ? body[0] && body[0].errorCode : body && body.errorCode;
6261
+ return errorCode === 'INVALID_SESSION_ID';
6262
+ }
5964
6263
  function buildLexRuntimeAuthExpirationRedirectResponseInterceptor(logger) {
5965
6264
  return async (response) => {
5966
6265
  if (response.status === 401) {
5967
6266
  try {
5968
6267
  const coercedResponse = (await coerceResponseToFetchResponse(response.clone()));
5969
- if (coercedResponse.body.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,143 @@ function buildCsrfRetryInterceptor() {
6975
7274
  };
6976
7275
  }
6977
7276
 
7277
+ /**
7278
+ * The context-seed key under which a custom command forwards its JWT mint
7279
+ * parameters. The framework's `buildServiceDescriptor` merges the request init's
7280
+ * `__contextSeed` onto the per-request interceptor context, so this interceptor
7281
+ * reads the params off `context[JWT_MINT_PARAMS_SEED_KEY]`.
7282
+ *
7283
+ * This is a cross-team contract: the custom command writes this key, this
7284
+ * interceptor reads it. It is intentionally an opaque key — OneStore does not
7285
+ * define it and never inspects the param shape; the typed shape lives in the
7286
+ * resolver.
7287
+ */
7288
+ const JWT_MINT_PARAMS_SEED_KEY = 'jwtMintParams';
7289
+ /**
7290
+ * Returns `true` if the fetch arguments already carry an `Authorization` header,
7291
+ * across the three shapes the framework's `setHeader` handles: a `Request`
7292
+ * resource's own headers, an `options.headers` `Headers` instance, or a plain
7293
+ * record. The descriptor's guarded legacy interceptor uses this to skip when the
7294
+ * parameterized interceptor has already authorized the request — avoiding a second
7295
+ * mint and the throw `setHeaderAuthorization` raises on an existing header.
7296
+ */
7297
+ function hasAuthorizationHeader([resource, options]) {
7298
+ if (resource instanceof Request && resource.headers.has('Authorization')) {
7299
+ return true;
7300
+ }
7301
+ const headers = options?.headers;
7302
+ if (headers === undefined) {
7303
+ return false;
7304
+ }
7305
+ if (headers instanceof Headers) {
7306
+ return headers.has('Authorization');
7307
+ }
7308
+ if (Array.isArray(headers)) {
7309
+ return headers.some(([name]) => name.toLowerCase() === 'authorization');
7310
+ }
7311
+ return Reflect.has(headers, 'Authorization');
7312
+ }
7313
+ /**
7314
+ * Builds the request interceptor that bridges the per-request context seed to the
7315
+ * dispatching SFAP JwtManager for the **parameterized** mint path.
7316
+ *
7317
+ * For a parameterized SFAP command, the context carries `jwtMintParams`. This
7318
+ * interceptor reads them, calls `jwtManager.getJwt(params)` (which the dispatching
7319
+ * resolver routes to the parameterized resolver), attaches the `Authorization`
7320
+ * header, and rewrites the request URL via the minted `baseUri`. The presence of
7321
+ * that `Authorization` header is the signal the descriptor's guarded legacy
7322
+ * interceptor uses to skip — so the legacy path does not mint a second time.
7323
+ *
7324
+ * For a legacy parameterless command the context carries no `jwtMintParams`, so
7325
+ * this interceptor **early-returns on its first line** and the request flows
7326
+ * unchanged to the legacy `buildJwtRequestHeaderInterceptor` — guaranteeing zero
7327
+ * behavior change for non-opted-in adapters.
7328
+ *
7329
+ * Lives in `lds-lightning-platform` (not OneStore) beside the existing SFAP/CSRF
7330
+ * interceptors, per the JWT-parameterization ADR §5: OneStore provides only the
7331
+ * generic interceptor mechanism and the opaque context-seed channel; the service-
7332
+ * specific bridge is a runtime-layer concern.
7333
+ *
7334
+ * @param jwtManager - the dispatching SFAP JwtManager
7335
+ * @param jwtRequestModifier - applies the minted `extraInfo.baseUri` to the request URL
7336
+ */
7337
+ function buildJwtParameterizationInterceptor(jwtManager, jwtRequestModifier = (_extraInfo, fetchArgs) => fetchArgs) {
7338
+ return (fetchArgs, context) => {
7339
+ const jwtContext = context;
7340
+ const mintParams = jwtContext?.[JWT_MINT_PARAMS_SEED_KEY];
7341
+ // No mint params → not a parameterized request. Do nothing; the legacy
7342
+ // interceptor handles it. This MUST be the first statement so non-opted-in
7343
+ // SFAP commands observe no work at all.
7344
+ if (mintParams === undefined) {
7345
+ return resolvedPromiseLike$2(fetchArgs);
7346
+ }
7347
+ return resolvedPromiseLike$2(jwtManager.getJwt(mintParams)).then((token) => {
7348
+ const fetchArgsWithAuthorization = setHeaderAuthorization(token, fetchArgs);
7349
+ return token.extraInfo
7350
+ ? jwtRequestModifier(token.extraInfo, fetchArgsWithAuthorization)
7351
+ : fetchArgsWithAuthorization;
7352
+ });
7353
+ };
7354
+ }
7355
+
6978
7356
  const SFAP_BASE_URL = 'api.salesforce.com';
7357
+ // The fetch-service tag the Data360 (direct-to-Data Cloud) custom command binds
7358
+ // to. Distinct from SFAP's `sfap_api` so the Data360 host-rewrite interceptor only
7359
+ // ever runs for that command — zero blast radius on the SFAP fleet.
7360
+ //
7361
+ // The tag is deliberately named after the *mechanism* (the `cdp_url` host rewrite),
7362
+ // not a capability, because this whole descriptor is interim: it exists only while
7363
+ // the mint service returns the fixed SFAP `baseUri` regardless of platform. When the
7364
+ // service returns the correct per-platform `baseUri`, the data 360 route can be re-evaluated.
7365
+ // This mechanism is NOT intended to be re-used outside of this initial context.
7366
+ // This is a client-side routing key only.
7367
+ const DATA_360_CDP_URL_REWRITE_AUTH_SCOPE = 'data_360_cdp_url_rewrite';
7368
+ // The minted-JWT claim carrying the Data Cloud tenant-specific endpoint (TSE).
7369
+ // The server's `SFAPJwtClaimHandlerImpl` emits it for CDP-provisioned orgs as a
7370
+ // verbatim passthrough of `DataCloudTenant.getApiEndpoint()` — no normalization.
7371
+ // The exact FORM therefore varies and must not be assumed: it may be a bare host
7372
+ // (`<hash>.c360a.salesforce.com`) or a full URL with scheme
7373
+ // (`https://a360.cdp.<region>.aws.sfdc.cl`), and the suffix is cloud-dependent
7374
+ // (commercial `.salesforce.com`, substrate `.aws.sfdc.cl`, GovCloud
7375
+ // `.salesforce.mil`). This is why the interceptor strips any scheme before parsing
7376
+ // and applies no host-suffix policy — see the interceptor docblock's trust boundary.
7377
+ const CDP_URL_CLAIM = 'cdp_url';
7378
+ function buildDispatchingSfapJwtResolver(legacyResolver, parameterizedResolver) {
7379
+ return {
7380
+ getJwt(params) {
7381
+ if (params === undefined) {
7382
+ return legacyResolver.getJwt();
7383
+ }
7384
+ return parameterizedResolver.getJwt(params);
7385
+ },
7386
+ };
7387
+ }
7388
+ // The parameterized mint is dispatched over **Aura transport**:
7389
+ // the resolver calls the SFAP Lightning JWT Service's auto-generated Aura method
7390
+ // `SalesforceApiPlatformController.postSFAPLightningJwtService`, so session + CSRF
7391
+ // are handled inside the Aura stack. This mirrors the legacy parameterless
7392
+ // `platformSfapJwtResolver`, which already mints over Aura. The minted JWT is then
7393
+ // attached as a Bearer token on the downstream SFAP API request (which stays HTTP).
7394
+ const parameterizedSfapJwtResolver = new ParameterizedSfapJwtAuraResolver();
7395
+ const sfapJwtResolver = buildDispatchingSfapJwtResolver(platformSfapJwtResolver, parameterizedSfapJwtResolver);
6979
7396
  const sfapJwtRepository = new JwtRepository();
6980
- const sfapJwtManager = new JwtManager(sfapJwtRepository, platformSfapJwtResolver);
7397
+ const sfapJwtManager = new JwtManager(sfapJwtRepository, sfapJwtResolver);
7398
+ function prefetchSfapJwt() {
7399
+ const maybePromise = sfapJwtManager.getJwt();
7400
+ if ('then' in maybePromise) {
7401
+ return maybePromise.then(() => undefined).catch(() => undefined);
7402
+ }
7403
+ return Promise.resolve(undefined);
7404
+ }
6981
7405
  function buildJwtAuthorizedSfapFetchServiceDescriptor(logger) {
6982
7406
  const jwtAuthorizedFetchService = buildServiceDescriptor$2({
6983
7407
  createContext: createInstrumentationIdContext(),
6984
- request: [buildThirdPartyTrackerRegisterInterceptor(), buildJwtRequestInterceptor(logger)],
6985
- retry: buildCsrfRetryInterceptor(),
7408
+ request: [
7409
+ buildThirdPartyTrackerRegisterInterceptor(),
7410
+ buildJwtParameterizationInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger)),
7411
+ // Guarded so it is skipped once the parameterized interceptor handled the request.
7412
+ buildGuardedLegacyJwtRequestInterceptor(logger),
7413
+ ],
6986
7414
  finally: [buildThirdPartyTrackerFinishInterceptor()],
6987
7415
  });
6988
7416
  return {
@@ -6990,6 +7418,46 @@ function buildJwtAuthorizedSfapFetchServiceDescriptor(logger) {
6990
7418
  tags: { authenticationScopes: 'sfap_api' },
6991
7419
  };
6992
7420
  }
7421
+ /**
7422
+ * Returns a service descriptor for the **direct-to-Data360** custom command
7423
+ * (CDP Query v3, host `*.c360a.salesforce.com`).
7424
+ *
7425
+ * Unlike SFAP, the per-tenant base host is NOT returned in the mint response's
7426
+ * `baseUri` (which is the fixed SFAP host); it rides on the minted JWT as the
7427
+ * `cdp_url` claim. The standard `JwtRequestModifier` only receives `extraInfo`,
7428
+ * never the decoded claims, so the host rewrite cannot be expressed as a modifier
7429
+ * — it must read the token directly. This descriptor therefore uses a bespoke
7430
+ * interceptor ({@link buildData360HostRewriteInterceptor}) that mints the
7431
+ * parameterized SFAP JWT, attaches the Bearer token, decodes `cdp_url`, and
7432
+ * rewrites the request host to that TSE.
7433
+ *
7434
+ * It reuses the module's `sfapJwtManager`: `cdp_url` is emitted on the same
7435
+ * `SFAP_API`-scope JWT, so the Data360 command mints the same kind of token — it
7436
+ * just forwards the data-cloud scopes via the context-seed `jwtMintParams`.
7437
+ *
7438
+ * Gated behind its own `data_360_cdp_url_rewrite` auth-scope tag so it binds ONLY to
7439
+ * the Data360 command — the SFAP fleet never flows past this interceptor.
7440
+ */
7441
+ function buildJwtAuthorizedData360FetchServiceDescriptor(logger) {
7442
+ const data360FetchService = buildServiceDescriptor$2({
7443
+ createContext: createInstrumentationIdContext(),
7444
+ request: [
7445
+ buildThirdPartyTrackerRegisterInterceptor(),
7446
+ buildData360HostRewriteInterceptor(logger),
7447
+ // Compression runs LAST, after the host-rewrite interceptor has minted,
7448
+ // attached the Bearer token, and rewritten the URL — so it only ever sees
7449
+ // the final request body. It is a strict pass-through: bodies that are
7450
+ // missing, non-string, or under the 1KB threshold flow through untouched,
7451
+ // preserving the tuple (URL + Authorization header) the rewrite produced.
7452
+ buildCompressionInterceptor({ algorithm: 'gzip' }),
7453
+ ],
7454
+ finally: [buildThirdPartyTrackerFinishInterceptor()],
7455
+ });
7456
+ return {
7457
+ ...data360FetchService,
7458
+ tags: { authenticationScopes: DATA_360_CDP_URL_REWRITE_AUTH_SCOPE },
7459
+ };
7460
+ }
6993
7461
  /**
6994
7462
  * Returns a service descriptor for a fetch service that includes one-off copilot
6995
7463
  * hacks. This fetch service is not intended for use by anything other than
@@ -7062,8 +7530,13 @@ function buildUnauthorizedFetchServiceDescriptor() {
7062
7530
  tags: { authenticationScopes: '' },
7063
7531
  };
7064
7532
  }
7065
- function buildJwtRequestInterceptor(logger) {
7066
- const jwtRequestModifier = ({ baseUri }, [resource, request]) => {
7533
+ /**
7534
+ * The `JwtRequestModifier` shared by both the legacy and parameterized SFAP
7535
+ * interceptors: it rewrites the request URL's host/protocol to the minted
7536
+ * `extraInfo.baseUri` (only for `api.salesforce.com` resources).
7537
+ */
7538
+ function buildSfapJwtRequestModifier(logger) {
7539
+ return ({ baseUri }, [resource, request]) => {
7067
7540
  if (typeof resource !== 'string' && !(resource instanceof URL)) {
7068
7541
  // istanbul ignore else: this will not be tested in NODE_ENV = production for test coverage
7069
7542
  if (process.env.NODE_ENV !== 'production') {
@@ -7081,8 +7554,91 @@ function buildJwtRequestInterceptor(logger) {
7081
7554
  url.protocol = overrideUrl.protocol;
7082
7555
  return [url, request];
7083
7556
  };
7084
- const jwtRequestHeaderInterceptor = buildJwtRequestHeaderInterceptor(sfapJwtManager, jwtRequestModifier);
7085
- return jwtRequestHeaderInterceptor;
7557
+ }
7558
+ function buildJwtRequestInterceptor(logger) {
7559
+ return buildJwtRequestHeaderInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger));
7560
+ }
7561
+ /**
7562
+ * Request interceptor for the direct-to-Data360 command. It cannot use the
7563
+ * standard `JwtRequestModifier` seam because the tenant host is a JWT *claim*
7564
+ * (`cdp_url`), and modifiers only receive `extraInfo`. So — like the copilot
7565
+ * interceptor that reads `decodedInfo.iss` — it holds the token directly:
7566
+ *
7567
+ * 1. Read the command's `jwtMintParams` off the per-request context seed and
7568
+ * mint (via the parameterized SFAP resolver, routed by the dispatching
7569
+ * resolver inside `sfapJwtManager`). Without mint params there is no token
7570
+ * to authorize with, so leave the request untouched.
7571
+ * 2. Decode `cdp_url` from the JWT and resolve the tenant-specific endpoint
7572
+ * (TSE). `cdp_url` is a bare host, so a scheme is prepended before parsing
7573
+ * and the protocol is forced to `https:`.
7574
+ * 3. Only after a usable `cdp_url` is confirmed, attach `Authorization: Bearer
7575
+ * <jwt>` and rewrite the request URL's host/protocol to the TSE.
7576
+ *
7577
+ * **Trust boundary.** `cdp_url` is a claim on a first-party, server-minted JWT
7578
+ * delivered over the same-origin Aura transport, so the interceptor does NOT police
7579
+ * its contents (no host-suffix or format allowlist) — the routing host is the
7580
+ * minting service's responsibility, and this mirrors the SFAP sibling, which
7581
+ * likewise trusts the server-provided `baseUri` rewrite target. The one guard that
7582
+ * remains is shape hygiene: if `cdp_url` is absent/empty/non-string (org not
7583
+ * CDP-provisioned, so the server's claim gate did not fire) the interceptor throws
7584
+ * a developer-facing error instead of an opaque parse failure. A minted token is
7585
+ * never attached until a usable host is in hand.
7586
+ */
7587
+ function buildData360HostRewriteInterceptor(logger) {
7588
+ return (fetchArgs, context) => {
7589
+ const mintParams = context?.[JWT_MINT_PARAMS_SEED_KEY];
7590
+ // No mint params → not a parameterized Data360 request. Nothing to do.
7591
+ if (mintParams === undefined) {
7592
+ return resolvedPromiseLike$2(fetchArgs);
7593
+ }
7594
+ return resolvedPromiseLike$2(sfapJwtManager.getJwt(mintParams)).then((token) => {
7595
+ const [resource, request] = fetchArgs;
7596
+ if (typeof resource !== 'string' && !(resource instanceof URL)) {
7597
+ // istanbul ignore else: not exercised under NODE_ENV=production
7598
+ if (process.env.NODE_ENV !== 'production') {
7599
+ throw new Error('Data360 fetch service expects a string or URL resource');
7600
+ }
7601
+ return fetchArgs;
7602
+ }
7603
+ const cdpUrl = token.decodedInfo?.[CDP_URL_CLAIM];
7604
+ // Require a non-empty string. This is shape hygiene, NOT a content
7605
+ // policy: the routing host is the minting service's responsibility, and
7606
+ // we deliberately do not couple to its contents (no host-suffix/format
7607
+ // check — the SFAP sibling likewise trusts its server-provided rewrite
7608
+ // target). We only confirm we were handed a usable host string; a
7609
+ // missing/empty/non-string claim (org not Data Cloud provisioned, so the
7610
+ // server's claim gate did not fire) fails with a clear error rather than
7611
+ // an opaque `new URL` TypeError. The `typeof` check also narrows `cdpUrl`
7612
+ // to `string` for the `.replace` below.
7613
+ if (typeof cdpUrl !== 'string' || cdpUrl.length === 0) {
7614
+ logger.warn(`Data360 fetch service: minted JWT has no usable "${CDP_URL_CLAIM}" claim. The org may not be Data Cloud provisioned.`);
7615
+ // eslint-disable-next-line @salesforce/lds/no-error-in-production
7616
+ throw new Error(`Data360 fetch service: minted JWT has no usable "${CDP_URL_CLAIM}" claim; cannot route the request to a Data Cloud tenant endpoint.`);
7617
+ }
7618
+ // `cdp_url` is a bare host (no scheme); prepend https so `new URL`
7619
+ // parses it as an origin. Force https regardless of any scheme in the
7620
+ // claim — the TSE is always TLS.
7621
+ const tse = new URL(`https://${cdpUrl.replace(/^[a-z]+:\/\//i, '')}`);
7622
+ const authorizedArgs = setHeaderAuthorization(token, [resource, request]);
7623
+ const url = typeof resource === 'string' ? new URL(resource) : new URL(resource.toString());
7624
+ url.host = tse.host;
7625
+ url.protocol = 'https:';
7626
+ return [url, authorizedArgs[1]];
7627
+ });
7628
+ };
7629
+ }
7630
+ /**
7631
+ * Wraps the legacy `buildJwtRequestHeaderInterceptor` with a pass-through guard:
7632
+ * when the parameterized interceptor has already authorized the request (an
7633
+ * `Authorization` header is present), this returns `args` untouched so the legacy
7634
+ * interceptor does not mint a second time or attempt to set a duplicate
7635
+ * Authorization header (which `setHeaderAuthorization` throws on). For every legacy
7636
+ * (non-parameterized) request no Authorization header is present yet, so the legacy
7637
+ * interceptor runs exactly as before — a strict pass-through.
7638
+ */
7639
+ function buildGuardedLegacyJwtRequestInterceptor(logger) {
7640
+ const legacyInterceptor = buildJwtRequestInterceptor(logger);
7641
+ return (args) => hasAuthorizationHeader(args) ? resolvedPromiseLike$2(args) : legacyInterceptor(args);
7086
7642
  }
7087
7643
 
7088
7644
  const PDL_EXECUTE_ASYNC_OPTIONS = {
@@ -10478,6 +11034,7 @@ function initializeOneStore(luvio) {
10478
11034
  buildLexRuntimeDefaultFetchServiceDescriptor(loggerService, retryService),
10479
11035
  buildUnauthorizedFetchServiceDescriptor(),
10480
11036
  buildJwtAuthorizedSfapFetchServiceDescriptor(loggerService),
11037
+ buildJwtAuthorizedData360FetchServiceDescriptor(loggerService),
10481
11038
  buildCopilotFetchServiceDescriptor(loggerService),
10482
11039
  buildAuraNetworkService(),
10483
11040
  buildServiceDescriptor$j(instrumentationServiceDescriptor.service),
@@ -10532,4 +11089,4 @@ function ldsEngineCreator() {
10532
11089
  }
10533
11090
 
10534
11091
  export { LexRequestStrategy, PdlPrefetcherEventType, PdlRequestPriority, buildPredictorForContext, configService, ldsEngineCreator as default, initializeLDS, initializeOneStore, notifyUpdateAvailableFactory, registerRequestStrategy, saveRequestAsPrediction, subscribeToPrefetcherEvents, unregisterRequestStrategy, whenPredictionsReady };
10535
- // version: 1.428.0-dev2-ad2109c7a1
11092
+ // version: 1.428.0-dev21-c9faba50c9