@salesforce/lds-runtime-mobile 1.241.0 → 1.242.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/main.js +181 -131
  2. package/package.json +6 -6
  3. package/sfdc/main.js +181 -131
package/dist/main.js CHANGED
@@ -16,7 +16,7 @@ import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrum
16
16
  import { HttpStatusCode, 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, visit, execute, buildSchema, isObjectType, defaultFieldResolver } from '@luvio/graphql-parser';
19
- import { getRecordId18, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from '@salesforce/lds-adapters-uiapi';
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, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from '@salesforce/lds-adapters-uiapi';
20
20
  import caseSensitiveUserId from '@salesforce/user/Id';
21
21
  import { idleDetector, getInstrumentation } from 'o11y/client';
22
22
  import ldsUseShortUrlGate from '@salesforce/gate/lds.useShortUrl';
@@ -554,6 +554,9 @@ function handleDurableStoreRejection(instrument) {
554
554
  }
555
555
 
556
556
  function isStoreEntryError(storeRecord) {
557
+ if (!storeRecord || typeof storeRecord !== 'object') {
558
+ return false;
559
+ }
557
560
  return storeRecord.__type === 'error';
558
561
  }
559
562
 
@@ -1578,30 +1581,13 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
1578
1581
  * For full license text, see the LICENSE.txt file
1579
1582
  */
1580
1583
 
1581
- const API_NAMESPACE = 'UiApi';
1582
- const RECORD_REPRESENTATION_NAME = 'RecordRepresentation';
1583
- const RECORD_VIEW_ENTITY_REPRESENTATION_NAME = 'RecordViewEntityRepresentation';
1584
- const RECORD_ID_PREFIX = `${API_NAMESPACE}::${RECORD_REPRESENTATION_NAME}:`;
1585
- const RECORD_VIEW_ENTITY_ID_PREFIX = `${API_NAMESPACE}::${RECORD_VIEW_ENTITY_REPRESENTATION_NAME}:Name:`;
1586
- const RECORD_FIELDS_KEY_JUNCTION = '__fields__';
1584
+
1587
1585
  function isStoreKeyRecordId(key) {
1588
1586
  return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) === -1;
1589
1587
  }
1590
- function isStoreKeyRecordViewEntity(key) {
1591
- return (key.indexOf(RECORD_VIEW_ENTITY_ID_PREFIX) > -1 &&
1592
- key.indexOf(RECORD_FIELDS_KEY_JUNCTION) === -1);
1593
- }
1594
1588
  function isStoreKeyRecordField(key) {
1595
1589
  return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) > -1;
1596
1590
  }
1597
- function extractRecordIdFromStoreKey(key) {
1598
- if (key === undefined ||
1599
- (key.indexOf(RECORD_ID_PREFIX) === -1 && key.indexOf(RECORD_VIEW_ENTITY_ID_PREFIX) === -1)) {
1600
- return undefined;
1601
- }
1602
- const parts = key.split(':');
1603
- return parts[parts.length - 1].split('_')[0];
1604
- }
1605
1591
  function buildRecordFieldStoreKey(recordKey, fieldName) {
1606
1592
  return `${recordKey}${RECORD_FIELDS_KEY_JUNCTION}${fieldName}`;
1607
1593
  }
@@ -8178,6 +8164,10 @@ function isOperationDefinitionNode(node) {
8178
8164
  return node.kind === 'OperationDefinition';
8179
8165
  }
8180
8166
 
8167
+ const POLYMORPHIC_PARENT_RELATIONSHIP = 'polymorphicParentRelationship';
8168
+ const PARENT_RELATIONSHIP = 'parentRelationship';
8169
+ const CHILD_RELATIONSHIP = 'childRelationship';
8170
+ const RECORD_QUERY = 'recordQuery';
8181
8171
  function requestsDraftsField(recordFieldNode) {
8182
8172
  if (!recordFieldNode.selectionSet)
8183
8173
  return false;
@@ -8193,18 +8183,41 @@ function isRecordQuery(recordQueryField) {
8193
8183
  directive.arguments
8194
8184
  .map((argument) => argument.value)
8195
8185
  .filter(isStringValueNode)
8196
- .some((categoryName) => categoryName.value === 'recordQuery'));
8186
+ .some((categoryName) => categoryName.value === RECORD_QUERY));
8197
8187
  });
8198
8188
  }
8199
8189
  return false;
8200
8190
  }
8201
- // finds field with 'recordQuery' and 'childRelationship' directive
8202
- function findNearestRecordQuery(ancestors) {
8203
- const recordQueryAncester = findNearestAncesterPath(ancestors, true).node;
8204
- return recordQueryAncester === undefined ? undefined : recordQueryAncester;
8191
+ // finds connection field with 'recordQuery' and 'childRelationship' directive.
8192
+ function findNearestConnection(ancestors) {
8193
+ const connectionAncestor = findNearestAncesterPath(ancestors, true).node;
8194
+ return connectionAncestor === undefined ? undefined : connectionAncestor;
8195
+ }
8196
+ // convinient method to find nearest connection with its path
8197
+ function findNearestConnectionWithPath(ancestors) {
8198
+ const closestAncestorPath = findNearestAncesterPath(ancestors, true);
8199
+ let connection = undefined;
8200
+ let connectionPath = undefined;
8201
+ if (closestAncestorPath.parentIndex > 0) {
8202
+ const connectionAncestor = closestAncestorPath.node;
8203
+ const connectionAncestors = ancestors.slice(0, closestAncestorPath.parentIndex);
8204
+ connection =
8205
+ connectionAncestor === undefined ? undefined : connectionAncestor;
8206
+ if (connection !== undefined) {
8207
+ const ancesterPath = findAncesterPath(connectionAncestors);
8208
+ connectionPath =
8209
+ ancesterPath === ''
8210
+ ? connection.name.value
8211
+ : `${ancesterPath}#${connection.name.value}`;
8212
+ }
8213
+ }
8214
+ return {
8215
+ connection,
8216
+ path: connectionPath,
8217
+ };
8205
8218
  }
8206
- // finds cloeset ancester. If 'parentRelationship' is allowed, it could be 'InlineFragmentNode' since it inherits the 'parent' relationship. 'InlineFragmentNode' makes sure that only one 'apiName' returns when tree is traversed.
8207
- function findNearestAncesterPath(ancestors, recordQueryOnly) {
8219
+ // finds closest ancestor. If node with 'parentRelationship' is the ancester, the end result could be 'InlineFragmentNode' since it inherits the 'parent' relationship. 'InlineFragmentNode' makes sure that only one 'apiName' returns when tree is traversed.
8220
+ function findNearestAncesterPath(ancestors, connectionOnly) {
8208
8221
  let recordQueryPath = { node: undefined, parentIndex: -1 };
8209
8222
  let relationship = '';
8210
8223
  for (let i = ancestors.length - 1; i >= 0; i--) {
@@ -8218,9 +8231,11 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
8218
8231
  continue;
8219
8232
  for (let arg of directive.arguments) {
8220
8233
  if (arg.value &&
8221
- (arg.value.value === 'recordQuery' ||
8222
- arg.value.value === 'childRelationship' ||
8223
- (!recordQueryOnly && arg.value.value === 'parentRelationship'))) {
8234
+ (arg.value.value === RECORD_QUERY ||
8235
+ arg.value.value === CHILD_RELATIONSHIP ||
8236
+ (!connectionOnly &&
8237
+ (arg.value.value === PARENT_RELATIONSHIP ||
8238
+ arg.value.value === POLYMORPHIC_PARENT_RELATIONSHIP)))) {
8224
8239
  recordQueryPath = { node: node, parentIndex: i };
8225
8240
  relationship = arg.value.value;
8226
8241
  break;
@@ -8235,17 +8250,19 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
8235
8250
  //checks if nearest ancester could be an inline fragment
8236
8251
  if (recordQueryPath.node !== undefined &&
8237
8252
  recordQueryPath.node.selectionSet &&
8238
- relationship === 'parentRelationship') {
8239
- //
8240
- if (recordQueryPath.node.selectionSet.selections.every(isInlineFragmentNode)) {
8241
- //
8242
- const inlineFragmentLoc = recordQueryPath.parentIndex + 2;
8243
- if (inlineFragmentLoc < ancestors.length && ancestors[inlineFragmentLoc]) {
8253
+ (relationship === PARENT_RELATIONSHIP || relationship === POLYMORPHIC_PARENT_RELATIONSHIP)) {
8254
+ // InlineFragment is usually 3 steps aways from its FieldNode parent within ancester hierarchy if it exists. The below search
8255
+ // is applied to adapt to future AST structure change
8256
+ let parentIndex = recordQueryPath.parentIndex + 1;
8257
+ while (parentIndex < ancestors.length) {
8258
+ if (isInlineFragmentNode(ancestors[parentIndex])) {
8244
8259
  recordQueryPath = {
8245
- node: ancestors[inlineFragmentLoc],
8246
- parentIndex: inlineFragmentLoc,
8260
+ node: ancestors[parentIndex],
8261
+ parentIndex,
8247
8262
  };
8263
+ break;
8248
8264
  }
8265
+ parentIndex++;
8249
8266
  }
8250
8267
  }
8251
8268
  return recordQueryPath;
@@ -8269,7 +8286,7 @@ function findAncesterPath(ancesters) {
8269
8286
  ? sectionPath
8270
8287
  : sectionPath === ''
8271
8288
  ? path
8272
- : `${sectionPath}_${path}`;
8289
+ : `${sectionPath}#${path}`;
8273
8290
  }
8274
8291
  }
8275
8292
  boundaryIndex = parentIndex;
@@ -8327,9 +8344,9 @@ function getRelation(node) {
8327
8344
  const relationships = args
8328
8345
  .map((arg) => arg.value)
8329
8346
  .filter(isStringValueNode)
8330
- .filter((valueNode) => valueNode.value === 'childRelationship' ||
8331
- valueNode.value === 'parentRelationship' ||
8332
- valueNode.value === 'polymorphicParentRelationship')
8347
+ .filter((valueNode) => valueNode.value === CHILD_RELATIONSHIP ||
8348
+ valueNode.value === PARENT_RELATIONSHIP ||
8349
+ valueNode.value === POLYMORPHIC_PARENT_RELATIONSHIP)
8333
8350
  .map((relationshipNode) => relationshipNode.value);
8334
8351
  if (relationships.length > 0) {
8335
8352
  return relationships[0];
@@ -8386,8 +8403,8 @@ function isFieldSpanning(node, parentNode) {
8386
8403
  */
8387
8404
  function isParentRelationship(node) {
8388
8405
  return (node &&
8389
- (isRelationship(node, 'parentRelationship') ||
8390
- isRelationship(node, 'polymorphicParentRelationship')));
8406
+ (isRelationship(node, PARENT_RELATIONSHIP) ||
8407
+ isRelationship(node, POLYMORPHIC_PARENT_RELATIONSHIP)));
8391
8408
  }
8392
8409
  /*
8393
8410
  checks if the InlineFragment spans
@@ -9479,7 +9496,7 @@ const parentRelationshipDirective = {
9479
9496
  },
9480
9497
  value: {
9481
9498
  kind: Kind.STRING,
9482
- value: 'parentRelationship',
9499
+ value: PARENT_RELATIONSHIP,
9483
9500
  block: false,
9484
9501
  },
9485
9502
  },
@@ -9493,7 +9510,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9493
9510
  // example 2 'ServiceAppointment' -> ['Owner']; 'Owner' -> ['User', 'Group']
9494
9511
  const objectNodeInfoTree = {};
9495
9512
  // save the field path to apiName map
9496
- // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account']; 'ServiceAppointment_Account_Owner' -> ['User']
9513
+ // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#ccount' -> ['Account']; 'ServiceAppointment#Account#Owner' -> ['User']
9497
9514
  const pathToObjectApiNamesMap = {};
9498
9515
  let startNodes = new Set();
9499
9516
  let totalNodes = new Set();
@@ -9508,11 +9525,11 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9508
9525
  visit(originalAST, {
9509
9526
  Argument: {
9510
9527
  enter(node, key, parent, path, ancestors) {
9511
- const recordQueryNode = findNearestRecordQuery(ancestors);
9512
- if (!recordQueryNode)
9528
+ const { connection: recordConnectionNode, path: ancesterPath } = findNearestConnectionWithPath(ancestors);
9529
+ if (!recordConnectionNode || !ancesterPath)
9513
9530
  return;
9514
- if (!objectNodeInfoTree[recordQueryNode.name.value]) {
9515
- objectNodeInfoTree[recordQueryNode.name.value] = [];
9531
+ if (!objectNodeInfoTree[ancesterPath]) {
9532
+ objectNodeInfoTree[ancesterPath] = [];
9516
9533
  }
9517
9534
  switch (node.name.value) {
9518
9535
  case 'orderBy':
@@ -9520,12 +9537,12 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9520
9537
  if (node.value.kind !== 'ObjectValue') {
9521
9538
  return;
9522
9539
  }
9523
- totalNodes.add(recordQueryNode.name.value);
9540
+ totalNodes.add(ancesterPath);
9524
9541
  // 'childRelationship' node is not taken as the startNode of the 'NodeInfoTree' graph. The field scanning will construct the graph which lead here.
9525
- if (isRecordQuery(recordQueryNode)) {
9526
- startNodes.add(recordQueryNode.name.value);
9542
+ if (isRecordQuery(recordConnectionNode)) {
9543
+ startNodes.add(recordConnectionNode.name.value);
9527
9544
  }
9528
- growObjectFieldTree(objectNodeInfoTree, recordQueryNode.name.value, node.value, totalNodes, startNodes);
9545
+ growObjectFieldTree(objectNodeInfoTree, ancesterPath, node.value, totalNodes, startNodes);
9529
9546
  break;
9530
9547
  case 'scope':
9531
9548
  if (!isScopeArgumentNodeWithType(node, 'ASSIGNEDTOME', variables)) {
@@ -9540,17 +9557,16 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9540
9557
  name: 'ServiceResources',
9541
9558
  });
9542
9559
  }
9543
- if (objectNodeInfoTree['ServiceResources'] === undefined) {
9544
- objectNodeInfoTree['ServiceResources'] = [];
9545
- }
9546
- if (!objectNodeInfoTree['ServiceResources'].some((child) => child.name === 'ServiceResource')) {
9547
- objectNodeInfoTree['ServiceResources'].push({
9548
- relation: 'parent',
9549
- name: 'ServiceResource',
9550
- });
9560
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources'] === undefined) {
9561
+ objectNodeInfoTree['ServiceAppointment#ServiceResources'] = [
9562
+ {
9563
+ relation: 'parent',
9564
+ name: 'ServiceResource',
9565
+ },
9566
+ ];
9551
9567
  }
9552
- if (objectNodeInfoTree['ServiceResource'] === undefined) {
9553
- objectNodeInfoTree['ServiceResource'] = [];
9568
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] === undefined) {
9569
+ objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] = [];
9554
9570
  }
9555
9571
  break;
9556
9572
  default:
@@ -9564,7 +9580,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9564
9580
  return;
9565
9581
  if (!node.selectionSet)
9566
9582
  return;
9567
- const recordQueryField = findNearestRecordQuery(ancestors);
9583
+ const recordQueryField = findNearestConnection(ancestors);
9568
9584
  //only injects fields for 'recordQuery' field. ignores the 'childRelationship' field since it will be traversed as the child of the 'recordQuery'
9569
9585
  if (isRecordQuery(recordQueryField) && recordQueryField) {
9570
9586
  totalNodes.add(recordQueryField.name.value);
@@ -9581,7 +9597,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9581
9597
  visit(originalAST, {
9582
9598
  Argument: {
9583
9599
  leave(node, key, parent, path, ancestors) {
9584
- const recordQueryField = findNearestRecordQuery(ancestors);
9600
+ const recordQueryField = findNearestConnection(ancestors);
9585
9601
  if (!recordQueryField)
9586
9602
  return;
9587
9603
  const ancestorPath = findAncesterPath(ancestors);
@@ -9629,7 +9645,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9629
9645
  case 'where': {
9630
9646
  inlineFragmentSelections[ancestorPath] = [
9631
9647
  ...inlineFragmentSelections[ancestorPath],
9632
- ...injectFilter(node, idState, ancestorPath, objectInfos, pathToObjectApiNamesMap, draftFunctions, recordReferenceNode),
9648
+ ...injectFilter(node, idState, ancestorPath, false, objectInfos, pathToObjectApiNamesMap, draftFunctions, recordReferenceNode),
9633
9649
  ];
9634
9650
  break;
9635
9651
  }
@@ -9645,7 +9661,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9645
9661
  if (!node.selectionSet)
9646
9662
  return;
9647
9663
  // it could be 'recordQuery' or 'childRelationship'
9648
- const recordQueryField = findNearestRecordQuery(ancestors);
9664
+ const recordQueryField = findNearestConnection(ancestors);
9649
9665
  if (!recordQueryField)
9650
9666
  return;
9651
9667
  const ancestorPath = findAncesterPath(ancestors);
@@ -9657,7 +9673,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9657
9673
  spanningSelections.push(selection);
9658
9674
  }
9659
9675
  }
9660
- const injectedFields = injectFields(spanningSelections, node, ancestors, objectInfos, pathToObjectApiNamesMap);
9676
+ const injectedFields = injectFields(spanningSelections, node, ancestorPath, ancestors, objectInfos, pathToObjectApiNamesMap);
9661
9677
  const mergedInjectedFields = mergeSelectionNodes(inlineFragmentSelections[ancestorPath], injectedFields);
9662
9678
  inlineFragmentSelections[ancestorPath] = mergedInjectedFields;
9663
9679
  },
@@ -9670,7 +9686,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9670
9686
  // removes 'ServicesResources' query field node if 'assignedtome' scope shows up
9671
9687
  if (assignedtomeQueryFieldNode !== undefined &&
9672
9688
  node.name.value === 'ServiceResources') {
9673
- const serviceResourcesAncestor = findNearestRecordQuery(ancestors);
9689
+ const serviceResourcesAncestor = findNearestConnection(ancestors);
9674
9690
  if (serviceResourcesAncestor === assignedtomeQueryFieldNode) {
9675
9691
  return null;
9676
9692
  }
@@ -9679,7 +9695,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9679
9695
  return;
9680
9696
  if (!node.selectionSet)
9681
9697
  return;
9682
- const recordQueryField = findNearestRecordQuery(ancestors);
9698
+ const recordQueryField = findNearestConnection(ancestors);
9683
9699
  if (!recordQueryField)
9684
9700
  return;
9685
9701
  const ancestorPath = findAncesterPath(ancestors);
@@ -9882,15 +9898,16 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
9882
9898
  }
9883
9899
  // example: 'Account'
9884
9900
  const childNode = objectFieldNode.name.value;
9901
+ const childNodepath = `${parentNode}#${childNode}`;
9885
9902
  if (!tree[parentNode].some((child) => child.name === childNode)) {
9886
9903
  tree[parentNode].push({
9887
9904
  relation: 'parent',
9888
9905
  name: childNode,
9889
9906
  });
9890
- totalNodes.add(childNode);
9907
+ totalNodes.add(childNodepath);
9891
9908
  }
9892
9909
  // recursively go to deeper level of filter.
9893
- growObjectFieldTree(tree, childNode, objectFieldNode.value, totalNodes, startNodes);
9910
+ growObjectFieldTree(tree, childNodepath, objectFieldNode.value, totalNodes, startNodes);
9894
9911
  }
9895
9912
  }
9896
9913
  }
@@ -9925,19 +9942,20 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
9925
9942
  }
9926
9943
  if (!tree[parentSectionPath].some((field) => field.name === fieldName)) {
9927
9944
  tree[parentSectionPath].push({
9928
- relation: relationType === 'parentRelationship' ||
9929
- relationType === 'polymorphicParentRelationship'
9945
+ relation: relationType === PARENT_RELATIONSHIP ||
9946
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
9930
9947
  ? 'parent'
9931
9948
  : 'child',
9932
9949
  name: fieldName,
9933
9950
  });
9934
- totalNodes.add(fieldName);
9951
+ totalNodes.add(`${parentSectionPath}#${fieldName}`);
9935
9952
  }
9936
9953
  if (entryNode.selectionSet && entryNode.selectionSet.selections) {
9937
9954
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
9938
9955
  // recursively build the traversal tree
9939
9956
  for (const child of childNodes) {
9940
- growFieldTree(tree, fieldName, child, entryNode, totalNodes, startNodes);
9957
+ const path = `${parentSectionPath}#${fieldName}`;
9958
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
9941
9959
  }
9942
9960
  }
9943
9961
  }
@@ -9967,23 +9985,23 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
9967
9985
  }
9968
9986
  if (!tree[parentSectionPath].some((field) => field.name === conditionName)) {
9969
9987
  tree[parentSectionPath].push({
9970
- relation: relationType === 'parentRelationship' ||
9971
- relationType === 'polymorphicParentRelationship'
9988
+ relation: relationType === PARENT_RELATIONSHIP ||
9989
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
9972
9990
  ? 'parent'
9973
9991
  : 'child',
9974
9992
  name: conditionName,
9975
9993
  });
9976
- totalNodes.add(conditionName);
9994
+ const path = `${parentSectionPath}#${conditionName}`;
9995
+ totalNodes.add(path);
9977
9996
  }
9978
9997
  }
9979
9998
  }
9980
9999
  // dive deep immediately for 'InlineFragment'
9981
10000
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
10001
+ const path = `${parentSectionPath}${entryNode.typeCondition ? '#' + entryNode.typeCondition.name.value : ''}`;
9982
10002
  // Navigates into InLineFragment
9983
10003
  for (const child of childNodes) {
9984
- growFieldTree(tree, entryNode.typeCondition
9985
- ? entryNode.typeCondition.name.value
9986
- : parentSectionPath, child, entryNode, totalNodes, startNodes);
10004
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
9987
10005
  }
9988
10006
  }
9989
10007
  }
@@ -10027,7 +10045,7 @@ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectI
10027
10045
  }
10028
10046
  const validObjectInfoNodes = [];
10029
10047
  let updatedObjectInfoMap = {};
10030
- // InlineFragment and polymorphic field support fits into this scenario pathToObjectApiNamesMap Entry: 'ServiceAppointment_Owner' -> ['User', 'Group']; ServiceAppointment_Owner_User' -> ['User']
10048
+ // InlineFragment and polymorphic field support fits into this scenario pathToObjectApiNamesMap Entry: 'ServiceAppointment#Owner' -> ['User', 'Group']; ServiceAppointment#Owner#User' -> ['User']
10031
10049
  if (objectInfoApiNames.length > 0 &&
10032
10050
  nodesAtSameLevel.length > 0 &&
10033
10051
  objectInfoApiNames.includes(nodesAtSameLevel[0].name)) {
@@ -10039,7 +10057,7 @@ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectI
10039
10057
  // eslint-disable-next-line
10040
10058
  throw new Error(`Condition ${field.name} does not exists for ${parentPath}`);
10041
10059
  }
10042
- const path = `${parentPath}_${field.name}`;
10060
+ const path = `${parentPath}#${field.name}`;
10043
10061
  pathToObjectApiNamesMap[path] = [field.name];
10044
10062
  }
10045
10063
  validObjectInfoNodes.push(...nodesAtSameLevel);
@@ -10055,7 +10073,7 @@ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectI
10055
10073
  let apiNames = [];
10056
10074
  for (const nodeInfo of nodesAtSameLevel) {
10057
10075
  const field = nodeInfo.name;
10058
- const path = `${parentPath}_${field}`;
10076
+ const path = `${parentPath}#${field}`;
10059
10077
  // Handle 'parentRelationship'
10060
10078
  if (nodeInfo.relation === 'parent') {
10061
10079
  const relationshipId = referenceIdFieldForRelationship(field);
@@ -10073,12 +10091,12 @@ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectI
10073
10091
  }
10074
10092
  }
10075
10093
  // This is a polymorphic field
10076
- if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[field]) {
10094
+ if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[path]) {
10077
10095
  // Fields needs to expand and heterogenous entity ObjectInfo needs to be fetched
10078
- const referencedNodeInfos = objectInfotree[field];
10096
+ const referencedNodeInfos = objectInfotree[path];
10079
10097
  const requestedApiNames = referencedNodeInfos.map((referenceNodeInfo) => referenceNodeInfo.name);
10080
10098
  // Fetches requested ObjectInfo only. Some entity's relation field could define more than 6 references. Only references show up in query need to be handled.
10081
- if (requestedApiNames.length > 0 && objectInfotree[field]) {
10099
+ if (requestedApiNames.length > 0 && objectInfotree[path]) {
10082
10100
  fieldDefinition.referenceToInfos
10083
10101
  .filter((referenceToInfo) => requestedApiNames.includes(referenceToInfo.apiName))
10084
10102
  .forEach((ref) => {
@@ -10138,8 +10156,8 @@ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectI
10138
10156
  }
10139
10157
  for (const nodeInfo of validObjectInfoNodes) {
10140
10158
  const field = nodeInfo.name;
10141
- const subLevelFields = objectInfotree[field];
10142
- const path = `${parentPath}_${field}`;
10159
+ const path = `${parentPath}#${field}`;
10160
+ const subLevelFields = objectInfotree[path];
10143
10161
  if (subLevelFields && subLevelFields.length > 0) {
10144
10162
  const subObjectInfos = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
10145
10163
  updatedObjectInfoMap = { ...updatedObjectInfoMap, ...subObjectInfos };
@@ -10156,27 +10174,29 @@ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectI
10156
10174
  * 'path' and 'queryNode' is 1 level above the 'filterNode'
10157
10175
  * @param filterNode filter node which needs to be injected. For example, 'State' ObjectFieldNode within filter 'where: { State: { eq: "Nova Scotia" }}'
10158
10176
  * @param idState ID state will be updated to determine if the ID fields in AST need to be swapped. The swapping happens later.
10159
- * @param path path to the current filterNode's parent. For example, path could be 'ServiceApointment' when filterNode is 'State'. If the path does not exist in 'pathToObjectApiNamesMap', parent node is not an field of relationship or recordQuery
10177
+ * @param parentPath path to the current filterNode's parent. For example, path could be 'ServiceApointment' when filterNode is 'State'. If the path does not exist in 'pathToObjectApiNamesMap', parent node is not an field of relationship or recordQuery
10178
+ * @param isParentPolymorphic true if parent points to a polymorphic field.
10160
10179
  * @param queryNode referece FieldNode which provides the information if 'filterNode' exist in it nor not.
10161
10180
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
10162
10181
  * @param pathToObjectApiNamesMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account'], 'ServiceAppointment_Owner' -> ['User', 'Group'].
10163
10182
  * @param draftFunctions functions for working with record ids that may be draft-created ids
10164
10183
  * @returns an array of nodes with injected fields
10165
10184
  */
10166
- function injectFilter(filterNode, idState, path, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode) {
10185
+ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode) {
10167
10186
  const injectedSelections = [];
10187
+ let isPolymorphicField = false;
10168
10188
  switch (filterNode.kind) {
10169
10189
  case Kind.ARGUMENT:
10170
10190
  if (filterNode.value.kind !== 'ObjectValue')
10171
10191
  return [];
10172
10192
  filterNode.value.fields.forEach((objectFieldNode) => {
10173
- let subResults = injectFilter(objectFieldNode, idState, path, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10193
+ let subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10174
10194
  for (const subResult of subResults) {
10175
10195
  mergeOrAddToGroup(injectedSelections, subResult);
10176
10196
  }
10177
10197
  // multiple Ids might need to be swapped. remember their paths for faster write.
10178
10198
  if (idState.swapNeeded) {
10179
- idState.paths.push(path);
10199
+ idState.paths.push(parentPath);
10180
10200
  }
10181
10201
  });
10182
10202
  return injectedSelections;
@@ -10185,7 +10205,7 @@ function injectFilter(filterNode, idState, path, objectInfos, pathToObjectApiNam
10185
10205
  case Kind.LIST: {
10186
10206
  filterNode.value.values.filter(isObjectValueNode).forEach((objectValueNode) => {
10187
10207
  objectValueNode.fields.forEach((objectFieldNode) => {
10188
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10208
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10189
10209
  for (const subResult of subResults) {
10190
10210
  mergeOrAddToGroup(injectedSelections, subResult);
10191
10211
  }
@@ -10196,7 +10216,7 @@ function injectFilter(filterNode, idState, path, objectInfos, pathToObjectApiNam
10196
10216
  case Kind.OBJECT: {
10197
10217
  if (filterNode.name.value === 'not') {
10198
10218
  filterNode.value.fields.forEach((objectFieldNode) => {
10199
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10219
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10200
10220
  for (const subResult of subResults) {
10201
10221
  mergeOrAddToGroup(injectedSelections, subResult);
10202
10222
  }
@@ -10206,15 +10226,15 @@ function injectFilter(filterNode, idState, path, objectInfos, pathToObjectApiNam
10206
10226
  let apiNames = [];
10207
10227
  let isScalarField = false;
10208
10228
  //It is possible that this is a polymorphic field
10209
- apiNames = pathToObjectApiNamesMap[path];
10210
- // example: path: 'ServiceAppointment_LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
10229
+ apiNames = pathToObjectApiNamesMap[parentPath];
10230
+ // example: path: 'ServiceAppointment#LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
10211
10231
  if (apiNames === undefined) {
10212
10232
  isScalarField = true;
10213
10233
  }
10214
10234
  else {
10215
10235
  if (apiNames.some((apiName) => objectInfos[apiName] === undefined)) {
10216
10236
  // eslint-disable-next-line
10217
- throw new Error(`ObjectInfo is missing for ${path}`);
10237
+ throw new Error(`ObjectInfo is missing for ${parentPath}`);
10218
10238
  }
10219
10239
  }
10220
10240
  if (isScalarField) {
@@ -10238,21 +10258,17 @@ function injectFilter(filterNode, idState, path, objectInfos, pathToObjectApiNam
10238
10258
  let isSpanning = false;
10239
10259
  // if true, current node is a polymorphic concrete type node. For example, field node `User` under `Owner`
10240
10260
  let isInlineFragment = false;
10241
- let isPolymorphicField = false;
10242
10261
  let isTypeNameExisting = false;
10243
10262
  let curPath;
10244
10263
  let fieldName = filterNode.name.value;
10245
- curPath = `${path}_${fieldName}`;
10264
+ curPath = `${parentPath}#${fieldName}`;
10246
10265
  if (pathToObjectApiNamesMap[curPath] &&
10247
10266
  pathToObjectApiNamesMap[curPath].length > 0) {
10248
10267
  isSpanning = true;
10249
- if (pathToObjectApiNamesMap[curPath].length === 1 &&
10250
- pathToObjectApiNamesMap[path] &&
10251
- pathToObjectApiNamesMap[path].length >= 1 &&
10252
- pathToObjectApiNamesMap[path].includes(pathToObjectApiNamesMap[curPath][0])) {
10253
- isInlineFragment = isPolymorphicFieldPath(path, pathToObjectApiNamesMap);
10254
- }
10255
- isPolymorphicField = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap);
10268
+ isInlineFragment =
10269
+ isParentPolymorphic &&
10270
+ pathToObjectApiNamesMap[curPath].length === 1;
10271
+ isPolymorphicField = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
10256
10272
  }
10257
10273
  // When filter node is at InLineFragment Level(a concrete entity of polymorphic field), query node is one level up. For example, ObjectFieldNode is ...{User:{...}}, queryNode is Owner:[User]
10258
10274
  if (isInlineFragment) {
@@ -10298,7 +10314,7 @@ function injectFilter(filterNode, idState, path, objectInfos, pathToObjectApiNam
10298
10314
  }
10299
10315
  const objectInfoName = pathToObjectApiNamesMap[curPath] !== undefined
10300
10316
  ? pathToObjectApiNamesMap[curPath][0]
10301
- : pathToObjectApiNamesMap[path][0];
10317
+ : pathToObjectApiNamesMap[parentPath][0];
10302
10318
  const isIdField = isFieldAnIdField(filterNode.name.value, objectInfos[objectInfoName]);
10303
10319
  if (!isIdField) {
10304
10320
  let subSelectionNodes = [];
@@ -10310,7 +10326,7 @@ function injectFilter(filterNode, idState, path, objectInfos, pathToObjectApiNam
10310
10326
  updateIDInfo(subFieldNode, idState, draftFunctions);
10311
10327
  }
10312
10328
  // try injecting the fields within predicate no matter it has relation or not.
10313
- let subResults = injectFilter(subFieldNode, idState, curPath, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
10329
+ let subResults = injectFilter(subFieldNode, idState, curPath, isPolymorphicField, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
10314
10330
  subSelectionNodes = subSelectionNodes.concat(subResults);
10315
10331
  });
10316
10332
  if (!subFieldsHasId) {
@@ -10471,15 +10487,43 @@ function mergeOrAddToGroup(group, element) {
10471
10487
  }
10472
10488
  group.push(element);
10473
10489
  }
10474
- // checks if the path points to a polymorphic field. If path points to a polymorphic field, its entry within `pathToObjectApiNamesMap` has the format {"Path" : ["elem1", "elem2"]}.
10475
- // There must be also two entries pointing to concrete polymorphic field path. The two entries are like {"Path_elem1" : ["elem1"]} and {"Path_elem2" : ["elem2"]}
10476
- // For example ServiceAppointment_Owner' --> ['User', 'Group']; 'ServiceAppointment_Owner_User' --> ['User'], 'ServiceAppointment_Owner_Group' --> ['Group']
10477
- function isPolymorphicFieldPath(path, pathToObjectApiNamesMap) {
10478
- const apiName = pathToObjectApiNamesMap[path][0];
10479
- const apiNamesAtChildPath = pathToObjectApiNamesMap[`${path}_${apiName}`];
10480
- return (apiNamesAtChildPath !== undefined &&
10481
- apiNamesAtChildPath.length === 1 &&
10482
- apiNamesAtChildPath[0] === apiName);
10490
+ // checks if the path points to a polymorphic field. For example, for the below `pathToObjectApiNamesMap`
10491
+ // {
10492
+ // 'ServiceAppointment' -> ['ServiceAppointment']
10493
+ // 'ServiceAppointment#Owner' --> ['User'],
10494
+ // 'ServiceAppointment#Owner#User' --> ['User']
10495
+ // }
10496
+ // path `ServiceAppointment#Owner` points to a polymorphic field, but path `ServiceAppointment#Owner#User` does not.
10497
+ function isPolymorphicFieldPath(path, pathToObjectApiNamesMap, objectInfos) {
10498
+ const lastSegmentIndex = path.lastIndexOf('#');
10499
+ if (lastSegmentIndex < 0) {
10500
+ return false;
10501
+ }
10502
+ const lastSegment = path.slice(lastSegmentIndex + 1);
10503
+ const parentApiPath = path.slice(0, lastSegmentIndex);
10504
+ if (pathToObjectApiNamesMap[parentApiPath] === undefined) {
10505
+ return false;
10506
+ }
10507
+ const parentObjectApiNames = pathToObjectApiNamesMap[parentApiPath];
10508
+ // If the last segment is a Polymorphic field, its immediate parent is a concrete object entity, which has 1 objectApiName mapped to the parent path in `pathToObjectApiNamesMap`.
10509
+ // For example, we like to check if `ServiceAppointment#Owner` path is polymorphic. The last segment is `Owner` and its parent `ServiceAppointment` has one element (which is also `ServiceAppointment`) array as its value.
10510
+ // Below are the entries in `pathToObjectApiNamesMap`
10511
+ // {
10512
+ // `ServiceAppointmen`t: [`ServiceAppointment`],
10513
+ // `ServiceAppointment#Owner`: [`User`, `Group`],
10514
+ // `ServiceAppointment#Owner#User`: [`User`],
10515
+ // `ServiceAppointment#Owner#Group`: [`Group`],
10516
+ // }
10517
+ if (parentObjectApiNames.length !== 1) {
10518
+ return false;
10519
+ }
10520
+ const parentObjectInfo = objectInfos[parentObjectApiNames[0]];
10521
+ const relationshipField = referenceIdFieldForRelationship(lastSegment);
10522
+ let fieldDefinition = parentObjectInfo.fields[relationshipField];
10523
+ if (fieldDefinition === undefined) {
10524
+ return false;
10525
+ }
10526
+ return fieldDefinition.polymorphicForeignKey;
10483
10527
  }
10484
10528
  function isFieldAnIdField(fieldName, objectInfo) {
10485
10529
  if (fieldName === 'Id')
@@ -10533,10 +10577,10 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
10533
10577
  * @param parentNode parent node of param 1
10534
10578
  * @param ancestors ancester of param 1
10535
10579
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
10536
- * @param pathToObjectApiNamesMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account'], 'ServiceAppointment_Owner' -> ['User', 'Group'].
10580
+ * @param pathToObjectApiNamesMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#Account' -> ['Account'], 'ServiceAppointment#Owner' -> ['User', 'Group'].
10537
10581
  * @return injected SelectionNodes used to construct the InlineFragment.
10538
10582
  */
10539
- function injectFields(selections, parentNode, ancestors, objectInfos, pathToObjectApiNamesMap) {
10583
+ function injectFields(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
10540
10584
  /**
10541
10585
  * 1 parentship can return 2 FieldNode which need to be flattened
10542
10586
  * Concact: { ** Contact { ** ContactId {
@@ -10554,6 +10598,10 @@ function injectFields(selections, parentNode, ancestors, objectInfos, pathToObje
10554
10598
  if (!selection.selectionSet) {
10555
10599
  return selection;
10556
10600
  }
10601
+ const segment = isFieldNode(selection)
10602
+ ? selection.name.value
10603
+ : selection.typeCondition.name.value;
10604
+ const curPath = `${parentPath}#${segment}`;
10557
10605
  const spanningSubSelections = [];
10558
10606
  for (const subSelection of selection.selectionSet.selections) {
10559
10607
  if (isFieldSpanning(subSelection, selection)) {
@@ -10561,7 +10609,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, pathToObje
10561
10609
  }
10562
10610
  }
10563
10611
  // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
10564
- const subInjectedSelections = injectFields(spanningSubSelections, selection, ancestors, objectInfos, pathToObjectApiNamesMap);
10612
+ const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
10565
10613
  if (!selection.selectionSet) {
10566
10614
  return selection;
10567
10615
  }
@@ -10604,7 +10652,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, pathToObje
10604
10652
  }
10605
10653
  }
10606
10654
  // For polymorphic fields, the Id field is excluded.
10607
- const excludeId = selection.selectionSet.selections.every(isInlineFragmentNode);
10655
+ const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
10608
10656
  const idSelection = [];
10609
10657
  if (!excludeId && !hasIdAlready) {
10610
10658
  idSelection.push({
@@ -10615,8 +10663,8 @@ function injectFields(selections, parentNode, ancestors, objectInfos, pathToObje
10615
10663
  },
10616
10664
  });
10617
10665
  }
10618
- // Inject '__typename' for polymorphic fields. '__typename' field acts as a reference to concrete type of a polymorphic field and is used to match JSON response with AST node. For more detail,
10619
- // please reference 'removeSyntheticFields'.
10666
+ // 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
10667
+ // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
10620
10668
  if (isInlineFragmentNode(selection) &&
10621
10669
  !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
10622
10670
  idSelection.push({
@@ -10650,7 +10698,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, pathToObje
10650
10698
  if (isFieldNode(parentNode) && parentNode.selectionSet && parentNode.name.value === 'node') {
10651
10699
  if (parentNode.selectionSet.selections
10652
10700
  .filter(isFieldOrInlineFragmentNode)
10653
- .some((selectionNode) => isRelationship(selectionNode, 'childRelationship'))) {
10701
+ .some((selectionNode) => isRelationship(selectionNode, CHILD_RELATIONSHIP))) {
10654
10702
  if (!parentNode.selectionSet.selections
10655
10703
  .filter(isFieldNode)
10656
10704
  .some((sibling) => sibling.name.value === 'Id')) {
@@ -10669,7 +10717,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, pathToObje
10669
10717
  if (parentInfo.parentIndex >= 0) {
10670
10718
  // example node { TimeSheetEntries { edges { node { Id }}}}
10671
10719
  const parent = parentInfo.node;
10672
- if (isRelationship(parent, 'childRelationship')) {
10720
+ if (isRelationship(parent, CHILD_RELATIONSHIP)) {
10673
10721
  const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
10674
10722
  // path : "TimeSheet"
10675
10723
  const grandParentPath = findAncesterPath(unVisitedAncestors);
@@ -10798,7 +10846,7 @@ const assignedToMeFragmentSelections = [
10798
10846
  },
10799
10847
  value: {
10800
10848
  kind: 'StringValue',
10801
- value: 'childRelationship',
10849
+ value: CHILD_RELATIONSHIP,
10802
10850
  block: false,
10803
10851
  },
10804
10852
  },
@@ -10890,7 +10938,7 @@ const assignedToMeFragmentSelections = [
10890
10938
  },
10891
10939
  value: {
10892
10940
  kind: 'StringValue',
10893
- value: 'parentRelationship',
10941
+ value: PARENT_RELATIONSHIP,
10894
10942
  block: false,
10895
10943
  },
10896
10944
  },
@@ -11027,7 +11075,9 @@ function handleNonArrayJsonProperty(selection, fieldName, jsonInput, jsonOutput)
11027
11075
  jsonOutput[fieldName] = null;
11028
11076
  return;
11029
11077
  }
11030
- jsonOutput[fieldName] = {};
11078
+ if (jsonOutput[fieldName] === undefined) {
11079
+ jsonOutput[fieldName] = {};
11080
+ }
11031
11081
  createUserJsonOutput(selection, jsonInput[fieldName], jsonOutput[fieldName]);
11032
11082
  }
11033
11083
  else {
@@ -17063,4 +17113,4 @@ register({
17063
17113
  });
17064
17114
 
17065
17115
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17066
- // version: 1.241.0-b14b649d4
17116
+ // version: 1.242.1-58f8f4bb1