@salesforce/lds-runtime-aura 1.287.0-dev14 → 1.287.0-dev16
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 +184 -30
- package/dist/types/predictive-loading/common/index.d.ts +6 -0
- package/dist/types/predictive-loading/prefetcher/lex-predictive-prefetcher.d.ts +4 -2
- package/dist/types/predictive-loading/prefetcher/predictive-prefetcher.d.ts +9 -4
- package/dist/types/predictive-loading/prefetcher/utils.d.ts +16 -0
- package/dist/types/predictive-loading/repository/prefetch-repository.d.ts +4 -7
- package/dist/types/predictive-loading/request-runner/lex-request-runner.d.ts +2 -1
- package/dist/types/predictive-loading/request-runner/request-runner.d.ts +2 -1
- package/dist/types/predictive-loading/request-strategy/get-object-info-request-strategy.d.ts +2 -1
- package/dist/types/predictive-loading/request-strategy/get-object-infos-request-strategy.d.ts +10 -0
- package/dist/types/predictive-loading/request-strategy/get-related-list-records-request-strategy.d.ts +2 -1
- package/dist/types/predictive-loading/request-strategy/luvio-adapter-request-strategy.d.ts +3 -2
- package/dist/types/predictive-loading/request-strategy/request-strategy.d.ts +2 -1
- package/package.json +13 -13
package/dist/ldsEngineCreator.js
CHANGED
|
@@ -16,6 +16,7 @@ import { HttpStatusCode, InMemoryStore as InMemoryStore$1, Environment, Luvio, I
|
|
|
16
16
|
import ldsTrackedFieldsBehaviorGate from '@salesforce/gate/lds.useNewTrackedFieldBehavior';
|
|
17
17
|
import usePredictiveLoading from '@salesforce/gate/lds.usePredictiveLoading';
|
|
18
18
|
import useRelatedListsPredictions from '@salesforce/gate/lds.pdl.useRelatedListsPredictions';
|
|
19
|
+
import applyPredictionRequestLimit from '@salesforce/gate/lds.pdl.applyRequestLimit';
|
|
19
20
|
import { instrument, getRecordAvatarsAdapterFactory, getRecordAdapterFactory, coerceFieldIdArray, getRecordsAdapterFactory, getRecordActionsAdapterFactory, getObjectInfosAdapterFactory, coerceObjectIdArray, getObjectInfoAdapterFactory, coerceObjectId, getRelatedListsActionsAdapterFactory, getRelatedListInfoBatchAdapterFactory, getRelatedListRecordsBatchAdapterFactory, getRelatedListRecordsAdapterFactory, configuration, InMemoryRecordRepresentationQueryEvaluator, UiApiNamespace, RecordRepresentationRepresentationType, registerPrefetcher } from 'force/ldsAdaptersUiapi';
|
|
20
21
|
import { withRegistration as withRegistration$1 } from 'force/luvioRegistry';
|
|
21
22
|
import oneStoreEnabled from '@salesforce/gate/lds.oneStoreEnabled.ltng';
|
|
@@ -1359,9 +1360,10 @@ function logCRUDLightningInteraction(eventSource, attributes) {
|
|
|
1359
1360
|
const instrumentation = new Instrumentation();
|
|
1360
1361
|
|
|
1361
1362
|
class ApplicationPredictivePrefetcher {
|
|
1362
|
-
constructor(context, repository, requestRunner) {
|
|
1363
|
+
constructor(context, repository, requestRunner, options) {
|
|
1363
1364
|
this.repository = repository;
|
|
1364
1365
|
this.requestRunner = requestRunner;
|
|
1366
|
+
this.options = options;
|
|
1365
1367
|
this.isRecording = false;
|
|
1366
1368
|
this.queuedPredictionRequests = [];
|
|
1367
1369
|
this._context = context;
|
|
@@ -1380,6 +1382,7 @@ class ApplicationPredictivePrefetcher {
|
|
|
1380
1382
|
}
|
|
1381
1383
|
startRecording() {
|
|
1382
1384
|
this.isRecording = true;
|
|
1385
|
+
this.repository.markPageStart();
|
|
1383
1386
|
this.repository.clearRequestBuffer();
|
|
1384
1387
|
}
|
|
1385
1388
|
saveRequest(request) {
|
|
@@ -1394,17 +1397,13 @@ class ApplicationPredictivePrefetcher {
|
|
|
1394
1397
|
}, PDL_EXECUTE_ASYNC_OPTIONS);
|
|
1395
1398
|
}
|
|
1396
1399
|
async predict() {
|
|
1397
|
-
const exactPageRequests = (await this.repository.getPageRequests(this.context)) || [];
|
|
1398
|
-
const similarPageRequests = await this.getSimilarPageRequests();
|
|
1399
1400
|
const alwaysRequests = this.page.getAlwaysRunRequests();
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
]),
|
|
1407
|
-
];
|
|
1401
|
+
const similarPageRequests = await this.getSimilarPageRequests();
|
|
1402
|
+
const exactPageRequests = (await this.repository.getPageRequests(this.context)) || [];
|
|
1403
|
+
const reducedRequests = this.requestRunner
|
|
1404
|
+
.reduceRequests([...exactPageRequests, ...similarPageRequests])
|
|
1405
|
+
.map((entry) => entry.request);
|
|
1406
|
+
const predictedRequests = [...alwaysRequests, ...reducedRequests];
|
|
1408
1407
|
this.queuedPredictionRequests.push(...predictedRequests);
|
|
1409
1408
|
return Promise.all(predictedRequests.map((request) => this.requestRunner.runRequest(request))).then();
|
|
1410
1409
|
}
|
|
@@ -1424,19 +1423,76 @@ class ApplicationPredictivePrefetcher {
|
|
|
1424
1423
|
if (this.page.similarContext !== undefined) {
|
|
1425
1424
|
const similarPageRequests = this.repository.getPageRequests(this.page.similarContext);
|
|
1426
1425
|
if (similarPageRequests !== undefined) {
|
|
1427
|
-
resolvedSimilarPageRequests = similarPageRequests.map((
|
|
1426
|
+
resolvedSimilarPageRequests = similarPageRequests.map((entry) => {
|
|
1427
|
+
return {
|
|
1428
|
+
...entry,
|
|
1429
|
+
request: this.page.resolveSimilarRequest(entry.request),
|
|
1430
|
+
};
|
|
1431
|
+
});
|
|
1428
1432
|
}
|
|
1429
1433
|
}
|
|
1430
1434
|
return resolvedSimilarPageRequests;
|
|
1431
1435
|
}
|
|
1432
1436
|
}
|
|
1433
1437
|
|
|
1438
|
+
/**
|
|
1439
|
+
* Runs a list of requests with a specified concurrency limit.
|
|
1440
|
+
*
|
|
1441
|
+
* @template Request - The type of the requests being processed.
|
|
1442
|
+
* @param {RequestEntry<Request>[]} requests - An array of request entries to be processed in the array order.
|
|
1443
|
+
* @param {RequestRunner<Request>} runner - The runner instance responsible for executing the requests.
|
|
1444
|
+
* @param {number} concurrentRequestsLimit - The maximum number of concurrent requests allowed.
|
|
1445
|
+
* @param {number} pageStartTime - The start time of the page load, used to calculate the time elapsed since the page starts loading.
|
|
1446
|
+
* @returns {Promise<void>} A promise that resolves when all requests have been processed.
|
|
1447
|
+
*
|
|
1448
|
+
* This function manages a queue of pending requests and processes them with a concurrency limit.
|
|
1449
|
+
* Requests are only processed if their `requestTime` is less than the time elapsed since `pageStartTime`.
|
|
1450
|
+
*/
|
|
1451
|
+
async function runRequestsWithLimit(requests, runner, concurrentRequestsLimit, pageStartTime) {
|
|
1452
|
+
// queue for pending prediction requests
|
|
1453
|
+
const requestQueue = [...requests];
|
|
1454
|
+
// Function to process the next request in the queue
|
|
1455
|
+
const processNextRequest = async (verifyPastTime = true) => {
|
|
1456
|
+
const timeInWaterfall = Date.now() - pageStartTime;
|
|
1457
|
+
while (requestQueue.length > 0 &&
|
|
1458
|
+
verifyPastTime &&
|
|
1459
|
+
requestQueue[0].requestMetadata.requestTime <= timeInWaterfall) {
|
|
1460
|
+
requestQueue.shift();
|
|
1461
|
+
}
|
|
1462
|
+
if (requestQueue.length > 0) {
|
|
1463
|
+
// (!) requestQueue will always have at least one element ensured by the above check.
|
|
1464
|
+
const nextRequest = requestQueue.shift();
|
|
1465
|
+
try {
|
|
1466
|
+
// Run the request and wait for it to complete
|
|
1467
|
+
await runner.runRequest(nextRequest.request);
|
|
1468
|
+
}
|
|
1469
|
+
finally {
|
|
1470
|
+
await processNextRequest();
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
// Start processing requests up to concurrentRequestsLimit
|
|
1475
|
+
const initialRequests = Math.min(concurrentRequestsLimit, requestQueue.length);
|
|
1476
|
+
const promises = [];
|
|
1477
|
+
for (let i = 0; i < initialRequests; i++) {
|
|
1478
|
+
// Initial requests should always execute, without verifying if they are past due.
|
|
1479
|
+
// Reasoning:
|
|
1480
|
+
// It may be that one of the alwaysRequest (with 0 as start time) that is reduced
|
|
1481
|
+
// with the regular requests to make these to have 0 as the initial time in the waterfall.
|
|
1482
|
+
// Because predictions are behind an await (see W-16139321), it could be that when this code is evaluated
|
|
1483
|
+
// is already past time for the request.
|
|
1484
|
+
promises.push(processNextRequest(false));
|
|
1485
|
+
}
|
|
1486
|
+
// Wait for all initial requests to complete
|
|
1487
|
+
await Promise.all(promises);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1434
1490
|
class LexPredictivePrefetcher extends ApplicationPredictivePrefetcher {
|
|
1435
1491
|
constructor(context, repository, requestRunner,
|
|
1436
1492
|
// These strategies need to be in sync with the "predictiveDataLoadCapable" list
|
|
1437
1493
|
// from scripts/lds-uiapi-plugin.js
|
|
1438
|
-
requestStrategies) {
|
|
1439
|
-
super(context, repository, requestRunner);
|
|
1494
|
+
requestStrategies, options) {
|
|
1495
|
+
super(context, repository, requestRunner, options);
|
|
1440
1496
|
this.requestStrategies = requestStrategies;
|
|
1441
1497
|
this.page = this.getPage();
|
|
1442
1498
|
}
|
|
@@ -1446,6 +1502,35 @@ class LexPredictivePrefetcher extends ApplicationPredictivePrefetcher {
|
|
|
1446
1502
|
}
|
|
1447
1503
|
return new LexDefaultPage(this.context);
|
|
1448
1504
|
}
|
|
1505
|
+
async predict() {
|
|
1506
|
+
const alwaysRequests = this.page.getAlwaysRunRequests();
|
|
1507
|
+
// IMPORTANT: The `await` has no effect on this operation because `getSimilarPageRequests`
|
|
1508
|
+
// and `getExactPageRequest` are sync; however if removed, it will have a negative
|
|
1509
|
+
// effect when used with Aura Network: predictions will be boxcar'd, which likely will
|
|
1510
|
+
// result in a perf regression.
|
|
1511
|
+
const similarPageRequests = await this.getSimilarPageRequests();
|
|
1512
|
+
const exactPageRequests = (await this.repository.getPageRequests(this.context)) || [];
|
|
1513
|
+
const alwaysRequestEntries = alwaysRequests.map((request) => {
|
|
1514
|
+
return {
|
|
1515
|
+
request,
|
|
1516
|
+
requestMetadata: { requestTime: 0 }, // ensures always requests are executed, and executed first.
|
|
1517
|
+
};
|
|
1518
|
+
});
|
|
1519
|
+
const reducedPredictions = this.requestRunner.reduceRequests([
|
|
1520
|
+
...exactPageRequests,
|
|
1521
|
+
...similarPageRequests,
|
|
1522
|
+
...alwaysRequestEntries,
|
|
1523
|
+
]); // In future - remove alwaysRequestEntries when on OneStore
|
|
1524
|
+
// Always requests must go by themself - Some of them are essential to keep the page rendering at the beginning.
|
|
1525
|
+
const predictedRequestsWithLimit = [...alwaysRequestEntries, ...reducedPredictions].sort((a, b) => a.requestMetadata.requestTime - b.requestMetadata.requestTime); // In future - choose sort algorithm via Gate?;
|
|
1526
|
+
await runRequestsWithLimit(predictedRequestsWithLimit, this.requestRunner, this.options.inflightRequestLimit,
|
|
1527
|
+
// `this.repository.pageStartTime` would be the correct here, but because
|
|
1528
|
+
// the await hack (see W-16139321), there's some delta by the time this executed,
|
|
1529
|
+
// on the other hand, when W-16139321 is fixed,
|
|
1530
|
+
// when doing predict+watch, it could be (set in watch) repository.startTime is not set yet,
|
|
1531
|
+
// Date.now() is a better alternative, that is correct, and works on both cases.
|
|
1532
|
+
Date.now());
|
|
1533
|
+
}
|
|
1449
1534
|
}
|
|
1450
1535
|
|
|
1451
1536
|
// Copy-pasted from adapter-utils. This util should be extracted from generated code and imported in prefetch repository.
|
|
@@ -1542,10 +1627,14 @@ class PrefetchRepository {
|
|
|
1542
1627
|
constructor(storage) {
|
|
1543
1628
|
this.storage = storage;
|
|
1544
1629
|
this.requestBuffer = new Map();
|
|
1630
|
+
this.pageStartTime = Date.now();
|
|
1545
1631
|
}
|
|
1546
1632
|
clearRequestBuffer() {
|
|
1547
1633
|
this.requestBuffer.clear();
|
|
1548
1634
|
}
|
|
1635
|
+
markPageStart() {
|
|
1636
|
+
this.pageStartTime = Date.now();
|
|
1637
|
+
}
|
|
1549
1638
|
async flushRequestsToStorage() {
|
|
1550
1639
|
const setPromises = [];
|
|
1551
1640
|
for (const [id, batch] of this.requestBuffer) {
|
|
@@ -1577,7 +1666,7 @@ class PrefetchRepository {
|
|
|
1577
1666
|
const batchForKey = this.requestBuffer.get(identifier) || [];
|
|
1578
1667
|
batchForKey.push({
|
|
1579
1668
|
request,
|
|
1580
|
-
requestTime: Date.now(),
|
|
1669
|
+
requestTime: Date.now() - this.pageStartTime,
|
|
1581
1670
|
});
|
|
1582
1671
|
this.requestBuffer.set(identifier, batchForKey);
|
|
1583
1672
|
}
|
|
@@ -1590,7 +1679,7 @@ class PrefetchRepository {
|
|
|
1590
1679
|
if (page === undefined) {
|
|
1591
1680
|
return [];
|
|
1592
1681
|
}
|
|
1593
|
-
return page.requests
|
|
1682
|
+
return page.requests;
|
|
1594
1683
|
}
|
|
1595
1684
|
}
|
|
1596
1685
|
|
|
@@ -1623,7 +1712,7 @@ class LuvioAdapterRequestStrategy extends RequestStrategy {
|
|
|
1623
1712
|
* @returns
|
|
1624
1713
|
*/
|
|
1625
1714
|
filterRequests(unfilteredRequests) {
|
|
1626
|
-
return unfilteredRequests.filter((
|
|
1715
|
+
return unfilteredRequests.filter((entry) => entry.request.adapterName === this.adapterName);
|
|
1627
1716
|
}
|
|
1628
1717
|
/**
|
|
1629
1718
|
* Reduce requests by combining those based on a strategies implementations
|
|
@@ -1641,9 +1730,15 @@ class LuvioAdapterRequestStrategy extends RequestStrategy {
|
|
|
1641
1730
|
const combinedRequest = { ...currentRequest };
|
|
1642
1731
|
for (let j = i + 1; j < n; j++) {
|
|
1643
1732
|
const hasNotBeenVisited = !visitedRequests.has(requests[j]);
|
|
1644
|
-
const canCombineConfigs = this.canCombine(combinedRequest.config, requests[j].config);
|
|
1733
|
+
const canCombineConfigs = this.canCombine(combinedRequest.request.config, requests[j].request.config);
|
|
1645
1734
|
if (hasNotBeenVisited && canCombineConfigs) {
|
|
1646
|
-
combinedRequest.config = this.combineRequests(combinedRequest.config, requests[j].config);
|
|
1735
|
+
combinedRequest.request.config = this.combineRequests(combinedRequest.request.config, requests[j].request.config);
|
|
1736
|
+
if (combinedRequest.requestMetadata.requestTime >
|
|
1737
|
+
requests[j].requestMetadata.requestTime) {
|
|
1738
|
+
// This logic is debateable - Currently this always assigns the lowest requestTime to a reduced request.
|
|
1739
|
+
combinedRequest.requestMetadata.requestTime =
|
|
1740
|
+
requests[j].requestMetadata.requestTime;
|
|
1741
|
+
}
|
|
1647
1742
|
visitedRequests.add(requests[j]);
|
|
1648
1743
|
}
|
|
1649
1744
|
}
|
|
@@ -1959,6 +2054,14 @@ class GetRecordActionsRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
|
1959
2054
|
}
|
|
1960
2055
|
|
|
1961
2056
|
const GET_OBJECT_INFO_BATCH_ADAPTER_NAME = 'getObjectInfos';
|
|
2057
|
+
/**
|
|
2058
|
+
* Returns true if A is a superset of B
|
|
2059
|
+
* @param a
|
|
2060
|
+
* @param b
|
|
2061
|
+
*/
|
|
2062
|
+
function isSuperSet(a, b) {
|
|
2063
|
+
return b.every((oan) => a.has(oan));
|
|
2064
|
+
}
|
|
1962
2065
|
class GetObjectInfosRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
1963
2066
|
constructor() {
|
|
1964
2067
|
super(...arguments);
|
|
@@ -1978,6 +2081,43 @@ class GetObjectInfosRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
|
1978
2081
|
},
|
|
1979
2082
|
};
|
|
1980
2083
|
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Reduces the given GetObjectInfosRequest requests by eliminating those for which config.objectApiNames
|
|
2086
|
+
* is a subset of another GetObjectInfosRequest.
|
|
2087
|
+
*
|
|
2088
|
+
* @param unfilteredRequests - Array of unfiltered requests
|
|
2089
|
+
* @returns RequestEntry<GetObjectInfosRequest>[] - Array of reduced requests
|
|
2090
|
+
*/
|
|
2091
|
+
reduce(unfilteredRequests) {
|
|
2092
|
+
// Filter and sort requests by the length of objectApiNames in ascending order.
|
|
2093
|
+
// This ensures a superset of request (i) can only be found in a request (j) such that i < j.
|
|
2094
|
+
const objectInfosRequests = this.filterRequests(unfilteredRequests).sort((a, b) => a.request.config.objectApiNames.length - b.request.config.objectApiNames.length);
|
|
2095
|
+
// Convert request configurations to sets for easier comparison, avoiding a new set construction each iteration.
|
|
2096
|
+
const requestConfigAsSet = objectInfosRequests.map((r) => new Set(r.request.config.objectApiNames));
|
|
2097
|
+
const reducedRequests = [];
|
|
2098
|
+
// Iterate over each request to determine if it is a subset of others
|
|
2099
|
+
for (let i = 0, n = objectInfosRequests.length; i < n; i++) {
|
|
2100
|
+
const current = objectInfosRequests[i];
|
|
2101
|
+
const { request: { config: currentRequestConfig }, requestMetadata: currentRequestMetadata, } = current;
|
|
2102
|
+
let isCurrentSubsetOfOthers = false;
|
|
2103
|
+
// Check if the current request is a subset of any subsequent requests
|
|
2104
|
+
for (let j = i + 1; j < n; j++) {
|
|
2105
|
+
const possibleSuperset = objectInfosRequests[j];
|
|
2106
|
+
if (isSuperSet(requestConfigAsSet[j], currentRequestConfig.objectApiNames)) {
|
|
2107
|
+
isCurrentSubsetOfOthers = true;
|
|
2108
|
+
if (currentRequestMetadata.requestTime <
|
|
2109
|
+
possibleSuperset.requestMetadata.requestTime) {
|
|
2110
|
+
possibleSuperset.requestMetadata.requestTime =
|
|
2111
|
+
currentRequestMetadata.requestTime;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
if (!isCurrentSubsetOfOthers) {
|
|
2116
|
+
reducedRequests.push(current);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
return reducedRequests;
|
|
2120
|
+
}
|
|
1981
2121
|
}
|
|
1982
2122
|
|
|
1983
2123
|
class GetObjectInfoRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
@@ -2028,13 +2168,13 @@ class GetObjectInfoRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
|
2028
2168
|
* @returns
|
|
2029
2169
|
*/
|
|
2030
2170
|
reduce(unfilteredRequests) {
|
|
2031
|
-
const objectApiNamesInBatchRequest = unfilteredRequests.filter((
|
|
2032
|
-
config.objectApiNames.forEach((apiName) => acc.add(apiName));
|
|
2171
|
+
const objectApiNamesInBatchRequest = unfilteredRequests.filter((entry) => entry.request.adapterName === GET_OBJECT_INFO_BATCH_ADAPTER_NAME).reduce((acc, { request }) => {
|
|
2172
|
+
request.config.objectApiNames.forEach((apiName) => acc.add(apiName));
|
|
2033
2173
|
return acc;
|
|
2034
2174
|
}, new Set());
|
|
2035
2175
|
const singleRequests = this.filterRequests(unfilteredRequests);
|
|
2036
|
-
return singleRequests.filter((
|
|
2037
|
-
return !objectApiNamesInBatchRequest.has(request.config.objectApiName);
|
|
2176
|
+
return singleRequests.filter((singleEntry) => {
|
|
2177
|
+
return !objectApiNamesInBatchRequest.has(singleEntry.request.config.objectApiName);
|
|
2038
2178
|
});
|
|
2039
2179
|
}
|
|
2040
2180
|
}
|
|
@@ -2234,19 +2374,19 @@ class GetRelatedListRecordsRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
|
2234
2374
|
*/
|
|
2235
2375
|
reduce(unfilteredRequests) {
|
|
2236
2376
|
// Batch requests by [parentRecordId]->[RelatedListIds]
|
|
2237
|
-
const batchRequests = unfilteredRequests.filter((
|
|
2377
|
+
const batchRequests = unfilteredRequests.filter((entry) => entry.request.adapterName === GET_RELATED_LIST_RECORDS_BATCH_ADAPTER_NAME).reduce((acc, entry) => {
|
|
2238
2378
|
// required properties, enforced by adapter typecheck
|
|
2239
|
-
const { parentRecordId, relatedListParameters } = request.config;
|
|
2379
|
+
const { parentRecordId, relatedListParameters } = entry.request.config;
|
|
2240
2380
|
const existingRlSet = acc.get(parentRecordId) || new Set();
|
|
2241
2381
|
// relatedListId enforced by adapter typecheck
|
|
2242
2382
|
relatedListParameters.forEach((rlParam) => existingRlSet.add(rlParam.relatedListId));
|
|
2243
2383
|
acc.set(parentRecordId, existingRlSet);
|
|
2244
2384
|
return acc;
|
|
2245
2385
|
}, new Map());
|
|
2246
|
-
const singleRequests = unfilteredRequests.filter((
|
|
2247
|
-
return singleRequests.filter((
|
|
2386
|
+
const singleRequests = unfilteredRequests.filter((entry) => entry.request.adapterName === this.adapterName);
|
|
2387
|
+
return singleRequests.filter((entry) => {
|
|
2248
2388
|
// required props enforced by adapter typecheck
|
|
2249
|
-
const { parentRecordId, relatedListId } = request.config;
|
|
2389
|
+
const { parentRecordId, relatedListId } = entry.request.config;
|
|
2250
2390
|
const batchForParentRecordId = batchRequests.get(parentRecordId);
|
|
2251
2391
|
return !(batchForParentRecordId && batchForParentRecordId.has(relatedListId));
|
|
2252
2392
|
});
|
|
@@ -2326,7 +2466,7 @@ const DEFAULT_STORAGE_OPTIONS = {
|
|
|
2326
2466
|
persistent: true,
|
|
2327
2467
|
secure: true,
|
|
2328
2468
|
maxSize: 7 * 1024 * 1024,
|
|
2329
|
-
expiration:
|
|
2469
|
+
expiration: 5 * 24 * 60 * 60,
|
|
2330
2470
|
clearOnInit: false,
|
|
2331
2471
|
debugLogging: false,
|
|
2332
2472
|
version: 3,
|
|
@@ -2531,10 +2671,22 @@ function setupQueryEvaluators(luvio, store) {
|
|
|
2531
2671
|
luvio.registerTypeQueryEvaluator(UiApiNamespace, RecordRepresentationRepresentationType, recordRepresentationQueryEvaluator);
|
|
2532
2672
|
}
|
|
2533
2673
|
let __lexPrefetcher;
|
|
2674
|
+
const HARDCODED_REQUEST_LIMIT = 9;
|
|
2675
|
+
function getInflightRequestLimit() {
|
|
2676
|
+
try {
|
|
2677
|
+
return window['$A'].clientService.maxAllowedParallelXHRCounts() - 3;
|
|
2678
|
+
}
|
|
2679
|
+
catch (e) {
|
|
2680
|
+
return HARDCODED_REQUEST_LIMIT;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2534
2683
|
function setupPredictivePrefetcher(luvio) {
|
|
2535
2684
|
const storage = buildAuraPrefetchStorage();
|
|
2536
2685
|
const repository = new PrefetchRepository(storage);
|
|
2537
2686
|
const requestRunner = new LexRequestRunner(luvio);
|
|
2687
|
+
const inflightRequestLimit = applyPredictionRequestLimit.isOpen({ fallback: false })
|
|
2688
|
+
? getInflightRequestLimit()
|
|
2689
|
+
: 1000;
|
|
2538
2690
|
const prefetcher = new LexPredictivePrefetcher({ context: 'unknown' }, repository, requestRunner, {
|
|
2539
2691
|
getRecord: new GetRecordRequestStrategy(),
|
|
2540
2692
|
getRecords: new GetRecordsRequestStrategy(),
|
|
@@ -2546,6 +2698,8 @@ function setupPredictivePrefetcher(luvio) {
|
|
|
2546
2698
|
getRelatedListRecords: new GetRelatedListRecordsRequestStrategy(),
|
|
2547
2699
|
getRelatedListRecordsBatch: new GetRelatedListRecordsBatchRequestStrategy(),
|
|
2548
2700
|
getRelatedListInfoBatch: new GetRelatedListInfoBatchRequestStrategy(),
|
|
2701
|
+
}, {
|
|
2702
|
+
inflightRequestLimit,
|
|
2549
2703
|
});
|
|
2550
2704
|
registerPrefetcher(luvio, prefetcher);
|
|
2551
2705
|
__lexPrefetcher = prefetcher;
|
|
@@ -2682,4 +2836,4 @@ function ldsEngineCreator() {
|
|
|
2682
2836
|
}
|
|
2683
2837
|
|
|
2684
2838
|
export { buildPredictorForContext, ldsEngineCreator as default, initializeLDS, initializeOneStore };
|
|
2685
|
-
// version: 1.287.0-
|
|
2839
|
+
// version: 1.287.0-dev16-db839640ab
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { DefaultPageContext, PredictivePrefetchPage } from '../pages';
|
|
2
1
|
import { ApplicationPredictivePrefetcher } from './predictive-prefetcher';
|
|
2
|
+
import type { DefaultPageContext, PredictivePrefetchPage } from '../pages';
|
|
3
|
+
import type { PrefetcherOptions } from './predictive-prefetcher';
|
|
3
4
|
import type { GetRecordActionsRequestStrategy, GetRecordAvatarsRequestStrategy, GetRecordRequestStrategy, GetRecordsRequestStrategy, GetObjectInfoRequestStrategy, GetObjectInfosRequestStrategy, GetRelatedListsActionsRequestStrategy, GetRelatedListInfoBatchRequestStrategy, GetRelatedListRecordsRequestStrategy, GetRelatedListRecordsBatchRequestStrategy } from '../request-strategy';
|
|
4
5
|
import type { RequestRunner } from '../request-runner';
|
|
5
6
|
import type { PrefetchRepository } from '../repository/prefetch-repository';
|
|
@@ -19,6 +20,7 @@ export declare class LexPredictivePrefetcher extends ApplicationPredictivePrefet
|
|
|
19
20
|
getRelatedListInfoBatch: GetRelatedListInfoBatchRequestStrategy;
|
|
20
21
|
getRelatedListRecords: GetRelatedListRecordsRequestStrategy;
|
|
21
22
|
getRelatedListRecordsBatch: GetRelatedListRecordsBatchRequestStrategy;
|
|
22
|
-
});
|
|
23
|
+
}, options: PrefetcherOptions);
|
|
23
24
|
getPage(): PredictivePrefetchPage<LexRequest, LexContext>;
|
|
25
|
+
predict(): Promise<void>;
|
|
24
26
|
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import type { PrefetchRepository } from '../repository/prefetch-repository';
|
|
2
2
|
import type { PredictivePrefetchPage } from '../pages';
|
|
3
3
|
import type { RequestRunner } from '../request-runner';
|
|
4
|
+
import type { RequestEntry } from '../common';
|
|
5
|
+
export type PrefetcherOptions = {
|
|
6
|
+
inflightRequestLimit: number;
|
|
7
|
+
};
|
|
4
8
|
export declare abstract class ApplicationPredictivePrefetcher<Request, Context extends Record<string, any>> {
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
protected repository: PrefetchRepository;
|
|
10
|
+
protected requestRunner: RequestRunner<Request>;
|
|
11
|
+
protected options: PrefetcherOptions;
|
|
7
12
|
private _context;
|
|
8
13
|
isRecording: boolean;
|
|
9
14
|
page: PredictivePrefetchPage<Request, Context>;
|
|
10
15
|
queuedPredictionRequests: Request[];
|
|
11
|
-
constructor(context: Context, repository: PrefetchRepository, requestRunner: RequestRunner<Request
|
|
16
|
+
constructor(context: Context, repository: PrefetchRepository, requestRunner: RequestRunner<Request>, options: PrefetcherOptions);
|
|
12
17
|
abstract getPage(): PredictivePrefetchPage<Request, Context>;
|
|
13
18
|
set context(value: Context);
|
|
14
19
|
get context(): Context;
|
|
@@ -21,5 +26,5 @@ export declare abstract class ApplicationPredictivePrefetcher<Request, Context e
|
|
|
21
26
|
similar: number;
|
|
22
27
|
};
|
|
23
28
|
hasPredictions(): boolean;
|
|
24
|
-
getSimilarPageRequests(): Request[];
|
|
29
|
+
getSimilarPageRequests(): RequestEntry<Request>[];
|
|
25
30
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RequestRunner } from '../request-runner';
|
|
2
|
+
import type { RequestEntry } from '../common';
|
|
3
|
+
/**
|
|
4
|
+
* Runs a list of requests with a specified concurrency limit.
|
|
5
|
+
*
|
|
6
|
+
* @template Request - The type of the requests being processed.
|
|
7
|
+
* @param {RequestEntry<Request>[]} requests - An array of request entries to be processed in the array order.
|
|
8
|
+
* @param {RequestRunner<Request>} runner - The runner instance responsible for executing the requests.
|
|
9
|
+
* @param {number} concurrentRequestsLimit - The maximum number of concurrent requests allowed.
|
|
10
|
+
* @param {number} pageStartTime - The start time of the page load, used to calculate the time elapsed since the page starts loading.
|
|
11
|
+
* @returns {Promise<void>} A promise that resolves when all requests have been processed.
|
|
12
|
+
*
|
|
13
|
+
* This function manages a queue of pending requests and processes them with a concurrency limit.
|
|
14
|
+
* Requests are only processed if their `requestTime` is less than the time elapsed since `pageStartTime`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runRequestsWithLimit<Request>(requests: RequestEntry<Request>[], runner: RequestRunner<Request>, concurrentRequestsLimit: number, pageStartTime: number): Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PrefetchStorage } from '../storage';
|
|
2
|
+
import type { RequestEntry } from '../common';
|
|
2
3
|
type Key = Record<string, any>;
|
|
3
4
|
export type History = {
|
|
4
5
|
version: '1.0';
|
|
@@ -8,21 +9,17 @@ export type PageEntry<Request> = {
|
|
|
8
9
|
id: string;
|
|
9
10
|
requests: RequestEntry<Request>[];
|
|
10
11
|
};
|
|
11
|
-
export type RequestEntry<Request> = {
|
|
12
|
-
request: Request;
|
|
13
|
-
requestMetadata: {
|
|
14
|
-
requestTime: number;
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
12
|
export declare class PrefetchRepository {
|
|
18
13
|
private storage;
|
|
19
14
|
private requestBuffer;
|
|
20
15
|
constructor(storage: PrefetchStorage);
|
|
21
16
|
clearRequestBuffer(): void;
|
|
17
|
+
pageStartTime: number;
|
|
18
|
+
markPageStart(): void;
|
|
22
19
|
flushRequestsToStorage(): Promise<void>;
|
|
23
20
|
getKeyId(key: Key): string;
|
|
24
21
|
saveRequest<Request>(key: Key, request: Request): Promise<void>;
|
|
25
22
|
getPage<Request>(key: Key): PageEntry<Request> | undefined;
|
|
26
|
-
getPageRequests<Request>(key: Key): Request[];
|
|
23
|
+
getPageRequests<Request>(key: Key): RequestEntry<Request>[];
|
|
27
24
|
}
|
|
28
25
|
export {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Luvio } from '@luvio/engine';
|
|
2
2
|
import type { LexRequest } from '../prefetcher';
|
|
3
3
|
import type { RequestRunner } from './request-runner';
|
|
4
|
+
import type { RequestEntry } from '../common';
|
|
4
5
|
export declare class LexRequestRunner implements RequestRunner<LexRequest> {
|
|
5
6
|
private luvio;
|
|
6
7
|
private requestStrategies;
|
|
7
8
|
constructor(luvio: Luvio);
|
|
8
|
-
reduceRequests(requests: LexRequest[]): LexRequest[];
|
|
9
|
+
reduceRequests(requests: RequestEntry<LexRequest>[]): RequestEntry<LexRequest>[];
|
|
9
10
|
runRequest(request: LexRequest): Promise<void>;
|
|
10
11
|
}
|
package/dist/types/predictive-loading/request-strategy/get-object-info-request-strategy.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GetObjectInfoConfig } from '@salesforce/lds-adapters-uiapi';
|
|
2
2
|
import type { LuvioAdapterRequest } from './luvio-adapter-request';
|
|
3
3
|
import { LuvioAdapterRequestStrategy } from './luvio-adapter-request-strategy';
|
|
4
|
+
import type { RequestEntry } from '../common';
|
|
4
5
|
export type GetObjectInfoRequest = {
|
|
5
6
|
adapterName: 'getObjectInfo';
|
|
6
7
|
config: GetObjectInfoConfig;
|
|
@@ -24,5 +25,5 @@ export declare class GetObjectInfoRequestStrategy extends LuvioAdapterRequestStr
|
|
|
24
25
|
* @param unfilteredRequests all prediction requests
|
|
25
26
|
* @returns
|
|
26
27
|
*/
|
|
27
|
-
reduce(unfilteredRequests: LuvioAdapterRequest<unknown
|
|
28
|
+
reduce(unfilteredRequests: RequestEntry<LuvioAdapterRequest<unknown>>[]): RequestEntry<GetObjectInfoRequest>[];
|
|
28
29
|
}
|
package/dist/types/predictive-loading/request-strategy/get-object-infos-request-strategy.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { GetObjectInfosConfig } from '@salesforce/lds-adapters-uiapi';
|
|
2
|
+
import type { RequestEntry } from '../common';
|
|
3
|
+
import type { LuvioAdapterRequest } from './luvio-adapter-request';
|
|
2
4
|
import { LuvioAdapterRequestStrategy } from './luvio-adapter-request-strategy';
|
|
3
5
|
export type GetObjectInfosRequest = {
|
|
4
6
|
adapterName: 'getObjectInfos';
|
|
@@ -10,4 +12,12 @@ export declare class GetObjectInfosRequestStrategy extends LuvioAdapterRequestSt
|
|
|
10
12
|
adapterFactory: import("@luvio/engine").AdapterFactory<GetObjectInfosConfig, import("@salesforce/lds-adapters-uiapi").SimplifiedBatchRepresentation>;
|
|
11
13
|
buildConcreteRequest(similarRequest: GetObjectInfosRequest): GetObjectInfosRequest;
|
|
12
14
|
transformForSave(request: GetObjectInfosRequest): GetObjectInfosRequest;
|
|
15
|
+
/**
|
|
16
|
+
* Reduces the given GetObjectInfosRequest requests by eliminating those for which config.objectApiNames
|
|
17
|
+
* is a subset of another GetObjectInfosRequest.
|
|
18
|
+
*
|
|
19
|
+
* @param unfilteredRequests - Array of unfiltered requests
|
|
20
|
+
* @returns RequestEntry<GetObjectInfosRequest>[] - Array of reduced requests
|
|
21
|
+
*/
|
|
22
|
+
reduce(unfilteredRequests: RequestEntry<LuvioAdapterRequest<unknown>>[]): RequestEntry<GetObjectInfosRequest>[];
|
|
13
23
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GetRelatedListRecordsConfig } from '@salesforce/lds-adapters-uiapi';
|
|
2
2
|
import type { LuvioAdapterRequest } from './luvio-adapter-request';
|
|
3
3
|
import { LuvioAdapterRequestStrategy } from './luvio-adapter-request-strategy';
|
|
4
|
+
import type { RequestEntry } from '../common';
|
|
4
5
|
export type GetRelatedListRecordsRequest = {
|
|
5
6
|
adapterName: 'getRelatedListRecords';
|
|
6
7
|
config: GetRelatedListRecordsConfig;
|
|
@@ -25,7 +26,7 @@ export declare class GetRelatedListRecordsRequestStrategy extends LuvioAdapterRe
|
|
|
25
26
|
* @param unfilteredRequests All of the request available for predictions.
|
|
26
27
|
* @returns GetRelatedListRecordsRequest[] That should be a prediction.
|
|
27
28
|
*/
|
|
28
|
-
reduce(unfilteredRequests: LuvioAdapterRequest<unknown
|
|
29
|
+
reduce(unfilteredRequests: RequestEntry<LuvioAdapterRequest<unknown>>[]): RequestEntry<GetRelatedListRecordsRequest>[];
|
|
29
30
|
buildSaveRequestData<C extends GetRelatedListRecordsContext>(similarContext: C, context: C, request: GetRelatedListRecordsRequest): {
|
|
30
31
|
request: GetRelatedListRecordsRequest;
|
|
31
32
|
context: C;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AdapterFactory } from '@luvio/engine';
|
|
2
2
|
import { RequestStrategy } from './request-strategy';
|
|
3
3
|
import type { LuvioAdapterRequest } from './luvio-adapter-request';
|
|
4
|
+
import type { RequestEntry } from '../common';
|
|
4
5
|
export declare abstract class LuvioAdapterRequestStrategy<AdapterConfig, Request extends LuvioAdapterRequest<AdapterConfig>, Context> extends RequestStrategy<Request, Context> {
|
|
5
6
|
/**
|
|
6
7
|
* Name of the adapter used in this strategy.
|
|
@@ -26,14 +27,14 @@ export declare abstract class LuvioAdapterRequestStrategy<AdapterConfig, Request
|
|
|
26
27
|
* @param unfilteredRequests array of requests to filter
|
|
27
28
|
* @returns
|
|
28
29
|
*/
|
|
29
|
-
protected filterRequests(unfilteredRequests: LuvioAdapterRequest<unknown
|
|
30
|
+
protected filterRequests(unfilteredRequests: RequestEntry<LuvioAdapterRequest<unknown>>[]): RequestEntry<Request>[];
|
|
30
31
|
/**
|
|
31
32
|
* Reduce requests by combining those based on a strategies implementations
|
|
32
33
|
* of canCombine and combineRequests.
|
|
33
34
|
* @param unfilteredRequests array of requests to filter
|
|
34
35
|
* @returns
|
|
35
36
|
*/
|
|
36
|
-
reduce(unfilteredRequests: LuvioAdapterRequest<unknown
|
|
37
|
+
reduce(unfilteredRequests: RequestEntry<LuvioAdapterRequest<unknown>>[]): RequestEntry<Request>[];
|
|
37
38
|
/**
|
|
38
39
|
* Check if two requests can be combined.
|
|
39
40
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { RequestEntry } from '../common';
|
|
1
2
|
export declare abstract class RequestStrategy<Request, Context> {
|
|
2
3
|
abstract buildConcreteRequest(similarRequest: Request, context: Context): Request;
|
|
3
4
|
transformForSave(request: Request): Request;
|
|
4
|
-
reduce(requests: Request[]): Request[];
|
|
5
|
+
reduce(requests: RequestEntry<Request>[]): RequestEntry<Request>[];
|
|
5
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-runtime-aura",
|
|
3
|
-
"version": "1.287.0-
|
|
3
|
+
"version": "1.287.0-dev16",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "LDS engine for Aura runtime",
|
|
6
6
|
"main": "dist/ldsEngineCreator.js",
|
|
@@ -44,25 +44,25 @@
|
|
|
44
44
|
"@luvio/service-subscription": "^1.0.0",
|
|
45
45
|
"@luvio/service-type-registry": "^1.0.0",
|
|
46
46
|
"@luvio/utils": "^3.6.0",
|
|
47
|
-
"@salesforce/lds-adapters-uiapi": "^1.287.0-
|
|
48
|
-
"@salesforce/lds-ads-bridge": "^1.287.0-
|
|
49
|
-
"@salesforce/lds-aura-storage": "^1.287.0-
|
|
50
|
-
"@salesforce/lds-bindings": "^1.287.0-
|
|
51
|
-
"@salesforce/lds-instrumentation": "^1.287.0-
|
|
52
|
-
"@salesforce/lds-network-aura": "^1.287.0-
|
|
53
|
-
"@salesforce/lds-network-fetch-with-jwt": "^1.287.0-
|
|
47
|
+
"@salesforce/lds-adapters-uiapi": "^1.287.0-dev16",
|
|
48
|
+
"@salesforce/lds-ads-bridge": "^1.287.0-dev16",
|
|
49
|
+
"@salesforce/lds-aura-storage": "^1.287.0-dev16",
|
|
50
|
+
"@salesforce/lds-bindings": "^1.287.0-dev16",
|
|
51
|
+
"@salesforce/lds-instrumentation": "^1.287.0-dev16",
|
|
52
|
+
"@salesforce/lds-network-aura": "^1.287.0-dev16",
|
|
53
|
+
"@salesforce/lds-network-fetch-with-jwt": "^1.287.0-dev16"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@luvio/network-adapter-composable": "0.154.17-
|
|
57
|
-
"@salesforce/lds-adapters-uiapi-lex": "^1.287.0-
|
|
56
|
+
"@luvio/network-adapter-composable": "0.154.17-dev3",
|
|
57
|
+
"@salesforce/lds-adapters-uiapi-lex": "^1.287.0-dev16"
|
|
58
58
|
},
|
|
59
59
|
"luvioBundlesize": [
|
|
60
60
|
{
|
|
61
61
|
"path": "./dist/ldsEngineCreator.js",
|
|
62
62
|
"maxSize": {
|
|
63
|
-
"none": "
|
|
64
|
-
"min": "
|
|
65
|
-
"compressed": "
|
|
63
|
+
"none": "112 kB",
|
|
64
|
+
"min": "46 kB",
|
|
65
|
+
"compressed": "21 kB"
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
],
|