@salesforce/lds-runtime-aura 1.244.0 → 1.246.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 +456 -4
- package/dist/types/aura-instrumentation/main.d.ts +2 -1
- package/dist/types/main.d.ts +16 -0
- package/dist/types/predictive-loading/index.d.ts +6 -0
- package/dist/types/predictive-loading/pages/index.d.ts +3 -0
- package/dist/types/predictive-loading/pages/lex-default-page.d.ts +11 -0
- package/dist/types/predictive-loading/pages/predictive-prefetch-page.d.ts +10 -0
- package/dist/types/predictive-loading/pages/record-home-page.d.ts +37 -0
- package/dist/types/predictive-loading/prefetcher/index.d.ts +2 -0
- package/dist/types/predictive-loading/prefetcher/lex-predictive-prefetcher.d.ts +16 -0
- package/dist/types/predictive-loading/prefetcher/predictive-prefetcher.d.ts +20 -0
- package/dist/types/predictive-loading/repository/index.d.ts +2 -0
- package/dist/types/predictive-loading/repository/prefetch-repository.d.ts +24 -0
- package/dist/types/predictive-loading/repository/utils.d.ts +13 -0
- package/dist/types/predictive-loading/request-runner/index.d.ts +2 -0
- package/dist/types/predictive-loading/request-runner/lex-request-runner.d.ts +10 -0
- package/dist/types/predictive-loading/request-runner/request-runner.d.ts +4 -0
- package/dist/types/predictive-loading/request-strategy/get-record-request-strategy.d.ts +16 -0
- package/dist/types/predictive-loading/request-strategy/get-records-request-strategy.d.ts +14 -0
- package/dist/types/predictive-loading/request-strategy/index.d.ts +5 -0
- package/dist/types/predictive-loading/request-strategy/luvio-adapter-request-strategy.d.ts +8 -0
- package/dist/types/predictive-loading/request-strategy/luvio-adapter-request.d.ts +4 -0
- package/dist/types/predictive-loading/request-strategy/request-strategy.d.ts +5 -0
- package/dist/types/predictive-loading/storage/in-memory-prefetch-storage.d.ts +6 -0
- package/dist/types/predictive-loading/storage/index.d.ts +6 -0
- package/dist/types/predictive-loading/storage/local-prefetch-storage.d.ts +7 -0
- package/package.json +9 -6
package/dist/ldsEngineCreator.js
CHANGED
|
@@ -14,16 +14,430 @@
|
|
|
14
14
|
/* proxy-compat-disable */
|
|
15
15
|
import { HttpStatusCode, InMemoryStore, Environment, Luvio, InMemoryStoreQueryEvaluator } from 'force/luvioEngine';
|
|
16
16
|
import ldsTrackedFieldsBehaviorGate from '@salesforce/gate/lds.useNewTrackedFieldBehavior';
|
|
17
|
-
import
|
|
17
|
+
import usePredictiveLoading from '@salesforce/gate/lds.usePredictiveLoading';
|
|
18
|
+
import { getRecordAdapterFactory, getRecordsAdapterFactory, instrument, configuration, InMemoryRecordRepresentationQueryEvaluator, UiApiNamespace, RecordRepresentationRepresentationType, registerPrefetcher } from 'force/ldsAdaptersUiapi';
|
|
18
19
|
import { withRegistration, register, setDefaultLuvio } from 'force/ldsEngine';
|
|
19
20
|
import { REFRESH_ADAPTER_EVENT, ADAPTER_UNFULFILLED_ERROR, instrument as instrument$2 } from 'force/ldsBindings';
|
|
20
21
|
import { counter, registerCacheStats, perfStart, perfEnd, registerPeriodicLogger, interaction, timer } from 'instrumentation/service';
|
|
21
|
-
import { LRUCache, instrumentAdapter, instrumentLuvio, setupInstrumentation as setupInstrumentation$1, logObjectInfoChanged as logObjectInfoChanged$1, updatePercentileHistogramMetric, incrementCounterMetric, incrementGetRecordNotifyChangeAllowCount, incrementGetRecordNotifyChangeDropCount, incrementNotifyRecordUpdateAvailableAllowCount, incrementNotifyRecordUpdateAvailableDropCount, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation } from 'force/ldsInstrumentation';
|
|
22
|
+
import { LRUCache, instrumentAdapter, instrumentLuvio, setupInstrumentation as setupInstrumentation$1, logObjectInfoChanged as logObjectInfoChanged$1, updatePercentileHistogramMetric, incrementCounterMetric, incrementGetRecordNotifyChangeAllowCount, incrementGetRecordNotifyChangeDropCount, incrementNotifyRecordUpdateAvailableAllowCount, incrementNotifyRecordUpdateAvailableDropCount, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation, onIdleDetected } from 'force/ldsInstrumentation';
|
|
22
23
|
import auraNetworkAdapter, { instrument as instrument$1, forceRecordTransactionsDisabled, ldsNetworkAdapterInstrument, dispatchAuraAction, defaultActionConfig } from 'force/ldsNetwork';
|
|
23
24
|
import { instrument as instrument$3 } from 'force/adsBridge';
|
|
24
25
|
import { buildJwtNetworkAdapter } from 'force/ldsNetworkFetchWithJwt';
|
|
25
26
|
import { clearStorages } from 'force/ldsStorage';
|
|
26
27
|
|
|
28
|
+
class PredictivePrefetchPage {
|
|
29
|
+
constructor(context) {
|
|
30
|
+
this.context = context;
|
|
31
|
+
this.similarContext = undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class LexDefaultPage extends PredictivePrefetchPage {
|
|
36
|
+
constructor(context) {
|
|
37
|
+
super(context);
|
|
38
|
+
}
|
|
39
|
+
buildSaveRequestData(request) {
|
|
40
|
+
return { context: this.context, request };
|
|
41
|
+
}
|
|
42
|
+
resolveSimilarRequest(similarRequest) {
|
|
43
|
+
return similarRequest;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class RecordHomePage extends PredictivePrefetchPage {
|
|
48
|
+
constructor(context, requestStrategies) {
|
|
49
|
+
super(context);
|
|
50
|
+
this.requestStrategies = requestStrategies;
|
|
51
|
+
const { recordId: _, ...rest } = this.context;
|
|
52
|
+
this.similarContext = {
|
|
53
|
+
recordId: '*',
|
|
54
|
+
...rest,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
buildSaveRequestData(request) {
|
|
58
|
+
if (request.adapterName === 'getRecord') {
|
|
59
|
+
return this.buildGetRecordSaveRequestData(request);
|
|
60
|
+
}
|
|
61
|
+
else if (request.adapterName === 'getRecords') {
|
|
62
|
+
return this.buildGetRecordsSaveRequestData(request);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return { request, context: this.context };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
buildGetRecordSaveRequestData(request) {
|
|
69
|
+
if (this.isGetRecordRequestContextDependent(request)) {
|
|
70
|
+
return {
|
|
71
|
+
request: this.requestStrategies.getRecord.transformForSave({
|
|
72
|
+
...request,
|
|
73
|
+
config: {
|
|
74
|
+
...request.config,
|
|
75
|
+
recordId: '*',
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
context: this.similarContext,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
request: this.requestStrategies.getRecord.transformForSave(request),
|
|
83
|
+
context: this.context,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
buildGetRecordsSaveRequestData(request) {
|
|
87
|
+
if (this.isGetRecordsRequestContextDependent(request)) {
|
|
88
|
+
return {
|
|
89
|
+
request: this.requestStrategies.getRecords.transformForSave({
|
|
90
|
+
...request,
|
|
91
|
+
config: {
|
|
92
|
+
...request.config,
|
|
93
|
+
records: [
|
|
94
|
+
{
|
|
95
|
+
...request.config.records[0],
|
|
96
|
+
recordIds: ['*'],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
context: this.context,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
request: this.requestStrategies.getRecords.transformForSave(request),
|
|
106
|
+
context: this.context,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
isGetRecordRequestContextDependent(request) {
|
|
110
|
+
return request.config.recordId === this.context.recordId;
|
|
111
|
+
}
|
|
112
|
+
isGetRecordsRequestContextDependent(request) {
|
|
113
|
+
const isSingleRecordRequest = request.config.records.length === 1 && request.config.records[0].recordIds.length === 1;
|
|
114
|
+
return (isSingleRecordRequest &&
|
|
115
|
+
request.config.records[0].recordIds[0] === this.context.recordId);
|
|
116
|
+
}
|
|
117
|
+
resolveSimilarRequest(similarRequest) {
|
|
118
|
+
if (similarRequest.adapterName === 'getRecord') {
|
|
119
|
+
return this.requestStrategies.getRecord.buildConcreteRequest(similarRequest, this.context);
|
|
120
|
+
}
|
|
121
|
+
if (similarRequest.adapterName === 'getRecords') {
|
|
122
|
+
return this.requestStrategies.getRecords.buildConcreteRequest(similarRequest, this.context);
|
|
123
|
+
}
|
|
124
|
+
return similarRequest;
|
|
125
|
+
}
|
|
126
|
+
static handlesContext(context) {
|
|
127
|
+
const maybeRecordHomePageContext = context;
|
|
128
|
+
return (maybeRecordHomePageContext !== undefined &&
|
|
129
|
+
maybeRecordHomePageContext.actionName !== undefined &&
|
|
130
|
+
maybeRecordHomePageContext.objectApiName !== undefined &&
|
|
131
|
+
maybeRecordHomePageContext.recordId !== undefined &&
|
|
132
|
+
maybeRecordHomePageContext.type === 'recordPage');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class ApplicationPredictivePrefetcher {
|
|
137
|
+
constructor(context, repository, requestRunner) {
|
|
138
|
+
this.repository = repository;
|
|
139
|
+
this.requestRunner = requestRunner;
|
|
140
|
+
this.isRecording = false;
|
|
141
|
+
this.queuedPredictionRequests = [];
|
|
142
|
+
this._context = context;
|
|
143
|
+
this.page = this.getPage();
|
|
144
|
+
}
|
|
145
|
+
set context(value) {
|
|
146
|
+
this._context = value;
|
|
147
|
+
this.page = this.getPage();
|
|
148
|
+
}
|
|
149
|
+
get context() {
|
|
150
|
+
return this._context;
|
|
151
|
+
}
|
|
152
|
+
stopRecording() {
|
|
153
|
+
this.isRecording = false;
|
|
154
|
+
}
|
|
155
|
+
startRecording() {
|
|
156
|
+
this.isRecording = true;
|
|
157
|
+
}
|
|
158
|
+
saveRequest(request) {
|
|
159
|
+
if (!this.isRecording) {
|
|
160
|
+
return Promise.resolve();
|
|
161
|
+
}
|
|
162
|
+
const { request: requestToSave, context } = this.page.buildSaveRequestData(request);
|
|
163
|
+
// No need to diferentiate from predictions requests because these
|
|
164
|
+
// are made from the adapters factory, which are not prediction aware.
|
|
165
|
+
return this.repository.saveRequest(context, requestToSave);
|
|
166
|
+
}
|
|
167
|
+
async predict() {
|
|
168
|
+
const exactPageRequests = (await this.repository.getPageRequests(this.context)) || [];
|
|
169
|
+
const similarPageRequests = await this.getSimilarPageRequests();
|
|
170
|
+
const predictedRequests = this.requestRunner.reduceRequests([
|
|
171
|
+
...exactPageRequests,
|
|
172
|
+
...similarPageRequests,
|
|
173
|
+
]);
|
|
174
|
+
this.queuedPredictionRequests.push(...predictedRequests);
|
|
175
|
+
return Promise.all(predictedRequests.map((request) => this.requestRunner.runRequest(request))).then();
|
|
176
|
+
}
|
|
177
|
+
async getSimilarPageRequests() {
|
|
178
|
+
let resolvedSimilarPageRequests = [];
|
|
179
|
+
if (this.page.similarContext !== undefined) {
|
|
180
|
+
const similarPageRequests = await this.repository.getPageRequests(this.page.similarContext);
|
|
181
|
+
if (similarPageRequests !== undefined) {
|
|
182
|
+
resolvedSimilarPageRequests = similarPageRequests.map((request) => this.page.resolveSimilarRequest(request));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return resolvedSimilarPageRequests;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
class LexPredictivePrefetcher extends ApplicationPredictivePrefetcher {
|
|
190
|
+
constructor(context, repository, requestRunner,
|
|
191
|
+
// These strategies need to be in sync with the "predictiveDataLoadCapable" list
|
|
192
|
+
// from scripts/lds-uiapi-plugin.js
|
|
193
|
+
requestStrategies) {
|
|
194
|
+
super(context, repository, requestRunner);
|
|
195
|
+
this.requestStrategies = requestStrategies;
|
|
196
|
+
this.page = this.getPage();
|
|
197
|
+
}
|
|
198
|
+
getPage() {
|
|
199
|
+
if (RecordHomePage.handlesContext(this.context)) {
|
|
200
|
+
return new RecordHomePage(this.context, this.requestStrategies);
|
|
201
|
+
}
|
|
202
|
+
return new LexDefaultPage(this.context);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Copy-pasted from adapter-utils. This util should be extracted from generated code and imported in prefetch repository.
|
|
207
|
+
const { keys: ObjectKeys } = Object;
|
|
208
|
+
const { stringify: JSONStringify } = JSON;
|
|
209
|
+
const { isArray: ArrayIsArray } = Array;
|
|
210
|
+
/**
|
|
211
|
+
* A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
|
|
212
|
+
* This is needed because insertion order for JSON.stringify(object) affects output:
|
|
213
|
+
* JSON.stringify({a: 1, b: 2})
|
|
214
|
+
* "{"a":1,"b":2}"
|
|
215
|
+
* JSON.stringify({b: 2, a: 1})
|
|
216
|
+
* "{"b":2,"a":1}"
|
|
217
|
+
* @param data Data to be JSON-stringified.
|
|
218
|
+
* @returns JSON.stringified value with consistent ordering of keys.
|
|
219
|
+
*/
|
|
220
|
+
function stableJSONStringify$1(node) {
|
|
221
|
+
// This is for Date values.
|
|
222
|
+
if (node && node.toJSON && typeof node.toJSON === 'function') {
|
|
223
|
+
// eslint-disable-next-line no-param-reassign
|
|
224
|
+
node = node.toJSON();
|
|
225
|
+
}
|
|
226
|
+
if (node === undefined) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (typeof node === 'number') {
|
|
230
|
+
return isFinite(node) ? '' + node : 'null';
|
|
231
|
+
}
|
|
232
|
+
if (typeof node !== 'object') {
|
|
233
|
+
return JSONStringify(node);
|
|
234
|
+
}
|
|
235
|
+
let i;
|
|
236
|
+
let out;
|
|
237
|
+
if (ArrayIsArray(node)) {
|
|
238
|
+
out = '[';
|
|
239
|
+
for (i = 0; i < node.length; i++) {
|
|
240
|
+
if (i) {
|
|
241
|
+
out += ',';
|
|
242
|
+
}
|
|
243
|
+
out += stableJSONStringify$1(node[i]) || 'null';
|
|
244
|
+
}
|
|
245
|
+
return out + ']';
|
|
246
|
+
}
|
|
247
|
+
if (node === null) {
|
|
248
|
+
return 'null';
|
|
249
|
+
}
|
|
250
|
+
const keys = ObjectKeys(node).sort();
|
|
251
|
+
out = '';
|
|
252
|
+
for (i = 0; i < keys.length; i++) {
|
|
253
|
+
const key = keys[i];
|
|
254
|
+
const value = stableJSONStringify$1(node[key]);
|
|
255
|
+
if (!value) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (out) {
|
|
259
|
+
out += ',';
|
|
260
|
+
}
|
|
261
|
+
out += JSONStringify(key) + ':' + value;
|
|
262
|
+
}
|
|
263
|
+
return '{' + out + '}';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
class PrefetchRepository {
|
|
267
|
+
constructor(storage) {
|
|
268
|
+
this.storage = storage;
|
|
269
|
+
}
|
|
270
|
+
async saveRequest(key, request) {
|
|
271
|
+
const identifier = stableJSONStringify$1(key);
|
|
272
|
+
const rawPage = await this.storage.get(identifier);
|
|
273
|
+
const page = rawPage === undefined ? { identifier, requests: [] } : JSON.parse(rawPage);
|
|
274
|
+
const stringifiedRequest = stableJSONStringify$1(request);
|
|
275
|
+
let existingRequestEntry = page.requests.find((requestEntry) => stableJSONStringify$1(requestEntry.request) === stringifiedRequest);
|
|
276
|
+
if (existingRequestEntry === undefined) {
|
|
277
|
+
existingRequestEntry = { request, requestMetadata: { requestTimes: [] } };
|
|
278
|
+
page.requests.push(existingRequestEntry);
|
|
279
|
+
}
|
|
280
|
+
existingRequestEntry.requestMetadata.requestTimes.push(Date.now());
|
|
281
|
+
return this.storage.set(identifier, stableJSONStringify$1(page));
|
|
282
|
+
}
|
|
283
|
+
async getPage(key) {
|
|
284
|
+
const identifier = stableJSONStringify$1(key);
|
|
285
|
+
const rawPage = await this.storage.get(identifier);
|
|
286
|
+
if (rawPage === undefined) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
return JSON.parse(rawPage);
|
|
290
|
+
}
|
|
291
|
+
async getPageRequests(key) {
|
|
292
|
+
const page = await this.getPage(key);
|
|
293
|
+
if (page === undefined) {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
return page.requests.map((requestEntry) => requestEntry.request);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
class RequestStrategy {
|
|
301
|
+
transformForSave(request) {
|
|
302
|
+
return request;
|
|
303
|
+
}
|
|
304
|
+
reduce(requests) {
|
|
305
|
+
return requests;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
class LuvioAdapterRequestStrategy extends RequestStrategy {
|
|
310
|
+
transformForSave(request) {
|
|
311
|
+
return request;
|
|
312
|
+
}
|
|
313
|
+
reduce(requests) {
|
|
314
|
+
return requests;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
class GetRecordRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
319
|
+
constructor() {
|
|
320
|
+
super(...arguments);
|
|
321
|
+
this.adapterFactory = getRecordAdapterFactory;
|
|
322
|
+
}
|
|
323
|
+
buildConcreteRequest(similarRequest, context) {
|
|
324
|
+
return {
|
|
325
|
+
...similarRequest,
|
|
326
|
+
config: {
|
|
327
|
+
...similarRequest.config,
|
|
328
|
+
recordId: context.recordId,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
transformForSave(request) {
|
|
333
|
+
if (request.config.fields === undefined && request.config.optionalFields === undefined) {
|
|
334
|
+
return request;
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
...request,
|
|
338
|
+
config: {
|
|
339
|
+
...request.config,
|
|
340
|
+
fields: undefined,
|
|
341
|
+
optionalFields: [
|
|
342
|
+
...(request.config.fields || []),
|
|
343
|
+
...(request.config.optionalFields || []),
|
|
344
|
+
],
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
reduce(requests) {
|
|
349
|
+
const recordIdToRequestMap = {};
|
|
350
|
+
const resultRequests = [];
|
|
351
|
+
requests.forEach((request) => {
|
|
352
|
+
if (request.config.fields === undefined &&
|
|
353
|
+
request.config.optionalFields === undefined) {
|
|
354
|
+
resultRequests.push(request);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (recordIdToRequestMap[request.config.recordId] === undefined) {
|
|
358
|
+
recordIdToRequestMap[request.config.recordId] = [];
|
|
359
|
+
}
|
|
360
|
+
recordIdToRequestMap[request.config.recordId].push(request);
|
|
361
|
+
});
|
|
362
|
+
Object.entries(recordIdToRequestMap).forEach(([recordId, requests]) => {
|
|
363
|
+
const fields = new Set();
|
|
364
|
+
const optionalFields = new Set();
|
|
365
|
+
requests.forEach((request) => {
|
|
366
|
+
if (request.config.fields !== undefined) {
|
|
367
|
+
request.config.fields.forEach((field) => {
|
|
368
|
+
fields.add(field);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
if (request.config.optionalFields !== undefined) {
|
|
372
|
+
request.config.optionalFields.forEach((field) => {
|
|
373
|
+
optionalFields.add(field);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
resultRequests.push({
|
|
378
|
+
adapterName: 'getRecord',
|
|
379
|
+
config: {
|
|
380
|
+
recordId,
|
|
381
|
+
fields: Array.from(fields),
|
|
382
|
+
optionalFields: Array.from(optionalFields),
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
return resultRequests;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
class GetRecordsRequestStrategy extends LuvioAdapterRequestStrategy {
|
|
391
|
+
constructor() {
|
|
392
|
+
super(...arguments);
|
|
393
|
+
this.adapterFactory = getRecordsAdapterFactory;
|
|
394
|
+
}
|
|
395
|
+
buildConcreteRequest(similarRequest, context) {
|
|
396
|
+
return {
|
|
397
|
+
...similarRequest,
|
|
398
|
+
config: {
|
|
399
|
+
...similarRequest.config,
|
|
400
|
+
records: [{ ...similarRequest.config.records[0], recordIds: [context.recordId] }],
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
class LexRequestRunner {
|
|
407
|
+
constructor(luvio) {
|
|
408
|
+
this.luvio = luvio;
|
|
409
|
+
this.requestStrategies = {
|
|
410
|
+
getRecord: new GetRecordRequestStrategy(),
|
|
411
|
+
getRecords: new GetRecordsRequestStrategy(),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
reduceRequests(requests) {
|
|
415
|
+
const getRecordRequests = requests.filter((request) => request.adapterName === 'getRecord');
|
|
416
|
+
const otherRequests = requests.filter((request) => request.adapterName !== 'getRecord');
|
|
417
|
+
return [...this.requestStrategies.getRecord.reduce(getRecordRequests), ...otherRequests];
|
|
418
|
+
}
|
|
419
|
+
runRequest(request) {
|
|
420
|
+
if (request.adapterName in this.requestStrategies) {
|
|
421
|
+
const adapterFactory = this.requestStrategies[request.adapterName].adapterFactory;
|
|
422
|
+
return Promise.resolve(adapterFactory(this.luvio)(request.config)).then();
|
|
423
|
+
}
|
|
424
|
+
return Promise.resolve(undefined);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
class LocalPrefetchStorage {
|
|
429
|
+
constructor(localStorage) {
|
|
430
|
+
this.localStorage = localStorage;
|
|
431
|
+
}
|
|
432
|
+
set(key, value) {
|
|
433
|
+
this.localStorage.setItem(key, value);
|
|
434
|
+
return Promise.resolve();
|
|
435
|
+
}
|
|
436
|
+
get(key) {
|
|
437
|
+
return Promise.resolve(this.localStorage.getItem(key) || undefined);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
27
441
|
/**
|
|
28
442
|
* Observability / Critical Availability Program (230+)
|
|
29
443
|
*
|
|
@@ -724,6 +1138,7 @@ function setupMetadataWatcher(luvio) {
|
|
|
724
1138
|
});
|
|
725
1139
|
}
|
|
726
1140
|
|
|
1141
|
+
// import { createStorage } from '@salesforce/lds-aura-storage';
|
|
727
1142
|
// This code *should* be in lds-network-adapter, but when combined with the Aura
|
|
728
1143
|
// component test workaround in lds-default-luvio it creates a circular dependecy
|
|
729
1144
|
// between lds-default-luvio and lds-network-adapter. We do the register on behalf
|
|
@@ -750,6 +1165,40 @@ function setupQueryEvaluators(luvio, store) {
|
|
|
750
1165
|
luvio.registerStoreQueryEvaluator(baseQueryEvaluator);
|
|
751
1166
|
luvio.registerTypeQueryEvaluator(UiApiNamespace, RecordRepresentationRepresentationType, recordRepresentationQueryEvaluator);
|
|
752
1167
|
}
|
|
1168
|
+
let __lexPrefetcher;
|
|
1169
|
+
function setupPredictivePrefetcher(luvio) {
|
|
1170
|
+
const storage = new LocalPrefetchStorage(window.localStorage);
|
|
1171
|
+
const repository = new PrefetchRepository(storage);
|
|
1172
|
+
const requestRunner = new LexRequestRunner(luvio);
|
|
1173
|
+
const prefetcher = new LexPredictivePrefetcher({ context: 'unknown' }, repository, requestRunner, {
|
|
1174
|
+
getRecord: new GetRecordRequestStrategy(),
|
|
1175
|
+
getRecords: new GetRecordsRequestStrategy(),
|
|
1176
|
+
});
|
|
1177
|
+
registerPrefetcher(luvio, prefetcher);
|
|
1178
|
+
__lexPrefetcher = prefetcher;
|
|
1179
|
+
}
|
|
1180
|
+
// Triggers a payload.
|
|
1181
|
+
async function predictiveLoadPage(preloadProps) {
|
|
1182
|
+
// the gate is disabled and the prefetcher was not setup.
|
|
1183
|
+
if (__lexPrefetcher === undefined) {
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
// This chunk configures which page we're going to use to try and preload.
|
|
1187
|
+
const { objectApiName } = preloadProps.context;
|
|
1188
|
+
const { recordId, actionName } = preloadProps.pageReference.attributes;
|
|
1189
|
+
__lexPrefetcher.context = {
|
|
1190
|
+
objectApiName,
|
|
1191
|
+
recordId,
|
|
1192
|
+
actionName,
|
|
1193
|
+
type: 'recordPage',
|
|
1194
|
+
};
|
|
1195
|
+
// This chunk tells the prefetcher to receive events, send off any predictions we have from previous loads, then setup idle detection to stop predicting.
|
|
1196
|
+
__lexPrefetcher.startRecording();
|
|
1197
|
+
__lexPrefetcher.predict();
|
|
1198
|
+
onIdleDetected(() => {
|
|
1199
|
+
__lexPrefetcher.stopRecording();
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
753
1202
|
// LDS initialization logic, invoked directly by Aura component tests
|
|
754
1203
|
function initializeLDS() {
|
|
755
1204
|
const storeOptions = {
|
|
@@ -765,6 +1214,9 @@ function initializeLDS() {
|
|
|
765
1214
|
setupQueryEvaluators(luvio, store);
|
|
766
1215
|
setDefaultLuvio({ luvio });
|
|
767
1216
|
setTrackedFieldsConfig(ldsTrackedFieldsBehaviorGate.isOpen({ fallback: false }));
|
|
1217
|
+
if (usePredictiveLoading.isOpen({ fallback: false })) {
|
|
1218
|
+
setupPredictivePrefetcher(luvio);
|
|
1219
|
+
}
|
|
768
1220
|
}
|
|
769
1221
|
// service function to be invoked by Aura
|
|
770
1222
|
function ldsEngineCreator() {
|
|
@@ -772,5 +1224,5 @@ function ldsEngineCreator() {
|
|
|
772
1224
|
return { name: 'ldsEngineCreator' };
|
|
773
1225
|
}
|
|
774
1226
|
|
|
775
|
-
export { ldsEngineCreator as default, initializeLDS };
|
|
776
|
-
// version: 1.
|
|
1227
|
+
export { ldsEngineCreator as default, initializeLDS, predictiveLoadPage };
|
|
1228
|
+
// version: 1.246.0-8357100fc
|
|
@@ -2,6 +2,8 @@ import type { FetchResponse, Luvio, InMemoryStore, Adapter, UnfulfilledSnapshot
|
|
|
2
2
|
import type { AdapterMetadata } from '@salesforce/lds-bindings';
|
|
3
3
|
import { ADAPTER_UNFULFILLED_ERROR } from '@salesforce/lds-bindings';
|
|
4
4
|
import type { CacheStatsLogger, Timer } from 'instrumentation/service';
|
|
5
|
+
import { onIdleDetected } from '@salesforce/lds-instrumentation';
|
|
6
|
+
export { onIdleDetected };
|
|
5
7
|
export interface AdapterUnfulfilledError {
|
|
6
8
|
[ADAPTER_UNFULFILLED_ERROR]: boolean;
|
|
7
9
|
adapterName: string;
|
|
@@ -176,4 +178,3 @@ export declare function setupInstrumentation(luvio: Luvio, store: InMemoryStore)
|
|
|
176
178
|
*/
|
|
177
179
|
export declare function logCRUDLightningInteraction(eventSource: string, attributes: object): void;
|
|
178
180
|
export declare const instrumentation: Instrumentation;
|
|
179
|
-
export {};
|
package/dist/types/main.d.ts
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
type PreloadProps = {
|
|
2
|
+
context: {
|
|
3
|
+
objectApiName: string;
|
|
4
|
+
};
|
|
5
|
+
pageReference: LexPageReference;
|
|
6
|
+
};
|
|
7
|
+
type LexPageReference = {
|
|
8
|
+
attributes: {
|
|
9
|
+
actionName: string;
|
|
10
|
+
objectApiName: string;
|
|
11
|
+
recordId: string;
|
|
12
|
+
};
|
|
13
|
+
state: any;
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function predictiveLoadPage(preloadProps: PreloadProps): Promise<void>;
|
|
1
17
|
export declare function initializeLDS(): void;
|
|
2
18
|
declare function ldsEngineCreator(): {
|
|
3
19
|
name: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LexRequest } from '../prefetcher';
|
|
2
|
+
import { PredictivePrefetchPage } from './predictive-prefetch-page';
|
|
3
|
+
export type DefaultPageContext = Record<string, any>;
|
|
4
|
+
export declare class LexDefaultPage extends PredictivePrefetchPage<LexRequest, DefaultPageContext> {
|
|
5
|
+
constructor(context: DefaultPageContext);
|
|
6
|
+
buildSaveRequestData(request: LexRequest): {
|
|
7
|
+
context: DefaultPageContext;
|
|
8
|
+
request: import("./record-home-page").RecordHomePageRequest;
|
|
9
|
+
};
|
|
10
|
+
resolveSimilarRequest(similarRequest: LexRequest): LexRequest;
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare abstract class PredictivePrefetchPage<Request, Context> {
|
|
2
|
+
context: Context;
|
|
3
|
+
similarContext: Partial<Context> | undefined;
|
|
4
|
+
constructor(context: Context);
|
|
5
|
+
abstract buildSaveRequestData(request: Request): {
|
|
6
|
+
context: Context;
|
|
7
|
+
request: Request;
|
|
8
|
+
};
|
|
9
|
+
abstract resolveSimilarRequest(similarRequest: Request): Request;
|
|
10
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LexContext } from '../prefetcher';
|
|
2
|
+
import type { GetRecordRequest, GetRecordsRequest, GetRecordRequestStrategy, GetRecordsRequestStrategy } from '../request-strategy';
|
|
3
|
+
import { PredictivePrefetchPage } from './predictive-prefetch-page';
|
|
4
|
+
export type RecordHomePageContext = {
|
|
5
|
+
objectApiName: string;
|
|
6
|
+
recordId: string;
|
|
7
|
+
actionName: string;
|
|
8
|
+
type: 'recordPage';
|
|
9
|
+
};
|
|
10
|
+
export type RecordHomePageRequest = GetRecordRequest | GetRecordsRequest;
|
|
11
|
+
export declare class RecordHomePage extends PredictivePrefetchPage<RecordHomePageRequest, RecordHomePageContext> {
|
|
12
|
+
private requestStrategies;
|
|
13
|
+
similarContext: RecordHomePageContext;
|
|
14
|
+
constructor(context: RecordHomePageContext, requestStrategies: {
|
|
15
|
+
getRecord: GetRecordRequestStrategy;
|
|
16
|
+
getRecords: GetRecordsRequestStrategy;
|
|
17
|
+
});
|
|
18
|
+
buildSaveRequestData(request: RecordHomePageRequest): {
|
|
19
|
+
request: GetRecordRequest;
|
|
20
|
+
context: RecordHomePageContext;
|
|
21
|
+
} | {
|
|
22
|
+
request: GetRecordsRequest;
|
|
23
|
+
context: RecordHomePageContext;
|
|
24
|
+
};
|
|
25
|
+
buildGetRecordSaveRequestData(request: GetRecordRequest): {
|
|
26
|
+
request: GetRecordRequest;
|
|
27
|
+
context: RecordHomePageContext;
|
|
28
|
+
};
|
|
29
|
+
buildGetRecordsSaveRequestData(request: GetRecordsRequest): {
|
|
30
|
+
request: GetRecordsRequest;
|
|
31
|
+
context: RecordHomePageContext;
|
|
32
|
+
};
|
|
33
|
+
isGetRecordRequestContextDependent(request: GetRecordRequest): boolean;
|
|
34
|
+
isGetRecordsRequestContextDependent(request: GetRecordsRequest): boolean;
|
|
35
|
+
resolveSimilarRequest(similarRequest: RecordHomePageRequest): RecordHomePageRequest;
|
|
36
|
+
static handlesContext(context: LexContext): context is RecordHomePageContext;
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DefaultPageContext, PredictivePrefetchPage } from '../pages';
|
|
2
|
+
import { ApplicationPredictivePrefetcher } from './predictive-prefetcher';
|
|
3
|
+
import type { GetRecordRequestStrategy, GetRecordsRequestStrategy } from '../request-strategy';
|
|
4
|
+
import type { RequestRunner } from '../request-runner';
|
|
5
|
+
import type { PrefetchRepository } from '../repository/prefetch-repository';
|
|
6
|
+
import type { RecordHomePageContext, RecordHomePageRequest } from '../pages/record-home-page';
|
|
7
|
+
export type LexRequest = RecordHomePageRequest;
|
|
8
|
+
export type LexContext = RecordHomePageContext | DefaultPageContext;
|
|
9
|
+
export declare class LexPredictivePrefetcher extends ApplicationPredictivePrefetcher<LexRequest, LexContext> {
|
|
10
|
+
private requestStrategies;
|
|
11
|
+
constructor(context: LexContext, repository: PrefetchRepository, requestRunner: RequestRunner<LexRequest>, requestStrategies: {
|
|
12
|
+
getRecord: GetRecordRequestStrategy;
|
|
13
|
+
getRecords: GetRecordsRequestStrategy;
|
|
14
|
+
});
|
|
15
|
+
getPage(): PredictivePrefetchPage<LexRequest, LexContext>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PrefetchRepository } from '../repository/prefetch-repository';
|
|
2
|
+
import type { PredictivePrefetchPage } from '../pages';
|
|
3
|
+
import type { RequestRunner } from '../request-runner';
|
|
4
|
+
export declare abstract class ApplicationPredictivePrefetcher<Request, Context extends Record<string, any>> {
|
|
5
|
+
private repository;
|
|
6
|
+
private requestRunner;
|
|
7
|
+
private _context;
|
|
8
|
+
isRecording: boolean;
|
|
9
|
+
page: PredictivePrefetchPage<Request, Context>;
|
|
10
|
+
queuedPredictionRequests: Request[];
|
|
11
|
+
constructor(context: Context, repository: PrefetchRepository, requestRunner: RequestRunner<Request>);
|
|
12
|
+
abstract getPage(): PredictivePrefetchPage<Request, Context>;
|
|
13
|
+
set context(value: Context);
|
|
14
|
+
get context(): Context;
|
|
15
|
+
stopRecording(): void;
|
|
16
|
+
startRecording(): void;
|
|
17
|
+
saveRequest(request: Request): Promise<void>;
|
|
18
|
+
predict(): Promise<void>;
|
|
19
|
+
getSimilarPageRequests(): Promise<Request[]>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PrefetchStorage } from '../storage';
|
|
2
|
+
type Key = Record<string, any>;
|
|
3
|
+
export type History = {
|
|
4
|
+
version: '1.0';
|
|
5
|
+
pages: Key[];
|
|
6
|
+
};
|
|
7
|
+
export type PageEntry<Request> = {
|
|
8
|
+
id: string;
|
|
9
|
+
requests: RequestEntry<Request>[];
|
|
10
|
+
};
|
|
11
|
+
export type RequestEntry<Request> = {
|
|
12
|
+
request: Request;
|
|
13
|
+
requestMetadata: {
|
|
14
|
+
requestTimes: number[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare class PrefetchRepository {
|
|
18
|
+
private storage;
|
|
19
|
+
constructor(storage: PrefetchStorage);
|
|
20
|
+
saveRequest<Request>(key: Key, request: Request): Promise<void>;
|
|
21
|
+
getPage<Request>(key: Key): Promise<PageEntry<Request> | undefined>;
|
|
22
|
+
getPageRequests<Request>(key: Key): Promise<Request[]>;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const ObjectPrototypeHasOwnProperty: (v: PropertyKey) => boolean;
|
|
2
|
+
export declare const ArrayIsArray: (arg: any) => arg is any[];
|
|
3
|
+
/**
|
|
4
|
+
* A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
|
|
5
|
+
* This is needed because insertion order for JSON.stringify(object) affects output:
|
|
6
|
+
* JSON.stringify({a: 1, b: 2})
|
|
7
|
+
* "{"a":1,"b":2}"
|
|
8
|
+
* JSON.stringify({b: 2, a: 1})
|
|
9
|
+
* "{"b":2,"a":1}"
|
|
10
|
+
* @param data Data to be JSON-stringified.
|
|
11
|
+
* @returns JSON.stringified value with consistent ordering of keys.
|
|
12
|
+
*/
|
|
13
|
+
export declare function stableJSONStringify(node: any): string | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Luvio } from '@luvio/engine';
|
|
2
|
+
import type { LexRequest } from '../prefetcher';
|
|
3
|
+
import type { RequestRunner } from './request-runner';
|
|
4
|
+
export declare class LexRequestRunner implements RequestRunner<LexRequest> {
|
|
5
|
+
private luvio;
|
|
6
|
+
private requestStrategies;
|
|
7
|
+
constructor(luvio: Luvio);
|
|
8
|
+
reduceRequests(requests: LexRequest[]): LexRequest[];
|
|
9
|
+
runRequest(request: LexRequest): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GetRecordConfig } from '@salesforce/lds-adapters-uiapi';
|
|
2
|
+
import { LuvioAdapterRequestStrategy } from './luvio-adapter-request-strategy';
|
|
3
|
+
export type GetRecordRequest = {
|
|
4
|
+
adapterName: 'getRecord';
|
|
5
|
+
config: GetRecordConfig;
|
|
6
|
+
};
|
|
7
|
+
type GetRecordContext = {
|
|
8
|
+
recordId: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class GetRecordRequestStrategy extends LuvioAdapterRequestStrategy<GetRecordConfig, GetRecordRequest, GetRecordContext> {
|
|
11
|
+
adapterFactory: import("@luvio/engine").AdapterFactory<GetRecordConfig, import("@salesforce/lds-adapters-uiapi").RecordRepresentation>;
|
|
12
|
+
buildConcreteRequest(similarRequest: GetRecordRequest, context: GetRecordContext): GetRecordRequest;
|
|
13
|
+
transformForSave(request: GetRecordRequest): GetRecordRequest;
|
|
14
|
+
reduce(requests: GetRecordRequest[]): GetRecordRequest[];
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GetRecordsConfig } from '@salesforce/lds-adapters-uiapi';
|
|
2
|
+
import { LuvioAdapterRequestStrategy } from './luvio-adapter-request-strategy';
|
|
3
|
+
export type GetRecordsRequest = {
|
|
4
|
+
adapterName: 'getRecords';
|
|
5
|
+
config: GetRecordsConfig;
|
|
6
|
+
};
|
|
7
|
+
type GetRecordsContext = {
|
|
8
|
+
recordId: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class GetRecordsRequestStrategy extends LuvioAdapterRequestStrategy<GetRecordsConfig, GetRecordsRequest, GetRecordsContext> {
|
|
11
|
+
adapterFactory: import("@luvio/engine").AdapterFactory<GetRecordsConfig, import("@salesforce/lds-adapters-uiapi").BatchRepresentation>;
|
|
12
|
+
buildConcreteRequest(similarRequest: GetRecordsRequest, context: GetRecordsContext): GetRecordsRequest;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AdapterFactory } from '@luvio/engine';
|
|
2
|
+
import { RequestStrategy } from './request-strategy';
|
|
3
|
+
import type { LuvioAdapterRequest } from './luvio-adapter-request';
|
|
4
|
+
export declare abstract class LuvioAdapterRequestStrategy<AdapterConfig, Request extends LuvioAdapterRequest<AdapterConfig>, Context> extends RequestStrategy<Request, Context> {
|
|
5
|
+
abstract adapterFactory: AdapterFactory<AdapterConfig, any>;
|
|
6
|
+
transformForSave(request: Request): Request;
|
|
7
|
+
reduce(requests: Request[]): Request[];
|
|
8
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PrefetchStorage } from '.';
|
|
2
|
+
export declare class LocalPrefetchStorage implements PrefetchStorage {
|
|
3
|
+
private localStorage;
|
|
4
|
+
constructor(localStorage: typeof window.localStorage);
|
|
5
|
+
set(key: string, value: string): Promise<void>;
|
|
6
|
+
get(key: string): Promise<string | undefined>;
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-runtime-aura",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.246.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "LDS engine for Aura runtime",
|
|
6
6
|
"main": "dist/ldsEngineCreator.js",
|
|
@@ -43,16 +43,19 @@
|
|
|
43
43
|
"@salesforce/lds-network-fetch-with-jwt": "*"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@luvio/network-adapter-composable": "0.150.
|
|
46
|
+
"@luvio/network-adapter-composable": "0.150.6"
|
|
47
47
|
},
|
|
48
48
|
"luvioBundlesize": [
|
|
49
49
|
{
|
|
50
50
|
"path": "./dist/ldsEngineCreator.js",
|
|
51
51
|
"maxSize": {
|
|
52
|
-
"none": "
|
|
53
|
-
"min": "
|
|
54
|
-
"compressed": "
|
|
52
|
+
"none": "50 kB",
|
|
53
|
+
"min": "22 kB",
|
|
54
|
+
"compressed": "10 kB"
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
]
|
|
57
|
+
],
|
|
58
|
+
"volta": {
|
|
59
|
+
"extends": "../../package.json"
|
|
60
|
+
}
|
|
58
61
|
}
|