@salesforce/lds-runtime-aura 1.347.1 → 1.348.1

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';
@@ -428,12 +429,18 @@ class CacheControlCommand extends BaseCommand {
428
429
  constructor(services) {
429
430
  super();
430
431
  this.services = services;
431
- this.keyUnsubscribe = () => { };
432
+ this.rebuildUnsubscribe = () => { };
433
+ this.refreshUnsubscribe = () => { };
432
434
  this.subscriptions = [];
433
- }
434
- execute() {
435
- this.keyUnsubscribe();
436
- const resultPromise = this.services.cacheController.execute(this.cacheControlStrategyConfig, (cache) => this.buildRequestRunner(cache));
435
+ this.instantiationTime = Date.now() / 1000; // in seconds
436
+ }
437
+ execute(overrides) {
438
+ this.rebuildUnsubscribe();
439
+ this.refreshUnsubscribe();
440
+ const mergedCacheControlConfig = mergeCacheControlConfigs(this.cacheControlStrategyConfig, overrides);
441
+ const resultPromise = this.services.cacheController.execute(mergedCacheControlConfig, (cache) => this.buildRequestRunner(cache), {
442
+ instrumentationAttributes: this.instrumentationAttributes,
443
+ });
437
444
  return resultPromise;
438
445
  }
439
446
  // TODO: This should likely be abstract in v2. For v1, provide default comparison logic.
@@ -452,6 +459,7 @@ class CacheControlCommand extends BaseCommand {
452
459
  writeToCacheAndPublish(cache, networkResult) {
453
460
  const recordableCache = cache.record();
454
461
  const writeResult = this.writeToCache(recordableCache, networkResult);
462
+ this.instantiationTime = Date.now() / 1000;
455
463
  if (this.services.pubSub) {
456
464
  this.services.pubSub.publish({
457
465
  type: 'cacheUpdate',
@@ -498,17 +506,12 @@ class CacheControlCommand extends BaseCommand {
498
506
  };
499
507
  }
500
508
  subscribeToKeys(keysRead, lastResult) {
501
- if (this.services.pubSub === undefined) {
509
+ const { pubSub } = this.services;
510
+ if (!pubSub) {
502
511
  return;
503
512
  }
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) => {
513
+ const executeAndHandleResult = (overrides) => {
514
+ return this.execute(overrides).then((result) => {
512
515
  if (result.isErr()) {
513
516
  this.invokeConsumerCallbacks(result);
514
517
  return;
@@ -518,6 +521,13 @@ class CacheControlCommand extends BaseCommand {
518
521
  }
519
522
  });
520
523
  };
524
+ const createKeySubscriber = (type, callback) => pubSub.subscribe({
525
+ type,
526
+ predicate: (event) => setOverlaps(event.data, keysRead),
527
+ callback,
528
+ });
529
+ this.rebuildUnsubscribe = createKeySubscriber('cacheUpdate', () => executeAndHandleResult({ now: this.instantiationTime }));
530
+ this.refreshUnsubscribe = createKeySubscriber('cacheInvalidation', () => executeAndHandleResult());
521
531
  }
522
532
  invokeConsumerCallbacks(data) {
523
533
  this.subscriptions.forEach((cb) => {
@@ -530,6 +540,45 @@ class CacheControlCommand extends BaseCommand {
530
540
  });
531
541
  }
532
542
  }
543
+ /**
544
+ * Merges two CacheControlStrategyConfig objects
545
+ *
546
+ */
547
+ function mergeCacheControlConfigs(baseConfig, overrides) {
548
+ var _a;
549
+ if (!overrides) {
550
+ return baseConfig;
551
+ }
552
+ const now = (_a = overrides.now) !== null && _a !== void 0 ? _a : baseConfig.now;
553
+ if (!overrides.cacheControlConfig) {
554
+ return {
555
+ ...baseConfig,
556
+ now,
557
+ };
558
+ }
559
+ return {
560
+ ...overrides.cacheControlConfig,
561
+ now,
562
+ };
563
+ }
564
+ // Needs TS 2023+ (esnext) AND node 20+ to remove
565
+ function setOverlaps(setA, setB) {
566
+ if (setA.size > setB.size) {
567
+ for (const key of setB.keys()) {
568
+ if (setA.has(key)) {
569
+ return true;
570
+ }
571
+ }
572
+ }
573
+ else {
574
+ for (const key of setA) {
575
+ if (setB.has(key)) {
576
+ return true;
577
+ }
578
+ }
579
+ }
580
+ return false;
581
+ }
533
582
 
534
583
  /**
535
584
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -545,7 +594,7 @@ class CacheControlCommand extends BaseCommand {
545
594
  * @typeParam NetworkResult cache result including network metadata
546
595
  * @typeParam ExtraServices additional named services needed by a subclass
547
596
  */
548
- class AuraResourceCacheControlCommand extends CacheControlCommand {
597
+ class AuraCacheControlCommand extends CacheControlCommand {
549
598
  constructor(services) {
550
599
  super(services);
551
600
  this.services = services;
@@ -556,15 +605,8 @@ class AuraResourceCacheControlCommand extends CacheControlCommand {
556
605
  storable: false,
557
606
  };
558
607
  }
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
608
  requestFromNetwork() {
567
- return this.convertAuraResponseToData(this.services.auraNetwork(this.endpoint, this.auraParams, this.actionConfig), this.coerceError);
609
+ return this.convertAuraResponseToData(this.services.auraNetwork(this.endpoint, this.auraParams, this.actionConfig), (errs) => this.coerceError(errs));
568
610
  }
569
611
  coerceError(auraErrors) {
570
612
  return toError(auraErrors[0]); // Default Implementation stringifies the response
@@ -585,6 +627,34 @@ class AuraResourceCacheControlCommand extends CacheControlCommand {
585
627
  return err(toError('Error fetching component'));
586
628
  });
587
629
  }
630
+ }
631
+
632
+ /**
633
+ * Copyright (c) 2022, Salesforce, Inc.,
634
+ * All rights reserved.
635
+ * For full license text, see the LICENSE.txt file
636
+ */
637
+
638
+
639
+ /**
640
+ * An implementation of BaseCommand that allows for extending abstract cache methods
641
+ *
642
+ * @typeParam Data cache result for read operations
643
+ * @typeParam NetworkResult cache result including network metadata
644
+ * @typeParam ExtraServices additional named services needed by a subclass
645
+ */
646
+ class AuraResourceCacheControlCommand extends AuraCacheControlCommand {
647
+ constructor(services) {
648
+ super(services);
649
+ this.services = services;
650
+ }
651
+ execute() {
652
+ return super.execute();
653
+ }
654
+ readFromCache(cache) {
655
+ var _a;
656
+ return resolvedPromiseLike(ok((_a = cache.get(this.buildKey())) === null || _a === void 0 ? void 0 : _a.value));
657
+ }
588
658
  writeToCache(cache, networkResult) {
589
659
  if (networkResult.isOk()) {
590
660
  cache.set(this.buildKey(), {
@@ -623,18 +693,18 @@ class FetchNetworkCommand extends NetworkCommand {
623
693
  this.services = services;
624
694
  }
625
695
  fetch() {
626
- return this.convertFetchResponseToData(this.services.fetch(...this.fetchParams), this.coerceError);
696
+ return this.convertFetchResponseToData(this.services.fetch(...this.fetchParams));
627
697
  }
628
698
  async coerceError(errorResponse) {
629
699
  return toError(errorResponse.statusText); // Default Behavior
630
700
  }
631
- convertFetchResponseToData(response, coerceError) {
701
+ convertFetchResponseToData(response) {
632
702
  return response.then((response) => {
633
703
  if (response.ok) {
634
704
  return response.json().then((json) => ok(json), (reason) => err(toError(reason)));
635
705
  }
636
706
  else {
637
- return coerceError(response).then((coercedError) => err(coercedError));
707
+ return this.coerceError(response).then((coercedError) => err(coercedError));
638
708
  }
639
709
  }, (reason) => err(toError(reason)));
640
710
  }
@@ -818,9 +888,9 @@ function buildInstrumentCommand(services) {
818
888
  execute(...args) {
819
889
  invocationCounter.add(1);
820
890
  let result;
821
- const start = performance.now();
891
+ const start = services.instrumentation.currentTimeMs();
822
892
  function recordDuration() {
823
- const end = performance.now();
893
+ const end = services.instrumentation.currentTimeMs();
824
894
  durationHistogram.record(end - start);
825
895
  }
826
896
  try {
@@ -1027,12 +1097,12 @@ class O11yCounter {
1027
1097
  this.o11yInstrumentation = o11yInstrumentation;
1028
1098
  this.logger = logger;
1029
1099
  }
1030
- add(value, _attributes, _context) {
1100
+ add(value, attributes, _context) {
1031
1101
  if (value < 0) {
1032
1102
  this.logger.warn(`Counter values must be non-negative. Got ${value}.`);
1033
1103
  return;
1034
1104
  }
1035
- this.o11yInstrumentation.incrementCounter(this.name, value);
1105
+ this.o11yInstrumentation.incrementCounter(this.name, value, undefined, sanitizeAttributes(attributes));
1036
1106
  }
1037
1107
  }
1038
1108
  class O11yHistogram {
@@ -1041,12 +1111,12 @@ class O11yHistogram {
1041
1111
  this.o11yInstrumentation = o11yInstrumentation;
1042
1112
  this.logger = logger;
1043
1113
  }
1044
- record(value, _attributes, _context) {
1114
+ record(value, attributes, _context) {
1045
1115
  if (value < 0) {
1046
1116
  this.logger.warn(`Histogram values must be non-negative. Got ${value}.`);
1047
1117
  return;
1048
1118
  }
1049
- this.o11yInstrumentation.trackValue(this.name, value);
1119
+ this.o11yInstrumentation.trackValue(this.name, value, undefined, sanitizeAttributes(attributes));
1050
1120
  }
1051
1121
  }
1052
1122
  class O11yUpDownCounter {
@@ -1090,10 +1160,22 @@ class O11yObservableUpDownCounter {
1090
1160
  this.logger.warn('O11yObservableUpDownCounter not supported yet. Defaulting to noop.');
1091
1161
  }
1092
1162
  }
1163
+ function sanitizeAttributes(attributes) {
1164
+ if (!attributes)
1165
+ return;
1166
+ const metricTags = {};
1167
+ Object.entries(attributes).forEach(([key, value]) => {
1168
+ if (value !== undefined && !Array.isArray(value)) {
1169
+ metricTags[key] = value;
1170
+ }
1171
+ });
1172
+ return metricTags;
1173
+ }
1093
1174
 
1094
1175
  class O11yInstrumentation {
1095
1176
  constructor(services) {
1096
1177
  this.services = services;
1178
+ this.currentTimeMs = () => performance.now();
1097
1179
  this.trace = new O11yOTelTraceAPI(this.services);
1098
1180
  this.metrics = new O11yOTelMetricsAPI(this.services);
1099
1181
  }
@@ -1113,63 +1195,6 @@ function buildServiceDescriptor$4(logger) {
1113
1195
  */
1114
1196
 
1115
1197
 
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
1198
  /* eslint-disable no-dupe-class-members */
1174
1199
  /**
1175
1200
  A utility class for recording actions made on a cache.
@@ -1177,10 +1202,10 @@ class KeySetImpl {
1177
1202
  class DefaultRecordableCache {
1178
1203
  constructor(baseCache) {
1179
1204
  this.baseCache = baseCache;
1180
- this.keysRead = new KeySetImpl();
1181
- this.missingKeysRead = new KeySetImpl();
1182
- this.keysUpdated = new KeySetImpl();
1183
- this.metadataKeysUpdated = new KeySetImpl();
1205
+ this.keysRead = new Set();
1206
+ this.missingKeysRead = new Set();
1207
+ this.keysUpdated = new Set();
1208
+ this.metadataKeysUpdated = new Set();
1184
1209
  }
1185
1210
  delete(key) {
1186
1211
  this.keysUpdated.add(key);
@@ -1212,8 +1237,8 @@ class DefaultRecordableCache {
1212
1237
  keys() {
1213
1238
  return this.baseCache.keys();
1214
1239
  }
1215
- toKeySet(keys) {
1216
- return this.baseCache.toKeySet(keys);
1240
+ entries() {
1241
+ return this.baseCache.entries();
1217
1242
  }
1218
1243
  record() {
1219
1244
  return new DefaultRecordableCache(this);
@@ -1221,6 +1246,9 @@ class DefaultRecordableCache {
1221
1246
  filter(predicate) {
1222
1247
  return new DefaultFilteredCache(this, predicate);
1223
1248
  }
1249
+ buildFixedTimeWritableCache(generatedTime) {
1250
+ return new FixedTimeWritableCache(this, generatedTime);
1251
+ }
1224
1252
  }
1225
1253
  /**
1226
1254
  A utility class for filtering cache entries based on a given predicate.
@@ -1251,13 +1279,13 @@ class DefaultFilteredCache {
1251
1279
  this.baseCache.setMetadata(key, cacheControlMetadata);
1252
1280
  }
1253
1281
  length() {
1254
- return this.getFilteredKeys().length;
1282
+ return this.getFilteredKeys().size;
1255
1283
  }
1256
1284
  keys() {
1257
1285
  return this.getFilteredKeys();
1258
1286
  }
1259
- toKeySet(keys) {
1260
- return this.baseCache.toKeySet(keys);
1287
+ entries() {
1288
+ return this.getFilteredEntries();
1261
1289
  }
1262
1290
  record() {
1263
1291
  return new DefaultRecordableCache(this);
@@ -1265,18 +1293,71 @@ class DefaultFilteredCache {
1265
1293
  filter(predicate) {
1266
1294
  return new DefaultFilteredCache(this, predicate);
1267
1295
  }
1296
+ getFilteredEntries() {
1297
+ return this.baseCache.entries().filter(([key, _value]) => {
1298
+ return this.get(key);
1299
+ });
1300
+ }
1268
1301
  getFilteredKeys() {
1269
- const filteredKeySet = new KeySetImpl();
1270
- this.baseCache
1271
- .keys()
1272
- .elements()
1273
- .forEach((key) => {
1302
+ const filteredKeySet = new Set();
1303
+ this.baseCache.keys().forEach((key) => {
1274
1304
  if (this.get(key)) {
1275
1305
  filteredKeySet.add(key);
1276
1306
  }
1277
1307
  });
1278
1308
  return filteredKeySet;
1279
1309
  }
1310
+ buildFixedTimeWritableCache(generatedTime) {
1311
+ return new FixedTimeWritableCache(this, generatedTime);
1312
+ }
1313
+ }
1314
+ /**
1315
+ A utility class for automatically setting the generated time on all write operations
1316
+ */
1317
+ class FixedTimeWritableCache {
1318
+ constructor(baseCache, generatedTime) {
1319
+ this.baseCache = baseCache;
1320
+ this.generatedTime = generatedTime;
1321
+ }
1322
+ delete(key) {
1323
+ this.baseCache.delete(key);
1324
+ }
1325
+ get(key, options) {
1326
+ return this.baseCache.get(key, options);
1327
+ }
1328
+ set(key, value) {
1329
+ this.baseCache.set(key, {
1330
+ ...value,
1331
+ metadata: {
1332
+ ...value.metadata,
1333
+ cacheControl: { ...value.metadata.cacheControl, generatedTime: this.generatedTime },
1334
+ },
1335
+ });
1336
+ }
1337
+ setMetadata(key, cacheControlMetadata) {
1338
+ this.baseCache.setMetadata(key, {
1339
+ ...cacheControlMetadata,
1340
+ generatedTime: this.generatedTime,
1341
+ });
1342
+ }
1343
+ length() {
1344
+ return this.baseCache.length();
1345
+ }
1346
+ keys() {
1347
+ return this.baseCache.keys();
1348
+ }
1349
+ entries() {
1350
+ return this.baseCache.entries();
1351
+ }
1352
+ record() {
1353
+ return new DefaultRecordableCache(this);
1354
+ }
1355
+ filter(predicate) {
1356
+ return new DefaultFilteredCache(this, predicate);
1357
+ }
1358
+ buildFixedTimeWritableCache(generatedTime) {
1359
+ return new FixedTimeWritableCache(this, generatedTime);
1360
+ }
1280
1361
  }
1281
1362
 
1282
1363
  /* eslint-disable no-dupe-class-members */
@@ -1300,7 +1381,20 @@ class DefaultCache {
1300
1381
  if (entry.metadata.cacheControl.type === 'no-store') {
1301
1382
  return;
1302
1383
  }
1303
- this.data[key] = entry;
1384
+ this.data[key] = {
1385
+ ...entry,
1386
+ metadata: {
1387
+ ...entry.metadata,
1388
+ type: entry.metadata.type || {
1389
+ namespace: 'OneStore:Internal',
1390
+ name: 'UnknownType',
1391
+ },
1392
+ cacheControl: {
1393
+ generatedTime: Date.now() / 1000,
1394
+ ...entry.metadata.cacheControl,
1395
+ },
1396
+ },
1397
+ };
1304
1398
  }
1305
1399
  /**
1306
1400
  * Removes the cache entry associated with the specified key.
@@ -1319,17 +1413,20 @@ class DefaultCache {
1319
1413
  */
1320
1414
  setMetadata(key, cacheControlMetadata) {
1321
1415
  if (key in this.data) {
1322
- this.data[key].metadata.cacheControl = cacheControlMetadata;
1416
+ this.data[key].metadata.cacheControl = {
1417
+ generatedTime: Date.now() / 1000,
1418
+ ...cacheControlMetadata,
1419
+ };
1323
1420
  }
1324
1421
  }
1325
1422
  length() {
1326
- return this.keys().length;
1423
+ return this.keys().size;
1327
1424
  }
1328
1425
  keys() {
1329
- return new KeySetImpl(Object.keys(this.data));
1426
+ return new Set(Object.keys(this.data));
1330
1427
  }
1331
- toKeySet(keys) {
1332
- return new KeySetImpl(keys);
1428
+ entries() {
1429
+ return Object.entries(this.data);
1333
1430
  }
1334
1431
  record() {
1335
1432
  return new DefaultRecordableCache(this);
@@ -1337,6 +1434,9 @@ class DefaultCache {
1337
1434
  filter(predicate) {
1338
1435
  return new DefaultFilteredCache(this, predicate);
1339
1436
  }
1437
+ buildFixedTimeWritableCache(generatedTime) {
1438
+ return new FixedTimeWritableCache(this, generatedTime);
1439
+ }
1340
1440
  }
1341
1441
 
1342
1442
  function buildServiceDescriptor$3() {
@@ -1379,15 +1479,19 @@ class CacheControlStrategy {
1379
1479
  }
1380
1480
 
1381
1481
  class MaxAgeCacheControlStrategy extends CacheControlStrategy {
1382
- execute() {
1482
+ execute(options) {
1483
+ const startTime = this.services.instrumentation
1484
+ ? this.services.instrumentation.currentTimeMs()
1485
+ : 0;
1383
1486
  return this.requestRunner.readFromCache().then((value) => {
1384
1487
  if (this.isCacheHit(value)) {
1488
+ this.collectCacheHitInstrumentation(startTime, options === null || options === void 0 ? void 0 : options.instrumentationAttributes);
1385
1489
  return value;
1386
1490
  }
1387
- return this.handleCacheMiss();
1491
+ return this.handleCacheMiss(startTime, options === null || options === void 0 ? void 0 : options.instrumentationAttributes);
1388
1492
  });
1389
1493
  }
1390
- handleCacheMiss() {
1494
+ handleCacheMiss(startTime, instrumentationAttributes) {
1391
1495
  let error;
1392
1496
  return this.requestRunner
1393
1497
  .requestFromNetwork()
@@ -1411,17 +1515,155 @@ class MaxAgeCacheControlStrategy extends CacheControlStrategy {
1411
1515
  if (!this.isCacheHit(value)) {
1412
1516
  return err(new Error('Cache miss after fetching from network'));
1413
1517
  }
1518
+ this.collectCacheMissInstrumentation(startTime, instrumentationAttributes);
1414
1519
  return value;
1415
1520
  });
1416
1521
  }
1522
+ collectCacheHitInstrumentation(startTime, instrumentationAttributes) {
1523
+ if (this.services.instrumentation) {
1524
+ const meter = this.services.instrumentation.metrics.getMeter('onestore');
1525
+ meter
1526
+ .createCounter(`command.max-age.cache-hit.count`)
1527
+ .add(1, instrumentationAttributes);
1528
+ meter
1529
+ .createHistogram(`command.max-age.cache-hit.duration`)
1530
+ .record(this.services.instrumentation.currentTimeMs() - startTime, instrumentationAttributes);
1531
+ }
1532
+ }
1533
+ collectCacheMissInstrumentation(startTime, instrumentationAttributes) {
1534
+ if (this.services.instrumentation) {
1535
+ const meter = this.services.instrumentation.metrics.getMeter('onestore');
1536
+ meter
1537
+ .createCounter(`command.max-age.cache-miss.count`)
1538
+ .add(1, instrumentationAttributes);
1539
+ meter
1540
+ .createHistogram(`command.max-age.cache-miss.duration`)
1541
+ .record(this.services.instrumentation.currentTimeMs() - startTime, instrumentationAttributes);
1542
+ }
1543
+ }
1417
1544
  get expiredChecks() {
1545
+ const config = this.config;
1418
1546
  return [
1419
1547
  ...super.expiredChecks,
1420
- (cacheControlMetadata) => cacheControlMetadata.generatedTime + this.config.requestMaxAge < this.config.now,
1548
+ (cacheControlMetadata) => {
1549
+ return cacheControlMetadata.generatedTime + config.requestMaxAge < config.now;
1550
+ },
1421
1551
  ];
1422
1552
  }
1423
1553
  }
1424
1554
 
1555
+ class NoCacheCacheControlStrategy extends CacheControlStrategy {
1556
+ execute() {
1557
+ return this.requestRunner.requestFromNetwork().then((networkResult) => {
1558
+ if (networkResult.isErr()) {
1559
+ return networkResult;
1560
+ }
1561
+ // Attempt to write to the cache
1562
+ return this.requestRunner
1563
+ .writeToCache(networkResult)
1564
+ .then(() => this.requestRunner.readFromCache())
1565
+ .then((cachedResult) => {
1566
+ if (!this.isCacheHit(cachedResult)) {
1567
+ return err(new Error('Cache miss after saving network result'));
1568
+ }
1569
+ return cachedResult;
1570
+ });
1571
+ });
1572
+ }
1573
+ }
1574
+
1575
+ const isAndQuery = (query) => '$and' in query;
1576
+ const isOrQuery = (query) => '$or' in query;
1577
+ const isNotQuery = (query) => '$not' in query;
1578
+ const matchesMetadata = (metadataQuery, cacheControl) => {
1579
+ var _a;
1580
+ if ('cacheControlType' in metadataQuery &&
1581
+ cacheControl.type !== metadataQuery.cacheControlType.$eq) {
1582
+ return false;
1583
+ }
1584
+ if ('maxAge' in metadataQuery && cacheControl.type === 'max-age') {
1585
+ const maxAge = (_a = cacheControl.maxAge) !== null && _a !== void 0 ? _a : 0;
1586
+ if ((metadataQuery.maxAge.$gte !== undefined && maxAge < metadataQuery.maxAge.$gte) ||
1587
+ (metadataQuery.maxAge.$lte !== undefined && maxAge > metadataQuery.maxAge.$lte)) {
1588
+ return false;
1589
+ }
1590
+ }
1591
+ return true;
1592
+ };
1593
+ function queryToPredicate(query) {
1594
+ return (key, entry) => {
1595
+ if (!query)
1596
+ return false;
1597
+ if (isAndQuery(query))
1598
+ return query.$and.every((subQuery) => queryToPredicate(subQuery)(key, entry));
1599
+ if (isOrQuery(query))
1600
+ return query.$or.some((subQuery) => queryToPredicate(subQuery)(key, entry));
1601
+ if (isNotQuery(query))
1602
+ return !queryToPredicate(query.$not)(key, entry);
1603
+ if ('key' in query)
1604
+ return matchesKey(query.key, key);
1605
+ if ('metadata' in query)
1606
+ return matchesMetadata(query.metadata, entry.metadata.cacheControl);
1607
+ if ('value' in query)
1608
+ return false; // TODO: Not implemented
1609
+ throw new Error('Unknown Query Operation');
1610
+ };
1611
+ }
1612
+ function matchesKey(keyQuery, key) {
1613
+ if ('$regex' in keyQuery) {
1614
+ return keyQuery.$regex.test(key);
1615
+ }
1616
+ return false;
1617
+ }
1618
+
1619
+ /**
1620
+ * Processes a cache update operation and determines the appropriate modification.
1621
+ *
1622
+ * This function analyzes the provided `update` and the `existing` cache entry
1623
+ * to determine the necessary update type. It returns one of three possible outcomes:
1624
+ *
1625
+ * - `{ type: 'entry', entry }`: A full cache entry update, including both value and metadata.
1626
+ * - `{ type: 'metadata', metadata }`: A metadata-only update, leaving the value unchanged.
1627
+ * - `{ type: 'no-op' }`: No changes are needed, and the cache should remain as is.
1628
+ *
1629
+ * @param update - The cache update operation to apply.
1630
+ * @param existing - The existing cache entry being modified.
1631
+ * @returns An object indicating the type of update:
1632
+ * - A full cache entry update (`type: 'entry'`)
1633
+ * - A metadata-only update (`type: 'metadata'`)
1634
+ * - A no-op (`type: 'no-op'`) if no changes are required.
1635
+ */
1636
+ function buildUpdate(update, existing) {
1637
+ switch (update.type) {
1638
+ case 'invalidate':
1639
+ const updatedCacheControl = buildInvalidatedCacheControl(existing.metadata.cacheControl);
1640
+ return updatedCacheControl !== undefined
1641
+ ? { type: 'metadata', metadata: updatedCacheControl }
1642
+ : { type: 'no-op' };
1643
+ default:
1644
+ throw new Error(`Invalid update operation: ${update.type}`);
1645
+ }
1646
+ }
1647
+ /**
1648
+ * Builds an updated CacheControlMetadata object that invalidates the cache entry.
1649
+ *
1650
+ * @param existingCacheControl - The current CacheControlMetadata.
1651
+ * @returns A new CacheControlMetadata object with `maxAge` set to `0`, or undefined if no changes are needed.
1652
+ */
1653
+ function buildInvalidatedCacheControl(existingCacheControl) {
1654
+ switch (existingCacheControl.type) {
1655
+ case 'max-age':
1656
+ case 'stale-while-revalidate':
1657
+ if (existingCacheControl.maxAge !== 0) {
1658
+ return {
1659
+ ...existingCacheControl,
1660
+ maxAge: 0,
1661
+ };
1662
+ }
1663
+ }
1664
+ return undefined; // No-op: no changes
1665
+ }
1666
+
1425
1667
  /**
1426
1668
  * A class that allows the execution of requested cache control strategies,
1427
1669
  * while also enforcing the canonical cache control metadata.
@@ -1430,16 +1672,50 @@ class CacheController {
1430
1672
  constructor(services) {
1431
1673
  this.services = services;
1432
1674
  }
1433
- execute(config, buildRequestRunner) {
1675
+ execute(config, buildRequestRunner, options) {
1434
1676
  const strategy = this.getCacheControlStrategy(config, buildRequestRunner);
1435
- return strategy.execute();
1677
+ return strategy.execute(options);
1436
1678
  }
1437
1679
  getCacheControlStrategy(config, buildRequestRunner) {
1438
1680
  if (config.type === 'max-age') {
1439
1681
  return new MaxAgeCacheControlStrategy(this.services, config, buildRequestRunner);
1440
1682
  }
1683
+ else if (config.type === 'no-cache') {
1684
+ return new NoCacheCacheControlStrategy(this.services, config, buildRequestRunner);
1685
+ }
1441
1686
  throw new Error(`Unknown cache control strategy ${config.type}`);
1442
1687
  }
1688
+ /**
1689
+ * Finds cache entries that match the given query.
1690
+ * Returns an async generator that yields `[key, entry]`.
1691
+ */
1692
+ async *find(query) {
1693
+ const cache = this.services.cache;
1694
+ const predicate = queryToPredicate(query);
1695
+ const filteredEntries = cache.filter(predicate).entries();
1696
+ for (const entry of filteredEntries) {
1697
+ yield entry;
1698
+ }
1699
+ }
1700
+ /**
1701
+ * Finds and modifies cache entries that match the given query.
1702
+ * Extends `find(query)` and returns an async generator of modified keys.
1703
+ */
1704
+ async *findAndModify(query, cacheUpdate) {
1705
+ for await (const [key, value] of this.find(query)) {
1706
+ const update = buildUpdate(cacheUpdate, value);
1707
+ switch (update.type) {
1708
+ case 'entry':
1709
+ this.services.cache.set(key, update.entry);
1710
+ yield key;
1711
+ break;
1712
+ case 'metadata':
1713
+ this.services.cache.setMetadata(key, update.metadata);
1714
+ yield key;
1715
+ break;
1716
+ }
1717
+ }
1718
+ }
1443
1719
  }
1444
1720
 
1445
1721
  function buildServiceDescriptor$2(cache) {
@@ -4926,6 +5202,22 @@ function buildAuraPrefetchStorage(options = {}) {
4926
5202
  }
4927
5203
  return new AuraPrefetchStorage(auraStorage, inMemoryStorage, onPredictionsReadyCallback);
4928
5204
  }
5205
+ function buildAuraLocalStoragePrefetchStorage(options = {}) {
5206
+ const { onPredictionsReadyCallback, ...storageOptions } = options;
5207
+ const auraStorage = createStorage({
5208
+ ...DEFAULT_STORAGE_OPTIONS,
5209
+ ...storageOptions,
5210
+ secure: false,
5211
+ adapter: 'localstorage',
5212
+ name: 'pdl',
5213
+ maxSize: 4096000, // 4MB
5214
+ });
5215
+ if (auraStorage === null) {
5216
+ return buildAuraPrefetchStorage(options);
5217
+ }
5218
+ const inMemoryStorage = new InMemoryPrefetchStorage();
5219
+ return new AuraPrefetchStorage(auraStorage, inMemoryStorage, onPredictionsReadyCallback);
5220
+ }
4929
5221
  class AuraPrefetchStorage {
4930
5222
  constructor(auraStorage, inMemoryStorage, onPredictionsReadyCallback = () => { }) {
4931
5223
  this.auraStorage = auraStorage;
@@ -4963,6 +5255,26 @@ class AuraPrefetchStorage {
4963
5255
  }
4964
5256
  }
4965
5257
 
5258
+ function isLocalStorageAvailable() {
5259
+ try {
5260
+ const testKey = 'test';
5261
+ window.localStorage.setItem(testKey, '1');
5262
+ window.localStorage.removeItem(testKey);
5263
+ return true;
5264
+ }
5265
+ catch (e) {
5266
+ return false;
5267
+ }
5268
+ }
5269
+ function buildPrefetchStorage(useLocalStorage, options) {
5270
+ if (useLocalStorage && isLocalStorageAvailable()) {
5271
+ return buildAuraLocalStoragePrefetchStorage(options);
5272
+ }
5273
+ else {
5274
+ return buildAuraPrefetchStorage(options);
5275
+ }
5276
+ }
5277
+
4966
5278
  class NoComposedAdapterTypeError extends TypeError {
4967
5279
  constructor(message, resourceRequest) {
4968
5280
  super(message);
@@ -5018,7 +5330,7 @@ function getEnvironmentSetting(name) {
5018
5330
  }
5019
5331
  return undefined;
5020
5332
  }
5021
- // version: 1.347.1-60ff64f212
5333
+ // version: 1.348.1-163f0bd450
5022
5334
 
5023
5335
  const forceRecordTransactionsDisabled = getEnvironmentSetting(EnvironmentSettings.ForceRecordTransactionsDisabled);
5024
5336
  //TODO: Some duplication here that can be most likely moved to a util class
@@ -5511,7 +5823,7 @@ function setupPredictivePrefetcher(luvio) {
5511
5823
  new GetRelatedListActionsRequestStrategy(luvio),
5512
5824
  ];
5513
5825
  requestStrategyManager.register(allStrategies);
5514
- const storage = buildAuraPrefetchStorage({
5826
+ const storage = buildPrefetchStorage(useLocalStorage.isOpen({ fallback: false }), {
5515
5827
  onPredictionsReadyCallback: () => predictionsReadyManager.notifyPredictionsReady(),
5516
5828
  });
5517
5829
  const requestRunner = new LexRequestRunner(requestStrategyManager);
@@ -5708,4 +6020,4 @@ function ldsEngineCreator() {
5708
6020
  }
5709
6021
 
5710
6022
  export { LexRequestStrategy, PdlRequestPriority, buildPredictorForContext, ldsEngineCreator as default, initializeLDS, initializeOneStore, registerRequestStrategy, saveRequestAsPrediction, unregisterRequestStrategy, whenPredictionsReady };
5711
- // version: 1.347.1-71b4105fee
6023
+ // version: 1.348.1-44d4bea72f
@@ -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.1",
3
+ "version": "1.348.1",
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.1",
39
- "@salesforce/lds-adapters-uiapi": "^1.347.1",
37
+ "@luvio/service-provisioner": "5.33.0",
38
+ "@salesforce/lds-adapters-apex": "^1.348.1",
39
+ "@salesforce/lds-adapters-uiapi": "^1.348.1",
40
40
  "@salesforce/lds-adapters-uiapi-lex": "^1.302.0",
41
- "@salesforce/lds-ads-bridge": "^1.347.1",
42
- "@salesforce/lds-aura-storage": "^1.347.1",
43
- "@salesforce/lds-bindings": "^1.347.1",
44
- "@salesforce/lds-instrumentation": "^1.347.1",
45
- "@salesforce/lds-network-aura": "^1.347.1",
46
- "@salesforce/lds-network-fetch": "^1.347.1",
41
+ "@salesforce/lds-ads-bridge": "^1.348.1",
42
+ "@salesforce/lds-aura-storage": "^1.348.1",
43
+ "@salesforce/lds-bindings": "^1.348.1",
44
+ "@salesforce/lds-instrumentation": "^1.348.1",
45
+ "@salesforce/lds-network-aura": "^1.348.1",
46
+ "@salesforce/lds-network-fetch": "^1.348.1",
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",
50
+ "@luvio/command-aura-network": "5.33.0",
51
+ "@luvio/command-aura-resource-cache-control": "5.33.0",
52
+ "@luvio/command-fetch-network": "5.33.0",
53
+ "@luvio/command-network": "5.33.0",
54
+ "@luvio/command-sse": "5.33.0",
55
+ "@luvio/command-streaming": "5.33.0",
56
56
  "@luvio/network-adapter-composable": "0.156.7",
57
57
  "@luvio/network-adapter-fetch": "0.156.7",
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.1"
58
+ "@luvio/service-aura-network": "5.33.0",
59
+ "@luvio/service-cache": "5.33.0",
60
+ "@luvio/service-cache-control": "5.33.0",
61
+ "@luvio/service-fetch-network": "5.33.0",
62
+ "@luvio/service-instrument-command": "5.33.0",
63
+ "@luvio/service-pubsub": "5.33.0",
64
+ "@luvio/service-store": "5.33.0",
65
+ "@luvio/utils": "5.33.0",
66
+ "@salesforce/lds-adapters-uiapi-lex": "^1.348.1"
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
  ],