@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.
@@ -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 predictedRequests = [
1401
- ...alwaysRequests,
1402
- ...this.requestRunner.reduceRequests([
1403
- ...exactPageRequests,
1404
- ...similarPageRequests,
1405
- ...this.page.getAlwaysRunRequests(),
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((request) => this.page.resolveSimilarRequest(request));
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.map((requestEntry) => requestEntry.request);
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((request) => request.adapterName === this.adapterName);
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((request) => request.adapterName === GET_OBJECT_INFO_BATCH_ADAPTER_NAME).reduce((acc, { config }) => {
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((request) => {
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((request) => request.adapterName === GET_RELATED_LIST_RECORDS_BATCH_ADAPTER_NAME).reduce((acc, request) => {
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((request) => request.adapterName === this.adapterName);
2247
- return singleRequests.filter((request) => {
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: 12 * 60 * 60,
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-dev14-85b5de0a8f
2839
+ // version: 1.287.0-dev16-db839640ab
@@ -2,3 +2,9 @@ export declare const PDL_EXECUTE_ASYNC_OPTIONS: {
2
2
  LOG_ERROR_ONLY: boolean;
3
3
  ERROR_SCOPE: string;
4
4
  };
5
+ export type RequestEntry<Request> = {
6
+ request: Request;
7
+ requestMetadata: {
8
+ requestTime: number;
9
+ };
10
+ };
@@ -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
- private repository;
6
- private requestRunner;
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
  }
@@ -1,4 +1,5 @@
1
+ import type { RequestEntry } from '../common';
1
2
  export type RequestRunner<Request> = {
2
3
  runRequest(request: Request): Promise<void>;
3
- reduceRequests(requests: Request[]): Request[];
4
+ reduceRequests(requests: RequestEntry<Request>[]): RequestEntry<Request>[];
4
5
  };
@@ -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>[]): GetObjectInfoRequest[];
28
+ reduce(unfilteredRequests: RequestEntry<LuvioAdapterRequest<unknown>>[]): RequestEntry<GetObjectInfoRequest>[];
28
29
  }
@@ -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>[]): GetRelatedListRecordsRequest[];
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>[]): Request[];
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>[]): Request[];
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-dev14",
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-dev14",
48
- "@salesforce/lds-ads-bridge": "^1.287.0-dev14",
49
- "@salesforce/lds-aura-storage": "^1.287.0-dev14",
50
- "@salesforce/lds-bindings": "^1.287.0-dev14",
51
- "@salesforce/lds-instrumentation": "^1.287.0-dev14",
52
- "@salesforce/lds-network-aura": "^1.287.0-dev14",
53
- "@salesforce/lds-network-fetch-with-jwt": "^1.287.0-dev14"
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-dev2",
57
- "@salesforce/lds-adapters-uiapi-lex": "^1.287.0-dev14"
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": "105 kB",
64
- "min": "44 kB",
65
- "compressed": "18.5 kB"
63
+ "none": "112 kB",
64
+ "min": "46 kB",
65
+ "compressed": "21 kB"
66
66
  }
67
67
  }
68
68
  ],