@salesforce/lds-runtime-mobile 1.249.0 → 1.250.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/main.js +66 -15
  2. package/package.json +1 -1
  3. package/sfdc/main.js +66 -15
package/dist/main.js CHANGED
@@ -4378,7 +4378,7 @@ function rootQuery(recordNodes, input) {
4378
4378
  if (fails.length > 0) {
4379
4379
  return failure(fails);
4380
4380
  }
4381
- return success({ type: 'root', connections });
4381
+ return success({ type: 'root', connections, queryKeys: input.queryKeys });
4382
4382
  }
4383
4383
  /**
4384
4384
  * Given a connection array of LuvioSelectionCustomFieldNode
@@ -4598,11 +4598,11 @@ class StoreEvalPreconditioner {
4598
4598
  // require at least this top level record present to resolve relationship lookups
4599
4599
  const recordSelections = findRecordSelections(ast);
4600
4600
  let metadata = {};
4601
+ const queryKeys = recordSelections.map((rs) => connectionKeyBuilder(rs, variables));
4601
4602
  if (excludeStaleRecordsGate.isOpen({ fallback: false })) {
4602
- const keys = recordSelections.map((rs) => connectionKeyBuilder(rs, variables));
4603
- let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${keys
4603
+ let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${queryKeys
4604
4604
  .map(() => '?')
4605
- .join(',')})`, keys);
4605
+ .join(',')})`, queryKeys);
4606
4606
  metadata = sqlResult.rows.reduce((metadata, row) => {
4607
4607
  metadata[row[0]] = JSON.parse(row[1]);
4608
4608
  return metadata;
@@ -4654,6 +4654,7 @@ class StoreEvalPreconditioner {
4654
4654
  draftFunctions,
4655
4655
  connectionKeyBuilder,
4656
4656
  metadata,
4657
+ queryKeys,
4657
4658
  });
4658
4659
  if (astTransformResult.isSuccess === false) {
4659
4660
  for (const error of astTransformResult.error) {
@@ -4725,6 +4726,11 @@ async function evaluateSqlite(query, eventEmitter, store) {
4725
4726
  eventEmitter({ type: 'graphql-db-read', sql, bindings, duration: Date.now() - start });
4726
4727
  const data = JSON.parse(rawValue);
4727
4728
  const seenRecords = createSeenRecords$1(data);
4729
+ if (query.queryKeys) {
4730
+ for (const queryKey of query.queryKeys) {
4731
+ seenRecords.add(queryKey);
4732
+ }
4733
+ }
4728
4734
  return { data, seenRecords };
4729
4735
  }
4730
4736
  const wrapStartEndEvents = (storeEval) => {
@@ -6948,6 +6954,20 @@ function buildQueryTypeStringKey(args) {
6948
6954
  return `${keyPrefix}::${schemaName}::${queryTypeName}[${serializeOperationNode(operationNode, variables, fragmentMap)}]`;
6949
6955
  }
6950
6956
 
6957
+ /**
6958
+ * @description Spec compliant way to retrieve the correct Operation from the Document that Luvio should operate on. https://spec.graphql.org/June2018/#sec-Named-Operation-Definitions
6959
+ * @param document
6960
+ * @param operationName
6961
+ * @returns The Operation in the GraphQL document we should use for the current call.
6962
+ */
6963
+ function getOperationFromDocument(document, operationName) {
6964
+ const operations = document.definitions.filter((def) => def.kind === 'OperationDefinition');
6965
+ if (operationName) {
6966
+ return operations.find((def) => def.name !== undefined && def.name.value === operationName);
6967
+ }
6968
+ return operations[0]; // If a named operation is not provided, we return the first one
6969
+ }
6970
+
6951
6971
  /**
6952
6972
  * Copyright (c) 2022, Salesforce, Inc.,
6953
6973
  * All rights reserved.
@@ -9553,9 +9573,9 @@ function extendExistingRecordType(schema, type, objectInfo, objectInfoMap) {
9553
9573
  const existingFields = keys$4(type.getFields());
9554
9574
  const missingFields = values$2(objectInfo.fields).filter((field) => {
9555
9575
  return (existingFields.includes(field.apiName) === false ||
9556
- (field.relationshipName !== null && field.referenceToInfos.length > 1));
9576
+ (field.relationshipName !== null && field.referenceToInfos.length > 0));
9557
9577
  });
9558
- const { fields, polymorphicFieldTypeNames } = makeRecordField(missingFields, objectInfoMap, parentRelationshipFields, 'Cached');
9578
+ const { fields, polymorphicFieldTypeNames } = makeRecordField(missingFields, objectInfoMap, parentRelationshipFields, 'Cached', existingFields);
9559
9579
  const { apiName, childRelationships } = objectInfo;
9560
9580
  // handles child relationship
9561
9581
  const { spanningRecordConnections, typedScalars: spanningConnectionTypedScalars } = makeSpanningRecordConnections(schema, childRelationships, objectInfoMap, parentRelationshipFields, existingFields);
@@ -9630,7 +9650,7 @@ function makeSpanningRecordConnections(schema, childRelationships, objectInfoMap
9630
9650
  * @param recordTypeInSchema
9631
9651
  * @returns
9632
9652
  */
9633
- function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema) {
9653
+ function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema, existingFields = []) {
9634
9654
  const polymorphicFieldTypeNames = new Set();
9635
9655
  let fields = ``;
9636
9656
  for (const field of values$2(fieldRepresentations)) {
@@ -9647,7 +9667,8 @@ function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRela
9647
9667
  // Only add the relationship if there is relevant objectinfos for it,
9648
9668
  // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9649
9669
  // the query.
9650
- if (objectInfoMap[relation.apiName] !== undefined) {
9670
+ if (objectInfoMap[relation.apiName] !== undefined &&
9671
+ existingFields.includes(field.relationshipName) === false) {
9651
9672
  existingParentRelationships.add(field.relationshipName);
9652
9673
  fields += `${field.relationshipName}: ${relation.apiName}\n`;
9653
9674
  }
@@ -9720,6 +9741,8 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
9720
9741
  // this is only wrapped in a try to execute the event after the result was returned
9721
9742
  try {
9722
9743
  eventEmitter({ type: 'graphql-eval-start' });
9744
+ const operationNode = getOperationFromDocument(config.query);
9745
+ let topLevelQueries = [];
9723
9746
  // assume that 'config.query' has required injected fields.
9724
9747
  const modifiedAST = visit(config.query, {
9725
9748
  Field: {
@@ -9749,6 +9772,25 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
9749
9772
  };
9750
9773
  },
9751
9774
  },
9775
+ Directive: {
9776
+ enter(node, _key, _parent, _path, ancester) {
9777
+ if (node.name.value === 'category' && node.arguments !== undefined) {
9778
+ for (let i = 0, len = node.arguments.length; i < len; i++) {
9779
+ const argument = node.arguments[i];
9780
+ if (isStringValueNode(argument.value) &&
9781
+ argument.value.value === 'recordQuery') {
9782
+ const parentNode = ancester[ancester.length - 1];
9783
+ if (isFieldNode(parentNode)) {
9784
+ topLevelQueries.push({
9785
+ argumentNodes: parentNode.arguments || [],
9786
+ recordName: parentNode.name.value,
9787
+ });
9788
+ }
9789
+ }
9790
+ }
9791
+ }
9792
+ },
9793
+ },
9752
9794
  });
9753
9795
  eventEmitter({ type: 'graphql-preconditions-met' });
9754
9796
  // create the resolver request context, runtime values and functions for
@@ -9767,7 +9809,18 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
9767
9809
  rootValue: {},
9768
9810
  }));
9769
9811
  eventEmitter({ type: 'graphql-evaluated' });
9770
- return { result, seenRecordIds: [...contextValue.seenRecordIds] };
9812
+ // add record key to seen ids
9813
+ const seenRecordIds = [...contextValue.seenRecordIds].map((id) => `UiApi::RecordRepresentation:${id}`);
9814
+ // if we have all the data to build the top level query
9815
+ // add it to the seen ids
9816
+ if (operationNode !== undefined && topLevelQueries.length > 0) {
9817
+ topLevelQueries.forEach((query) => {
9818
+ const { recordName, argumentNodes } = query;
9819
+ const queryString = buildKeyStringForRecordQuery(operationNode, config.variables || {}, argumentNodes, recordName);
9820
+ seenRecordIds.push(queryString);
9821
+ });
9822
+ }
9823
+ return { result, seenRecordIds };
9771
9824
  }
9772
9825
  finally {
9773
9826
  eventEmitter({ type: 'graphql-eval-end' });
@@ -13147,14 +13200,12 @@ function isLocalEvalSnapshot(snapshot) {
13147
13200
  return 'rebuildWithLocalEval' in snapshot;
13148
13201
  }
13149
13202
  function createSeenRecords(ids, currentSnapshot) {
13150
- let seenRecords = ids
13151
- .map((id) => `UiApi::RecordRepresentation:${id}`)
13152
- .reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
13203
+ let seenRecords = ids.reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
13153
13204
  if (currentSnapshot.state !== 'Error') {
13154
13205
  currentSnapshot.seenRecords.forEach((record) => {
13155
13206
  let keyString = typeof record !== 'string' ? serializeStructuredKey(record) : record;
13156
- if (keyString !== 'UiApi::GraphQLRepresentation__uiapi' &&
13157
- keyString !== 'UiApi::GraphQLRepresentation__uiapi__query' &&
13207
+ if (keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi' &&
13208
+ keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi__query' &&
13158
13209
  seenRecords.has(record) === false) {
13159
13210
  seenRecords.add(record);
13160
13211
  }
@@ -17508,4 +17559,4 @@ register({
17508
17559
  });
17509
17560
 
17510
17561
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17511
- // version: 1.249.0-11c3e1ed5
17562
+ // version: 1.250.0-9df9bc3d1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.249.0",
3
+ "version": "1.250.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
package/sfdc/main.js CHANGED
@@ -4378,7 +4378,7 @@ function rootQuery(recordNodes, input) {
4378
4378
  if (fails.length > 0) {
4379
4379
  return failure(fails);
4380
4380
  }
4381
- return success({ type: 'root', connections });
4381
+ return success({ type: 'root', connections, queryKeys: input.queryKeys });
4382
4382
  }
4383
4383
  /**
4384
4384
  * Given a connection array of LuvioSelectionCustomFieldNode
@@ -4598,11 +4598,11 @@ class StoreEvalPreconditioner {
4598
4598
  // require at least this top level record present to resolve relationship lookups
4599
4599
  const recordSelections = findRecordSelections(ast);
4600
4600
  let metadata = {};
4601
+ const queryKeys = recordSelections.map((rs) => connectionKeyBuilder(rs, variables));
4601
4602
  if (excludeStaleRecordsGate.isOpen({ fallback: false })) {
4602
- const keys = recordSelections.map((rs) => connectionKeyBuilder(rs, variables));
4603
- let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${keys
4603
+ let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${queryKeys
4604
4604
  .map(() => '?')
4605
- .join(',')})`, keys);
4605
+ .join(',')})`, queryKeys);
4606
4606
  metadata = sqlResult.rows.reduce((metadata, row) => {
4607
4607
  metadata[row[0]] = JSON.parse(row[1]);
4608
4608
  return metadata;
@@ -4654,6 +4654,7 @@ class StoreEvalPreconditioner {
4654
4654
  draftFunctions,
4655
4655
  connectionKeyBuilder,
4656
4656
  metadata,
4657
+ queryKeys,
4657
4658
  });
4658
4659
  if (astTransformResult.isSuccess === false) {
4659
4660
  for (const error of astTransformResult.error) {
@@ -4725,6 +4726,11 @@ async function evaluateSqlite(query, eventEmitter, store) {
4725
4726
  eventEmitter({ type: 'graphql-db-read', sql, bindings, duration: Date.now() - start });
4726
4727
  const data = JSON.parse(rawValue);
4727
4728
  const seenRecords = createSeenRecords$1(data);
4729
+ if (query.queryKeys) {
4730
+ for (const queryKey of query.queryKeys) {
4731
+ seenRecords.add(queryKey);
4732
+ }
4733
+ }
4728
4734
  return { data, seenRecords };
4729
4735
  }
4730
4736
  const wrapStartEndEvents = (storeEval) => {
@@ -6948,6 +6954,20 @@ function buildQueryTypeStringKey(args) {
6948
6954
  return `${keyPrefix}::${schemaName}::${queryTypeName}[${serializeOperationNode(operationNode, variables, fragmentMap)}]`;
6949
6955
  }
6950
6956
 
6957
+ /**
6958
+ * @description Spec compliant way to retrieve the correct Operation from the Document that Luvio should operate on. https://spec.graphql.org/June2018/#sec-Named-Operation-Definitions
6959
+ * @param document
6960
+ * @param operationName
6961
+ * @returns The Operation in the GraphQL document we should use for the current call.
6962
+ */
6963
+ function getOperationFromDocument(document, operationName) {
6964
+ const operations = document.definitions.filter((def) => def.kind === 'OperationDefinition');
6965
+ if (operationName) {
6966
+ return operations.find((def) => def.name !== undefined && def.name.value === operationName);
6967
+ }
6968
+ return operations[0]; // If a named operation is not provided, we return the first one
6969
+ }
6970
+
6951
6971
  /**
6952
6972
  * Copyright (c) 2022, Salesforce, Inc.,
6953
6973
  * All rights reserved.
@@ -9553,9 +9573,9 @@ function extendExistingRecordType(schema, type, objectInfo, objectInfoMap) {
9553
9573
  const existingFields = keys$4(type.getFields());
9554
9574
  const missingFields = values$2(objectInfo.fields).filter((field) => {
9555
9575
  return (existingFields.includes(field.apiName) === false ||
9556
- (field.relationshipName !== null && field.referenceToInfos.length > 1));
9576
+ (field.relationshipName !== null && field.referenceToInfos.length > 0));
9557
9577
  });
9558
- const { fields, polymorphicFieldTypeNames } = makeRecordField(missingFields, objectInfoMap, parentRelationshipFields, 'Cached');
9578
+ const { fields, polymorphicFieldTypeNames } = makeRecordField(missingFields, objectInfoMap, parentRelationshipFields, 'Cached', existingFields);
9559
9579
  const { apiName, childRelationships } = objectInfo;
9560
9580
  // handles child relationship
9561
9581
  const { spanningRecordConnections, typedScalars: spanningConnectionTypedScalars } = makeSpanningRecordConnections(schema, childRelationships, objectInfoMap, parentRelationshipFields, existingFields);
@@ -9630,7 +9650,7 @@ function makeSpanningRecordConnections(schema, childRelationships, objectInfoMap
9630
9650
  * @param recordTypeInSchema
9631
9651
  * @returns
9632
9652
  */
9633
- function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema) {
9653
+ function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema, existingFields = []) {
9634
9654
  const polymorphicFieldTypeNames = new Set();
9635
9655
  let fields = ``;
9636
9656
  for (const field of values$2(fieldRepresentations)) {
@@ -9647,7 +9667,8 @@ function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRela
9647
9667
  // Only add the relationship if there is relevant objectinfos for it,
9648
9668
  // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9649
9669
  // the query.
9650
- if (objectInfoMap[relation.apiName] !== undefined) {
9670
+ if (objectInfoMap[relation.apiName] !== undefined &&
9671
+ existingFields.includes(field.relationshipName) === false) {
9651
9672
  existingParentRelationships.add(field.relationshipName);
9652
9673
  fields += `${field.relationshipName}: ${relation.apiName}\n`;
9653
9674
  }
@@ -9720,6 +9741,8 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
9720
9741
  // this is only wrapped in a try to execute the event after the result was returned
9721
9742
  try {
9722
9743
  eventEmitter({ type: 'graphql-eval-start' });
9744
+ const operationNode = getOperationFromDocument(config.query);
9745
+ let topLevelQueries = [];
9723
9746
  // assume that 'config.query' has required injected fields.
9724
9747
  const modifiedAST = visit(config.query, {
9725
9748
  Field: {
@@ -9749,6 +9772,25 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
9749
9772
  };
9750
9773
  },
9751
9774
  },
9775
+ Directive: {
9776
+ enter(node, _key, _parent, _path, ancester) {
9777
+ if (node.name.value === 'category' && node.arguments !== undefined) {
9778
+ for (let i = 0, len = node.arguments.length; i < len; i++) {
9779
+ const argument = node.arguments[i];
9780
+ if (isStringValueNode(argument.value) &&
9781
+ argument.value.value === 'recordQuery') {
9782
+ const parentNode = ancester[ancester.length - 1];
9783
+ if (isFieldNode(parentNode)) {
9784
+ topLevelQueries.push({
9785
+ argumentNodes: parentNode.arguments || [],
9786
+ recordName: parentNode.name.value,
9787
+ });
9788
+ }
9789
+ }
9790
+ }
9791
+ }
9792
+ },
9793
+ },
9752
9794
  });
9753
9795
  eventEmitter({ type: 'graphql-preconditions-met' });
9754
9796
  // create the resolver request context, runtime values and functions for
@@ -9767,7 +9809,18 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
9767
9809
  rootValue: {},
9768
9810
  }));
9769
9811
  eventEmitter({ type: 'graphql-evaluated' });
9770
- return { result, seenRecordIds: [...contextValue.seenRecordIds] };
9812
+ // add record key to seen ids
9813
+ const seenRecordIds = [...contextValue.seenRecordIds].map((id) => `UiApi::RecordRepresentation:${id}`);
9814
+ // if we have all the data to build the top level query
9815
+ // add it to the seen ids
9816
+ if (operationNode !== undefined && topLevelQueries.length > 0) {
9817
+ topLevelQueries.forEach((query) => {
9818
+ const { recordName, argumentNodes } = query;
9819
+ const queryString = buildKeyStringForRecordQuery(operationNode, config.variables || {}, argumentNodes, recordName);
9820
+ seenRecordIds.push(queryString);
9821
+ });
9822
+ }
9823
+ return { result, seenRecordIds };
9771
9824
  }
9772
9825
  finally {
9773
9826
  eventEmitter({ type: 'graphql-eval-end' });
@@ -13147,14 +13200,12 @@ function isLocalEvalSnapshot(snapshot) {
13147
13200
  return 'rebuildWithLocalEval' in snapshot;
13148
13201
  }
13149
13202
  function createSeenRecords(ids, currentSnapshot) {
13150
- let seenRecords = ids
13151
- .map((id) => `UiApi::RecordRepresentation:${id}`)
13152
- .reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
13203
+ let seenRecords = ids.reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
13153
13204
  if (currentSnapshot.state !== 'Error') {
13154
13205
  currentSnapshot.seenRecords.forEach((record) => {
13155
13206
  let keyString = typeof record !== 'string' ? serializeStructuredKey(record) : record;
13156
- if (keyString !== 'UiApi::GraphQLRepresentation__uiapi' &&
13157
- keyString !== 'UiApi::GraphQLRepresentation__uiapi__query' &&
13207
+ if (keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi' &&
13208
+ keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi__query' &&
13158
13209
  seenRecords.has(record) === false) {
13159
13210
  seenRecords.add(record);
13160
13211
  }
@@ -17508,4 +17559,4 @@ register({
17508
17559
  });
17509
17560
 
17510
17561
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17511
- // version: 1.249.0-11c3e1ed5
17562
+ // version: 1.250.0-9df9bc3d1