@salesforce/lds-runtime-aura 1.442.0 → 1.444.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.
- package/dist/ldsEngineCreator.js +705 -188
- package/dist/types/aura-utils.d.ts +28 -0
- package/dist/types/fetch-service-descriptors/jwt-authorized-fetch-service.d.ts +3 -0
- package/dist/types/network-sfap.d.ts +1 -0
- package/dist/types/parameterized-sfap-jwt-aura-resolver.d.ts +34 -0
- package/dist/types/request-interceptors/csrf-manager.d.ts +6 -43
- package/dist/types/request-interceptors/csrf-service.d.ts +19 -0
- package/dist/types/request-interceptors/first-party-header.d.ts +17 -0
- package/dist/types/request-interceptors/jwt-parameterization.d.ts +50 -0
- package/dist/types/response-interceptors/error-body-normalization.d.ts +20 -0
- package/dist/types/response-interceptors/{lex-runtime-auth-expiration-redirect.d.ts → lex-runtime-session-expiration.d.ts} +2 -2
- package/dist/types/retry-policies/csrf-token-retry-policy.d.ts +0 -1
- package/dist/types/retry-policies/luvio-csrf-token-retry-policy.d.ts +0 -1
- package/dist/types/sfap-jwt-mint-params.d.ts +69 -0
- package/package.json +46 -44
package/dist/ldsEngineCreator.js
CHANGED
|
@@ -22,9 +22,9 @@ import { instrument, getRecordAvatarsAdapterFactory, getRecordAdapterFactory, co
|
|
|
22
22
|
import { getInstrumentation } from 'o11y/client';
|
|
23
23
|
import { findExecutableOperation, buildGraphQLInputExtension, addTypenameToDocument } from 'force/luvioGraphqlNormalization';
|
|
24
24
|
import { print, resolveAndValidateGraphQLConfig, toGraphQLErrorResponse } from 'force/luvioOnestoreGraphqlParser';
|
|
25
|
-
import { setServices } from 'force/luvioServiceProvisioner1';
|
|
25
|
+
import getServices, { setServices } from 'force/luvioServiceProvisioner1';
|
|
26
26
|
import { assertIsValid, JsonSchemaViolationError, MissingRequiredPropertyError } from 'force/luvioJsonschemaValidate5';
|
|
27
|
-
import { dispatchGlobalEvent, executeGlobalControllerRawResponse } from 'aura';
|
|
27
|
+
import { dispatchGlobalEvent as dispatchGlobalEvent$1, executeGlobalControllerRawResponse } from 'aura';
|
|
28
28
|
import auraNetworkAdapter, { dispatchAuraAction, defaultActionConfig, instrument as instrument$1, forceRecordTransactionsDisabled as forceRecordTransactionsDisabled$1, ldsNetworkAdapterInstrument, CrudEventState, CrudEventType, UIAPI_RECORDS_PATH, UIAPI_RELATED_LIST_RECORDS_BATCH_PATH, UIAPI_RELATED_LIST_RECORDS_PATH } from 'force/ldsNetwork';
|
|
29
29
|
import { ThirdPartyTracker } from 'instrumentation:thirdPartyTracker';
|
|
30
30
|
import { markStart, markEnd, counter, registerCacheStats, perfStart, perfEnd, registerPeriodicLogger, interaction, timer, mark } from 'instrumentation/service';
|
|
@@ -36,7 +36,6 @@ import { instrument as instrument$5 } from '@lwc/state';
|
|
|
36
36
|
import { withRegistration, register, setDefaultLuvio } from 'force/ldsEngine';
|
|
37
37
|
import applyPredictionRequestLimit from '@salesforce/gate/lds.pdl.applyRequestLimit';
|
|
38
38
|
import { pageScopedCache } from 'instrumentation/utility';
|
|
39
|
-
import { createStorage, clearStorages } from 'force/ldsDurableStorage';
|
|
40
39
|
import lightningConnectEnabled from '@salesforce/gate/ui.services.LightningConnect.enabled';
|
|
41
40
|
import bypassAppRestrictionEnabled from '@salesforce/gate/ui.services.LightningConnect.BypassAppRestriction.enabled';
|
|
42
41
|
import csrfValidationEnabled from '@salesforce/gate/ui.services.LightningConnect.CsrfValidation.enabled';
|
|
@@ -47,6 +46,7 @@ import useHttpUiapiOneRuntimePublic from '@salesforce/gate/lds.useHttpUiapiOneRu
|
|
|
47
46
|
import useHttpUiapiOneRuntimePrivate from '@salesforce/gate/lds.useHttpUiapiOneRuntimePrivate';
|
|
48
47
|
import disableCreateContentDocumentAndVersionHTTPLexRuntime from '@salesforce/gate/lds.lex.http.disableCreateContentDocumentAndVersion';
|
|
49
48
|
import useHotspotLimit from '@salesforce/gate/lds.pdl.useHotspotLimit';
|
|
49
|
+
import { createStorage, clearStorages } from 'force/ldsDurableStorage';
|
|
50
50
|
import { registerSubRequestNetworkAdapter } from 'force/ldsNetwork';
|
|
51
51
|
|
|
52
52
|
const { create: create$1, freeze, keys: keys$2, entries: entries$1 } = Object;
|
|
@@ -234,6 +234,19 @@ var HttpStatusCode$1 = /* @__PURE__ */ ((HttpStatusCode2) => {
|
|
|
234
234
|
HttpStatusCode2[HttpStatusCode2["GatewayTimeout"] = 504] = "GatewayTimeout";
|
|
235
235
|
return HttpStatusCode2;
|
|
236
236
|
})(HttpStatusCode$1 || {});
|
|
237
|
+
function getFetchResponseFromAuraError(err2) {
|
|
238
|
+
if (err2.data !== void 0 && err2.data.statusCode !== void 0) {
|
|
239
|
+
let data = {};
|
|
240
|
+
data = err2.data;
|
|
241
|
+
if (err2.id !== void 0) {
|
|
242
|
+
data.id = err2.id;
|
|
243
|
+
}
|
|
244
|
+
return new FetchResponse(data.statusCode, data);
|
|
245
|
+
}
|
|
246
|
+
return new FetchResponse(500, {
|
|
247
|
+
error: err2.message
|
|
248
|
+
});
|
|
249
|
+
}
|
|
237
250
|
async function coerceResponseToFetchResponse(response) {
|
|
238
251
|
const { status } = response;
|
|
239
252
|
const responseHeaders = {};
|
|
@@ -388,7 +401,7 @@ class AuraNetworkCommand extends NetworkCommand$1 {
|
|
|
388
401
|
);
|
|
389
402
|
}
|
|
390
403
|
coerceAuraErrors(auraErrors) {
|
|
391
|
-
return
|
|
404
|
+
return new UserVisibleError(getFetchResponseFromAuraError(auraErrors[0]));
|
|
392
405
|
}
|
|
393
406
|
/**
|
|
394
407
|
* Customize how non-2xx fetch fallback responses are converted into errors.
|
|
@@ -573,7 +586,10 @@ function deepEquals$1(x, y) {
|
|
|
573
586
|
}
|
|
574
587
|
for (let i = 0; i < xkeys.length; ++i) {
|
|
575
588
|
const key = xkeys[i];
|
|
576
|
-
if (!deepEquals$1(
|
|
589
|
+
if (!deepEquals$1(
|
|
590
|
+
x[key],
|
|
591
|
+
y[key]
|
|
592
|
+
)) {
|
|
577
593
|
return false;
|
|
578
594
|
}
|
|
579
595
|
}
|
|
@@ -2644,7 +2660,7 @@ function buildServiceDescriptor$d(luvio) {
|
|
|
2644
2660
|
},
|
|
2645
2661
|
};
|
|
2646
2662
|
}
|
|
2647
|
-
// version: 1.
|
|
2663
|
+
// version: 1.444.0-a7f42f9edf
|
|
2648
2664
|
|
|
2649
2665
|
class AuraGraphQLNormalizedCacheControlCommand extends AuraNormalizedCacheControlCommand {
|
|
2650
2666
|
constructor(config, documentRootType, services) {
|
|
@@ -2982,7 +2998,7 @@ function buildServiceDescriptor$9(notifyRecordUpdateAvailable, getNormalizedLuvi
|
|
|
2982
2998
|
},
|
|
2983
2999
|
};
|
|
2984
3000
|
}
|
|
2985
|
-
// version: 1.
|
|
3001
|
+
// version: 1.444.0-a7f42f9edf
|
|
2986
3002
|
|
|
2987
3003
|
class RetryService {
|
|
2988
3004
|
constructor(defaultRetryPolicy) {
|
|
@@ -3134,6 +3150,50 @@ function buildServiceDescriptor$8(defaultRetryPolicy) {
|
|
|
3134
3150
|
};
|
|
3135
3151
|
}
|
|
3136
3152
|
|
|
3153
|
+
class BasicRenewableResourceManager {
|
|
3154
|
+
constructor(config) {
|
|
3155
|
+
this.config = config;
|
|
3156
|
+
}
|
|
3157
|
+
get() {
|
|
3158
|
+
return this.config.storage.get().then((cached) => cached !== void 0 ? cached : this.fetchAndStore());
|
|
3159
|
+
}
|
|
3160
|
+
refresh(staleValue) {
|
|
3161
|
+
if (staleValue === void 0) {
|
|
3162
|
+
return this.fetchAndStore();
|
|
3163
|
+
}
|
|
3164
|
+
return this.config.storage.get().then(
|
|
3165
|
+
(current) => current !== void 0 && current !== staleValue ? current : this.fetchAndStore()
|
|
3166
|
+
);
|
|
3167
|
+
}
|
|
3168
|
+
clear() {
|
|
3169
|
+
return this.config.storage.clear();
|
|
3170
|
+
}
|
|
3171
|
+
/**
|
|
3172
|
+
* Single in-flight fetch shared by all concurrent `get`/`refresh` callers.
|
|
3173
|
+
* Overwrites storage on a successful fetch of a defined value only; a
|
|
3174
|
+
* rejected fetch or a resolved-`undefined` leaves the cached value intact.
|
|
3175
|
+
*/
|
|
3176
|
+
fetchAndStore() {
|
|
3177
|
+
if (this.inFlightFetch) return this.inFlightFetch;
|
|
3178
|
+
const pending = this.config.fetch().then(
|
|
3179
|
+
(fetched) => fetched === void 0 ? fetched : this.config.storage.set(fetched).then(() => fetched)
|
|
3180
|
+
);
|
|
3181
|
+
this.inFlightFetch = pending;
|
|
3182
|
+
const releaseSlot = () => {
|
|
3183
|
+
if (this.inFlightFetch === pending) this.inFlightFetch = void 0;
|
|
3184
|
+
};
|
|
3185
|
+
pending.then(releaseSlot, releaseSlot);
|
|
3186
|
+
return pending;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
function buildRenewableResourceManagerDescriptor(type, service) {
|
|
3190
|
+
return {
|
|
3191
|
+
version: "1.0",
|
|
3192
|
+
service,
|
|
3193
|
+
type
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3137
3197
|
function isUserVisibleError(error) {
|
|
3138
3198
|
return error instanceof Error && "type" in error && error.type === "user-visible";
|
|
3139
3199
|
}
|
|
@@ -3364,7 +3424,10 @@ class GraphQLImperativeBindingsService {
|
|
|
3364
3424
|
const options = {
|
|
3365
3425
|
acceptedOperations: ["query"]
|
|
3366
3426
|
};
|
|
3367
|
-
const result = resolveAndValidateGraphQLConfig(
|
|
3427
|
+
const result = resolveAndValidateGraphQLConfig(
|
|
3428
|
+
params[0],
|
|
3429
|
+
options
|
|
3430
|
+
);
|
|
3368
3431
|
if (result?.isErr()) {
|
|
3369
3432
|
return result.error;
|
|
3370
3433
|
}
|
|
@@ -3561,7 +3624,10 @@ class GraphQLMutationBindingsService {
|
|
|
3561
3624
|
const options = {
|
|
3562
3625
|
acceptedOperations: ["mutation"]
|
|
3563
3626
|
};
|
|
3564
|
-
const result2 = resolveAndValidateGraphQLConfig(
|
|
3627
|
+
const result2 = resolveAndValidateGraphQLConfig(
|
|
3628
|
+
params[0],
|
|
3629
|
+
options
|
|
3630
|
+
);
|
|
3565
3631
|
if (result2?.isErr()) {
|
|
3566
3632
|
return {
|
|
3567
3633
|
data: void 0,
|
|
@@ -4623,7 +4689,7 @@ var TypeCheckShapes;
|
|
|
4623
4689
|
TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
|
|
4624
4690
|
TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
|
|
4625
4691
|
})(TypeCheckShapes || (TypeCheckShapes = {}));
|
|
4626
|
-
// engine version: 0.
|
|
4692
|
+
// engine version: 0.161.0-fe06f180
|
|
4627
4693
|
|
|
4628
4694
|
const { keys: keys$1 } = Object;
|
|
4629
4695
|
|
|
@@ -4718,6 +4784,10 @@ const SALESFORCE_API_BASE_URI_FLAG = 'api.salesforce.com';
|
|
|
4718
4784
|
const X_REQUEST_ID_HEADER = 'x-request-id';
|
|
4719
4785
|
const SFAPController = 'SalesforceApiPlatformController';
|
|
4720
4786
|
const SFAPJwtMethod = 'getSFAPLightningJwtService';
|
|
4787
|
+
// Parameterized mint: the POST signature's auto-generated Aura method
|
|
4788
|
+
// (`@ConnectSignature(..., generateAuraMethod = true)` on the SFAP Lightning JWT
|
|
4789
|
+
// Service Connect resource). Takes a single `requestBody` named param.
|
|
4790
|
+
const SFAPJwtPostMethod = 'postSFAPLightningJwtService';
|
|
4721
4791
|
/**
|
|
4722
4792
|
* We expect jwt info and baseUri to be present in the response.
|
|
4723
4793
|
*
|
|
@@ -4864,6 +4934,144 @@ function generateRequestId$1() {
|
|
|
4864
4934
|
}
|
|
4865
4935
|
}
|
|
4866
4936
|
|
|
4937
|
+
/**
|
|
4938
|
+
* Normalize SFAP-shaped params into the opaque `JwtMintParams` bag that callers
|
|
4939
|
+
* hand to `JwtManager.getJwt(params)`. Scopes are sorted because they are
|
|
4940
|
+
* semantically a set — without this, `['a', 'b']` and `['b', 'a']` would cache
|
|
4941
|
+
* as separate entries (OneStore treats arrays as ordered under stable-JSON
|
|
4942
|
+
* serialization; see ADR "JWT Parameterization" §2).
|
|
4943
|
+
*
|
|
4944
|
+
* MANDATORY CONSUMER ENTRY POINT. Every consumer that mints a parameterized
|
|
4945
|
+
* SFAP JWT MUST assemble its params through this function before calling the
|
|
4946
|
+
* manager. The cache key is derived by `JwtManager` from the caller's params
|
|
4947
|
+
* (`cacheKeyFor(params)`) *before* the resolver runs — so the resolver cannot
|
|
4948
|
+
* normalize after the fact. Set-stable caching therefore depends on the caller
|
|
4949
|
+
* routing params through here. Do NOT sort inside the resolver: that would only
|
|
4950
|
+
* reorder the wire body, not the cache key, and mutating the caller's params
|
|
4951
|
+
* mid-call would desync the in-flight-dedup key from the stored-token key.
|
|
4952
|
+
*/
|
|
4953
|
+
/**
|
|
4954
|
+
* Validate and narrow an opaque `JwtMintParams` bag to the SFAP-shaped subset the
|
|
4955
|
+
* resolver forwards. Returns `{ error }` (surfaced as a resolver rejection) for a
|
|
4956
|
+
* malformed bag rather than throwing.
|
|
4957
|
+
*/
|
|
4958
|
+
function coerceToSfapParams(params) {
|
|
4959
|
+
const scopes = params.scopes;
|
|
4960
|
+
const dynamicParams = params.dynamicParams;
|
|
4961
|
+
if (scopes !== undefined && !isStringArray(scopes)) {
|
|
4962
|
+
return { error: 'SFAP JWT params.scopes must be a string[] when provided.' };
|
|
4963
|
+
}
|
|
4964
|
+
if (dynamicParams !== undefined && !isStringRecord(dynamicParams)) {
|
|
4965
|
+
return {
|
|
4966
|
+
error: 'SFAP JWT params.dynamicParams must be a Record<string, string> when provided.',
|
|
4967
|
+
};
|
|
4968
|
+
}
|
|
4969
|
+
return { params: { scopes, dynamicParams } };
|
|
4970
|
+
}
|
|
4971
|
+
function isStringArray(value) {
|
|
4972
|
+
return Array.isArray(value) && value.every((s) => typeof s === 'string');
|
|
4973
|
+
}
|
|
4974
|
+
function isStringRecord(value) {
|
|
4975
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
4976
|
+
return false;
|
|
4977
|
+
}
|
|
4978
|
+
return Object.values(value).every((v) => typeof v === 'string');
|
|
4979
|
+
}
|
|
4980
|
+
/**
|
|
4981
|
+
* Build the SFAP mint request body from the coerced params: `scopes` joined into a
|
|
4982
|
+
* single space-delimited string, `dynamicParams` mapped to `dynamicParameters.items`.
|
|
4983
|
+
* Empty scopes / dynamic params are omitted.
|
|
4984
|
+
*/
|
|
4985
|
+
function buildRequestBody(params) {
|
|
4986
|
+
const body = {};
|
|
4987
|
+
if (params.scopes && params.scopes.length > 0) {
|
|
4988
|
+
body.scopes = params.scopes.join(' ');
|
|
4989
|
+
}
|
|
4990
|
+
if (params.dynamicParams) {
|
|
4991
|
+
const items = Object.entries(params.dynamicParams).map(([name, value]) => ({ name, value }));
|
|
4992
|
+
if (items.length > 0) {
|
|
4993
|
+
body.dynamicParameters = { items };
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
return body;
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
/**
|
|
5000
|
+
* Parameterized SFAP JWT resolver that mints over **Aura transport** instead of a
|
|
5001
|
+
* direct HTTP `fetch`.
|
|
5002
|
+
*
|
|
5003
|
+
* It dispatches the SFAP Lightning JWT Service's parameterized POST via its
|
|
5004
|
+
* auto-generated Aura controller method
|
|
5005
|
+
* `SalesforceApiPlatformController.postSFAPLightningJwtService` (generated by
|
|
5006
|
+
* `@ConnectSignature(..., generateAuraMethod = true)` on the Connect resource).
|
|
5007
|
+
* The mint inputs ride as the single `requestBody` named param, in the same
|
|
5008
|
+
* `{ scopes, dynamicParameters: { items } }` shape the HTTP resolver POSTs — built
|
|
5009
|
+
* by the shared {@link buildRequestBody}, so the two transports stay in lockstep.
|
|
5010
|
+
*
|
|
5011
|
+
* Why Aura (not the HTTP resolver): the mint endpoint is a same-origin core
|
|
5012
|
+
* resource, and routing it over Aura keeps session/CSRF handling inside the Aura
|
|
5013
|
+
* stack rather than issuing a credentialed cross-cutting `fetch` from the client.
|
|
5014
|
+
* This mirrors the legacy parameterless `platformSfapJwtResolver` in
|
|
5015
|
+
* `network-sfap.ts`, which already mints over Aura via the `getSFAPLightningJwtService`
|
|
5016
|
+
* generated method — this is the parameterized sibling of that call.
|
|
5017
|
+
*
|
|
5018
|
+
* A `JwtResolver` is invoked directly by `JwtManager` (not through Luvio's
|
|
5019
|
+
* `appRouter`/`ResourceRequest` pipeline), so the correct mechanism is a direct
|
|
5020
|
+
* named-controller `dispatchAuraAction`, not the `auraNetworkAdapter`/connect-route
|
|
5021
|
+
* table. The SFAP JWT endpoint is not registered as a connect-over-Aura route, and
|
|
5022
|
+
* a resolver has no `ResourceRequest` for the router to look up.
|
|
5023
|
+
*/
|
|
5024
|
+
class ParameterizedSfapJwtAuraResolver {
|
|
5025
|
+
getJwt(params) {
|
|
5026
|
+
return new Promise((resolve, reject) => {
|
|
5027
|
+
if (params === undefined) {
|
|
5028
|
+
// Misuse: the dispatching resolver routes parameterless calls to the
|
|
5029
|
+
// legacy resolver. Reject so production fails loudly here.
|
|
5030
|
+
reject('ParameterizedSfapJwtAuraResolver requires JwtMintParams. The legacy parameterless path should be served by platformSfapJwtResolver.');
|
|
5031
|
+
return;
|
|
5032
|
+
}
|
|
5033
|
+
const coerced = coerceToSfapParams(params);
|
|
5034
|
+
if ('error' in coerced) {
|
|
5035
|
+
reject(coerced.error);
|
|
5036
|
+
return;
|
|
5037
|
+
}
|
|
5038
|
+
// The mint inputs become the single `requestBody` named param of the
|
|
5039
|
+
// generated Aura method (Aura binds top-level keys to the controller
|
|
5040
|
+
// method's named args). Reuses the HTTP resolver's body builder so both
|
|
5041
|
+
// transports send an identical shape.
|
|
5042
|
+
const requestBody = buildRequestBody(coerced.params);
|
|
5043
|
+
// No fetchImpl / requestInterceptor / CSRF / credentials handling here —
|
|
5044
|
+
// the Aura stack owns session + CSRF. Matches the legacy resolver's call.
|
|
5045
|
+
dispatchAuraAction(`${SFAPController}.${SFAPJwtPostMethod}`, { requestBody }, defaultActionConfig)
|
|
5046
|
+
.then((response) => {
|
|
5047
|
+
const body = response.body;
|
|
5048
|
+
if (!body || typeof body.jwt !== 'string' || typeof body.baseUri !== 'string') {
|
|
5049
|
+
// Never serialize the body into the error — it may carry a JWT.
|
|
5050
|
+
reject('SFAP JWT response missing required fields (jwt, baseUri)');
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
resolve({ jwt: body.jwt, extraInfo: { baseUri: body.baseUri } });
|
|
5054
|
+
})
|
|
5055
|
+
.catch((error) => {
|
|
5056
|
+
// Error mapping ported from the legacy platformSfapJwtResolver
|
|
5057
|
+
// (network-sfap.ts): plain Errors carry a message; non-500
|
|
5058
|
+
// AuraFetchResponses are ConnectInJava errors with a typed body;
|
|
5059
|
+
// 500s carry an { error } body.
|
|
5060
|
+
if (error instanceof Error) {
|
|
5061
|
+
reject(error.message);
|
|
5062
|
+
return;
|
|
5063
|
+
}
|
|
5064
|
+
const { status } = error;
|
|
5065
|
+
if (status !== HttpStatusCode$2.ServerError) {
|
|
5066
|
+
reject(error.body.message);
|
|
5067
|
+
return;
|
|
5068
|
+
}
|
|
5069
|
+
reject(error.body.error);
|
|
5070
|
+
});
|
|
5071
|
+
});
|
|
5072
|
+
}
|
|
5073
|
+
}
|
|
5074
|
+
|
|
4867
5075
|
/**
|
|
4868
5076
|
* Observability / Critical Availability Program (230+)
|
|
4869
5077
|
*
|
|
@@ -5614,7 +5822,9 @@ class PrioritizedConfigService {
|
|
|
5614
5822
|
// ConfigService.set
|
|
5615
5823
|
set(priority, setter) {
|
|
5616
5824
|
this.validated = false;
|
|
5617
|
-
return setter(
|
|
5825
|
+
return setter(
|
|
5826
|
+
this.prioritizedConfigData[priority]
|
|
5827
|
+
);
|
|
5618
5828
|
}
|
|
5619
5829
|
// returns the highest priority value for a given path, undefined if no value
|
|
5620
5830
|
// is defined for the path at any priority
|
|
@@ -5673,9 +5883,36 @@ function getEnvironmentSetting(name) {
|
|
|
5673
5883
|
}
|
|
5674
5884
|
return undefined;
|
|
5675
5885
|
}
|
|
5676
|
-
// version: 1.
|
|
5886
|
+
// version: 1.444.0-86f5a57eb3
|
|
5887
|
+
|
|
5888
|
+
/**
|
|
5889
|
+
* Helpers for reaching the Aura framework from the LDS Aura runtime.
|
|
5890
|
+
*
|
|
5891
|
+
* `window.$A` is only present when the runtime is hosted inside LEX; in tests
|
|
5892
|
+
* and non-Aura runtimes it is absent. Centralizing the guarded access keeps the
|
|
5893
|
+
* `$A` lookup in one place instead of duplicating the `environmentHasAura`
|
|
5894
|
+
* check across modules.
|
|
5895
|
+
*/
|
|
5896
|
+
/**
|
|
5897
|
+
* Fires an Aura application-level event (e.g. `aura:invalidSession`). Thin
|
|
5898
|
+
* pass-through to the `aura` framework module so callers depend on this util
|
|
5899
|
+
* rather than importing `aura` directly.
|
|
5900
|
+
*/
|
|
5901
|
+
function dispatchGlobalEvent(...args) {
|
|
5902
|
+
dispatchGlobalEvent$1(...args);
|
|
5903
|
+
}
|
|
5904
|
+
/**
|
|
5905
|
+
* Returns `$A.clientService` when running inside an Aura environment, or
|
|
5906
|
+
* `undefined` otherwise. Defensive: never throws.
|
|
5907
|
+
*/
|
|
5908
|
+
function getAuraClientService() {
|
|
5909
|
+
if (typeof window === 'undefined' || typeof window.$A === 'undefined') {
|
|
5910
|
+
return undefined;
|
|
5911
|
+
}
|
|
5912
|
+
return window.$A.clientService;
|
|
5913
|
+
}
|
|
5677
5914
|
|
|
5678
|
-
const
|
|
5915
|
+
const auraClientService = getAuraClientService();
|
|
5679
5916
|
const defaultConfig = {
|
|
5680
5917
|
maxAllowedParallelXHRCounts: 9,
|
|
5681
5918
|
forceRecordTransactionsDisabled: false,
|
|
@@ -5686,10 +5923,11 @@ const configServiceDescriptor = buildServiceDescriptor$1({
|
|
|
5686
5923
|
default: defaultConfig,
|
|
5687
5924
|
});
|
|
5688
5925
|
const configService = configServiceDescriptor.service;
|
|
5689
|
-
if (
|
|
5926
|
+
if (auraClientService?.maxAllowedParallelXHRCounts) {
|
|
5690
5927
|
configService.set('lex', (config) => {
|
|
5691
5928
|
config.maxAllowedParallelXHRCounts =
|
|
5692
|
-
|
|
5929
|
+
auraClientService.maxAllowedParallelXHRCounts?.() ??
|
|
5930
|
+
defaultConfig.maxAllowedParallelXHRCounts;
|
|
5693
5931
|
config.forceRecordTransactionsDisabled = getEnvironmentSetting(EnvironmentSettings.ForceRecordTransactionsDisabled);
|
|
5694
5932
|
});
|
|
5695
5933
|
}
|
|
@@ -5933,34 +6171,54 @@ function buildLuvioPageScopedCacheRequestInterceptor() {
|
|
|
5933
6171
|
};
|
|
5934
6172
|
}
|
|
5935
6173
|
|
|
5936
|
-
|
|
6174
|
+
// Two distinct session-expiration cases on the LEX runtime path:
|
|
6175
|
+
// - 401 + INVALID_SESSION_ID — auth token rejected
|
|
6176
|
+
// - 403 + `x-sfdc-csrf-failure: true` — CSRF token rejected, session is dead
|
|
6177
|
+
// Both fire `aura:invalidSession`, which routes the user back to login.
|
|
6178
|
+
//
|
|
6179
|
+
// 403 access denials (INSUFFICIENT_ACCESS / INSUFFICIENT_ACCESS_OR_READONLY) are
|
|
6180
|
+
// intentionally NOT handled here — `aura:noAccess` without a `redirectURL`
|
|
6181
|
+
// triggers AuraClientService.hardRefresh() which is too heavy-handed for routine
|
|
6182
|
+
// "feature not enabled for this org" responses. The wire layer surfaces those
|
|
6183
|
+
// as normal errors; onestore PR #838 (`aura-network-command`) handles the
|
|
6184
|
+
// gack-noise on the Aura action path.
|
|
6185
|
+
function isSessionExpired(status, headers, body) {
|
|
6186
|
+
if (status === 403) {
|
|
6187
|
+
return headers && headers['x-sfdc-csrf-failure'] === 'true';
|
|
6188
|
+
}
|
|
6189
|
+
if (status === 401) {
|
|
6190
|
+
// Connect REST returns errors as `[{ errorCode, ... }, ...]`; Aura Shape A
|
|
6191
|
+
// uses `{ errorCode, ... }`. This interceptor runs before
|
|
6192
|
+
// `buildLuvioErrorBodyNormalizationInterceptor`, so handle both.
|
|
6193
|
+
const errorCode = Array.isArray(body)
|
|
6194
|
+
? body[0] && body[0].errorCode
|
|
6195
|
+
: body && body.errorCode;
|
|
6196
|
+
return errorCode === 'INVALID_SESSION_ID';
|
|
6197
|
+
}
|
|
6198
|
+
return false;
|
|
6199
|
+
}
|
|
6200
|
+
function buildLexRuntimeSessionExpirationResponseInterceptor(logger) {
|
|
5937
6201
|
return async (response) => {
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
}, 0);
|
|
5947
|
-
}
|
|
5948
|
-
}
|
|
5949
|
-
catch (error) {
|
|
5950
|
-
logger.warn(`Error parsing response from LEX runtime service: ${error}`);
|
|
6202
|
+
const { status } = response;
|
|
6203
|
+
if (status !== 401 && status !== 403)
|
|
6204
|
+
return response;
|
|
6205
|
+
try {
|
|
6206
|
+
const coerced = (await coerceResponseToFetchResponse(response.clone()));
|
|
6207
|
+
if (isSessionExpired(status, coerced.headers, coerced.body)) {
|
|
6208
|
+
logger.warn(`Received ${status} status code from LEX runtime service`);
|
|
6209
|
+
window.setTimeout(() => dispatchGlobalEvent('aura:invalidSession'), 0);
|
|
5951
6210
|
}
|
|
5952
6211
|
}
|
|
6212
|
+
catch (error) {
|
|
6213
|
+
logger.warn(`Error parsing response from LEX runtime service: ${error}`);
|
|
6214
|
+
}
|
|
5953
6215
|
return response;
|
|
5954
6216
|
};
|
|
5955
6217
|
}
|
|
5956
|
-
function
|
|
6218
|
+
function buildLexRuntimeLuvioSessionExpirationResponseInterceptor() {
|
|
5957
6219
|
return async (response) => {
|
|
5958
|
-
if (response.status
|
|
5959
|
-
|
|
5960
|
-
window.setTimeout(() => {
|
|
5961
|
-
dispatchGlobalEvent('aura:invalidSession');
|
|
5962
|
-
}, 0);
|
|
5963
|
-
}
|
|
6220
|
+
if (isSessionExpired(response.status, response.headers, response.body)) {
|
|
6221
|
+
window.setTimeout(() => dispatchGlobalEvent('aura:invalidSession'), 0);
|
|
5964
6222
|
}
|
|
5965
6223
|
return response;
|
|
5966
6224
|
};
|
|
@@ -6119,146 +6377,91 @@ function buildLexRuntimeLuvio5xxStatusResponseInterceptor() {
|
|
|
6119
6377
|
};
|
|
6120
6378
|
}
|
|
6121
6379
|
|
|
6122
|
-
const CSRF_TOKEN_KEY = 'salesforce_csrf_token';
|
|
6123
|
-
const CSRF_STORAGE_NAME = 'ldsCSRFToken';
|
|
6124
|
-
const BASE_URI = '/services/data/v68.0';
|
|
6125
|
-
const UI_API_BASE_URI = `${BASE_URI}/ui-api`;
|
|
6126
|
-
const CSRF_TOKEN_ENDPOINT = `${UI_API_BASE_URI}/session/csrf`;
|
|
6127
|
-
const CSRF_STORAGE_CONFIG = {
|
|
6128
|
-
name: CSRF_STORAGE_NAME,
|
|
6129
|
-
persistent: true,
|
|
6130
|
-
secure: true,
|
|
6131
|
-
maxSize: 1024,
|
|
6132
|
-
expiration: 24 * 60 * 60,
|
|
6133
|
-
clearOnInit: false,
|
|
6134
|
-
debugLogging: false,
|
|
6135
|
-
};
|
|
6136
6380
|
/**
|
|
6137
|
-
*
|
|
6138
|
-
*
|
|
6381
|
+
* Normalizes Connect REST error envelopes into the Aura Shape A
|
|
6382
|
+
* (ConnectInJava) shape, so consumers can read `response.body.errorCode`
|
|
6383
|
+
* regardless of transport.
|
|
6384
|
+
*
|
|
6385
|
+
* - HTTP error envelope (this path): `[{ errorCode, message }, ...]`
|
|
6386
|
+
* - Aura Shape A: `{ errorCode, message, statusCode, ... }`
|
|
6387
|
+
*
|
|
6388
|
+
* The full array is preserved at `body.enhancedErrorInfo.allErrors` since
|
|
6389
|
+
* Connect can return multiple errors per response.
|
|
6390
|
+
*
|
|
6391
|
+
* Aura Shape B (`{ error: "..." }`) is an Aura-only fallback that never
|
|
6392
|
+
* appears on the HTTP path, so no normalization is needed for it here.
|
|
6393
|
+
*
|
|
6394
|
+
* Ordering: must run AFTER any other interceptor that inspects the raw
|
|
6395
|
+
* error body shape (e.g. the 5xx interceptor's ErrorId extraction reads
|
|
6396
|
+
* `body[0].message` from the array form).
|
|
6139
6397
|
*/
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
return CsrfTokenManager.instance;
|
|
6152
|
-
}
|
|
6153
|
-
/**
|
|
6154
|
-
* Obtain a CSRF token, either from AuraStorage or by fetching a fresh one.
|
|
6155
|
-
*
|
|
6156
|
-
* @private
|
|
6157
|
-
*/
|
|
6158
|
-
async loadOrFetchToken() {
|
|
6159
|
-
// First try to get token from AuraStorage
|
|
6160
|
-
if (this.storage) {
|
|
6161
|
-
try {
|
|
6162
|
-
const cachedToken = await this.storage.get(CSRF_TOKEN_KEY);
|
|
6163
|
-
if (typeof cachedToken === 'string' && cachedToken) {
|
|
6164
|
-
return cachedToken;
|
|
6165
|
-
}
|
|
6166
|
-
}
|
|
6167
|
-
catch {
|
|
6168
|
-
// If storage read fails, continue to fetch
|
|
6169
|
-
}
|
|
6170
|
-
}
|
|
6171
|
-
// No cached token, fetch from server
|
|
6172
|
-
return this.fetchFreshToken();
|
|
6173
|
-
}
|
|
6174
|
-
/**
|
|
6175
|
-
* Call API endpoint to acquire a CSRF token and cache it.
|
|
6176
|
-
*
|
|
6177
|
-
* @private
|
|
6178
|
-
*/
|
|
6179
|
-
async fetchFreshToken() {
|
|
6180
|
-
try {
|
|
6181
|
-
const response = await fetch(CSRF_TOKEN_ENDPOINT, {
|
|
6182
|
-
method: 'GET',
|
|
6183
|
-
credentials: 'same-origin',
|
|
6184
|
-
});
|
|
6185
|
-
if (!response.ok) {
|
|
6186
|
-
return undefined;
|
|
6187
|
-
}
|
|
6188
|
-
const data = await response.json();
|
|
6189
|
-
const token = data.csrfToken;
|
|
6190
|
-
if (token && this.storage) {
|
|
6191
|
-
// Cache the token in AuraStorage
|
|
6192
|
-
try {
|
|
6193
|
-
await this.storage.set(CSRF_TOKEN_KEY, token);
|
|
6194
|
-
}
|
|
6195
|
-
catch {
|
|
6196
|
-
// Non-fatal: token is still available even if caching fails
|
|
6197
|
-
}
|
|
6198
|
-
}
|
|
6199
|
-
return token;
|
|
6200
|
-
}
|
|
6201
|
-
catch {
|
|
6202
|
-
return undefined;
|
|
6203
|
-
}
|
|
6204
|
-
}
|
|
6205
|
-
/**
|
|
6206
|
-
* Returns the current token value as a Promise.
|
|
6207
|
-
* Lazy-loads the token on first call (from cache or by fetching).
|
|
6208
|
-
*/
|
|
6209
|
-
async getToken() {
|
|
6210
|
-
// Lazy initialization: only fetch token when actually needed
|
|
6211
|
-
if (!this.tokenPromise) {
|
|
6212
|
-
this.tokenPromise = this.loadOrFetchToken();
|
|
6213
|
-
}
|
|
6214
|
-
return this.tokenPromise;
|
|
6215
|
-
}
|
|
6216
|
-
/**
|
|
6217
|
-
* Obtains and returns a new token value as a promise.
|
|
6218
|
-
* This will clear the cached token and fetch a fresh one.
|
|
6219
|
-
*
|
|
6220
|
-
* Concurrent calls coalesce onto a single in-flight refresh — important when
|
|
6221
|
-
* multiple requests fail with INVALID_ACCESS_TOKEN simultaneously and each
|
|
6222
|
-
* retry policy independently calls refreshToken(). Without this, every caller
|
|
6223
|
-
* triggers its own /session/csrf round-trip.
|
|
6224
|
-
*/
|
|
6225
|
-
refreshToken() {
|
|
6226
|
-
if (this.refreshInFlight) {
|
|
6227
|
-
return this.refreshInFlight;
|
|
6398
|
+
function buildLuvioErrorBodyNormalizationInterceptor() {
|
|
6399
|
+
return async (response) => {
|
|
6400
|
+
if (!response.ok && Array.isArray(response.body)) {
|
|
6401
|
+
const allErrors = response.body;
|
|
6402
|
+
response.body = {
|
|
6403
|
+
...allErrors[0],
|
|
6404
|
+
statusCode: response.status,
|
|
6405
|
+
enhancedErrorInfo: {
|
|
6406
|
+
allErrors,
|
|
6407
|
+
},
|
|
6408
|
+
};
|
|
6228
6409
|
}
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6410
|
+
return response;
|
|
6411
|
+
};
|
|
6412
|
+
}
|
|
6413
|
+
|
|
6414
|
+
/**
|
|
6415
|
+
* Service name the CSRF token manager is registered under by
|
|
6416
|
+
* `initializeOneStore` (see `buildRenewableResourceManagerDescriptor`).
|
|
6417
|
+
*/
|
|
6418
|
+
const CSRF_TOKEN_MANAGER_SERVICE = 'csrfTokenManager';
|
|
6419
|
+
const CSRF_TOKEN_MANAGER_REQUEST = {
|
|
6420
|
+
[CSRF_TOKEN_MANAGER_SERVICE]: {
|
|
6421
|
+
type: CSRF_TOKEN_MANAGER_SERVICE,
|
|
6422
|
+
version: '1.0',
|
|
6423
|
+
},
|
|
6424
|
+
};
|
|
6425
|
+
let cached;
|
|
6426
|
+
/**
|
|
6427
|
+
* Resolves the CSRF token manager from the service provisioner.
|
|
6428
|
+
*
|
|
6429
|
+
* The manager is registered during `initializeOneStore`, which runs before any
|
|
6430
|
+
* request flows; the interceptors and retry policies that call this resolve
|
|
6431
|
+
* lazily (per request / per retry), so the service is always available by then.
|
|
6432
|
+
*
|
|
6433
|
+
* The resolved promise is memoized so resolution happens once rather than per
|
|
6434
|
+
* request. A rejection is NOT memoized — `getServices` rejects when the service
|
|
6435
|
+
* is unavailable, and caching that would permanently wedge CSRF handling — so a
|
|
6436
|
+
* later call retries the resolution.
|
|
6437
|
+
*/
|
|
6438
|
+
function getCsrfTokenManager() {
|
|
6439
|
+
if (!cached) {
|
|
6440
|
+
cached = Promise.resolve(getServices(CSRF_TOKEN_MANAGER_REQUEST))
|
|
6441
|
+
.then((services) => services[CSRF_TOKEN_MANAGER_SERVICE])
|
|
6442
|
+
.catch((error) => {
|
|
6443
|
+
cached = undefined;
|
|
6444
|
+
throw error;
|
|
6248
6445
|
});
|
|
6249
|
-
return refresh;
|
|
6250
|
-
}
|
|
6251
|
-
/**
|
|
6252
|
-
* Reset the singleton instance (useful for testing).
|
|
6253
|
-
* @internal
|
|
6254
|
-
*/
|
|
6255
|
-
static resetInstance() {
|
|
6256
|
-
CsrfTokenManager.instance = null;
|
|
6257
6446
|
}
|
|
6447
|
+
return cached;
|
|
6258
6448
|
}
|
|
6259
|
-
CsrfTokenManager.instance = null;
|
|
6260
6449
|
|
|
6261
6450
|
const CSRF_TOKEN_HEADER = 'X-CSRF-Token';
|
|
6451
|
+
/**
|
|
6452
|
+
* Resolves the CSRF token manager and returns the current token. Returns
|
|
6453
|
+
* `undefined` if the manager cannot be resolved (service not yet provisioned)
|
|
6454
|
+
* or has no token, so a request is never blocked by token resolution.
|
|
6455
|
+
*/
|
|
6456
|
+
async function getCsrfToken() {
|
|
6457
|
+
try {
|
|
6458
|
+
const manager = await getCsrfTokenManager();
|
|
6459
|
+
return await manager.get();
|
|
6460
|
+
}
|
|
6461
|
+
catch {
|
|
6462
|
+
return undefined;
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6262
6465
|
/**
|
|
6263
6466
|
* Checks if all required gates are enabled for CSRF token interceptor.
|
|
6264
6467
|
*
|
|
@@ -6301,7 +6504,6 @@ function isCsrfMethod(method) {
|
|
|
6301
6504
|
* @returns A RequestInterceptor function for FetchParameters
|
|
6302
6505
|
*/
|
|
6303
6506
|
function buildCsrfTokenInterceptor() {
|
|
6304
|
-
const csrfTokenManager = CsrfTokenManager.getInstance();
|
|
6305
6507
|
return async (fetchArgs) => {
|
|
6306
6508
|
// Check if all required gates are enabled before running
|
|
6307
6509
|
if (!areCsrfGatesEnabled()) {
|
|
@@ -6318,7 +6520,7 @@ function buildCsrfTokenInterceptor() {
|
|
|
6318
6520
|
}
|
|
6319
6521
|
// Only add CSRF token for mutating operations
|
|
6320
6522
|
if (isCsrfMethod(method)) {
|
|
6321
|
-
const token = await
|
|
6523
|
+
const token = await getCsrfToken();
|
|
6322
6524
|
if (token) {
|
|
6323
6525
|
// eslint-disable-next-line no-param-reassign
|
|
6324
6526
|
fetchArgs = setHeader(CSRF_TOKEN_HEADER, token, fetchArgs);
|
|
@@ -6335,7 +6537,6 @@ function buildCsrfTokenInterceptor() {
|
|
|
6335
6537
|
* @returns A request interceptor function for Luvio ResourceRequest objects
|
|
6336
6538
|
*/
|
|
6337
6539
|
function buildLuvioCsrfTokenInterceptor() {
|
|
6338
|
-
const csrfTokenManager = CsrfTokenManager.getInstance();
|
|
6339
6540
|
return async (resourceRequest) => {
|
|
6340
6541
|
// Check if all required gates are enabled before running
|
|
6341
6542
|
if (!areCsrfGatesEnabled()) {
|
|
@@ -6349,7 +6550,7 @@ function buildLuvioCsrfTokenInterceptor() {
|
|
|
6349
6550
|
if (isCsrfMethod(resourceRequest.method)) {
|
|
6350
6551
|
// Don't overwrite existing CSRF token header if it already exists
|
|
6351
6552
|
if (!resourceRequest.headers[CSRF_TOKEN_HEADER]) {
|
|
6352
|
-
const token = await
|
|
6553
|
+
const token = await getCsrfToken();
|
|
6353
6554
|
if (token) {
|
|
6354
6555
|
resourceRequest.headers[CSRF_TOKEN_HEADER] = token;
|
|
6355
6556
|
}
|
|
@@ -6389,6 +6590,39 @@ function buildLuvioEntityEncodingInterceptor() {
|
|
|
6389
6590
|
};
|
|
6390
6591
|
}
|
|
6391
6592
|
|
|
6593
|
+
const FIRST_PARTY_HEADER = 'X-Salesforce-First-Party';
|
|
6594
|
+
const FIRST_PARTY_VALUE = 'platform-ui';
|
|
6595
|
+
/**
|
|
6596
|
+
* Builds a request interceptor that adds the LDS first party header to every
|
|
6597
|
+
* outbound request.
|
|
6598
|
+
*
|
|
6599
|
+
* @returns A RequestInterceptor function for FetchParameters
|
|
6600
|
+
*/
|
|
6601
|
+
function buildFirstPartyHeaderInterceptor() {
|
|
6602
|
+
return async (fetchArgs) => {
|
|
6603
|
+
const returnedFetchArgs = setHeader(FIRST_PARTY_HEADER, FIRST_PARTY_VALUE, fetchArgs);
|
|
6604
|
+
return resolvedPromiseLike$2(returnedFetchArgs);
|
|
6605
|
+
};
|
|
6606
|
+
}
|
|
6607
|
+
/**
|
|
6608
|
+
* Builds a Luvio request interceptor that adds the LDS first party header to
|
|
6609
|
+
* every outbound `ResourceRequest`. See {@link buildFirstPartyHeaderInterceptor} for
|
|
6610
|
+
* the header semantics.
|
|
6611
|
+
*
|
|
6612
|
+
* @returns A request interceptor for Luvio ResourceRequest objects
|
|
6613
|
+
*/
|
|
6614
|
+
function buildLuvioFirstPartyHeaderInterceptor() {
|
|
6615
|
+
return async (resourceRequest) => {
|
|
6616
|
+
if (!resourceRequest.headers) {
|
|
6617
|
+
resourceRequest.headers = {};
|
|
6618
|
+
}
|
|
6619
|
+
if (!resourceRequest.headers[FIRST_PARTY_HEADER]) {
|
|
6620
|
+
resourceRequest.headers[FIRST_PARTY_HEADER] = FIRST_PARTY_VALUE;
|
|
6621
|
+
}
|
|
6622
|
+
return resolvedPromiseLike$2(resourceRequest);
|
|
6623
|
+
};
|
|
6624
|
+
}
|
|
6625
|
+
|
|
6392
6626
|
function createInstrumentationIdContext() {
|
|
6393
6627
|
return () => ({
|
|
6394
6628
|
instrumentationId: generateRequestId(),
|
|
@@ -6618,7 +6852,6 @@ class LuvioCsrfTokenRetryPolicy extends RetryPolicy {
|
|
|
6618
6852
|
constructor(config = DEFAULT_CONFIG$3) {
|
|
6619
6853
|
super();
|
|
6620
6854
|
this.config = config;
|
|
6621
|
-
this.csrfTokenManager = CsrfTokenManager.getInstance();
|
|
6622
6855
|
}
|
|
6623
6856
|
setRequestContext(context) {
|
|
6624
6857
|
this.requestContext = context;
|
|
@@ -6640,11 +6873,20 @@ class LuvioCsrfTokenRetryPolicy extends RetryPolicy {
|
|
|
6640
6873
|
if (!this.requestContext) {
|
|
6641
6874
|
return;
|
|
6642
6875
|
}
|
|
6643
|
-
|
|
6876
|
+
let newToken;
|
|
6877
|
+
try {
|
|
6878
|
+
const manager = await getCsrfTokenManager();
|
|
6879
|
+
newToken = await manager.refresh();
|
|
6880
|
+
}
|
|
6881
|
+
catch {
|
|
6882
|
+
// Manager unavailable — drop the stale token below so the request
|
|
6883
|
+
// interceptor refetches on the retry.
|
|
6884
|
+
newToken = undefined;
|
|
6885
|
+
}
|
|
6644
6886
|
const req = this.requestContext.request;
|
|
6645
6887
|
const { [CSRF_TOKEN_HEADER]: _stale, ...remainingHeaders } = req.headers ?? {};
|
|
6646
6888
|
// If refresh failed, drop the stale token entirely so the request interceptor
|
|
6647
|
-
// will fetch a fresh one via
|
|
6889
|
+
// will fetch a fresh one via get() on the retry.
|
|
6648
6890
|
const headers = newToken
|
|
6649
6891
|
? { ...remainingHeaders, [CSRF_TOKEN_HEADER]: newToken }
|
|
6650
6892
|
: remainingHeaders;
|
|
@@ -7149,12 +7391,14 @@ const composedFetchNetworkAdapter = {
|
|
|
7149
7391
|
buildLuvioTransportMarksSendInterceptor(),
|
|
7150
7392
|
buildLuvioPageScopedCacheRequestInterceptor(),
|
|
7151
7393
|
buildLuvioCsrfTokenInterceptor(),
|
|
7394
|
+
buildLuvioFirstPartyHeaderInterceptor(),
|
|
7152
7395
|
buildLuvioEntityEncodingInterceptor(),
|
|
7153
7396
|
],
|
|
7154
7397
|
retry: buildLuvioFetchRetryInterceptor(),
|
|
7155
7398
|
response: [
|
|
7156
7399
|
buildLexRuntimeLuvio5xxStatusResponseInterceptor(),
|
|
7157
|
-
|
|
7400
|
+
buildLexRuntimeLuvioSessionExpirationResponseInterceptor(),
|
|
7401
|
+
buildLuvioErrorBodyNormalizationInterceptor(),
|
|
7158
7402
|
buildLuvioTransportMarksReceiveInterceptor(),
|
|
7159
7403
|
buildLuvioActionMarksReceiveInterceptor(),
|
|
7160
7404
|
],
|
|
@@ -7199,7 +7443,6 @@ class CsrfTokenRetryPolicy extends RetryPolicy {
|
|
|
7199
7443
|
constructor(config = DEFAULT_CONFIG$1) {
|
|
7200
7444
|
super();
|
|
7201
7445
|
this.config = config;
|
|
7202
|
-
this.csrfTokenManager = CsrfTokenManager.getInstance();
|
|
7203
7446
|
}
|
|
7204
7447
|
/**
|
|
7205
7448
|
* Allows the fetch service to pass mutable request context.
|
|
@@ -7251,7 +7494,15 @@ class CsrfTokenRetryPolicy extends RetryPolicy {
|
|
|
7251
7494
|
*/
|
|
7252
7495
|
async prepareRetry(_result, _context) {
|
|
7253
7496
|
// Refresh the CSRF token (we already know this is a CSRF error from shouldRetry)
|
|
7254
|
-
|
|
7497
|
+
let newToken;
|
|
7498
|
+
try {
|
|
7499
|
+
const manager = await getCsrfTokenManager();
|
|
7500
|
+
newToken = await manager.refresh();
|
|
7501
|
+
}
|
|
7502
|
+
catch {
|
|
7503
|
+
// Manager unavailable — the retry will fail again, which is expected.
|
|
7504
|
+
newToken = undefined;
|
|
7505
|
+
}
|
|
7255
7506
|
if (!newToken || !this.requestContext) {
|
|
7256
7507
|
// If we can't get a new token or don't have request context,
|
|
7257
7508
|
// the retry will fail again but that's expected
|
|
@@ -7312,9 +7563,106 @@ function buildCsrfRetryInterceptor() {
|
|
|
7312
7563
|
};
|
|
7313
7564
|
}
|
|
7314
7565
|
|
|
7566
|
+
/**
|
|
7567
|
+
* The context-seed key under which a custom command forwards its JWT mint
|
|
7568
|
+
* parameters. The framework's `buildServiceDescriptor` merges the request init's
|
|
7569
|
+
* `__contextSeed` onto the per-request interceptor context, so this interceptor
|
|
7570
|
+
* reads the params off `context[JWT_MINT_PARAMS_SEED_KEY]`.
|
|
7571
|
+
*
|
|
7572
|
+
* This is a cross-team contract: the custom command writes this key, this
|
|
7573
|
+
* interceptor reads it. It is intentionally an opaque key — OneStore does not
|
|
7574
|
+
* define it and never inspects the param shape; the typed shape lives in the
|
|
7575
|
+
* resolver.
|
|
7576
|
+
*/
|
|
7577
|
+
const JWT_MINT_PARAMS_SEED_KEY = 'jwtMintParams';
|
|
7578
|
+
/**
|
|
7579
|
+
* Returns `true` if the fetch arguments already carry an `Authorization` header,
|
|
7580
|
+
* across the three shapes the framework's `setHeader` handles: a `Request`
|
|
7581
|
+
* resource's own headers, an `options.headers` `Headers` instance, or a plain
|
|
7582
|
+
* record. The descriptor's guarded legacy interceptor uses this to skip when the
|
|
7583
|
+
* parameterized interceptor has already authorized the request — avoiding a second
|
|
7584
|
+
* mint and the throw `setHeaderAuthorization` raises on an existing header.
|
|
7585
|
+
*/
|
|
7586
|
+
function hasAuthorizationHeader([resource, options]) {
|
|
7587
|
+
if (resource instanceof Request && resource.headers.has('Authorization')) {
|
|
7588
|
+
return true;
|
|
7589
|
+
}
|
|
7590
|
+
const headers = options?.headers;
|
|
7591
|
+
if (headers === undefined) {
|
|
7592
|
+
return false;
|
|
7593
|
+
}
|
|
7594
|
+
if (headers instanceof Headers) {
|
|
7595
|
+
return headers.has('Authorization');
|
|
7596
|
+
}
|
|
7597
|
+
if (Array.isArray(headers)) {
|
|
7598
|
+
return headers.some(([name]) => name.toLowerCase() === 'authorization');
|
|
7599
|
+
}
|
|
7600
|
+
return Reflect.has(headers, 'Authorization');
|
|
7601
|
+
}
|
|
7602
|
+
/**
|
|
7603
|
+
* Builds the request interceptor that bridges the per-request context seed to the
|
|
7604
|
+
* dispatching SFAP JwtManager for the **parameterized** mint path.
|
|
7605
|
+
*
|
|
7606
|
+
* For a parameterized SFAP command, the context carries `jwtMintParams`. This
|
|
7607
|
+
* interceptor reads them, calls `jwtManager.getJwt(params)` (which the dispatching
|
|
7608
|
+
* resolver routes to the parameterized resolver), attaches the `Authorization`
|
|
7609
|
+
* header, and rewrites the request URL via the minted `baseUri`. The presence of
|
|
7610
|
+
* that `Authorization` header is the signal the descriptor's guarded legacy
|
|
7611
|
+
* interceptor uses to skip — so the legacy path does not mint a second time.
|
|
7612
|
+
*
|
|
7613
|
+
* For a legacy parameterless command the context carries no `jwtMintParams`, so
|
|
7614
|
+
* this interceptor **early-returns on its first line** and the request flows
|
|
7615
|
+
* unchanged to the legacy `buildJwtRequestHeaderInterceptor` — guaranteeing zero
|
|
7616
|
+
* behavior change for non-opted-in adapters.
|
|
7617
|
+
*
|
|
7618
|
+
* Lives in `lds-lightning-platform` (not OneStore) beside the existing SFAP/CSRF
|
|
7619
|
+
* interceptors, per the JWT-parameterization ADR §5: OneStore provides only the
|
|
7620
|
+
* generic interceptor mechanism and the opaque context-seed channel; the service-
|
|
7621
|
+
* specific bridge is a runtime-layer concern.
|
|
7622
|
+
*
|
|
7623
|
+
* @param jwtManager - the dispatching SFAP JwtManager
|
|
7624
|
+
* @param jwtRequestModifier - applies the minted `extraInfo.baseUri` to the request URL
|
|
7625
|
+
*/
|
|
7626
|
+
function buildJwtParameterizationInterceptor(jwtManager, jwtRequestModifier = (_extraInfo, fetchArgs) => fetchArgs) {
|
|
7627
|
+
return (fetchArgs, context) => {
|
|
7628
|
+
const jwtContext = context;
|
|
7629
|
+
const mintParams = jwtContext?.[JWT_MINT_PARAMS_SEED_KEY];
|
|
7630
|
+
// No mint params → not a parameterized request. Do nothing; the legacy
|
|
7631
|
+
// interceptor handles it. This MUST be the first statement so non-opted-in
|
|
7632
|
+
// SFAP commands observe no work at all.
|
|
7633
|
+
if (mintParams === undefined) {
|
|
7634
|
+
return resolvedPromiseLike$2(fetchArgs);
|
|
7635
|
+
}
|
|
7636
|
+
return resolvedPromiseLike$2(jwtManager.getJwt(mintParams)).then((token) => {
|
|
7637
|
+
const fetchArgsWithAuthorization = setHeaderAuthorization(token, fetchArgs);
|
|
7638
|
+
return token.extraInfo
|
|
7639
|
+
? jwtRequestModifier(token.extraInfo, fetchArgsWithAuthorization)
|
|
7640
|
+
: fetchArgsWithAuthorization;
|
|
7641
|
+
});
|
|
7642
|
+
};
|
|
7643
|
+
}
|
|
7644
|
+
|
|
7315
7645
|
const SFAP_BASE_URL = 'api.salesforce.com';
|
|
7646
|
+
function buildDispatchingSfapJwtResolver(legacyResolver, parameterizedResolver) {
|
|
7647
|
+
return {
|
|
7648
|
+
getJwt(params) {
|
|
7649
|
+
if (params === undefined) {
|
|
7650
|
+
return legacyResolver.getJwt();
|
|
7651
|
+
}
|
|
7652
|
+
return parameterizedResolver.getJwt(params);
|
|
7653
|
+
},
|
|
7654
|
+
};
|
|
7655
|
+
}
|
|
7656
|
+
// The parameterized mint is dispatched over **Aura transport**:
|
|
7657
|
+
// the resolver calls the SFAP Lightning JWT Service's auto-generated Aura method
|
|
7658
|
+
// `SalesforceApiPlatformController.postSFAPLightningJwtService`, so session + CSRF
|
|
7659
|
+
// are handled inside the Aura stack. This mirrors the legacy parameterless
|
|
7660
|
+
// `platformSfapJwtResolver`, which already mints over Aura. The minted JWT is then
|
|
7661
|
+
// attached as a Bearer token on the downstream SFAP API request (which stays HTTP).
|
|
7662
|
+
const parameterizedSfapJwtResolver = new ParameterizedSfapJwtAuraResolver();
|
|
7663
|
+
const sfapJwtResolver = buildDispatchingSfapJwtResolver(platformSfapJwtResolver, parameterizedSfapJwtResolver);
|
|
7316
7664
|
const sfapJwtRepository = new JwtRepository();
|
|
7317
|
-
const sfapJwtManager = new JwtManager(sfapJwtRepository,
|
|
7665
|
+
const sfapJwtManager = new JwtManager(sfapJwtRepository, sfapJwtResolver);
|
|
7318
7666
|
function prefetchSfapJwt() {
|
|
7319
7667
|
const maybePromise = sfapJwtManager.getJwt();
|
|
7320
7668
|
if ('then' in maybePromise) {
|
|
@@ -7325,8 +7673,12 @@ function prefetchSfapJwt() {
|
|
|
7325
7673
|
function buildJwtAuthorizedSfapFetchServiceDescriptor(logger) {
|
|
7326
7674
|
const jwtAuthorizedFetchService = buildServiceDescriptor$2({
|
|
7327
7675
|
createContext: createInstrumentationIdContext(),
|
|
7328
|
-
request: [
|
|
7329
|
-
|
|
7676
|
+
request: [
|
|
7677
|
+
buildThirdPartyTrackerRegisterInterceptor(),
|
|
7678
|
+
buildJwtParameterizationInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger)),
|
|
7679
|
+
// Guarded so it is skipped once the parameterized interceptor handled the request.
|
|
7680
|
+
buildGuardedLegacyJwtRequestInterceptor(logger),
|
|
7681
|
+
],
|
|
7330
7682
|
finally: [buildThirdPartyTrackerFinishInterceptor()],
|
|
7331
7683
|
});
|
|
7332
7684
|
return {
|
|
@@ -7406,8 +7758,13 @@ function buildUnauthorizedFetchServiceDescriptor() {
|
|
|
7406
7758
|
tags: { authenticationScopes: '' },
|
|
7407
7759
|
};
|
|
7408
7760
|
}
|
|
7409
|
-
|
|
7410
|
-
|
|
7761
|
+
/**
|
|
7762
|
+
* The `JwtRequestModifier` shared by both the legacy and parameterized SFAP
|
|
7763
|
+
* interceptors: it rewrites the request URL's host/protocol to the minted
|
|
7764
|
+
* `extraInfo.baseUri` (only for `api.salesforce.com` resources).
|
|
7765
|
+
*/
|
|
7766
|
+
function buildSfapJwtRequestModifier(logger) {
|
|
7767
|
+
return ({ baseUri }, [resource, request]) => {
|
|
7411
7768
|
if (typeof resource !== 'string' && !(resource instanceof URL)) {
|
|
7412
7769
|
// istanbul ignore else: this will not be tested in NODE_ENV = production for test coverage
|
|
7413
7770
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -7425,8 +7782,22 @@ function buildJwtRequestInterceptor(logger) {
|
|
|
7425
7782
|
url.protocol = overrideUrl.protocol;
|
|
7426
7783
|
return [url, request];
|
|
7427
7784
|
};
|
|
7428
|
-
|
|
7429
|
-
|
|
7785
|
+
}
|
|
7786
|
+
function buildJwtRequestInterceptor(logger) {
|
|
7787
|
+
return buildJwtRequestHeaderInterceptor(sfapJwtManager, buildSfapJwtRequestModifier(logger));
|
|
7788
|
+
}
|
|
7789
|
+
/**
|
|
7790
|
+
* Wraps the legacy `buildJwtRequestHeaderInterceptor` with a pass-through guard:
|
|
7791
|
+
* when the parameterized interceptor has already authorized the request (an
|
|
7792
|
+
* `Authorization` header is present), this returns `args` untouched so the legacy
|
|
7793
|
+
* interceptor does not mint a second time or attempt to set a duplicate
|
|
7794
|
+
* Authorization header (which `setHeaderAuthorization` throws on). For every legacy
|
|
7795
|
+
* (non-parameterized) request no Authorization header is present yet, so the legacy
|
|
7796
|
+
* interceptor runs exactly as before — a strict pass-through.
|
|
7797
|
+
*/
|
|
7798
|
+
function buildGuardedLegacyJwtRequestInterceptor(logger) {
|
|
7799
|
+
const legacyInterceptor = buildJwtRequestInterceptor(logger);
|
|
7800
|
+
return (args) => hasAuthorizationHeader(args) ? resolvedPromiseLike$2(args) : legacyInterceptor(args);
|
|
7430
7801
|
}
|
|
7431
7802
|
|
|
7432
7803
|
const PDL_EXECUTE_ASYNC_OPTIONS = {
|
|
@@ -10084,11 +10455,12 @@ function getLexRuntimeDefaultInterceptorConfig(logger) {
|
|
|
10084
10455
|
buildTransportMarksSendInterceptor(),
|
|
10085
10456
|
buildCsrfTokenInterceptor(),
|
|
10086
10457
|
buildEntityEncodingInterceptor(),
|
|
10458
|
+
buildFirstPartyHeaderInterceptor(),
|
|
10087
10459
|
],
|
|
10088
10460
|
retry: buildCsrfRetryInterceptor(),
|
|
10089
10461
|
response: [
|
|
10090
10462
|
buildLexRuntime5xxStatusResponseInterceptor(logger),
|
|
10091
|
-
|
|
10463
|
+
buildLexRuntimeSessionExpirationResponseInterceptor(logger),
|
|
10092
10464
|
buildTransportMarksReceiveInterceptor(),
|
|
10093
10465
|
buildActionMarksReceiveInterceptor(),
|
|
10094
10466
|
],
|
|
@@ -10105,7 +10477,7 @@ function buildLexRuntimeAllow5xxFetchServiceDescriptor(logger, retryService) {
|
|
|
10105
10477
|
...config,
|
|
10106
10478
|
// Omit 5xx interceptor - allow 5xx responses to pass through
|
|
10107
10479
|
response: [
|
|
10108
|
-
|
|
10480
|
+
buildLexRuntimeSessionExpirationResponseInterceptor(logger),
|
|
10109
10481
|
buildTransportMarksReceiveInterceptor(),
|
|
10110
10482
|
buildActionMarksReceiveInterceptor(),
|
|
10111
10483
|
],
|
|
@@ -10195,6 +10567,149 @@ class FetchThrottlingRetryPolicy extends RetryPolicy {
|
|
|
10195
10567
|
}
|
|
10196
10568
|
}
|
|
10197
10569
|
|
|
10570
|
+
const CSRF_TOKEN_KEY = 'salesforce_csrf_token';
|
|
10571
|
+
const CSRF_STORAGE_NAME = 'ldsCSRFToken';
|
|
10572
|
+
const BASE_URI = '/services/data/v68.0';
|
|
10573
|
+
const UI_API_BASE_URI = `${BASE_URI}/ui-api`;
|
|
10574
|
+
const CSRF_TOKEN_ENDPOINT = `${UI_API_BASE_URI}/session/csrf`;
|
|
10575
|
+
const CSRF_STORAGE_CONFIG = {
|
|
10576
|
+
name: CSRF_STORAGE_NAME,
|
|
10577
|
+
persistent: true,
|
|
10578
|
+
secure: true,
|
|
10579
|
+
maxSize: 1024,
|
|
10580
|
+
expiration: 24 * 60 * 60,
|
|
10581
|
+
clearOnInit: false,
|
|
10582
|
+
debugLogging: false,
|
|
10583
|
+
};
|
|
10584
|
+
/**
|
|
10585
|
+
* Reads the CSRF token Aura preloads onto the bootstrap payload. When the
|
|
10586
|
+
* Connect Framework CSRF token is minted at template-render time it is exposed
|
|
10587
|
+
* via `$A.clientService.getConnectCsrfToken()`. Reading it here lets the first
|
|
10588
|
+
* state-changing request skip the cold `/ui-api/session/csrf` round trip.
|
|
10589
|
+
*
|
|
10590
|
+
* Defensive against a missing getter or server killswitch: returns `undefined`,
|
|
10591
|
+
* never throws.
|
|
10592
|
+
*/
|
|
10593
|
+
function readPreloadedToken() {
|
|
10594
|
+
const clientService = getAuraClientService();
|
|
10595
|
+
if (typeof clientService?.getConnectCsrfToken !== 'function') {
|
|
10596
|
+
return undefined;
|
|
10597
|
+
}
|
|
10598
|
+
try {
|
|
10599
|
+
const token = clientService.getConnectCsrfToken();
|
|
10600
|
+
return typeof token === 'string' && token ? token : undefined;
|
|
10601
|
+
}
|
|
10602
|
+
catch {
|
|
10603
|
+
return undefined;
|
|
10604
|
+
}
|
|
10605
|
+
}
|
|
10606
|
+
/**
|
|
10607
|
+
* Fetches a fresh CSRF token from the server. Resolves `undefined` (not a
|
|
10608
|
+
* rejection) on any non-ok response, missing token, or network/parse error so
|
|
10609
|
+
* the manager treats it as "no value" and leaves any cached token intact. The
|
|
10610
|
+
* CSRF retry policy — not this fetch — owns retry semantics.
|
|
10611
|
+
*/
|
|
10612
|
+
async function fetchFreshToken() {
|
|
10613
|
+
try {
|
|
10614
|
+
const response = await fetch(CSRF_TOKEN_ENDPOINT, {
|
|
10615
|
+
method: 'GET',
|
|
10616
|
+
credentials: 'same-origin',
|
|
10617
|
+
});
|
|
10618
|
+
if (!response.ok) {
|
|
10619
|
+
return undefined;
|
|
10620
|
+
}
|
|
10621
|
+
const data = await response.json();
|
|
10622
|
+
const token = data.csrfToken;
|
|
10623
|
+
return typeof token === 'string' && token ? token : undefined;
|
|
10624
|
+
}
|
|
10625
|
+
catch {
|
|
10626
|
+
return undefined;
|
|
10627
|
+
}
|
|
10628
|
+
}
|
|
10629
|
+
/**
|
|
10630
|
+
* Adapts the durable AuraStorage backing store to the {@link ResourceStorage}
|
|
10631
|
+
* contract the renewable-resource manager expects.
|
|
10632
|
+
*
|
|
10633
|
+
* The Aura-preloaded token is written into the store **once at creation** (only
|
|
10634
|
+
* when the store is otherwise empty), so the first `get()` finds it and the
|
|
10635
|
+
* first mutation skips the network. Seeding the store — rather than having
|
|
10636
|
+
* `get()` fall back to the preload on every empty read — means a later
|
|
10637
|
+
* `clear()` (logout / org switch) is not silently resurrected by the preload.
|
|
10638
|
+
*
|
|
10639
|
+
* Every operation awaits the one-time seed so reads and writes observe a
|
|
10640
|
+
* consistent store. Storage read/write failures are non-fatal.
|
|
10641
|
+
*/
|
|
10642
|
+
function buildCsrfResourceStorage() {
|
|
10643
|
+
const storage = createStorage(CSRF_STORAGE_CONFIG);
|
|
10644
|
+
// Pre-seed the preloaded token into the store, once, if the store is empty.
|
|
10645
|
+
const seeded = (async () => {
|
|
10646
|
+
const preloadedToken = readPreloadedToken();
|
|
10647
|
+
if (!storage || !preloadedToken) {
|
|
10648
|
+
return;
|
|
10649
|
+
}
|
|
10650
|
+
try {
|
|
10651
|
+
const existing = await storage.get(CSRF_TOKEN_KEY);
|
|
10652
|
+
if (typeof existing !== 'string' || !existing) {
|
|
10653
|
+
await storage.set(CSRF_TOKEN_KEY, preloadedToken);
|
|
10654
|
+
}
|
|
10655
|
+
}
|
|
10656
|
+
catch {
|
|
10657
|
+
// Non-fatal: fall back to fetching a fresh token on first use.
|
|
10658
|
+
}
|
|
10659
|
+
})();
|
|
10660
|
+
return {
|
|
10661
|
+
get: async () => {
|
|
10662
|
+
await seeded;
|
|
10663
|
+
if (storage) {
|
|
10664
|
+
try {
|
|
10665
|
+
const cached = await storage.get(CSRF_TOKEN_KEY);
|
|
10666
|
+
if (typeof cached === 'string' && cached) {
|
|
10667
|
+
return cached;
|
|
10668
|
+
}
|
|
10669
|
+
}
|
|
10670
|
+
catch {
|
|
10671
|
+
// Non-fatal: treat as a cache miss so the manager fetches.
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10674
|
+
return undefined;
|
|
10675
|
+
},
|
|
10676
|
+
set: async (value) => {
|
|
10677
|
+
await seeded;
|
|
10678
|
+
if (storage) {
|
|
10679
|
+
try {
|
|
10680
|
+
await storage.set(CSRF_TOKEN_KEY, value);
|
|
10681
|
+
}
|
|
10682
|
+
catch {
|
|
10683
|
+
// Non-fatal: token is still usable even if caching fails.
|
|
10684
|
+
}
|
|
10685
|
+
}
|
|
10686
|
+
},
|
|
10687
|
+
clear: async () => {
|
|
10688
|
+
await seeded;
|
|
10689
|
+
if (storage) {
|
|
10690
|
+
try {
|
|
10691
|
+
await storage.remove(CSRF_TOKEN_KEY);
|
|
10692
|
+
}
|
|
10693
|
+
catch {
|
|
10694
|
+
// Non-fatal: continue even if the clear fails.
|
|
10695
|
+
}
|
|
10696
|
+
}
|
|
10697
|
+
},
|
|
10698
|
+
};
|
|
10699
|
+
}
|
|
10700
|
+
/**
|
|
10701
|
+
* Builds the CSRF token manager: a {@link BasicRenewableResourceManager} that
|
|
10702
|
+
* lazily caches the token in durable storage, coalesces concurrent fetches onto
|
|
10703
|
+
* a single in-flight request, and dedups refresh storms. Registered as a
|
|
10704
|
+
* provisioner service (`csrfTokenManager`) by `initializeOneStore`.
|
|
10705
|
+
*/
|
|
10706
|
+
function buildCsrfTokenManager() {
|
|
10707
|
+
return new BasicRenewableResourceManager({
|
|
10708
|
+
fetch: fetchFreshToken,
|
|
10709
|
+
storage: buildCsrfResourceStorage(),
|
|
10710
|
+
});
|
|
10711
|
+
}
|
|
10712
|
+
|
|
10198
10713
|
/* eslint-disable no-console */
|
|
10199
10714
|
/**
|
|
10200
10715
|
* Default storage configuration for the durable cache
|
|
@@ -10789,6 +11304,7 @@ function initializeOneStore(luvio) {
|
|
|
10789
11304
|
const retryPolicy = new ComposedRetryPolicy([throttlingPolicy, csrfPolicy]);
|
|
10790
11305
|
const retryServiceDescriptor = buildServiceDescriptor$8(retryPolicy);
|
|
10791
11306
|
const retryService = retryServiceDescriptor.service;
|
|
11307
|
+
const csrfTokenManagerServiceDescriptor = buildRenewableResourceManagerDescriptor(CSRF_TOKEN_MANAGER_SERVICE, buildCsrfTokenManager());
|
|
10792
11308
|
const prefetchSfapJwtServiceDescriptor = {
|
|
10793
11309
|
type: 'prefetchSfapJwt',
|
|
10794
11310
|
version: '1.0',
|
|
@@ -10836,6 +11352,7 @@ function initializeOneStore(luvio) {
|
|
|
10836
11352
|
buildLWCGraphQLWireBindingsServiceDescriptor(),
|
|
10837
11353
|
configServiceDescriptor,
|
|
10838
11354
|
prefetchSfapJwtServiceDescriptor,
|
|
11355
|
+
csrfTokenManagerServiceDescriptor,
|
|
10839
11356
|
];
|
|
10840
11357
|
setServices(services);
|
|
10841
11358
|
}
|
|
@@ -10855,4 +11372,4 @@ function ldsEngineCreator() {
|
|
|
10855
11372
|
}
|
|
10856
11373
|
|
|
10857
11374
|
export { LexRequestStrategy, PdlPrefetcherEventType, PdlRequestPriority, buildPredictorForContext, configService, ldsEngineCreator as default, initializeLDS, initializeOneStore, notifyUpdateAvailableFactory, registerRequestStrategy, saveRequestAsPrediction, subscribeToPrefetcherEvents, unregisterRequestStrategy, whenPredictionsReady };
|
|
10858
|
-
// version: 1.
|
|
11375
|
+
// version: 1.444.0-a7f42f9edf
|