@salesforce/lds-runtime-aura 1.347.0 → 1.348.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.
@@ -20,6 +20,7 @@ import useRelatedListsPredictions from '@salesforce/gate/lds.pdl.useRelatedLists
20
20
  import useRelatedListPlus from '@salesforce/gate/lds.pdl.useRelatedListPlus';
21
21
  import useCmpDefPredictions from '@salesforce/gate/lds.pdl.useCmpDefPredictions';
22
22
  import applyPredictionRequestLimit from '@salesforce/gate/lds.pdl.applyRequestLimit';
23
+ import useLocalStorage from '@salesforce/gate/lds.pdl.useLocalStorage';
23
24
  import { GetApexWireAdapterFactory, registerPrefetcher as registerPrefetcher$1 } from 'force/ldsAdaptersApex';
24
25
  import { getRecordAvatarsAdapterFactory, getRecordAdapterFactory, coerceFieldIdArray, getRecordsAdapterFactory, getRecordActionsAdapterFactory, getObjectInfosAdapterFactory, coerceObjectIdArray, getObjectInfoAdapterFactory, coerceObjectId, getRelatedListsActionsAdapterFactory, getRelatedListInfoBatchAdapterFactory, getRelatedListInfoAdapterFactory, getRelatedListRecordsBatchAdapterFactory, getRelatedListRecordsAdapterFactory, getListInfoByNameAdapterFactory, getListInfosByObjectNameAdapterFactory, getListRecordsByNameAdapterFactory, getListObjectInfoAdapterFactory, getRelatedListsInfoAdapterFactory, getRelatedListActionsAdapterFactory, getRecordId18Array, instrument, configuration, InMemoryRecordRepresentationQueryEvaluator, UiApiNamespace, RecordRepresentationRepresentationType, registerPrefetcher } from 'force/ldsAdaptersUiapi';
25
26
  import { getInstrumentation } from 'o11y/client';
@@ -39,48 +40,6 @@ import { createStorage, clearStorages } from 'force/ldsStorage';
39
40
  import useHttpInsteadAuraTransport from '@salesforce/gate/lds.useHttpInsteadAuraTransport';
40
41
  import { ThirdPartyTracker } from 'instrumentation:beaconLib';
41
42
 
42
- /**
43
- * Copyright (c) 2022, Salesforce, Inc.,
44
- * All rights reserved.
45
- * For full license text, see the LICENSE.txt file
46
- */
47
-
48
- /**
49
- * BaseCommand is an abstract implementation of SubscribableCommand. It adds the
50
- * notions of typed configuration, request context, and a set of runtime services
51
- * to the contract defined by Command/SubscribableCommand.
52
- */
53
- class BaseCommand {
54
- }
55
-
56
- /**
57
- * Copyright (c) 2022, Salesforce, Inc.,
58
- * All rights reserved.
59
- * For full license text, see the LICENSE.txt file
60
- */
61
-
62
-
63
- /**
64
- * An implementation of BaseCommand that makes network requests but does not try to
65
- * use the store.
66
- */
67
- class NetworkCommand extends BaseCommand {
68
- constructor(services) {
69
- super();
70
- this.services = services;
71
- }
72
- execute() {
73
- return this.fetch();
74
- }
75
- }
76
- function buildServiceDescriptor$b() {
77
- return {
78
- type: 'networkCommandBaseClass',
79
- version: '1.0',
80
- service: NetworkCommand,
81
- };
82
- }
83
-
84
43
  /**
85
44
  * Copyright (c) 2022, Salesforce, Inc.,
86
45
  * All rights reserved.
@@ -357,6 +316,78 @@ var HttpStatusCode$2;
357
316
  HttpStatusCode[HttpStatusCode["GatewayTimeout"] = 504] = "GatewayTimeout";
358
317
  })(HttpStatusCode$2 || (HttpStatusCode$2 = {}));
359
318
 
319
+ /**
320
+ * Copyright (c) 2022, Salesforce, Inc.,
321
+ * All rights reserved.
322
+ * For full license text, see the LICENSE.txt file
323
+ */
324
+
325
+ /**
326
+ * BaseCommand is an abstract implementation of SubscribableCommand. It adds the
327
+ * notions of typed configuration, request context, and a set of runtime services
328
+ * to the contract defined by Command/SubscribableCommand.
329
+ */
330
+ class BaseCommand {
331
+ }
332
+
333
+ /**
334
+ * Copyright (c) 2022, Salesforce, Inc.,
335
+ * All rights reserved.
336
+ * For full license text, see the LICENSE.txt file
337
+ */
338
+
339
+
340
+ /**
341
+ * An implementation of BaseCommand that makes network requests but does not try to
342
+ * use the store.
343
+ */
344
+ class NetworkCommand extends BaseCommand {
345
+ constructor(services) {
346
+ super();
347
+ this.services = services;
348
+ this.subscriptions = [];
349
+ }
350
+ execute() {
351
+ return this.fetch().then((networkResult) => {
352
+ if (networkResult.isErr()) {
353
+ return err(networkResult.error);
354
+ }
355
+ else {
356
+ const data = networkResult.value;
357
+ return ok({
358
+ data,
359
+ subscribe: (cb) => {
360
+ this.subscriptions.push(cb);
361
+ return () => {
362
+ this.subscriptions = this.subscriptions.filter((cb2) => cb2 !== cb);
363
+ };
364
+ },
365
+ refresh: () => this.refresh(),
366
+ });
367
+ }
368
+ });
369
+ }
370
+ refresh() {
371
+ return this.execute().then((newResult) => {
372
+ if (newResult.isOk()) {
373
+ const value = newResult.value;
374
+ this.subscriptions.forEach((cb) => {
375
+ cb(ok(value.data));
376
+ });
377
+ return ok(undefined);
378
+ }
379
+ return err(newResult.error);
380
+ });
381
+ }
382
+ }
383
+ function buildServiceDescriptor$b() {
384
+ return {
385
+ type: 'networkCommandBaseClass',
386
+ version: '1.0',
387
+ service: NetworkCommand,
388
+ };
389
+ }
390
+
360
391
  /**
361
392
  * Copyright (c) 2022, Salesforce, Inc.,
362
393
  * All rights reserved.
@@ -428,12 +459,18 @@ class CacheControlCommand extends BaseCommand {
428
459
  constructor(services) {
429
460
  super();
430
461
  this.services = services;
431
- this.keyUnsubscribe = () => { };
462
+ this.rebuildUnsubscribe = () => { };
463
+ this.refreshUnsubscribe = () => { };
432
464
  this.subscriptions = [];
433
- }
434
- execute() {
435
- this.keyUnsubscribe();
436
- const resultPromise = this.services.cacheController.execute(this.cacheControlStrategyConfig, (cache) => this.buildRequestRunner(cache));
465
+ this.instantiationTime = Date.now() / 1000; // in seconds
466
+ }
467
+ execute(overrides) {
468
+ this.rebuildUnsubscribe();
469
+ this.refreshUnsubscribe();
470
+ const mergedCacheControlConfig = mergeCacheControlConfigs(this.cacheControlStrategyConfig, overrides);
471
+ const resultPromise = this.services.cacheController.execute(mergedCacheControlConfig, (cache) => this.buildRequestRunner(cache), {
472
+ instrumentationAttributes: this.instrumentationAttributes,
473
+ });
437
474
  return resultPromise;
438
475
  }
439
476
  // TODO: This should likely be abstract in v2. For v1, provide default comparison logic.
@@ -447,11 +484,20 @@ class CacheControlCommand extends BaseCommand {
447
484
  writeToCache: (networkResult) => this.writeToCacheAndPublish(cache, networkResult),
448
485
  };
449
486
  }
487
+ refresh() {
488
+ return this.execute({ cacheControlConfig: { type: 'no-cache' } }).then((res) => {
489
+ if (res.isOk()) {
490
+ return ok(undefined);
491
+ }
492
+ return err(res.error);
493
+ });
494
+ }
450
495
  // TODO: This is added as a temporary measure for ensuring that cache write events are
451
496
  // published to pubSub. A follow-up will be required to find the right home for this logic.
452
497
  writeToCacheAndPublish(cache, networkResult) {
453
498
  const recordableCache = cache.record();
454
499
  const writeResult = this.writeToCache(recordableCache, networkResult);
500
+ this.instantiationTime = Date.now() / 1000;
455
501
  if (this.services.pubSub) {
456
502
  this.services.pubSub.publish({
457
503
  type: 'cacheUpdate',
@@ -474,6 +520,7 @@ class CacheControlCommand extends BaseCommand {
474
520
  return ok({
475
521
  data,
476
522
  subscribe: this.buildSubscribe(),
523
+ refresh: () => this.refresh(),
477
524
  });
478
525
  }
479
526
  return ok(undefined);
@@ -498,17 +545,12 @@ class CacheControlCommand extends BaseCommand {
498
545
  };
499
546
  }
500
547
  subscribeToKeys(keysRead, lastResult) {
501
- if (this.services.pubSub === undefined) {
548
+ const { pubSub } = this.services;
549
+ if (!pubSub) {
502
550
  return;
503
551
  }
504
- this.keyUnsubscribe = this.services.pubSub.subscribe({
505
- type: 'cacheUpdate',
506
- predicate: (event) => event.data.overlaps(keysRead),
507
- callback: () => keyChangedCallback(),
508
- });
509
- const keyChangedCallback = () => {
510
- const newResultPromise = this.execute();
511
- return newResultPromise.then((result) => {
552
+ const executeAndHandleResult = (overrides) => {
553
+ return this.execute(overrides).then((result) => {
512
554
  if (result.isErr()) {
513
555
  this.invokeConsumerCallbacks(result);
514
556
  return;
@@ -518,6 +560,13 @@ class CacheControlCommand extends BaseCommand {
518
560
  }
519
561
  });
520
562
  };
563
+ const createKeySubscriber = (type, callback) => pubSub.subscribe({
564
+ type,
565
+ predicate: (event) => setOverlaps(event.data, keysRead),
566
+ callback,
567
+ });
568
+ this.rebuildUnsubscribe = createKeySubscriber('cacheUpdate', () => executeAndHandleResult({ now: this.instantiationTime }));
569
+ this.refreshUnsubscribe = createKeySubscriber('cacheInvalidation', () => executeAndHandleResult());
521
570
  }
522
571
  invokeConsumerCallbacks(data) {
523
572
  this.subscriptions.forEach((cb) => {
@@ -530,6 +579,45 @@ class CacheControlCommand extends BaseCommand {
530
579
  });
531
580
  }
532
581
  }
582
+ /**
583
+ * Merges two CacheControlStrategyConfig objects
584
+ *
585
+ */
586
+ function mergeCacheControlConfigs(baseConfig, overrides) {
587
+ var _a;
588
+ if (!overrides) {
589
+ return baseConfig;
590
+ }
591
+ const now = (_a = overrides.now) !== null && _a !== void 0 ? _a : baseConfig.now;
592
+ if (!overrides.cacheControlConfig) {
593
+ return {
594
+ ...baseConfig,
595
+ now,
596
+ };
597
+ }
598
+ return {
599
+ ...overrides.cacheControlConfig,
600
+ now,
601
+ };
602
+ }
603
+ // Needs TS 2023+ (esnext) AND node 20+ to remove
604
+ function setOverlaps(setA, setB) {
605
+ if (setA.size > setB.size) {
606
+ for (const key of setB.keys()) {
607
+ if (setA.has(key)) {
608
+ return true;
609
+ }
610
+ }
611
+ }
612
+ else {
613
+ for (const key of setA) {
614
+ if (setB.has(key)) {
615
+ return true;
616
+ }
617
+ }
618
+ }
619
+ return false;
620
+ }
533
621
 
534
622
  /**
535
623
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -545,7 +633,7 @@ class CacheControlCommand extends BaseCommand {
545
633
  * @typeParam NetworkResult cache result including network metadata
546
634
  * @typeParam ExtraServices additional named services needed by a subclass
547
635
  */
548
- class AuraResourceCacheControlCommand extends CacheControlCommand {
636
+ class AuraCacheControlCommand extends CacheControlCommand {
549
637
  constructor(services) {
550
638
  super(services);
551
639
  this.services = services;
@@ -556,15 +644,8 @@ class AuraResourceCacheControlCommand extends CacheControlCommand {
556
644
  storable: false,
557
645
  };
558
646
  }
559
- execute() {
560
- return super.execute();
561
- }
562
- readFromCache(cache) {
563
- var _a;
564
- return resolvedPromiseLike(ok((_a = cache.get(this.buildKey())) === null || _a === void 0 ? void 0 : _a.value));
565
- }
566
647
  requestFromNetwork() {
567
- return this.convertAuraResponseToData(this.services.auraNetwork(this.endpoint, this.auraParams, this.actionConfig), this.coerceError);
648
+ return this.convertAuraResponseToData(this.services.auraNetwork(this.endpoint, this.auraParams, this.actionConfig), (errs) => this.coerceError(errs));
568
649
  }
569
650
  coerceError(auraErrors) {
570
651
  return toError(auraErrors[0]); // Default Implementation stringifies the response
@@ -585,6 +666,34 @@ class AuraResourceCacheControlCommand extends CacheControlCommand {
585
666
  return err(toError('Error fetching component'));
586
667
  });
587
668
  }
669
+ }
670
+
671
+ /**
672
+ * Copyright (c) 2022, Salesforce, Inc.,
673
+ * All rights reserved.
674
+ * For full license text, see the LICENSE.txt file
675
+ */
676
+
677
+
678
+ /**
679
+ * An implementation of BaseCommand that allows for extending abstract cache methods
680
+ *
681
+ * @typeParam Data cache result for read operations
682
+ * @typeParam NetworkResult cache result including network metadata
683
+ * @typeParam ExtraServices additional named services needed by a subclass
684
+ */
685
+ class AuraResourceCacheControlCommand extends AuraCacheControlCommand {
686
+ constructor(services) {
687
+ super(services);
688
+ this.services = services;
689
+ }
690
+ execute() {
691
+ return super.execute();
692
+ }
693
+ readFromCache(cache) {
694
+ var _a;
695
+ return resolvedPromiseLike(ok((_a = cache.get(this.buildKey())) === null || _a === void 0 ? void 0 : _a.value));
696
+ }
588
697
  writeToCache(cache, networkResult) {
589
698
  if (networkResult.isOk()) {
590
699
  cache.set(this.buildKey(), {
@@ -623,18 +732,18 @@ class FetchNetworkCommand extends NetworkCommand {
623
732
  this.services = services;
624
733
  }
625
734
  fetch() {
626
- return this.convertFetchResponseToData(this.services.fetch(...this.fetchParams), this.coerceError);
735
+ return this.convertFetchResponseToData(this.services.fetch(...this.fetchParams));
627
736
  }
628
737
  async coerceError(errorResponse) {
629
738
  return toError(errorResponse.statusText); // Default Behavior
630
739
  }
631
- convertFetchResponseToData(response, coerceError) {
740
+ convertFetchResponseToData(response) {
632
741
  return response.then((response) => {
633
742
  if (response.ok) {
634
743
  return response.json().then((json) => ok(json), (reason) => err(toError(reason)));
635
744
  }
636
745
  else {
637
- return coerceError(response).then((coercedError) => err(coercedError));
746
+ return this.coerceError(response).then((coercedError) => err(coercedError));
638
747
  }
639
748
  }, (reason) => err(toError(reason)));
640
749
  }
@@ -818,9 +927,9 @@ function buildInstrumentCommand(services) {
818
927
  execute(...args) {
819
928
  invocationCounter.add(1);
820
929
  let result;
821
- const start = performance.now();
930
+ const start = services.instrumentation.currentTimeMs();
822
931
  function recordDuration() {
823
- const end = performance.now();
932
+ const end = services.instrumentation.currentTimeMs();
824
933
  durationHistogram.record(end - start);
825
934
  }
826
935
  try {
@@ -1027,12 +1136,12 @@ class O11yCounter {
1027
1136
  this.o11yInstrumentation = o11yInstrumentation;
1028
1137
  this.logger = logger;
1029
1138
  }
1030
- add(value, _attributes, _context) {
1139
+ add(value, attributes, _context) {
1031
1140
  if (value < 0) {
1032
1141
  this.logger.warn(`Counter values must be non-negative. Got ${value}.`);
1033
1142
  return;
1034
1143
  }
1035
- this.o11yInstrumentation.incrementCounter(this.name, value);
1144
+ this.o11yInstrumentation.incrementCounter(this.name, value, undefined, sanitizeAttributes(attributes));
1036
1145
  }
1037
1146
  }
1038
1147
  class O11yHistogram {
@@ -1041,12 +1150,12 @@ class O11yHistogram {
1041
1150
  this.o11yInstrumentation = o11yInstrumentation;
1042
1151
  this.logger = logger;
1043
1152
  }
1044
- record(value, _attributes, _context) {
1153
+ record(value, attributes, _context) {
1045
1154
  if (value < 0) {
1046
1155
  this.logger.warn(`Histogram values must be non-negative. Got ${value}.`);
1047
1156
  return;
1048
1157
  }
1049
- this.o11yInstrumentation.trackValue(this.name, value);
1158
+ this.o11yInstrumentation.trackValue(this.name, value, undefined, sanitizeAttributes(attributes));
1050
1159
  }
1051
1160
  }
1052
1161
  class O11yUpDownCounter {
@@ -1090,10 +1199,22 @@ class O11yObservableUpDownCounter {
1090
1199
  this.logger.warn('O11yObservableUpDownCounter not supported yet. Defaulting to noop.');
1091
1200
  }
1092
1201
  }
1202
+ function sanitizeAttributes(attributes) {
1203
+ if (!attributes)
1204
+ return;
1205
+ const metricTags = {};
1206
+ Object.entries(attributes).forEach(([key, value]) => {
1207
+ if (value !== undefined && !Array.isArray(value)) {
1208
+ metricTags[key] = value;
1209
+ }
1210
+ });
1211
+ return metricTags;
1212
+ }
1093
1213
 
1094
1214
  class O11yInstrumentation {
1095
1215
  constructor(services) {
1096
1216
  this.services = services;
1217
+ this.currentTimeMs = () => performance.now();
1097
1218
  this.trace = new O11yOTelTraceAPI(this.services);
1098
1219
  this.metrics = new O11yOTelMetricsAPI(this.services);
1099
1220
  }
@@ -1113,63 +1234,6 @@ function buildServiceDescriptor$4(logger) {
1113
1234
  */
1114
1235
 
1115
1236
 
1116
- /**
1117
- * A collection of keys, in no particular order.
1118
- */
1119
- class KeySetImpl {
1120
- constructor(initialKeys) {
1121
- // TODO - probably better to use Set<Key>
1122
- this.data = {};
1123
- this.lengthInternal = 0;
1124
- if (initialKeys) {
1125
- initialKeys.forEach((key) => {
1126
- this.add(key);
1127
- });
1128
- }
1129
- }
1130
- add(key) {
1131
- this.data[key] = true;
1132
- // TODO - need to account for adding a key that was already in the set
1133
- this.lengthInternal++;
1134
- }
1135
- contains(key) {
1136
- return this.data[key] === true;
1137
- }
1138
- elements() {
1139
- return Object.keys(this.data);
1140
- }
1141
- get length() {
1142
- return this.lengthInternal;
1143
- }
1144
- overlaps(other) {
1145
- const otherKeys = other.elements();
1146
- for (let j = 0; j < otherKeys.length; ++j) {
1147
- if (this.contains(otherKeys[j])) {
1148
- return true;
1149
- }
1150
- }
1151
- return false;
1152
- }
1153
- difference(other) {
1154
- const value = new KeySetImpl();
1155
- this.elements().forEach((key) => {
1156
- if (!other.contains(key)) {
1157
- value.add(key);
1158
- }
1159
- });
1160
- return value;
1161
- }
1162
- isSubsetOf(other) {
1163
- return this.difference(other).length === 0;
1164
- }
1165
- equals(other) {
1166
- return this.length === other.length && this.elements().every((key) => other.contains(key));
1167
- }
1168
- toString() {
1169
- return `<<${JSON.stringify(this.elements())}>>`;
1170
- }
1171
- }
1172
-
1173
1237
  /* eslint-disable no-dupe-class-members */
1174
1238
  /**
1175
1239
  A utility class for recording actions made on a cache.
@@ -1177,10 +1241,10 @@ class KeySetImpl {
1177
1241
  class DefaultRecordableCache {
1178
1242
  constructor(baseCache) {
1179
1243
  this.baseCache = baseCache;
1180
- this.keysRead = new KeySetImpl();
1181
- this.missingKeysRead = new KeySetImpl();
1182
- this.keysUpdated = new KeySetImpl();
1183
- this.metadataKeysUpdated = new KeySetImpl();
1244
+ this.keysRead = new Set();
1245
+ this.missingKeysRead = new Set();
1246
+ this.keysUpdated = new Set();
1247
+ this.metadataKeysUpdated = new Set();
1184
1248
  }
1185
1249
  delete(key) {
1186
1250
  this.keysUpdated.add(key);
@@ -1212,8 +1276,8 @@ class DefaultRecordableCache {
1212
1276
  keys() {
1213
1277
  return this.baseCache.keys();
1214
1278
  }
1215
- toKeySet(keys) {
1216
- return this.baseCache.toKeySet(keys);
1279
+ entries() {
1280
+ return this.baseCache.entries();
1217
1281
  }
1218
1282
  record() {
1219
1283
  return new DefaultRecordableCache(this);
@@ -1221,6 +1285,9 @@ class DefaultRecordableCache {
1221
1285
  filter(predicate) {
1222
1286
  return new DefaultFilteredCache(this, predicate);
1223
1287
  }
1288
+ buildFixedTimeWritableCache(generatedTime) {
1289
+ return new FixedTimeWritableCache(this, generatedTime);
1290
+ }
1224
1291
  }
1225
1292
  /**
1226
1293
  A utility class for filtering cache entries based on a given predicate.
@@ -1251,13 +1318,13 @@ class DefaultFilteredCache {
1251
1318
  this.baseCache.setMetadata(key, cacheControlMetadata);
1252
1319
  }
1253
1320
  length() {
1254
- return this.getFilteredKeys().length;
1321
+ return this.getFilteredKeys().size;
1255
1322
  }
1256
1323
  keys() {
1257
1324
  return this.getFilteredKeys();
1258
1325
  }
1259
- toKeySet(keys) {
1260
- return this.baseCache.toKeySet(keys);
1326
+ entries() {
1327
+ return this.getFilteredEntries();
1261
1328
  }
1262
1329
  record() {
1263
1330
  return new DefaultRecordableCache(this);
@@ -1265,18 +1332,71 @@ class DefaultFilteredCache {
1265
1332
  filter(predicate) {
1266
1333
  return new DefaultFilteredCache(this, predicate);
1267
1334
  }
1335
+ getFilteredEntries() {
1336
+ return this.baseCache.entries().filter(([key, _value]) => {
1337
+ return this.get(key);
1338
+ });
1339
+ }
1268
1340
  getFilteredKeys() {
1269
- const filteredKeySet = new KeySetImpl();
1270
- this.baseCache
1271
- .keys()
1272
- .elements()
1273
- .forEach((key) => {
1341
+ const filteredKeySet = new Set();
1342
+ this.baseCache.keys().forEach((key) => {
1274
1343
  if (this.get(key)) {
1275
1344
  filteredKeySet.add(key);
1276
1345
  }
1277
1346
  });
1278
1347
  return filteredKeySet;
1279
1348
  }
1349
+ buildFixedTimeWritableCache(generatedTime) {
1350
+ return new FixedTimeWritableCache(this, generatedTime);
1351
+ }
1352
+ }
1353
+ /**
1354
+ A utility class for automatically setting the generated time on all write operations
1355
+ */
1356
+ class FixedTimeWritableCache {
1357
+ constructor(baseCache, generatedTime) {
1358
+ this.baseCache = baseCache;
1359
+ this.generatedTime = generatedTime;
1360
+ }
1361
+ delete(key) {
1362
+ this.baseCache.delete(key);
1363
+ }
1364
+ get(key, options) {
1365
+ return this.baseCache.get(key, options);
1366
+ }
1367
+ set(key, value) {
1368
+ this.baseCache.set(key, {
1369
+ ...value,
1370
+ metadata: {
1371
+ ...value.metadata,
1372
+ cacheControl: { ...value.metadata.cacheControl, generatedTime: this.generatedTime },
1373
+ },
1374
+ });
1375
+ }
1376
+ setMetadata(key, cacheControlMetadata) {
1377
+ this.baseCache.setMetadata(key, {
1378
+ ...cacheControlMetadata,
1379
+ generatedTime: this.generatedTime,
1380
+ });
1381
+ }
1382
+ length() {
1383
+ return this.baseCache.length();
1384
+ }
1385
+ keys() {
1386
+ return this.baseCache.keys();
1387
+ }
1388
+ entries() {
1389
+ return this.baseCache.entries();
1390
+ }
1391
+ record() {
1392
+ return new DefaultRecordableCache(this);
1393
+ }
1394
+ filter(predicate) {
1395
+ return new DefaultFilteredCache(this, predicate);
1396
+ }
1397
+ buildFixedTimeWritableCache(generatedTime) {
1398
+ return new FixedTimeWritableCache(this, generatedTime);
1399
+ }
1280
1400
  }
1281
1401
 
1282
1402
  /* eslint-disable no-dupe-class-members */
@@ -1300,7 +1420,20 @@ class DefaultCache {
1300
1420
  if (entry.metadata.cacheControl.type === 'no-store') {
1301
1421
  return;
1302
1422
  }
1303
- this.data[key] = entry;
1423
+ this.data[key] = {
1424
+ ...entry,
1425
+ metadata: {
1426
+ ...entry.metadata,
1427
+ type: entry.metadata.type || {
1428
+ namespace: 'OneStore:Internal',
1429
+ name: 'UnknownType',
1430
+ },
1431
+ cacheControl: {
1432
+ generatedTime: Date.now() / 1000,
1433
+ ...entry.metadata.cacheControl,
1434
+ },
1435
+ },
1436
+ };
1304
1437
  }
1305
1438
  /**
1306
1439
  * Removes the cache entry associated with the specified key.
@@ -1319,17 +1452,20 @@ class DefaultCache {
1319
1452
  */
1320
1453
  setMetadata(key, cacheControlMetadata) {
1321
1454
  if (key in this.data) {
1322
- this.data[key].metadata.cacheControl = cacheControlMetadata;
1455
+ this.data[key].metadata.cacheControl = {
1456
+ generatedTime: Date.now() / 1000,
1457
+ ...cacheControlMetadata,
1458
+ };
1323
1459
  }
1324
1460
  }
1325
1461
  length() {
1326
- return this.keys().length;
1462
+ return this.keys().size;
1327
1463
  }
1328
1464
  keys() {
1329
- return new KeySetImpl(Object.keys(this.data));
1465
+ return new Set(Object.keys(this.data));
1330
1466
  }
1331
- toKeySet(keys) {
1332
- return new KeySetImpl(keys);
1467
+ entries() {
1468
+ return Object.entries(this.data);
1333
1469
  }
1334
1470
  record() {
1335
1471
  return new DefaultRecordableCache(this);
@@ -1337,6 +1473,9 @@ class DefaultCache {
1337
1473
  filter(predicate) {
1338
1474
  return new DefaultFilteredCache(this, predicate);
1339
1475
  }
1476
+ buildFixedTimeWritableCache(generatedTime) {
1477
+ return new FixedTimeWritableCache(this, generatedTime);
1478
+ }
1340
1479
  }
1341
1480
 
1342
1481
  function buildServiceDescriptor$3() {
@@ -1379,15 +1518,19 @@ class CacheControlStrategy {
1379
1518
  }
1380
1519
 
1381
1520
  class MaxAgeCacheControlStrategy extends CacheControlStrategy {
1382
- execute() {
1521
+ execute(options) {
1522
+ const startTime = this.services.instrumentation
1523
+ ? this.services.instrumentation.currentTimeMs()
1524
+ : 0;
1383
1525
  return this.requestRunner.readFromCache().then((value) => {
1384
1526
  if (this.isCacheHit(value)) {
1527
+ this.collectCacheHitInstrumentation(startTime, options === null || options === void 0 ? void 0 : options.instrumentationAttributes);
1385
1528
  return value;
1386
1529
  }
1387
- return this.handleCacheMiss();
1530
+ return this.handleCacheMiss(startTime, options === null || options === void 0 ? void 0 : options.instrumentationAttributes);
1388
1531
  });
1389
1532
  }
1390
- handleCacheMiss() {
1533
+ handleCacheMiss(startTime, instrumentationAttributes) {
1391
1534
  let error;
1392
1535
  return this.requestRunner
1393
1536
  .requestFromNetwork()
@@ -1411,17 +1554,155 @@ class MaxAgeCacheControlStrategy extends CacheControlStrategy {
1411
1554
  if (!this.isCacheHit(value)) {
1412
1555
  return err(new Error('Cache miss after fetching from network'));
1413
1556
  }
1557
+ this.collectCacheMissInstrumentation(startTime, instrumentationAttributes);
1414
1558
  return value;
1415
1559
  });
1416
1560
  }
1561
+ collectCacheHitInstrumentation(startTime, instrumentationAttributes) {
1562
+ if (this.services.instrumentation) {
1563
+ const meter = this.services.instrumentation.metrics.getMeter('onestore');
1564
+ meter
1565
+ .createCounter(`command.max-age.cache-hit.count`)
1566
+ .add(1, instrumentationAttributes);
1567
+ meter
1568
+ .createHistogram(`command.max-age.cache-hit.duration`)
1569
+ .record(this.services.instrumentation.currentTimeMs() - startTime, instrumentationAttributes);
1570
+ }
1571
+ }
1572
+ collectCacheMissInstrumentation(startTime, instrumentationAttributes) {
1573
+ if (this.services.instrumentation) {
1574
+ const meter = this.services.instrumentation.metrics.getMeter('onestore');
1575
+ meter
1576
+ .createCounter(`command.max-age.cache-miss.count`)
1577
+ .add(1, instrumentationAttributes);
1578
+ meter
1579
+ .createHistogram(`command.max-age.cache-miss.duration`)
1580
+ .record(this.services.instrumentation.currentTimeMs() - startTime, instrumentationAttributes);
1581
+ }
1582
+ }
1417
1583
  get expiredChecks() {
1584
+ const config = this.config;
1418
1585
  return [
1419
1586
  ...super.expiredChecks,
1420
- (cacheControlMetadata) => cacheControlMetadata.generatedTime + this.config.requestMaxAge < this.config.now,
1587
+ (cacheControlMetadata) => {
1588
+ return cacheControlMetadata.generatedTime + config.requestMaxAge < config.now;
1589
+ },
1421
1590
  ];
1422
1591
  }
1423
1592
  }
1424
1593
 
1594
+ class NoCacheCacheControlStrategy extends CacheControlStrategy {
1595
+ execute() {
1596
+ return this.requestRunner.requestFromNetwork().then((networkResult) => {
1597
+ if (networkResult.isErr()) {
1598
+ return networkResult;
1599
+ }
1600
+ // Attempt to write to the cache
1601
+ return this.requestRunner
1602
+ .writeToCache(networkResult)
1603
+ .then(() => this.requestRunner.readFromCache())
1604
+ .then((cachedResult) => {
1605
+ if (!this.isCacheHit(cachedResult)) {
1606
+ return err(new Error('Cache miss after saving network result'));
1607
+ }
1608
+ return cachedResult;
1609
+ });
1610
+ });
1611
+ }
1612
+ }
1613
+
1614
+ const isAndQuery = (query) => '$and' in query;
1615
+ const isOrQuery = (query) => '$or' in query;
1616
+ const isNotQuery = (query) => '$not' in query;
1617
+ const matchesMetadata = (metadataQuery, cacheControl) => {
1618
+ var _a;
1619
+ if ('cacheControlType' in metadataQuery &&
1620
+ cacheControl.type !== metadataQuery.cacheControlType.$eq) {
1621
+ return false;
1622
+ }
1623
+ if ('maxAge' in metadataQuery && cacheControl.type === 'max-age') {
1624
+ const maxAge = (_a = cacheControl.maxAge) !== null && _a !== void 0 ? _a : 0;
1625
+ if ((metadataQuery.maxAge.$gte !== undefined && maxAge < metadataQuery.maxAge.$gte) ||
1626
+ (metadataQuery.maxAge.$lte !== undefined && maxAge > metadataQuery.maxAge.$lte)) {
1627
+ return false;
1628
+ }
1629
+ }
1630
+ return true;
1631
+ };
1632
+ function queryToPredicate(query) {
1633
+ return (key, entry) => {
1634
+ if (!query)
1635
+ return false;
1636
+ if (isAndQuery(query))
1637
+ return query.$and.every((subQuery) => queryToPredicate(subQuery)(key, entry));
1638
+ if (isOrQuery(query))
1639
+ return query.$or.some((subQuery) => queryToPredicate(subQuery)(key, entry));
1640
+ if (isNotQuery(query))
1641
+ return !queryToPredicate(query.$not)(key, entry);
1642
+ if ('key' in query)
1643
+ return matchesKey(query.key, key);
1644
+ if ('metadata' in query)
1645
+ return matchesMetadata(query.metadata, entry.metadata.cacheControl);
1646
+ if ('value' in query)
1647
+ return false; // TODO: Not implemented
1648
+ throw new Error('Unknown Query Operation');
1649
+ };
1650
+ }
1651
+ function matchesKey(keyQuery, key) {
1652
+ if ('$regex' in keyQuery) {
1653
+ return keyQuery.$regex.test(key);
1654
+ }
1655
+ return false;
1656
+ }
1657
+
1658
+ /**
1659
+ * Processes a cache update operation and determines the appropriate modification.
1660
+ *
1661
+ * This function analyzes the provided `update` and the `existing` cache entry
1662
+ * to determine the necessary update type. It returns one of three possible outcomes:
1663
+ *
1664
+ * - `{ type: 'entry', entry }`: A full cache entry update, including both value and metadata.
1665
+ * - `{ type: 'metadata', metadata }`: A metadata-only update, leaving the value unchanged.
1666
+ * - `{ type: 'no-op' }`: No changes are needed, and the cache should remain as is.
1667
+ *
1668
+ * @param update - The cache update operation to apply.
1669
+ * @param existing - The existing cache entry being modified.
1670
+ * @returns An object indicating the type of update:
1671
+ * - A full cache entry update (`type: 'entry'`)
1672
+ * - A metadata-only update (`type: 'metadata'`)
1673
+ * - A no-op (`type: 'no-op'`) if no changes are required.
1674
+ */
1675
+ function buildUpdate(update, existing) {
1676
+ switch (update.type) {
1677
+ case 'invalidate':
1678
+ const updatedCacheControl = buildInvalidatedCacheControl(existing.metadata.cacheControl);
1679
+ return updatedCacheControl !== undefined
1680
+ ? { type: 'metadata', metadata: updatedCacheControl }
1681
+ : { type: 'no-op' };
1682
+ default:
1683
+ throw new Error(`Invalid update operation: ${update.type}`);
1684
+ }
1685
+ }
1686
+ /**
1687
+ * Builds an updated CacheControlMetadata object that invalidates the cache entry.
1688
+ *
1689
+ * @param existingCacheControl - The current CacheControlMetadata.
1690
+ * @returns A new CacheControlMetadata object with `maxAge` set to `0`, or undefined if no changes are needed.
1691
+ */
1692
+ function buildInvalidatedCacheControl(existingCacheControl) {
1693
+ switch (existingCacheControl.type) {
1694
+ case 'max-age':
1695
+ case 'stale-while-revalidate':
1696
+ if (existingCacheControl.maxAge !== 0) {
1697
+ return {
1698
+ ...existingCacheControl,
1699
+ maxAge: 0,
1700
+ };
1701
+ }
1702
+ }
1703
+ return undefined; // No-op: no changes
1704
+ }
1705
+
1425
1706
  /**
1426
1707
  * A class that allows the execution of requested cache control strategies,
1427
1708
  * while also enforcing the canonical cache control metadata.
@@ -1430,16 +1711,50 @@ class CacheController {
1430
1711
  constructor(services) {
1431
1712
  this.services = services;
1432
1713
  }
1433
- execute(config, buildRequestRunner) {
1714
+ execute(config, buildRequestRunner, options) {
1434
1715
  const strategy = this.getCacheControlStrategy(config, buildRequestRunner);
1435
- return strategy.execute();
1716
+ return strategy.execute(options);
1436
1717
  }
1437
1718
  getCacheControlStrategy(config, buildRequestRunner) {
1438
1719
  if (config.type === 'max-age') {
1439
1720
  return new MaxAgeCacheControlStrategy(this.services, config, buildRequestRunner);
1440
1721
  }
1722
+ else if (config.type === 'no-cache') {
1723
+ return new NoCacheCacheControlStrategy(this.services, config, buildRequestRunner);
1724
+ }
1441
1725
  throw new Error(`Unknown cache control strategy ${config.type}`);
1442
1726
  }
1727
+ /**
1728
+ * Finds cache entries that match the given query.
1729
+ * Returns an async generator that yields `[key, entry]`.
1730
+ */
1731
+ async *find(query) {
1732
+ const cache = this.services.cache;
1733
+ const predicate = queryToPredicate(query);
1734
+ const filteredEntries = cache.filter(predicate).entries();
1735
+ for (const entry of filteredEntries) {
1736
+ yield entry;
1737
+ }
1738
+ }
1739
+ /**
1740
+ * Finds and modifies cache entries that match the given query.
1741
+ * Extends `find(query)` and returns an async generator of modified keys.
1742
+ */
1743
+ async *findAndModify(query, cacheUpdate) {
1744
+ for await (const [key, value] of this.find(query)) {
1745
+ const update = buildUpdate(cacheUpdate, value);
1746
+ switch (update.type) {
1747
+ case 'entry':
1748
+ this.services.cache.set(key, update.entry);
1749
+ yield key;
1750
+ break;
1751
+ case 'metadata':
1752
+ this.services.cache.setMetadata(key, update.metadata);
1753
+ yield key;
1754
+ break;
1755
+ }
1756
+ }
1757
+ }
1443
1758
  }
1444
1759
 
1445
1760
  function buildServiceDescriptor$2(cache) {
@@ -2069,7 +2384,7 @@ var TypeCheckShapes;
2069
2384
  TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
2070
2385
  TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
2071
2386
  })(TypeCheckShapes || (TypeCheckShapes = {}));
2072
- // engine version: 0.156.6-042d5d87
2387
+ // engine version: 0.156.7-d2b9c7ef
2073
2388
 
2074
2389
  const { keys: keys$1 } = Object;
2075
2390
 
@@ -4926,6 +5241,22 @@ function buildAuraPrefetchStorage(options = {}) {
4926
5241
  }
4927
5242
  return new AuraPrefetchStorage(auraStorage, inMemoryStorage, onPredictionsReadyCallback);
4928
5243
  }
5244
+ function buildAuraLocalStoragePrefetchStorage(options = {}) {
5245
+ const { onPredictionsReadyCallback, ...storageOptions } = options;
5246
+ const auraStorage = createStorage({
5247
+ ...DEFAULT_STORAGE_OPTIONS,
5248
+ ...storageOptions,
5249
+ secure: false,
5250
+ adapter: 'localstorage',
5251
+ name: 'pdl',
5252
+ maxSize: 4096000, // 4MB
5253
+ });
5254
+ if (auraStorage === null) {
5255
+ return buildAuraPrefetchStorage(options);
5256
+ }
5257
+ const inMemoryStorage = new InMemoryPrefetchStorage();
5258
+ return new AuraPrefetchStorage(auraStorage, inMemoryStorage, onPredictionsReadyCallback);
5259
+ }
4929
5260
  class AuraPrefetchStorage {
4930
5261
  constructor(auraStorage, inMemoryStorage, onPredictionsReadyCallback = () => { }) {
4931
5262
  this.auraStorage = auraStorage;
@@ -4963,6 +5294,26 @@ class AuraPrefetchStorage {
4963
5294
  }
4964
5295
  }
4965
5296
 
5297
+ function isLocalStorageAvailable() {
5298
+ try {
5299
+ const testKey = 'test';
5300
+ window.localStorage.setItem(testKey, '1');
5301
+ window.localStorage.removeItem(testKey);
5302
+ return true;
5303
+ }
5304
+ catch (e) {
5305
+ return false;
5306
+ }
5307
+ }
5308
+ function buildPrefetchStorage(useLocalStorage, options) {
5309
+ if (useLocalStorage && isLocalStorageAvailable()) {
5310
+ return buildAuraLocalStoragePrefetchStorage(options);
5311
+ }
5312
+ else {
5313
+ return buildAuraPrefetchStorage(options);
5314
+ }
5315
+ }
5316
+
4966
5317
  class NoComposedAdapterTypeError extends TypeError {
4967
5318
  constructor(message, resourceRequest) {
4968
5319
  super(message);
@@ -5018,7 +5369,7 @@ function getEnvironmentSetting(name) {
5018
5369
  }
5019
5370
  return undefined;
5020
5371
  }
5021
- // version: 1.347.0-31679f40fc
5372
+ // version: 1.348.0-4aa8c0c0cb
5022
5373
 
5023
5374
  const forceRecordTransactionsDisabled = getEnvironmentSetting(EnvironmentSettings.ForceRecordTransactionsDisabled);
5024
5375
  //TODO: Some duplication here that can be most likely moved to a util class
@@ -5511,7 +5862,7 @@ function setupPredictivePrefetcher(luvio) {
5511
5862
  new GetRelatedListActionsRequestStrategy(luvio),
5512
5863
  ];
5513
5864
  requestStrategyManager.register(allStrategies);
5514
- const storage = buildAuraPrefetchStorage({
5865
+ const storage = buildPrefetchStorage(useLocalStorage.isOpen({ fallback: false }), {
5515
5866
  onPredictionsReadyCallback: () => predictionsReadyManager.notifyPredictionsReady(),
5516
5867
  });
5517
5868
  const requestRunner = new LexRequestRunner(requestStrategyManager);
@@ -5708,4 +6059,4 @@ function ldsEngineCreator() {
5708
6059
  }
5709
6060
 
5710
6061
  export { LexRequestStrategy, PdlRequestPriority, buildPredictorForContext, ldsEngineCreator as default, initializeLDS, initializeOneStore, registerRequestStrategy, saveRequestAsPrediction, unregisterRequestStrategy, whenPredictionsReady };
5711
- // version: 1.347.0-ef72dda80d
6062
+ // version: 1.348.0-09ae8e3e37
@@ -11,9 +11,11 @@ export declare const DEFAULT_STORAGE_OPTIONS: {
11
11
  debugLogging: boolean;
12
12
  version: number;
13
13
  };
14
- export declare function buildAuraPrefetchStorage(options?: Partial<AuraStorageConfig> & {
14
+ export type StorageOptions = Partial<AuraStorageConfig> & {
15
15
  onPredictionsReadyCallback?: () => void;
16
- }): PrefetchStorage;
16
+ };
17
+ export declare function buildAuraPrefetchStorage(options?: StorageOptions): PrefetchStorage;
18
+ export declare function buildAuraLocalStoragePrefetchStorage(options?: StorageOptions): PrefetchStorage;
17
19
  export declare class AuraPrefetchStorage implements PrefetchStorage {
18
20
  private auraStorage;
19
21
  private inMemoryStorage;
@@ -1,7 +1,8 @@
1
+ import { StorageOptions } from './aura-prefetch-storage';
1
2
  export type PrefetchStorage = {
2
3
  get<T>(key: string): T | undefined;
3
4
  set<T>(key: string, value: T): Promise<void>;
4
5
  };
6
+ export declare function buildPrefetchStorage(useLocalStorage: boolean, options: StorageOptions): PrefetchStorage;
5
7
  export * from './in-memory-prefetch-storage';
6
8
  export * from './local-prefetch-storage';
7
- export { buildAuraPrefetchStorage } from './aura-prefetch-storage';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-aura",
3
- "version": "1.347.0",
3
+ "version": "1.348.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS engine for Aura runtime",
6
6
  "main": "dist/ldsEngineCreator.js",
@@ -34,44 +34,44 @@
34
34
  "release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-runtime-aura"
35
35
  },
36
36
  "devDependencies": {
37
- "@luvio/service-provisioner": "5.29.0",
38
- "@salesforce/lds-adapters-apex": "^1.347.0",
39
- "@salesforce/lds-adapters-uiapi": "^1.347.0",
37
+ "@luvio/service-provisioner": "5.32.1",
38
+ "@salesforce/lds-adapters-apex": "^1.348.0",
39
+ "@salesforce/lds-adapters-uiapi": "^1.348.0",
40
40
  "@salesforce/lds-adapters-uiapi-lex": "^1.302.0",
41
- "@salesforce/lds-ads-bridge": "^1.347.0",
42
- "@salesforce/lds-aura-storage": "^1.347.0",
43
- "@salesforce/lds-bindings": "^1.347.0",
44
- "@salesforce/lds-instrumentation": "^1.347.0",
45
- "@salesforce/lds-network-aura": "^1.347.0",
46
- "@salesforce/lds-network-fetch": "^1.347.0",
41
+ "@salesforce/lds-ads-bridge": "^1.348.0",
42
+ "@salesforce/lds-aura-storage": "^1.348.0",
43
+ "@salesforce/lds-bindings": "^1.348.0",
44
+ "@salesforce/lds-instrumentation": "^1.348.0",
45
+ "@salesforce/lds-network-aura": "^1.348.0",
46
+ "@salesforce/lds-network-fetch": "^1.348.0",
47
47
  "jwt-encode": "1.0.1"
48
48
  },
49
49
  "dependencies": {
50
- "@luvio/command-aura-network": "5.29.0",
51
- "@luvio/command-aura-resource-cache-control": "5.29.0",
52
- "@luvio/command-fetch-network": "5.29.0",
53
- "@luvio/command-network": "5.29.0",
54
- "@luvio/command-sse": "5.29.0",
55
- "@luvio/command-streaming": "5.29.0",
56
- "@luvio/network-adapter-composable": "0.156.6",
57
- "@luvio/network-adapter-fetch": "0.156.6",
58
- "@luvio/service-aura-network": "5.29.0",
59
- "@luvio/service-cache": "5.29.0",
60
- "@luvio/service-cache-control": "5.29.0",
61
- "@luvio/service-fetch-network": "5.29.0",
62
- "@luvio/service-instrument-command": "5.29.0",
63
- "@luvio/service-pubsub": "5.29.0",
64
- "@luvio/service-store": "5.29.0",
65
- "@luvio/utils": "5.29.0",
66
- "@salesforce/lds-adapters-uiapi-lex": "^1.347.0"
50
+ "@luvio/command-aura-network": "5.32.1",
51
+ "@luvio/command-aura-resource-cache-control": "5.32.1",
52
+ "@luvio/command-fetch-network": "5.32.1",
53
+ "@luvio/command-network": "5.32.1",
54
+ "@luvio/command-sse": "5.32.1",
55
+ "@luvio/command-streaming": "5.32.1",
56
+ "@luvio/network-adapter-composable": "0.156.7",
57
+ "@luvio/network-adapter-fetch": "0.156.7",
58
+ "@luvio/service-aura-network": "5.32.1",
59
+ "@luvio/service-cache": "5.32.1",
60
+ "@luvio/service-cache-control": "5.32.1",
61
+ "@luvio/service-fetch-network": "5.32.1",
62
+ "@luvio/service-instrument-command": "5.32.1",
63
+ "@luvio/service-pubsub": "5.32.1",
64
+ "@luvio/service-store": "5.32.1",
65
+ "@luvio/utils": "5.32.1",
66
+ "@salesforce/lds-adapters-uiapi-lex": "^1.348.0"
67
67
  },
68
68
  "luvioBundlesize": [
69
69
  {
70
70
  "path": "./dist/ldsEngineCreator.js",
71
71
  "maxSize": {
72
- "none": "215 kB",
73
- "min": "89 kB",
74
- "compressed": "38 kB"
72
+ "none": "226.5 kB",
73
+ "min": "93.5 kB",
74
+ "compressed": "39.5 kB"
75
75
  }
76
76
  }
77
77
  ],