@salesforce/lds-runtime-aura 1.297.0 → 1.299.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.
@@ -17,12 +17,14 @@ import ldsTrackedFieldsBehaviorGate from '@salesforce/gate/lds.useNewTrackedFiel
17
17
  import usePredictiveLoading from '@salesforce/gate/lds.usePredictiveLoading';
18
18
  import useApexPredictions from '@salesforce/gate/lds.pdl.useApexPredictions';
19
19
  import useRelatedListsPredictions from '@salesforce/gate/lds.pdl.useRelatedListsPredictions';
20
+ import useCmpDefPredictions from '@salesforce/gate/lds.pdl.useCmpDefPredictions';
20
21
  import applyPredictionRequestLimit from '@salesforce/gate/lds.pdl.applyRequestLimit';
21
22
  import { GetApexWireAdapterFactory, registerPrefetcher as registerPrefetcher$1 } from 'force/ldsAdaptersApex';
22
- import { instrument, getRecordAvatarsAdapterFactory, getRecordAdapterFactory, coerceFieldIdArray, getRecordsAdapterFactory, getRecordActionsAdapterFactory, getObjectInfosAdapterFactory, coerceObjectIdArray, getObjectInfoAdapterFactory, coerceObjectId, getRelatedListsActionsAdapterFactory, getRelatedListInfoBatchAdapterFactory, getRelatedListRecordsBatchAdapterFactory, getRelatedListRecordsAdapterFactory, configuration, InMemoryRecordRepresentationQueryEvaluator, UiApiNamespace, RecordRepresentationRepresentationType, registerPrefetcher } from 'force/ldsAdaptersUiapi';
23
- import { withRegistration as withRegistration$1 } from 'force/luvioRegistry3';
23
+ import { instrument, getRecordAvatarsAdapterFactory, getRecordAdapterFactory, coerceFieldIdArray, getRecordsAdapterFactory, getRecordActionsAdapterFactory, getObjectInfosAdapterFactory, coerceObjectIdArray, getObjectInfoAdapterFactory, coerceObjectId, getRelatedListsActionsAdapterFactory, getRelatedListInfoBatchAdapterFactory, getRelatedListRecordsBatchAdapterFactory, getRelatedListRecordsAdapterFactory, getListInfoByNameAdapterFactory, getListInfosByObjectNameAdapterFactory, getListRecordsByNameAdapterFactory, configuration, InMemoryRecordRepresentationQueryEvaluator, UiApiNamespace, RecordRepresentationRepresentationType, registerPrefetcher } from 'force/ldsAdaptersUiapi';
24
+ import { serviceBroker } from 'force/luvioServiceBroker4';
24
25
  import oneStoreEnabled from '@salesforce/gate/lds.oneStoreEnabled.ltng';
25
- import { executeGlobalControllerRawResponse } from 'aura';
26
+ import oneStoreUiapiEnabled from '@salesforce/gate/lds.oneStoreUiapiEnabled.ltng';
27
+ import { getDefinition, executeGlobalControllerRawResponse } from 'aura';
26
28
  import { LRUCache, instrumentAdapter, instrumentLuvio, setupInstrumentation as setupInstrumentation$1, logObjectInfoChanged as logObjectInfoChanged$1, updatePercentileHistogramMetric, incrementCounterMetric, incrementGetRecordNotifyChangeAllowCount, incrementGetRecordNotifyChangeDropCount, incrementNotifyRecordUpdateAvailableAllowCount, incrementNotifyRecordUpdateAvailableDropCount, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation, executeAsyncActivity, METRIC_KEYS, onIdleDetected } from 'force/ldsInstrumentation';
27
29
  import { REFRESH_ADAPTER_EVENT, ADAPTER_UNFULFILLED_ERROR, instrument as instrument$2 } from 'force/ldsBindings';
28
30
  import { counter, registerCacheStats, perfStart, perfEnd, registerPeriodicLogger, interaction, timer } from 'instrumentation/service';
@@ -136,7 +138,9 @@ class InMemoryStore {
136
138
  */
137
139
  function buildInMemoryStoreService() {
138
140
  return {
139
- store: new InMemoryStore(),
141
+ type: 'store',
142
+ version: '1.0',
143
+ service: new InMemoryStore(),
140
144
  };
141
145
  }
142
146
  /**
@@ -181,7 +185,9 @@ class InMemoryMetadataRepository {
181
185
  */
182
186
  function buildInMemoryMetadataRepositoryService() {
183
187
  return {
184
- metadataRepository: new InMemoryMetadataRepository(),
188
+ type: 'metadataRepository',
189
+ version: '1.0',
190
+ service: new InMemoryMetadataRepository(),
185
191
  };
186
192
  }
187
193
 
@@ -267,56 +273,23 @@ class TTLFilteredStore {
267
273
  */
268
274
 
269
275
 
270
- /**
271
- * Utility function to generate a strongly-typed, empty set of Services. Aside from
272
- * having the correct type, the returned set behaves as {} except it will throw an
273
- * exception if an attempt is made to get a Service that has not been set.
274
- *
275
- * @typeParam Services the set of Services that will be stored in the set, typically
276
- * expressed as an intersection of Service types, e.g. FooService & BarService
277
- * @returns empty set of Services
278
- */
279
- function emptyServices() {
280
- return new Proxy({}, {
281
- get(services, serviceName, receiver) {
282
- if (!(serviceName in services)) {
283
- throw new Error(`service ${serviceName.toString()} not found`);
284
- }
285
- return Reflect.get(services, serviceName, receiver);
286
- },
287
- });
288
- }
289
- /**
290
- * Indicates if a given instance of a service satisfies a request. Note that this function
291
- * assumes the version numbers are valid strings.
292
- *
293
- * @param provided ServiceVersion of the service instance to be provided
294
- * @param requested ServiceVersion requested
295
- * @returns true if the service instance to be provided satisfies the request
296
- */
297
- function satisfies(provided, requested) {
298
- const providedN = provided.split('.').map((s) => parseInt(s));
299
- const requestedN = requested.split('.').map((s) => parseInt(s));
300
- return providedN[0] === requestedN[0] && providedN[1] >= requestedN[1];
301
- }
302
-
303
- function resolvedPromiseLike(result) {
276
+ function resolvedPromiseLike$1(result) {
304
277
  // Don't nest anything promise like
305
- if (isPromiseOrPromiseLike(result)) {
278
+ if (isPromiseOrPromiseLike$1(result)) {
306
279
  return result.then((nextResult) => nextResult);
307
280
  }
308
281
  return {
309
282
  then: (onFulfilled, _onRejected) => {
310
283
  if (onFulfilled) {
311
284
  try {
312
- return resolvedPromiseLike(onFulfilled(result));
285
+ return resolvedPromiseLike$1(onFulfilled(result));
313
286
  }
314
287
  catch (e) {
315
- return rejectedPromiseLike(e);
288
+ return rejectedPromiseLike$1(e);
316
289
  }
317
290
  }
318
291
  // assume TResult1 == Result and just pass result down the chain
319
- return resolvedPromiseLike(result);
292
+ return resolvedPromiseLike$1(result);
320
293
  },
321
294
  };
322
295
  }
@@ -326,49 +299,30 @@ function resolvedPromiseLike(result) {
326
299
  * @param reason rejection value
327
300
  * @returns PromiseLike that rejects with reason
328
301
  */
329
- function rejectedPromiseLike(reason) {
330
- if (isPromiseOrPromiseLike(reason)) {
302
+ function rejectedPromiseLike$1(reason) {
303
+ if (isPromiseOrPromiseLike$1(reason)) {
331
304
  return reason.then((nextResult) => nextResult);
332
305
  }
333
306
  return {
334
307
  then: (_onFulfilled, onRejected) => {
335
308
  if (onRejected) {
336
309
  try {
337
- return resolvedPromiseLike(onRejected(reason));
310
+ return resolvedPromiseLike$1(onRejected(reason));
338
311
  }
339
312
  catch (e) {
340
- return rejectedPromiseLike(e);
313
+ return rejectedPromiseLike$1(e);
341
314
  }
342
315
  }
343
316
  // assume TResult2 == Result and just pass rejection down the chain
344
- return rejectedPromiseLike(reason);
317
+ return rejectedPromiseLike$1(reason);
345
318
  },
346
319
  };
347
320
  }
348
- function isPromiseOrPromiseLike(value) {
321
+ function isPromiseOrPromiseLike$1(value) {
349
322
  return (value instanceof Promise ||
350
323
  (typeof value === 'object' && value !== null && typeof value.then === 'function'));
351
324
  }
352
325
 
353
- // an error to indicate that the data inside a WithErrors construct
354
- // is missing or incomplete
355
- class DataNotFoundError extends Error {
356
- constructor(message) {
357
- super(message);
358
- this.name = 'DataNotFoundError';
359
- }
360
- }
361
- function isDataNotFoundError(error) {
362
- return error instanceof DataNotFoundError || error.name === 'DataNotFoundError';
363
- }
364
- function isCacheHitOrError(value) {
365
- // return cache result if data was found or error was encountered
366
- const { data, errors } = value;
367
- const cacheHit = data !== undefined && errors.length === 0;
368
- const errorEncountered = errors.length > 0 && !isDataNotFoundError(errors[0]);
369
- return cacheHit || errorEncountered;
370
- }
371
-
372
326
  /**
373
327
  * Copyright (c) 2022, Salesforce, Inc.,
374
328
  * All rights reserved.
@@ -388,7 +342,7 @@ class InMemoryCacheInclusionPolicy {
388
342
  const { l1, readFromL1 } = options;
389
343
  // l1 is all we've got
390
344
  const readResult = readFromL1(l1);
391
- return resolvedPromiseLike(readResult);
345
+ return resolvedPromiseLike$1(readResult);
392
346
  }
393
347
  /**
394
348
  * Writes data to a single level in memory store.
@@ -396,7 +350,7 @@ class InMemoryCacheInclusionPolicy {
396
350
  write(options) {
397
351
  const { l1, writeToL1 } = options;
398
352
  writeToL1(l1);
399
- return resolvedPromiseLike(undefined);
353
+ return resolvedPromiseLike$1(undefined);
400
354
  }
401
355
  }
402
356
  /**
@@ -406,7 +360,9 @@ class InMemoryCacheInclusionPolicy {
406
360
  */
407
361
  function buildInMemoryCacheInclusionPolicyService() {
408
362
  return {
409
- cacheInclusionPolicy: new InMemoryCacheInclusionPolicy(),
363
+ service: new InMemoryCacheInclusionPolicy(),
364
+ type: 'cacheInclusionPolicy',
365
+ version: '1.0',
410
366
  };
411
367
  }
412
368
 
@@ -417,6 +373,32 @@ function buildInMemoryCacheInclusionPolicyService() {
417
373
  */
418
374
 
419
375
 
376
+ // an error to indicate that the data inside a WithErrors construct
377
+ // is missing or incomplete
378
+ let DataNotFoundError$1 = class DataNotFoundError extends Error {
379
+ constructor(message) {
380
+ super(message);
381
+ this.name = 'DataNotFoundError';
382
+ }
383
+ };
384
+ function isDataNotFoundError$1(error) {
385
+ return error instanceof DataNotFoundError$1 || error.name === 'DataNotFoundError';
386
+ }
387
+ function isCacheHitOrError$1(value) {
388
+ // return cache result if data was found or error was encountered
389
+ const { data, errors } = value;
390
+ const cacheHit = data !== undefined && errors.length === 0;
391
+ const errorEncountered = errors.length > 0 && !isDataNotFoundError$1(errors[0]);
392
+ return cacheHit || errorEncountered;
393
+ }
394
+
395
+ /**
396
+ * Copyright (c) 2022, Salesforce, Inc.,
397
+ * All rights reserved.
398
+ * For full license text, see the LICENSE.txt file
399
+ */
400
+
401
+
420
402
  class CacheThenNetworkPolicy {
421
403
  constructor(services, validator) {
422
404
  this.services = services;
@@ -434,7 +416,7 @@ class CacheThenNetworkPolicy {
434
416
  writeToCache: (services, networkResult) => this.writeWithValidation(() => writeToCacheOriginal(networkResult, services)),
435
417
  });
436
418
  return readFromCacheDedupe({ ...this.services, store: ttlStore }).then((value) => {
437
- if (isCacheHitOrError(value)) {
419
+ if (isCacheHitOrError$1(value)) {
438
420
  return value;
439
421
  }
440
422
  // result not found in cache, try network
@@ -471,6 +453,32 @@ function buildCacheThenNetworkPolicy(services, validator) {
471
453
  */
472
454
 
473
455
 
456
+ // an error to indicate that the data inside a WithErrors construct
457
+ // is missing or incomplete
458
+ class DataNotFoundError extends Error {
459
+ constructor(message) {
460
+ super(message);
461
+ this.name = 'DataNotFoundError';
462
+ }
463
+ }
464
+ function isDataNotFoundError(error) {
465
+ return error instanceof DataNotFoundError || error.name === 'DataNotFoundError';
466
+ }
467
+ function isCacheHitOrError(value) {
468
+ // return cache result if data was found or error was encountered
469
+ const { data, errors } = value;
470
+ const cacheHit = data !== undefined && errors.length === 0;
471
+ const errorEncountered = errors.length > 0 && !isDataNotFoundError(errors[0]);
472
+ return cacheHit || errorEncountered;
473
+ }
474
+
475
+ /**
476
+ * Copyright (c) 2022, Salesforce, Inc.,
477
+ * All rights reserved.
478
+ * For full license text, see the LICENSE.txt file
479
+ */
480
+
481
+
474
482
  /**
475
483
  * Exported for testing purposes only
476
484
  */
@@ -581,7 +589,68 @@ class KeyBasedRequestDedupe {
581
589
  }
582
590
  }
583
591
  function buildKeyBasedRequestDedupeService() {
584
- return { requestDedupe: new KeyBasedRequestDedupe() };
592
+ return {
593
+ type: 'requestDedupe',
594
+ version: '1.0',
595
+ service: new KeyBasedRequestDedupe(),
596
+ };
597
+ }
598
+
599
+ /**
600
+ * Copyright (c) 2022, Salesforce, Inc.,
601
+ * All rights reserved.
602
+ * For full license text, see the LICENSE.txt file
603
+ */
604
+
605
+
606
+ function resolvedPromiseLike(result) {
607
+ // Don't nest anything promise like
608
+ if (isPromiseOrPromiseLike(result)) {
609
+ return result.then((nextResult) => nextResult);
610
+ }
611
+ return {
612
+ then: (onFulfilled, _onRejected) => {
613
+ if (onFulfilled) {
614
+ try {
615
+ return resolvedPromiseLike(onFulfilled(result));
616
+ }
617
+ catch (e) {
618
+ return rejectedPromiseLike(e);
619
+ }
620
+ }
621
+ // assume TResult1 == Result and just pass result down the chain
622
+ return resolvedPromiseLike(result);
623
+ },
624
+ };
625
+ }
626
+ /**
627
+ * Returns a PromiseLike object that rejects with the specified reason.
628
+ *
629
+ * @param reason rejection value
630
+ * @returns PromiseLike that rejects with reason
631
+ */
632
+ function rejectedPromiseLike(reason) {
633
+ if (isPromiseOrPromiseLike(reason)) {
634
+ return reason.then((nextResult) => nextResult);
635
+ }
636
+ return {
637
+ then: (_onFulfilled, onRejected) => {
638
+ if (onRejected) {
639
+ try {
640
+ return resolvedPromiseLike(onRejected(reason));
641
+ }
642
+ catch (e) {
643
+ return rejectedPromiseLike(e);
644
+ }
645
+ }
646
+ // assume TResult2 == Result and just pass rejection down the chain
647
+ return rejectedPromiseLike(reason);
648
+ },
649
+ };
650
+ }
651
+ function isPromiseOrPromiseLike(value) {
652
+ return (value instanceof Promise ||
653
+ (typeof value === 'object' && value !== null && typeof value.then === 'function'));
585
654
  }
586
655
 
587
656
  /**
@@ -625,7 +694,9 @@ class DefaultKeySubscriptionService {
625
694
  */
626
695
  function buildDefaultKeySubscriptionService() {
627
696
  return {
628
- keySubscription: new DefaultKeySubscriptionService(),
697
+ type: 'keySubscription',
698
+ version: '1.0',
699
+ service: new DefaultKeySubscriptionService(),
629
700
  };
630
701
  }
631
702
 
@@ -668,7 +739,9 @@ class DefaultTypeRegistry {
668
739
  */
669
740
  function buildDefaultTypeRegistryService() {
670
741
  return {
671
- typeRegistry: new DefaultTypeRegistry(),
742
+ type: 'typeRegistry',
743
+ version: '1.0',
744
+ service: new DefaultTypeRegistry(),
672
745
  };
673
746
  }
674
747
 
@@ -711,56 +784,17 @@ class RecordHomePage extends PredictivePrefetchPage {
711
784
  }
712
785
  buildSaveRequestData(request) {
713
786
  const { adapterName } = request;
714
- switch (adapterName) {
715
- case 'getRecord':
716
- return this.requestStrategies.getRecord.buildSaveRequestData(this.similarContext, this.context, request);
717
- case 'getRecords':
718
- return this.requestStrategies.getRecords.buildSaveRequestData(this.similarContext, this.context, request);
719
- case 'getRecordActions':
720
- return this.requestStrategies.getRecordActions.buildSaveRequestData(this.similarContext, this.context, request);
721
- case 'getRecordAvatars':
722
- return this.requestStrategies.getRecordAvatars.buildSaveRequestData(this.similarContext, this.context, request);
723
- case 'getObjectInfo':
724
- return this.requestStrategies.getObjectInfo.buildSaveRequestData(this.similarContext, this.context, request);
725
- case 'getObjectInfos':
726
- return this.requestStrategies.getObjectInfos.buildSaveRequestData(this.similarContext, this.context, request);
727
- case 'getRelatedListsActions':
728
- return this.requestStrategies.getRelatedListsActions.buildSaveRequestData(this.similarContext, this.context, request);
729
- case 'getRelatedListRecords':
730
- return this.requestStrategies.getRelatedListRecords.buildSaveRequestData(this.similarContext, this.context, request);
731
- case 'getRelatedListRecordsBatch':
732
- return this.requestStrategies.getRelatedListRecordsBatch.buildSaveRequestData(this.similarContext, this.context, request);
733
- default:
734
- return { request, context: this.context };
787
+ const matchingRequestStrategy = this.requestStrategies[adapterName];
788
+ if (matchingRequestStrategy) {
789
+ return matchingRequestStrategy.buildSaveRequestData(this.similarContext, this.context, request);
735
790
  }
791
+ return { request, context: this.context };
736
792
  }
737
793
  resolveSimilarRequest(similarRequest) {
738
- if (similarRequest.adapterName === 'getRecord') {
739
- return this.requestStrategies.getRecord.buildConcreteRequest(similarRequest, this.context);
740
- }
741
- if (similarRequest.adapterName === 'getRecords') {
742
- return this.requestStrategies.getRecords.buildConcreteRequest(similarRequest, this.context);
743
- }
744
- if (similarRequest.adapterName === 'getRecordActions') {
745
- return this.requestStrategies.getRecordActions.buildConcreteRequest(similarRequest, this.context);
746
- }
747
- if (similarRequest.adapterName === 'getRecordAvatars') {
748
- return this.requestStrategies.getRecordAvatars.buildConcreteRequest(similarRequest, this.context);
749
- }
750
- if (similarRequest.adapterName === 'getObjectInfo') {
751
- return this.requestStrategies.getObjectInfo.buildConcreteRequest(similarRequest, this.context);
752
- }
753
- if (similarRequest.adapterName === 'getObjectInfos') {
754
- return this.requestStrategies.getObjectInfos.buildConcreteRequest(similarRequest);
755
- }
756
- if (similarRequest.adapterName === 'getRelatedListsActions') {
757
- return this.requestStrategies.getRelatedListsActions.buildConcreteRequest(similarRequest, this.context);
758
- }
759
- if (similarRequest.adapterName === 'getRelatedListRecords') {
760
- return this.requestStrategies.getRelatedListRecords.buildConcreteRequest(similarRequest, this.context);
761
- }
762
- if (similarRequest.adapterName === 'getRelatedListRecordsBatch') {
763
- return this.requestStrategies.getRelatedListRecordsBatch.buildConcreteRequest(similarRequest, this.context);
794
+ const { adapterName } = similarRequest;
795
+ const matchingRequestStrategy = this.requestStrategies[adapterName];
796
+ if (matchingRequestStrategy) {
797
+ return matchingRequestStrategy.buildConcreteRequest(similarRequest, this.context);
764
798
  }
765
799
  return similarRequest;
766
800
  }
@@ -787,6 +821,63 @@ class RecordHomePage extends PredictivePrefetchPage {
787
821
  }
788
822
  }
789
823
 
824
+ class ObjectHomePage extends PredictivePrefetchPage {
825
+ constructor(context, requestStrategies) {
826
+ super(context);
827
+ this.requestStrategies = requestStrategies;
828
+ this.similarContext = context;
829
+ }
830
+ buildSaveRequestData(request) {
831
+ const { adapterName } = request;
832
+ const matchingRequestStrategy = this.requestStrategies[adapterName];
833
+ if (matchingRequestStrategy) {
834
+ return matchingRequestStrategy.buildSaveRequestData(this.similarContext, this.context, request);
835
+ }
836
+ return { request, context: this.context };
837
+ }
838
+ // no similar requests between LVs
839
+ resolveSimilarRequest(similarRequest) {
840
+ return similarRequest;
841
+ }
842
+ // these are requests that run always regardless of any other request existing
843
+ getAlwaysRunRequests() {
844
+ const { listViewApiName, objectApiName } = this.context;
845
+ return [
846
+ {
847
+ adapterName: 'getListInfoByName',
848
+ config: {
849
+ objectApiName: objectApiName,
850
+ listViewApiName: listViewApiName,
851
+ },
852
+ },
853
+ {
854
+ adapterName: 'getListInfosByObjectName',
855
+ config: {
856
+ objectApiName: objectApiName,
857
+ pageSize: 100,
858
+ q: '',
859
+ },
860
+ },
861
+ {
862
+ adapterName: 'getListInfosByObjectName',
863
+ config: {
864
+ objectApiName: objectApiName,
865
+ pageSize: 10,
866
+ recentListsOnly: true,
867
+ },
868
+ },
869
+ ];
870
+ }
871
+ // Identifies a valid ObjectHomeContext
872
+ static handlesContext(context) {
873
+ const maybeObjectHomePageContext = context;
874
+ return (maybeObjectHomePageContext !== undefined &&
875
+ maybeObjectHomePageContext.listViewApiName !== undefined &&
876
+ maybeObjectHomePageContext.objectApiName !== undefined &&
877
+ maybeObjectHomePageContext.type === 'objectHomePage');
878
+ }
879
+ }
880
+
790
881
  /**
791
882
  * Observability / Critical Availability Program (230+)
792
883
  *
@@ -1400,20 +1491,11 @@ class ApplicationPredictivePrefetcher {
1400
1491
  async predict() {
1401
1492
  const alwaysRequests = this.page.getAlwaysRunRequests();
1402
1493
  const similarPageRequests = await this.getSimilarPageRequests();
1403
- const exactPageRequests = (await this.repository.getPageRequests(this.context)) || [];
1404
- // Runs all reducers, then limits the total # of requests, prioritizing earlier requests over later ones.
1405
- const alwaysRequestEntries = alwaysRequests.map((request) => {
1406
- return {
1407
- request,
1408
- requestMetadata: { requestTime: 0 },
1409
- };
1410
- });
1494
+ const exactPageRequests = await this.getExactPageRequest();
1495
+ // Always requests can't be reduced in - Some of them are essential to keep the page rendering at the beginning.
1411
1496
  const reducedRequests = this.requestRunner
1412
- .reduceRequests([...exactPageRequests, ...similarPageRequests, ...alwaysRequestEntries]) // In future - remove alwaysRequestEntries when on OneStore
1413
- .sort((a, b) => a.requestMetadata.requestTime - b.requestMetadata.requestTime) // In future - choose sort algorithm via Gate?
1414
- .slice(0, this.options.inflightRequestLimit - alwaysRequests.length)
1497
+ .reduceRequests([...exactPageRequests, ...similarPageRequests])
1415
1498
  .map((entry) => entry.request);
1416
- // Always requests can't be reduced in - Some of them are essential to keep the page rendering at the beginning.
1417
1499
  const predictedRequests = [...alwaysRequests, ...reducedRequests];
1418
1500
  this.queuedPredictionRequests.push(...predictedRequests);
1419
1501
  return Promise.all(predictedRequests.map((request) => this.requestRunner.runRequest(request))).then();
@@ -1444,8 +1526,14 @@ class ApplicationPredictivePrefetcher {
1444
1526
  }
1445
1527
  return resolvedSimilarPageRequests;
1446
1528
  }
1529
+ getExactPageRequest() {
1530
+ return this.repository.getPageRequests(this.context) || [];
1531
+ }
1447
1532
  }
1448
1533
 
1534
+ function isCmpDefsRequest({ request: { adapterName }, }) {
1535
+ return adapterName === 'getComponentsDef';
1536
+ }
1449
1537
  class LexPredictivePrefetcher extends ApplicationPredictivePrefetcher {
1450
1538
  constructor(context, repository, requestRunner,
1451
1539
  // These strategies need to be in sync with the "predictiveDataLoadCapable" list
@@ -1459,8 +1547,44 @@ class LexPredictivePrefetcher extends ApplicationPredictivePrefetcher {
1459
1547
  if (RecordHomePage.handlesContext(this.context)) {
1460
1548
  return new RecordHomePage(this.context, this.requestStrategies);
1461
1549
  }
1550
+ else if (ObjectHomePage.handlesContext(this.context)) {
1551
+ return new ObjectHomePage(this.context, this.requestStrategies);
1552
+ }
1462
1553
  return new LexDefaultPage(this.context);
1463
1554
  }
1555
+ async predict() {
1556
+ const alwaysRequests = this.page.getAlwaysRunRequests();
1557
+ // IMPORTANT: The `await` has no effect on this operation because `getSimilarPageRequests`
1558
+ // and `getExactPageRequest` are sync; however if removed, it will have a negative
1559
+ // effect when used with Aura Network: predictions will be boxcar'd, which likely will
1560
+ // result in a perf regression.
1561
+ const similarPageRequests = await this.getSimilarPageRequests();
1562
+ const exactPageRequests = await this.getExactPageRequest();
1563
+ const alwaysRequestEntries = alwaysRequests.map((request) => {
1564
+ return {
1565
+ request,
1566
+ requestMetadata: { requestTime: 0 }, // ensures always requests are executed first.
1567
+ };
1568
+ });
1569
+ const reducedPredictions = this.requestRunner.reduceRequests([
1570
+ ...exactPageRequests,
1571
+ ...similarPageRequests,
1572
+ ...alwaysRequestEntries,
1573
+ ]); // In future - remove alwaysRequestEntries when on OneStore
1574
+ const nonCmpDefPredictions = reducedPredictions.filter((r) => !isCmpDefsRequest(r));
1575
+ const cmpDefPredictions = reducedPredictions.filter(isCmpDefsRequest);
1576
+ // Non CmpDefs requests are limited by AuraActions concurent inflight limit.
1577
+ const reducedRequests = nonCmpDefPredictions
1578
+ .sort((a, b) => a.requestMetadata.requestTime - b.requestMetadata.requestTime) // In future - choose sort algorithm via Gate?
1579
+ .slice(0, this.options.inflightRequestLimit - alwaysRequests.length)
1580
+ .map((entry) => entry.request);
1581
+ // Always requests must go by themself - Some of them are essential to keep the page rendering at the beginning.
1582
+ const predictedRequestsWithLimit = [...alwaysRequests, ...reducedRequests];
1583
+ this.queuedPredictionRequests.push(...predictedRequestsWithLimit);
1584
+ const inflightPredictionRequests = predictedRequestsWithLimit.map((request) => this.requestRunner.runRequest(request));
1585
+ const inflightCmpRequests = cmpDefPredictions.map((request) => this.requestRunner.runRequest(request.request));
1586
+ return Promise.all([...inflightPredictionRequests, ...inflightCmpRequests]).then();
1587
+ }
1464
1588
  }
1465
1589
 
1466
1590
  // Copy-pasted from adapter-utils. This util should be extracted from generated code and imported in prefetch repository.
@@ -1726,6 +1850,156 @@ class LuvioAdapterRequestStrategy extends RequestStrategy {
1726
1850
  }
1727
1851
  }
1728
1852
 
1853
+ /**
1854
+ * This is a base class for non-luvio request strategies, which needs to be treated as
1855
+ * LuvioAdapterRequestStrategy.
1856
+ */
1857
+ class ConfigBasedRequestStrategy extends LuvioAdapterRequestStrategy {
1858
+ constructor() {
1859
+ super(...arguments);
1860
+ /**
1861
+ * Config based request strategies are not based on Luvio factory, however
1862
+ * the lex-predictive*, they all expect a LuvioAdapterRequestStrategy.
1863
+ */
1864
+ this.adapterFactory = (_luvio) => {
1865
+ return (config) => this.executeWithConfig(config);
1866
+ };
1867
+ }
1868
+ }
1869
+
1870
+ const noop = () => { };
1871
+ // Taken from https://sourcegraph.soma.salesforce.com/perforce.soma.salesforce.com/app/main/core/-/blob/ui-global-components/components/one/one/oneController.js?L75
1872
+ // In theory this should not be here, but for now, we don't have an alternative,
1873
+ // given these are started before o11y tells PDL the EPT windows ended (even before the timestamp sent by o11y).
1874
+ const onePreloads = new Set([
1875
+ 'markup://emailui:formattedEmailWrapper',
1876
+ 'markup://emailui:outputEmail',
1877
+ 'markup://flexipage:baseRecordHomeTemplateDesktop',
1878
+ 'markup://force:actionWindowLink',
1879
+ 'markup://force:inlineEditCell',
1880
+ 'markup://force:inputField',
1881
+ 'markup://force:recordPreviewItem',
1882
+ 'markup://force:relatedListDesktop',
1883
+ 'markup://force:relatedListQuickLinksContainer',
1884
+ 'markup://lst:relatedListQuickLinksContainer',
1885
+ 'markup://lst:secondDegreeRelatedListSingleContainer',
1886
+ 'markup://force:socialPhotoWrapper',
1887
+ 'markup://forceContent:contentVersionsEditWizard',
1888
+ 'markup://forceContent:outputTitle',
1889
+ 'markup://forceContent:virtualRelatedListStencil',
1890
+ 'markup://forceSearch:resultsFilters',
1891
+ 'markup://interop:unstable_uiRecordApi',
1892
+ 'markup://lightning:formattedPhone',
1893
+ 'markup://notes:contentNoteRelatedListStencil',
1894
+ 'markup://one:alohaPage',
1895
+ 'markup://one:consoleObjectHome',
1896
+ 'markup://one:recordActionWrapper',
1897
+ 'markup://records:lwcDetailPanel',
1898
+ 'markup://records:lwcHighlightsPanel',
1899
+ 'markup://records:recordLayoutInputDateTime',
1900
+ 'markup://records:recordLayoutInputLocation',
1901
+ 'markup://records:recordLayoutItem',
1902
+ 'markup://records:recordLayoutLookup',
1903
+ 'markup://records:recordLayoutRichText',
1904
+ 'markup://records:recordLayoutRow',
1905
+ 'markup://records:recordLayoutSection',
1906
+ 'markup://records:recordLayoutTextArea',
1907
+ 'markup://records:recordPicklist',
1908
+ 'markup://sfa:outputNameWithHierarchyIcon',
1909
+ 'markup://runtime_platform_actions:actionHeadlessFormCancel',
1910
+ 'markup://runtime_platform_actions:actionHeadlessFormSave',
1911
+ 'markup://runtime_platform_actions:actionHeadlessFormSaveAndNew',
1912
+ 'markup://lightning:iconSvgTemplatesCustom',
1913
+ 'markup://lightning:iconSvgTemplatesDocType',
1914
+ 'markup://record_flexipage:recordHomeFlexipageUtil',
1915
+ 'markup://record_flexipage:recordFieldInstancesHandlers',
1916
+ 'markup://force:outputCustomLinkUrl',
1917
+ 'markup://force:quickActionRunnable',
1918
+ 'markup://force:inputURL',
1919
+ 'markup://force:inputMultiPicklist',
1920
+ 'markup://runtime_sales_activities:activityPanel',
1921
+ 'markup://support:outputLookupWithPreviewForSubject',
1922
+ 'markup://runtime_sales_activities:activitySubjectListView',
1923
+ 'markup://support:outputCaseSubjectField',
1924
+ 'markup://sfa:inputOpportunityAmount',
1925
+ 'markup://forceChatter:contentFileSize',
1926
+ 'markup://flexipage:column2',
1927
+ 'markup://sfa:outputNameWithHierarchyIconAccount',
1928
+ 'markup://emailui:formattedEmailAccount',
1929
+ 'markup://emailui:formattedEmailContact',
1930
+ 'markup://emailui:formattedEmailLead',
1931
+ 'markup://e.aura:serverActionError',
1932
+ 'markup://records:recordType',
1933
+ 'markup://flexipage:recordHomeWithSubheaderTemplateDesktop2',
1934
+ 'markup://force:customLinkUrl',
1935
+ 'markup://sfa:outputOpportunityAmount',
1936
+ 'markup://emailui:formattedEmailCase',
1937
+ 'markup://runtime_sales_activities:activitySubject',
1938
+ 'markup://lightning:quickActionAPI',
1939
+ 'markup://force:listViewManagerGridWrapText',
1940
+ 'markup://flexipage:recordHomeSimpleViewTemplate2',
1941
+ 'markup://flexipage:accordion2',
1942
+ 'markup://flexipage:accordionSection2',
1943
+ 'markup://flexipage:field',
1944
+ 'markup://runtime_iag_core:onboardingManager',
1945
+ 'markup://records:entityLabel',
1946
+ 'markup://records:highlightsHeaderRightContent',
1947
+ 'markup://records:formattedRichText',
1948
+ 'markup://force:socialRecordAvatarWrapper',
1949
+ 'markup://runtime_pipeline_inspector:pipelineInspectorHome',
1950
+ 'markup://sfa:inspectionDesktopObjectHome',
1951
+ 'markup://records:outputPhone',
1952
+ ]);
1953
+ function requestComponents(config) {
1954
+ try {
1955
+ for (let index = 0, n = config.length; index < n; index++) {
1956
+ const def = config[index];
1957
+ if (!(def.includes('forceGenerated') ||
1958
+ def.includes('one:onePreloads') ||
1959
+ onePreloads.has(def))) {
1960
+ getDefinition(def, noop);
1961
+ }
1962
+ }
1963
+ }
1964
+ catch (e) {
1965
+ // dismiss the error.
1966
+ }
1967
+ }
1968
+ class GetComponentsDefStrategy extends ConfigBasedRequestStrategy {
1969
+ constructor() {
1970
+ super(...arguments);
1971
+ this.adapterName = 'getComponentsDef';
1972
+ }
1973
+ executeWithConfig(config) {
1974
+ return requestComponents(config);
1975
+ }
1976
+ buildConcreteRequest(similarRequest, _context) {
1977
+ return {
1978
+ ...similarRequest,
1979
+ };
1980
+ }
1981
+ buildSaveRequestData(similarContext, _context, request) {
1982
+ return {
1983
+ request: this.transformForSave(request),
1984
+ context: similarContext,
1985
+ };
1986
+ }
1987
+ transformForSave(request) {
1988
+ return {
1989
+ ...request,
1990
+ config: request.config || [],
1991
+ };
1992
+ }
1993
+ canCombine() {
1994
+ return true;
1995
+ }
1996
+ combineRequests(reqA, reqB) {
1997
+ const result = new Set(reqA);
1998
+ reqB.forEach((c) => result.add(c));
1999
+ return [...result];
2000
+ }
2001
+ }
2002
+
1729
2003
  function normalizeRecordIds$1(recordIds) {
1730
2004
  if (!Array.isArray(recordIds)) {
1731
2005
  return [recordIds];
@@ -2311,6 +2585,116 @@ class GetApexRequestStrategy extends LuvioAdapterRequestStrategy {
2311
2585
  }
2312
2586
  }
2313
2587
 
2588
+ const GET_LIST_INFO_BY_NAME_ADAPTER_NAME = 'getListInfoByName';
2589
+ class GetListInfoByNameRequestStrategy extends LuvioAdapterRequestStrategy {
2590
+ constructor() {
2591
+ super(...arguments);
2592
+ this.adapterName = GET_LIST_INFO_BY_NAME_ADAPTER_NAME;
2593
+ this.adapterFactory = getListInfoByNameAdapterFactory;
2594
+ }
2595
+ buildConcreteRequest(similarRequest) {
2596
+ return similarRequest;
2597
+ }
2598
+ transformForSave(request) {
2599
+ return {
2600
+ ...request,
2601
+ config: {
2602
+ ...request.config,
2603
+ // (!): if we are saving this request is because the adapter already verified is valid.
2604
+ objectApiName: coerceObjectId(request.config.objectApiName),
2605
+ },
2606
+ };
2607
+ }
2608
+ buildSaveRequestData(_similarContext, context, request) {
2609
+ return {
2610
+ request: this.transformForSave(request),
2611
+ context,
2612
+ };
2613
+ }
2614
+ canCombine(reqA, reqB) {
2615
+ return (reqA.objectApiName === reqB.objectApiName &&
2616
+ reqA.listViewApiName === reqB.listViewApiName);
2617
+ }
2618
+ combineRequests(reqA, _reqB) {
2619
+ return reqA;
2620
+ }
2621
+ }
2622
+
2623
+ const GET_LIST_INFOS_BY_OBJECT_NAME_ADAPTER_NAME = 'getListInfosByObjectName';
2624
+ class GetListInfosByObjectNameRequestStrategy extends LuvioAdapterRequestStrategy {
2625
+ constructor() {
2626
+ super(...arguments);
2627
+ this.adapterName = GET_LIST_INFOS_BY_OBJECT_NAME_ADAPTER_NAME;
2628
+ this.adapterFactory = getListInfosByObjectNameAdapterFactory;
2629
+ }
2630
+ buildConcreteRequest(similarRequest) {
2631
+ return similarRequest;
2632
+ }
2633
+ transformForSave(request) {
2634
+ return {
2635
+ ...request,
2636
+ config: {
2637
+ ...request.config,
2638
+ // (!): if we are saving this request is because the adapter already verified is valid.
2639
+ objectApiName: coerceObjectId(request.config.objectApiName),
2640
+ },
2641
+ };
2642
+ }
2643
+ buildSaveRequestData(_similarContext, context, request) {
2644
+ return {
2645
+ request: this.transformForSave(request),
2646
+ context,
2647
+ };
2648
+ }
2649
+ canCombine(reqA, reqB) {
2650
+ return (reqA.objectApiName === reqB.objectApiName &&
2651
+ reqA.q === reqB.q &&
2652
+ reqA.recentListsOnly === reqB.recentListsOnly &&
2653
+ reqA.pageSize === reqB.pageSize);
2654
+ }
2655
+ combineRequests(reqA, _reqB) {
2656
+ return reqA;
2657
+ }
2658
+ }
2659
+
2660
+ const GET_LIST_RECORDS_BY_NAME_ADAPTER_NAME = 'getListRecordsByName';
2661
+ class GetListRecordsByNameRequestStrategy extends LuvioAdapterRequestStrategy {
2662
+ constructor() {
2663
+ super(...arguments);
2664
+ this.adapterName = GET_LIST_RECORDS_BY_NAME_ADAPTER_NAME;
2665
+ this.adapterFactory = getListRecordsByNameAdapterFactory;
2666
+ }
2667
+ buildConcreteRequest(similarRequest) {
2668
+ return similarRequest;
2669
+ }
2670
+ transformForSave(request) {
2671
+ // if (request.config.fields === undefined && request.config.optionalFields === undefined) {
2672
+ // return request;
2673
+ // }
2674
+ // let fields = request.config.fields || [];
2675
+ // let optionalFields = request.config.optionalFields || [];
2676
+ // return {
2677
+ // ...request,
2678
+ // config: {
2679
+ // ...request.config,
2680
+ // fields: [],
2681
+ // optionalFields: Array.from(new Set([...fields, ...optionalFields]),
2682
+ // },
2683
+ // };
2684
+ // Right now getListRecordsByName has both fields and optional fiends in the key
2685
+ // which means if we try to move fields to optionalFields then the wire will fire twice
2686
+ // A raml artifact which combines fields and optional fields into one larger list and uses that
2687
+ // to build the key should solve this issue till then we can't move fields into optional fields
2688
+ return request;
2689
+ }
2690
+ buildSaveRequestData(_similarContext, context, request) {
2691
+ return {
2692
+ request: this.transformForSave(request),
2693
+ context,
2694
+ };
2695
+ }
2696
+ }
2697
+
2314
2698
  const LDS_PDL_CMP_IDENTIFIER = 'lds:pdl';
2315
2699
  class LexRequestRunner {
2316
2700
  constructor(luvio) {
@@ -2326,7 +2710,11 @@ class LexRequestRunner {
2326
2710
  getRelatedListInfoBatch: new GetRelatedListInfoBatchRequestStrategy(),
2327
2711
  getRelatedListRecords: new GetRelatedListRecordsRequestStrategy(),
2328
2712
  getRelatedListRecordsBatch: new GetRelatedListRecordsBatchRequestStrategy(),
2713
+ getListInfoByName: new GetListInfoByNameRequestStrategy(),
2714
+ getListRecordsByName: new GetListRecordsByNameRequestStrategy(),
2329
2715
  getApex: new GetApexRequestStrategy(),
2716
+ getComponentsDef: new GetComponentsDefStrategy(),
2717
+ getListInfosByObjectName: new GetListInfosByObjectNameRequestStrategy(),
2330
2718
  };
2331
2719
  }
2332
2720
  reduceRequests(requests) {
@@ -2588,7 +2976,11 @@ function setupPredictivePrefetcher(luvio) {
2588
2976
  getRelatedListRecords: new GetRelatedListRecordsRequestStrategy(),
2589
2977
  getRelatedListRecordsBatch: new GetRelatedListRecordsBatchRequestStrategy(),
2590
2978
  getRelatedListInfoBatch: new GetRelatedListInfoBatchRequestStrategy(),
2979
+ getListInfoByName: new GetListInfoByNameRequestStrategy(),
2980
+ getListRecordsByName: new GetListRecordsByNameRequestStrategy(),
2591
2981
  getApexStrategy: new GetApexRequestStrategy(),
2982
+ getComponentsDef: new GetComponentsDefStrategy(),
2983
+ getListInfosByObjectName: new GetListInfosByObjectNameRequestStrategy(),
2592
2984
  }, {
2593
2985
  inflightRequestLimit,
2594
2986
  });
@@ -2597,6 +2989,30 @@ function setupPredictivePrefetcher(luvio) {
2597
2989
  registerPrefetcher$1(luvio, prefetcher);
2598
2990
  }
2599
2991
  __lexPrefetcher = prefetcher;
2992
+ if (useCmpDefPredictions.isOpen({ fallback: false })) {
2993
+ window['$A'].installOverride('ComponentDefLoader.loadingComplete', (...args) => {
2994
+ /**
2995
+ * To install an override (taken from the Aura.Utils.Override):
2996
+ *
2997
+ * The function supplied should have the following code in it:
2998
+ * ------
2999
+ * var config = Array.prototype.shift.apply(arguments);
3000
+ * var ret = config["fn"].apply(config["scope"], arguments);
3001
+ * return ret
3002
+ * ------
3003
+ */
3004
+ const config = Array.prototype.shift.apply(args);
3005
+ const ret = config['fn'].apply(config['scope'], args);
3006
+ const [hasErrors, descriptors] = args;
3007
+ if (!hasErrors && Array.isArray(descriptors) && descriptors.length > 0) {
3008
+ __lexPrefetcher.saveRequest({
3009
+ adapterName: 'getComponentsDef',
3010
+ config: descriptors,
3011
+ });
3012
+ }
3013
+ return ret;
3014
+ });
3015
+ }
2600
3016
  }
2601
3017
  /**
2602
3018
  * @typedef {Object} RecordHomePageContext
@@ -2678,56 +3094,60 @@ function initializeOneStore() {
2678
3094
  validateWriteResult() { },
2679
3095
  validateBuildResult() { },
2680
3096
  };
2681
- const services = emptyServices();
2682
- // Build default set of services
2683
- Object.assign(services, buildDefaultKeySubscriptionService(), buildInMemoryMetadataRepositoryService(), buildInMemoryStoreService(), buildDefaultTypeRegistryService(), buildAuraNetworkService(), buildInMemoryCacheInclusionPolicyService(), {
2684
- cachePolicy: {
2685
- cachePolicies: [buildCacheThenNetworkPolicy(services, validator)],
3097
+ const keySubscriptionServiceDescriptor = buildDefaultKeySubscriptionService();
3098
+ const metadataRepositoryServiceDescriptor = buildInMemoryMetadataRepositoryService();
3099
+ const storeServiceDescriptor = buildInMemoryStoreService();
3100
+ const requestDedupeServiceDescriptor = buildKeyBasedRequestDedupeService();
3101
+ const cachePolicyServiceDescriptor = {
3102
+ type: 'cachePolicy',
3103
+ version: '1.0',
3104
+ service: {
3105
+ cachePolicies: [
3106
+ buildCacheThenNetworkPolicy({
3107
+ keySubscription: keySubscriptionServiceDescriptor.service,
3108
+ metadataRepository: metadataRepositoryServiceDescriptor.service,
3109
+ requestDedupe: requestDedupeServiceDescriptor.service,
3110
+ store: storeServiceDescriptor.service,
3111
+ }, validator),
3112
+ ],
2686
3113
  defaultCachePolicyName: 'cache-then-network',
2687
3114
  },
2688
- }, buildKeyBasedRequestDedupeService());
2689
- const serviceVersions = {
2690
- keySubscription: "1.0" /* KeySubscriptionServiceInfo.VERSION */,
2691
- metadataRepository: "1.0" /* MetadataRepositoryServiceInfo.VERSION */,
2692
- store: "1.0" /* StoreServiceInfo.VERSION */,
2693
- typeRegistry: "1.0" /* TypeRegistryServiceInfo.VERSION */,
2694
- auraNetwork: "1.0" /* AuraNetworkServiceInfo.VERSION */,
2695
- cacheInclusionPolicy: "1.0" /* CacheInclusionPolicyServiceInfo.VERSION */,
2696
- cachePolicy: "1.0" /* CachePolicyServiceInfo.VERSION */,
2697
3115
  };
2698
- withRegistration$1('commandModule', (registration) => {
2699
- const matchingServices = {};
2700
- if (Object.entries(registration.services).every(([service, version]) => service in services &&
2701
- satisfies(serviceVersions[service], version) &&
2702
- (matchingServices[service] = services[service]))) {
2703
- registration.setServices(matchingServices);
2704
- }
2705
- });
2706
- withRegistration$1('commandModule-uiapi', (registration) => {
2707
- if (Object.entries(registration.services).every(([service, version]) => service in services &&
2708
- satisfies(serviceVersions[service], version) &&
2709
- (services[service]))) {
2710
- registration.setServices(services);
2711
- configuration.setGetObjectInfoAdapter(getObjectInfo);
2712
- configuration.setGetObjectInfosAdapter(getObjectInfos);
2713
- }
2714
- });
3116
+ const services = [
3117
+ keySubscriptionServiceDescriptor,
3118
+ metadataRepositoryServiceDescriptor,
3119
+ storeServiceDescriptor,
3120
+ requestDedupeServiceDescriptor,
3121
+ cachePolicyServiceDescriptor,
3122
+ buildDefaultTypeRegistryService(),
3123
+ buildAuraNetworkService(),
3124
+ buildInMemoryCacheInclusionPolicyService(),
3125
+ ];
3126
+ serviceBroker.publish(services);
3127
+ }
3128
+ function initializeOnestoreUiApiAdapters() {
3129
+ configuration.setGetObjectInfoAdapter(getObjectInfo);
3130
+ configuration.setGetObjectInfosAdapter(getObjectInfos);
2715
3131
  }
2716
3132
  function buildAuraNetworkService() {
2717
- const auraNetwork = {
2718
- auraNetwork: executeGlobalControllerRawResponse,
3133
+ return {
3134
+ type: 'auraNetwork',
3135
+ version: '1.0',
3136
+ service: executeGlobalControllerRawResponse,
2719
3137
  };
2720
- return auraNetwork;
2721
3138
  }
2722
3139
  // service function to be invoked by Aura
2723
3140
  function ldsEngineCreator() {
2724
3141
  // One store initialization needs to happen first in order to set the one store adapter correctly in configuration object.
2725
3142
  if (oneStoreEnabled.isOpen({ fallback: false })) {
2726
3143
  initializeOneStore();
3144
+ if (oneStoreUiapiEnabled.isOpen({ fallback: false })) {
3145
+ initializeOnestoreUiApiAdapters();
3146
+ }
2727
3147
  }
2728
3148
  initializeLDS();
2729
3149
  return { name: 'ldsEngineCreator' };
2730
3150
  }
2731
3151
 
2732
3152
  export { buildPredictorForContext, ldsEngineCreator as default, initializeLDS, initializeOneStore };
2733
- // version: 1.297.0-1fc775982
3153
+ // version: 1.299.0-83936bf1de