@salesforce/lds-runtime-one-app 1.453.0 → 1.455.0

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.
@@ -48,7 +48,7 @@ import { createStorage, clearStorages } from 'force/ldsDurableStorage';
48
48
  import { registerSubRequestNetworkAdapter } from 'force/ldsNetwork';
49
49
 
50
50
  const { create: create$1, freeze, keys: keys$2, entries: entries$1 } = Object;
51
- const { isArray: isArray$3 } = Array;
51
+ const { isArray: isArray$4 } = Array;
52
52
  const { stringify: stringify$3, parse: parse$2 } = JSON;
53
53
  const WeakSetConstructor = WeakSet;
54
54
  const LogLevelMap = {
@@ -190,7 +190,7 @@ function stableJSONStringify$2(node) {
190
190
  }
191
191
  let i;
192
192
  let out;
193
- if (isArray$3(node)) {
193
+ if (isArray$4(node)) {
194
194
  out = "[";
195
195
  for (i = 0; i < node.length; i++) {
196
196
  if (i) {
@@ -298,7 +298,7 @@ function deepFreeze(value) {
298
298
  return;
299
299
  }
300
300
  deeplyFrozen.add(value);
301
- if (isArray$3(value)) {
301
+ if (isArray$4(value)) {
302
302
  for (let i = 0, len = value.length; i < len; i += 1) {
303
303
  deepFreeze(value[i]);
304
304
  }
@@ -525,7 +525,7 @@ function buildServiceDescriptor$p() {
525
525
  };
526
526
  }
527
527
 
528
- const { isArray: isArray$2 } = Array;
528
+ const { isArray: isArray$3 } = Array;
529
529
  let Ok$1 = class Ok {
530
530
  constructor(value) {
531
531
  this.value = value;
@@ -605,8 +605,8 @@ function deepEquals$1(x, y) {
605
605
  return y === null;
606
606
  } else if (y === null) {
607
607
  return x === null;
608
- } else if (isArray$2(x)) {
609
- if (!isArray$2(y) || x.length !== y.length) {
608
+ } else if (isArray$3(x)) {
609
+ if (!isArray$3(y) || x.length !== y.length) {
610
610
  return false;
611
611
  }
612
612
  for (let i = 0; i < x.length; ++i) {
@@ -2588,7 +2588,7 @@ function buildServiceDescriptor$d(luvio) {
2588
2588
  },
2589
2589
  };
2590
2590
  }
2591
- // version: 1.453.0-725e09cf25
2591
+ // version: 1.455.0-c53c97de2a
2592
2592
 
2593
2593
  class AuraGraphQLNormalizedCacheControlCommand extends AuraNormalizedCacheControlCommand {
2594
2594
  constructor(config, documentRootType, services) {
@@ -2927,7 +2927,7 @@ function buildServiceDescriptor$9(notifyRecordUpdateAvailable, getNormalizedLuvi
2927
2927
  },
2928
2928
  };
2929
2929
  }
2930
- // version: 1.453.0-725e09cf25
2930
+ // version: 1.455.0-c53c97de2a
2931
2931
 
2932
2932
  class RetryService {
2933
2933
  constructor(defaultRetryPolicy) {
@@ -4640,9 +4640,10 @@ var TypeCheckShapes;
4640
4640
  TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
4641
4641
  TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
4642
4642
  })(TypeCheckShapes || (TypeCheckShapes = {}));
4643
- // engine version: 0.161.0-fe06f180
4643
+ // engine version: 0.161.2-bd1bd38a
4644
4644
 
4645
4645
  const { keys: keys$1 } = Object;
4646
+ const { isArray: isArray$2 } = Array;
4646
4647
 
4647
4648
  // we're going to intentionally bundle this small bit of luvio engine code into
4648
4649
  // this module to keep it runtime dependency-free
@@ -4715,7 +4716,14 @@ const fetchNetworkAdapter = async (resourceRequest, _resourceRequestContext) =>
4715
4716
  function generateQueryString(params) {
4716
4717
  const searchParams = new URLSearchParams();
4717
4718
  for (const key of keys$1(params)) {
4718
- searchParams.append(key, String(params[key]));
4719
+ const value = params[key];
4720
+ // omit undefined and empty-array values entirely: String(undefined) === 'undefined'
4721
+ // and String([]) === '', both of which would otherwise serialize as a valueless
4722
+ // `key=` or literal `key=undefined` query param that servers can reject.
4723
+ if (value === undefined || (isArray$2(value) && value.length === 0)) {
4724
+ continue;
4725
+ }
4726
+ searchParams.append(key, String(value));
4719
4727
  }
4720
4728
  const queryString = searchParams.toString();
4721
4729
  if (queryString.length > 0) {
@@ -5732,7 +5740,7 @@ class PrioritizedConfigService {
5732
5740
  get: (_, property) => {
5733
5741
  const propertyPath = [...path, property];
5734
5742
  const value = this.valueFor(propertyPath);
5735
- return typeof value === "object" && value !== null && !isArray$3(value) ? this.buildReadonlyObjectProxy(propertyPath) : value;
5743
+ return typeof value === "object" && value !== null && !isArray$4(value) ? this.buildReadonlyObjectProxy(propertyPath) : value;
5736
5744
  },
5737
5745
  getOwnPropertyDescriptor: (target, property) => {
5738
5746
  const propertyPath = [...path, property];
@@ -5740,7 +5748,7 @@ class PrioritizedConfigService {
5740
5748
  return {
5741
5749
  value: (
5742
5750
  // TODO - need to wrap arrays in a Proxy as well
5743
- typeof value === "object" && value !== null && !isArray$3(value) ? this.buildReadonlyObjectProxy(propertyPath) : value
5751
+ typeof value === "object" && value !== null && !isArray$4(value) ? this.buildReadonlyObjectProxy(propertyPath) : value
5744
5752
  ),
5745
5753
  writable: false,
5746
5754
  configurable: true,
@@ -5841,7 +5849,7 @@ function getEnvironmentSetting(name) {
5841
5849
  }
5842
5850
  return undefined;
5843
5851
  }
5844
- // version: 1.453.0-e6dc0c039a
5852
+ // version: 1.455.0-96e9b41a18
5845
5853
 
5846
5854
  /**
5847
5855
  * Helpers for reaching the Aura framework from the LDS Aura runtime.
@@ -6414,6 +6422,45 @@ function getCsrfTokenManager() {
6414
6422
  return cached;
6415
6423
  }
6416
6424
 
6425
+ /**
6426
+ * Extracts the HTTP method from FetchParameters, matching the Fetch API's own
6427
+ * precedence: an options.method override wins over a Request object's method
6428
+ * (e.g. `fetch(new Request(url, { method: 'GET' }), { method: 'POST' })`
6429
+ * resolves to `POST`).
6430
+ */
6431
+ function getFetchMethod([urlOrRequest, options]) {
6432
+ if (options && 'method' in options) {
6433
+ return options.method;
6434
+ }
6435
+ if (typeof urlOrRequest !== 'string' && 'method' in urlOrRequest) {
6436
+ return urlOrRequest.method;
6437
+ }
6438
+ return undefined;
6439
+ }
6440
+ /**
6441
+ * Only GET requests are safe to replay automatically on 429/503 — replaying a
6442
+ * write (POST/PUT/PATCH/DELETE) risks executing a mutation more than once.
6443
+ * Shared by both the Luvio and generic-fetch throttling retry policies so the
6444
+ * write-guard rule lives in exactly one place.
6445
+ */
6446
+ function isRetryableMethod(method) {
6447
+ return method?.toLowerCase() === 'get';
6448
+ }
6449
+ /**
6450
+ * Determines if the HTTP method requires CSRF protection.
6451
+ * Only mutating operations (POST, PUT, PATCH, DELETE) require CSRF tokens.
6452
+ */
6453
+ function isCsrfMethod(method) {
6454
+ if (!method) {
6455
+ return false;
6456
+ }
6457
+ const normalizedMethod = method.toLowerCase();
6458
+ return (normalizedMethod === 'post' ||
6459
+ normalizedMethod === 'put' ||
6460
+ normalizedMethod === 'patch' ||
6461
+ normalizedMethod === 'delete');
6462
+ }
6463
+
6417
6464
  const CSRF_TOKEN_HEADER = 'X-CSRF-Token';
6418
6465
  /**
6419
6466
  * Resolves the CSRF token manager and returns the current token. Returns
@@ -6446,23 +6493,6 @@ function areCsrfGatesEnabled() {
6446
6493
  return false;
6447
6494
  }
6448
6495
  }
6449
- /**
6450
- * Determines if the HTTP method requires CSRF protection.
6451
- * Only mutating operations (POST, PUT, PATCH, DELETE) require CSRF tokens.
6452
- *
6453
- * @param method - The HTTP method to check
6454
- * @returns true if the method requires CSRF protection
6455
- */
6456
- function isCsrfMethod(method) {
6457
- if (!method) {
6458
- return false;
6459
- }
6460
- const normalizedMethod = method.toLowerCase();
6461
- return (normalizedMethod === 'post' ||
6462
- normalizedMethod === 'put' ||
6463
- normalizedMethod === 'patch' ||
6464
- normalizedMethod === 'delete');
6465
- }
6466
6496
  /**
6467
6497
  * Builds a request interceptor that adds CSRF token headers to mutating requests.
6468
6498
  * The CSRF token is fetched once and cached for subsequent requests.
@@ -6476,17 +6506,8 @@ function buildCsrfTokenInterceptor() {
6476
6506
  if (!areCsrfGatesEnabled()) {
6477
6507
  return resolvedPromiseLike$2(fetchArgs);
6478
6508
  }
6479
- const [urlOrRequest, options] = fetchArgs;
6480
- // Determine the method from either Request object or options
6481
- let method;
6482
- if (typeof urlOrRequest !== 'string' && 'method' in urlOrRequest) {
6483
- method = urlOrRequest.method;
6484
- }
6485
- else if (options && 'method' in options) {
6486
- method = options.method;
6487
- }
6488
6509
  // Only add CSRF token for mutating operations
6489
- if (isCsrfMethod(method)) {
6510
+ if (isCsrfMethod(getFetchMethod(fetchArgs))) {
6490
6511
  const token = await getCsrfToken();
6491
6512
  if (token) {
6492
6513
  // eslint-disable-next-line no-param-reassign
@@ -6894,12 +6915,14 @@ const DEFAULT_CONFIG$2 = {
6894
6915
  jitterPercent: 0.5,
6895
6916
  };
6896
6917
  class LuvioFetchThrottlingRetryPolicy extends RetryPolicy {
6897
- constructor(config = DEFAULT_CONFIG$2) {
6918
+ constructor(config = DEFAULT_CONFIG$2, method) {
6898
6919
  super();
6899
6920
  this.config = config;
6921
+ this.method = method;
6900
6922
  }
6901
6923
  async shouldRetry(result, context) {
6902
- return ((result.status === 429 || result.status === 503) &&
6924
+ return (isRetryableMethod(this.method) &&
6925
+ (result.status === 429 || result.status === 503) &&
6903
6926
  context.attempt < this.config.maxRetries &&
6904
6927
  context.totalElapsedMs <= this.config.maxTimeToRetry);
6905
6928
  }
@@ -6962,7 +6985,7 @@ function buildLuvioFetchRetryInterceptor() {
6962
6985
  return (request, doFetch) => {
6963
6986
  const csrfPolicy = new LuvioCsrfTokenRetryPolicy();
6964
6987
  const composedPolicy = new ComposedRetryPolicy([
6965
- new LuvioFetchThrottlingRetryPolicy(),
6988
+ new LuvioFetchThrottlingRetryPolicy(undefined, request.method),
6966
6989
  csrfPolicy,
6967
6990
  ]);
6968
6991
  const retryService = new RetryService(composedPolicy);
@@ -7512,28 +7535,118 @@ async function isCsrfError(response) {
7512
7535
  }
7513
7536
  }
7514
7537
 
7515
- function buildCsrfRetryInterceptor() {
7538
+ const DEFAULT_CONFIG = {
7539
+ maxRetries: 3,
7540
+ maxTimeToRetry: 10000,
7541
+ baseDelay: 250,
7542
+ maxDelay: 5000,
7543
+ exponentialFactor: 2,
7544
+ jitterPercent: 0.5,
7545
+ };
7546
+ class FetchThrottlingRetryPolicy extends RetryPolicy {
7547
+ constructor(config = DEFAULT_CONFIG, method) {
7548
+ super();
7549
+ this.config = config;
7550
+ this.method = method;
7551
+ }
7552
+ async shouldRetry(result, context) {
7553
+ return (isRetryableMethod(this.method) &&
7554
+ (result.status === 429 || result.status === 503) &&
7555
+ context.attempt < this.config.maxRetries &&
7556
+ context.totalElapsedMs <= this.config.maxTimeToRetry);
7557
+ }
7558
+ async calculateDelay(result, context) {
7559
+ let delay;
7560
+ // If retry-after header is present and valid, use it
7561
+ const retryAfterHeader = this.parseRetryAfterHeader(result);
7562
+ if (retryAfterHeader !== undefined) {
7563
+ delay = Math.min(retryAfterHeader, this.config.maxDelay);
7564
+ }
7565
+ else {
7566
+ // Exponential backoff
7567
+ delay = Math.min(this.config.baseDelay * Math.pow(this.config.exponentialFactor, context.attempt), this.config.maxDelay);
7568
+ }
7569
+ // Add jitter to prevent thundering herd
7570
+ const jitter = delay * this.config.jitterPercent * (Math.random() - 0.5);
7571
+ return Math.max(0, delay + jitter);
7572
+ }
7573
+ parseRetryAfterHeader(result) {
7574
+ if (!result.headers) {
7575
+ return undefined;
7576
+ }
7577
+ const value = result.headers.get('Retry-After');
7578
+ if (!value) {
7579
+ return undefined;
7580
+ }
7581
+ // The Retry-After header can be either a number of seconds or an HTTP-date (IMF-fixdate)
7582
+ // Retry-After: <delay-seconds>
7583
+ // Retry-After: <http-date>
7584
+ const trimmed = value.trim();
7585
+ // Strictly treat only all-digits as seconds
7586
+ if (/^\d+$/.test(trimmed)) {
7587
+ const seconds = Number(trimmed);
7588
+ if (Number.isFinite(seconds) && seconds >= 0) {
7589
+ return seconds * 1000; // Convert to milliseconds
7590
+ }
7591
+ return undefined;
7592
+ }
7593
+ // Strict RFC 7231 IMF-fixdate format (e.g., "Sun, 06 Nov 1994 08:49:37 GMT")
7594
+ const IMF_FIXDATE = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
7595
+ if (!IMF_FIXDATE.test(trimmed)) {
7596
+ return undefined;
7597
+ }
7598
+ // Use Date.parse to avoid any engine-specific fallbacks
7599
+ const millis = Date.parse(trimmed);
7600
+ if (Number.isFinite(millis)) {
7601
+ return Math.max(0, millis - Date.now());
7602
+ }
7603
+ return undefined;
7604
+ }
7605
+ }
7606
+
7607
+ /**
7608
+ * A mutable holder for the current fetch args. CsrfTokenRetryPolicy's
7609
+ * prepareRetry hook updates `args` in place (e.g. to swap in a refreshed CSRF
7610
+ * token) so the retried fetch call picks up the change.
7611
+ */
7612
+ function buildCsrfPolicy(mutableRequest) {
7613
+ const csrfPolicy = new CsrfTokenRetryPolicy();
7614
+ csrfPolicy.setRequestContext(mutableRequest);
7615
+ return csrfPolicy;
7616
+ }
7617
+ /**
7618
+ * Built fresh per request so the write-guard check sees this request's own
7619
+ * HTTP method, rather than one method baked in for the life of the service.
7620
+ */
7621
+ function buildThrottlingPolicy(fetchArgs) {
7622
+ return new FetchThrottlingRetryPolicy(undefined, getFetchMethod(fetchArgs));
7623
+ }
7624
+ /**
7625
+ * Builds the retry interceptor used by the generic Conduit fetch service.
7626
+ *
7627
+ * Composes two independent per-request retry policies — throttling (429/503,
7628
+ * GET-only) and CSRF (401 token refresh) — built fresh per request rather
7629
+ * than reused off a shared defaultRetryPolicy. This keeps concurrent requests
7630
+ * from sharing mutable CSRF request-context state, and lets the throttling
7631
+ * policy see this request's own HTTP method.
7632
+ *
7633
+ * The `retryService` handed in by the framework is used only as the runner for
7634
+ * `applyRetry` — the real policy is always the per-request `composedPolicy`
7635
+ * passed as the override, never the service's own `defaultRetryPolicy`. When no
7636
+ * `retryService` is injected (e.g. the copilot/unauthorized descriptors, which
7637
+ * intentionally opt out of retries), we fall through to a plain fetch.
7638
+ */
7639
+ function buildFetchRetryInterceptor() {
7516
7640
  return async (fetchArgs, retryService, _context) => {
7517
7641
  if (retryService) {
7518
- // Create mutable context for CSRF retry policy
7519
7642
  const mutableRequest = { args: fetchArgs };
7520
- // Pass context to CSRF policy if it's being used
7521
- const policy = retryService.defaultRetryPolicy;
7522
- // Handle both direct CSRF policy and composed policy
7523
- if (policy instanceof CsrfTokenRetryPolicy) {
7524
- policy.setRequestContext(mutableRequest);
7525
- }
7526
- else if (policy instanceof ComposedRetryPolicy) {
7527
- // Extract CSRF policy from composed policy
7528
- const csrfPolicy = policy.getPolicyByType(CsrfTokenRetryPolicy);
7529
- if (csrfPolicy) {
7530
- csrfPolicy.setRequestContext(mutableRequest);
7531
- }
7532
- }
7533
- // Retry service will call prepareRetry hook which updates mutableRequest.args
7643
+ const composedPolicy = new ComposedRetryPolicy([
7644
+ buildThrottlingPolicy(fetchArgs),
7645
+ buildCsrfPolicy(mutableRequest),
7646
+ ]);
7534
7647
  return retryService.applyRetry(async () => {
7535
7648
  return fetch(...mutableRequest.args);
7536
- });
7649
+ }, composedPolicy);
7537
7650
  }
7538
7651
  return fetch(...fetchArgs);
7539
7652
  };
@@ -7776,7 +7889,7 @@ function buildCopilotFetchServiceDescriptor(logger) {
7776
7889
  },
7777
7890
  buildJwtRequestInterceptor(logger),
7778
7891
  ],
7779
- retry: buildCsrfRetryInterceptor(),
7892
+ retry: buildFetchRetryInterceptor(),
7780
7893
  finally: [buildThirdPartyTrackerFinishInterceptor()],
7781
7894
  }),
7782
7895
  tags: { specialHacksFor: 'copilot' },
@@ -7786,7 +7899,7 @@ function buildUnauthorizedFetchServiceDescriptor() {
7786
7899
  const fetchService = buildServiceDescriptor$2({
7787
7900
  createContext: createInstrumentationIdContext(),
7788
7901
  request: [buildThirdPartyTrackerRegisterInterceptor()],
7789
- retry: buildCsrfRetryInterceptor(),
7902
+ retry: buildFetchRetryInterceptor(),
7790
7903
  finally: [buildThirdPartyTrackerFinishInterceptor()],
7791
7904
  });
7792
7905
  return {
@@ -10566,7 +10679,7 @@ function getLexRuntimeDefaultInterceptorConfig(logger) {
10566
10679
  buildEntityEncodingInterceptor(),
10567
10680
  buildFirstPartyHeaderInterceptor(),
10568
10681
  ],
10569
- retry: buildCsrfRetryInterceptor(),
10682
+ retry: buildFetchRetryInterceptor(),
10570
10683
  response: [
10571
10684
  buildLexRuntime5xxStatusResponseInterceptor(logger),
10572
10685
  buildLexRuntimeSessionExpirationResponseInterceptor(logger),
@@ -10609,73 +10722,6 @@ function buildLexRuntimeCompressedFetchServiceDescriptor(logger, retryService) {
10609
10722
  };
10610
10723
  }
10611
10724
 
10612
- const DEFAULT_CONFIG = {
10613
- maxRetries: 3,
10614
- maxTimeToRetry: 10000,
10615
- baseDelay: 250,
10616
- maxDelay: 5000,
10617
- exponentialFactor: 2,
10618
- jitterPercent: 0.5,
10619
- };
10620
- class FetchThrottlingRetryPolicy extends RetryPolicy {
10621
- constructor(config = DEFAULT_CONFIG) {
10622
- super();
10623
- this.config = config;
10624
- }
10625
- async shouldRetry(result, context) {
10626
- return ((result.status === 429 || result.status === 503) &&
10627
- context.attempt < this.config.maxRetries &&
10628
- context.totalElapsedMs <= this.config.maxTimeToRetry);
10629
- }
10630
- async calculateDelay(result, context) {
10631
- let delay;
10632
- // If retry-after header is present and valid, use it
10633
- const retryAfterHeader = this.parseRetryAfterHeader(result);
10634
- if (retryAfterHeader !== undefined) {
10635
- delay = Math.min(retryAfterHeader, this.config.maxDelay);
10636
- }
10637
- else {
10638
- // Exponential backoff
10639
- delay = Math.min(this.config.baseDelay * Math.pow(this.config.exponentialFactor, context.attempt), this.config.maxDelay);
10640
- }
10641
- // Add jitter to prevent thundering herd
10642
- const jitter = delay * this.config.jitterPercent * (Math.random() - 0.5);
10643
- return Math.max(0, delay + jitter);
10644
- }
10645
- parseRetryAfterHeader(result) {
10646
- if (!result.headers) {
10647
- return undefined;
10648
- }
10649
- const value = result.headers.get('Retry-After');
10650
- if (!value) {
10651
- return undefined;
10652
- }
10653
- // The Retry-After header can be either a number of seconds or an HTTP-date (IMF-fixdate)
10654
- // Retry-After: <delay-seconds>
10655
- // Retry-After: <http-date>
10656
- const trimmed = value.trim();
10657
- // Strictly treat only all-digits as seconds
10658
- if (/^\d+$/.test(trimmed)) {
10659
- const seconds = Number(trimmed);
10660
- if (Number.isFinite(seconds) && seconds >= 0) {
10661
- return seconds * 1000; // Convert to milliseconds
10662
- }
10663
- return undefined;
10664
- }
10665
- // Strict RFC 7231 IMF-fixdate format (e.g., "Sun, 06 Nov 1994 08:49:37 GMT")
10666
- const IMF_FIXDATE = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
10667
- if (!IMF_FIXDATE.test(trimmed)) {
10668
- return undefined;
10669
- }
10670
- // Use Date.parse to avoid any engine-specific fallbacks
10671
- const millis = Date.parse(trimmed);
10672
- if (Number.isFinite(millis)) {
10673
- return Math.max(0, millis - Date.now());
10674
- }
10675
- return undefined;
10676
- }
10677
- }
10678
-
10679
10725
  const CSRF_TOKEN_KEY = 'salesforce_csrf_token';
10680
10726
  const CSRF_STORAGE_NAME = 'ldsCSRFToken';
10681
10727
  const BASE_URI = '/services/data/v68.0';
@@ -10819,6 +10865,35 @@ function buildCsrfTokenManager() {
10819
10865
  });
10820
10866
  }
10821
10867
 
10868
+ /**
10869
+ * The default policy for the runtime's shared retry service.
10870
+ *
10871
+ * Every fetch-service descriptor wired with the retry service also supplies its
10872
+ * own `retry:` interceptor, which passes a per-request policy as the override to
10873
+ * `applyRetry` — so this default is never consulted on any real path. It is
10874
+ * reachable only if a descriptor is registered with the retry service but omits
10875
+ * its `retry:` interceptor; in that case service-fetch-network falls back to
10876
+ * `retryService.applyRetry(fetch)` with no override, and this policy runs.
10877
+ *
10878
+ * That is a wiring mistake, not a runtime condition. We surface it loudly in
10879
+ * development rather than silently degrading to never-retry (the behavior of a
10880
+ * plain empty policy), following the repo convention for should-never-run paths.
10881
+ */
10882
+ class UnconfiguredRetryPolicy extends RetryPolicy {
10883
+ async shouldRetry(_result, _context) {
10884
+ // istanbul ignore else: not exercised in NODE_ENV = production for test coverage
10885
+ if (process.env.NODE_ENV !== 'production') {
10886
+ throw new Error('A fetch-service descriptor was registered with the retry service but did ' +
10887
+ 'not supply a `retry:` interceptor. Retries would silently never run — ' +
10888
+ 'add a `retry:` interceptor (e.g. buildFetchRetryInterceptor()) to the descriptor.');
10889
+ }
10890
+ return false;
10891
+ }
10892
+ async calculateDelay(_result, _context) {
10893
+ return 0;
10894
+ }
10895
+ }
10896
+
10822
10897
  /* eslint-disable no-console */
10823
10898
  /**
10824
10899
  * Default storage configuration for the durable cache
@@ -11420,11 +11495,15 @@ function initializeOneStore(luvio) {
11420
11495
  reader.unMarkMissing();
11421
11496
  return linkedData.data;
11422
11497
  });
11423
- // Compose both throttling and CSRF retry policies
11424
- const throttlingPolicy = new FetchThrottlingRetryPolicy();
11425
- const csrfPolicy = new CsrfTokenRetryPolicy();
11426
- const retryPolicy = new ComposedRetryPolicy([throttlingPolicy, csrfPolicy]);
11427
- const retryServiceDescriptor = buildServiceDescriptor$8(retryPolicy);
11498
+ // The real throttling/CSRF policies are built fresh per request in
11499
+ // buildFetchRetryInterceptor (retry-interceptors/fetch-retry.ts) so each
11500
+ // request gets its own CSRF context and sees its own HTTP method. This
11501
+ // default is never consulted by any current fetch-service descriptor (they
11502
+ // all supply their own `retry` interceptor + override); UnconfiguredRetryPolicy
11503
+ // exists so that if a future descriptor is wired with this retry service but
11504
+ // forgets its `retry:` interceptor, the silent never-retry fallback surfaces
11505
+ // loudly in development instead.
11506
+ const retryServiceDescriptor = buildServiceDescriptor$8(new UnconfiguredRetryPolicy());
11428
11507
  const retryService = retryServiceDescriptor.service;
11429
11508
  const csrfTokenManagerServiceDescriptor = buildRenewableResourceManagerDescriptor(CSRF_TOKEN_MANAGER_SERVICE, buildCsrfTokenManager());
11430
11509
  const prefetchSfapJwtServiceDescriptor = {
@@ -11500,4 +11579,4 @@ function ldsEngineCreator() {
11500
11579
  }
11501
11580
 
11502
11581
  export { LexRequestStrategy, PDL_ENGINE_REGISTRATION_ID, PdlPrefetcherEventType, PdlRequestPriority, buildPredictorForContext, configService, ldsEngineCreator as default, initializeLDS, initializeOneStore, notifyUpdateAvailableFactory, registerRequestStrategy, saveRequestAsPrediction, subscribeToPrefetcherEvents, unregisterRequestStrategy, whenPredictionsReady };
11503
- // version: 1.453.0-725e09cf25
11582
+ // version: 1.455.0-c53c97de2a
@@ -0,0 +1,20 @@
1
+ import type { FetchParameters } from '@conduit-client/service-fetch-network/v1';
2
+ /**
3
+ * Extracts the HTTP method from FetchParameters, matching the Fetch API's own
4
+ * precedence: an options.method override wins over a Request object's method
5
+ * (e.g. `fetch(new Request(url, { method: 'GET' }), { method: 'POST' })`
6
+ * resolves to `POST`).
7
+ */
8
+ export declare function getFetchMethod([urlOrRequest, options]: FetchParameters): string | undefined;
9
+ /**
10
+ * Only GET requests are safe to replay automatically on 429/503 — replaying a
11
+ * write (POST/PUT/PATCH/DELETE) risks executing a mutation more than once.
12
+ * Shared by both the Luvio and generic-fetch throttling retry policies so the
13
+ * write-guard rule lives in exactly one place.
14
+ */
15
+ export declare function isRetryableMethod(method: string | undefined): boolean;
16
+ /**
17
+ * Determines if the HTTP method requires CSRF protection.
18
+ * Only mutating operations (POST, PUT, PATCH, DELETE) require CSRF tokens.
19
+ */
20
+ export declare function isCsrfMethod(method: string | undefined): boolean;
@@ -0,0 +1,17 @@
1
+ import type { RetryInterceptor } from '@conduit-client/service-fetch-network/v1';
2
+ /**
3
+ * Builds the retry interceptor used by the generic Conduit fetch service.
4
+ *
5
+ * Composes two independent per-request retry policies — throttling (429/503,
6
+ * GET-only) and CSRF (401 token refresh) — built fresh per request rather
7
+ * than reused off a shared defaultRetryPolicy. This keeps concurrent requests
8
+ * from sharing mutable CSRF request-context state, and lets the throttling
9
+ * policy see this request's own HTTP method.
10
+ *
11
+ * The `retryService` handed in by the framework is used only as the runner for
12
+ * `applyRetry` — the real policy is always the per-request `composedPolicy`
13
+ * passed as the override, never the service's own `defaultRetryPolicy`. When no
14
+ * `retryService` is injected (e.g. the copilot/unauthorized descriptors, which
15
+ * intentionally opt out of retries), we fall through to a plain fetch.
16
+ */
17
+ export declare function buildFetchRetryInterceptor(): RetryInterceptor;
@@ -10,7 +10,8 @@ type FetchThrottlingRetryPolicyConfig = {
10
10
  };
11
11
  export declare class FetchThrottlingRetryPolicy extends RetryPolicy<Response> {
12
12
  private config;
13
- constructor(config?: FetchThrottlingRetryPolicyConfig);
13
+ private method?;
14
+ constructor(config?: FetchThrottlingRetryPolicyConfig, method?: string | undefined);
14
15
  shouldRetry(result: Response, context: RetryContext<Response>): Promise<boolean>;
15
16
  calculateDelay(result: Response, context: RetryContext<Response>): Promise<number>;
16
17
  parseRetryAfterHeader(result: Response): number | undefined;
@@ -10,7 +10,8 @@ type LuvioFetchThrottlingRetryPolicyConfig = {
10
10
  };
11
11
  export declare class LuvioFetchThrottlingRetryPolicy extends RetryPolicy<FetchResponse<any>> {
12
12
  private config;
13
- constructor(config?: LuvioFetchThrottlingRetryPolicyConfig);
13
+ private method?;
14
+ constructor(config?: LuvioFetchThrottlingRetryPolicyConfig, method?: string | undefined);
14
15
  shouldRetry(result: FetchResponse<any>, context: RetryContext<FetchResponse<any>>): Promise<boolean>;
15
16
  calculateDelay(result: FetchResponse<any>, context: RetryContext<FetchResponse<any>>): Promise<number>;
16
17
  parseRetryAfterHeader(result: FetchResponse<any>): number | undefined;
@@ -0,0 +1,20 @@
1
+ import type { RetryContext } from '@conduit-client/service-retry/v1';
2
+ import { RetryPolicy } from '@conduit-client/service-retry/v1';
3
+ /**
4
+ * The default policy for the runtime's shared retry service.
5
+ *
6
+ * Every fetch-service descriptor wired with the retry service also supplies its
7
+ * own `retry:` interceptor, which passes a per-request policy as the override to
8
+ * `applyRetry` — so this default is never consulted on any real path. It is
9
+ * reachable only if a descriptor is registered with the retry service but omits
10
+ * its `retry:` interceptor; in that case service-fetch-network falls back to
11
+ * `retryService.applyRetry(fetch)` with no override, and this policy runs.
12
+ *
13
+ * That is a wiring mistake, not a runtime condition. We surface it loudly in
14
+ * development rather than silently degrading to never-retry (the behavior of a
15
+ * plain empty policy), following the repo convention for should-never-run paths.
16
+ */
17
+ export declare class UnconfiguredRetryPolicy extends RetryPolicy<Response> {
18
+ shouldRetry(_result: Response, _context: RetryContext<Response>): Promise<boolean>;
19
+ calculateDelay(_result: Response, _context: RetryContext<Response>): Promise<number>;
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-one-app",
3
- "version": "1.453.0",
3
+ "version": "1.455.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS engine for the one.app (LEX) runtime.",
6
6
  "main": "dist/oneAppLdsEngineCreator.js",
@@ -34,61 +34,61 @@
34
34
  "release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-runtime-one-app"
35
35
  },
36
36
  "devDependencies": {
37
- "@conduit-client/service-provisioner": "3.26.1",
38
- "@conduit-client/tools-core": "3.26.1",
39
- "@salesforce/lds-adapters-apex": "^1.453.0",
40
- "@salesforce/lds-adapters-uiapi": "^1.453.0",
41
- "@salesforce/lds-ads-bridge": "^1.453.0",
42
- "@salesforce/lds-aura-storage": "^1.453.0",
43
- "@salesforce/lds-bindings": "^1.453.0",
44
- "@salesforce/lds-instrumentation": "^1.453.0",
45
- "@salesforce/lds-network-adapter": "^1.453.0",
46
- "@salesforce/lds-network-aura": "^1.453.0",
47
- "@salesforce/lds-network-fetch": "^1.453.0",
37
+ "@conduit-client/service-provisioner": "3.26.3",
38
+ "@conduit-client/tools-core": "3.26.3",
39
+ "@salesforce/lds-adapters-apex": "^1.455.0",
40
+ "@salesforce/lds-adapters-uiapi": "^1.455.0",
41
+ "@salesforce/lds-ads-bridge": "^1.455.0",
42
+ "@salesforce/lds-aura-storage": "^1.455.0",
43
+ "@salesforce/lds-bindings": "^1.455.0",
44
+ "@salesforce/lds-instrumentation": "^1.455.0",
45
+ "@salesforce/lds-network-adapter": "^1.455.0",
46
+ "@salesforce/lds-network-aura": "^1.455.0",
47
+ "@salesforce/lds-network-fetch": "^1.455.0",
48
48
  "jwt-encode": "1.0.1"
49
49
  },
50
50
  "dependencies": {
51
- "@conduit-client/command-aura-graphql-normalized-cache-control": "3.26.1",
52
- "@conduit-client/command-aura-network": "3.26.1",
53
- "@conduit-client/command-aura-normalized-cache-control": "3.26.1",
54
- "@conduit-client/command-fetch-network": "3.26.1",
55
- "@conduit-client/command-http-graphql-normalized-cache-control": "3.26.1",
56
- "@conduit-client/command-http-normalized-cache-control": "3.26.1",
57
- "@conduit-client/command-ndjson": "3.26.1",
58
- "@conduit-client/command-network": "3.26.1",
59
- "@conduit-client/command-sse": "3.26.1",
60
- "@conduit-client/command-streaming": "3.26.1",
61
- "@conduit-client/jwt-manager": "3.26.1",
62
- "@conduit-client/service-aura-network": "3.26.1",
63
- "@conduit-client/service-bindings-imperative": "3.26.1",
64
- "@conduit-client/service-bindings-lwc": "3.26.1",
65
- "@conduit-client/service-cache": "3.26.1",
66
- "@conduit-client/service-cache-control": "3.26.1",
67
- "@conduit-client/service-cache-inclusion-policy": "3.26.1",
68
- "@conduit-client/service-config": "3.26.1",
69
- "@conduit-client/service-feature-flags": "3.26.1",
70
- "@conduit-client/service-fetch-network": "3.26.1",
71
- "@conduit-client/service-instrument-command": "3.26.1",
72
- "@conduit-client/service-pubsub": "3.26.1",
73
- "@conduit-client/service-renewable-resource-manager": "3.26.1",
74
- "@conduit-client/service-store": "3.26.1",
75
- "@conduit-client/utils": "3.26.1",
76
- "@luvio/network-adapter-composable": "0.161.0",
77
- "@luvio/network-adapter-fetch": "0.161.0",
51
+ "@conduit-client/command-aura-graphql-normalized-cache-control": "3.26.3",
52
+ "@conduit-client/command-aura-network": "3.26.3",
53
+ "@conduit-client/command-aura-normalized-cache-control": "3.26.3",
54
+ "@conduit-client/command-fetch-network": "3.26.3",
55
+ "@conduit-client/command-http-graphql-normalized-cache-control": "3.26.3",
56
+ "@conduit-client/command-http-normalized-cache-control": "3.26.3",
57
+ "@conduit-client/command-ndjson": "3.26.3",
58
+ "@conduit-client/command-network": "3.26.3",
59
+ "@conduit-client/command-sse": "3.26.3",
60
+ "@conduit-client/command-streaming": "3.26.3",
61
+ "@conduit-client/jwt-manager": "3.26.3",
62
+ "@conduit-client/service-aura-network": "3.26.3",
63
+ "@conduit-client/service-bindings-imperative": "3.26.3",
64
+ "@conduit-client/service-bindings-lwc": "3.26.3",
65
+ "@conduit-client/service-cache": "3.26.3",
66
+ "@conduit-client/service-cache-control": "3.26.3",
67
+ "@conduit-client/service-cache-inclusion-policy": "3.26.3",
68
+ "@conduit-client/service-config": "3.26.3",
69
+ "@conduit-client/service-feature-flags": "3.26.3",
70
+ "@conduit-client/service-fetch-network": "3.26.3",
71
+ "@conduit-client/service-instrument-command": "3.26.3",
72
+ "@conduit-client/service-pubsub": "3.26.3",
73
+ "@conduit-client/service-renewable-resource-manager": "3.26.3",
74
+ "@conduit-client/service-store": "3.26.3",
75
+ "@conduit-client/utils": "3.26.3",
76
+ "@luvio/network-adapter-composable": "0.161.2",
77
+ "@luvio/network-adapter-fetch": "0.161.2",
78
78
  "@lwc/state": "^0.29.0",
79
- "@salesforce/lds-adapters-onestore-graphql": "^1.453.0",
79
+ "@salesforce/lds-adapters-onestore-graphql": "^1.455.0",
80
80
  "@salesforce/lds-adapters-uiapi-lex": "^1.415.0",
81
- "@salesforce/lds-durable-storage": "^1.453.0",
82
- "@salesforce/lds-luvio-service": "^1.453.0",
83
- "@salesforce/lds-luvio-uiapi-records-service": "^1.453.0"
81
+ "@salesforce/lds-durable-storage": "^1.455.0",
82
+ "@salesforce/lds-luvio-service": "^1.455.0",
83
+ "@salesforce/lds-luvio-uiapi-records-service": "^1.455.0"
84
84
  },
85
85
  "luvioBundlesize": [
86
86
  {
87
87
  "path": "./dist/oneAppLdsEngineCreator.js",
88
88
  "maxSize": {
89
- "none": "416 kB",
89
+ "none": "418 kB",
90
90
  "min": "190 kB",
91
- "compressed": "73 kB"
91
+ "compressed": "74 kB"
92
92
  }
93
93
  }
94
94
  ],
@@ -1,2 +0,0 @@
1
- import type { RetryInterceptor } from '@conduit-client/service-fetch-network/v1';
2
- export declare function buildCsrfRetryInterceptor(): RetryInterceptor;