@salesforce/lds-runtime-aura 1.435.1 → 1.437.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.
@@ -41,8 +41,10 @@ import lightningConnectEnabled from '@salesforce/gate/ui.services.LightningConne
41
41
  import bypassAppRestrictionEnabled from '@salesforce/gate/ui.services.LightningConnect.BypassAppRestriction.enabled';
42
42
  import csrfValidationEnabled from '@salesforce/gate/ui.services.LightningConnect.CsrfValidation.enabled';
43
43
  import sessionApiEnabled from '@salesforce/gate/ui.uisdk.session.api.enabled';
44
- import useHttpUiapiOneApp from '@salesforce/gate/lds.useHttpUiapiOneApp';
45
- import useHttpUiapiOneRuntime from '@salesforce/gate/lds.useHttpUiapiOneRuntime';
44
+ import useHttpUiapiOneAppPublic from '@salesforce/gate/lds.useHttpUiapiOneAppPublic';
45
+ import useHttpUiapiOneAppPrivate from '@salesforce/gate/lds.useHttpUiapiOneAppPrivate';
46
+ import useHttpUiapiOneRuntimePublic from '@salesforce/gate/lds.useHttpUiapiOneRuntimePublic';
47
+ import useHttpUiapiOneRuntimePrivate from '@salesforce/gate/lds.useHttpUiapiOneRuntimePrivate';
46
48
  import disableCreateContentDocumentAndVersionHTTPLexRuntime from '@salesforce/gate/lds.lex.http.disableCreateContentDocumentAndVersion';
47
49
  import useHotspotLimit from '@salesforce/gate/lds.pdl.useHotspotLimit';
48
50
  import { registerSubRequestNetworkAdapter } from 'force/ldsNetwork';
@@ -2760,7 +2762,7 @@ function buildServiceDescriptor$d(luvio) {
2760
2762
  },
2761
2763
  };
2762
2764
  }
2763
- // version: 1.435.1-4492aa27df
2765
+ // version: 1.437.0-90398d3223
2764
2766
 
2765
2767
  /*!
2766
2768
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -3113,7 +3115,7 @@ function buildServiceDescriptor$9(notifyRecordUpdateAvailable, getNormalizedLuvi
3113
3115
  },
3114
3116
  };
3115
3117
  }
3116
- // version: 1.435.1-4492aa27df
3118
+ // version: 1.437.0-90398d3223
3117
3119
 
3118
3120
  /*!
3119
3121
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -4623,7 +4625,9 @@ var HttpStatusCode;
4623
4625
  HttpStatusCode[HttpStatusCode["Unauthorized"] = 401] = "Unauthorized";
4624
4626
  HttpStatusCode[HttpStatusCode["Forbidden"] = 403] = "Forbidden";
4625
4627
  HttpStatusCode[HttpStatusCode["NotFound"] = 404] = "NotFound";
4628
+ HttpStatusCode[HttpStatusCode["TooManyRequests"] = 429] = "TooManyRequests";
4626
4629
  HttpStatusCode[HttpStatusCode["ServerError"] = 500] = "ServerError";
4630
+ HttpStatusCode[HttpStatusCode["ServiceUnavailable"] = 503] = "ServiceUnavailable";
4627
4631
  HttpStatusCode[HttpStatusCode["GatewayTimeout"] = 504] = "GatewayTimeout";
4628
4632
  })(HttpStatusCode || (HttpStatusCode = {}));
4629
4633
  /**
@@ -4686,7 +4690,7 @@ var TypeCheckShapes;
4686
4690
  TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
4687
4691
  TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
4688
4692
  })(TypeCheckShapes || (TypeCheckShapes = {}));
4689
- // engine version: 0.160.4-b7e0ea82
4693
+ // engine version: 0.160.5-e6ada846
4690
4694
 
4691
4695
  const { keys: keys$1 } = Object;
4692
4696
 
@@ -5743,7 +5747,7 @@ function getEnvironmentSetting(name) {
5743
5747
  }
5744
5748
  return undefined;
5745
5749
  }
5746
- // version: 1.435.1-a9f05717b8
5750
+ // version: 1.437.0-f680421dc4
5747
5751
 
5748
5752
  const environmentHasAura = typeof window !== 'undefined' && typeof window.$A !== 'undefined';
5749
5753
  const defaultConfig = {
@@ -6210,6 +6214,7 @@ const CSRF_STORAGE_CONFIG = {
6210
6214
  class CsrfTokenManager {
6211
6215
  constructor() {
6212
6216
  this.tokenPromise = null;
6217
+ this.refreshInFlight = null;
6213
6218
  // Initialize AuraStorage
6214
6219
  this.storage = createStorage(CSRF_STORAGE_CONFIG);
6215
6220
  }
@@ -6285,20 +6290,37 @@ class CsrfTokenManager {
6285
6290
  /**
6286
6291
  * Obtains and returns a new token value as a promise.
6287
6292
  * This will clear the cached token and fetch a fresh one.
6293
+ *
6294
+ * Concurrent calls coalesce onto a single in-flight refresh — important when
6295
+ * multiple requests fail with INVALID_ACCESS_TOKEN simultaneously and each
6296
+ * retry policy independently calls refreshToken(). Without this, every caller
6297
+ * triggers its own /session/csrf round-trip.
6288
6298
  */
6289
- async refreshToken() {
6290
- // Clear cached token
6291
- if (this.storage) {
6292
- try {
6293
- await this.storage.remove(CSRF_TOKEN_KEY);
6299
+ refreshToken() {
6300
+ if (this.refreshInFlight) {
6301
+ return this.refreshInFlight;
6302
+ }
6303
+ const refresh = (async () => {
6304
+ if (this.storage) {
6305
+ try {
6306
+ await this.storage.remove(CSRF_TOKEN_KEY);
6307
+ }
6308
+ catch {
6309
+ // Non-fatal: continue with refresh even if clear fails
6310
+ }
6294
6311
  }
6295
- catch {
6296
- // Non-fatal: continue with refresh even if clear fails
6312
+ return this.fetchFreshToken();
6313
+ })();
6314
+ this.refreshInFlight = refresh;
6315
+ this.tokenPromise = refresh;
6316
+ // Clear the in-flight reference once settled (success or failure) so
6317
+ // subsequent token expirations can trigger a fresh refresh.
6318
+ refresh.finally(() => {
6319
+ if (this.refreshInFlight === refresh) {
6320
+ this.refreshInFlight = null;
6297
6321
  }
6298
- }
6299
- // Fetch (and cache) fresh token
6300
- this.tokenPromise = this.fetchFreshToken();
6301
- return this.tokenPromise;
6322
+ });
6323
+ return refresh;
6302
6324
  }
6303
6325
  /**
6304
6326
  * Reset the singleton instance (useful for testing).
@@ -6633,6 +6655,147 @@ function buildLuvioThirdPartyTrackerFinishInterceptor() {
6633
6655
  };
6634
6656
  }
6635
6657
 
6658
+ const DEFAULT_CONFIG$3 = {
6659
+ maxRetries: 1,
6660
+ };
6661
+ class LuvioCsrfTokenRetryPolicy extends RetryPolicy {
6662
+ constructor(config = DEFAULT_CONFIG$3) {
6663
+ super();
6664
+ this.config = config;
6665
+ this.csrfTokenManager = CsrfTokenManager.getInstance();
6666
+ }
6667
+ setRequestContext(context) {
6668
+ this.requestContext = context;
6669
+ }
6670
+ async shouldRetry(result, context) {
6671
+ if (context.attempt >= this.config.maxRetries) {
6672
+ return false;
6673
+ }
6674
+ // UIAPI returns 401 for invalid/expired CSRF tokens; the default is 400
6675
+ if (result.status !== 400 && result.status !== 401) {
6676
+ return false;
6677
+ }
6678
+ return isCsrfError$1(result);
6679
+ }
6680
+ async calculateDelay(_result, _context) {
6681
+ return 0;
6682
+ }
6683
+ async prepareRetry(_result, _context) {
6684
+ if (!this.requestContext) {
6685
+ return;
6686
+ }
6687
+ const newToken = await this.csrfTokenManager.refreshToken();
6688
+ const req = this.requestContext.request;
6689
+ const { [CSRF_TOKEN_HEADER]: _stale, ...remainingHeaders } = req.headers ?? {};
6690
+ // If refresh failed, drop the stale token entirely so the request interceptor
6691
+ // will fetch a fresh one via getToken() on the retry.
6692
+ const headers = newToken
6693
+ ? { ...remainingHeaders, [CSRF_TOKEN_HEADER]: newToken }
6694
+ : remainingHeaders;
6695
+ this.requestContext.request = { ...req, headers };
6696
+ }
6697
+ }
6698
+ /**
6699
+ * Returns true when a Luvio FetchResponse indicates a CSRF token error.
6700
+ * Body is already parsed JSON on FetchResponse, so no clone/json() needed.
6701
+ *
6702
+ * UIAPI error bodies come in both shapes: an object `{ errorCode, message }`
6703
+ * for single errors, or an array `[{ errorCode, message }]` for endpoints that
6704
+ * return collections of errors. Handle both.
6705
+ */
6706
+ function isCsrfError$1(response) {
6707
+ const body = response.body;
6708
+ const errorCode = Array.isArray(body) ? body[0]?.errorCode : body?.errorCode;
6709
+ return errorCode === 'INVALID_ACCESS_TOKEN';
6710
+ }
6711
+
6712
+ const DEFAULT_CONFIG$2 = {
6713
+ maxRetries: 3,
6714
+ maxTimeToRetry: 10000,
6715
+ baseDelay: 250,
6716
+ maxDelay: 5000,
6717
+ exponentialFactor: 2,
6718
+ jitterPercent: 0.5,
6719
+ };
6720
+ class LuvioFetchThrottlingRetryPolicy extends RetryPolicy {
6721
+ constructor(config = DEFAULT_CONFIG$2) {
6722
+ super();
6723
+ this.config = config;
6724
+ }
6725
+ async shouldRetry(result, context) {
6726
+ return ((result.status === 429 || result.status === 503) &&
6727
+ context.attempt < this.config.maxRetries &&
6728
+ context.totalElapsedMs <= this.config.maxTimeToRetry);
6729
+ }
6730
+ async calculateDelay(result, context) {
6731
+ let delay;
6732
+ const retryAfterHeader = this.parseRetryAfterHeader(result);
6733
+ if (retryAfterHeader !== undefined) {
6734
+ delay = Math.min(retryAfterHeader, this.config.maxDelay);
6735
+ }
6736
+ else {
6737
+ delay = Math.min(this.config.baseDelay * Math.pow(this.config.exponentialFactor, context.attempt), this.config.maxDelay);
6738
+ }
6739
+ const jitter = delay * this.config.jitterPercent * (Math.random() - 0.5);
6740
+ return Math.max(0, delay + jitter);
6741
+ }
6742
+ parseRetryAfterHeader(result) {
6743
+ const headers = result.headers;
6744
+ if (!headers) {
6745
+ return undefined;
6746
+ }
6747
+ // FetchResponse headers is a plain { [key: string]: string } object
6748
+ // (no Headers.get available), so do a case-insensitive lookup manually.
6749
+ let value;
6750
+ for (const key in headers) {
6751
+ if (key.toLowerCase() === 'retry-after') {
6752
+ value = headers[key];
6753
+ break;
6754
+ }
6755
+ }
6756
+ if (!value) {
6757
+ return undefined;
6758
+ }
6759
+ const trimmed = value.trim();
6760
+ if (/^\d+$/.test(trimmed)) {
6761
+ const seconds = Number(trimmed);
6762
+ if (Number.isFinite(seconds) && seconds >= 0) {
6763
+ return seconds * 1000;
6764
+ }
6765
+ return undefined;
6766
+ }
6767
+ 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$/;
6768
+ if (!IMF_FIXDATE.test(trimmed)) {
6769
+ return undefined;
6770
+ }
6771
+ const millis = Date.parse(trimmed);
6772
+ if (Number.isFinite(millis)) {
6773
+ return Math.max(0, millis - Date.now());
6774
+ }
6775
+ return undefined;
6776
+ }
6777
+ }
6778
+
6779
+ /**
6780
+ * Builds the LuvioRetryInterceptor used by the UIAPI fetch adapter.
6781
+ *
6782
+ * A fresh RetryService and policy instances are created per request to keep the
6783
+ * CSRF policy's mutableRequest context isolated from concurrent requests.
6784
+ */
6785
+ function buildLuvioFetchRetryInterceptor() {
6786
+ return (request, doFetch) => {
6787
+ const csrfPolicy = new LuvioCsrfTokenRetryPolicy();
6788
+ const composedPolicy = new ComposedRetryPolicy([
6789
+ new LuvioFetchThrottlingRetryPolicy(),
6790
+ csrfPolicy,
6791
+ ]);
6792
+ const retryService = new RetryService(composedPolicy);
6793
+ const mutableRequest = { request };
6794
+ csrfPolicy.setRequestContext(mutableRequest);
6795
+ return retryService.applyRetry(() => doFetch(mutableRequest.request));
6796
+ };
6797
+ }
6798
+
6636
6799
  const SFDC_REQUEST_ID_KEY = 'X-SFDC-Request-Id';
6637
6800
  function buildTransportMarksSendInterceptor() {
6638
6801
  return async (fetchArgs, context) => {
@@ -6738,31 +6901,187 @@ function isOneRuntime() {
6738
6901
  return !!globalThis.LWR;
6739
6902
  }
6740
6903
  /**
6741
- * Base set of url paths that this adapter can serve via HTTP when enabled.
6742
- * Extend this list to support additional endpoints (see predicate wiring below).
6904
+ * Public UIAPI url paths verified for HTTP transport.
6905
+ *
6906
+ * Inclusion criteria:
6907
+ * 1. The path's underlying Connect resource does NOT carry
6908
+ * `clientFilters=@ConnectClientFilters({@ConnectClientFilter(key = "UiTier")...})`.
6909
+ * 2. LDS actually invokes this URL via a Luvio adapter wired into
6910
+ * `ldsEngineCreator.js` (i.e. there is a corresponding `*AdapterFactory`
6911
+ * import). Connect resources with no LDS adapter are intentionally
6912
+ * excluded — adding them produces no traffic and just bloats the matcher.
6743
6913
  *
6744
- * Examples:
6745
- * - Add another endpoint pattern:
6746
- * "/ui-api/records/{recordId}"
6747
- * - Keep placeholders for variable segments wrapped in "{}".
6914
+ * Where a single Connect resource exposes multiple URL shapes (e.g. a 4-segment
6915
+ * GET form alongside a 3-segment POST form), all forms LDS adapters can hit at
6916
+ * runtime are registered.
6748
6917
  */
6749
- const UIAPI_PATHS = [
6750
- // getObjectInfo
6918
+ const UIAPI_PUBLIC_PATHS = [
6919
+ // ----- Object Info -----
6920
+ // getObjectInfo — IObjectInfoResource
6751
6921
  '/ui-api/object-info/{objectApiName}',
6752
- // getObjectInfos
6922
+ // getObjectInfos (batch) — IObjectInfoBatchResource
6753
6923
  '/ui-api/object-info/batch/{objectApiNames}',
6754
- // getPicklistValuesByRecordType
6924
+ // getPicklistValues — IPicklistValuesResource
6925
+ '/ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}/{fieldApiName}',
6926
+ // getPicklistValuesByRecordType — IPicklistValuesByRecordTypeResource
6755
6927
  '/ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}',
6756
- // getDuplicates
6757
- '/ui-api/predupe',
6758
- // getRecord
6928
+ // ----- Records -----
6929
+ // createRecord (POST) — IRecordDataCollectionResource
6930
+ '/ui-api/records',
6931
+ // getRecord / updateRecord (PATCH) / deleteRecord (DELETE) — IRecordDataResource
6759
6932
  '/ui-api/records/{recordId}',
6760
- // getRecordUi
6933
+ // getRecords (batch GET) — IBatchRecordDataResource
6934
+ '/ui-api/records/batch/{recordIds}',
6935
+ // executeBatchRecordOperations (POST, no recordIds segment) — IBatchRecordResource
6936
+ '/ui-api/records/batch',
6937
+ // getRecordUi — IRecordUiResource
6761
6938
  '/ui-api/record-ui/{recordIds}',
6762
- // getRelatedListInfo
6939
+ // ----- Layout -----
6940
+ // getLayout — ILayoutResource
6941
+ '/ui-api/layout/{objectApiName}',
6942
+ // getPathLayout — IPathLayoutResource
6943
+ '/ui-api/path/layout/{objectApiName}',
6944
+ // ----- Record Defaults -----
6945
+ // getRecordCreateDefaults — IRecordCreateDefaultsResource
6946
+ '/ui-api/record-defaults/create/{objectApiName}',
6947
+ // getRecordDefaultsTemplateForCreate — IRecordDefaultsTemplateCreateResource
6948
+ '/ui-api/record-defaults/template/create/{objectApiName}',
6949
+ // getRecordDefaultsTemplateClone — IRecordDefaultsTemplateCloneResource
6950
+ '/ui-api/record-defaults/template/clone/{recordId}',
6951
+ // ----- Duplicates -----
6952
+ // getDuplicateConfig — IDuplicateConfigResource
6953
+ '/ui-api/duplicates/{objectApiName}',
6954
+ // findDuplicates (POST) — IPredupeResource
6955
+ '/ui-api/predupe',
6956
+ // ----- GraphQL -----
6957
+ // executeGraphQL (POST) — IGraphQLResource (no /ui-api prefix)
6958
+ '/graphql',
6959
+ // ----- List UI -----
6960
+ // getListUiByName (2-segment form) — IListUiResource212
6961
+ '/ui-api/list-ui/{objectApiName}/{listViewApiName}',
6962
+ // 1-segment form covers both:
6963
+ // /ui-api/list-ui/{listViewId} — IListUiResource
6964
+ // /ui-api/list-ui/{objectApiName} — IListCollectionResource
6965
+ '/ui-api/list-ui/{objectApiNameOrListViewId}',
6966
+ // getListInfoByName (GET) / updateListInfoByApiName (PATCH) / deleteListInfo (DELETE) — IListInfoResource
6967
+ // (Also covers /ui-api/list-info/{listViewId} via shape collision.)
6968
+ '/ui-api/list-info/{objectApiName}/{listViewApiName}',
6969
+ // getListInfosByObjectName (GET) / createListInfo (POST) — IListInfoCreationAndCollectionResource
6970
+ // (Also covers /ui-api/list-info/batch — IListInfoBatchResource — via shape collision.)
6971
+ '/ui-api/list-info/{objectApiName}',
6972
+ // getListObjectInfo — IListObjectInfoResource
6973
+ '/ui-api/list-object-info/{objectApiName}',
6974
+ // postListRecordsByName (POST) — IListRecordsResource
6975
+ '/ui-api/list-records/{objectApiName}/{listViewApiName}',
6976
+ // getListPreferences (GET) / updateListPreferences (PATCH) — IListPreferencesResource
6977
+ '/ui-api/list-preferences/{objectApiName}/{listViewApiName}',
6978
+ // ----- Related List UI -----
6979
+ // getRelatedListInfo (GET) / updateRelatedListInfo (PATCH) — IRelatedListInfoResource
6980
+ // (Also covers /ui-api/related-list-info/{parentRecordId}/{relatedListId} via shape collision.)
6763
6981
  '/ui-api/related-list-info/{parentObjectApiName}/{relatedListId}',
6764
- // getRelatedListRecords
6982
+ // getRelatedListInfoBatch — IRelatedListInfoBatchResource
6983
+ '/ui-api/related-list-info/batch/{parentObjectApiName}/{relatedListNames}',
6984
+ // getRelatedListsInfo (collection) — IRelatedListInfoCollectionResource
6985
+ '/ui-api/related-list-info/{parentObjectApiName}',
6986
+ // postRelatedListRecords (POST) — IRelatedListRecordsResource
6765
6987
  '/ui-api/related-list-records/{parentRecordId}/{relatedListId}',
6988
+ // postRelatedListRecordsBatch (POST, 3-segment form) — IRelatedListRecordsBatchResource
6989
+ '/ui-api/related-list-records/batch/{parentRecordId}',
6990
+ // getRelatedListRecordsBatch (GET, 4-segment form) — IRelatedListRecordsBatchResource [newly added]
6991
+ '/ui-api/related-list-records/batch/{parentRecordId}/{relatedListIds}',
6992
+ // getRelatedListCount — IRelatedListRecordCountResource
6993
+ '/ui-api/related-list-count/{parentRecordId}/{relatedListId}',
6994
+ // getRelatedListsCount (batch) — IRelatedListRecordCountBatchResource
6995
+ '/ui-api/related-list-count/batch/{parentRecordId}/{relatedListNames}',
6996
+ // getRelatedListPreferences (GET) / updateRelatedListPreferences (PATCH) — IRelatedListPreferencesResource
6997
+ '/ui-api/related-list-preferences/{preferencesId}',
6998
+ // getRelatedListPreferencesBatch — IRelatedListPreferencesBatchResource
6999
+ '/ui-api/related-list-preferences/batch/{preferencesIds}',
7000
+ // ----- Actions -----
7001
+ // getGlobalActions — IActionGlobalResource
7002
+ '/ui-api/actions/global',
7003
+ // getActionLayout — IActionLayoutResource
7004
+ '/ui-api/actions/layout/{actionApiName}',
7005
+ // getLookupActions — IActionLookupResource
7006
+ '/ui-api/actions/lookup/{objectApiNames}',
7007
+ // getObjectCreateActions — IActionObjectResource
7008
+ '/ui-api/actions/object/{objectApiName}/record-create',
7009
+ // getActionOverrides — IActionOverrideResource
7010
+ '/ui-api/actions/overrides/{objectApiName}',
7011
+ // performQuickAction (POST) / performUpdateRecordQuickAction (PATCH) — IActionPerformQuickActionResource
7012
+ '/ui-api/actions/perform-quick-action/{actionApiName}',
7013
+ // getQuickActionInfo — IActionQuickActionInfoResource
7014
+ '/ui-api/actions/quick-action-info/{actionApiName}',
7015
+ // getRecordActions — IActionRecordResource
7016
+ '/ui-api/actions/record/{recordIds}',
7017
+ // getRecordEditActions — IActionRecordEditResource
7018
+ '/ui-api/actions/record/{recordIds}/record-edit',
7019
+ // getAllRelatedListActionsForRecord (4-segment form) — IActionRelatedListResource [newly added]
7020
+ '/ui-api/actions/record/{recordIds}/related-list',
7021
+ // postRelatedListActions (POST) — IActionRelatedListResource
7022
+ '/ui-api/actions/record/{recordIds}/related-list/{relatedListId}',
7023
+ // postRelatedListsActions (POST) / getRelatedListsActions (GET, 5-segment form) — IActionRelatedListBatchResource
7024
+ '/ui-api/actions/record/{recordIds}/related-list/batch',
7025
+ // getRelatedListsActions (GET, 6-segment form) — IActionRelatedListBatchResource [newly added]
7026
+ '/ui-api/actions/record/{recordIds}/related-list/batch/{relatedListIds}',
7027
+ // getRelatedListRecordActions — IActionRelatedListRecordResource
7028
+ '/ui-api/actions/record/{recordIds}/related-list-record/{relatedListRecordIds}',
7029
+ // getQuickActionDefaults — IActionQuickActionDefaultsResource
7030
+ '/ui-api/actions/record-defaults/{actionApiName}',
7031
+ // getFlexipageFormulaOverrides — IActionFlexipageFormulaActivationResource
7032
+ '/ui-api/actions/formula-activation/{actionFeature}',
7033
+ // ----- Lookup / Search -----
7034
+ // lookup (POST) — ILookupEntityResource
7035
+ '/ui-api/lookups/{objectApiName}/{keyPrefix}/{targetApiName}',
7036
+ // searchResults (POST) — ISearchResultsResource
7037
+ '/ui-api/search/results',
7038
+ // searchKeywordResults (POST) — IKeywordSearchResultsResource
7039
+ '/ui-api/search/results/keyword',
7040
+ // getSearchFilterMetadata — ISearchInfoFilterMetadataCollectionResource
7041
+ '/ui-api/search-info/{objectApiName}/filters',
7042
+ // getSearchFilterOptions — ISearchInfoFilterOptionsResource
7043
+ '/ui-api/search-info/{objectApiName}/filters/{filterApiName}/options',
7044
+ // getLookupMetadata — ILookupMetadataResource
7045
+ '/ui-api/search-info/{objectApiName}/lookup/{fieldApiName}',
7046
+ // ----- MRU Lists -----
7047
+ // getMruListUi — IMruListUiResource
7048
+ '/ui-api/mru-list-ui/{objectApiName}',
7049
+ // getMruListRecords — IMruListRecordsResource
7050
+ '/ui-api/mru-list-records/{objectApiName}',
7051
+ // ----- Apps / Nav -----
7052
+ // getNavItems — INavItemsResource
7053
+ '/ui-api/nav-items',
7054
+ // getAllApps — IAppsResource
7055
+ '/ui-api/apps',
7056
+ // getAppDetails — IAppResource
7057
+ '/ui-api/apps/{appId}',
7058
+ ];
7059
+ /**
7060
+ * Private UIAPI url paths gated by the private-endpoint gates per runtime.
7061
+ *
7062
+ * Inclusion criteria:
7063
+ * 1. The path's underlying Connect resource carries
7064
+ * `clientFilters=@ConnectClientFilters({@ConnectClientFilter(key = "UiTier")...})`,
7065
+ * which restricts callers to the UI tier (one.app / UISDK runtime) and
7066
+ * excludes the public REST API.
7067
+ * 2. LDS actually invokes this URL via a Luvio adapter wired into
7068
+ * `ldsEngineCreator.js`.
7069
+ */
7070
+ const UIAPI_PRIVATE_PATHS = [
7071
+ // ----- Record Avatars -----
7072
+ // getRecordAvatars (batch) — IRecordAvatarsBatchResource
7073
+ '/ui-api/record-avatars/batch/{recordIds}',
7074
+ // updateRecordAvatar (POST) — IRecordAvatarsAssociationResource
7075
+ '/ui-api/record-avatars/{recordId}/association',
7076
+ // ----- Layout -----
7077
+ // getLayoutUserState (GET) / updateLayoutUserState (PATCH) — ILayoutUserStateResource
7078
+ '/ui-api/layout/{objectApiName}/user-state',
7079
+ // ----- GraphQL -----
7080
+ // executeGraphQLBatch (POST) — IGraphQLBatchResource (no /ui-api prefix)
7081
+ '/graphql/batch',
7082
+ // ----- Aggregate UI -----
7083
+ // executeAggregateUi (POST) — IAggregateUiResource
7084
+ '/ui-api/aggregate-ui',
6766
7085
  ];
6767
7086
  /**
6768
7087
  * Single content documents version URL enabled/disabled by killswitch.
@@ -6784,15 +7103,25 @@ const CONTENT_DOCUMENTS_VERSIONS_PATH = '/ui-api/records/content-documents/conte
6784
7103
  * }
6785
7104
  */
6786
7105
  const PREDICATE_PATH_SETS = [
6787
- // One Runtime gate
7106
+ // One Runtime — public UIAPI endpoints
7107
+ {
7108
+ predicate: () => isOneRuntime() && useHttpUiapiOneRuntimePublic.isOpen({ fallback: false }),
7109
+ paths: UIAPI_PUBLIC_PATHS,
7110
+ },
7111
+ // One Runtime — private UIAPI endpoints
7112
+ {
7113
+ predicate: () => isOneRuntime() && useHttpUiapiOneRuntimePrivate.isOpen({ fallback: false }),
7114
+ paths: UIAPI_PRIVATE_PATHS,
7115
+ },
7116
+ // one.app — public UIAPI endpoints
6788
7117
  {
6789
- predicate: () => isOneRuntime() && useHttpUiapiOneRuntime.isOpen({ fallback: false }),
6790
- paths: UIAPI_PATHS,
7118
+ predicate: () => !isOneRuntime() && useHttpUiapiOneAppPublic.isOpen({ fallback: false }),
7119
+ paths: UIAPI_PUBLIC_PATHS,
6791
7120
  },
6792
- // one.app gate
7121
+ // one.app — private UIAPI endpoints
6793
7122
  {
6794
- predicate: () => !isOneRuntime() && useHttpUiapiOneApp.isOpen({ fallback: false }),
6795
- paths: UIAPI_PATHS,
7123
+ predicate: () => !isOneRuntime() && useHttpUiapiOneAppPrivate.isOpen({ fallback: false }),
7124
+ paths: UIAPI_PRIVATE_PATHS,
6796
7125
  },
6797
7126
  // disableCreateContentDocumentAndVersionHTTPLexRuntime killswitch
6798
7127
  {
@@ -6865,6 +7194,7 @@ const composedFetchNetworkAdapter = {
6865
7194
  buildLuvioPageScopedCacheRequestInterceptor(),
6866
7195
  buildLuvioCsrfTokenInterceptor(),
6867
7196
  ],
7197
+ retry: buildLuvioFetchRetryInterceptor(),
6868
7198
  response: [
6869
7199
  buildLexRuntimeLuvio5xxStatusResponseInterceptor(),
6870
7200
  buildLexRuntimeLuvioAuthExpirationRedirectResponseInterceptor(),
@@ -6931,12 +7261,12 @@ class CsrfTokenRetryPolicy extends RetryPolicy {
6931
7261
  if (context.attempt >= this.config.maxRetries) {
6932
7262
  return false;
6933
7263
  }
6934
- // Only retry on 400 status
6935
- if (result.status !== 400) {
7264
+ // UIAPI returns 401 for invalid/expired CSRF tokens; the default is 400.
7265
+ if (result.status !== 400 && result.status !== 401) {
6936
7266
  return false;
6937
7267
  }
6938
7268
  // Check if this is a CSRF error by examining the response body
6939
- // This avoids retrying all 400s (validation errors, bad requests, etc.)
7269
+ // This avoids retrying all 400s/401s (validation errors, auth errors, etc.)
6940
7270
  return isCsrfError(result);
6941
7271
  }
6942
7272
  /**
@@ -6985,8 +7315,10 @@ async function isCsrfError(response) {
6985
7315
  // Clone to avoid consuming the original response
6986
7316
  const cloned = response.clone();
6987
7317
  const body = await cloned.json();
6988
- // Check the error array format: body[0].errorCode
6989
- const errorCode = body?.[0]?.errorCode;
7318
+ // UIAPI error bodies come in both shapes: an object `{ errorCode, message }`
7319
+ // for single errors, or an array `[{ errorCode, message }]` for endpoints
7320
+ // that return collections of errors. Handle both.
7321
+ const errorCode = Array.isArray(body) ? body[0]?.errorCode : body?.errorCode;
6990
7322
  return errorCode === 'INVALID_ACCESS_TOKEN';
6991
7323
  }
6992
7324
  catch {
@@ -10585,4 +10917,4 @@ function ldsEngineCreator() {
10585
10917
  }
10586
10918
 
10587
10919
  export { LexRequestStrategy, PdlPrefetcherEventType, PdlRequestPriority, buildPredictorForContext, configService, ldsEngineCreator as default, initializeLDS, initializeOneStore, notifyUpdateAvailableFactory, registerRequestStrategy, saveRequestAsPrediction, subscribeToPrefetcherEvents, unregisterRequestStrategy, whenPredictionsReady };
10588
- // version: 1.435.1-4492aa27df
10920
+ // version: 1.437.0-90398d3223
@@ -2,15 +2,33 @@ import { type ResourceRequest } from '@luvio/engine';
2
2
  import type { RequestLogger } from '@salesforce/lds-network-fetch';
3
3
  import type { RequestTracker } from './instrumentation-utils';
4
4
  /**
5
- * Base set of url paths that this adapter can serve via HTTP when enabled.
6
- * Extend this list to support additional endpoints (see predicate wiring below).
5
+ * Public UIAPI url paths verified for HTTP transport.
7
6
  *
8
- * Examples:
9
- * - Add another endpoint pattern:
10
- * "/ui-api/records/{recordId}"
11
- * - Keep placeholders for variable segments wrapped in "{}".
7
+ * Inclusion criteria:
8
+ * 1. The path's underlying Connect resource does NOT carry
9
+ * `clientFilters=@ConnectClientFilters({@ConnectClientFilter(key = "UiTier")...})`.
10
+ * 2. LDS actually invokes this URL via a Luvio adapter wired into
11
+ * `ldsEngineCreator.js` (i.e. there is a corresponding `*AdapterFactory`
12
+ * import). Connect resources with no LDS adapter are intentionally
13
+ * excluded — adding them produces no traffic and just bloats the matcher.
14
+ *
15
+ * Where a single Connect resource exposes multiple URL shapes (e.g. a 4-segment
16
+ * GET form alongside a 3-segment POST form), all forms LDS adapters can hit at
17
+ * runtime are registered.
18
+ */
19
+ export declare const UIAPI_PUBLIC_PATHS: string[];
20
+ /**
21
+ * Private UIAPI url paths gated by the private-endpoint gates per runtime.
22
+ *
23
+ * Inclusion criteria:
24
+ * 1. The path's underlying Connect resource carries
25
+ * `clientFilters=@ConnectClientFilters({@ConnectClientFilter(key = "UiTier")...})`,
26
+ * which restricts callers to the UI tier (one.app / UISDK runtime) and
27
+ * excludes the public REST API.
28
+ * 2. LDS actually invokes this URL via a Luvio adapter wired into
29
+ * `ldsEngineCreator.js`.
12
30
  */
13
- export declare const UIAPI_PATHS: string[];
31
+ export declare const UIAPI_PRIVATE_PATHS: string[];
14
32
  /**
15
33
  * Single content documents version URL enabled/disabled by killswitch.
16
34
  * Exported for reuse in tests.
@@ -5,6 +5,7 @@
5
5
  declare class CsrfTokenManager {
6
6
  private static instance;
7
7
  private tokenPromise;
8
+ private refreshInFlight;
8
9
  private storage;
9
10
  private constructor();
10
11
  static getInstance(): CsrfTokenManager;
@@ -28,6 +29,11 @@ declare class CsrfTokenManager {
28
29
  /**
29
30
  * Obtains and returns a new token value as a promise.
30
31
  * This will clear the cached token and fetch a fresh one.
32
+ *
33
+ * Concurrent calls coalesce onto a single in-flight refresh — important when
34
+ * multiple requests fail with INVALID_ACCESS_TOKEN simultaneously and each
35
+ * retry policy independently calls refreshToken(). Without this, every caller
36
+ * triggers its own /session/csrf round-trip.
31
37
  */
32
38
  refreshToken(): Promise<string | undefined>;
33
39
  /**
@@ -1,5 +1,6 @@
1
1
  import type { RequestInterceptor } from '@conduit-client/service-fetch-network/v1';
2
2
  import type { ResourceRequest } from '@luvio/engine';
3
+ export declare const CSRF_TOKEN_HEADER = "X-CSRF-Token";
3
4
  /**
4
5
  * Builds a request interceptor that adds CSRF token headers to mutating requests.
5
6
  * The CSRF token is fetched once and cached for subsequent requests.
@@ -0,0 +1,8 @@
1
+ import type { LuvioRetryInterceptor } from '@salesforce/lds-network-fetch';
2
+ /**
3
+ * Builds the LuvioRetryInterceptor used by the UIAPI fetch adapter.
4
+ *
5
+ * A fresh RetryService and policy instances are created per request to keep the
6
+ * CSRF policy's mutableRequest context isolated from concurrent requests.
7
+ */
8
+ export declare function buildLuvioFetchRetryInterceptor(): LuvioRetryInterceptor;
@@ -0,0 +1,32 @@
1
+ import type { RetryContext } from '@conduit-client/service-retry/v1';
2
+ import { RetryPolicy } from '@conduit-client/service-retry/v1';
3
+ import type { FetchResponse, ResourceRequest } from '@luvio/engine';
4
+ type LuvioCsrfTokenRetryPolicyConfig = {
5
+ maxRetries: number;
6
+ };
7
+ /**
8
+ * Mutable container for the ResourceRequest that can be updated between retries.
9
+ */
10
+ export interface MutableLuvioRequest {
11
+ request: ResourceRequest;
12
+ }
13
+ export declare class LuvioCsrfTokenRetryPolicy extends RetryPolicy<FetchResponse<any>> {
14
+ private config;
15
+ private csrfTokenManager;
16
+ private requestContext?;
17
+ constructor(config?: LuvioCsrfTokenRetryPolicyConfig);
18
+ setRequestContext(context: MutableLuvioRequest): void;
19
+ shouldRetry(result: FetchResponse<any>, context: RetryContext<FetchResponse<any>>): Promise<boolean>;
20
+ calculateDelay(_result: FetchResponse<any>, _context: RetryContext<FetchResponse<any>>): Promise<number>;
21
+ prepareRetry(_result: FetchResponse<any>, _context: RetryContext<FetchResponse<any>>): Promise<void>;
22
+ }
23
+ /**
24
+ * Returns true when a Luvio FetchResponse indicates a CSRF token error.
25
+ * Body is already parsed JSON on FetchResponse, so no clone/json() needed.
26
+ *
27
+ * UIAPI error bodies come in both shapes: an object `{ errorCode, message }`
28
+ * for single errors, or an array `[{ errorCode, message }]` for endpoints that
29
+ * return collections of errors. Handle both.
30
+ */
31
+ export declare function isCsrfError(response: FetchResponse<any>): boolean;
32
+ export {};
@@ -0,0 +1,18 @@
1
+ import { RetryPolicy, type RetryContext } from '@conduit-client/service-retry/v1';
2
+ import type { FetchResponse } from '@luvio/engine';
3
+ type LuvioFetchThrottlingRetryPolicyConfig = {
4
+ maxRetries: number;
5
+ maxTimeToRetry: number;
6
+ baseDelay: number;
7
+ maxDelay: number;
8
+ exponentialFactor: number;
9
+ jitterPercent: number;
10
+ };
11
+ export declare class LuvioFetchThrottlingRetryPolicy extends RetryPolicy<FetchResponse<any>> {
12
+ private config;
13
+ constructor(config?: LuvioFetchThrottlingRetryPolicyConfig);
14
+ shouldRetry(result: FetchResponse<any>, context: RetryContext<FetchResponse<any>>): Promise<boolean>;
15
+ calculateDelay(result: FetchResponse<any>, context: RetryContext<FetchResponse<any>>): Promise<number>;
16
+ parseRetryAfterHeader(result: FetchResponse<any>): number | undefined;
17
+ }
18
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-aura",
3
- "version": "1.435.1",
3
+ "version": "1.437.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS engine for Aura runtime",
6
6
  "main": "dist/ldsEngineCreator.js",
@@ -36,15 +36,15 @@
36
36
  "devDependencies": {
37
37
  "@conduit-client/service-provisioner": "3.19.5",
38
38
  "@conduit-client/tools-core": "3.19.5",
39
- "@salesforce/lds-adapters-apex": "^1.435.1",
40
- "@salesforce/lds-adapters-uiapi": "^1.435.1",
41
- "@salesforce/lds-ads-bridge": "^1.435.1",
42
- "@salesforce/lds-aura-storage": "^1.435.1",
43
- "@salesforce/lds-bindings": "^1.435.1",
44
- "@salesforce/lds-instrumentation": "^1.435.1",
45
- "@salesforce/lds-network-adapter": "^1.435.1",
46
- "@salesforce/lds-network-aura": "^1.435.1",
47
- "@salesforce/lds-network-fetch": "^1.435.1",
39
+ "@salesforce/lds-adapters-apex": "^1.437.0",
40
+ "@salesforce/lds-adapters-uiapi": "^1.437.0",
41
+ "@salesforce/lds-ads-bridge": "^1.437.0",
42
+ "@salesforce/lds-aura-storage": "^1.437.0",
43
+ "@salesforce/lds-bindings": "^1.437.0",
44
+ "@salesforce/lds-instrumentation": "^1.437.0",
45
+ "@salesforce/lds-network-adapter": "^1.437.0",
46
+ "@salesforce/lds-network-aura": "^1.437.0",
47
+ "@salesforce/lds-network-fetch": "^1.437.0",
48
48
  "jwt-encode": "1.0.1"
49
49
  },
50
50
  "dependencies": {
@@ -72,22 +72,22 @@
72
72
  "@conduit-client/service-pubsub": "3.19.5",
73
73
  "@conduit-client/service-store": "3.19.5",
74
74
  "@conduit-client/utils": "3.19.5",
75
- "@luvio/network-adapter-composable": "0.160.4",
76
- "@luvio/network-adapter-fetch": "0.160.4",
75
+ "@luvio/network-adapter-composable": "0.160.5",
76
+ "@luvio/network-adapter-fetch": "0.160.5",
77
77
  "@lwc/state": "^0.29.0",
78
- "@salesforce/lds-adapters-onestore-graphql": "^1.435.1",
78
+ "@salesforce/lds-adapters-onestore-graphql": "^1.437.0",
79
79
  "@salesforce/lds-adapters-uiapi-lex": "^1.415.0",
80
- "@salesforce/lds-durable-storage": "^1.435.1",
81
- "@salesforce/lds-luvio-service": "^1.435.1",
82
- "@salesforce/lds-luvio-uiapi-records-service": "^1.435.1"
80
+ "@salesforce/lds-durable-storage": "^1.437.0",
81
+ "@salesforce/lds-luvio-service": "^1.437.0",
82
+ "@salesforce/lds-luvio-uiapi-records-service": "^1.437.0"
83
83
  },
84
84
  "luvioBundlesize": [
85
85
  {
86
86
  "path": "./dist/ldsEngineCreator.js",
87
87
  "maxSize": {
88
- "none": "370 kB",
88
+ "none": "383 kB",
89
89
  "min": "190 kB",
90
- "compressed": "61.9 kB"
90
+ "compressed": "63 kB"
91
91
  }
92
92
  }
93
93
  ],