@salesforce/lds-runtime-mobile 1.266.0-dev2 → 1.266.0-dev20

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.
Files changed (3) hide show
  1. package/dist/main.js +133 -52
  2. package/package.json +16 -16
  3. package/sfdc/main.js +133 -52
package/dist/main.js CHANGED
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { withRegistration, register } from '@salesforce/lds-default-luvio';
15
15
  import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrumentLuvio, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation } from '@salesforce/lds-instrumentation';
16
- import { HttpStatusCode, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from '@luvio/engine';
16
+ import { HttpStatusCode, setBypassDeepFreeze, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from '@luvio/engine';
17
17
  import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
18
18
  import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from '@luvio/graphql-parser';
19
19
  import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from '@salesforce/lds-adapters-uiapi';
@@ -1014,7 +1014,9 @@ function isUnfulfilledSnapshot$1(cachedSnapshotResult) {
1014
1014
  * @param durableStore A DurableStore implementation
1015
1015
  * @param instrumentation An instrumentation function implementation
1016
1016
  */
1017
- function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, }) {
1017
+ function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, disableDeepFreeze = false, }) {
1018
+ // runtimes can choose to disable deepFreeze, e.g. headless mobile runtime
1019
+ setBypassDeepFreeze(disableDeepFreeze);
1018
1020
  let stagingStore = null;
1019
1021
  const durableTTLStore = new DurableTTLStore(durableStore);
1020
1022
  const mergeKeysPromiseMap = new Map();
@@ -1385,6 +1387,10 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
1385
1387
  }
1386
1388
  return {};
1387
1389
  };
1390
+ const getIngestStagingStore = function () {
1391
+ validateNotDisposed();
1392
+ return stagingStore === null || stagingStore === void 0 ? void 0 : stagingStore.fallbackStringKeyInMemoryStore;
1393
+ };
1388
1394
  const handleSuccessResponse = async function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
1389
1395
  validateNotDisposed();
1390
1396
  const cacheKeyMap = getResponseCacheKeysFunc();
@@ -1577,6 +1583,7 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
1577
1583
  applyCachePolicy: { value: applyCachePolicy },
1578
1584
  getIngestStagingStoreRecords: { value: getIngestStagingStoreRecords },
1579
1585
  getIngestStagingStoreMetadata: { value: getIngestStagingStoreMetadata },
1586
+ getIngestStagingStore: { value: getIngestStagingStore },
1580
1587
  handleSuccessResponse: { value: handleSuccessResponse },
1581
1588
  handleErrorResponse: { value: handleErrorResponse },
1582
1589
  getNotifyChangeStoreEntries: { value: getNotifyChangeStoreEntries },
@@ -4374,9 +4381,10 @@ function rootRecordQuery(selection, input) {
4374
4381
  // If there is no metadata for this query or it somehow lacks a timestamp
4375
4382
  // skip setting the root timestamp
4376
4383
  if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
4377
- // adjust the timestamp to account for ingestion processing time
4378
- // 30s is used because this is the default record TTL
4379
- input.rootTimestamp = queryMetadata.ingestionTimestamp - 30000;
4384
+ const timestamp = Number(queryMetadata.ingestionTimestamp);
4385
+ if (!isNaN(timestamp)) {
4386
+ input.rootTimestamp = timestamp;
4387
+ }
4380
4388
  }
4381
4389
  }
4382
4390
  return recordQuery(selection, alias, apiName, [], input);
@@ -5842,7 +5850,12 @@ class DurableDraftQueue {
5842
5850
  }
5843
5851
  finally {
5844
5852
  this.replacingAction = undefined;
5845
- await this.startQueueSafe();
5853
+ try {
5854
+ await this.startQueueSafe();
5855
+ }
5856
+ catch (_a) {
5857
+ // An error starting the queue should not bubble up from this method
5858
+ }
5846
5859
  }
5847
5860
  return updatedTarget;
5848
5861
  });
@@ -8005,6 +8018,9 @@ function createSinglePredicate(val, operator, field, alias) {
8005
8018
  else if (field.apiName === 'weakEtag') {
8006
8019
  leftPath = '$.weakEtag';
8007
8020
  }
8021
+ else if (field.apiName === 'RecordTypeId') {
8022
+ leftPath = '$.recordTypeId';
8023
+ }
8008
8024
  return {
8009
8025
  alias,
8010
8026
  leftPath,
@@ -9041,6 +9057,20 @@ function findFieldInfo(objectInfo, fieldName) {
9041
9057
  return values$2(objectInfo.fields).find((field) => field.apiName === fieldName ||
9042
9058
  (field.dataType === 'Reference' && field.relationshipName === fieldName));
9043
9059
  }
9060
+ async function readIngestionTimestampForKey(key, query) {
9061
+ let ingestionTimestamp = 0;
9062
+ const sql = `SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') FROM lds_data WHERE key IS ?`;
9063
+ const results = await query(sql, [key]);
9064
+ const [timestamp] = results.rows.map((row) => row[0]);
9065
+ if (timestamp !== null) {
9066
+ const numericalTimestamp = Number(timestamp);
9067
+ if (isNaN(numericalTimestamp)) {
9068
+ return ingestionTimestamp;
9069
+ }
9070
+ ingestionTimestamp = numericalTimestamp;
9071
+ }
9072
+ return ingestionTimestamp;
9073
+ }
9044
9074
 
9045
9075
  function findSpanningField(name) {
9046
9076
  return (field) => {
@@ -9365,6 +9395,12 @@ function addResolversToSchema(schema, polyFields) {
9365
9395
  break;
9366
9396
  case 'LastModifiedDate':
9367
9397
  field.resolve = ({ recordRepresentation: record }) => {
9398
+ // In UIAPI record reps, LastModifiedDate might be present as a field,
9399
+ // which will include both the value and displayValue
9400
+ if (record.fields['LastModifiedDate']) {
9401
+ return record.fields['LastModifiedDate'];
9402
+ }
9403
+ // If the field is not present, just return the value of the root property
9368
9404
  return record.lastModifiedDate
9369
9405
  ? { value: record.lastModifiedDate }
9370
9406
  : null;
@@ -9399,16 +9435,26 @@ function addResolversToSchema(schema, polyFields) {
9399
9435
  : null;
9400
9436
  };
9401
9437
  const { recordRepresentation: record, ingestionTimestamp } = obj;
9402
- const fieldName = field.name.endsWith('__r')
9403
- ? field.name.replace('__r', '__c')
9404
- : field.name;
9405
- const id = (record.fields[fieldName] && record.fields[fieldName].value) ||
9406
- (record.fields[`${fieldName}Id`] &&
9407
- record.fields[`${fieldName}Id`].value);
9408
- if (!id)
9438
+ let id = undefined;
9439
+ if (field.name === 'RecordType') {
9440
+ // RecordTypeId has special handling during ingest and is
9441
+ // not in record.fields, so check for it at the UIAPI root property location
9442
+ id = record.recordTypeId;
9443
+ }
9444
+ else if (field.name.endsWith('__r')) {
9445
+ // Custom relationships end in `__r` and the corresponding ID field should be `__c`
9446
+ let fieldName = field.name.replace('__r', '__c');
9447
+ id = record.fields[fieldName] && record.fields[fieldName].value;
9448
+ }
9449
+ else {
9450
+ // Standard relationships are just FieldNameId
9451
+ let fieldName = field.name + 'Id';
9452
+ id = record.fields[fieldName] && record.fields[fieldName].value;
9453
+ }
9454
+ if (!id || typeof id !== 'string') {
9455
+ // possibly field injection did not inject the necessary Id field
9456
+ // for the relationship, or we found a non-string value.
9409
9457
  return null;
9410
- if (id['__ref'] !== undefined) {
9411
- return fetchRecordOrNull(record.fields[`${field.name}Id`].value);
9412
9458
  }
9413
9459
  seenRecordIds.add(id);
9414
9460
  return fetchRecordOrNull(id);
@@ -9560,18 +9606,7 @@ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
9560
9606
  const key = buildKeyStringForRecordQuery(operation,
9561
9607
  // join varables passed from query to the argument variables given from the AST
9562
9608
  { ...variableValues, ...args }, info.fieldNodes[0].arguments, apiName);
9563
- const sql = `
9564
- SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
9565
- FROM lds_data
9566
- WHERE key IS ?
9567
- `;
9568
- const results = await query(sql, [key]);
9569
- const [timestamp] = results.rows.map((row) => row[0]);
9570
- if (timestamp !== null && typeof timestamp === 'number') {
9571
- // adjust the timestamp to account for ingestion processing time
9572
- // 30s is used because this is the default record TTL
9573
- ingestionTimestamp = timestamp - 30000;
9574
- }
9609
+ return readIngestionTimestampForKey(key, query);
9575
9610
  }
9576
9611
  return ingestionTimestamp;
9577
9612
  }
@@ -11638,7 +11673,7 @@ function createUserJsonOutput(selection, jsonInput, jsonOutput) {
11638
11673
  function createjsonOutput(selections, jsonInput, jsonOutput) {
11639
11674
  const keys$1 = keys$4(jsonInput);
11640
11675
  selections.filter(isFieldNode).forEach((subSelection) => {
11641
- const fieldName = subSelection.name.value;
11676
+ const fieldName = subSelection.alias ? subSelection.alias.value : subSelection.name.value;
11642
11677
  if (keys$1.includes(fieldName)) {
11643
11678
  if (isArray$2(jsonInput[fieldName])) {
11644
11679
  jsonOutput[fieldName] = [];
@@ -12358,13 +12393,21 @@ function buildSyntheticRecordRepresentation(luvio, createOperation, userId, obje
12358
12393
  draftFields[DEFAULT_FIELD_LAST_MODIFIED_DATE] = { value: timestampString, displayValue: null };
12359
12394
  draftFields[DEFAULT_FIELD_OWNER_ID] = { value: userId, displayValue: null };
12360
12395
  draftFields[DEFAULT_FIELD_ID] = { value: recordId, displayValue: null };
12361
- if (objectInfo !== undefined) {
12362
- const allObjectFields = keys$3(objectInfo.fields);
12363
- allObjectFields.forEach((fieldName) => {
12364
- if (draftFields[fieldName] === undefined) {
12365
- draftFields[fieldName] = { value: null, displayValue: null };
12366
- }
12367
- });
12396
+ const allObjectFields = keys$3(objectInfo.fields);
12397
+ allObjectFields.forEach((fieldName) => {
12398
+ if (draftFields[fieldName] === undefined) {
12399
+ draftFields[fieldName] = { value: null, displayValue: null };
12400
+ }
12401
+ });
12402
+ // TODO [W-14915806]: lightning-record-form injects the `IsPersonAccount`
12403
+ // field for all `Account` and `PersonAccount` records. However, not all
12404
+ // orgs use person accounts, and if that field is not present in the object
12405
+ // info then it is not synthesized. We force it to be synthesized here to
12406
+ // ensure lightning-record-form will work correctly with draft-created
12407
+ // accounts.
12408
+ if ((apiName === 'Account' || apiName === 'PersonAccount') &&
12409
+ draftFields['IsPersonAccount'] === undefined) {
12410
+ draftFields['IsPersonAccount'] = { value: null, displayValue: null };
12368
12411
  }
12369
12412
  return {
12370
12413
  id: recordId,
@@ -12951,10 +12994,13 @@ function normalizeRecordFields(key, entry) {
12951
12994
  * Transforms a record for storage in the durable store. The transformation involves denormalizing
12952
12995
  * scalar fields and persisting link metadata to transform back into a normalized representation
12953
12996
  *
12997
+ * If the record contains pending fields this will return undefined as pending records do not get persisted
12998
+ * to the durable store. There should be a refresh operation outbound that will bring in the updated record.
12999
+ *
12954
13000
  * @param normalizedRecord Record containing normalized field links
12955
13001
  * @param recordStore a store containing referenced record fields
12956
13002
  */
12957
- function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntries) {
13003
+ function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntries, store) {
12958
13004
  const fields = normalizedRecord.fields;
12959
13005
  const filteredFields = {};
12960
13006
  const links = {};
@@ -12965,7 +13011,9 @@ function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntr
12965
13011
  // pending fields get filtered out of the durable store
12966
13012
  const { pending } = field;
12967
13013
  if (pending === true) {
12968
- continue;
13014
+ // do not write records with pending fields to the durable store
13015
+ // there should be a refresh operation outbound that will bring in the updated record
13016
+ return undefined;
12969
13017
  }
12970
13018
  const { __ref } = field;
12971
13019
  if (__ref !== undefined) {
@@ -12981,6 +13029,19 @@ function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntr
12981
13029
  if (ref !== undefined) {
12982
13030
  filteredFields[fieldName] = ref;
12983
13031
  }
13032
+ else {
13033
+ // if we have a store to read, try to find the field there too
13034
+ // The durable ingest staging store may pass through to L1, and
13035
+ // not all fields are necessarily published every time, so it is
13036
+ // important to check L1 and not just the fields being published,
13037
+ // otherwise we risk truncating the fields on the record.
13038
+ if (store) {
13039
+ ref = store.readEntry(__ref);
13040
+ if (ref !== undefined) {
13041
+ filteredFields[fieldName] = ref;
13042
+ }
13043
+ }
13044
+ }
12984
13045
  }
12985
13046
  // we want to preserve fields that are missing nodes
12986
13047
  if (filteredFields[fieldName] !== undefined || field.isMissing === true) {
@@ -13002,7 +13063,7 @@ function getDenormalizedKey(originalKey, recordId, luvio) {
13002
13063
  }
13003
13064
  return keyBuilderRecord(luvio, { recordId });
13004
13065
  }
13005
- function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecords, getStoreMetadata) {
13066
+ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecords, getStoreMetadata, getStore) {
13006
13067
  const getEntries = function (entries, segment) {
13007
13068
  // this HOF only inspects records in the default segment
13008
13069
  if (segment !== DefaultDurableSegment) {
@@ -13070,6 +13131,7 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
13070
13131
  const putRecordViews = {};
13071
13132
  const storeRecords = getStoreRecords !== undefined ? getStoreRecords() : {};
13072
13133
  const storeMetadata = getStoreMetadata !== undefined ? getStoreMetadata() : {};
13134
+ const store = getStore();
13073
13135
  for (let i = 0, len = keys$1.length; i < len; i++) {
13074
13136
  const key = keys$1[i];
13075
13137
  let value = entries[key];
@@ -13116,11 +13178,13 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
13116
13178
  metadataVersion: DURABLE_METADATA_VERSION,
13117
13179
  };
13118
13180
  }
13119
- const denormalizedRecord = buildDurableRecordRepresentation(record, storeRecords, recordEntries);
13120
- putEntries[recordKey] = {
13121
- data: denormalizedRecord,
13122
- metadata,
13123
- };
13181
+ const denormalizedRecord = buildDurableRecordRepresentation(record, storeRecords, recordEntries, store);
13182
+ if (denormalizedRecord !== undefined) {
13183
+ putEntries[recordKey] = {
13184
+ data: denormalizedRecord,
13185
+ metadata,
13186
+ };
13187
+ }
13124
13188
  }
13125
13189
  else {
13126
13190
  putEntries[key] = value;
@@ -15948,9 +16012,9 @@ function instrumentAdapter(adapter, metadata) {
15948
16012
  }
15949
16013
  }
15950
16014
  return instrumentAdapter$1(instrumentedMobileAdapter, metadata, {
15951
- trackL1Hits: true,
15952
- trackL2Hits: true,
15953
- trackCacheMisses: true,
16015
+ trackL1Hits: false,
16016
+ trackL2Hits: false,
16017
+ trackCacheMisses: false,
15954
16018
  reportObserver: (report) => {
15955
16019
  for (const observer of reportObservers) {
15956
16020
  observer(report);
@@ -16757,6 +16821,7 @@ function findReferenceFieldForSpanningField(fieldName, objectInfo) {
16757
16821
  function buildFieldUnionArray(existingRecord, incomingRecord, objectInfo) {
16758
16822
  const allFields = Array.from(new Set([...Object.keys(existingRecord.fields), ...Object.keys(incomingRecord.fields)]));
16759
16823
  const fieldUnion = [];
16824
+ let includesSpanningFields = false;
16760
16825
  allFields.forEach((fieldName) => {
16761
16826
  const objectInfoField = objectInfo.fields[fieldName];
16762
16827
  if (objectInfoField === undefined) {
@@ -16764,13 +16829,14 @@ function buildFieldUnionArray(existingRecord, incomingRecord, objectInfo) {
16764
16829
  const referenceField = findReferenceFieldForSpanningField(fieldName, objectInfo);
16765
16830
  if (referenceField !== undefined) {
16766
16831
  fieldUnion.push(`${fieldName}.Id`);
16832
+ includesSpanningFields = true;
16767
16833
  }
16768
16834
  }
16769
16835
  else {
16770
16836
  fieldUnion.push(fieldName);
16771
16837
  }
16772
16838
  });
16773
- return fieldUnion;
16839
+ return { fields: fieldUnion, includesSpanningFields };
16774
16840
  }
16775
16841
  /**
16776
16842
  * Merges (if possible) an incoming record from a priming session with an existing record in the durable store.
@@ -16802,7 +16868,7 @@ function mergeRecord(existingRecord, incomingRecord, objectInfo) {
16802
16868
  ok: false,
16803
16869
  code: 'conflict-drafts',
16804
16870
  hasDraft: true,
16805
- fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
16871
+ fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo).fields,
16806
16872
  };
16807
16873
  }
16808
16874
  // Check if incoming record's Etag is equal to the existing one
@@ -16864,10 +16930,19 @@ function mergeRecord(existingRecord, incomingRecord, objectInfo) {
16864
16930
  };
16865
16931
  }
16866
16932
  // If Etags do not match and the incoming record does not contain all fields, re-request the record
16933
+ const { fields, includesSpanningFields } = buildFieldUnionArray(existingRecord, incomingRecord, objectInfo);
16934
+ if (includesSpanningFields) {
16935
+ return {
16936
+ ok: false,
16937
+ code: 'conflict-spanning-record',
16938
+ fieldUnion: fields,
16939
+ hasDraft: false,
16940
+ };
16941
+ }
16867
16942
  return {
16868
16943
  ok: false,
16869
16944
  code: 'conflict-missing-fields',
16870
- fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
16945
+ fieldUnion: fields,
16871
16946
  hasDraft: false,
16872
16947
  };
16873
16948
  }
@@ -17793,30 +17868,36 @@ function getRuntime() {
17793
17868
  const internalAdapterStore = new InMemoryStore();
17794
17869
  let getIngestRecordsForInternalAdapters;
17795
17870
  let getIngestMetadataForInternalAdapters;
17871
+ let getIngestStoreInternal;
17796
17872
  const internalAdapterDurableStore = makeRecordDenormalizingDurableStore(lazyLuvio, lazyBaseDurableStore, () => getIngestRecordsForInternalAdapters !== undefined
17797
17873
  ? getIngestRecordsForInternalAdapters()
17798
17874
  : {}, () => getIngestMetadataForInternalAdapters !== undefined
17799
17875
  ? getIngestMetadataForInternalAdapters()
17800
- : {});
17876
+ : {}, () => (getIngestStoreInternal !== undefined ? getIngestStoreInternal() : undefined));
17801
17877
  const { adapters: { getObjectInfo, getObjectInfos, getRecord, getObjectInfoDirectory }, durableEnvironment: internalAdapterDurableEnvironment, luvio: internalLuvio, } = buildInternalAdapters(internalAdapterStore, lazyNetworkAdapter, internalAdapterDurableStore, (apiName, objectInfo) => lazyObjectInfoService.ensureObjectInfoCached(apiName, objectInfo));
17802
17878
  lazyInternalLuvio = internalLuvio;
17803
17879
  getIngestRecordsForInternalAdapters =
17804
17880
  internalAdapterDurableEnvironment.getIngestStagingStoreRecords;
17805
17881
  getIngestMetadataForInternalAdapters =
17806
17882
  internalAdapterDurableEnvironment.getIngestStagingStoreRecords;
17883
+ getIngestStoreInternal = internalAdapterDurableEnvironment.getIngestStagingStore;
17807
17884
  lazyObjectInfoService = new ObjectInfoService(getObjectInfo, getObjectInfos, getObjectInfoDirectory, lazyBaseDurableStore);
17808
17885
  // creates a durable store that denormalizes scalar fields for records
17809
17886
  let getIngestRecords;
17810
17887
  let getIngestMetadata;
17811
- const recordDenormingStore = makeRecordDenormalizingDurableStore(lazyLuvio, lazyBaseDurableStore, () => (getIngestRecords !== undefined ? getIngestRecords() : {}), () => (getIngestMetadata !== undefined ? getIngestMetadata() : {}));
17888
+ let getIngestStore;
17889
+ const recordDenormingStore = makeRecordDenormalizingDurableStore(lazyLuvio, lazyBaseDurableStore, () => (getIngestRecords !== undefined ? getIngestRecords() : {}), () => (getIngestMetadata !== undefined ? getIngestMetadata() : {}), () => (getIngestStore !== undefined ? getIngestStore() : undefined));
17812
17890
  const baseEnv = new Environment(store, lazyNetworkAdapter);
17813
17891
  const gqlEnv = makeEnvironmentGraphqlAware(baseEnv);
17814
17892
  const durableEnv = makeDurable(gqlEnv, {
17815
17893
  durableStore: recordDenormingStore,
17816
17894
  enableDurableMetadataRefresh: ldsMetadataRefreshEnabled.isOpen({ fallback: false }),
17895
+ // disable luvio deep freeze in headless environments
17896
+ disableDeepFreeze: typeof window === 'undefined',
17817
17897
  });
17818
17898
  getIngestRecords = durableEnv.getIngestStagingStoreRecords;
17819
17899
  getIngestMetadata = durableEnv.getIngestStagingStoreMetadata;
17900
+ getIngestStore = durableEnv.getIngestStagingStore;
17820
17901
  // draft queue
17821
17902
  lazyDraftQueue = buildLdsDraftQueue(recordDenormingStore);
17822
17903
  const draftService = new UiApiDraftRecordService(lazyDraftQueue, () => lazyLuvio, recordDenormingStore, getObjectInfo, newRecordId, userId, formatDisplayValue);
@@ -17916,4 +17997,4 @@ register({
17916
17997
  });
17917
17998
 
17918
17999
  export { O11Y_NAMESPACE_LDS_MOBILE, getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17919
- // version: 1.266.0-dev2-7c2f3615f
18000
+ // version: 1.266.0-dev20-117d849b4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.266.0-dev2",
3
+ "version": "1.266.0-dev20",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
@@ -32,25 +32,25 @@
32
32
  "release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-runtime-mobile"
33
33
  },
34
34
  "dependencies": {
35
- "@salesforce/lds-adapters-uiapi": "^1.266.0-dev2",
36
- "@salesforce/lds-bindings": "^1.266.0-dev2",
37
- "@salesforce/lds-instrumentation": "^1.266.0-dev2",
38
- "@salesforce/lds-priming": "^1.266.0-dev2",
35
+ "@salesforce/lds-adapters-uiapi": "^1.266.0-dev20",
36
+ "@salesforce/lds-bindings": "^1.266.0-dev20",
37
+ "@salesforce/lds-instrumentation": "^1.266.0-dev20",
38
+ "@salesforce/lds-priming": "^1.266.0-dev20",
39
39
  "@salesforce/user": "0.0.21",
40
40
  "o11y": "244.0.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@salesforce/lds-adapters-graphql": "^1.266.0-dev2",
44
- "@salesforce/lds-drafts": "^1.266.0-dev2",
45
- "@salesforce/lds-drafts-adapters-uiapi": "^1.266.0-dev2",
46
- "@salesforce/lds-graphql-eval": "^1.266.0-dev2",
47
- "@salesforce/lds-network-adapter": "^1.266.0-dev2",
48
- "@salesforce/lds-network-nimbus": "^1.266.0-dev2",
49
- "@salesforce/lds-store-binary": "^1.266.0-dev2",
50
- "@salesforce/lds-store-nimbus": "^1.266.0-dev2",
51
- "@salesforce/lds-store-sql": "^1.266.0-dev2",
52
- "@salesforce/lds-utils-adapters": "^1.266.0-dev2",
53
- "@salesforce/nimbus-plugin-lds": "^1.266.0-dev2",
43
+ "@salesforce/lds-adapters-graphql": "^1.266.0-dev20",
44
+ "@salesforce/lds-drafts": "^1.266.0-dev20",
45
+ "@salesforce/lds-drafts-adapters-uiapi": "^1.266.0-dev20",
46
+ "@salesforce/lds-graphql-eval": "^1.266.0-dev20",
47
+ "@salesforce/lds-network-adapter": "^1.266.0-dev20",
48
+ "@salesforce/lds-network-nimbus": "^1.266.0-dev20",
49
+ "@salesforce/lds-store-binary": "^1.266.0-dev20",
50
+ "@salesforce/lds-store-nimbus": "^1.266.0-dev20",
51
+ "@salesforce/lds-store-sql": "^1.266.0-dev20",
52
+ "@salesforce/lds-utils-adapters": "^1.266.0-dev20",
53
+ "@salesforce/nimbus-plugin-lds": "^1.266.0-dev20",
54
54
  "babel-plugin-dynamic-import-node": "^2.3.3",
55
55
  "wait-for-expect": "^3.0.2"
56
56
  },
package/sfdc/main.js CHANGED
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { withRegistration, register } from 'native/ldsEngineMobile';
15
15
  import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrumentLuvio, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation } from 'force/ldsInstrumentation';
16
- import { HttpStatusCode, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from 'force/luvioEngine';
16
+ import { HttpStatusCode, setBypassDeepFreeze, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from 'force/luvioEngine';
17
17
  import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
18
18
  import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from 'force/ldsGraphqlParser';
19
19
  import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from 'force/ldsAdaptersUiapi';
@@ -1014,7 +1014,9 @@ function isUnfulfilledSnapshot$1(cachedSnapshotResult) {
1014
1014
  * @param durableStore A DurableStore implementation
1015
1015
  * @param instrumentation An instrumentation function implementation
1016
1016
  */
1017
- function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, }) {
1017
+ function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, disableDeepFreeze = false, }) {
1018
+ // runtimes can choose to disable deepFreeze, e.g. headless mobile runtime
1019
+ setBypassDeepFreeze(disableDeepFreeze);
1018
1020
  let stagingStore = null;
1019
1021
  const durableTTLStore = new DurableTTLStore(durableStore);
1020
1022
  const mergeKeysPromiseMap = new Map();
@@ -1385,6 +1387,10 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
1385
1387
  }
1386
1388
  return {};
1387
1389
  };
1390
+ const getIngestStagingStore = function () {
1391
+ validateNotDisposed();
1392
+ return stagingStore === null || stagingStore === void 0 ? void 0 : stagingStore.fallbackStringKeyInMemoryStore;
1393
+ };
1388
1394
  const handleSuccessResponse = async function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
1389
1395
  validateNotDisposed();
1390
1396
  const cacheKeyMap = getResponseCacheKeysFunc();
@@ -1577,6 +1583,7 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
1577
1583
  applyCachePolicy: { value: applyCachePolicy },
1578
1584
  getIngestStagingStoreRecords: { value: getIngestStagingStoreRecords },
1579
1585
  getIngestStagingStoreMetadata: { value: getIngestStagingStoreMetadata },
1586
+ getIngestStagingStore: { value: getIngestStagingStore },
1580
1587
  handleSuccessResponse: { value: handleSuccessResponse },
1581
1588
  handleErrorResponse: { value: handleErrorResponse },
1582
1589
  getNotifyChangeStoreEntries: { value: getNotifyChangeStoreEntries },
@@ -4374,9 +4381,10 @@ function rootRecordQuery(selection, input) {
4374
4381
  // If there is no metadata for this query or it somehow lacks a timestamp
4375
4382
  // skip setting the root timestamp
4376
4383
  if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
4377
- // adjust the timestamp to account for ingestion processing time
4378
- // 30s is used because this is the default record TTL
4379
- input.rootTimestamp = queryMetadata.ingestionTimestamp - 30000;
4384
+ const timestamp = Number(queryMetadata.ingestionTimestamp);
4385
+ if (!isNaN(timestamp)) {
4386
+ input.rootTimestamp = timestamp;
4387
+ }
4380
4388
  }
4381
4389
  }
4382
4390
  return recordQuery(selection, alias, apiName, [], input);
@@ -5842,7 +5850,12 @@ class DurableDraftQueue {
5842
5850
  }
5843
5851
  finally {
5844
5852
  this.replacingAction = undefined;
5845
- await this.startQueueSafe();
5853
+ try {
5854
+ await this.startQueueSafe();
5855
+ }
5856
+ catch (_a) {
5857
+ // An error starting the queue should not bubble up from this method
5858
+ }
5846
5859
  }
5847
5860
  return updatedTarget;
5848
5861
  });
@@ -8005,6 +8018,9 @@ function createSinglePredicate(val, operator, field, alias) {
8005
8018
  else if (field.apiName === 'weakEtag') {
8006
8019
  leftPath = '$.weakEtag';
8007
8020
  }
8021
+ else if (field.apiName === 'RecordTypeId') {
8022
+ leftPath = '$.recordTypeId';
8023
+ }
8008
8024
  return {
8009
8025
  alias,
8010
8026
  leftPath,
@@ -9041,6 +9057,20 @@ function findFieldInfo(objectInfo, fieldName) {
9041
9057
  return values$2(objectInfo.fields).find((field) => field.apiName === fieldName ||
9042
9058
  (field.dataType === 'Reference' && field.relationshipName === fieldName));
9043
9059
  }
9060
+ async function readIngestionTimestampForKey(key, query) {
9061
+ let ingestionTimestamp = 0;
9062
+ const sql = `SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') FROM lds_data WHERE key IS ?`;
9063
+ const results = await query(sql, [key]);
9064
+ const [timestamp] = results.rows.map((row) => row[0]);
9065
+ if (timestamp !== null) {
9066
+ const numericalTimestamp = Number(timestamp);
9067
+ if (isNaN(numericalTimestamp)) {
9068
+ return ingestionTimestamp;
9069
+ }
9070
+ ingestionTimestamp = numericalTimestamp;
9071
+ }
9072
+ return ingestionTimestamp;
9073
+ }
9044
9074
 
9045
9075
  function findSpanningField(name) {
9046
9076
  return (field) => {
@@ -9365,6 +9395,12 @@ function addResolversToSchema(schema, polyFields) {
9365
9395
  break;
9366
9396
  case 'LastModifiedDate':
9367
9397
  field.resolve = ({ recordRepresentation: record }) => {
9398
+ // In UIAPI record reps, LastModifiedDate might be present as a field,
9399
+ // which will include both the value and displayValue
9400
+ if (record.fields['LastModifiedDate']) {
9401
+ return record.fields['LastModifiedDate'];
9402
+ }
9403
+ // If the field is not present, just return the value of the root property
9368
9404
  return record.lastModifiedDate
9369
9405
  ? { value: record.lastModifiedDate }
9370
9406
  : null;
@@ -9399,16 +9435,26 @@ function addResolversToSchema(schema, polyFields) {
9399
9435
  : null;
9400
9436
  };
9401
9437
  const { recordRepresentation: record, ingestionTimestamp } = obj;
9402
- const fieldName = field.name.endsWith('__r')
9403
- ? field.name.replace('__r', '__c')
9404
- : field.name;
9405
- const id = (record.fields[fieldName] && record.fields[fieldName].value) ||
9406
- (record.fields[`${fieldName}Id`] &&
9407
- record.fields[`${fieldName}Id`].value);
9408
- if (!id)
9438
+ let id = undefined;
9439
+ if (field.name === 'RecordType') {
9440
+ // RecordTypeId has special handling during ingest and is
9441
+ // not in record.fields, so check for it at the UIAPI root property location
9442
+ id = record.recordTypeId;
9443
+ }
9444
+ else if (field.name.endsWith('__r')) {
9445
+ // Custom relationships end in `__r` and the corresponding ID field should be `__c`
9446
+ let fieldName = field.name.replace('__r', '__c');
9447
+ id = record.fields[fieldName] && record.fields[fieldName].value;
9448
+ }
9449
+ else {
9450
+ // Standard relationships are just FieldNameId
9451
+ let fieldName = field.name + 'Id';
9452
+ id = record.fields[fieldName] && record.fields[fieldName].value;
9453
+ }
9454
+ if (!id || typeof id !== 'string') {
9455
+ // possibly field injection did not inject the necessary Id field
9456
+ // for the relationship, or we found a non-string value.
9409
9457
  return null;
9410
- if (id['__ref'] !== undefined) {
9411
- return fetchRecordOrNull(record.fields[`${field.name}Id`].value);
9412
9458
  }
9413
9459
  seenRecordIds.add(id);
9414
9460
  return fetchRecordOrNull(id);
@@ -9560,18 +9606,7 @@ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
9560
9606
  const key = buildKeyStringForRecordQuery(operation,
9561
9607
  // join varables passed from query to the argument variables given from the AST
9562
9608
  { ...variableValues, ...args }, info.fieldNodes[0].arguments, apiName);
9563
- const sql = `
9564
- SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
9565
- FROM lds_data
9566
- WHERE key IS ?
9567
- `;
9568
- const results = await query(sql, [key]);
9569
- const [timestamp] = results.rows.map((row) => row[0]);
9570
- if (timestamp !== null && typeof timestamp === 'number') {
9571
- // adjust the timestamp to account for ingestion processing time
9572
- // 30s is used because this is the default record TTL
9573
- ingestionTimestamp = timestamp - 30000;
9574
- }
9609
+ return readIngestionTimestampForKey(key, query);
9575
9610
  }
9576
9611
  return ingestionTimestamp;
9577
9612
  }
@@ -11638,7 +11673,7 @@ function createUserJsonOutput(selection, jsonInput, jsonOutput) {
11638
11673
  function createjsonOutput(selections, jsonInput, jsonOutput) {
11639
11674
  const keys$1 = keys$4(jsonInput);
11640
11675
  selections.filter(isFieldNode).forEach((subSelection) => {
11641
- const fieldName = subSelection.name.value;
11676
+ const fieldName = subSelection.alias ? subSelection.alias.value : subSelection.name.value;
11642
11677
  if (keys$1.includes(fieldName)) {
11643
11678
  if (isArray$2(jsonInput[fieldName])) {
11644
11679
  jsonOutput[fieldName] = [];
@@ -12358,13 +12393,21 @@ function buildSyntheticRecordRepresentation(luvio, createOperation, userId, obje
12358
12393
  draftFields[DEFAULT_FIELD_LAST_MODIFIED_DATE] = { value: timestampString, displayValue: null };
12359
12394
  draftFields[DEFAULT_FIELD_OWNER_ID] = { value: userId, displayValue: null };
12360
12395
  draftFields[DEFAULT_FIELD_ID] = { value: recordId, displayValue: null };
12361
- if (objectInfo !== undefined) {
12362
- const allObjectFields = keys$3(objectInfo.fields);
12363
- allObjectFields.forEach((fieldName) => {
12364
- if (draftFields[fieldName] === undefined) {
12365
- draftFields[fieldName] = { value: null, displayValue: null };
12366
- }
12367
- });
12396
+ const allObjectFields = keys$3(objectInfo.fields);
12397
+ allObjectFields.forEach((fieldName) => {
12398
+ if (draftFields[fieldName] === undefined) {
12399
+ draftFields[fieldName] = { value: null, displayValue: null };
12400
+ }
12401
+ });
12402
+ // TODO [W-14915806]: lightning-record-form injects the `IsPersonAccount`
12403
+ // field for all `Account` and `PersonAccount` records. However, not all
12404
+ // orgs use person accounts, and if that field is not present in the object
12405
+ // info then it is not synthesized. We force it to be synthesized here to
12406
+ // ensure lightning-record-form will work correctly with draft-created
12407
+ // accounts.
12408
+ if ((apiName === 'Account' || apiName === 'PersonAccount') &&
12409
+ draftFields['IsPersonAccount'] === undefined) {
12410
+ draftFields['IsPersonAccount'] = { value: null, displayValue: null };
12368
12411
  }
12369
12412
  return {
12370
12413
  id: recordId,
@@ -12951,10 +12994,13 @@ function normalizeRecordFields(key, entry) {
12951
12994
  * Transforms a record for storage in the durable store. The transformation involves denormalizing
12952
12995
  * scalar fields and persisting link metadata to transform back into a normalized representation
12953
12996
  *
12997
+ * If the record contains pending fields this will return undefined as pending records do not get persisted
12998
+ * to the durable store. There should be a refresh operation outbound that will bring in the updated record.
12999
+ *
12954
13000
  * @param normalizedRecord Record containing normalized field links
12955
13001
  * @param recordStore a store containing referenced record fields
12956
13002
  */
12957
- function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntries) {
13003
+ function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntries, store) {
12958
13004
  const fields = normalizedRecord.fields;
12959
13005
  const filteredFields = {};
12960
13006
  const links = {};
@@ -12965,7 +13011,9 @@ function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntr
12965
13011
  // pending fields get filtered out of the durable store
12966
13012
  const { pending } = field;
12967
13013
  if (pending === true) {
12968
- continue;
13014
+ // do not write records with pending fields to the durable store
13015
+ // there should be a refresh operation outbound that will bring in the updated record
13016
+ return undefined;
12969
13017
  }
12970
13018
  const { __ref } = field;
12971
13019
  if (__ref !== undefined) {
@@ -12981,6 +13029,19 @@ function buildDurableRecordRepresentation(normalizedRecord, records, pendingEntr
12981
13029
  if (ref !== undefined) {
12982
13030
  filteredFields[fieldName] = ref;
12983
13031
  }
13032
+ else {
13033
+ // if we have a store to read, try to find the field there too
13034
+ // The durable ingest staging store may pass through to L1, and
13035
+ // not all fields are necessarily published every time, so it is
13036
+ // important to check L1 and not just the fields being published,
13037
+ // otherwise we risk truncating the fields on the record.
13038
+ if (store) {
13039
+ ref = store.readEntry(__ref);
13040
+ if (ref !== undefined) {
13041
+ filteredFields[fieldName] = ref;
13042
+ }
13043
+ }
13044
+ }
12984
13045
  }
12985
13046
  // we want to preserve fields that are missing nodes
12986
13047
  if (filteredFields[fieldName] !== undefined || field.isMissing === true) {
@@ -13002,7 +13063,7 @@ function getDenormalizedKey(originalKey, recordId, luvio) {
13002
13063
  }
13003
13064
  return keyBuilderRecord(luvio, { recordId });
13004
13065
  }
13005
- function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecords, getStoreMetadata) {
13066
+ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecords, getStoreMetadata, getStore) {
13006
13067
  const getEntries = function (entries, segment) {
13007
13068
  // this HOF only inspects records in the default segment
13008
13069
  if (segment !== DefaultDurableSegment) {
@@ -13070,6 +13131,7 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
13070
13131
  const putRecordViews = {};
13071
13132
  const storeRecords = getStoreRecords !== undefined ? getStoreRecords() : {};
13072
13133
  const storeMetadata = getStoreMetadata !== undefined ? getStoreMetadata() : {};
13134
+ const store = getStore();
13073
13135
  for (let i = 0, len = keys$1.length; i < len; i++) {
13074
13136
  const key = keys$1[i];
13075
13137
  let value = entries[key];
@@ -13116,11 +13178,13 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
13116
13178
  metadataVersion: DURABLE_METADATA_VERSION,
13117
13179
  };
13118
13180
  }
13119
- const denormalizedRecord = buildDurableRecordRepresentation(record, storeRecords, recordEntries);
13120
- putEntries[recordKey] = {
13121
- data: denormalizedRecord,
13122
- metadata,
13123
- };
13181
+ const denormalizedRecord = buildDurableRecordRepresentation(record, storeRecords, recordEntries, store);
13182
+ if (denormalizedRecord !== undefined) {
13183
+ putEntries[recordKey] = {
13184
+ data: denormalizedRecord,
13185
+ metadata,
13186
+ };
13187
+ }
13124
13188
  }
13125
13189
  else {
13126
13190
  putEntries[key] = value;
@@ -15948,9 +16012,9 @@ function instrumentAdapter(adapter, metadata) {
15948
16012
  }
15949
16013
  }
15950
16014
  return instrumentAdapter$1(instrumentedMobileAdapter, metadata, {
15951
- trackL1Hits: true,
15952
- trackL2Hits: true,
15953
- trackCacheMisses: true,
16015
+ trackL1Hits: false,
16016
+ trackL2Hits: false,
16017
+ trackCacheMisses: false,
15954
16018
  reportObserver: (report) => {
15955
16019
  for (const observer of reportObservers) {
15956
16020
  observer(report);
@@ -16757,6 +16821,7 @@ function findReferenceFieldForSpanningField(fieldName, objectInfo) {
16757
16821
  function buildFieldUnionArray(existingRecord, incomingRecord, objectInfo) {
16758
16822
  const allFields = Array.from(new Set([...Object.keys(existingRecord.fields), ...Object.keys(incomingRecord.fields)]));
16759
16823
  const fieldUnion = [];
16824
+ let includesSpanningFields = false;
16760
16825
  allFields.forEach((fieldName) => {
16761
16826
  const objectInfoField = objectInfo.fields[fieldName];
16762
16827
  if (objectInfoField === undefined) {
@@ -16764,13 +16829,14 @@ function buildFieldUnionArray(existingRecord, incomingRecord, objectInfo) {
16764
16829
  const referenceField = findReferenceFieldForSpanningField(fieldName, objectInfo);
16765
16830
  if (referenceField !== undefined) {
16766
16831
  fieldUnion.push(`${fieldName}.Id`);
16832
+ includesSpanningFields = true;
16767
16833
  }
16768
16834
  }
16769
16835
  else {
16770
16836
  fieldUnion.push(fieldName);
16771
16837
  }
16772
16838
  });
16773
- return fieldUnion;
16839
+ return { fields: fieldUnion, includesSpanningFields };
16774
16840
  }
16775
16841
  /**
16776
16842
  * Merges (if possible) an incoming record from a priming session with an existing record in the durable store.
@@ -16802,7 +16868,7 @@ function mergeRecord(existingRecord, incomingRecord, objectInfo) {
16802
16868
  ok: false,
16803
16869
  code: 'conflict-drafts',
16804
16870
  hasDraft: true,
16805
- fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
16871
+ fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo).fields,
16806
16872
  };
16807
16873
  }
16808
16874
  // Check if incoming record's Etag is equal to the existing one
@@ -16864,10 +16930,19 @@ function mergeRecord(existingRecord, incomingRecord, objectInfo) {
16864
16930
  };
16865
16931
  }
16866
16932
  // If Etags do not match and the incoming record does not contain all fields, re-request the record
16933
+ const { fields, includesSpanningFields } = buildFieldUnionArray(existingRecord, incomingRecord, objectInfo);
16934
+ if (includesSpanningFields) {
16935
+ return {
16936
+ ok: false,
16937
+ code: 'conflict-spanning-record',
16938
+ fieldUnion: fields,
16939
+ hasDraft: false,
16940
+ };
16941
+ }
16867
16942
  return {
16868
16943
  ok: false,
16869
16944
  code: 'conflict-missing-fields',
16870
- fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
16945
+ fieldUnion: fields,
16871
16946
  hasDraft: false,
16872
16947
  };
16873
16948
  }
@@ -17793,30 +17868,36 @@ function getRuntime() {
17793
17868
  const internalAdapterStore = new InMemoryStore();
17794
17869
  let getIngestRecordsForInternalAdapters;
17795
17870
  let getIngestMetadataForInternalAdapters;
17871
+ let getIngestStoreInternal;
17796
17872
  const internalAdapterDurableStore = makeRecordDenormalizingDurableStore(lazyLuvio, lazyBaseDurableStore, () => getIngestRecordsForInternalAdapters !== undefined
17797
17873
  ? getIngestRecordsForInternalAdapters()
17798
17874
  : {}, () => getIngestMetadataForInternalAdapters !== undefined
17799
17875
  ? getIngestMetadataForInternalAdapters()
17800
- : {});
17876
+ : {}, () => (getIngestStoreInternal !== undefined ? getIngestStoreInternal() : undefined));
17801
17877
  const { adapters: { getObjectInfo, getObjectInfos, getRecord, getObjectInfoDirectory }, durableEnvironment: internalAdapterDurableEnvironment, luvio: internalLuvio, } = buildInternalAdapters(internalAdapterStore, lazyNetworkAdapter, internalAdapterDurableStore, (apiName, objectInfo) => lazyObjectInfoService.ensureObjectInfoCached(apiName, objectInfo));
17802
17878
  lazyInternalLuvio = internalLuvio;
17803
17879
  getIngestRecordsForInternalAdapters =
17804
17880
  internalAdapterDurableEnvironment.getIngestStagingStoreRecords;
17805
17881
  getIngestMetadataForInternalAdapters =
17806
17882
  internalAdapterDurableEnvironment.getIngestStagingStoreRecords;
17883
+ getIngestStoreInternal = internalAdapterDurableEnvironment.getIngestStagingStore;
17807
17884
  lazyObjectInfoService = new ObjectInfoService(getObjectInfo, getObjectInfos, getObjectInfoDirectory, lazyBaseDurableStore);
17808
17885
  // creates a durable store that denormalizes scalar fields for records
17809
17886
  let getIngestRecords;
17810
17887
  let getIngestMetadata;
17811
- const recordDenormingStore = makeRecordDenormalizingDurableStore(lazyLuvio, lazyBaseDurableStore, () => (getIngestRecords !== undefined ? getIngestRecords() : {}), () => (getIngestMetadata !== undefined ? getIngestMetadata() : {}));
17888
+ let getIngestStore;
17889
+ const recordDenormingStore = makeRecordDenormalizingDurableStore(lazyLuvio, lazyBaseDurableStore, () => (getIngestRecords !== undefined ? getIngestRecords() : {}), () => (getIngestMetadata !== undefined ? getIngestMetadata() : {}), () => (getIngestStore !== undefined ? getIngestStore() : undefined));
17812
17890
  const baseEnv = new Environment(store, lazyNetworkAdapter);
17813
17891
  const gqlEnv = makeEnvironmentGraphqlAware(baseEnv);
17814
17892
  const durableEnv = makeDurable(gqlEnv, {
17815
17893
  durableStore: recordDenormingStore,
17816
17894
  enableDurableMetadataRefresh: ldsMetadataRefreshEnabled.isOpen({ fallback: false }),
17895
+ // disable luvio deep freeze in headless environments
17896
+ disableDeepFreeze: typeof window === 'undefined',
17817
17897
  });
17818
17898
  getIngestRecords = durableEnv.getIngestStagingStoreRecords;
17819
17899
  getIngestMetadata = durableEnv.getIngestStagingStoreMetadata;
17900
+ getIngestStore = durableEnv.getIngestStagingStore;
17820
17901
  // draft queue
17821
17902
  lazyDraftQueue = buildLdsDraftQueue(recordDenormingStore);
17822
17903
  const draftService = new UiApiDraftRecordService(lazyDraftQueue, () => lazyLuvio, recordDenormingStore, getObjectInfo, newRecordId, userId, formatDisplayValue);
@@ -17916,4 +17997,4 @@ register({
17916
17997
  });
17917
17998
 
17918
17999
  export { O11Y_NAMESPACE_LDS_MOBILE, getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17919
- // version: 1.266.0-dev2-7c2f3615f
18000
+ // version: 1.266.0-dev20-117d849b4