@salesforce/lds-runtime-mobile 1.280.0 → 1.282.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.
Files changed (3) hide show
  1. package/dist/main.js +289 -166
  2. package/package.json +18 -18
  3. package/sfdc/main.js +289 -166
package/dist/main.js CHANGED
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import { withRegistration, register } from '@salesforce/lds-default-luvio';
19
19
  import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrumentLuvio, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation } from '@salesforce/lds-instrumentation';
20
- import { HttpStatusCode, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from '@luvio/engine';
20
+ import { HttpStatusCode, setBypassDeepFreeze, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from '@luvio/engine';
21
21
  import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
22
22
  import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from '@luvio/graphql-parser';
23
23
  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';
@@ -1019,7 +1019,9 @@ function isUnfulfilledSnapshot$1(cachedSnapshotResult) {
1019
1019
  * @param durableStore A DurableStore implementation
1020
1020
  * @param instrumentation An instrumentation function implementation
1021
1021
  */
1022
- function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, }) {
1022
+ function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, disableDeepFreeze = false, }) {
1023
+ // runtimes can choose to disable deepFreeze, e.g. headless mobile runtime
1024
+ setBypassDeepFreeze(disableDeepFreeze);
1023
1025
  let stagingStore = null;
1024
1026
  const durableTTLStore = new DurableTTLStore(durableStore);
1025
1027
  const mergeKeysPromiseMap = new Map();
@@ -7967,6 +7969,9 @@ function sanitizePredicateIDValue(value, draftFunction) {
7967
7969
  if (isArray$2(value)) {
7968
7970
  return value.map((singleValue) => sanitizePredicateIDValue(singleValue, draftFunction));
7969
7971
  }
7972
+ else if (typeof value === 'string' && value === '') {
7973
+ return value;
7974
+ }
7970
7975
  else {
7971
7976
  const coercedId = getRecordId18(value);
7972
7977
  if (coercedId !== undefined) {
@@ -8657,24 +8662,36 @@ function findAncesterPath(ancesters) {
8657
8662
  }
8658
8663
  return path;
8659
8664
  }
8665
+ function isCategoryDirective(node) {
8666
+ return node.name.value === 'category';
8667
+ }
8660
8668
  /**
8661
8669
  * checks if the 'SelectionNode' has the potential to have relationship(parent or child) with its ancestor.
8662
8670
  * @param node
8663
8671
  * @returns
8664
8672
  */
8665
8673
  function isCapableRelationship(node) {
8666
- if (!isFieldOrInlineFragmentNode(node))
8667
- return false;
8668
- if (!node.selectionSet)
8669
- return false;
8670
- return node.selectionSet.selections.some((selection) => {
8671
- if (selection.kind !== Kind.FIELD && selection.kind !== Kind.INLINE_FRAGMENT)
8672
- return false;
8673
- // example: Account { Id }
8674
- if (selection.kind === Kind.FIELD && selection.name.value === 'Id')
8675
- return true;
8676
- return selection.selectionSet !== undefined;
8677
- });
8674
+ if (isFieldNode(node)) {
8675
+ if (node.directives !== undefined) {
8676
+ const argNodes = node.directives
8677
+ .flatMap((directive) => {
8678
+ if (isCategoryDirective(directive) === false)
8679
+ return undefined;
8680
+ return directive.arguments;
8681
+ })
8682
+ .filter((x) => x);
8683
+ return argNodes.some((argument) => {
8684
+ if (argument.name.value !== 'name')
8685
+ return false;
8686
+ if (argument.value.kind !== 'StringValue')
8687
+ return false;
8688
+ return (argument.value.value === PARENT_RELATIONSHIP ||
8689
+ argument.value.value === CHILD_RELATIONSHIP ||
8690
+ argument.value.value === POLYMORPHIC_PARENT_RELATIONSHIP);
8691
+ });
8692
+ }
8693
+ }
8694
+ return isInlineFragmentNode(node);
8678
8695
  }
8679
8696
  /**
8680
8697
  * checks if the 'ArgumentNode' is a specific scope type
@@ -9412,6 +9429,12 @@ function addResolversToSchema(schema, polyFields) {
9412
9429
  break;
9413
9430
  case 'LastModifiedDate':
9414
9431
  field.resolve = ({ recordRepresentation: record }) => {
9432
+ // In UIAPI record reps, LastModifiedDate might be present as a field,
9433
+ // which will include both the value and displayValue
9434
+ if (record.fields['LastModifiedDate']) {
9435
+ return record.fields['LastModifiedDate'];
9436
+ }
9437
+ // If the field is not present, just return the value of the root property
9415
9438
  return record.lastModifiedDate
9416
9439
  ? { value: record.lastModifiedDate }
9417
9440
  : null;
@@ -9539,7 +9562,6 @@ _, { objectInfos }) {
9539
9562
  }
9540
9563
  }
9541
9564
  }
9542
- // TODO [W-14660068]: composite name fields
9543
9565
  return null;
9544
9566
  }
9545
9567
  async function connectionEdgeResolver(obj, _args, context) {
@@ -10603,6 +10625,9 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
10603
10625
  }
10604
10626
  });
10605
10627
  }
10628
+ function isNodeOrEdges(node) {
10629
+ return isFieldNode(node) && (node.name.value === 'node' || node.name.value === 'edges');
10630
+ }
10606
10631
  /**
10607
10632
  * Build a relationship tree from the top FieldNode 'node'
10608
10633
  * @param tree
@@ -10613,7 +10638,7 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
10613
10638
  * @param startNodes
10614
10639
  */
10615
10640
  function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNodes, startNodes) {
10616
- if (isCapableRelationship(entryNode)) {
10641
+ if (isCapableRelationship(entryNode) || isNodeOrEdges(entryNode)) {
10617
10642
  if (isFieldNode(entryNode)) {
10618
10643
  const relationType = getRelation(entryNode);
10619
10644
  const fieldName = entryNode.name.value;
@@ -11200,6 +11225,131 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
11200
11225
  }
11201
11226
  }
11202
11227
  }
11228
+ function findIdFieldNode(node) {
11229
+ if (node.selectionSet === undefined)
11230
+ return false;
11231
+ return node.selectionSet.selections.some((selection) => {
11232
+ if (isFieldNode(selection)) {
11233
+ return selection.name.value === 'Id';
11234
+ }
11235
+ return false;
11236
+ });
11237
+ }
11238
+ /**
11239
+ * 1 parentship can return 2 FieldNode which need to be flattened
11240
+ * Concact: { ** Contact { ** ContactId {
11241
+ * Name: { ** Id ** value
11242
+ * value ** } ** }
11243
+ * } **
11244
+ * }
11245
+ * `parentRelationships` is a 2-dimensional array. 1 element at the top level of the array is an array of injected fields(which could has 2 elements) for
11246
+ * one field(`Contact`, for example), For example, `Contact` field could map to field `Contact { Id }` and `ContactId { value }` which are injected.
11247
+ * `Account` field could map to field `Account { Id }` and `AccountID { value }`.
11248
+ * Returned `parentRelationships` would look like [ [`Contact { Id }`, `ContactId { value }`], [`Account { Id }`, `AccountId { value }`]]. It needs to
11249
+ * be flattened to [`Contact { Id }`, `ContactId { value }`, `Account { Id }`, `AccountId { value }`]
11250
+ */
11251
+ function injectParentRelationships(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
11252
+ let parentInjectedNodes = [];
11253
+ for (let i = 0, len = selections.length; i < len; i++) {
11254
+ const selection = selections[i];
11255
+ // have to wrap in the if statement to make rollup happy :(
11256
+ if (isFieldOrInlineFragmentNode(selection)) {
11257
+ if (selection.selectionSet === undefined) {
11258
+ parentInjectedNodes.push(selection);
11259
+ continue;
11260
+ }
11261
+ const segment = isFieldNode(selection)
11262
+ ? selection.name.value
11263
+ : selection.typeCondition.name.value;
11264
+ const curPath = `${parentPath}#${segment}`;
11265
+ const spanningSubSelections = [];
11266
+ for (const subSelection of selection.selectionSet.selections) {
11267
+ if (isFieldSpanning(subSelection, selection)) {
11268
+ spanningSubSelections.push(subSelection);
11269
+ }
11270
+ }
11271
+ // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
11272
+ const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
11273
+ const hasIdNode = findIdFieldNode(selection);
11274
+ // For InlineFragment like '... on User', do not create relationship. Relationship is handled at the upper level.
11275
+ if (isFieldNode(selection)) {
11276
+ const fieldName = selection.name.value;
11277
+ const relationshipId = referenceIdFieldForRelationship(fieldName);
11278
+ // check if relation field like 'AccountId' does exist in query
11279
+ const existingRelationFields = [parentNode]
11280
+ .filter(isFieldOrInlineFragmentNode)
11281
+ .reduce(extractSelections, [])
11282
+ .filter(isFieldNode)
11283
+ .filter((subNode) => subNode.name.value === relationshipId);
11284
+ if (existingRelationFields.length === 0) {
11285
+ parentInjectedNodes.push(createFieldNode(relationshipId, FieldValueNodeSelectionSet));
11286
+ }
11287
+ }
11288
+ // For polymorphic fields, the Id field is excluded.
11289
+ const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
11290
+ const idSelection = [];
11291
+ if (!excludeId && hasIdNode === false) {
11292
+ idSelection.push(createFieldNode('Id'));
11293
+ }
11294
+ // Inject '__typename' for InlineFragment. '__typename' field acts as a reference to concrete type of a polymorphic field or a standard field in the returned GQL response, which equals to
11295
+ // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
11296
+ if (isInlineFragmentNode(selection) &&
11297
+ !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
11298
+ idSelection.push(createFieldNode('__typename'));
11299
+ }
11300
+ // 'ServiceAppointment' --> 'Contact' --> 'Id', Inject 'Contact' with Id. 'Id' field is at the sub level.
11301
+ if (idSelection.length > 0 || subInjectedSelections.length > 0) {
11302
+ parentInjectedNodes.push({
11303
+ ...selection,
11304
+ selectionSet: {
11305
+ kind: Kind.SELECTION_SET,
11306
+ selections: [...idSelection, ...subInjectedSelections],
11307
+ },
11308
+ });
11309
+ }
11310
+ }
11311
+ }
11312
+ return parentInjectedNodes;
11313
+ }
11314
+ /**
11315
+ * Find any name fields needed for the DisplayValue field
11316
+ * @param param0
11317
+ * @param parentNode
11318
+ * @param objectInfos
11319
+ * @returns
11320
+ */
11321
+ function injectFieldsForDisplayValue(topNode, parentNode, objectInfos) {
11322
+ const { selectionSet } = topNode;
11323
+ if (selectionSet === undefined)
11324
+ return [];
11325
+ let displayValueNameFields = [];
11326
+ const { selections } = selectionSet;
11327
+ // see if node selection has DisplayValue and needs to inject
11328
+ let displayValue;
11329
+ for (let i = 0, len = selections.length; i < len; i++) {
11330
+ const node = selections[i];
11331
+ if (isFieldNode(node) && node.name.value === 'DisplayValue') {
11332
+ displayValue = node;
11333
+ break;
11334
+ }
11335
+ if (isInlineFragmentNode(node)) {
11336
+ const name = node.typeCondition !== undefined ? node.typeCondition.name : parentNode.name;
11337
+ displayValueNameFields = injectFieldsForDisplayValue(node, { ...parentNode, name }, objectInfos);
11338
+ }
11339
+ }
11340
+ if (displayValue !== undefined) {
11341
+ const apiName = parentNode.name.value;
11342
+ const objectInfo = objectInfos[apiName];
11343
+ if (objectInfo !== undefined &&
11344
+ objectInfo.nameFields !== undefined &&
11345
+ objectInfo.nameFields.length > 0) {
11346
+ displayValueNameFields = objectInfo.nameFields.map((fieldName) => {
11347
+ return createFieldNode(fieldName, FieldValueNodeSelectionSet);
11348
+ });
11349
+ }
11350
+ }
11351
+ return displayValueNameFields;
11352
+ }
11203
11353
  /**
11204
11354
  * injects fields under query
11205
11355
  * @param selections selection nodes need to be injected.
@@ -11210,160 +11360,67 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
11210
11360
  * @return injected SelectionNodes used to construct the InlineFragment.
11211
11361
  */
11212
11362
  function injectFields(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
11213
- /**
11214
- * 1 parentship can return 2 FieldNode which need to be flattened
11215
- * Concact: { ** Contact { ** ContactId {
11216
- * Name: { ** Id ** value
11217
- * value ** } ** }
11218
- * } **
11219
- * }
11220
- * `parentRelationships` is a 2-dimensional array. 1 element at the top level of the array is an array of injected fields(which could has 2 elements) for
11221
- * one field(`Contact`, for example), For example, `Contact` field could map to field `Contact { Id }` and `ContactId { value }` which are injected.
11222
- * `Account` field could map to field `Account { Id }` and `AccountID { value }`.
11223
- * Returned `parentRelationships` would look like [ [`Contact { Id }`, `ContactId { value }`], [`Account { Id }`, `AccountId { value }`]]. It needs to
11224
- * be flattened to [`Contact { Id }`, `ContactId { value }`, `Account { Id }`, `AccountId { value }`]
11225
- */
11226
- const parentRelaltionships = selections.filter(isFieldOrInlineFragmentNode).map((selection) => {
11227
- if (!selection.selectionSet) {
11228
- return selection;
11229
- }
11230
- const segment = isFieldNode(selection)
11231
- ? selection.name.value
11232
- : selection.typeCondition.name.value;
11233
- const curPath = `${parentPath}#${segment}`;
11234
- const spanningSubSelections = [];
11235
- for (const subSelection of selection.selectionSet.selections) {
11236
- if (isFieldSpanning(subSelection, selection)) {
11237
- spanningSubSelections.push(subSelection);
11238
- }
11239
- }
11240
- // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
11241
- const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
11242
- if (!selection.selectionSet) {
11243
- return selection;
11244
- }
11245
- const hasIdAlready = selection.selectionSet.selections
11246
- .filter(isFieldNode)
11247
- .find((s) => s.name.value === 'Id');
11248
- // For InlineFragment like '... on User', do not create relationship. Relationship is handled at the upper level.
11249
- let injectedRelationshipField = [];
11250
- if (isFieldNode(selection)) {
11251
- const fieldName = selection.name.value;
11252
- const relationshipId = referenceIdFieldForRelationship(fieldName);
11253
- // check if relation field like 'AccountId' does exist in query
11254
- const existingRelationFields = [parentNode]
11255
- .filter(isFieldOrInlineFragmentNode)
11256
- .reduce(extractSelections, [])
11257
- .filter(isFieldNode)
11258
- .filter((subNode) => subNode.name.value === relationshipId);
11259
- if (existingRelationFields.length === 0) {
11260
- injectedRelationshipField = [
11261
- createFieldNode(relationshipId, FieldValueNodeSelectionSet),
11262
- ];
11263
- }
11264
- }
11265
- // For polymorphic fields, the Id field is excluded.
11266
- const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
11267
- const idSelection = [];
11268
- if (!excludeId && !hasIdAlready) {
11269
- idSelection.push(createFieldNode('Id'));
11270
- }
11271
- // Inject '__typename' for InlineFragment. '__typename' field acts as a reference to concrete type of a polymorphic field or a standard field in the returned GQL response, which equals to
11272
- // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
11273
- if (isInlineFragmentNode(selection) &&
11274
- !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
11275
- idSelection.push(createFieldNode('__typename'));
11276
- }
11277
- // 'ServiceAppointment' --> 'Contact' --> 'Id', Inject 'Contact' with Id. 'Id' field is at the sub level.
11278
- const injectedSelectionIdField = idSelection.length > 0 || subInjectedSelections.length > 0
11279
- ? [
11280
- {
11281
- ...selection,
11282
- selectionSet: {
11283
- kind: Kind.SELECTION_SET,
11284
- selections: [...idSelection, ...subInjectedSelections],
11285
- },
11286
- },
11287
- ]
11288
- : [];
11289
- return [...injectedSelectionIdField, ...injectedRelationshipField];
11290
- });
11291
- // injects Id if the parent of the 'childRelationship' does not contain an Id field. The logic here only deals with 'parentNode'
11292
- const idForChildRelationship = [];
11363
+ const parentRelaltionships = injectParentRelationships(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap);
11293
11364
  // inject related field for the parent of the 'childRelationship'.
11294
11365
  const relatedIdForChildRelationship = [];
11295
11366
  // injected fields for DisplayValue
11296
11367
  let displayValueNameFields = [];
11368
+ let rootQueryIdField = [];
11297
11369
  // injects 'childRelatiship' at the 'node' level, it does not matter if the 'selections' is empty or not.
11298
11370
  // the operation happens at the same level as the 'childRelationship' field.
11299
- if (isFieldNode(parentNode) && parentNode.selectionSet && parentNode.name.value === 'node') {
11300
- if (parentNode.selectionSet.selections
11301
- .filter(isFieldOrInlineFragmentNode)
11302
- .some((selectionNode) => isRelationship(selectionNode, CHILD_RELATIONSHIP))) {
11303
- if (!parentNode.selectionSet.selections
11304
- .filter(isFieldNode)
11305
- .some((sibling) => sibling.name.value === 'Id')) {
11306
- idForChildRelationship.push(createFieldNode('Id'));
11307
- }
11308
- }
11309
- else {
11310
- // example { node { Id } }. The operation happens at the 'edges' -> 'node' level of the 'childRelationship' field
11311
- const parentInfo = findNearestAncesterPath(ancestors, true);
11312
- if (parentInfo.parentIndex >= 0) {
11313
- // example node { TimeSheetEntries { edges { node { Id }}}}
11314
- const parent = parentInfo.node;
11315
- if (isRelationship(parent, CHILD_RELATIONSHIP)) {
11316
- const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
11317
- // path : "TimeSheet"
11318
- const grandParentPath = findAncesterPath(unVisitedAncestors);
11319
- if (pathToObjectApiNamesMap &&
11320
- pathToObjectApiNamesMap[grandParentPath] &&
11321
- objectInfos &&
11322
- objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
11323
- const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
11324
- // exmaple "TimeSheetEntries"
11325
- const parentFieldName = parent.name.value;
11326
- const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
11327
- return childRelationship.relationshipName === parentFieldName;
11328
- });
11329
- if (targetRelationship !== undefined) {
11330
- const injectedParentFieldName = targetRelationship.fieldName;
11331
- if (!parentNode.selectionSet.selections
11332
- .filter(isFieldNode)
11333
- .some((sibling) => sibling.name.value === injectedParentFieldName)) {
11334
- // example: TimeSheetId { value }
11335
- relatedIdForChildRelationship.push(createFieldNode(injectedParentFieldName, FieldValueNodeSelectionSet));
11336
- }
11371
+ if (isFieldNode(parentNode) &&
11372
+ parentNode.selectionSet !== undefined &&
11373
+ (parentNode.name.value === 'node' || isCapableRelationship(parentNode))) {
11374
+ //check if the root query has the Id and inject if it needed
11375
+ if (parentNode.name.value === 'node') {
11376
+ const idNode = findIdFieldNode(parentNode);
11377
+ if (idNode === false) {
11378
+ rootQueryIdField.push(createFieldNode('Id'));
11379
+ }
11380
+ }
11381
+ // example { node { Id } }. The operation happens at the 'edges' -> 'node' level of the 'childRelationship' field
11382
+ const parentInfo = findNearestAncesterPath(ancestors, true);
11383
+ if (parentInfo.parentIndex >= 0) {
11384
+ // example node { TimeSheetEntries { edges { node { Id }}}}
11385
+ const parent = parentNode.name.value === 'node' ? parentInfo.node : parentNode;
11386
+ if (isRelationship(parent, CHILD_RELATIONSHIP)) {
11387
+ const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
11388
+ // path : "TimeSheet"
11389
+ const grandParentPath = findAncesterPath(unVisitedAncestors);
11390
+ if (pathToObjectApiNamesMap &&
11391
+ pathToObjectApiNamesMap[grandParentPath] &&
11392
+ objectInfos &&
11393
+ objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
11394
+ const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
11395
+ // exmaple "TimeSheetEntries"
11396
+ const parentFieldName = parent.name.value;
11397
+ const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
11398
+ return childRelationship.relationshipName === parentFieldName;
11399
+ });
11400
+ if (targetRelationship !== undefined) {
11401
+ const injectedParentFieldName = targetRelationship.fieldName;
11402
+ if (!parentNode.selectionSet.selections
11403
+ .filter(isFieldNode)
11404
+ .some((sibling) => sibling.name.value === injectedParentFieldName)) {
11405
+ // example: TimeSheetId { value }
11406
+ relatedIdForChildRelationship.push(createFieldNode(injectedParentFieldName, FieldValueNodeSelectionSet));
11337
11407
  }
11338
- }
11339
- }
11340
- const { selectionSet: { selections }, } = parentNode;
11341
- // see if node selection has DisplayValue and needs to inject
11342
- let displayValue;
11343
- for (let i = 0, len = selections.length; i < len; i++) {
11344
- const node = selections[i];
11345
- if (isFieldNode(node) && node.name.value === 'DisplayValue') {
11346
- displayValue = node;
11347
- break;
11348
- }
11349
- }
11350
- if (displayValue !== undefined) {
11351
- const apiName = parent.name.value;
11352
- const objectInfo = objectInfos[apiName];
11353
- if (objectInfo !== undefined &&
11354
- objectInfo.nameFields !== undefined &&
11355
- objectInfo.nameFields.length > 0) {
11356
- displayValueNameFields = objectInfo.nameFields.map((fieldName) => {
11357
- return createFieldNode(fieldName, FieldValueNodeSelectionSet);
11358
- });
11408
+ displayValueNameFields.push(...injectFieldsForDisplayValue(parentNode, {
11409
+ ...parent,
11410
+ name: {
11411
+ ...parent.name,
11412
+ value: targetRelationship.childObjectApiName,
11413
+ },
11414
+ }, objectInfos));
11359
11415
  }
11360
11416
  }
11361
11417
  }
11418
+ displayValueNameFields.push(...injectFieldsForDisplayValue(parentNode, parent, objectInfos));
11362
11419
  }
11363
11420
  }
11364
11421
  return [
11422
+ ...rootQueryIdField,
11365
11423
  ...flat(parentRelaltionships),
11366
- ...idForChildRelationship,
11367
11424
  ...relatedIdForChildRelationship,
11368
11425
  ...displayValueNameFields,
11369
11426
  ];
@@ -12135,6 +12192,60 @@ function replayDraftsOnRecord(record, draftMetadata) {
12135
12192
  }
12136
12193
  return recursivelyApplyDraftsToRecord(baseRecord, draftMetadata, draftMetadata.recordOperations);
12137
12194
  }
12195
+ /**
12196
+ * checks if updated fields are apart of the compound Name field
12197
+ * will generate a compound Name field if it is not already apart of the draft
12198
+ * @param record
12199
+ * @param draftFields
12200
+ * @param apiName
12201
+ * @param objectInfos
12202
+ * @returns
12203
+ */
12204
+ function compoundNameFieldFromDraftUpdates(record, draftFields, apiName, objectInfos) {
12205
+ let updatedNameFields = {};
12206
+ const objectInfo = objectInfos.get(apiName);
12207
+ if (draftFields['Name'] !== undefined ||
12208
+ objectInfo === undefined ||
12209
+ objectInfo.nameFields === undefined ||
12210
+ objectInfo.nameFields.length === 0) {
12211
+ return updatedNameFields;
12212
+ }
12213
+ const nameField = objectInfo.nameFields.find((x) => x === 'Name');
12214
+ if (nameField === undefined) {
12215
+ return updatedNameFields;
12216
+ }
12217
+ const { compound, updateable } = objectInfo.fields[nameField];
12218
+ if (objectInfo.nameFields.length <= 1 && compound === false && updateable === true) {
12219
+ return updatedNameFields;
12220
+ }
12221
+ let changedNameFields = {};
12222
+ const filteredNameFields = objectInfo.nameFields.filter((x) => x !== 'Name');
12223
+ for (let i = 0, len = filteredNameFields.length; i < len; i++) {
12224
+ const fieldName = filteredNameFields[i];
12225
+ const fieldValue = draftFields[fieldName];
12226
+ if (fieldValue !== undefined) {
12227
+ changedNameFields[fieldName] = fieldValue;
12228
+ }
12229
+ }
12230
+ if (keys$3(changedNameFields).length > 0) {
12231
+ const newNameValue = filteredNameFields
12232
+ .map((key) => {
12233
+ if (changedNameFields[key] !== undefined) {
12234
+ return changedNameFields[key];
12235
+ }
12236
+ else if (record.fields[key] !== undefined) {
12237
+ return record.fields[key].value;
12238
+ }
12239
+ else {
12240
+ return '';
12241
+ }
12242
+ })
12243
+ .join(' ')
12244
+ .trim();
12245
+ updatedNameFields['Name'] = newNameValue;
12246
+ }
12247
+ return updatedNameFields;
12248
+ }
12138
12249
  function recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations) {
12139
12250
  const { luvio, objectInfos, userId, formatDisplayValue, referencedRecords } = draftMetadata;
12140
12251
  if (recordOperations.length === 0) {
@@ -12194,8 +12305,15 @@ function recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations)
12194
12305
  record.drafts.deleted = true;
12195
12306
  }
12196
12307
  // 'lastModifiedById' and 'lastModifiedDate' are modified in 'delete' and 'update' action
12197
- const fields = draftActionType === 'delete' ? {} : draftOperation.fields;
12308
+ const draftOperationFields = draftActionType === 'delete' ? {} : draftOperation.fields;
12198
12309
  const apiName = draftOperation.apiName;
12310
+ const injectedCompoundNameField = compoundNameFieldFromDraftUpdates(record, draftOperationFields, apiName, objectInfos);
12311
+ // inject the compound Name field into the DraftOperation so on broadcast it doesnt try to
12312
+ // synthesize the Name field a second time when the durable store change notifier is triggered
12313
+ if (keys$3(injectedCompoundNameField).length > 0 && draftActionType === 'update') {
12314
+ draftOperation.fields['Name'] = injectedCompoundNameField['Name'];
12315
+ }
12316
+ const fields = { ...draftOperationFields, ...injectedCompoundNameField };
12199
12317
  // automaticlly add 'lastModifiedById' and 'lastModifiedByDate' and use an internal draft field copy since it is mutable
12200
12318
  const internalFields = {
12201
12319
  ...fields,
@@ -15996,9 +16114,9 @@ function instrumentAdapter(adapter, metadata) {
15996
16114
  }
15997
16115
  }
15998
16116
  return instrumentAdapter$1(instrumentedMobileAdapter, metadata, {
15999
- trackL1Hits: true,
16000
- trackL2Hits: true,
16001
- trackCacheMisses: true,
16117
+ trackL1Hits: false,
16118
+ trackL2Hits: false,
16119
+ trackCacheMisses: false,
16002
16120
  reportObserver: (report) => {
16003
16121
  for (const observer of reportObservers) {
16004
16122
  observer(report);
@@ -16887,13 +17005,13 @@ function mergeRecord(existingRecord, incomingRecord, objectInfo) {
16887
17005
  // since none of the changed fields are part of the incoming record
16888
17006
  if (missingFields.every((field) => {
16889
17007
  const referenceFieldName = findReferenceFieldForSpanningField(field, objectInfo);
16890
- if (referenceFieldName !== undefined) {
16891
- return (incomingRecord.fields[referenceFieldName].value ===
16892
- existingRecord.fields[referenceFieldName].value);
16893
- }
16894
- else {
17008
+ if (referenceFieldName === undefined) {
16895
17009
  return false;
16896
17010
  }
17011
+ return (existingRecord.fields[referenceFieldName] &&
17012
+ incomingRecord.fields[referenceFieldName] &&
17013
+ incomingRecord.fields[referenceFieldName].value ===
17014
+ existingRecord.fields[referenceFieldName].value);
16897
17015
  })) {
16898
17016
  return {
16899
17017
  ok: true,
@@ -17772,6 +17890,9 @@ class LdsPrimingRecordRefresher {
17772
17890
  optionalFields: value.fields.map((f) => `${_apiName}.${f}`),
17773
17891
  },
17774
17892
  ],
17893
+ }, {
17894
+ cachePolicy: { type: 'no-cache' },
17895
+ priority: 'background',
17775
17896
  }));
17776
17897
  });
17777
17898
  const promiseResults = await allSettled(promises);
@@ -17876,6 +17997,8 @@ function getRuntime() {
17876
17997
  const durableEnv = makeDurable(gqlEnv, {
17877
17998
  durableStore: recordDenormingStore,
17878
17999
  enableDurableMetadataRefresh: ldsMetadataRefreshEnabled.isOpen({ fallback: false }),
18000
+ // disable luvio deep freeze in headless environments
18001
+ disableDeepFreeze: typeof window === 'undefined',
17879
18002
  });
17880
18003
  getIngestRecords = durableEnv.getIngestStagingStoreRecords;
17881
18004
  getIngestMetadata = durableEnv.getIngestStagingStoreMetadata;
@@ -17981,4 +18104,4 @@ register({
17981
18104
  });
17982
18105
 
17983
18106
  export { O11Y_NAMESPACE_LDS_MOBILE, getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17984
- // version: 1.280.0-92c104b03
18107
+ // version: 1.282.0-f3e0ca0c7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.280.0",
3
+ "version": "1.282.0",
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.280.0",
36
- "@salesforce/lds-bindings": "^1.280.0",
37
- "@salesforce/lds-instrumentation": "^1.280.0",
38
- "@salesforce/lds-priming": "^1.280.0",
35
+ "@salesforce/lds-adapters-uiapi": "^1.282.0",
36
+ "@salesforce/lds-bindings": "^1.282.0",
37
+ "@salesforce/lds-instrumentation": "^1.282.0",
38
+ "@salesforce/lds-priming": "^1.282.0",
39
39
  "@salesforce/user": "0.0.21",
40
40
  "o11y": "244.0.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@salesforce/lds-adapters-graphql": "^1.280.0",
44
- "@salesforce/lds-drafts": "^1.280.0",
45
- "@salesforce/lds-drafts-adapters-uiapi": "^1.280.0",
46
- "@salesforce/lds-graphql-eval": "^1.280.0",
47
- "@salesforce/lds-network-adapter": "^1.280.0",
48
- "@salesforce/lds-network-nimbus": "^1.280.0",
49
- "@salesforce/lds-store-binary": "^1.280.0",
50
- "@salesforce/lds-store-nimbus": "^1.280.0",
51
- "@salesforce/lds-store-sql": "^1.280.0",
52
- "@salesforce/lds-utils-adapters": "^1.280.0",
53
- "@salesforce/nimbus-plugin-lds": "^1.280.0",
43
+ "@salesforce/lds-adapters-graphql": "^1.282.0",
44
+ "@salesforce/lds-drafts": "^1.282.0",
45
+ "@salesforce/lds-drafts-adapters-uiapi": "^1.282.0",
46
+ "@salesforce/lds-graphql-eval": "^1.282.0",
47
+ "@salesforce/lds-network-adapter": "^1.282.0",
48
+ "@salesforce/lds-network-nimbus": "^1.282.0",
49
+ "@salesforce/lds-store-binary": "^1.282.0",
50
+ "@salesforce/lds-store-nimbus": "^1.282.0",
51
+ "@salesforce/lds-store-sql": "^1.282.0",
52
+ "@salesforce/lds-utils-adapters": "^1.282.0",
53
+ "@salesforce/nimbus-plugin-lds": "^1.282.0",
54
54
  "babel-plugin-dynamic-import-node": "^2.3.3",
55
55
  "wait-for-expect": "^3.0.2"
56
56
  },
@@ -59,7 +59,7 @@
59
59
  "path": "./dist/main.js",
60
60
  "maxSize": {
61
61
  "none": "800 kB",
62
- "min": "320 kB",
62
+ "min": "321 kB",
63
63
  "compressed": "150 kB"
64
64
  }
65
65
  },
@@ -67,7 +67,7 @@
67
67
  "path": "./sfdc/main.js",
68
68
  "maxSize": {
69
69
  "none": "800 kB",
70
- "min": "320 kB",
70
+ "min": "321 kB",
71
71
  "compressed": "150 kB"
72
72
  }
73
73
  }
package/sfdc/main.js CHANGED
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import { withRegistration, register } from 'native/ldsEngineMobile';
19
19
  import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrumentLuvio, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation } from 'force/ldsInstrumentation';
20
- import { HttpStatusCode, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from 'force/luvioEngine';
20
+ import { HttpStatusCode, setBypassDeepFreeze, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from 'force/luvioEngine';
21
21
  import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
22
22
  import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from 'force/ldsGraphqlParser';
23
23
  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';
@@ -1019,7 +1019,9 @@ function isUnfulfilledSnapshot$1(cachedSnapshotResult) {
1019
1019
  * @param durableStore A DurableStore implementation
1020
1020
  * @param instrumentation An instrumentation function implementation
1021
1021
  */
1022
- function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, }) {
1022
+ function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, disableDeepFreeze = false, }) {
1023
+ // runtimes can choose to disable deepFreeze, e.g. headless mobile runtime
1024
+ setBypassDeepFreeze(disableDeepFreeze);
1023
1025
  let stagingStore = null;
1024
1026
  const durableTTLStore = new DurableTTLStore(durableStore);
1025
1027
  const mergeKeysPromiseMap = new Map();
@@ -7967,6 +7969,9 @@ function sanitizePredicateIDValue(value, draftFunction) {
7967
7969
  if (isArray$2(value)) {
7968
7970
  return value.map((singleValue) => sanitizePredicateIDValue(singleValue, draftFunction));
7969
7971
  }
7972
+ else if (typeof value === 'string' && value === '') {
7973
+ return value;
7974
+ }
7970
7975
  else {
7971
7976
  const coercedId = getRecordId18(value);
7972
7977
  if (coercedId !== undefined) {
@@ -8657,24 +8662,36 @@ function findAncesterPath(ancesters) {
8657
8662
  }
8658
8663
  return path;
8659
8664
  }
8665
+ function isCategoryDirective(node) {
8666
+ return node.name.value === 'category';
8667
+ }
8660
8668
  /**
8661
8669
  * checks if the 'SelectionNode' has the potential to have relationship(parent or child) with its ancestor.
8662
8670
  * @param node
8663
8671
  * @returns
8664
8672
  */
8665
8673
  function isCapableRelationship(node) {
8666
- if (!isFieldOrInlineFragmentNode(node))
8667
- return false;
8668
- if (!node.selectionSet)
8669
- return false;
8670
- return node.selectionSet.selections.some((selection) => {
8671
- if (selection.kind !== Kind.FIELD && selection.kind !== Kind.INLINE_FRAGMENT)
8672
- return false;
8673
- // example: Account { Id }
8674
- if (selection.kind === Kind.FIELD && selection.name.value === 'Id')
8675
- return true;
8676
- return selection.selectionSet !== undefined;
8677
- });
8674
+ if (isFieldNode(node)) {
8675
+ if (node.directives !== undefined) {
8676
+ const argNodes = node.directives
8677
+ .flatMap((directive) => {
8678
+ if (isCategoryDirective(directive) === false)
8679
+ return undefined;
8680
+ return directive.arguments;
8681
+ })
8682
+ .filter((x) => x);
8683
+ return argNodes.some((argument) => {
8684
+ if (argument.name.value !== 'name')
8685
+ return false;
8686
+ if (argument.value.kind !== 'StringValue')
8687
+ return false;
8688
+ return (argument.value.value === PARENT_RELATIONSHIP ||
8689
+ argument.value.value === CHILD_RELATIONSHIP ||
8690
+ argument.value.value === POLYMORPHIC_PARENT_RELATIONSHIP);
8691
+ });
8692
+ }
8693
+ }
8694
+ return isInlineFragmentNode(node);
8678
8695
  }
8679
8696
  /**
8680
8697
  * checks if the 'ArgumentNode' is a specific scope type
@@ -9412,6 +9429,12 @@ function addResolversToSchema(schema, polyFields) {
9412
9429
  break;
9413
9430
  case 'LastModifiedDate':
9414
9431
  field.resolve = ({ recordRepresentation: record }) => {
9432
+ // In UIAPI record reps, LastModifiedDate might be present as a field,
9433
+ // which will include both the value and displayValue
9434
+ if (record.fields['LastModifiedDate']) {
9435
+ return record.fields['LastModifiedDate'];
9436
+ }
9437
+ // If the field is not present, just return the value of the root property
9415
9438
  return record.lastModifiedDate
9416
9439
  ? { value: record.lastModifiedDate }
9417
9440
  : null;
@@ -9539,7 +9562,6 @@ _, { objectInfos }) {
9539
9562
  }
9540
9563
  }
9541
9564
  }
9542
- // TODO [W-14660068]: composite name fields
9543
9565
  return null;
9544
9566
  }
9545
9567
  async function connectionEdgeResolver(obj, _args, context) {
@@ -10603,6 +10625,9 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
10603
10625
  }
10604
10626
  });
10605
10627
  }
10628
+ function isNodeOrEdges(node) {
10629
+ return isFieldNode(node) && (node.name.value === 'node' || node.name.value === 'edges');
10630
+ }
10606
10631
  /**
10607
10632
  * Build a relationship tree from the top FieldNode 'node'
10608
10633
  * @param tree
@@ -10613,7 +10638,7 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
10613
10638
  * @param startNodes
10614
10639
  */
10615
10640
  function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNodes, startNodes) {
10616
- if (isCapableRelationship(entryNode)) {
10641
+ if (isCapableRelationship(entryNode) || isNodeOrEdges(entryNode)) {
10617
10642
  if (isFieldNode(entryNode)) {
10618
10643
  const relationType = getRelation(entryNode);
10619
10644
  const fieldName = entryNode.name.value;
@@ -11200,6 +11225,131 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
11200
11225
  }
11201
11226
  }
11202
11227
  }
11228
+ function findIdFieldNode(node) {
11229
+ if (node.selectionSet === undefined)
11230
+ return false;
11231
+ return node.selectionSet.selections.some((selection) => {
11232
+ if (isFieldNode(selection)) {
11233
+ return selection.name.value === 'Id';
11234
+ }
11235
+ return false;
11236
+ });
11237
+ }
11238
+ /**
11239
+ * 1 parentship can return 2 FieldNode which need to be flattened
11240
+ * Concact: { ** Contact { ** ContactId {
11241
+ * Name: { ** Id ** value
11242
+ * value ** } ** }
11243
+ * } **
11244
+ * }
11245
+ * `parentRelationships` is a 2-dimensional array. 1 element at the top level of the array is an array of injected fields(which could has 2 elements) for
11246
+ * one field(`Contact`, for example), For example, `Contact` field could map to field `Contact { Id }` and `ContactId { value }` which are injected.
11247
+ * `Account` field could map to field `Account { Id }` and `AccountID { value }`.
11248
+ * Returned `parentRelationships` would look like [ [`Contact { Id }`, `ContactId { value }`], [`Account { Id }`, `AccountId { value }`]]. It needs to
11249
+ * be flattened to [`Contact { Id }`, `ContactId { value }`, `Account { Id }`, `AccountId { value }`]
11250
+ */
11251
+ function injectParentRelationships(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
11252
+ let parentInjectedNodes = [];
11253
+ for (let i = 0, len = selections.length; i < len; i++) {
11254
+ const selection = selections[i];
11255
+ // have to wrap in the if statement to make rollup happy :(
11256
+ if (isFieldOrInlineFragmentNode(selection)) {
11257
+ if (selection.selectionSet === undefined) {
11258
+ parentInjectedNodes.push(selection);
11259
+ continue;
11260
+ }
11261
+ const segment = isFieldNode(selection)
11262
+ ? selection.name.value
11263
+ : selection.typeCondition.name.value;
11264
+ const curPath = `${parentPath}#${segment}`;
11265
+ const spanningSubSelections = [];
11266
+ for (const subSelection of selection.selectionSet.selections) {
11267
+ if (isFieldSpanning(subSelection, selection)) {
11268
+ spanningSubSelections.push(subSelection);
11269
+ }
11270
+ }
11271
+ // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
11272
+ const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
11273
+ const hasIdNode = findIdFieldNode(selection);
11274
+ // For InlineFragment like '... on User', do not create relationship. Relationship is handled at the upper level.
11275
+ if (isFieldNode(selection)) {
11276
+ const fieldName = selection.name.value;
11277
+ const relationshipId = referenceIdFieldForRelationship(fieldName);
11278
+ // check if relation field like 'AccountId' does exist in query
11279
+ const existingRelationFields = [parentNode]
11280
+ .filter(isFieldOrInlineFragmentNode)
11281
+ .reduce(extractSelections, [])
11282
+ .filter(isFieldNode)
11283
+ .filter((subNode) => subNode.name.value === relationshipId);
11284
+ if (existingRelationFields.length === 0) {
11285
+ parentInjectedNodes.push(createFieldNode(relationshipId, FieldValueNodeSelectionSet));
11286
+ }
11287
+ }
11288
+ // For polymorphic fields, the Id field is excluded.
11289
+ const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
11290
+ const idSelection = [];
11291
+ if (!excludeId && hasIdNode === false) {
11292
+ idSelection.push(createFieldNode('Id'));
11293
+ }
11294
+ // Inject '__typename' for InlineFragment. '__typename' field acts as a reference to concrete type of a polymorphic field or a standard field in the returned GQL response, which equals to
11295
+ // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
11296
+ if (isInlineFragmentNode(selection) &&
11297
+ !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
11298
+ idSelection.push(createFieldNode('__typename'));
11299
+ }
11300
+ // 'ServiceAppointment' --> 'Contact' --> 'Id', Inject 'Contact' with Id. 'Id' field is at the sub level.
11301
+ if (idSelection.length > 0 || subInjectedSelections.length > 0) {
11302
+ parentInjectedNodes.push({
11303
+ ...selection,
11304
+ selectionSet: {
11305
+ kind: Kind.SELECTION_SET,
11306
+ selections: [...idSelection, ...subInjectedSelections],
11307
+ },
11308
+ });
11309
+ }
11310
+ }
11311
+ }
11312
+ return parentInjectedNodes;
11313
+ }
11314
+ /**
11315
+ * Find any name fields needed for the DisplayValue field
11316
+ * @param param0
11317
+ * @param parentNode
11318
+ * @param objectInfos
11319
+ * @returns
11320
+ */
11321
+ function injectFieldsForDisplayValue(topNode, parentNode, objectInfos) {
11322
+ const { selectionSet } = topNode;
11323
+ if (selectionSet === undefined)
11324
+ return [];
11325
+ let displayValueNameFields = [];
11326
+ const { selections } = selectionSet;
11327
+ // see if node selection has DisplayValue and needs to inject
11328
+ let displayValue;
11329
+ for (let i = 0, len = selections.length; i < len; i++) {
11330
+ const node = selections[i];
11331
+ if (isFieldNode(node) && node.name.value === 'DisplayValue') {
11332
+ displayValue = node;
11333
+ break;
11334
+ }
11335
+ if (isInlineFragmentNode(node)) {
11336
+ const name = node.typeCondition !== undefined ? node.typeCondition.name : parentNode.name;
11337
+ displayValueNameFields = injectFieldsForDisplayValue(node, { ...parentNode, name }, objectInfos);
11338
+ }
11339
+ }
11340
+ if (displayValue !== undefined) {
11341
+ const apiName = parentNode.name.value;
11342
+ const objectInfo = objectInfos[apiName];
11343
+ if (objectInfo !== undefined &&
11344
+ objectInfo.nameFields !== undefined &&
11345
+ objectInfo.nameFields.length > 0) {
11346
+ displayValueNameFields = objectInfo.nameFields.map((fieldName) => {
11347
+ return createFieldNode(fieldName, FieldValueNodeSelectionSet);
11348
+ });
11349
+ }
11350
+ }
11351
+ return displayValueNameFields;
11352
+ }
11203
11353
  /**
11204
11354
  * injects fields under query
11205
11355
  * @param selections selection nodes need to be injected.
@@ -11210,160 +11360,67 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
11210
11360
  * @return injected SelectionNodes used to construct the InlineFragment.
11211
11361
  */
11212
11362
  function injectFields(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
11213
- /**
11214
- * 1 parentship can return 2 FieldNode which need to be flattened
11215
- * Concact: { ** Contact { ** ContactId {
11216
- * Name: { ** Id ** value
11217
- * value ** } ** }
11218
- * } **
11219
- * }
11220
- * `parentRelationships` is a 2-dimensional array. 1 element at the top level of the array is an array of injected fields(which could has 2 elements) for
11221
- * one field(`Contact`, for example), For example, `Contact` field could map to field `Contact { Id }` and `ContactId { value }` which are injected.
11222
- * `Account` field could map to field `Account { Id }` and `AccountID { value }`.
11223
- * Returned `parentRelationships` would look like [ [`Contact { Id }`, `ContactId { value }`], [`Account { Id }`, `AccountId { value }`]]. It needs to
11224
- * be flattened to [`Contact { Id }`, `ContactId { value }`, `Account { Id }`, `AccountId { value }`]
11225
- */
11226
- const parentRelaltionships = selections.filter(isFieldOrInlineFragmentNode).map((selection) => {
11227
- if (!selection.selectionSet) {
11228
- return selection;
11229
- }
11230
- const segment = isFieldNode(selection)
11231
- ? selection.name.value
11232
- : selection.typeCondition.name.value;
11233
- const curPath = `${parentPath}#${segment}`;
11234
- const spanningSubSelections = [];
11235
- for (const subSelection of selection.selectionSet.selections) {
11236
- if (isFieldSpanning(subSelection, selection)) {
11237
- spanningSubSelections.push(subSelection);
11238
- }
11239
- }
11240
- // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
11241
- const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
11242
- if (!selection.selectionSet) {
11243
- return selection;
11244
- }
11245
- const hasIdAlready = selection.selectionSet.selections
11246
- .filter(isFieldNode)
11247
- .find((s) => s.name.value === 'Id');
11248
- // For InlineFragment like '... on User', do not create relationship. Relationship is handled at the upper level.
11249
- let injectedRelationshipField = [];
11250
- if (isFieldNode(selection)) {
11251
- const fieldName = selection.name.value;
11252
- const relationshipId = referenceIdFieldForRelationship(fieldName);
11253
- // check if relation field like 'AccountId' does exist in query
11254
- const existingRelationFields = [parentNode]
11255
- .filter(isFieldOrInlineFragmentNode)
11256
- .reduce(extractSelections, [])
11257
- .filter(isFieldNode)
11258
- .filter((subNode) => subNode.name.value === relationshipId);
11259
- if (existingRelationFields.length === 0) {
11260
- injectedRelationshipField = [
11261
- createFieldNode(relationshipId, FieldValueNodeSelectionSet),
11262
- ];
11263
- }
11264
- }
11265
- // For polymorphic fields, the Id field is excluded.
11266
- const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
11267
- const idSelection = [];
11268
- if (!excludeId && !hasIdAlready) {
11269
- idSelection.push(createFieldNode('Id'));
11270
- }
11271
- // Inject '__typename' for InlineFragment. '__typename' field acts as a reference to concrete type of a polymorphic field or a standard field in the returned GQL response, which equals to
11272
- // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
11273
- if (isInlineFragmentNode(selection) &&
11274
- !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
11275
- idSelection.push(createFieldNode('__typename'));
11276
- }
11277
- // 'ServiceAppointment' --> 'Contact' --> 'Id', Inject 'Contact' with Id. 'Id' field is at the sub level.
11278
- const injectedSelectionIdField = idSelection.length > 0 || subInjectedSelections.length > 0
11279
- ? [
11280
- {
11281
- ...selection,
11282
- selectionSet: {
11283
- kind: Kind.SELECTION_SET,
11284
- selections: [...idSelection, ...subInjectedSelections],
11285
- },
11286
- },
11287
- ]
11288
- : [];
11289
- return [...injectedSelectionIdField, ...injectedRelationshipField];
11290
- });
11291
- // injects Id if the parent of the 'childRelationship' does not contain an Id field. The logic here only deals with 'parentNode'
11292
- const idForChildRelationship = [];
11363
+ const parentRelaltionships = injectParentRelationships(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap);
11293
11364
  // inject related field for the parent of the 'childRelationship'.
11294
11365
  const relatedIdForChildRelationship = [];
11295
11366
  // injected fields for DisplayValue
11296
11367
  let displayValueNameFields = [];
11368
+ let rootQueryIdField = [];
11297
11369
  // injects 'childRelatiship' at the 'node' level, it does not matter if the 'selections' is empty or not.
11298
11370
  // the operation happens at the same level as the 'childRelationship' field.
11299
- if (isFieldNode(parentNode) && parentNode.selectionSet && parentNode.name.value === 'node') {
11300
- if (parentNode.selectionSet.selections
11301
- .filter(isFieldOrInlineFragmentNode)
11302
- .some((selectionNode) => isRelationship(selectionNode, CHILD_RELATIONSHIP))) {
11303
- if (!parentNode.selectionSet.selections
11304
- .filter(isFieldNode)
11305
- .some((sibling) => sibling.name.value === 'Id')) {
11306
- idForChildRelationship.push(createFieldNode('Id'));
11307
- }
11308
- }
11309
- else {
11310
- // example { node { Id } }. The operation happens at the 'edges' -> 'node' level of the 'childRelationship' field
11311
- const parentInfo = findNearestAncesterPath(ancestors, true);
11312
- if (parentInfo.parentIndex >= 0) {
11313
- // example node { TimeSheetEntries { edges { node { Id }}}}
11314
- const parent = parentInfo.node;
11315
- if (isRelationship(parent, CHILD_RELATIONSHIP)) {
11316
- const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
11317
- // path : "TimeSheet"
11318
- const grandParentPath = findAncesterPath(unVisitedAncestors);
11319
- if (pathToObjectApiNamesMap &&
11320
- pathToObjectApiNamesMap[grandParentPath] &&
11321
- objectInfos &&
11322
- objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
11323
- const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
11324
- // exmaple "TimeSheetEntries"
11325
- const parentFieldName = parent.name.value;
11326
- const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
11327
- return childRelationship.relationshipName === parentFieldName;
11328
- });
11329
- if (targetRelationship !== undefined) {
11330
- const injectedParentFieldName = targetRelationship.fieldName;
11331
- if (!parentNode.selectionSet.selections
11332
- .filter(isFieldNode)
11333
- .some((sibling) => sibling.name.value === injectedParentFieldName)) {
11334
- // example: TimeSheetId { value }
11335
- relatedIdForChildRelationship.push(createFieldNode(injectedParentFieldName, FieldValueNodeSelectionSet));
11336
- }
11371
+ if (isFieldNode(parentNode) &&
11372
+ parentNode.selectionSet !== undefined &&
11373
+ (parentNode.name.value === 'node' || isCapableRelationship(parentNode))) {
11374
+ //check if the root query has the Id and inject if it needed
11375
+ if (parentNode.name.value === 'node') {
11376
+ const idNode = findIdFieldNode(parentNode);
11377
+ if (idNode === false) {
11378
+ rootQueryIdField.push(createFieldNode('Id'));
11379
+ }
11380
+ }
11381
+ // example { node { Id } }. The operation happens at the 'edges' -> 'node' level of the 'childRelationship' field
11382
+ const parentInfo = findNearestAncesterPath(ancestors, true);
11383
+ if (parentInfo.parentIndex >= 0) {
11384
+ // example node { TimeSheetEntries { edges { node { Id }}}}
11385
+ const parent = parentNode.name.value === 'node' ? parentInfo.node : parentNode;
11386
+ if (isRelationship(parent, CHILD_RELATIONSHIP)) {
11387
+ const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
11388
+ // path : "TimeSheet"
11389
+ const grandParentPath = findAncesterPath(unVisitedAncestors);
11390
+ if (pathToObjectApiNamesMap &&
11391
+ pathToObjectApiNamesMap[grandParentPath] &&
11392
+ objectInfos &&
11393
+ objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
11394
+ const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
11395
+ // exmaple "TimeSheetEntries"
11396
+ const parentFieldName = parent.name.value;
11397
+ const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
11398
+ return childRelationship.relationshipName === parentFieldName;
11399
+ });
11400
+ if (targetRelationship !== undefined) {
11401
+ const injectedParentFieldName = targetRelationship.fieldName;
11402
+ if (!parentNode.selectionSet.selections
11403
+ .filter(isFieldNode)
11404
+ .some((sibling) => sibling.name.value === injectedParentFieldName)) {
11405
+ // example: TimeSheetId { value }
11406
+ relatedIdForChildRelationship.push(createFieldNode(injectedParentFieldName, FieldValueNodeSelectionSet));
11337
11407
  }
11338
- }
11339
- }
11340
- const { selectionSet: { selections }, } = parentNode;
11341
- // see if node selection has DisplayValue and needs to inject
11342
- let displayValue;
11343
- for (let i = 0, len = selections.length; i < len; i++) {
11344
- const node = selections[i];
11345
- if (isFieldNode(node) && node.name.value === 'DisplayValue') {
11346
- displayValue = node;
11347
- break;
11348
- }
11349
- }
11350
- if (displayValue !== undefined) {
11351
- const apiName = parent.name.value;
11352
- const objectInfo = objectInfos[apiName];
11353
- if (objectInfo !== undefined &&
11354
- objectInfo.nameFields !== undefined &&
11355
- objectInfo.nameFields.length > 0) {
11356
- displayValueNameFields = objectInfo.nameFields.map((fieldName) => {
11357
- return createFieldNode(fieldName, FieldValueNodeSelectionSet);
11358
- });
11408
+ displayValueNameFields.push(...injectFieldsForDisplayValue(parentNode, {
11409
+ ...parent,
11410
+ name: {
11411
+ ...parent.name,
11412
+ value: targetRelationship.childObjectApiName,
11413
+ },
11414
+ }, objectInfos));
11359
11415
  }
11360
11416
  }
11361
11417
  }
11418
+ displayValueNameFields.push(...injectFieldsForDisplayValue(parentNode, parent, objectInfos));
11362
11419
  }
11363
11420
  }
11364
11421
  return [
11422
+ ...rootQueryIdField,
11365
11423
  ...flat(parentRelaltionships),
11366
- ...idForChildRelationship,
11367
11424
  ...relatedIdForChildRelationship,
11368
11425
  ...displayValueNameFields,
11369
11426
  ];
@@ -12135,6 +12192,60 @@ function replayDraftsOnRecord(record, draftMetadata) {
12135
12192
  }
12136
12193
  return recursivelyApplyDraftsToRecord(baseRecord, draftMetadata, draftMetadata.recordOperations);
12137
12194
  }
12195
+ /**
12196
+ * checks if updated fields are apart of the compound Name field
12197
+ * will generate a compound Name field if it is not already apart of the draft
12198
+ * @param record
12199
+ * @param draftFields
12200
+ * @param apiName
12201
+ * @param objectInfos
12202
+ * @returns
12203
+ */
12204
+ function compoundNameFieldFromDraftUpdates(record, draftFields, apiName, objectInfos) {
12205
+ let updatedNameFields = {};
12206
+ const objectInfo = objectInfos.get(apiName);
12207
+ if (draftFields['Name'] !== undefined ||
12208
+ objectInfo === undefined ||
12209
+ objectInfo.nameFields === undefined ||
12210
+ objectInfo.nameFields.length === 0) {
12211
+ return updatedNameFields;
12212
+ }
12213
+ const nameField = objectInfo.nameFields.find((x) => x === 'Name');
12214
+ if (nameField === undefined) {
12215
+ return updatedNameFields;
12216
+ }
12217
+ const { compound, updateable } = objectInfo.fields[nameField];
12218
+ if (objectInfo.nameFields.length <= 1 && compound === false && updateable === true) {
12219
+ return updatedNameFields;
12220
+ }
12221
+ let changedNameFields = {};
12222
+ const filteredNameFields = objectInfo.nameFields.filter((x) => x !== 'Name');
12223
+ for (let i = 0, len = filteredNameFields.length; i < len; i++) {
12224
+ const fieldName = filteredNameFields[i];
12225
+ const fieldValue = draftFields[fieldName];
12226
+ if (fieldValue !== undefined) {
12227
+ changedNameFields[fieldName] = fieldValue;
12228
+ }
12229
+ }
12230
+ if (keys$3(changedNameFields).length > 0) {
12231
+ const newNameValue = filteredNameFields
12232
+ .map((key) => {
12233
+ if (changedNameFields[key] !== undefined) {
12234
+ return changedNameFields[key];
12235
+ }
12236
+ else if (record.fields[key] !== undefined) {
12237
+ return record.fields[key].value;
12238
+ }
12239
+ else {
12240
+ return '';
12241
+ }
12242
+ })
12243
+ .join(' ')
12244
+ .trim();
12245
+ updatedNameFields['Name'] = newNameValue;
12246
+ }
12247
+ return updatedNameFields;
12248
+ }
12138
12249
  function recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations) {
12139
12250
  const { luvio, objectInfos, userId, formatDisplayValue, referencedRecords } = draftMetadata;
12140
12251
  if (recordOperations.length === 0) {
@@ -12194,8 +12305,15 @@ function recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations)
12194
12305
  record.drafts.deleted = true;
12195
12306
  }
12196
12307
  // 'lastModifiedById' and 'lastModifiedDate' are modified in 'delete' and 'update' action
12197
- const fields = draftActionType === 'delete' ? {} : draftOperation.fields;
12308
+ const draftOperationFields = draftActionType === 'delete' ? {} : draftOperation.fields;
12198
12309
  const apiName = draftOperation.apiName;
12310
+ const injectedCompoundNameField = compoundNameFieldFromDraftUpdates(record, draftOperationFields, apiName, objectInfos);
12311
+ // inject the compound Name field into the DraftOperation so on broadcast it doesnt try to
12312
+ // synthesize the Name field a second time when the durable store change notifier is triggered
12313
+ if (keys$3(injectedCompoundNameField).length > 0 && draftActionType === 'update') {
12314
+ draftOperation.fields['Name'] = injectedCompoundNameField['Name'];
12315
+ }
12316
+ const fields = { ...draftOperationFields, ...injectedCompoundNameField };
12199
12317
  // automaticlly add 'lastModifiedById' and 'lastModifiedByDate' and use an internal draft field copy since it is mutable
12200
12318
  const internalFields = {
12201
12319
  ...fields,
@@ -15996,9 +16114,9 @@ function instrumentAdapter(adapter, metadata) {
15996
16114
  }
15997
16115
  }
15998
16116
  return instrumentAdapter$1(instrumentedMobileAdapter, metadata, {
15999
- trackL1Hits: true,
16000
- trackL2Hits: true,
16001
- trackCacheMisses: true,
16117
+ trackL1Hits: false,
16118
+ trackL2Hits: false,
16119
+ trackCacheMisses: false,
16002
16120
  reportObserver: (report) => {
16003
16121
  for (const observer of reportObservers) {
16004
16122
  observer(report);
@@ -16887,13 +17005,13 @@ function mergeRecord(existingRecord, incomingRecord, objectInfo) {
16887
17005
  // since none of the changed fields are part of the incoming record
16888
17006
  if (missingFields.every((field) => {
16889
17007
  const referenceFieldName = findReferenceFieldForSpanningField(field, objectInfo);
16890
- if (referenceFieldName !== undefined) {
16891
- return (incomingRecord.fields[referenceFieldName].value ===
16892
- existingRecord.fields[referenceFieldName].value);
16893
- }
16894
- else {
17008
+ if (referenceFieldName === undefined) {
16895
17009
  return false;
16896
17010
  }
17011
+ return (existingRecord.fields[referenceFieldName] &&
17012
+ incomingRecord.fields[referenceFieldName] &&
17013
+ incomingRecord.fields[referenceFieldName].value ===
17014
+ existingRecord.fields[referenceFieldName].value);
16897
17015
  })) {
16898
17016
  return {
16899
17017
  ok: true,
@@ -17772,6 +17890,9 @@ class LdsPrimingRecordRefresher {
17772
17890
  optionalFields: value.fields.map((f) => `${_apiName}.${f}`),
17773
17891
  },
17774
17892
  ],
17893
+ }, {
17894
+ cachePolicy: { type: 'no-cache' },
17895
+ priority: 'background',
17775
17896
  }));
17776
17897
  });
17777
17898
  const promiseResults = await allSettled(promises);
@@ -17876,6 +17997,8 @@ function getRuntime() {
17876
17997
  const durableEnv = makeDurable(gqlEnv, {
17877
17998
  durableStore: recordDenormingStore,
17878
17999
  enableDurableMetadataRefresh: ldsMetadataRefreshEnabled.isOpen({ fallback: false }),
18000
+ // disable luvio deep freeze in headless environments
18001
+ disableDeepFreeze: typeof window === 'undefined',
17879
18002
  });
17880
18003
  getIngestRecords = durableEnv.getIngestStagingStoreRecords;
17881
18004
  getIngestMetadata = durableEnv.getIngestStagingStoreMetadata;
@@ -17981,4 +18104,4 @@ register({
17981
18104
  });
17982
18105
 
17983
18106
  export { O11Y_NAMESPACE_LDS_MOBILE, getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17984
- // version: 1.280.0-92c104b03
18107
+ // version: 1.282.0-f3e0ca0c7