@salesforce/lds-worker-api 1.302.0 → 1.304.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.
@@ -841,14 +841,7 @@
841
841
  // of the function, in case the reference changes (because of an unsubscribe)
842
842
  const { snapshotSubscriptions } = this;
843
843
  // read metadata for each key, and mark as expired
844
- const expirationTimestamp = Date.now();
845
- for (let i = 0, len = keys.length; i < len; i++) {
846
- const key = keys[i];
847
- const metadata = this.readMetadata(key);
848
- if (metadata !== undefined) {
849
- this.publishMetadata(key, { ...metadata, expirationTimestamp });
850
- }
851
- }
844
+ this.expirePossibleStaleRecords(keys);
852
845
  // Process snapshot subscriptions
853
846
  const pendingPromises = [];
854
847
  for (let i = 0, len = snapshotSubscriptions.length; i < len; i++) {
@@ -943,6 +936,16 @@
943
936
  this.metadata[canonicalKey] = storeMetadata;
944
937
  }
945
938
  }
939
+ expirePossibleStaleRecords(keys) {
940
+ const expirationTimestamp = Date.now();
941
+ for (let i = 0, len = keys.length; i < len; i++) {
942
+ const key = keys[i];
943
+ const metadata = this.readMetadata(key);
944
+ if (metadata !== undefined) {
945
+ this.publishMetadata(key, { ...metadata, expirationTimestamp });
946
+ }
947
+ }
948
+ }
946
949
  setTTLOverride(namespace, representationName, ttl) {
947
950
  this.ttlOverrides[getTTLOverrideKey(namespace, representationName)] = ttl;
948
951
  }
@@ -1537,14 +1540,7 @@
1537
1540
  // of the function, in case the reference changes (because of an unsubscribe)
1538
1541
  const { snapshotSubscriptions } = this;
1539
1542
  // read metadata for each key, and mark as expired
1540
- const expirationTimestamp = Date.now();
1541
- for (let i = 0, len = keys.length; i < len; i++) {
1542
- const key = keys[i];
1543
- const metadata = this.readMetadata(key);
1544
- if (metadata !== undefined) {
1545
- this.publishMetadata(key, { ...metadata, expirationTimestamp });
1546
- }
1547
- }
1543
+ this.expirePossibleStaleRecords(keys);
1548
1544
  // Process snapshot subscriptions
1549
1545
  const pendingPromises = [];
1550
1546
  for (let i = 0, len = snapshotSubscriptions.length; i < len; i++) {
@@ -1667,6 +1663,19 @@
1667
1663
  this.metadataMap.set(canonicalKey, storeMetadata);
1668
1664
  }
1669
1665
  }
1666
+ expirePossibleStaleRecords(keys) {
1667
+ if (keys.length > 0 && typeof keys[0] === 'string') {
1668
+ return this.fallbackStringKeyInMemoryStore.expirePossibleStaleRecords(keys);
1669
+ }
1670
+ const expirationTimestamp = Date.now();
1671
+ for (let i = 0, len = keys.length; i < len; i++) {
1672
+ const key = keys[i];
1673
+ const metadata = this.readMetadata(key);
1674
+ if (metadata !== undefined) {
1675
+ this.publishMetadata(key, { ...metadata, expirationTimestamp });
1676
+ }
1677
+ }
1678
+ }
1670
1679
  setTTLOverride(namespace, representationName, ttl) {
1671
1680
  // Set the TTLs in both the stores
1672
1681
  this.fallbackStringKeyInMemoryStore.setTTLOverride(namespace, representationName, ttl);
@@ -2256,6 +2265,20 @@
2256
2265
  const value = this.data[propertyName];
2257
2266
  return typeof value !== 'object' || value === null;
2258
2267
  }
2268
+ isMissing(propertyName) {
2269
+ const value = this.data[propertyName];
2270
+ if (value && typeof value.__state === 'object' && value.__state !== null) {
2271
+ return !!value.__state.isMissing;
2272
+ }
2273
+ return false;
2274
+ }
2275
+ isPending(propertyName) {
2276
+ const value = this.data[propertyName];
2277
+ if (value && typeof value.__state === 'object' && value.__state !== null) {
2278
+ return !!value.__state.pending;
2279
+ }
2280
+ return false;
2281
+ }
2259
2282
  write(propertyName, value) {
2260
2283
  this.data[propertyName] = value;
2261
2284
  const canonicalKey = this.store.getCanonicalRecordId(this.storeKey);
@@ -3639,6 +3662,30 @@
3639
3662
  buildStructuredKey(namespace, representationName, idValues) {
3640
3663
  return this.store.buildStructuredKey(namespace, representationName, idValues);
3641
3664
  }
3665
+ /**
3666
+ * Take a list of keys and marks them as stale to be refreshed.
3667
+ * Then will be refreshed with the provided refresh function.
3668
+ * If no refresh and makeConfig functions are provided it will refresh
3669
+ * time that record is trying to be fetched
3670
+ *
3671
+ * Example: one record from graphql needs to be refreshed and not
3672
+ * the entire graphql query
3673
+ *
3674
+ * @param keys
3675
+ * @param makeConfig
3676
+ * @param refresh
3677
+ * @returns
3678
+ */
3679
+ expirePossibleStaleRecords(keys, config, refresh) {
3680
+ this.store.expirePossibleStaleRecords(keys);
3681
+ if (refresh !== undefined && config !== undefined) {
3682
+ return this.refreshPossibleStaleRecords(config, refresh);
3683
+ }
3684
+ return Promise.resolve();
3685
+ }
3686
+ refreshPossibleStaleRecords(config, refresh) {
3687
+ return Promise.resolve(refresh(config, { cachePolicy: { type: 'no-cache' } })).then(() => { });
3688
+ }
3642
3689
  }
3643
3690
 
3644
3691
  class Luvio {
@@ -3705,6 +3752,9 @@
3705
3752
  storeCleanup() {
3706
3753
  this.environment.storeCleanup();
3707
3754
  }
3755
+ storeExpirePossibleStaleRecords(keys, config, refresh) {
3756
+ return this.environment.expirePossibleStaleRecords(keys, config, refresh);
3757
+ }
3708
3758
  createSnapshot(selector, refresh) {
3709
3759
  return this.environment.createSnapshot(selector, refresh);
3710
3760
  }
@@ -4093,7 +4143,7 @@
4093
4143
  }
4094
4144
  return resourceParams;
4095
4145
  }
4096
- // engine version: 0.155.1-284dbf66
4146
+ // engine version: 0.156.3-04c1a80e
4097
4147
 
4098
4148
  /**
4099
4149
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -4221,7 +4271,7 @@
4221
4271
  }
4222
4272
  callbacks.push(callback);
4223
4273
  }
4224
- // version: 1.302.0-e45992a1f4
4274
+ // version: 1.304.0-aa3e5f9550
4225
4275
 
4226
4276
  // TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
4227
4277
  function instrumentAdapter$1(createFunction, _metadata) {
@@ -15720,7 +15770,7 @@
15720
15770
  }
15721
15771
  return superResult;
15722
15772
  }
15723
- // version: 1.302.0-e45992a1f4
15773
+ // version: 1.304.0-aa3e5f9550
15724
15774
 
15725
15775
  function unwrap(data) {
15726
15776
  // The lwc-luvio bindings import a function from lwc called "unwrap".
@@ -16649,7 +16699,7 @@
16649
16699
  const { apiFamily, name } = metadata;
16650
16700
  return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
16651
16701
  }
16652
- // version: 1.302.0-e45992a1f4
16702
+ // version: 1.304.0-aa3e5f9550
16653
16703
 
16654
16704
  /**
16655
16705
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -16748,7 +16798,7 @@
16748
16798
  TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
16749
16799
  TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
16750
16800
  })(TypeCheckShapes || (TypeCheckShapes = {}));
16751
- // engine version: 0.155.1-284dbf66
16801
+ // engine version: 0.156.3-04c1a80e
16752
16802
 
16753
16803
  const { keys: ObjectKeys$3, create: ObjectCreate$3 } = Object;
16754
16804
 
@@ -18223,7 +18273,7 @@
18223
18273
  // be applied to a RecordRepresentation in environments configured with
18224
18274
  // drafts when the record has draft changes applied to it
18225
18275
  // TODO [W-8237087]: explore if this selection can only be added in environments where drafts are enabled
18226
- const DRAFTS_SELECTION = {
18276
+ const DRAFTS_SELECTION$1 = {
18227
18277
  kind: 'Object',
18228
18278
  opaque: true,
18229
18279
  name: 'drafts',
@@ -18237,7 +18287,7 @@
18237
18287
  childRelationships: CHILD_RELATIONSHIP_SELECTION,
18238
18288
  fields: createPathSelection('fields', fieldDefinition),
18239
18289
  });
18240
- return [...sel.selections, DRAFTS_SELECTION];
18290
+ return [...sel.selections, DRAFTS_SELECTION$1];
18241
18291
  }
18242
18292
  /**
18243
18293
  * Convert a list of fields and optional fields into RecordRepresentation its equivalent
@@ -18254,7 +18304,7 @@
18254
18304
  childRelationships: CHILD_RELATIONSHIP_SELECTION,
18255
18305
  fields: createPathSelectionFromValue(record.fields),
18256
18306
  });
18257
- return [...sel.selections, DRAFTS_SELECTION];
18307
+ return [...sel.selections, DRAFTS_SELECTION$1];
18258
18308
  }
18259
18309
 
18260
18310
  const MAX_RECORD_DEPTH = 5;
@@ -20478,6 +20528,12 @@
20478
20528
  }
20479
20529
  return fieldNode.__state.fields;
20480
20530
  }
20531
+ function writeFieldStateNodeValue(fieldNode, propertyName, value) {
20532
+ const node = fieldNode;
20533
+ const state = node.__state || {};
20534
+ state[propertyName] = value;
20535
+ node.write('__state', state);
20536
+ }
20481
20537
 
20482
20538
  const CUSTOM_API_NAME_SUFFIX = '__c';
20483
20539
  const DMO_API_NAME_SUFFIX = '__dlm';
@@ -20526,7 +20582,7 @@
20526
20582
  name: key,
20527
20583
  children: {},
20528
20584
  };
20529
- if (isMissing(fieldRep)) {
20585
+ if (fields.isMissing(key)) {
20530
20586
  current.children[key] = next;
20531
20587
  continue;
20532
20588
  }
@@ -20585,14 +20641,6 @@
20585
20641
  }
20586
20642
  return reduce$2.call(childKeys, (acc, cur) => concat$2.call(acc, convertTrieToFieldsRecursively(root.children[cur]).map((i) => `${root.name}.${i}`)), []);
20587
20643
  }
20588
- function isMissing(node) {
20589
- // TODO [W-15867870]: JHORST add support for isMissing on graphnode object
20590
- return node.data && node.data.__state && node.data.__state.isMissing === true;
20591
- }
20592
- function isPending(node) {
20593
- // TODO [W-15867870]: JHORST add support for pending on graphnode object
20594
- return node.data && node.data.__state && node.data.__state.pending === true;
20595
- }
20596
20644
  const BLANK_RECORD_FIELDS_TRIE = freeze$4({
20597
20645
  name: '',
20598
20646
  children: {},
@@ -20684,8 +20732,9 @@
20684
20732
  !isFrozen$2(resolved.data)) {
20685
20733
  const stateFields = readFieldStateFromValueNode(resolved.data);
20686
20734
  const fields = stateFields === undefined ? [] : stateFields;
20687
- // TODO [W-15838292]: JHORST add support for node state on graphnode object
20688
- resolved.write('__state', { fields: dedupe$2([...fields, path.join('.')]) });
20735
+ // Note that GraphNodes are frozen when NODE_ENV != production.
20736
+ // Use with care.
20737
+ writeFieldStateNodeValue(resolved, 'fields', dedupe$2([...fields, path.join('.')]));
20689
20738
  }
20690
20739
  }
20691
20740
  function markNulledOutRequiredFields(record, fields) {
@@ -20712,7 +20761,7 @@
20712
20761
  return;
20713
20762
  }
20714
20763
  const fieldValueValue = fieldValueRepresentation.object(fieldName);
20715
- if (isPending(fieldValueValue)) {
20764
+ if (fieldValueRepresentation.isPending(fieldName)) {
20716
20765
  writeMissingFieldToStore(fieldValueRepresentation, fieldName);
20717
20766
  return;
20718
20767
  }
@@ -20742,7 +20791,8 @@
20742
20791
  function writeMissingFieldToStore(field, fieldName) {
20743
20792
  // TODO [W-6900046]: remove cast, make RecordRepresentationNormalized['fields'] accept
20744
20793
  // an undefined/non-present __ref if isMissing is present
20745
- // TODO [W-15867870]: JHORST add support for isMissing on graphnode object
20794
+ // Note that GraphNodes are frozen when NODE_ENV != production.
20795
+ // Use with care.
20746
20796
  field.write(fieldName, {
20747
20797
  __state: {
20748
20798
  isMissing: true,
@@ -26509,6 +26559,12 @@
26509
26559
  function normalize$A(input, existing, path, luvio, store, timestamp) {
26510
26560
  return input;
26511
26561
  }
26562
+ const DRAFTS_SELECTION = {
26563
+ kind: 'Object',
26564
+ opaque: true,
26565
+ name: 'drafts',
26566
+ required: false,
26567
+ };
26512
26568
  const select$1A = function QuickActionExecutionRepresentationSelect() {
26513
26569
  return {
26514
26570
  kind: 'Fragment',
@@ -26540,7 +26596,7 @@
26540
26596
  {
26541
26597
  name: 'successMessage',
26542
26598
  kind: 'Scalar'
26543
- }
26599
+ }, DRAFTS_SELECTION,
26544
26600
  ]
26545
26601
  };
26546
26602
  };
@@ -44136,7 +44192,7 @@
44136
44192
  throttle(60, 60000, setupNotifyAllListRecordUpdateAvailable(luvio));
44137
44193
  throttle(60, 60000, setupNotifyAllListInfoSummaryUpdateAvailable(luvio));
44138
44194
  });
44139
- // version: 1.302.0-5fb014108f
44195
+ // version: 1.304.0-d87b57badb
44140
44196
 
44141
44197
  var ldsIdempotencyWriteDisabled = {
44142
44198
  isOpen: function (e) {
@@ -45820,6 +45876,32 @@
45820
45876
  }, revivingStore).finally(() => {
45821
45877
  });
45822
45878
  };
45879
+ const expirePossibleStaleRecords = async function (keys$1, config, refresh) {
45880
+ validateNotDisposed();
45881
+ const metadataKeys = keys$1.map(serializeStructuredKey);
45882
+ const now = Date.now();
45883
+ const entries = await durableStore.getMetadata(metadataKeys, DefaultDurableSegment);
45884
+ if (entries === undefined || keys$8(entries).length === 0) {
45885
+ return environment.expirePossibleStaleRecords(keys$1);
45886
+ }
45887
+ let metaDataChanged = false;
45888
+ const metadataEntries = metadataKeys.reduce((accu, key) => {
45889
+ const metadataEntry = entries[key];
45890
+ if (metadataEntry.metadata !== undefined) {
45891
+ const metadata = { ...metadataEntry.metadata, expirationTimestamp: now };
45892
+ accu[key] = { metadata };
45893
+ metaDataChanged = true;
45894
+ }
45895
+ return accu;
45896
+ }, {});
45897
+ if (metaDataChanged) {
45898
+ await durableStore.setMetadata(metadataEntries, DefaultDurableSegment);
45899
+ }
45900
+ if (config !== undefined && refresh !== undefined) {
45901
+ return environment.refreshPossibleStaleRecords(config, refresh);
45902
+ }
45903
+ return Promise.resolve();
45904
+ };
45823
45905
  // set the default cache policy of the base environment
45824
45906
  environment.setDefaultCachePolicy({
45825
45907
  type: 'stale-while-revalidate',
@@ -45852,6 +45934,7 @@
45852
45934
  handleErrorResponse: { value: handleErrorResponse },
45853
45935
  getNotifyChangeStoreEntries: { value: getNotifyChangeStoreEntries },
45854
45936
  notifyStoreUpdateAvailable: { value: notifyStoreUpdateAvailable },
45937
+ expirePossibleStaleRecords: { value: expirePossibleStaleRecords },
45855
45938
  });
45856
45939
  }
45857
45940
 
@@ -49898,7 +49981,7 @@
49898
49981
  if (status === DraftActionStatus.Error) {
49899
49982
  this.state = DraftQueueState.Error;
49900
49983
  this.processingAction = undefined;
49901
- this.notifyChangedListeners({
49984
+ await this.notifyChangedListeners({
49902
49985
  type: DraftQueueEventType.ActionFailed,
49903
49986
  action: action,
49904
49987
  });
@@ -49914,7 +49997,7 @@
49914
49997
  if (this.state === DraftQueueState.Waiting) {
49915
49998
  this.state = DraftQueueState.Started;
49916
49999
  }
49917
- this.notifyChangedListeners({
50000
+ await this.notifyChangedListeners({
49918
50001
  type: DraftQueueEventType.ActionUploading,
49919
50002
  action: { ...action, status: DraftActionStatus.Uploading },
49920
50003
  });
@@ -49991,6 +50074,31 @@
49991
50074
  await this.startQueue();
49992
50075
  }
49993
50076
  }
50077
+ async updateDraftAction(action) {
50078
+ // stop queue manually
50079
+ this.stopQueueManually();
50080
+ const actionStatus = await this.statusOfAction(action.id);
50081
+ if (actionStatus === DraftActionStatus.Uploading) {
50082
+ return Promise.reject('cannot update an uploading action');
50083
+ }
50084
+ // save the action into the draft store
50085
+ await this.draftStore.writeAction(action);
50086
+ // make the handler replay these drafts on the record
50087
+ const handler = this.getHandler(action.handler);
50088
+ const queue = await this.getQueueActions();
50089
+ await handler.handleActionEnqueued(action, queue);
50090
+ // start queue safely
50091
+ return this.startQueueSafe();
50092
+ }
50093
+ async statusOfAction(actionId) {
50094
+ const queue = await this.getQueueActions();
50095
+ const actions = queue.filter((action) => action.id === actionId);
50096
+ if (actions.length === 0) {
50097
+ return Promise.reject('cannot update non-existent action');
50098
+ }
50099
+ const action = actions[0];
50100
+ return action.status;
50101
+ }
49994
50102
  replaceAction(targetActionId, sourceActionId) {
49995
50103
  return this.replaceOrMergeActions(targetActionId, sourceActionId, false);
49996
50104
  }
@@ -50021,17 +50129,21 @@
50021
50129
  });
50022
50130
  return action;
50023
50131
  }
50024
- scheduleRetryWithSpecifiedDelay(retryDelayInMs) {
50132
+ async scheduleRetryWithSpecifiedDelay(retryDelayInMs) {
50133
+ await this.notifyChangedListeners({
50134
+ type: DraftQueueEventType.QueueStateChanged,
50135
+ state: DraftQueueState.Waiting,
50136
+ });
50025
50137
  this.timeoutHandler = setTimeout(() => {
50026
50138
  if (this.state !== DraftQueueState.Stopped) {
50027
50139
  this.processNextAction();
50028
50140
  }
50029
50141
  }, retryDelayInMs);
50030
50142
  }
50031
- scheduleRetry() {
50143
+ async scheduleRetry() {
50032
50144
  const newInterval = this.retryIntervalMilliseconds * 2;
50033
50145
  this.retryIntervalMilliseconds = Math.min(Math.max(newInterval, this.minimumRetryInterval), this.maximumRetryInterval);
50034
- this.scheduleRetryWithSpecifiedDelay(this.retryIntervalMilliseconds);
50146
+ return this.scheduleRetryWithSpecifiedDelay(this.retryIntervalMilliseconds);
50035
50147
  }
50036
50148
  async getActionsForReplaceOrMerge(targetActionId, sourceActionId) {
50037
50149
  const actions = await this.getQueueActions();
@@ -50157,7 +50269,8 @@
50157
50269
  const actionArray = [];
50158
50270
  for (let i = 0, len = keys$1.length; i < len; i++) {
50159
50271
  const key = keys$1[i];
50160
- actionArray.push(draftStore[key]);
50272
+ // clone draft so we don't expose the internal draft store
50273
+ actionArray.push(clone$1(draftStore[key]));
50161
50274
  }
50162
50275
  return actionArray;
50163
50276
  });
@@ -50792,6 +50905,7 @@
50792
50905
  DraftQueueOperationType["ItemUpdated"] = "updated";
50793
50906
  DraftQueueOperationType["QueueStarted"] = "started";
50794
50907
  DraftQueueOperationType["QueueStopped"] = "stopped";
50908
+ DraftQueueOperationType["QueueWaiting"] = "waiting";
50795
50909
  })(DraftQueueOperationType || (DraftQueueOperationType = {}));
50796
50910
  /**
50797
50911
  * Converts the internal DraftAction's ResourceRequest into
@@ -50834,6 +50948,16 @@
50834
50948
  };
50835
50949
  }
50836
50950
  class DraftManager {
50951
+ shouldEmitEvent(event) {
50952
+ // Waiting events cannot be emitted prior to 252 native clients
50953
+ // TODO [W-16102411]: we can safely remove this backwards compatible code in 256
50954
+ if (isDraftQueueStateChangeEvent(event) &&
50955
+ event.state === DraftQueueState.Waiting &&
50956
+ this.listenerVersion === undefined) {
50957
+ return false;
50958
+ }
50959
+ return this.draftEventsShouldBeEmitted.includes(event.type);
50960
+ }
50837
50961
  constructor(draftQueue) {
50838
50962
  this.listeners = [];
50839
50963
  this.draftEventsShouldBeEmitted = [
@@ -50847,7 +50971,7 @@
50847
50971
  ];
50848
50972
  this.draftQueue = draftQueue;
50849
50973
  draftQueue.registerOnChangedListener((event) => {
50850
- if (this.draftEventsShouldBeEmitted.includes(event.type)) {
50974
+ if (this.shouldEmitEvent(event)) {
50851
50975
  return this.callListeners(event);
50852
50976
  }
50853
50977
  return Promise.resolve();
@@ -50877,6 +51001,8 @@
50877
51001
  return DraftQueueOperationType.QueueStarted;
50878
51002
  case DraftQueueState.Stopped:
50879
51003
  return DraftQueueOperationType.QueueStopped;
51004
+ case DraftQueueState.Waiting:
51005
+ return DraftQueueOperationType.QueueWaiting;
50880
51006
  default:
50881
51007
  throw Error('Unsupported event type');
50882
51008
  }
@@ -50934,7 +51060,8 @@
50934
51060
  *
50935
51061
  * @param listener The listener closure to subscribe to changes
50936
51062
  */
50937
- registerDraftQueueChangedListener(listener) {
51063
+ registerDraftQueueChangedListener(listener, version = undefined) {
51064
+ this.listenerVersion = version;
50938
51065
  this.listeners.push(listener);
50939
51066
  return () => {
50940
51067
  this.listeners = this.listeners.filter((l) => {
@@ -50961,6 +51088,60 @@
50961
51088
  };
50962
51089
  });
50963
51090
  }
51091
+ async mergePerformQuickAction(actionId, fields) {
51092
+ if (!this.isValidFieldMap(fields)) {
51093
+ return Promise.reject('fields is not valid');
51094
+ }
51095
+ const queue = await this.draftQueue.getQueueActions();
51096
+ const actions = queue.filter((action) => action.id === actionId);
51097
+ if (actions.length === 0) {
51098
+ return Promise.reject('cannot edit non-existent action');
51099
+ }
51100
+ const action = actions[0];
51101
+ if (!this.isPerformQuickActionDraft(action, 'post')) {
51102
+ return Promise.reject('cannot edit incompatible action type or uploading actions');
51103
+ }
51104
+ action.data.body.fields = { ...action.data.body.fields, ...fields };
51105
+ await this.draftQueue.updateDraftAction(action);
51106
+ return this.buildDraftQueueItem(action);
51107
+ }
51108
+ isValidFieldMap(fields) {
51109
+ const keys$1 = keys$6(fields);
51110
+ const validTypes = ['string', 'number', 'null', 'boolean'];
51111
+ for (let i = 0; i < keys$1.length; i++) {
51112
+ const key = keys$1[i];
51113
+ const value = fields[key];
51114
+ if (!validTypes.includes(typeof value)) {
51115
+ return false;
51116
+ }
51117
+ }
51118
+ return true;
51119
+ }
51120
+ isPerformQuickActionDraft(action, method) {
51121
+ const data = action.data;
51122
+ const isPerformQuickAction = data.basePath.startsWith('/ui-api/actions/perform-quick-action/');
51123
+ const methodMatches = data.method === method;
51124
+ const notUploading = action.status !== DraftActionStatus.Uploading;
51125
+ return isPerformQuickAction && methodMatches && notUploading;
51126
+ }
51127
+ async mergePerformUpdateRecordQuickAction(actionId, fields) {
51128
+ if (!this.isValidFieldMap(fields)) {
51129
+ return Promise.reject('fields is not valid');
51130
+ }
51131
+ const queue = await this.draftQueue.getQueueActions();
51132
+ const actions = queue.filter((action) => action.id === actionId);
51133
+ if (actions.length === 0) {
51134
+ return Promise.reject('cannot edit non-existent action');
51135
+ }
51136
+ const action = actions[0];
51137
+ if (!this.isPerformQuickActionDraft(action, 'patch')) {
51138
+ return Promise.reject('cannot edit incompatible action type or uploading actions');
51139
+ }
51140
+ const data = action.data;
51141
+ data.body.fields = { ...data.body.fields, ...fields };
51142
+ await this.draftQueue.updateDraftAction(action);
51143
+ return this.buildDraftQueueItem(action);
51144
+ }
50964
51145
  buildDraftQueueItem(action) {
50965
51146
  const operationType = getOperationTypeFrom(action);
50966
51147
  const { id, status, timestamp, targetId, metadata } = action;
@@ -51268,6 +51449,12 @@
51268
51449
  function isStoreRecordError(storeRecord) {
51269
51450
  return storeRecord.__type === 'error';
51270
51451
  }
51452
+ function isDraftFieldPending(field) {
51453
+ return !!(field.__state && field.__state.pending === true);
51454
+ }
51455
+ function isDraftFieldMissing(field) {
51456
+ return !!(field.__state && field.__state.isMissing === true);
51457
+ }
51271
51458
 
51272
51459
  /**
51273
51460
  * Checks if a resource request is a GET method on the record endpoint
@@ -51959,12 +52146,9 @@
51959
52146
  }
51960
52147
  const { dataType, relationshipName, referenceToInfos } = fieldInfo;
51961
52148
  const draftFieldNode = record.fields[draftField];
51962
- // JHORST: revisit this logic
51963
52149
  // do not try to apply drafts on nodes that are pending or missing
51964
- if (draftFieldNode.__state !== undefined) {
51965
- if (draftFieldNode.__state.pending === true ||
51966
- draftFieldNode.__state.isMissing === true)
51967
- continue;
52150
+ if (isDraftFieldPending(draftFieldNode) || isDraftFieldMissing(draftFieldNode)) {
52151
+ continue;
51968
52152
  }
51969
52153
  const draftFieldValue = draftFieldNode.value;
51970
52154
  if (dataType === 'Reference' && relationshipName !== null) {
@@ -52759,6 +52943,7 @@
52759
52943
  isCreated: true,
52760
52944
  isSuccess: true,
52761
52945
  successMessage: `record created.`,
52946
+ drafts: { draftActionId: action.id },
52762
52947
  });
52763
52948
  }
52764
52949
  getDraftMetadata(_key) {
@@ -52876,6 +53061,7 @@
52876
53061
  isCreated: false,
52877
53062
  isSuccess: true,
52878
53063
  successMessage: `record updated.`,
53064
+ drafts: { draftActionId: action.id },
52879
53065
  });
52880
53066
  }
52881
53067
  async getDraftMetadata(key) {
@@ -52953,7 +53139,29 @@
52953
53139
  return customEvent.namespace === CONTENT_DOCUMENT_AND_VERSION_NAMESPACE;
52954
53140
  }
52955
53141
 
53142
+ // so eslint doesn't complain about nimbus
53143
+ /* global __nimbus */
52956
53144
  const ContentDocumentCompositeKeyPrefix = 'UiApi::ContentDocumentCompositeRepresentation:';
53145
+ function chunkToBase64(chunk) {
53146
+ let binary = '';
53147
+ const chunkSize = 32 * 1024;
53148
+ for (let i = 0; i < chunk.length; i += chunkSize) {
53149
+ binary += String.fromCharCode.apply(null, chunk.subarray(i, i + chunkSize));
53150
+ }
53151
+ return btoa(binary);
53152
+ }
53153
+ async function streamBufferToBinaryStore(binaryStore, buffer, mimeType) {
53154
+ const uri = await binaryStore.createStream(mimeType);
53155
+ const bufferSize = 64 * 1024; // 64k buffer size
53156
+ const uint8Array = new Uint8Array(buffer);
53157
+ for (let offset = 0; offset < uint8Array.length; offset += bufferSize) {
53158
+ const chunk = uint8Array.subarray(offset, Math.min(offset + bufferSize, uint8Array.length));
53159
+ const base64Chunk = chunkToBase64(chunk);
53160
+ await binaryStore.writeToStream(uri, base64Chunk);
53161
+ }
53162
+ await binaryStore.closeStream(uri);
53163
+ return uri;
53164
+ }
52957
53165
  function createContentDocumentAndVersionDraftAdapterFactory(luvio, binaryStore, actionHandler) {
52958
53166
  const overriddenLuvio = buildLuvioOverrideForDraftAdapters(luvio, actionHandler, (key) => {
52959
53167
  // if the key is for our top-level response shape
@@ -52976,7 +53184,14 @@
52976
53184
  const { fileData } = config;
52977
53185
  const { name, size, type } = fileData;
52978
53186
  const buffer = await fileData.arrayBuffer();
52979
- const uri = await binaryStore.store(new Uint8Array(buffer), type, size);
53187
+ var uri;
53188
+ // see if new chunking-api exists, if it doesnt fall back to memory-intensive mobile api
53189
+ if (!__nimbus.plugins.LdsBinaryStorePlugin.createStream) {
53190
+ uri = await binaryStore.store(new Uint8Array(buffer), type, size);
53191
+ }
53192
+ else {
53193
+ uri = await streamBufferToBinaryStore(binaryStore, buffer, type);
53194
+ }
52980
53195
  config.fileData = {
52981
53196
  isFileReference: true,
52982
53197
  handle: uri,
@@ -53815,7 +54030,7 @@
53815
54030
  return new DataLoader(batchRecordQuery);
53816
54031
  }
53817
54032
 
53818
- function createContext(store, objectInfos, eventEmitter, settings, snapshot, draftFunctions) {
54033
+ function createContext(store, objectInfos, eventEmitter, settings, snapshot, mappedCursors = new Map(), draftFunctions) {
53819
54034
  store.query.bind(store);
53820
54035
  const query = (sql, params) => {
53821
54036
  const now = Date.now();
@@ -53842,7 +54057,9 @@
53842
54057
  Record,
53843
54058
  snapshot,
53844
54059
  seenRecordIds: new Set(),
54060
+ possibleStaleRecordMap: new Map(),
53845
54061
  draftFunctions,
54062
+ mappedCursors,
53846
54063
  };
53847
54064
  }
53848
54065
 
@@ -54453,7 +54670,6 @@
54453
54670
 
54454
54671
  const JSON_EXTRACT_PATH_INGESTION_TIMESTAMP = '$.ingestionTimestamp';
54455
54672
  const JSON_EXTRACT_PATH_INGESTION_APINAME = '$.apiName';
54456
- const JSON_EXTRACT_PATH_DRAFTS = '$.drafts';
54457
54673
 
54458
54674
  const MultiPickListValueSeparator = ';';
54459
54675
  function filterToPredicates(where, recordType, alias, objectInfoMap, joins, draftFunctions) {
@@ -54994,14 +55210,10 @@
54994
55210
  const predicates = buildPredicates(config);
54995
55211
  const orderBy = buildOrderBy(config);
54996
55212
  const sql = `
54997
- SELECT "${config.alias}".data
55213
+ SELECT "${config.alias}".data, "${config.alias}".metadata
54998
55214
  FROM lds_data "${config.alias}" ${joins.sql}
54999
55215
  WHERE "${config.alias}".key like 'UiApi::RecordRepresentation:%'
55000
55216
  AND json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_INGESTION_APINAME}') = '${config.alias}'
55001
- AND (
55002
- json_extract("${config.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ?
55003
- OR json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL
55004
- )
55005
55217
  ${predicates.sql}
55006
55218
  ${orderBy.sql}
55007
55219
  LIMIT ?
@@ -55013,7 +55225,6 @@
55013
55225
  const bindings = [
55014
55226
  // bindings from predicates on joins
55015
55227
  ...joins.bindings,
55016
- config.ingestionTimestamp,
55017
55228
  // where clause and parent scope bindings
55018
55229
  ...predicates.bindings,
55019
55230
  // limit binding
@@ -55040,29 +55251,19 @@
55040
55251
  if (allJoins.length === 0)
55041
55252
  return { sql, bindings };
55042
55253
  sql = allJoins.reduce((joinAccumulator, join) => {
55043
- let timestampAdded = false;
55044
55254
  const joinConditions = join.conditions.reduce((conditionAccumulator, condition) => {
55045
55255
  let joined_sql;
55046
- const joinMetadataTimestamp = ` AND (json_extract("${join.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ? OR json_extract("${join.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL)`;
55047
55256
  // predicate on a value, use the newly joined table
55048
55257
  if ('type' in condition) {
55049
55258
  const { sql, binding } = predicateToSQL(condition, join.alias);
55050
- joined_sql = ` AND ${sql}${timestampAdded ? '' : joinMetadataTimestamp}`;
55259
+ joined_sql = ` AND ${sql}`;
55051
55260
  bindings.push(...binding);
55052
- if (timestampAdded === false) {
55053
- bindings.push(config.ingestionTimestamp);
55054
- timestampAdded = true;
55055
- }
55056
55261
  }
55057
55262
  else {
55058
55263
  // predicate on a path
55059
55264
  const left = ` AND json_extract("${join.to}".data, '${condition.leftPath}')`;
55060
55265
  const right = `json_extract("${join.alias}".data, '${condition.rightPath}')`;
55061
- joined_sql = `${left} = ${right}${timestampAdded ? '' : joinMetadataTimestamp}`;
55062
- if (timestampAdded === false) {
55063
- bindings.push(config.ingestionTimestamp);
55064
- timestampAdded = true;
55065
- }
55266
+ joined_sql = `${left} = ${right}`;
55066
55267
  }
55067
55268
  conditionAccumulator += joined_sql;
55068
55269
  return conditionAccumulator;
@@ -55731,11 +55932,15 @@
55731
55932
  }
55732
55933
  return ingestionTimestamp;
55733
55934
  }
55734
- async function readPaginationMetadataForKey(key, query) {
55735
- const sql = `SELECT data FROM lds_data WHERE key=?`;
55736
- const results = await query(sql, [key + '__pagination']);
55737
- const [paginationMetadata] = results.rows.map((row) => parse$3(row[0]));
55738
- return paginationMetadata || {};
55935
+ function isObjectDefinitionNode(node) {
55936
+ const { kind } = node;
55937
+ return typeof kind === 'string' && kind === 'OperationDefinition';
55938
+ }
55939
+ function operationNodeAncestor(ancestors) {
55940
+ let operationNode = ancestors.find((a) => {
55941
+ return !(a instanceof Array) && isObjectDefinitionNode(a);
55942
+ });
55943
+ return operationNode;
55739
55944
  }
55740
55945
 
55741
55946
  function findSpanningField(name) {
@@ -55936,44 +56141,87 @@
55936
56141
  const base64encode = typeof btoa === 'function' ? btoa : btoaPolyfill;
55937
56142
  const base64decode = typeof atob === 'function' ? atob : atobPolyfill;
55938
56143
 
56144
+ // this truthy value is used to indicate a premature end of results
56145
+ const EARLY_END = 1;
55939
56146
  function cursorResolver(source) {
55940
- return encodeV1Cursor(source.index);
56147
+ let cursor = {
56148
+ i: source.index,
56149
+ };
56150
+ if (source.earlyEnd) {
56151
+ cursor.e = EARLY_END;
56152
+ }
56153
+ return encodeV1Cursor(cursor);
55941
56154
  }
55942
56155
  function pageInfoResolver(source) {
55943
56156
  if (source.records.length === 0) {
56157
+ // we may have found no records, but if more exist we need to
56158
+ // return a valid cursor that can be passed as the next `after`
56159
+ if (source.earlyEnd) {
56160
+ return {
56161
+ startCursor: null,
56162
+ endCursor: encodeV1Cursor({
56163
+ i: source.offset,
56164
+ e: EARLY_END,
56165
+ }),
56166
+ hasNextPage: source.hasNextPage,
56167
+ };
56168
+ }
55944
56169
  return {
55945
56170
  startCursor: null,
55946
56171
  endCursor: null,
55947
- hasNextPage: false,
56172
+ hasNextPage: source.hasNextPage,
55948
56173
  };
55949
56174
  }
55950
56175
  let startIndex = source.records[0].index;
56176
+ let startCursor = {
56177
+ i: startIndex,
56178
+ };
55951
56179
  let endIndex = source.records[source.records.length - 1].index;
56180
+ let endCursor = {
56181
+ i: endIndex,
56182
+ };
56183
+ if (source.earlyEnd) {
56184
+ startCursor.e = EARLY_END;
56185
+ endCursor.e = EARLY_END;
56186
+ }
55952
56187
  return {
55953
- startCursor: encodeV1Cursor(startIndex),
55954
- endCursor: encodeV1Cursor(endIndex),
56188
+ startCursor: encodeV1Cursor(startCursor),
56189
+ endCursor: encodeV1Cursor(endCursor),
55955
56190
  hasNextPage: source.hasNextPage,
55956
56191
  };
55957
56192
  }
55958
56193
  function pageResultCountResolver(source) {
55959
56194
  return source.records.length;
55960
56195
  }
55961
- function encodeV1Cursor(index) {
55962
- return base64encode(`v1:${index}`);
56196
+ function isLocalCursor(maybeCursor) {
56197
+ return (!!maybeCursor &&
56198
+ typeof maybeCursor === 'object' &&
56199
+ 'i' in maybeCursor &&
56200
+ typeof maybeCursor.i === 'number');
55963
56201
  }
55964
- const cursorRegex = /^v1:(?<index>\d+)$/;
56202
+ function encodeV1Cursor(cursor) {
56203
+ return base64encode(stringify$3(cursor));
56204
+ }
56205
+ const CURSOR_PARSE_ERROR = 'Unable to parse cursor';
55965
56206
  function decodeV1Cursor(base64cursor) {
55966
- const cursor = base64decode(base64cursor);
55967
- if (!cursor) {
56207
+ let maybeCursor;
56208
+ try {
56209
+ const cursorString = base64decode(base64cursor);
56210
+ maybeCursor = parse$3(cursorString);
56211
+ }
56212
+ catch (error) {
56213
+ let message = CURSOR_PARSE_ERROR;
56214
+ if (error instanceof Error) {
56215
+ message += ': ' + error.message;
56216
+ }
55968
56217
  // eslint-disable-next-line @salesforce/lds/no-error-in-production
55969
- throw new Error('Unable to parse cursor');
56218
+ throw new Error(message);
55970
56219
  }
55971
- const found = cursor.match(cursorRegex);
55972
- if (!found || !found.groups) {
56220
+ if (!isLocalCursor(maybeCursor)) {
55973
56221
  // eslint-disable-next-line @salesforce/lds/no-error-in-production
55974
- throw new Error('Unable to parse cursor');
56222
+ throw new Error(CURSOR_PARSE_ERROR);
55975
56223
  }
55976
- return Number(found.groups.index);
56224
+ return maybeCursor;
55977
56225
  }
55978
56226
  /**
55979
56227
  * Check the selections for any selection matching `pageInfo { hasNextPage }`
@@ -56011,6 +56259,164 @@
56011
56259
  return false;
56012
56260
  }
56013
56261
 
56262
+ const END_CURSOR = '__END__';
56263
+ // find the closest matching cursor in the server pagination metadata
56264
+ function mapCursorValue(originalValue, paginationMetadata) {
56265
+ let mappedValue = null;
56266
+ if (!originalValue) {
56267
+ return mappedValue;
56268
+ }
56269
+ // flip the pagination metadata into an array by index.
56270
+ let cursors = [];
56271
+ for (const [cursor, index] of Object.entries(paginationMetadata)) {
56272
+ if (index === undefined)
56273
+ continue;
56274
+ cursors[index] = cursor;
56275
+ }
56276
+ let cursor = decodeV1Cursor(originalValue);
56277
+ // cursors containe 1-based indexes, adjust back to 0-based
56278
+ let index = cursor.i - 1;
56279
+ if (
56280
+ // cursor.e being truthy means we had premature end of results and
56281
+ // should pin to the last known server cursor
56282
+ !cursor.e &&
56283
+ // check that the index we have is within the bounds of known cursors
56284
+ index >= 0 &&
56285
+ index < cursors.length &&
56286
+ // and make sure the cursor is not the server end marker
56287
+ cursors[index] !== END_CURSOR) {
56288
+ mappedValue = cursors[index];
56289
+ }
56290
+ else {
56291
+ // in this case, either our local cursor is beyond the max server cursor, or
56292
+ // the local cursor precedes the max server cursor and we ran out of locally
56293
+ // cached results. either way, find the last known server cursor and map to that.
56294
+ for (let i = cursors.length; i > 0; --i) {
56295
+ let cursor = cursors[i - 1];
56296
+ if (cursor !== END_CURSOR) {
56297
+ mappedValue = cursor;
56298
+ break;
56299
+ }
56300
+ }
56301
+ }
56302
+ return mappedValue;
56303
+ }
56304
+ // map all pagination cursors in the document
56305
+ async function mapPaginationCursors(originalAST, variables, store) {
56306
+ // first pass, identify record query cache keys for reading pagination metadata
56307
+ let requiredPaginationMetadataKeys = [];
56308
+ visit$1(originalAST, {
56309
+ Field(node, _key, _parent, _path, ancestors) {
56310
+ // is it a record query?
56311
+ if (!isRecordQuery(node)) {
56312
+ return;
56313
+ }
56314
+ // does it have a defined `after` argument?
56315
+ let after = node.arguments &&
56316
+ node.arguments.find((a) => {
56317
+ return a.name.value === 'after';
56318
+ });
56319
+ if (after && (after.value.kind === 'StringValue' || after.value.kind === 'Variable')) {
56320
+ let operationNode = operationNodeAncestor(ancestors);
56321
+ if (!operationNode) {
56322
+ return false;
56323
+ }
56324
+ let key = buildKeyStringForRecordQuery(operationNode, variables, node.arguments || [], node.name.value);
56325
+ requiredPaginationMetadataKeys.push(key);
56326
+ }
56327
+ // don't need to descend into this node
56328
+ return false;
56329
+ },
56330
+ });
56331
+ // read pagination metadata for identified record queries
56332
+ let paginationMetadataMap = await readPaginationMetadataForKeys(requiredPaginationMetadataKeys, store.query.bind(store));
56333
+ // holds the original cursor values that were mapped back to server cursors
56334
+ let mappedCursors = new Map();
56335
+ // rewrite nodes/variables with mapped cursors now that we read the pagination metadata
56336
+ let ast = visit$1(originalAST, {
56337
+ Field(node, _key, _parent, _path, ancestors) {
56338
+ // is it a record query?
56339
+ if (!isRecordQuery(node)) {
56340
+ // not returning false, we might be in the parent of a record query
56341
+ return;
56342
+ }
56343
+ // does it have a defined `after` argument?
56344
+ if (!node.arguments)
56345
+ return false;
56346
+ let after = node.arguments.find((a) => {
56347
+ return a.name.value === 'after';
56348
+ });
56349
+ if (!after)
56350
+ return false;
56351
+ if (after.value.kind === 'StringValue' || after.value.kind === 'Variable') {
56352
+ let operationNode = operationNodeAncestor(ancestors);
56353
+ if (!operationNode) {
56354
+ return false;
56355
+ }
56356
+ let key = buildKeyStringForRecordQuery(operationNode, variables, node.arguments || [], node.name.value);
56357
+ // pagination metadata may be missing, e.g. due to being offline
56358
+ let paginationMetadata = paginationMetadataMap.get(key) || {};
56359
+ if (after.value.kind === 'StringValue') {
56360
+ let originalValue = after.value.value;
56361
+ mappedCursors.set(key, originalValue);
56362
+ let mappedValue = mapCursorValue(originalValue, paginationMetadata);
56363
+ if (!mappedValue) {
56364
+ // there were no results from the server, remove after argument
56365
+ return {
56366
+ ...node,
56367
+ arguments: node.arguments.filter((a) => a !== after),
56368
+ };
56369
+ }
56370
+ // return a new replacement node
56371
+ return {
56372
+ ...node,
56373
+ arguments: node.arguments.map((a) => {
56374
+ if (a !== after)
56375
+ return a;
56376
+ return {
56377
+ ...a,
56378
+ value: {
56379
+ kind: 'StringValue',
56380
+ value: mappedValue,
56381
+ },
56382
+ };
56383
+ }),
56384
+ };
56385
+ }
56386
+ else if (after.value.kind === 'Variable') {
56387
+ // rewrite the variable
56388
+ let variableName = after.value.name.value;
56389
+ let variableValue = variables[variableName];
56390
+ mappedCursors.set(key, variableValue);
56391
+ let mappedValue = mapCursorValue(variableValue, paginationMetadata);
56392
+ variables[variableName] = mappedValue;
56393
+ }
56394
+ // don't need to descend into this node
56395
+ return false;
56396
+ }
56397
+ },
56398
+ });
56399
+ return {
56400
+ ast,
56401
+ mappedCursors,
56402
+ };
56403
+ }
56404
+ async function readPaginationMetadataForKeys(keys, query) {
56405
+ let metadataMap = new Map();
56406
+ if (keys.length === 0)
56407
+ return metadataMap;
56408
+ const sql = `SELECT key, data FROM lds_data WHERE key in (${Array(keys.length)
56409
+ .fill('?')
56410
+ .join(',')})`;
56411
+ const results = await query(sql, keys.map((k) => k + '__pagination'));
56412
+ for (let row of results.rows) {
56413
+ let key = row[0].replace(/__pagination$/, '');
56414
+ let metadata = parse$3(row[1]);
56415
+ metadataMap.set(key, metadata);
56416
+ }
56417
+ return metadataMap;
56418
+ }
56419
+
56014
56420
  /*
56015
56421
  resolves connections...
56016
56422
  */
@@ -56032,8 +56438,14 @@
56032
56438
  const childRelationship = parentObjectInfo &&
56033
56439
  parentObjectInfo.childRelationships.find((rel) => rel.relationshipName === info.fieldName);
56034
56440
  // or emit/throw if we want to report it
56035
- if (!childRelationship)
56036
- return { records: [], hasNextPage: false };
56441
+ if (!childRelationship) {
56442
+ return {
56443
+ records: [],
56444
+ hasNextPage: false,
56445
+ earlyEnd: false,
56446
+ offset: 0,
56447
+ };
56448
+ }
56037
56449
  alias = childRelationship.childObjectApiName;
56038
56450
  childRelationshipFieldName = childRelationship.fieldName;
56039
56451
  }
@@ -56052,7 +56464,12 @@
56052
56464
  }
56053
56465
  let offset = 0;
56054
56466
  if (args.after) {
56055
- offset = decodeV1Cursor(args.after) + 1;
56467
+ let originalCursor = context.mappedCursors.get(queryCacheKey);
56468
+ if (!originalCursor) {
56469
+ // eslint-disable-next-line @salesforce/lds/no-error-in-production
56470
+ throw new Error('Internal Error: unable to determine `after` cursor value');
56471
+ }
56472
+ offset = decodeV1Cursor(originalCursor).i;
56056
56473
  }
56057
56474
  // if the query wants to know `hasNextPage` then we need to request 1 additional record
56058
56475
  let selections = info.fieldNodes
@@ -56061,7 +56478,7 @@
56061
56478
  let wantsHasNextPage = selectionIncludesHasNextPage(selections, info.fragments);
56062
56479
  let paginationMetadata = undefined;
56063
56480
  if (wantsHasNextPage) {
56064
- paginationMetadata = await readPaginationMetadataForKey(queryCacheKey, query);
56481
+ paginationMetadata = await readPaginationMetadataForKeys([queryCacheKey], query);
56065
56482
  }
56066
56483
  let internalLimit = limit + (wantsHasNextPage ? 1 : 0);
56067
56484
  // Alias starts as entity's ApiName
@@ -56072,36 +56489,60 @@
56072
56489
  orderBy: orderByToPredicate(args.orderBy, alias, alias, context.objectInfos),
56073
56490
  limit: internalLimit,
56074
56491
  offset,
56075
- ingestionTimestamp,
56076
56492
  };
56077
56493
  const { sql, bindings } = buildQuery(queryConfig);
56078
56494
  const results = await query(sql, bindings);
56079
56495
  let hasNextPage = false;
56496
+ let earlyEnd = false;
56080
56497
  if (wantsHasNextPage) {
56081
56498
  if (results.rows.length > limit) {
56082
56499
  // more records exist in the cache
56083
56500
  hasNextPage = true;
56084
56501
  results.rows.pop();
56085
56502
  }
56086
- else if (!paginationMetadata || paginationMetadata.__END__ === undefined) {
56503
+ else if (!paginationMetadata ||
56504
+ !paginationMetadata.has(queryCacheKey) ||
56505
+ paginationMetadata.get(queryCacheKey).__END__ === undefined) {
56087
56506
  // more records may exist on the server
56088
56507
  hasNextPage = true;
56508
+ // we hit the end of our local records, so we need to know that we
56509
+ // should start at the end of known server cursors
56510
+ if (results.rows.length < limit) {
56511
+ earlyEnd = true;
56512
+ }
56089
56513
  }
56090
56514
  }
56091
56515
  //map each sql result with the ingestion timestamp to pass it down a level
56092
- let records = results.rows
56093
- .map((row) => parse$3(row[0]))
56094
- .map((recordRepresentation, index) => {
56516
+ let records = results.rows.map((row, index) => {
56517
+ const recordMetadataResult = {
56518
+ recordRepresentation: parse$3(row[0]),
56519
+ metadata: parse$3(row[1]),
56520
+ };
56521
+ const { recordRepresentation, metadata } = recordMetadataResult;
56095
56522
  context.seenRecordIds.add(recordRepresentation.id);
56523
+ if (metadata.ingestionTimestamp < ingestionTimestamp &&
56524
+ recordRepresentation.drafts === undefined) {
56525
+ if (context.possibleStaleRecordMap.has(recordRepresentation.apiName) === false) {
56526
+ context.possibleStaleRecordMap.set(recordRepresentation.apiName, []);
56527
+ }
56528
+ const ids = context.possibleStaleRecordMap.get(recordRepresentation.apiName);
56529
+ if (ids !== undefined) {
56530
+ ids.push(recordRepresentation.id);
56531
+ context.possibleStaleRecordMap.set(recordRepresentation.apiName, ids);
56532
+ }
56533
+ }
56096
56534
  return {
56097
56535
  recordRepresentation,
56098
56536
  ingestionTimestamp,
56099
- index: index + offset,
56537
+ index: index + offset + 1,
56538
+ earlyEnd,
56100
56539
  };
56101
56540
  });
56102
56541
  return {
56103
56542
  records,
56104
56543
  hasNextPage,
56544
+ earlyEnd,
56545
+ offset,
56105
56546
  };
56106
56547
  }
56107
56548
  /**
@@ -56899,7 +57340,7 @@
56899
57340
  return 'TextAreaValue';
56900
57341
  }
56901
57342
 
56902
- async function evaluate(config, observers, settings, objectInfos, store, snapshot, cache, draftFunctions) {
57343
+ async function evaluate(config, observers, settings, objectInfos, store, snapshot, cache, draftFunctions, mappedCursors) {
56903
57344
  const eventEmitter = createCustomAdapterEventEmitter(GRAPHQL_EVAL_NAMESPACE, observers);
56904
57345
  // this is only wrapped in a try to execute the event after the result was returned
56905
57346
  try {
@@ -56958,7 +57399,7 @@
56958
57399
  eventEmitter({ type: 'graphql-preconditions-met' });
56959
57400
  // create the resolver request context, runtime values and functions for
56960
57401
  // resolvers to do their job.
56961
- const contextValue = createContext(store, objectInfos, eventEmitter, settings, snapshot, draftFunctions);
57402
+ const contextValue = createContext(store, objectInfos, eventEmitter, settings, snapshot, mappedCursors, draftFunctions);
56962
57403
  // We're building this from scratch from each request. If this becomes a
56963
57404
  // hotspot we can pull it up and memoize it later
56964
57405
  const schema = createSchemaWithCache(objectInfos, cache);
@@ -56983,7 +57424,11 @@
56983
57424
  seenRecordIds.push(queryString);
56984
57425
  });
56985
57426
  }
56986
- return { result, seenRecordIds };
57427
+ return {
57428
+ result,
57429
+ seenRecordIds,
57430
+ possibleStaleRecordMap: contextValue.possibleStaleRecordMap,
57431
+ };
56987
57432
  }
56988
57433
  finally {
56989
57434
  eventEmitter({ type: 'graphql-eval-end' });
@@ -58707,7 +59152,11 @@
58707
59152
  return async function draftAwareGraphQLAdapter(config, buildCachedSnapshotCachePolicy, buildNetworkSnapshotCachePolicy, requestContext = {}) {
58708
59153
  //create a copy to not accidentally modify the AST in the astResolver map of luvio
58709
59154
  const copy = parse$3(stringify$3(config.query));
59155
+ // the injected ast has extra fields needed for eval in it
58710
59156
  let injectedAST;
59157
+ // the cursor mapped ast is passed upstream so it won't reject on our local cursors
59158
+ let cursorMappedAST;
59159
+ let mappedCursors = new Map();
58711
59160
  let objectInfoNeeded = {};
58712
59161
  let unmappedDraftIDs;
58713
59162
  let internalRequestContext = {
@@ -58723,6 +59172,7 @@
58723
59172
  objectInfos: objectInfoNeeded,
58724
59173
  unmappedDraftIDs,
58725
59174
  } = await injectSyntheticFields(copy, objectInfoService, draftFunctions, config.variables));
59175
+ ({ ast: cursorMappedAST, mappedCursors } = await mapPaginationCursors(injectedAST, config.variables || {}, store));
58726
59176
  if (config.variables) {
58727
59177
  config.variables = replaceDraftIdsInVariables$1(config.variables, draftFunctions, unmappedDraftIDs);
58728
59178
  }
@@ -58754,7 +59204,7 @@
58754
59204
  const nonEvaluatedSnapshot = (await luvio.applyCachePolicy(internalRequestContext, {
58755
59205
  config: {
58756
59206
  ...config,
58757
- query: injectedAST,
59207
+ query: cursorMappedAST,
58758
59208
  },
58759
59209
  luvio,
58760
59210
  gqlEval: true,
@@ -58767,12 +59217,17 @@
58767
59217
  : [];
58768
59218
  let gqlResult;
58769
59219
  let seenRecordIds;
59220
+ let possibleStaleRecordMap;
58770
59221
  try {
58771
- ({ result: gqlResult, seenRecordIds } = await evaluate({
59222
+ ({
59223
+ result: gqlResult,
59224
+ seenRecordIds,
59225
+ possibleStaleRecordMap,
59226
+ } = await evaluate({
58772
59227
  ...config,
58773
59228
  //need to create another copy of the ast for future writes
58774
59229
  query: parse$3(stringify$3(injectedAST)),
58775
- }, observers, { userId }, objectInfoNeeded, store, nonEvaluatedSnapshot, graphqlSchemaCache));
59230
+ }, observers, { userId }, objectInfoNeeded, store, nonEvaluatedSnapshot, graphqlSchemaCache, draftFunctions, mappedCursors));
58776
59231
  }
58777
59232
  catch (throwable) {
58778
59233
  const error = throwable;
@@ -58798,13 +59253,18 @@
58798
59253
  const seenRecords = createSeenRecords(seenRecordIds, nonEvaluatedSnapshot);
58799
59254
  const recordId = generateUniqueRecordId();
58800
59255
  const rebuildWithLocalEval = async (originalSnapshot) => {
58801
- let { result: rebuildResult, seenRecordIds } = await evaluate({
59256
+ let { result: rebuildResult, seenRecordIds, possibleStaleRecordMap, } = await evaluate({
58802
59257
  ...config,
58803
59258
  query: injectedAST,
58804
- }, observers, { userId }, objectInfoNeeded, store, originalSnapshot, graphqlSchemaCache, draftFunctions);
59259
+ }, observers, { userId }, objectInfoNeeded, store, originalSnapshot, graphqlSchemaCache, draftFunctions, mappedCursors);
58805
59260
  if (!rebuildResult.errors) {
58806
59261
  rebuildResult = removeSyntheticFields(rebuildResult, config.query);
58807
59262
  }
59263
+ let snapshotState = 'Fulfilled';
59264
+ if (possibleStaleRecordMap.size > 0) {
59265
+ initiateStaleRecordRefresh(luvio, possibleStaleRecordMap);
59266
+ snapshotState = 'Stale';
59267
+ }
58808
59268
  if (objectsDeepEqual(rebuildResult, originalSnapshot.data)) {
58809
59269
  return originalSnapshot;
58810
59270
  }
@@ -58813,6 +59273,7 @@
58813
59273
  ...originalSnapshot,
58814
59274
  data: rebuildResult,
58815
59275
  recordId,
59276
+ state: snapshotState,
58816
59277
  seenRecords: createSeenRecords(seenRecordIds, nonEvaluatedSnapshot),
58817
59278
  rebuildWithLocalEval,
58818
59279
  };
@@ -58850,9 +59311,31 @@
58850
59311
  },
58851
59312
  };
58852
59313
  }
59314
+ if (possibleStaleRecordMap.size > 0) {
59315
+ initiateStaleRecordRefresh(luvio, possibleStaleRecordMap);
59316
+ resultSnapshot.state = 'Stale';
59317
+ }
58853
59318
  return resultSnapshot;
58854
59319
  };
58855
59320
  }
59321
+ function initiateStaleRecordRefresh(luvio, keyMap) {
59322
+ const staleRecordKeys = from$1(keyMap.values())
59323
+ .flat()
59324
+ .map((id) => `UiApi::RecordRepresentation:${id}`);
59325
+ luvio.storeExpirePossibleStaleRecords(staleRecordKeys, makeGetRecordsConfig(keyMap), getRecordsAdapterFactory(luvio));
59326
+ }
59327
+ function makeGetRecordsConfig(keyMap) {
59328
+ const records = [];
59329
+ keyMap.forEach((recordIds, apiName) => {
59330
+ records.push({
59331
+ recordIds,
59332
+ fields: [`${apiName}.Id`],
59333
+ });
59334
+ });
59335
+ return {
59336
+ records,
59337
+ };
59338
+ }
58856
59339
 
58857
59340
  function environmentAwareGraphQLBatchAdapterFactory(objectInfoService, luvio, isDraftId) {
58858
59341
  return async function environmentAwareGraphQLBatchAdapter(config, buildCachedSnapshotCachePolicy, buildNetworkSnapshotCachePolicy, requestContext = {}) {
@@ -59998,6 +60481,9 @@
59998
60481
  removeHandler(_id) {
59999
60482
  return Promise.reject(new Error('Cannot call setMetadata from the NimbusDraftQueue'));
60000
60483
  }
60484
+ updateDraftAction(_action) {
60485
+ return Promise.reject(new Error('Cannot call updateDraftAction from the NimbusDraftQueue'));
60486
+ }
60001
60487
  }
60002
60488
 
60003
60489
  function attachObserversToAdapterRequestContext(observers, adapterRequestContext) {
@@ -61111,6 +61597,21 @@
61111
61597
  __nimbus.plugins.LdsBinaryStorePlugin.setCanonicalUrl(uri, canonicalUrl, ttlSeconds, resolve, (err) => reject(errorMessageToError(err)));
61112
61598
  });
61113
61599
  },
61600
+ createStream: function (type) {
61601
+ return new Promise((resolve, reject) => {
61602
+ __nimbus.plugins.LdsBinaryStorePlugin.createStream(type, resolve, (err) => reject(errorMessageToError(err)));
61603
+ });
61604
+ },
61605
+ writeToStream: function (uri, chunk) {
61606
+ return new Promise((resolve, reject) => {
61607
+ __nimbus.plugins.LdsBinaryStorePlugin.writeToStream(uri, chunk, resolve, (err) => reject(errorMessageToError(err)));
61608
+ });
61609
+ },
61610
+ closeStream: function (uri) {
61611
+ return new Promise((resolve, reject) => {
61612
+ __nimbus.plugins.LdsBinaryStorePlugin.closeStream(uri, resolve, (err) => reject(errorMessageToError(err)));
61613
+ });
61614
+ },
61114
61615
  };
61115
61616
 
61116
61617
  /**
@@ -62438,7 +62939,6 @@
62438
62939
  let lazyNetworkAdapter;
62439
62940
  let lazyObjectInfoService;
62440
62941
  let lazyGetRecords;
62441
- // TODO [W-123]: JHORST hoist, optimize and test this function
62442
62942
  const shouldFlush = (key, value) => {
62443
62943
  if (!isStoreKeyRecordId$1(key)) {
62444
62944
  return { flushValue: true };
@@ -62609,7 +63109,7 @@
62609
63109
  id: '@salesforce/lds-network-adapter',
62610
63110
  instrument: instrument$2,
62611
63111
  });
62612
- // version: 1.302.0-e45992a1f4
63112
+ // version: 1.304.0-aa3e5f9550
62613
63113
 
62614
63114
  const { create: create$3, keys: keys$3 } = Object;
62615
63115
  const { stringify: stringify$1, parse: parse$1 } = JSON;
@@ -82645,7 +83145,7 @@
82645
83145
  configuration: { ...configurationForGraphQLAdapters$1 },
82646
83146
  instrument: instrument$1,
82647
83147
  });
82648
- // version: 1.302.0-5fb014108f
83148
+ // version: 1.304.0-d87b57badb
82649
83149
 
82650
83150
  // On core the unstable adapters are re-exported with different names,
82651
83151
  // we want to match them here.
@@ -84901,7 +85401,7 @@
84901
85401
  unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
84902
85402
  graphQLImperative = ldsAdapter;
84903
85403
  });
84904
- // version: 1.302.0-5fb014108f
85404
+ // version: 1.304.0-d87b57badb
84905
85405
 
84906
85406
  var gqlApi = /*#__PURE__*/Object.freeze({
84907
85407
  __proto__: null,
@@ -85636,7 +86136,7 @@
85636
86136
  function register(r) {
85637
86137
  callbacks$1.forEach((callback) => callback(r));
85638
86138
  }
85639
- // version: 1.302.0-e45992a1f4
86139
+ // version: 1.304.0-aa3e5f9550
85640
86140
 
85641
86141
  /**
85642
86142
  * Returns true if the value acts like a Promise, i.e. has a "then" function,
@@ -90617,4 +91117,4 @@
90617
91117
  exports.subscribeToAdapter = subscribeToAdapter;
90618
91118
 
90619
91119
  }));
90620
- // version: 1.302.0-e45992a1f4
91120
+ // version: 1.304.0-aa3e5f9550