@salesforce/lds-runtime-mobile 1.145.0 → 1.147.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -518,6 +518,7 @@ function isDeprecatedDurableStoreEntry(durableRecord) {
518
518
  return false;
519
519
  }
520
520
  const DefaultDurableSegment = 'DEFAULT';
521
+ const RedirectDurableSegment = 'REDIRECT_KEYS';
521
522
 
522
523
  const { keys: keys$6, create: create$5, assign: assign$4, freeze: freeze$1 } = Object;
523
524
 
@@ -761,7 +762,7 @@ class DurableTTLStore {
761
762
  }
762
763
  }
763
764
 
764
- function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, additionalDurableStoreOperations = []) {
765
+ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations = []) {
765
766
  const durableRecords = create$5(null);
766
767
  const evictedRecords = create$5(null);
767
768
  const { records, metadata: storeMetadata, visitedIds, refreshedIds, } = store.fallbackStringKeyInMemoryStore;
@@ -798,6 +799,18 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
798
799
  segment: DefaultDurableSegment,
799
800
  });
800
801
  }
802
+ // redirects
803
+ redirects.forEach((value, key) => {
804
+ durableStoreOperations.push({
805
+ type: 'setEntries',
806
+ entries: {
807
+ [key]: {
808
+ data: { key, redirect: value },
809
+ },
810
+ },
811
+ segment: RedirectDurableSegment,
812
+ });
813
+ });
801
814
  // evicts
802
815
  const evictedKeys = keys$6(evictedRecords);
803
816
  if (evictedKeys.length > 0) {
@@ -843,6 +856,19 @@ function buildIngestStagingStore(environment) {
843
856
  return environment.storeBuildIngestionStagingStore();
844
857
  }
845
858
 
859
+ async function reviveRedirects(durableStore, env) {
860
+ const entries = await durableStore.getAllEntries(RedirectDurableSegment);
861
+ if (entries) {
862
+ for (const durableEntry of Object.keys(entries)) {
863
+ const entry = entries[durableEntry];
864
+ const { data: { key, redirect }, } = entry;
865
+ if (entry) {
866
+ env.storeRedirect(key, redirect);
867
+ }
868
+ }
869
+ }
870
+ }
871
+
846
872
  const AdapterContextSegment = 'ADAPTER-CONTEXT';
847
873
  const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
848
874
  async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
@@ -906,13 +932,18 @@ function makeDurable(environment, { durableStore, instrumentation }) {
906
932
  // event. If this instance of makeDurable caused that L2 write we can ignore that
907
933
  // on change event. This Set helps us do that.
908
934
  const pendingContextStoreKeys = new Set();
935
+ // redirects that need to be flushed to the durable store
936
+ const pendingStoreRedirects = new Map();
909
937
  const contextStores = create$5(null);
910
938
  let initializationPromise = new Promise((resolve) => {
911
939
  const finish = () => {
912
940
  resolve();
913
941
  initializationPromise = undefined;
914
942
  };
915
- reviveTTLOverrides(durableTTLStore, environment).then(finish);
943
+ Promise.all([
944
+ reviveTTLOverrides(durableTTLStore, environment),
945
+ reviveRedirects(durableStore, environment),
946
+ ]).then(finish);
916
947
  });
917
948
  //instrumentation for durable store errors
918
949
  const durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
@@ -925,6 +956,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
925
956
  const unsubscribe = durableStore.registerOnChangedListener(async (changes) => {
926
957
  const defaultSegmentKeys = [];
927
958
  const adapterContextSegmentKeys = [];
959
+ const redirectSegmentKeys = [];
960
+ let shouldBroadcast = false;
928
961
  for (let i = 0, len = changes.length; i < len; i++) {
929
962
  const change = changes[i];
930
963
  // we only care about changes to the data which is stored in the default
@@ -935,6 +968,20 @@ function makeDurable(environment, { durableStore, instrumentation }) {
935
968
  else if (change.segment === AdapterContextSegment) {
936
969
  adapterContextSegmentKeys.push(...change.ids);
937
970
  }
971
+ else if (change.segment === RedirectDurableSegment) {
972
+ redirectSegmentKeys.push(...change.ids);
973
+ }
974
+ }
975
+ if (redirectSegmentKeys.length > 0) {
976
+ const redirectEntries = await durableStore.getEntries(redirectSegmentKeys, RedirectDurableSegment);
977
+ if (redirectEntries !== undefined) {
978
+ const redirectKeys = Object.keys(redirectEntries);
979
+ for (const key of redirectKeys) {
980
+ const redirectData = redirectEntries[key];
981
+ environment.storeRedirect(redirectData.data.key, redirectData.data.redirect);
982
+ shouldBroadcast = true;
983
+ }
984
+ }
938
985
  }
939
986
  // process adapter context changes
940
987
  const adapterContextKeysFromDifferentInstance = [];
@@ -971,10 +1018,6 @@ function makeDurable(environment, { durableStore, instrumentation }) {
971
1018
  if (defaultSegmentKeysLength > 0) {
972
1019
  for (let i = 0; i < defaultSegmentKeysLength; i++) {
973
1020
  const key = defaultSegmentKeys[i];
974
- const canonical = environment.storeGetCanonicalKey(key);
975
- if (canonical !== key) {
976
- continue;
977
- }
978
1021
  // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
979
1022
  // if we stored expiration and data at different keys (or same keys in different segments)
980
1023
  // then we could know if only the expiration has changed and we wouldn't need to evict
@@ -982,6 +1025,9 @@ function makeDurable(environment, { durableStore, instrumentation }) {
982
1025
  // call base environment storeEvict so this evict is not tracked for durable deletion
983
1026
  environment.storeEvict(key);
984
1027
  }
1028
+ shouldBroadcast = true;
1029
+ }
1030
+ if (shouldBroadcast) {
985
1031
  await environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
986
1032
  }
987
1033
  });
@@ -1037,7 +1083,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1037
1083
  if (ingestStagingStore === null) {
1038
1084
  return Promise.resolve();
1039
1085
  }
1040
- const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler, additionalDurableStoreOperations);
1086
+ const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler, new Map(pendingStoreRedirects), additionalDurableStoreOperations);
1087
+ pendingStoreRedirects.clear();
1041
1088
  ingestStagingStore = null;
1042
1089
  return promise;
1043
1090
  };
@@ -1119,6 +1166,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1119
1166
  };
1120
1167
  const storeRedirect = function (existingKey, canonicalKey) {
1121
1168
  validateNotDisposed();
1169
+ pendingStoreRedirects.set(existingKey, canonicalKey);
1122
1170
  // call redirect on staging store so "old" keys are removed from L2 on
1123
1171
  // the next publishChangesToDurableStore. NOTE: we don't need to call
1124
1172
  // redirect on the base environment store because staging store and base
@@ -4830,7 +4878,6 @@ function buildLuvioOverrideForDraftAdapters(luvio, handler, extractTargetIdFromC
4830
4878
  }
4831
4879
 
4832
4880
  const DraftIdMappingKeyPrefix240 = 'DraftIdMapping::';
4833
- const DraftKeyMappingKeyPrefix = 'DraftKeyMapping::V2::';
4834
4881
  const DRAFT_ID_MAPPINGS_SEGMENT = 'DRAFT_ID_MAPPINGS';
4835
4882
  function isLegacyDraftIdMapping(key, data) {
4836
4883
  return key.startsWith(DraftIdMappingKeyPrefix240);
@@ -4840,9 +4887,6 @@ function isLegacyDraftIdMapping(key, data) {
4840
4887
  function getRecordKeyForId$1(id) {
4841
4888
  return `UiApi::RecordRepresentation:${id}`;
4842
4889
  }
4843
- function generateDraftIdMappingKey(draftIdMapping) {
4844
- return `${DraftKeyMappingKeyPrefix}${draftIdMapping.draftKey}::${draftIdMapping.canonicalKey}`;
4845
- }
4846
4890
  /**
4847
4891
  *
4848
4892
  * @param mappingIds (optional) requested mapping ids, if undefined all will be retrieved
@@ -4876,6 +4920,15 @@ async function getDraftIdMappings(durableStore, mappingIds) {
4876
4920
  }
4877
4921
  return mappings;
4878
4922
  }
4923
+ async function clearDraftIdSegment(durableStore) {
4924
+ const entries = await durableStore.getAllEntries(DRAFT_ID_MAPPINGS_SEGMENT);
4925
+ if (entries) {
4926
+ const keys$1 = keys$4(entries);
4927
+ if (keys$1.length > 0) {
4928
+ await durableStore.evictEntries(keys$1, DRAFT_ID_MAPPINGS_SEGMENT);
4929
+ }
4930
+ }
4931
+ }
4879
4932
 
4880
4933
  /**
4881
4934
  * Generates a time-ordered, unique id to associate with a DraftAction. Ensures
@@ -4966,9 +5019,6 @@ function customActionHandler(executor, id, draftQueue) {
4966
5019
  });
4967
5020
  return queueOperations;
4968
5021
  };
4969
- const getRedirectMappings = (_action) => {
4970
- return undefined;
4971
- };
4972
5022
  return {
4973
5023
  handlerId: id,
4974
5024
  enqueue: (data) => {
@@ -4980,7 +5030,6 @@ function customActionHandler(executor, id, draftQueue) {
4980
5030
  handleReplaceAction: () => {
4981
5031
  throw Error('replaceAction not supported for custom actions');
4982
5032
  },
4983
- getRedirectMappings,
4984
5033
  handleActionRemoved: () => Promise.resolve(),
4985
5034
  handleActionCompleted: () => Promise.resolve(),
4986
5035
  handleActionEnqueued: () => Promise.resolve(),
@@ -5155,17 +5204,11 @@ class DurableDraftQueue {
5155
5204
  const handler = this.getHandler(action.handler);
5156
5205
  let queue = await this.getQueueActions();
5157
5206
  const queueOperations = handler.getQueueOperationsForCompletingDrafts(queue, action);
5158
- const idAndKeyMappings = handler.getRedirectMappings(action);
5159
- const keyMappings = idAndKeyMappings === undefined
5160
- ? undefined
5161
- : idAndKeyMappings.map((m) => {
5162
- return { draftKey: m.draftKey, canonicalKey: m.canonicalKey };
5163
- });
5164
- await this.draftStore.completeAction(queueOperations, keyMappings);
5165
- queue = await this.getQueueActions();
5207
+ // write the queue operations to the store prior to ingesting the result
5208
+ await this.draftStore.completeAction(queueOperations);
5209
+ await handler.handleActionCompleted(action, queueOperations, values$1(this.handlers));
5166
5210
  this.retryIntervalMilliseconds = 0;
5167
5211
  this.uploadingActionId = undefined;
5168
- await handler.handleActionCompleted(action, queueOperations, queue, values$1(this.handlers));
5169
5212
  await this.notifyChangedListeners({
5170
5213
  type: DraftQueueEventType.ActionCompleted,
5171
5214
  action,
@@ -5488,7 +5531,7 @@ class DurableDraftStore {
5488
5531
  };
5489
5532
  return this.enqueueAction(deleteAction);
5490
5533
  }
5491
- completeAction(queueOperations, mappings) {
5534
+ completeAction(queueOperations) {
5492
5535
  const action = () => {
5493
5536
  const durableStoreOperations = [];
5494
5537
  const { draftStore } = this;
@@ -5521,18 +5564,6 @@ class DurableDraftStore {
5521
5564
  });
5522
5565
  }
5523
5566
  }
5524
- if (mappings !== undefined) {
5525
- const entries = {};
5526
- for (const mapping of mappings) {
5527
- const mappingKey = generateDraftIdMappingKey(mapping);
5528
- entries[mappingKey] = { data: mapping };
5529
- }
5530
- durableStoreOperations.push({
5531
- entries,
5532
- type: 'setEntries',
5533
- segment: DRAFT_ID_MAPPINGS_SEGMENT,
5534
- });
5535
- }
5536
5567
  return this.durableStore.batchOperations(durableStoreOperations);
5537
5568
  };
5538
5569
  return this.enqueueAction(action);
@@ -5811,7 +5842,7 @@ class AbstractResourceRequestActionHandler {
5811
5842
  },
5812
5843
  ];
5813
5844
  }
5814
- async handleActionCompleted(action, queueOperations, _queue, allHandlers) {
5845
+ async handleActionCompleted(action, queueOperations, allHandlers) {
5815
5846
  const { data: request, tag } = action;
5816
5847
  const { method } = request;
5817
5848
  if (method === 'delete') {
@@ -5925,11 +5956,18 @@ class AbstractResourceRequestActionHandler {
5925
5956
  async ingestResponses(responses, action) {
5926
5957
  const luvio = this.getLuvio();
5927
5958
  await luvio.handleSuccessResponse(() => {
5959
+ if (action.status === DraftActionStatus.Completed) {
5960
+ const mappings = this.getRedirectMappings(action);
5961
+ if (mappings) {
5962
+ mappings.forEach((mapping) => {
5963
+ luvio.storeRedirect(mapping.draftKey, mapping.canonicalKey);
5964
+ });
5965
+ }
5966
+ }
5928
5967
  for (const entry of responses) {
5929
5968
  const { response, synchronousIngest } = entry;
5930
5969
  synchronousIngest(response, action);
5931
5970
  }
5932
- // must call base broadcast
5933
5971
  return luvio.storeBroadcast();
5934
5972
  },
5935
5973
  // getTypeCacheKeysRecord uses the response, not the full path factory
@@ -6258,6 +6296,8 @@ class DraftManager {
6258
6296
 
6259
6297
  function makeEnvironmentDraftAware(luvio, env, durableStore, handlers, draftQueue) {
6260
6298
  const draftMetadata = {};
6299
+ // in 246 luvio took charge of persisting redirect mappings, this needs to stick around
6300
+ // for a couple of releases to support older environments
6261
6301
  // setup existing store redirects when bootstrapping the environment
6262
6302
  (async () => {
6263
6303
  const mappings = await getDraftIdMappings(durableStore);
@@ -6265,23 +6305,9 @@ function makeEnvironmentDraftAware(luvio, env, durableStore, handlers, draftQueu
6265
6305
  const { draftKey, canonicalKey } = mapping;
6266
6306
  env.storeRedirect(draftKey, canonicalKey);
6267
6307
  });
6308
+ await env.storeBroadcast(env.rebuildSnapshot, env.snapshotAvailable);
6309
+ await clearDraftIdSegment(durableStore);
6268
6310
  })();
6269
- durableStore.registerOnChangedListener(async (changes) => {
6270
- const draftIdMappingsIds = [];
6271
- for (let i = 0, len = changes.length; i < len; i++) {
6272
- const change = changes[i];
6273
- if (change.segment === DRAFT_ID_MAPPINGS_SEGMENT) {
6274
- draftIdMappingsIds.push(...change.ids);
6275
- }
6276
- }
6277
- if (draftIdMappingsIds.length > 0) {
6278
- const mappings = await getDraftIdMappings(durableStore, draftIdMappingsIds);
6279
- mappings.forEach((mapping) => {
6280
- const { draftKey, canonicalKey } = mapping;
6281
- env.storeRedirect(draftKey, canonicalKey);
6282
- });
6283
- }
6284
- });
6285
6311
  const handleSuccessResponse = async function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
6286
6312
  const queue = await draftQueue.getQueueActions();
6287
6313
  if (queue.length === 0) {
@@ -14717,6 +14743,7 @@ function instrumentAdapter(adapter, metadata) {
14717
14743
  return instrumentAdapter$1(instrumentedMobileAdapter, metadata, {
14718
14744
  trackL1Hits: true,
14719
14745
  trackL2Hits: true,
14746
+ trackCacheMisses: true,
14720
14747
  reportObserver: (report) => {
14721
14748
  for (const observer of reportObservers) {
14722
14749
  observer(report);
@@ -15019,7 +15046,7 @@ class NimbusSqliteStore {
15019
15046
  registerOnChangedListener(listener) {
15020
15047
  let unsubscribeId = undefined;
15021
15048
  this.plugin
15022
- .registerOnChangedListener((changes) => {
15049
+ .registerOnChangedListener(async (changes) => {
15023
15050
  const durableChanges = changes.map((c) => {
15024
15051
  return {
15025
15052
  type: c.type === 'upsert' ? 'setEntries' : 'evictEntries',
@@ -15028,7 +15055,7 @@ class NimbusSqliteStore {
15028
15055
  segment: c.context.segment,
15029
15056
  };
15030
15057
  });
15031
- listener(durableChanges);
15058
+ await listener(durableChanges);
15032
15059
  })
15033
15060
  .then((unsub) => {
15034
15061
  unsubscribeId = unsub;
@@ -15832,6 +15859,7 @@ function primingSessionFactory(config) {
15832
15859
  let lazyDraftQueue;
15833
15860
  let lazyDraftManager;
15834
15861
  let lazyLuvio;
15862
+ let lazyEnvironment;
15835
15863
  let lazyBaseDurableStore;
15836
15864
  let lazyNetworkAdapter;
15837
15865
  let lazyObjectInfoService;
@@ -15839,7 +15867,10 @@ let lazyObjectInfoService;
15839
15867
  * This returns the LDS on Mobile Runtime singleton object.
15840
15868
  */
15841
15869
  function getRuntime() {
15842
- if (lazyLuvio === undefined || lazyDraftManager === undefined || lazyDraftQueue === undefined) {
15870
+ if (lazyLuvio === undefined ||
15871
+ lazyEnvironment === undefined ||
15872
+ lazyDraftManager === undefined ||
15873
+ lazyDraftQueue === undefined) {
15843
15874
  // coerce runtime provided userID values to the 18 character version used in LDS
15844
15875
  const userId = getRecordId18(caseSensitiveUserId);
15845
15876
  if (userId === undefined || userId.length !== 18) {
@@ -15897,7 +15928,8 @@ function getRuntime() {
15897
15928
  isDraftId: isGenerated,
15898
15929
  }, draftEnv);
15899
15930
  draftEnv = performQuickActionDraftEnvironment(lazyLuvio, draftEnv, quickActionHandler);
15900
- lazyLuvio = new Luvio(draftEnv, {
15931
+ lazyEnvironment = draftEnv;
15932
+ lazyLuvio = new Luvio(lazyEnvironment, {
15901
15933
  instrument: instrumentLuvio,
15902
15934
  });
15903
15935
  // Currently instruments store runtime perf
@@ -15943,6 +15975,7 @@ function getRuntime() {
15943
15975
  }
15944
15976
  return {
15945
15977
  luvio: lazyLuvio,
15978
+ environment: lazyEnvironment,
15946
15979
  draftManager: lazyDraftManager,
15947
15980
  draftQueue: lazyDraftQueue,
15948
15981
  createPrimingSession: (config) => {
@@ -15970,4 +16003,4 @@ register({
15970
16003
  });
15971
16004
 
15972
16005
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15973
- // version: 1.145.0-a93c76713
16006
+ // version: 1.147.0-f9a768a91
@@ -1,9 +1,10 @@
1
- import { Luvio } from '@luvio/engine';
1
+ import { Luvio, Environment } from '@luvio/engine';
2
2
  import { DraftManager } from '@salesforce/lds-drafts';
3
3
  import type { DraftQueue } from '@salesforce/lds-drafts';
4
4
  import type { PrimingSession } from '@salesforce/lds-priming';
5
5
  export interface Runtime {
6
6
  luvio: Luvio;
7
+ environment: Environment;
7
8
  draftQueue: DraftQueue;
8
9
  draftManager: DraftManager;
9
10
  createPrimingSession: (config: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.145.0",
3
+ "version": "1.147.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
package/sfdc/main.js CHANGED
@@ -518,6 +518,7 @@ function isDeprecatedDurableStoreEntry(durableRecord) {
518
518
  return false;
519
519
  }
520
520
  const DefaultDurableSegment = 'DEFAULT';
521
+ const RedirectDurableSegment = 'REDIRECT_KEYS';
521
522
 
522
523
  const { keys: keys$6, create: create$5, assign: assign$4, freeze: freeze$1 } = Object;
523
524
 
@@ -761,7 +762,7 @@ class DurableTTLStore {
761
762
  }
762
763
  }
763
764
 
764
- function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, additionalDurableStoreOperations = []) {
765
+ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations = []) {
765
766
  const durableRecords = create$5(null);
766
767
  const evictedRecords = create$5(null);
767
768
  const { records, metadata: storeMetadata, visitedIds, refreshedIds, } = store.fallbackStringKeyInMemoryStore;
@@ -798,6 +799,18 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
798
799
  segment: DefaultDurableSegment,
799
800
  });
800
801
  }
802
+ // redirects
803
+ redirects.forEach((value, key) => {
804
+ durableStoreOperations.push({
805
+ type: 'setEntries',
806
+ entries: {
807
+ [key]: {
808
+ data: { key, redirect: value },
809
+ },
810
+ },
811
+ segment: RedirectDurableSegment,
812
+ });
813
+ });
801
814
  // evicts
802
815
  const evictedKeys = keys$6(evictedRecords);
803
816
  if (evictedKeys.length > 0) {
@@ -843,6 +856,19 @@ function buildIngestStagingStore(environment) {
843
856
  return environment.storeBuildIngestionStagingStore();
844
857
  }
845
858
 
859
+ async function reviveRedirects(durableStore, env) {
860
+ const entries = await durableStore.getAllEntries(RedirectDurableSegment);
861
+ if (entries) {
862
+ for (const durableEntry of Object.keys(entries)) {
863
+ const entry = entries[durableEntry];
864
+ const { data: { key, redirect }, } = entry;
865
+ if (entry) {
866
+ env.storeRedirect(key, redirect);
867
+ }
868
+ }
869
+ }
870
+ }
871
+
846
872
  const AdapterContextSegment = 'ADAPTER-CONTEXT';
847
873
  const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
848
874
  async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
@@ -906,13 +932,18 @@ function makeDurable(environment, { durableStore, instrumentation }) {
906
932
  // event. If this instance of makeDurable caused that L2 write we can ignore that
907
933
  // on change event. This Set helps us do that.
908
934
  const pendingContextStoreKeys = new Set();
935
+ // redirects that need to be flushed to the durable store
936
+ const pendingStoreRedirects = new Map();
909
937
  const contextStores = create$5(null);
910
938
  let initializationPromise = new Promise((resolve) => {
911
939
  const finish = () => {
912
940
  resolve();
913
941
  initializationPromise = undefined;
914
942
  };
915
- reviveTTLOverrides(durableTTLStore, environment).then(finish);
943
+ Promise.all([
944
+ reviveTTLOverrides(durableTTLStore, environment),
945
+ reviveRedirects(durableStore, environment),
946
+ ]).then(finish);
916
947
  });
917
948
  //instrumentation for durable store errors
918
949
  const durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
@@ -925,6 +956,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
925
956
  const unsubscribe = durableStore.registerOnChangedListener(async (changes) => {
926
957
  const defaultSegmentKeys = [];
927
958
  const adapterContextSegmentKeys = [];
959
+ const redirectSegmentKeys = [];
960
+ let shouldBroadcast = false;
928
961
  for (let i = 0, len = changes.length; i < len; i++) {
929
962
  const change = changes[i];
930
963
  // we only care about changes to the data which is stored in the default
@@ -935,6 +968,20 @@ function makeDurable(environment, { durableStore, instrumentation }) {
935
968
  else if (change.segment === AdapterContextSegment) {
936
969
  adapterContextSegmentKeys.push(...change.ids);
937
970
  }
971
+ else if (change.segment === RedirectDurableSegment) {
972
+ redirectSegmentKeys.push(...change.ids);
973
+ }
974
+ }
975
+ if (redirectSegmentKeys.length > 0) {
976
+ const redirectEntries = await durableStore.getEntries(redirectSegmentKeys, RedirectDurableSegment);
977
+ if (redirectEntries !== undefined) {
978
+ const redirectKeys = Object.keys(redirectEntries);
979
+ for (const key of redirectKeys) {
980
+ const redirectData = redirectEntries[key];
981
+ environment.storeRedirect(redirectData.data.key, redirectData.data.redirect);
982
+ shouldBroadcast = true;
983
+ }
984
+ }
938
985
  }
939
986
  // process adapter context changes
940
987
  const adapterContextKeysFromDifferentInstance = [];
@@ -971,10 +1018,6 @@ function makeDurable(environment, { durableStore, instrumentation }) {
971
1018
  if (defaultSegmentKeysLength > 0) {
972
1019
  for (let i = 0; i < defaultSegmentKeysLength; i++) {
973
1020
  const key = defaultSegmentKeys[i];
974
- const canonical = environment.storeGetCanonicalKey(key);
975
- if (canonical !== key) {
976
- continue;
977
- }
978
1021
  // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
979
1022
  // if we stored expiration and data at different keys (or same keys in different segments)
980
1023
  // then we could know if only the expiration has changed and we wouldn't need to evict
@@ -982,6 +1025,9 @@ function makeDurable(environment, { durableStore, instrumentation }) {
982
1025
  // call base environment storeEvict so this evict is not tracked for durable deletion
983
1026
  environment.storeEvict(key);
984
1027
  }
1028
+ shouldBroadcast = true;
1029
+ }
1030
+ if (shouldBroadcast) {
985
1031
  await environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
986
1032
  }
987
1033
  });
@@ -1037,7 +1083,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1037
1083
  if (ingestStagingStore === null) {
1038
1084
  return Promise.resolve();
1039
1085
  }
1040
- const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler, additionalDurableStoreOperations);
1086
+ const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler, new Map(pendingStoreRedirects), additionalDurableStoreOperations);
1087
+ pendingStoreRedirects.clear();
1041
1088
  ingestStagingStore = null;
1042
1089
  return promise;
1043
1090
  };
@@ -1119,6 +1166,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1119
1166
  };
1120
1167
  const storeRedirect = function (existingKey, canonicalKey) {
1121
1168
  validateNotDisposed();
1169
+ pendingStoreRedirects.set(existingKey, canonicalKey);
1122
1170
  // call redirect on staging store so "old" keys are removed from L2 on
1123
1171
  // the next publishChangesToDurableStore. NOTE: we don't need to call
1124
1172
  // redirect on the base environment store because staging store and base
@@ -4830,7 +4878,6 @@ function buildLuvioOverrideForDraftAdapters(luvio, handler, extractTargetIdFromC
4830
4878
  }
4831
4879
 
4832
4880
  const DraftIdMappingKeyPrefix240 = 'DraftIdMapping::';
4833
- const DraftKeyMappingKeyPrefix = 'DraftKeyMapping::V2::';
4834
4881
  const DRAFT_ID_MAPPINGS_SEGMENT = 'DRAFT_ID_MAPPINGS';
4835
4882
  function isLegacyDraftIdMapping(key, data) {
4836
4883
  return key.startsWith(DraftIdMappingKeyPrefix240);
@@ -4840,9 +4887,6 @@ function isLegacyDraftIdMapping(key, data) {
4840
4887
  function getRecordKeyForId$1(id) {
4841
4888
  return `UiApi::RecordRepresentation:${id}`;
4842
4889
  }
4843
- function generateDraftIdMappingKey(draftIdMapping) {
4844
- return `${DraftKeyMappingKeyPrefix}${draftIdMapping.draftKey}::${draftIdMapping.canonicalKey}`;
4845
- }
4846
4890
  /**
4847
4891
  *
4848
4892
  * @param mappingIds (optional) requested mapping ids, if undefined all will be retrieved
@@ -4876,6 +4920,15 @@ async function getDraftIdMappings(durableStore, mappingIds) {
4876
4920
  }
4877
4921
  return mappings;
4878
4922
  }
4923
+ async function clearDraftIdSegment(durableStore) {
4924
+ const entries = await durableStore.getAllEntries(DRAFT_ID_MAPPINGS_SEGMENT);
4925
+ if (entries) {
4926
+ const keys$1 = keys$4(entries);
4927
+ if (keys$1.length > 0) {
4928
+ await durableStore.evictEntries(keys$1, DRAFT_ID_MAPPINGS_SEGMENT);
4929
+ }
4930
+ }
4931
+ }
4879
4932
 
4880
4933
  /**
4881
4934
  * Generates a time-ordered, unique id to associate with a DraftAction. Ensures
@@ -4966,9 +5019,6 @@ function customActionHandler(executor, id, draftQueue) {
4966
5019
  });
4967
5020
  return queueOperations;
4968
5021
  };
4969
- const getRedirectMappings = (_action) => {
4970
- return undefined;
4971
- };
4972
5022
  return {
4973
5023
  handlerId: id,
4974
5024
  enqueue: (data) => {
@@ -4980,7 +5030,6 @@ function customActionHandler(executor, id, draftQueue) {
4980
5030
  handleReplaceAction: () => {
4981
5031
  throw Error('replaceAction not supported for custom actions');
4982
5032
  },
4983
- getRedirectMappings,
4984
5033
  handleActionRemoved: () => Promise.resolve(),
4985
5034
  handleActionCompleted: () => Promise.resolve(),
4986
5035
  handleActionEnqueued: () => Promise.resolve(),
@@ -5155,17 +5204,11 @@ class DurableDraftQueue {
5155
5204
  const handler = this.getHandler(action.handler);
5156
5205
  let queue = await this.getQueueActions();
5157
5206
  const queueOperations = handler.getQueueOperationsForCompletingDrafts(queue, action);
5158
- const idAndKeyMappings = handler.getRedirectMappings(action);
5159
- const keyMappings = idAndKeyMappings === undefined
5160
- ? undefined
5161
- : idAndKeyMappings.map((m) => {
5162
- return { draftKey: m.draftKey, canonicalKey: m.canonicalKey };
5163
- });
5164
- await this.draftStore.completeAction(queueOperations, keyMappings);
5165
- queue = await this.getQueueActions();
5207
+ // write the queue operations to the store prior to ingesting the result
5208
+ await this.draftStore.completeAction(queueOperations);
5209
+ await handler.handleActionCompleted(action, queueOperations, values$1(this.handlers));
5166
5210
  this.retryIntervalMilliseconds = 0;
5167
5211
  this.uploadingActionId = undefined;
5168
- await handler.handleActionCompleted(action, queueOperations, queue, values$1(this.handlers));
5169
5212
  await this.notifyChangedListeners({
5170
5213
  type: DraftQueueEventType.ActionCompleted,
5171
5214
  action,
@@ -5488,7 +5531,7 @@ class DurableDraftStore {
5488
5531
  };
5489
5532
  return this.enqueueAction(deleteAction);
5490
5533
  }
5491
- completeAction(queueOperations, mappings) {
5534
+ completeAction(queueOperations) {
5492
5535
  const action = () => {
5493
5536
  const durableStoreOperations = [];
5494
5537
  const { draftStore } = this;
@@ -5521,18 +5564,6 @@ class DurableDraftStore {
5521
5564
  });
5522
5565
  }
5523
5566
  }
5524
- if (mappings !== undefined) {
5525
- const entries = {};
5526
- for (const mapping of mappings) {
5527
- const mappingKey = generateDraftIdMappingKey(mapping);
5528
- entries[mappingKey] = { data: mapping };
5529
- }
5530
- durableStoreOperations.push({
5531
- entries,
5532
- type: 'setEntries',
5533
- segment: DRAFT_ID_MAPPINGS_SEGMENT,
5534
- });
5535
- }
5536
5567
  return this.durableStore.batchOperations(durableStoreOperations);
5537
5568
  };
5538
5569
  return this.enqueueAction(action);
@@ -5811,7 +5842,7 @@ class AbstractResourceRequestActionHandler {
5811
5842
  },
5812
5843
  ];
5813
5844
  }
5814
- async handleActionCompleted(action, queueOperations, _queue, allHandlers) {
5845
+ async handleActionCompleted(action, queueOperations, allHandlers) {
5815
5846
  const { data: request, tag } = action;
5816
5847
  const { method } = request;
5817
5848
  if (method === 'delete') {
@@ -5925,11 +5956,18 @@ class AbstractResourceRequestActionHandler {
5925
5956
  async ingestResponses(responses, action) {
5926
5957
  const luvio = this.getLuvio();
5927
5958
  await luvio.handleSuccessResponse(() => {
5959
+ if (action.status === DraftActionStatus.Completed) {
5960
+ const mappings = this.getRedirectMappings(action);
5961
+ if (mappings) {
5962
+ mappings.forEach((mapping) => {
5963
+ luvio.storeRedirect(mapping.draftKey, mapping.canonicalKey);
5964
+ });
5965
+ }
5966
+ }
5928
5967
  for (const entry of responses) {
5929
5968
  const { response, synchronousIngest } = entry;
5930
5969
  synchronousIngest(response, action);
5931
5970
  }
5932
- // must call base broadcast
5933
5971
  return luvio.storeBroadcast();
5934
5972
  },
5935
5973
  // getTypeCacheKeysRecord uses the response, not the full path factory
@@ -6258,6 +6296,8 @@ class DraftManager {
6258
6296
 
6259
6297
  function makeEnvironmentDraftAware(luvio, env, durableStore, handlers, draftQueue) {
6260
6298
  const draftMetadata = {};
6299
+ // in 246 luvio took charge of persisting redirect mappings, this needs to stick around
6300
+ // for a couple of releases to support older environments
6261
6301
  // setup existing store redirects when bootstrapping the environment
6262
6302
  (async () => {
6263
6303
  const mappings = await getDraftIdMappings(durableStore);
@@ -6265,23 +6305,9 @@ function makeEnvironmentDraftAware(luvio, env, durableStore, handlers, draftQueu
6265
6305
  const { draftKey, canonicalKey } = mapping;
6266
6306
  env.storeRedirect(draftKey, canonicalKey);
6267
6307
  });
6308
+ await env.storeBroadcast(env.rebuildSnapshot, env.snapshotAvailable);
6309
+ await clearDraftIdSegment(durableStore);
6268
6310
  })();
6269
- durableStore.registerOnChangedListener(async (changes) => {
6270
- const draftIdMappingsIds = [];
6271
- for (let i = 0, len = changes.length; i < len; i++) {
6272
- const change = changes[i];
6273
- if (change.segment === DRAFT_ID_MAPPINGS_SEGMENT) {
6274
- draftIdMappingsIds.push(...change.ids);
6275
- }
6276
- }
6277
- if (draftIdMappingsIds.length > 0) {
6278
- const mappings = await getDraftIdMappings(durableStore, draftIdMappingsIds);
6279
- mappings.forEach((mapping) => {
6280
- const { draftKey, canonicalKey } = mapping;
6281
- env.storeRedirect(draftKey, canonicalKey);
6282
- });
6283
- }
6284
- });
6285
6311
  const handleSuccessResponse = async function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
6286
6312
  const queue = await draftQueue.getQueueActions();
6287
6313
  if (queue.length === 0) {
@@ -14717,6 +14743,7 @@ function instrumentAdapter(adapter, metadata) {
14717
14743
  return instrumentAdapter$1(instrumentedMobileAdapter, metadata, {
14718
14744
  trackL1Hits: true,
14719
14745
  trackL2Hits: true,
14746
+ trackCacheMisses: true,
14720
14747
  reportObserver: (report) => {
14721
14748
  for (const observer of reportObservers) {
14722
14749
  observer(report);
@@ -15019,7 +15046,7 @@ class NimbusSqliteStore {
15019
15046
  registerOnChangedListener(listener) {
15020
15047
  let unsubscribeId = undefined;
15021
15048
  this.plugin
15022
- .registerOnChangedListener((changes) => {
15049
+ .registerOnChangedListener(async (changes) => {
15023
15050
  const durableChanges = changes.map((c) => {
15024
15051
  return {
15025
15052
  type: c.type === 'upsert' ? 'setEntries' : 'evictEntries',
@@ -15028,7 +15055,7 @@ class NimbusSqliteStore {
15028
15055
  segment: c.context.segment,
15029
15056
  };
15030
15057
  });
15031
- listener(durableChanges);
15058
+ await listener(durableChanges);
15032
15059
  })
15033
15060
  .then((unsub) => {
15034
15061
  unsubscribeId = unsub;
@@ -15832,6 +15859,7 @@ function primingSessionFactory(config) {
15832
15859
  let lazyDraftQueue;
15833
15860
  let lazyDraftManager;
15834
15861
  let lazyLuvio;
15862
+ let lazyEnvironment;
15835
15863
  let lazyBaseDurableStore;
15836
15864
  let lazyNetworkAdapter;
15837
15865
  let lazyObjectInfoService;
@@ -15839,7 +15867,10 @@ let lazyObjectInfoService;
15839
15867
  * This returns the LDS on Mobile Runtime singleton object.
15840
15868
  */
15841
15869
  function getRuntime() {
15842
- if (lazyLuvio === undefined || lazyDraftManager === undefined || lazyDraftQueue === undefined) {
15870
+ if (lazyLuvio === undefined ||
15871
+ lazyEnvironment === undefined ||
15872
+ lazyDraftManager === undefined ||
15873
+ lazyDraftQueue === undefined) {
15843
15874
  // coerce runtime provided userID values to the 18 character version used in LDS
15844
15875
  const userId = getRecordId18(caseSensitiveUserId);
15845
15876
  if (userId === undefined || userId.length !== 18) {
@@ -15897,7 +15928,8 @@ function getRuntime() {
15897
15928
  isDraftId: isGenerated,
15898
15929
  }, draftEnv);
15899
15930
  draftEnv = performQuickActionDraftEnvironment(lazyLuvio, draftEnv, quickActionHandler);
15900
- lazyLuvio = new Luvio(draftEnv, {
15931
+ lazyEnvironment = draftEnv;
15932
+ lazyLuvio = new Luvio(lazyEnvironment, {
15901
15933
  instrument: instrumentLuvio,
15902
15934
  });
15903
15935
  // Currently instruments store runtime perf
@@ -15943,6 +15975,7 @@ function getRuntime() {
15943
15975
  }
15944
15976
  return {
15945
15977
  luvio: lazyLuvio,
15978
+ environment: lazyEnvironment,
15946
15979
  draftManager: lazyDraftManager,
15947
15980
  draftQueue: lazyDraftQueue,
15948
15981
  createPrimingSession: (config) => {
@@ -15970,4 +16003,4 @@ register({
15970
16003
  });
15971
16004
 
15972
16005
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15973
- // version: 1.145.0-a93c76713
16006
+ // version: 1.147.0-f9a768a91
@@ -1,9 +1,10 @@
1
- import { Luvio } from '@luvio/engine';
1
+ import { Luvio, Environment } from '@luvio/engine';
2
2
  import { DraftManager } from '@salesforce/lds-drafts';
3
3
  import type { DraftQueue } from '@salesforce/lds-drafts';
4
4
  import type { PrimingSession } from '@salesforce/lds-priming';
5
5
  export interface Runtime {
6
6
  luvio: Luvio;
7
+ environment: Environment;
7
8
  draftQueue: DraftQueue;
8
9
  draftManager: DraftManager;
9
10
  createPrimingSession: (config: {