@salesforce/lds-runtime-mobile 1.229.0-dev1 → 1.229.0-dev3

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 +247 -163
  2. package/package.json +18 -18
  3. package/sfdc/main.js +247 -163
package/dist/main.js CHANGED
@@ -552,6 +552,9 @@ function handleDurableStoreRejection(instrument) {
552
552
  }
553
553
 
554
554
  function isStoreEntryError(storeRecord) {
555
+ if (!storeRecord || typeof storeRecord !== 'object') {
556
+ return false;
557
+ }
555
558
  return storeRecord.__type === 'error';
556
559
  }
557
560
 
@@ -4260,8 +4263,8 @@ function rootRecordQuery(selection, input) {
4260
4263
  // If there is no metadata for this query or it somehow lacks a timestamp
4261
4264
  // skip setting the root timestamp
4262
4265
  if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
4263
- // subtract 10ms from timestamp to account for ingestion processing time
4264
- input.rootTimestamp = queryMetadata.ingestionTimestamp - 10;
4266
+ // subtract 1000ms from timestamp to account for ingestion processing time
4267
+ input.rootTimestamp = queryMetadata.ingestionTimestamp - 1000;
4265
4268
  }
4266
4269
  }
4267
4270
  return recordQuery(selection, alias, apiName, [], input);
@@ -8057,6 +8060,10 @@ function isOperationDefinitionNode(node) {
8057
8060
  return node.kind === 'OperationDefinition';
8058
8061
  }
8059
8062
 
8063
+ const POLYMORPHIC_PARENT_RELATIONSHIP = 'polymorphicParentRelationship';
8064
+ const PARENT_RELATIONSHIP = 'parentRelationship';
8065
+ const CHILD_RELATIONSHIP = 'childRelationship';
8066
+ const RECORD_QUERY = 'recordQuery';
8060
8067
  function requestsDraftsField(recordFieldNode) {
8061
8068
  if (!recordFieldNode.selectionSet)
8062
8069
  return false;
@@ -8072,18 +8079,41 @@ function isRecordQuery(recordQueryField) {
8072
8079
  directive.arguments
8073
8080
  .map((argument) => argument.value)
8074
8081
  .filter(isStringValueNode)
8075
- .some((categoryName) => categoryName.value === 'recordQuery'));
8082
+ .some((categoryName) => categoryName.value === RECORD_QUERY));
8076
8083
  });
8077
8084
  }
8078
8085
  return false;
8079
8086
  }
8080
- // finds field with 'recordQuery' and 'childRelationship' directive
8081
- function findNearestRecordQuery(ancestors) {
8082
- const recordQueryAncester = findNearestAncesterPath(ancestors, true).node;
8083
- return recordQueryAncester === undefined ? undefined : recordQueryAncester;
8087
+ // finds connection field with 'recordQuery' and 'childRelationship' directive.
8088
+ function findNearestConnection(ancestors) {
8089
+ const connectionAncestor = findNearestAncesterPath(ancestors, true).node;
8090
+ return connectionAncestor === undefined ? undefined : connectionAncestor;
8091
+ }
8092
+ // convinient method to find nearest connection with its path
8093
+ function findNearestConnectionWithPath(ancestors) {
8094
+ const closestAncestorPath = findNearestAncesterPath(ancestors, true);
8095
+ let connection = undefined;
8096
+ let connectionPath = undefined;
8097
+ if (closestAncestorPath.parentIndex > 0) {
8098
+ const connectionAncestor = closestAncestorPath.node;
8099
+ const connectionAncestors = ancestors.slice(0, closestAncestorPath.parentIndex);
8100
+ connection =
8101
+ connectionAncestor === undefined ? undefined : connectionAncestor;
8102
+ if (connection !== undefined) {
8103
+ const ancesterPath = findAncesterPath(connectionAncestors);
8104
+ connectionPath =
8105
+ ancesterPath === ''
8106
+ ? connection.name.value
8107
+ : `${ancesterPath}#${connection.name.value}`;
8108
+ }
8109
+ }
8110
+ return {
8111
+ connection,
8112
+ path: connectionPath,
8113
+ };
8084
8114
  }
8085
- // 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.
8086
- function findNearestAncesterPath(ancestors, recordQueryOnly) {
8115
+ // 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.
8116
+ function findNearestAncesterPath(ancestors, connectionOnly) {
8087
8117
  let recordQueryPath = { node: undefined, parentIndex: -1 };
8088
8118
  let relationship = '';
8089
8119
  for (let i = ancestors.length - 1; i >= 0; i--) {
@@ -8097,9 +8127,11 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
8097
8127
  continue;
8098
8128
  for (let arg of directive.arguments) {
8099
8129
  if (arg.value &&
8100
- (arg.value.value === 'recordQuery' ||
8101
- arg.value.value === 'childRelationship' ||
8102
- (!recordQueryOnly && arg.value.value === 'parentRelationship'))) {
8130
+ (arg.value.value === RECORD_QUERY ||
8131
+ arg.value.value === CHILD_RELATIONSHIP ||
8132
+ (!connectionOnly &&
8133
+ (arg.value.value === PARENT_RELATIONSHIP ||
8134
+ arg.value.value === POLYMORPHIC_PARENT_RELATIONSHIP)))) {
8103
8135
  recordQueryPath = { node: node, parentIndex: i };
8104
8136
  relationship = arg.value.value;
8105
8137
  break;
@@ -8114,17 +8146,19 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
8114
8146
  //checks if nearest ancester could be an inline fragment
8115
8147
  if (recordQueryPath.node !== undefined &&
8116
8148
  recordQueryPath.node.selectionSet &&
8117
- relationship === 'parentRelationship') {
8118
- //
8119
- if (recordQueryPath.node.selectionSet.selections.every(isInlineFragmentNode)) {
8120
- //
8121
- const inlineFragmentLoc = recordQueryPath.parentIndex + 2;
8122
- if (inlineFragmentLoc < ancestors.length && ancestors[inlineFragmentLoc]) {
8149
+ (relationship === PARENT_RELATIONSHIP || relationship === POLYMORPHIC_PARENT_RELATIONSHIP)) {
8150
+ // InlineFragment is usually 3 steps aways from its FieldNode parent within ancester hierarchy if it exists. The below search
8151
+ // is applied to adapt to future AST structure change
8152
+ let parentIndex = recordQueryPath.parentIndex + 1;
8153
+ while (parentIndex < ancestors.length) {
8154
+ if (isInlineFragmentNode(ancestors[parentIndex])) {
8123
8155
  recordQueryPath = {
8124
- node: ancestors[inlineFragmentLoc],
8125
- parentIndex: inlineFragmentLoc,
8156
+ node: ancestors[parentIndex],
8157
+ parentIndex,
8126
8158
  };
8159
+ break;
8127
8160
  }
8161
+ parentIndex++;
8128
8162
  }
8129
8163
  }
8130
8164
  return recordQueryPath;
@@ -8148,7 +8182,7 @@ function findAncesterPath(ancesters) {
8148
8182
  ? sectionPath
8149
8183
  : sectionPath === ''
8150
8184
  ? path
8151
- : `${sectionPath}_${path}`;
8185
+ : `${sectionPath}#${path}`;
8152
8186
  }
8153
8187
  }
8154
8188
  boundaryIndex = parentIndex;
@@ -8206,9 +8240,9 @@ function getRelation(node) {
8206
8240
  const relationships = args
8207
8241
  .map((arg) => arg.value)
8208
8242
  .filter(isStringValueNode)
8209
- .filter((valueNode) => valueNode.value === 'childRelationship' ||
8210
- valueNode.value === 'parentRelationship' ||
8211
- valueNode.value === 'polymorphicParentRelationship')
8243
+ .filter((valueNode) => valueNode.value === CHILD_RELATIONSHIP ||
8244
+ valueNode.value === PARENT_RELATIONSHIP ||
8245
+ valueNode.value === POLYMORPHIC_PARENT_RELATIONSHIP)
8212
8246
  .map((relationshipNode) => relationshipNode.value);
8213
8247
  if (relationships.length > 0) {
8214
8248
  return relationships[0];
@@ -8265,8 +8299,8 @@ function isFieldSpanning(node, parentNode) {
8265
8299
  */
8266
8300
  function isParentRelationship(node) {
8267
8301
  return (node &&
8268
- (isRelationship(node, 'parentRelationship') ||
8269
- isRelationship(node, 'polymorphicParentRelationship')));
8302
+ (isRelationship(node, PARENT_RELATIONSHIP) ||
8303
+ isRelationship(node, POLYMORPHIC_PARENT_RELATIONSHIP)));
8270
8304
  }
8271
8305
  /*
8272
8306
  checks if the InlineFragment spans
@@ -9099,8 +9133,8 @@ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
9099
9133
  const results = await query(sql, [key]);
9100
9134
  const [timestamp] = results.rows.map((row) => row[0]);
9101
9135
  if (timestamp !== null && typeof timestamp === 'number') {
9102
- //go back 10 ms to adjust for margin of error when top level query is stored and when raml objects are stored
9103
- ingestionTimestamp = timestamp - 10;
9136
+ //go back 1000 ms to adjust for margin of error when top level query is stored and when raml objects are stored
9137
+ ingestionTimestamp = timestamp - 1000;
9104
9138
  }
9105
9139
  }
9106
9140
  return ingestionTimestamp;
@@ -9149,26 +9183,20 @@ function generateRecordQueries(objectInfos) {
9149
9183
  let recordConnections = ``;
9150
9184
  const polymorphicFieldTypeNames = new Set();
9151
9185
  let typedScalars = new Set();
9186
+ let parentRelationshipFields = new Set();
9152
9187
  for (const objectInfo of values$1(objectInfos)) {
9153
9188
  const { apiName, childRelationships } = objectInfo;
9154
9189
  let fields = ``;
9155
9190
  typedScalars.add(`${apiName}_Filter`);
9156
9191
  typedScalars.add(`${apiName}_OrderBy`);
9157
- for (const childRelationship of childRelationships) {
9158
- const { childObjectApiName } = childRelationship;
9159
- // Only add the relationship if there is relevant objectinfos for it,
9160
- // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9161
- // the query.
9162
- if (objectInfos[childObjectApiName] !== undefined) {
9163
- fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
9164
- typedScalars.add(`${childObjectApiName}_Filter`);
9165
- typedScalars.add(`${childObjectApiName}_OrderBy`);
9166
- }
9167
- }
9168
9192
  for (const field of values$1(objectInfo.fields)) {
9169
9193
  if (!fieldsStaticallyAdded.includes(field.apiName)) {
9170
9194
  fields += `${field.apiName}: ${dataTypeToType(field.dataType, field.apiName)}\n`;
9171
9195
  }
9196
+ //handles parent relationship
9197
+ if (field.relationshipName === null) {
9198
+ continue;
9199
+ }
9172
9200
  // For spanning parent relationships with no union types
9173
9201
  if (field.referenceToInfos.length === 1) {
9174
9202
  const [relation] = field.referenceToInfos;
@@ -9176,11 +9204,13 @@ function generateRecordQueries(objectInfos) {
9176
9204
  // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9177
9205
  // the query.
9178
9206
  if (objectInfos[relation.apiName] !== undefined) {
9207
+ parentRelationshipFields.add(field.relationshipName);
9179
9208
  fields += `${field.relationshipName}: ${relation.apiName}\n`;
9180
9209
  }
9181
9210
  // For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
9182
9211
  }
9183
9212
  else if (field.referenceToInfos.length > 1) {
9213
+ parentRelationshipFields.add(field.relationshipName);
9184
9214
  fields += `${field.relationshipName}: Record\n`;
9185
9215
  for (const relation of field.referenceToInfos) {
9186
9216
  if (objectInfos[relation.apiName] !== undefined) {
@@ -9189,6 +9219,20 @@ function generateRecordQueries(objectInfos) {
9189
9219
  }
9190
9220
  }
9191
9221
  }
9222
+ // handles child relationship
9223
+ for (const childRelationship of childRelationships) {
9224
+ const { childObjectApiName } = childRelationship;
9225
+ // Only add the relationship if there is relevant objectinfos for it,
9226
+ // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9227
+ // the query.
9228
+ // If one field has both parent relationship and child relationship with the same name, the child relationship is ignored. This is how the server GQL has implemented as date of 08/07/2023
9229
+ if (objectInfos[childObjectApiName] !== undefined &&
9230
+ !parentRelationshipFields.has(childRelationship.relationshipName)) {
9231
+ fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
9232
+ typedScalars.add(`${childObjectApiName}_Filter`);
9233
+ typedScalars.add(`${childObjectApiName}_OrderBy`);
9234
+ }
9235
+ }
9192
9236
  recordQueries += `${apiName}(first: Int, where: ${apiName}_Filter, orderBy: ${apiName}_OrderBy, scope: SupportedScopes): ${apiName}Connection\n`;
9193
9237
  const isServiceAppointment = apiName === 'ServiceAppointment';
9194
9238
  recordConnections += /* GraphQL */ `
@@ -9348,7 +9392,7 @@ const parentRelationshipDirective = {
9348
9392
  },
9349
9393
  value: {
9350
9394
  kind: Kind.STRING,
9351
- value: 'parentRelationship',
9395
+ value: PARENT_RELATIONSHIP,
9352
9396
  block: false,
9353
9397
  },
9354
9398
  },
@@ -9362,8 +9406,8 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9362
9406
  // example 2 'ServiceAppointment' -> ['Owner']; 'Owner' -> ['User', 'Group']
9363
9407
  const objectNodeInfoTree = {};
9364
9408
  // save the field path to apiName map
9365
- // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account']; 'ServiceAppointment_Account_Owner' -> ['User']
9366
- const objectInfoApiMap = {};
9409
+ // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#ccount' -> ['Account']; 'ServiceAppointment#Account#Owner' -> ['User']
9410
+ const pathToObjectApiNamesMap = {};
9367
9411
  let startNodes = new Set();
9368
9412
  let totalNodes = new Set();
9369
9413
  let objectInfos = {};
@@ -9377,11 +9421,11 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9377
9421
  visit(originalAST, {
9378
9422
  Argument: {
9379
9423
  enter(node, key, parent, path, ancestors) {
9380
- const recordQueryNode = findNearestRecordQuery(ancestors);
9381
- if (!recordQueryNode)
9424
+ const { connection: recordConnectionNode, path: ancesterPath } = findNearestConnectionWithPath(ancestors);
9425
+ if (!recordConnectionNode || !ancesterPath)
9382
9426
  return;
9383
- if (!objectNodeInfoTree[recordQueryNode.name.value]) {
9384
- objectNodeInfoTree[recordQueryNode.name.value] = [];
9427
+ if (!objectNodeInfoTree[ancesterPath]) {
9428
+ objectNodeInfoTree[ancesterPath] = [];
9385
9429
  }
9386
9430
  switch (node.name.value) {
9387
9431
  case 'orderBy':
@@ -9389,12 +9433,12 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9389
9433
  if (node.value.kind !== 'ObjectValue') {
9390
9434
  return;
9391
9435
  }
9392
- totalNodes.add(recordQueryNode.name.value);
9436
+ totalNodes.add(ancesterPath);
9393
9437
  // 'childRelationship' node is not taken as the startNode of the 'NodeInfoTree' graph. The field scanning will construct the graph which lead here.
9394
- if (isRecordQuery(recordQueryNode)) {
9395
- startNodes.add(recordQueryNode.name.value);
9438
+ if (isRecordQuery(recordConnectionNode)) {
9439
+ startNodes.add(recordConnectionNode.name.value);
9396
9440
  }
9397
- growObjectFieldTree(objectNodeInfoTree, recordQueryNode.name.value, node.value, totalNodes, startNodes);
9441
+ growObjectFieldTree(objectNodeInfoTree, ancesterPath, node.value, totalNodes, startNodes);
9398
9442
  break;
9399
9443
  case 'scope':
9400
9444
  if (!isScopeArgumentNodeWithType(node, 'ASSIGNEDTOME', variables)) {
@@ -9409,17 +9453,16 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9409
9453
  name: 'ServiceResources',
9410
9454
  });
9411
9455
  }
9412
- if (objectNodeInfoTree['ServiceResources'] === undefined) {
9413
- objectNodeInfoTree['ServiceResources'] = [];
9414
- }
9415
- if (!objectNodeInfoTree['ServiceResources'].some((child) => child.name === 'ServiceResource')) {
9416
- objectNodeInfoTree['ServiceResources'].push({
9417
- relation: 'parent',
9418
- name: 'ServiceResource',
9419
- });
9456
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources'] === undefined) {
9457
+ objectNodeInfoTree['ServiceAppointment#ServiceResources'] = [
9458
+ {
9459
+ relation: 'parent',
9460
+ name: 'ServiceResource',
9461
+ },
9462
+ ];
9420
9463
  }
9421
- if (objectNodeInfoTree['ServiceResource'] === undefined) {
9422
- objectNodeInfoTree['ServiceResource'] = [];
9464
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] === undefined) {
9465
+ objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] = [];
9423
9466
  }
9424
9467
  break;
9425
9468
  default:
@@ -9433,7 +9476,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9433
9476
  return;
9434
9477
  if (!node.selectionSet)
9435
9478
  return;
9436
- const recordQueryField = findNearestRecordQuery(ancestors);
9479
+ const recordQueryField = findNearestConnection(ancestors);
9437
9480
  //only injects fields for 'recordQuery' field. ignores the 'childRelationship' field since it will be traversed as the child of the 'recordQuery'
9438
9481
  if (isRecordQuery(recordQueryField) && recordQueryField) {
9439
9482
  totalNodes.add(recordQueryField.name.value);
@@ -9444,21 +9487,21 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9444
9487
  },
9445
9488
  });
9446
9489
  if (objectInfoService && startNodes.size > 0) {
9447
- objectInfos = await resolveObjectInfos(objectNodeInfoTree, objectInfoApiMap, startNodes, objectInfoService);
9490
+ objectInfos = await resolveObjectInfos(objectNodeInfoTree, pathToObjectApiNamesMap, startNodes, objectInfoService);
9448
9491
  }
9449
9492
  // read pass; gather whats needed
9450
9493
  visit(originalAST, {
9451
9494
  Argument: {
9452
9495
  leave(node, key, parent, path, ancestors) {
9453
- const recordQueryField = findNearestRecordQuery(ancestors);
9496
+ const recordQueryField = findNearestConnection(ancestors);
9454
9497
  if (!recordQueryField)
9455
9498
  return;
9456
9499
  const ancestorPath = findAncesterPath(ancestors);
9457
9500
  if (!inlineFragmentSelections[ancestorPath]) {
9458
9501
  inlineFragmentSelections[ancestorPath] = [];
9459
9502
  }
9460
- const recordQueryApiName = objectInfoApiMap[ancestorPath]
9461
- ? objectInfoApiMap[ancestorPath][0]
9503
+ const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
9504
+ ? pathToObjectApiNamesMap[ancestorPath][0]
9462
9505
  : recordQueryField.name.value;
9463
9506
  // The record node acts as the reference. The duplicated field in the record node is not injected
9464
9507
  const recordReferenceNode = [recordQueryField]
@@ -9472,7 +9515,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9472
9515
  case 'scope':
9473
9516
  // Hanle 'MINE' field
9474
9517
  if (isScopeArgumentNodeWithType(node, 'MINE', variables)) {
9475
- if (isMineScopeAvailable(ancestorPath, objectInfoApiMap, objectInfos)) {
9518
+ if (isMineScopeAvailable(ancestorPath, pathToObjectApiNamesMap, objectInfos)) {
9476
9519
  // 'typeConditon' is added when the 'InlineFragmentNode' is appended at the write pass
9477
9520
  inlineFragmentSelections[ancestorPath].push(...mineFragmentSelections);
9478
9521
  }
@@ -9498,7 +9541,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9498
9541
  case 'where': {
9499
9542
  inlineFragmentSelections[ancestorPath] = [
9500
9543
  ...inlineFragmentSelections[ancestorPath],
9501
- ...injectFilter(node, idState, ancestorPath, objectInfos, objectInfoApiMap, draftFunctions, recordReferenceNode),
9544
+ ...injectFilter(node, idState, ancestorPath, false, objectInfos, pathToObjectApiNamesMap, draftFunctions, recordReferenceNode),
9502
9545
  ];
9503
9546
  break;
9504
9547
  }
@@ -9514,7 +9557,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9514
9557
  if (!node.selectionSet)
9515
9558
  return;
9516
9559
  // it could be 'recordQuery' or 'childRelationship'
9517
- const recordQueryField = findNearestRecordQuery(ancestors);
9560
+ const recordQueryField = findNearestConnection(ancestors);
9518
9561
  if (!recordQueryField)
9519
9562
  return;
9520
9563
  const ancestorPath = findAncesterPath(ancestors);
@@ -9526,7 +9569,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9526
9569
  spanningSelections.push(selection);
9527
9570
  }
9528
9571
  }
9529
- const injectedFields = injectFields(spanningSelections, node, ancestors, objectInfos, objectInfoApiMap);
9572
+ const injectedFields = injectFields(spanningSelections, node, ancestorPath, ancestors, objectInfos, pathToObjectApiNamesMap);
9530
9573
  const mergedInjectedFields = mergeSelectionNodes(inlineFragmentSelections[ancestorPath], injectedFields);
9531
9574
  inlineFragmentSelections[ancestorPath] = mergedInjectedFields;
9532
9575
  },
@@ -9539,7 +9582,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9539
9582
  // removes 'ServicesResources' query field node if 'assignedtome' scope shows up
9540
9583
  if (assignedtomeQueryFieldNode !== undefined &&
9541
9584
  node.name.value === 'ServiceResources') {
9542
- const serviceResourcesAncestor = findNearestRecordQuery(ancestors);
9585
+ const serviceResourcesAncestor = findNearestConnection(ancestors);
9543
9586
  if (serviceResourcesAncestor === assignedtomeQueryFieldNode) {
9544
9587
  return null;
9545
9588
  }
@@ -9548,7 +9591,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9548
9591
  return;
9549
9592
  if (!node.selectionSet)
9550
9593
  return;
9551
- const recordQueryField = findNearestRecordQuery(ancestors);
9594
+ const recordQueryField = findNearestConnection(ancestors);
9552
9595
  if (!recordQueryField)
9553
9596
  return;
9554
9597
  const ancestorPath = findAncesterPath(ancestors);
@@ -9557,8 +9600,8 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9557
9600
  return;
9558
9601
  //const recordQueryPath = findAncesterPath(ancestors);
9559
9602
  // 'apiName' has to be at index 0 since 'node' record type could only be of 'recordQuery' or 'childRelationship'. They can not have the 'InlineFragmentNode' as its children.
9560
- const recordQueryApiName = objectInfoApiMap[ancestorPath]
9561
- ? objectInfoApiMap[ancestorPath][0]
9603
+ const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
9604
+ ? pathToObjectApiNamesMap[ancestorPath][0]
9562
9605
  : recordQueryField.name.value;
9563
9606
  const nodeWithFragments = {
9564
9607
  ...node,
@@ -9595,7 +9638,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
9595
9638
  if (node.name.value === 'where') {
9596
9639
  const ancestorPath = findAncesterPath(ancestors);
9597
9640
  if (idState.paths.includes(ancestorPath)) {
9598
- const apiName = objectInfoApiMap[ancestorPath][0];
9641
+ const apiName = pathToObjectApiNamesMap[ancestorPath][0];
9599
9642
  const objectInfo = objectInfos[apiName];
9600
9643
  const swappedIdFilter = swapIdField(node.value, objectInfo, false, idState, draftFunctions);
9601
9644
  return {
@@ -9661,8 +9704,8 @@ function swapIdField(filterFields, objectInfo, swapped, idState, draftFunctions)
9661
9704
  };
9662
9705
  }
9663
9706
  }
9664
- function isMineScopeAvailable(apiNamePath, objectInfoApiMap, objectInfos) {
9665
- const apiName = objectInfoApiMap[apiNamePath];
9707
+ function isMineScopeAvailable(apiNamePath, pathToObjectApiNamesMap, objectInfos) {
9708
+ const apiName = pathToObjectApiNamesMap[apiNamePath];
9666
9709
  if (!apiName)
9667
9710
  return false;
9668
9711
  const objectInfo = objectInfos[apiName[0]];
@@ -9751,15 +9794,16 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
9751
9794
  }
9752
9795
  // example: 'Account'
9753
9796
  const childNode = objectFieldNode.name.value;
9797
+ const childNodepath = `${parentNode}#${childNode}`;
9754
9798
  if (!tree[parentNode].some((child) => child.name === childNode)) {
9755
9799
  tree[parentNode].push({
9756
9800
  relation: 'parent',
9757
9801
  name: childNode,
9758
9802
  });
9759
- totalNodes.add(childNode);
9803
+ totalNodes.add(childNodepath);
9760
9804
  }
9761
9805
  // recursively go to deeper level of filter.
9762
- growObjectFieldTree(tree, childNode, objectFieldNode.value, totalNodes, startNodes);
9806
+ growObjectFieldTree(tree, childNodepath, objectFieldNode.value, totalNodes, startNodes);
9763
9807
  }
9764
9808
  }
9765
9809
  }
@@ -9794,19 +9838,20 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
9794
9838
  }
9795
9839
  if (!tree[parentSectionPath].some((field) => field.name === fieldName)) {
9796
9840
  tree[parentSectionPath].push({
9797
- relation: relationType === 'parentRelationship' ||
9798
- relationType === 'polymorphicParentRelationship'
9841
+ relation: relationType === PARENT_RELATIONSHIP ||
9842
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
9799
9843
  ? 'parent'
9800
9844
  : 'child',
9801
9845
  name: fieldName,
9802
9846
  });
9803
- totalNodes.add(fieldName);
9847
+ totalNodes.add(`${parentSectionPath}#${fieldName}`);
9804
9848
  }
9805
9849
  if (entryNode.selectionSet && entryNode.selectionSet.selections) {
9806
9850
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
9807
9851
  // recursively build the traversal tree
9808
9852
  for (const child of childNodes) {
9809
- growFieldTree(tree, fieldName, child, entryNode, totalNodes, startNodes);
9853
+ const path = `${parentSectionPath}#${fieldName}`;
9854
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
9810
9855
  }
9811
9856
  }
9812
9857
  }
@@ -9836,23 +9881,23 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
9836
9881
  }
9837
9882
  if (!tree[parentSectionPath].some((field) => field.name === conditionName)) {
9838
9883
  tree[parentSectionPath].push({
9839
- relation: relationType === 'parentRelationship' ||
9840
- relationType === 'polymorphicParentRelationship'
9884
+ relation: relationType === PARENT_RELATIONSHIP ||
9885
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
9841
9886
  ? 'parent'
9842
9887
  : 'child',
9843
9888
  name: conditionName,
9844
9889
  });
9845
- totalNodes.add(conditionName);
9890
+ const path = `${parentSectionPath}#${conditionName}`;
9891
+ totalNodes.add(path);
9846
9892
  }
9847
9893
  }
9848
9894
  }
9849
9895
  // dive deep immediately for 'InlineFragment'
9850
9896
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
9897
+ const path = `${parentSectionPath}${entryNode.typeCondition ? '#' + entryNode.typeCondition.name.value : ''}`;
9851
9898
  // Navigates into InLineFragment
9852
9899
  for (const child of childNodes) {
9853
- growFieldTree(tree, entryNode.typeCondition
9854
- ? entryNode.typeCondition.name.value
9855
- : parentSectionPath, child, entryNode, totalNodes, startNodes);
9900
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
9856
9901
  }
9857
9902
  }
9858
9903
  }
@@ -9864,7 +9909,7 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
9864
9909
  * @param startNodes start nodes of the tree. It can be used to fetch ObjectInfo immediately
9865
9910
  * @param path
9866
9911
  */
9867
- async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes, objectInfoService) {
9912
+ async function resolveObjectInfos(objectInfotree, pathToObjectApiNamesMap, startNodes, objectInfoService) {
9868
9913
  let objectInfos;
9869
9914
  try {
9870
9915
  objectInfos = await objectInfoService.getObjectInfos(Array.from(startNodes));
@@ -9878,9 +9923,9 @@ async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes,
9878
9923
  throw new Error(`Unable to resolve ObjectInfo(s) for ${Array.from(startNodes)}`);
9879
9924
  }
9880
9925
  for (const startNode of startNodes) {
9881
- objectInfoApiMap[startNode] = [startNode];
9926
+ pathToObjectApiNamesMap[startNode] = [startNode];
9882
9927
  const children = objectInfotree[startNode];
9883
- const subObjectInfoMap = await fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfos, children, startNode, objectInfoService);
9928
+ const subObjectInfoMap = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfos, children, startNode, objectInfoService);
9884
9929
  objectInfos = { ...objectInfos, ...subObjectInfoMap };
9885
9930
  }
9886
9931
  return objectInfos;
@@ -9888,15 +9933,15 @@ async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes,
9888
9933
  // example 1: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Account']
9889
9934
  // example 2: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Owner'], this example has 2 apiName for the node 'Owner'
9890
9935
  // example 3: 'parentPath': 'ServiceAppointment_Owner', 'nodesAtSameLevel': ['User', 'Group']
9891
- async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap, nodesAtSameLevel, parentPath, objectInfoService) {
9892
- const objectInfoApiNames = objectInfoApiMap[parentPath];
9936
+ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfoMap, nodesAtSameLevel, parentPath, objectInfoService) {
9937
+ const objectInfoApiNames = pathToObjectApiNamesMap[parentPath];
9893
9938
  if (!objectInfoApiNames) {
9894
9939
  // eslint-disable-next-line
9895
9940
  throw new Error(`Object Info does not exist for ${parentPath}`);
9896
9941
  }
9897
9942
  const validObjectInfoNodes = [];
9898
9943
  let updatedObjectInfoMap = {};
9899
- // InlineFragment and polymorphic field support fits into this scenario ObjectInfoApiMap Entry: 'ServiceAppointment_Owner' -> ['User', 'Group']; ServiceAppointment_Owner_User' -> ['User']
9944
+ // InlineFragment and polymorphic field support fits into this scenario pathToObjectApiNamesMap Entry: 'ServiceAppointment#Owner' -> ['User', 'Group']; ServiceAppointment#Owner#User' -> ['User']
9900
9945
  if (objectInfoApiNames.length > 0 &&
9901
9946
  nodesAtSameLevel.length > 0 &&
9902
9947
  objectInfoApiNames.includes(nodesAtSameLevel[0].name)) {
@@ -9908,8 +9953,8 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
9908
9953
  // eslint-disable-next-line
9909
9954
  throw new Error(`Condition ${field.name} does not exists for ${parentPath}`);
9910
9955
  }
9911
- const path = `${parentPath}_${field.name}`;
9912
- objectInfoApiMap[path] = [field.name];
9956
+ const path = `${parentPath}#${field.name}`;
9957
+ pathToObjectApiNamesMap[path] = [field.name];
9913
9958
  }
9914
9959
  validObjectInfoNodes.push(...nodesAtSameLevel);
9915
9960
  updatedObjectInfoMap = { ...objectInfoMap };
@@ -9924,7 +9969,7 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
9924
9969
  let apiNames = [];
9925
9970
  for (const nodeInfo of nodesAtSameLevel) {
9926
9971
  const field = nodeInfo.name;
9927
- const path = `${parentPath}_${field}`;
9972
+ const path = `${parentPath}#${field}`;
9928
9973
  // Handle 'parentRelationship'
9929
9974
  if (nodeInfo.relation === 'parent') {
9930
9975
  const relationshipId = referenceIdFieldForRelationship(field);
@@ -9942,21 +9987,21 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
9942
9987
  }
9943
9988
  }
9944
9989
  // This is a polymorphic field
9945
- if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[field]) {
9990
+ if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[path]) {
9946
9991
  // Fields needs to expand and heterogenous entity ObjectInfo needs to be fetched
9947
- const referencedNodeInfos = objectInfotree[field];
9992
+ const referencedNodeInfos = objectInfotree[path];
9948
9993
  const requestedApiNames = referencedNodeInfos.map((referenceNodeInfo) => referenceNodeInfo.name);
9949
9994
  // 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.
9950
- if (requestedApiNames.length > 0 && objectInfotree[field]) {
9995
+ if (requestedApiNames.length > 0 && objectInfotree[path]) {
9951
9996
  fieldDefinition.referenceToInfos
9952
9997
  .filter((referenceToInfo) => requestedApiNames.includes(referenceToInfo.apiName))
9953
9998
  .forEach((ref) => {
9954
- if (!objectInfoApiMap[path]) {
9955
- objectInfoApiMap[path] = [];
9999
+ if (!pathToObjectApiNamesMap[path]) {
10000
+ pathToObjectApiNamesMap[path] = [];
9956
10001
  }
9957
10002
  // 'ServiceAppointment_Owner' ->['User', 'Group']
9958
- if (!objectInfoApiMap[path].includes(ref.apiName)) {
9959
- objectInfoApiMap[path].push(ref.apiName);
10003
+ if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
10004
+ pathToObjectApiNamesMap[path].push(ref.apiName);
9960
10005
  }
9961
10006
  if (!apiNames.includes(ref.apiName)) {
9962
10007
  apiNames.push(ref.apiName);
@@ -9966,11 +10011,11 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
9966
10011
  }
9967
10012
  else if (fieldDefinition.referenceToInfos.length === 1) {
9968
10013
  const ref = fieldDefinition.referenceToInfos[0];
9969
- if (!objectInfoApiMap[path]) {
9970
- objectInfoApiMap[path] = [];
10014
+ if (!pathToObjectApiNamesMap[path]) {
10015
+ pathToObjectApiNamesMap[path] = [];
9971
10016
  }
9972
- if (!objectInfoApiMap[path].includes(ref.apiName)) {
9973
- objectInfoApiMap[path].push(ref.apiName);
10017
+ if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
10018
+ pathToObjectApiNamesMap[path].push(ref.apiName);
9974
10019
  }
9975
10020
  if (!apiNames.includes(ref.apiName)) {
9976
10021
  apiNames.push(ref.apiName);
@@ -9981,11 +10026,11 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
9981
10026
  // handles 'childRelationship'
9982
10027
  const childRelationship = parentObjectInfo.childRelationships.find((childRelationship) => childRelationship.relationshipName === field);
9983
10028
  if (childRelationship) {
9984
- if (!objectInfoApiMap[path]) {
9985
- objectInfoApiMap[path] = [];
10029
+ if (!pathToObjectApiNamesMap[path]) {
10030
+ pathToObjectApiNamesMap[path] = [];
9986
10031
  }
9987
- if (!objectInfoApiMap[path].includes(childRelationship.childObjectApiName)) {
9988
- objectInfoApiMap[path].push(childRelationship.childObjectApiName);
10032
+ if (!pathToObjectApiNamesMap[path].includes(childRelationship.childObjectApiName)) {
10033
+ pathToObjectApiNamesMap[path].push(childRelationship.childObjectApiName);
9989
10034
  }
9990
10035
  if (!apiNames.includes(childRelationship.childObjectApiName)) {
9991
10036
  apiNames.push(childRelationship.childObjectApiName);
@@ -10007,10 +10052,10 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
10007
10052
  }
10008
10053
  for (const nodeInfo of validObjectInfoNodes) {
10009
10054
  const field = nodeInfo.name;
10010
- const subLevelFields = objectInfotree[field];
10011
- const path = `${parentPath}_${field}`;
10055
+ const path = `${parentPath}#${field}`;
10056
+ const subLevelFields = objectInfotree[path];
10012
10057
  if (subLevelFields && subLevelFields.length > 0) {
10013
- const subObjectInfos = await fetchObjectInfos(objectInfotree, objectInfoApiMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
10058
+ const subObjectInfos = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
10014
10059
  updatedObjectInfoMap = { ...updatedObjectInfoMap, ...subObjectInfos };
10015
10060
  }
10016
10061
  }
@@ -10025,27 +10070,29 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
10025
10070
  * 'path' and 'queryNode' is 1 level above the 'filterNode'
10026
10071
  * @param filterNode filter node which needs to be injected. For example, 'State' ObjectFieldNode within filter 'where: { State: { eq: "Nova Scotia" }}'
10027
10072
  * @param idState ID state will be updated to determine if the ID fields in AST need to be swapped. The swapping happens later.
10028
- * @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 'objectInfoApiMap', parent node is not an field of relationship or recordQuery
10073
+ * @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
10074
+ * @param isParentPolymorphic true if parent points to a polymorphic field.
10029
10075
  * @param queryNode referece FieldNode which provides the information if 'filterNode' exist in it nor not.
10030
10076
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
10031
- * @param objectInfoApiMap 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'].
10077
+ * @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'].
10032
10078
  * @param draftFunctions functions for working with record ids that may be draft-created ids
10033
10079
  * @returns an array of nodes with injected fields
10034
10080
  */
10035
- function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode) {
10081
+ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode) {
10036
10082
  const injectedSelections = [];
10083
+ let isPolymorphicField = false;
10037
10084
  switch (filterNode.kind) {
10038
10085
  case Kind.ARGUMENT:
10039
10086
  if (filterNode.value.kind !== 'ObjectValue')
10040
10087
  return [];
10041
10088
  filterNode.value.fields.forEach((objectFieldNode) => {
10042
- let subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
10089
+ let subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10043
10090
  for (const subResult of subResults) {
10044
10091
  mergeOrAddToGroup(injectedSelections, subResult);
10045
10092
  }
10046
10093
  // multiple Ids might need to be swapped. remember their paths for faster write.
10047
10094
  if (idState.swapNeeded) {
10048
- idState.paths.push(path);
10095
+ idState.paths.push(parentPath);
10049
10096
  }
10050
10097
  });
10051
10098
  return injectedSelections;
@@ -10054,7 +10101,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
10054
10101
  case Kind.LIST: {
10055
10102
  filterNode.value.values.filter(isObjectValueNode).forEach((objectValueNode) => {
10056
10103
  objectValueNode.fields.forEach((objectFieldNode) => {
10057
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
10104
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10058
10105
  for (const subResult of subResults) {
10059
10106
  mergeOrAddToGroup(injectedSelections, subResult);
10060
10107
  }
@@ -10065,7 +10112,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
10065
10112
  case Kind.OBJECT: {
10066
10113
  if (filterNode.name.value === 'not') {
10067
10114
  filterNode.value.fields.forEach((objectFieldNode) => {
10068
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
10115
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
10069
10116
  for (const subResult of subResults) {
10070
10117
  mergeOrAddToGroup(injectedSelections, subResult);
10071
10118
  }
@@ -10075,15 +10122,15 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
10075
10122
  let apiNames = [];
10076
10123
  let isScalarField = false;
10077
10124
  //It is possible that this is a polymorphic field
10078
- apiNames = objectInfoApiMap[path];
10079
- // example: path: 'ServiceAppointment_LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
10125
+ apiNames = pathToObjectApiNamesMap[parentPath];
10126
+ // example: path: 'ServiceAppointment#LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
10080
10127
  if (apiNames === undefined) {
10081
10128
  isScalarField = true;
10082
10129
  }
10083
10130
  else {
10084
10131
  if (apiNames.some((apiName) => objectInfos[apiName] === undefined)) {
10085
10132
  // eslint-disable-next-line
10086
- throw new Error(`ObjectInfo is missing for ${path}`);
10133
+ throw new Error(`ObjectInfo is missing for ${parentPath}`);
10087
10134
  }
10088
10135
  }
10089
10136
  if (isScalarField) {
@@ -10105,29 +10152,19 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
10105
10152
  }
10106
10153
  });
10107
10154
  let isSpanning = false;
10155
+ // if true, current node is a polymorphic concrete type node. For example, field node `User` under `Owner`
10108
10156
  let isInlineFragment = false;
10109
- let isPolymorphicField = false;
10110
10157
  let isTypeNameExisting = false;
10111
10158
  let curPath;
10112
10159
  let fieldName = filterNode.name.value;
10113
- curPath = `${path}_${fieldName}`;
10114
- if (objectInfoApiMap[curPath] && objectInfoApiMap[curPath].length > 0) {
10160
+ curPath = `${parentPath}#${fieldName}`;
10161
+ if (pathToObjectApiNamesMap[curPath] &&
10162
+ pathToObjectApiNamesMap[curPath].length > 0) {
10115
10163
  isSpanning = true;
10116
- if (objectInfoApiMap[curPath].length === 1) {
10117
- if (objectInfoApiMap[path] &&
10118
- objectInfoApiMap[path].length >= 1 &&
10119
- objectInfoApiMap[path].includes(objectInfoApiMap[curPath][0])) {
10120
- isInlineFragment = true;
10121
- }
10122
- }
10123
- // Checks if the current filter node is a polymorphic field. 'ServiceAppointment_Owner' --> ['User']; 'ServiceAppointment_Owner_User' --> ['User']
10124
- const childApiName = objectInfoApiMap[curPath][0];
10125
- const trialApiNames = objectInfoApiMap[`${curPath}_${childApiName}`];
10126
- if (trialApiNames !== undefined &&
10127
- trialApiNames.length === 1 &&
10128
- trialApiNames[0] === childApiName) {
10129
- isPolymorphicField = true;
10130
- }
10164
+ isInlineFragment =
10165
+ isParentPolymorphic &&
10166
+ pathToObjectApiNamesMap[curPath].length === 1;
10167
+ isPolymorphicField = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
10131
10168
  }
10132
10169
  // 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]
10133
10170
  if (isInlineFragment) {
@@ -10171,9 +10208,9 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
10171
10208
  throw new Error(`Field ${fieldName} does not exist in ${apiNames[0]}`);
10172
10209
  }
10173
10210
  }
10174
- const objectInfoName = objectInfoApiMap[curPath] !== undefined
10175
- ? objectInfoApiMap[curPath][0]
10176
- : objectInfoApiMap[path][0];
10211
+ const objectInfoName = pathToObjectApiNamesMap[curPath] !== undefined
10212
+ ? pathToObjectApiNamesMap[curPath][0]
10213
+ : pathToObjectApiNamesMap[parentPath][0];
10177
10214
  const isIdField = isFieldAnIdField(filterNode.name.value, objectInfos[objectInfoName]);
10178
10215
  if (!isIdField) {
10179
10216
  let subSelectionNodes = [];
@@ -10185,7 +10222,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
10185
10222
  updateIDInfo(subFieldNode, idState, draftFunctions);
10186
10223
  }
10187
10224
  // try injecting the fields within predicate no matter it has relation or not.
10188
- let subResults = injectFilter(subFieldNode, idState, curPath, objectInfos, objectInfoApiMap, draftFunctions, existingFields ? existingFields[0] : undefined);
10225
+ let subResults = injectFilter(subFieldNode, idState, curPath, isPolymorphicField, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
10189
10226
  subSelectionNodes = subSelectionNodes.concat(subResults);
10190
10227
  });
10191
10228
  if (!subFieldsHasId) {
@@ -10346,6 +10383,44 @@ function mergeOrAddToGroup(group, element) {
10346
10383
  }
10347
10384
  group.push(element);
10348
10385
  }
10386
+ // checks if the path points to a polymorphic field. For example, for the below `pathToObjectApiNamesMap`
10387
+ // {
10388
+ // 'ServiceAppointment' -> ['ServiceAppointment']
10389
+ // 'ServiceAppointment#Owner' --> ['User'],
10390
+ // 'ServiceAppointment#Owner#User' --> ['User']
10391
+ // }
10392
+ // path `ServiceAppointment#Owner` points to a polymorphic field, but path `ServiceAppointment#Owner#User` does not.
10393
+ function isPolymorphicFieldPath(path, pathToObjectApiNamesMap, objectInfos) {
10394
+ const lastSegmentIndex = path.lastIndexOf('#');
10395
+ if (lastSegmentIndex < 0) {
10396
+ return false;
10397
+ }
10398
+ const lastSegment = path.slice(lastSegmentIndex + 1);
10399
+ const parentApiPath = path.slice(0, lastSegmentIndex);
10400
+ if (pathToObjectApiNamesMap[parentApiPath] === undefined) {
10401
+ return false;
10402
+ }
10403
+ const parentObjectApiNames = pathToObjectApiNamesMap[parentApiPath];
10404
+ // 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`.
10405
+ // 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.
10406
+ // Below are the entries in `pathToObjectApiNamesMap`
10407
+ // {
10408
+ // `ServiceAppointmen`t: [`ServiceAppointment`],
10409
+ // `ServiceAppointment#Owner`: [`User`, `Group`],
10410
+ // `ServiceAppointment#Owner#User`: [`User`],
10411
+ // `ServiceAppointment#Owner#Group`: [`Group`],
10412
+ // }
10413
+ if (parentObjectApiNames.length !== 1) {
10414
+ return false;
10415
+ }
10416
+ const parentObjectInfo = objectInfos[parentObjectApiNames[0]];
10417
+ const relationshipField = referenceIdFieldForRelationship(lastSegment);
10418
+ let fieldDefinition = parentObjectInfo.fields[relationshipField];
10419
+ if (fieldDefinition === undefined) {
10420
+ return false;
10421
+ }
10422
+ return fieldDefinition.polymorphicForeignKey;
10423
+ }
10349
10424
  function isFieldAnIdField(fieldName, objectInfo) {
10350
10425
  if (fieldName === 'Id')
10351
10426
  return true;
@@ -10398,10 +10473,10 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
10398
10473
  * @param parentNode parent node of param 1
10399
10474
  * @param ancestors ancester of param 1
10400
10475
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
10401
- * @param objectInfoApiMap 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'].
10476
+ * @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'].
10402
10477
  * @return injected SelectionNodes used to construct the InlineFragment.
10403
10478
  */
10404
- function injectFields(selections, parentNode, ancestors, objectInfos, objectInfoApiMap) {
10479
+ function injectFields(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
10405
10480
  /**
10406
10481
  * 1 parentship can return 2 FieldNode which need to be flattened
10407
10482
  * Concact: { ** Contact { ** ContactId {
@@ -10419,6 +10494,10 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
10419
10494
  if (!selection.selectionSet) {
10420
10495
  return selection;
10421
10496
  }
10497
+ const segment = isFieldNode(selection)
10498
+ ? selection.name.value
10499
+ : selection.typeCondition.name.value;
10500
+ const curPath = `${parentPath}#${segment}`;
10422
10501
  const spanningSubSelections = [];
10423
10502
  for (const subSelection of selection.selectionSet.selections) {
10424
10503
  if (isFieldSpanning(subSelection, selection)) {
@@ -10426,7 +10505,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
10426
10505
  }
10427
10506
  }
10428
10507
  // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
10429
- const subInjectedSelections = injectFields(spanningSubSelections, selection, ancestors, objectInfos, objectInfoApiMap);
10508
+ const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
10430
10509
  if (!selection.selectionSet) {
10431
10510
  return selection;
10432
10511
  }
@@ -10469,7 +10548,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
10469
10548
  }
10470
10549
  }
10471
10550
  // For polymorphic fields, the Id field is excluded.
10472
- const excludeId = selection.selectionSet.selections.every(isInlineFragmentNode);
10551
+ const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
10473
10552
  const idSelection = [];
10474
10553
  if (!excludeId && !hasIdAlready) {
10475
10554
  idSelection.push({
@@ -10480,8 +10559,8 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
10480
10559
  },
10481
10560
  });
10482
10561
  }
10483
- // 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,
10484
- // please reference 'removeSyntheticFields'.
10562
+ // 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
10563
+ // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
10485
10564
  if (isInlineFragmentNode(selection) &&
10486
10565
  !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
10487
10566
  idSelection.push({
@@ -10515,7 +10594,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
10515
10594
  if (isFieldNode(parentNode) && parentNode.selectionSet && parentNode.name.value === 'node') {
10516
10595
  if (parentNode.selectionSet.selections
10517
10596
  .filter(isFieldOrInlineFragmentNode)
10518
- .some((selectionNode) => isRelationship(selectionNode, 'childRelationship'))) {
10597
+ .some((selectionNode) => isRelationship(selectionNode, CHILD_RELATIONSHIP))) {
10519
10598
  if (!parentNode.selectionSet.selections
10520
10599
  .filter(isFieldNode)
10521
10600
  .some((sibling) => sibling.name.value === 'Id')) {
@@ -10534,15 +10613,15 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
10534
10613
  if (parentInfo.parentIndex >= 0) {
10535
10614
  // example node { TimeSheetEntries { edges { node { Id }}}}
10536
10615
  const parent = parentInfo.node;
10537
- if (isRelationship(parent, 'childRelationship')) {
10616
+ if (isRelationship(parent, CHILD_RELATIONSHIP)) {
10538
10617
  const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
10539
10618
  // path : "TimeSheet"
10540
10619
  const grandParentPath = findAncesterPath(unVisitedAncestors);
10541
- if (objectInfoApiMap &&
10542
- objectInfoApiMap[grandParentPath] &&
10620
+ if (pathToObjectApiNamesMap &&
10621
+ pathToObjectApiNamesMap[grandParentPath] &&
10543
10622
  objectInfos &&
10544
- objectInfos[objectInfoApiMap[grandParentPath][0]]) {
10545
- const grandParentObjectInfo = objectInfos[objectInfoApiMap[grandParentPath][0]];
10623
+ objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
10624
+ const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
10546
10625
  // exmaple "TimeSheetEntries"
10547
10626
  const parentFieldName = parent.name.value;
10548
10627
  const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
@@ -10663,7 +10742,7 @@ const assignedToMeFragmentSelections = [
10663
10742
  },
10664
10743
  value: {
10665
10744
  kind: 'StringValue',
10666
- value: 'childRelationship',
10745
+ value: CHILD_RELATIONSHIP,
10667
10746
  block: false,
10668
10747
  },
10669
10748
  },
@@ -10755,7 +10834,7 @@ const assignedToMeFragmentSelections = [
10755
10834
  },
10756
10835
  value: {
10757
10836
  kind: 'StringValue',
10758
- value: 'parentRelationship',
10837
+ value: PARENT_RELATIONSHIP,
10759
10838
  block: false,
10760
10839
  },
10761
10840
  },
@@ -10892,7 +10971,9 @@ function handleNonArrayJsonProperty(selection, fieldName, jsonInput, jsonOutput)
10892
10971
  jsonOutput[fieldName] = null;
10893
10972
  return;
10894
10973
  }
10895
- jsonOutput[fieldName] = {};
10974
+ if (jsonOutput[fieldName] === undefined) {
10975
+ jsonOutput[fieldName] = {};
10976
+ }
10896
10977
  createUserJsonOutput(selection, jsonInput[fieldName], jsonOutput[fieldName]);
10897
10978
  }
10898
10979
  else {
@@ -15900,6 +15981,9 @@ class PrimingSession extends EventEmitter {
15900
15981
  this.ldsRecordRefresher = config.ldsRecordRefresher;
15901
15982
  this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
15902
15983
  this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
15984
+ if (this.useBatchGQL) {
15985
+ this.batchSize = this.batchSize / DEFAULT_GQL_QUERY_BATCH_SIZE;
15986
+ }
15903
15987
  this.conflictPool = new ConflictPool(config.store, this.objectInfoLoader);
15904
15988
  }
15905
15989
  // function that enqueues priming work
@@ -16761,4 +16845,4 @@ register({
16761
16845
  });
16762
16846
 
16763
16847
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
16764
- // version: 1.229.0-dev1-f69d054a9
16848
+ // version: 1.229.0-dev3-175ac936b