@salesforce/lds-runtime-mobile 1.152.4 → 1.154.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 +243 -34
  2. package/package.json +5 -5
  3. package/sfdc/main.js +243 -34
package/dist/main.js CHANGED
@@ -521,7 +521,7 @@ function isDeprecatedDurableStoreEntry(durableRecord) {
521
521
  const DefaultDurableSegment = 'DEFAULT';
522
522
  const RedirectDurableSegment = 'REDIRECT_KEYS';
523
523
 
524
- const { keys: keys$6, create: create$5, assign: assign$4, freeze: freeze$1 } = Object;
524
+ const { keys: keys$6, create: create$5, assign: assign$5, freeze: freeze$1 } = Object;
525
525
 
526
526
  //Durable store error instrumentation key
527
527
  const DURABLE_STORE_ERROR = 'durable-store-error';
@@ -4892,7 +4892,7 @@ function createDraftSynthesisErrorResponse(message = 'failed to synthesize draft
4892
4892
  return new DraftErrorFetchResponse(HttpStatusCode.BadRequest, error);
4893
4893
  }
4894
4894
 
4895
- const { keys: keys$4, create: create$4, assign: assign$3, values: values$2 } = Object;
4895
+ const { keys: keys$4, create: create$4, assign: assign$4, values: values$2 } = Object;
4896
4896
  const { stringify: stringify$4, parse: parse$4 } = JSON;
4897
4897
  const { isArray: isArray$3 } = Array;
4898
4898
 
@@ -6471,6 +6471,104 @@ function isEntryDurableRecordRepresentation(entry, key) {
6471
6471
  entry.data.__type === undefined);
6472
6472
  }
6473
6473
 
6474
+ function serializeFieldArguments(argumentNodes, variables) {
6475
+ const mutableArgumentNodes = Object.assign([], argumentNodes);
6476
+ return `args__(${mutableArgumentNodes
6477
+ .sort((a, b) => {
6478
+ const aName = a.name.value.toUpperCase();
6479
+ const bName = b.name.value.toUpperCase();
6480
+ return aName < bName ? -1 : aName > bName ? 1 : 0;
6481
+ })
6482
+ .map((node) => serializeArgNode(node, variables))
6483
+ .join('::')})`;
6484
+ }
6485
+ function serializeArgNode(argumentNode, variables) {
6486
+ const argName = argumentNode.name.value;
6487
+ return `${argName}:${serializeValueNode(argumentNode.value, variables)}`;
6488
+ }
6489
+ function serializeValueNode(valueNode, variables) {
6490
+ switch (valueNode.kind) {
6491
+ case 'BooleanValue':
6492
+ return valueNode.value + '';
6493
+ case 'IntValue':
6494
+ case 'FloatValue':
6495
+ case 'EnumValue':
6496
+ case 'StringValue':
6497
+ return valueNode.value;
6498
+ case 'ListValue': {
6499
+ const mutableValueNodeList = Object.assign([], valueNode.values);
6500
+ return mutableValueNodeList
6501
+ .sort((a, b) => {
6502
+ const aVal = serializeValueNode(a, variables).toUpperCase();
6503
+ const bVal = serializeValueNode(b, variables).toUpperCase();
6504
+ return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
6505
+ })
6506
+ .map((val, i) => `${serializeValueNode(val, variables)}[${i}]`)
6507
+ .join(',');
6508
+ }
6509
+ case 'Variable': {
6510
+ const variableValue = variables[valueNode.name.value];
6511
+ return typeof variableValue === 'string'
6512
+ ? variableValue
6513
+ : JSON.stringify(variableValue);
6514
+ }
6515
+ case 'NullValue':
6516
+ return 'null';
6517
+ case 'ObjectValue': {
6518
+ const mutableFieldNodeList = Object.assign([], valueNode.fields);
6519
+ return mutableFieldNodeList
6520
+ .sort((a, b) => {
6521
+ const aName = a.name.value.toUpperCase();
6522
+ const bName = b.name.value.toUpperCase();
6523
+ return aName < bName ? -1 : aName > bName ? 1 : 0;
6524
+ })
6525
+ .map((field) => field.name.value + ':' + serializeValueNode(field.value, variables))
6526
+ .join(',');
6527
+ }
6528
+ }
6529
+ }
6530
+
6531
+ function serializeOperationNode(operationNode, variables, fragmentMap) {
6532
+ return `${serializeSelectionSet(operationNode.selectionSet, variables, fragmentMap)}`;
6533
+ }
6534
+ function serializeSelectionSet(selectionSetNode, variables, fragmentMap) {
6535
+ return `${selectionSetNode.selections
6536
+ .map((selection) => serializeSelectionNode(selection, variables, fragmentMap))
6537
+ .join()}`;
6538
+ }
6539
+ /**
6540
+ *
6541
+ * @description This function takes a GraphQL SelectionNode from an AST and serializes it in a stable way, so we can
6542
+ * use it for property names and Store keys.
6543
+ * @param selectionNode
6544
+ * @param variables
6545
+ * @param fragmentMap
6546
+ * @returns string
6547
+ */
6548
+ function serializeSelectionNode(selectionNode, variables, fragmentMap) {
6549
+ switch (selectionNode.kind) {
6550
+ case 'Field': {
6551
+ const hasArguments = selectionNode.arguments !== undefined && selectionNode.arguments.length > 0;
6552
+ const argumentSuffix = hasArguments
6553
+ ? `__${serializeFieldArguments(selectionNode.arguments, variables)}`
6554
+ : '';
6555
+ return `${selectionNode.name.value}${argumentSuffix}`;
6556
+ }
6557
+ case 'FragmentSpread': {
6558
+ const fragment = fragmentMap[selectionNode.name.value];
6559
+ return fragment === undefined
6560
+ ? selectionNode.name.value
6561
+ : serializeSelectionSet(fragment.selectionSet, variables, fragmentMap);
6562
+ }
6563
+ case 'InlineFragment':
6564
+ return serializeSelectionSet(selectionNode.selectionSet, variables, fragmentMap);
6565
+ }
6566
+ }
6567
+ function buildQueryTypeStringKey(args) {
6568
+ const { keyPrefix, schemaName, queryTypeName, operationNode, variables, fragmentMap } = args;
6569
+ return `${keyPrefix}::${schemaName}::${queryTypeName}[${serializeOperationNode(operationNode, variables, fragmentMap)}]`;
6570
+ }
6571
+
6474
6572
  /**
6475
6573
  * Copyright (c) 2022, Salesforce, Inc.,
6476
6574
  * All rights reserved.
@@ -6604,7 +6702,7 @@ function isArrayLike(x) {
6604
6702
  (x.length === 0 || (x.length > 0 && Object.prototype.hasOwnProperty.call(x, x.length - 1))));
6605
6703
  }
6606
6704
 
6607
- const { create: create$3, keys: keys$3, values: values$1, entries: entries$2 } = Object;
6705
+ const { create: create$3, keys: keys$3, values: values$1, entries: entries$2, assign: assign$3 } = Object;
6608
6706
  const { stringify: stringify$3, parse: parse$3 } = JSON;
6609
6707
  const { isArray: isArray$2 } = Array;
6610
6708
 
@@ -7048,6 +7146,10 @@ function dateRangesFrom(dateRange, input, dateFunction) {
7048
7146
  }
7049
7147
  }
7050
7148
 
7149
+ const JSON_EXTRACT_PATH_INGESTION_TIMESTAMP = '$.ingestionTimestamp';
7150
+ const JSON_EXTRACT_PATH_INGESTION_APINAME = '$.apiName';
7151
+ const JSON_EXTRACT_PATH_DRAFTS = '$.drafts';
7152
+
7051
7153
  const MultiPickListValueSeparator = ';';
7052
7154
  function filterToPredicates(where, recordType, alias, objectInfoMap, joins, draftFunctions) {
7053
7155
  if (!where)
@@ -7109,7 +7211,7 @@ function filterToPredicates(where, recordType, alias, objectInfoMap, joins, draf
7109
7211
  return [
7110
7212
  {
7111
7213
  alias: childAlias,
7112
- leftPath: '$.apiName',
7214
+ leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
7113
7215
  operator: '=',
7114
7216
  value: entityName,
7115
7217
  dataType: 'String',
@@ -7570,11 +7672,18 @@ function buildQuery(config) {
7570
7672
  const joins = buildJoins(config);
7571
7673
  const predicates = buildPredicates(config);
7572
7674
  const orderBy = buildOrderBy(config);
7675
+ const staleRecordsSql = excludeStaleRecordsGate.isOpen({ fallback: false })
7676
+ ? `AND (
7677
+ json_extract("${config.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ?
7678
+ OR json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL
7679
+ )`
7680
+ : '';
7573
7681
  const sql = `
7574
7682
  SELECT "${config.alias}".data
7575
7683
  FROM lds_data "${config.alias}" ${joins.sql}
7576
7684
  WHERE "${config.alias}".key like 'UiApi::RecordRepresentation:%'
7577
- AND json_extract("${config.alias}".data, '$.apiName') = ?
7685
+ AND json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_INGESTION_APINAME}') = ?
7686
+ ${staleRecordsSql}
7578
7687
  ${predicates.sql}
7579
7688
  ${orderBy.sql}
7580
7689
  LIMIT ?
@@ -7587,6 +7696,7 @@ function buildQuery(config) {
7587
7696
  ...joins.bindings,
7588
7697
  // the api name for the main record type
7589
7698
  config.alias,
7699
+ ...(excludeStaleRecordsGate.isOpen({ fallback: false }) ? [config.ingestionTimestamp] : []),
7590
7700
  // where clause and parent scope bindings
7591
7701
  ...predicates.bindings,
7592
7702
  // limit binding
@@ -7612,19 +7722,33 @@ function buildJoins(config) {
7612
7722
  if (allJoins.length === 0)
7613
7723
  return { sql, bindings };
7614
7724
  sql = allJoins.reduce((joinAccumulator, join) => {
7725
+ let timestampAdded = false;
7615
7726
  const joinConditions = join.conditions.reduce((conditionAccumulator, condition) => {
7616
7727
  let joined_sql;
7728
+ const joinMetadataTimestamp = excludeStaleRecordsGate.isOpen({ fallback: false })
7729
+ ? ` AND (json_extract("${join.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ? OR json_extract("${join.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL)`
7730
+ : '';
7617
7731
  // predicate on a value, use the newly joined table
7618
7732
  if ('type' in condition) {
7619
7733
  const { sql, binding } = predicateToSQL(condition, join.alias);
7620
- joined_sql = ` AND ${sql}`;
7734
+ joined_sql = ` AND ${sql}${timestampAdded ? '' : joinMetadataTimestamp}`;
7621
7735
  bindings.push(...binding);
7736
+ if (excludeStaleRecordsGate.isOpen({ fallback: false }) &&
7737
+ timestampAdded === false) {
7738
+ bindings.push(config.ingestionTimestamp);
7739
+ timestampAdded = true;
7740
+ }
7622
7741
  }
7623
7742
  else {
7624
7743
  // predicate on a path
7625
7744
  const left = ` AND json_extract("${join.to}".data, '${condition.leftPath}')`;
7626
7745
  const right = `json_extract("${join.alias}".data, '${condition.rightPath}')`;
7627
- joined_sql = `${left} = ${right}`;
7746
+ joined_sql = `${left} = ${right}${timestampAdded ? '' : joinMetadataTimestamp}`;
7747
+ if (excludeStaleRecordsGate.isOpen({ fallback: false }) &&
7748
+ timestampAdded === false) {
7749
+ bindings.push(config.ingestionTimestamp);
7750
+ timestampAdded = true;
7751
+ }
7628
7752
  }
7629
7753
  conditionAccumulator += joined_sql;
7630
7754
  return conditionAccumulator;
@@ -8303,7 +8427,7 @@ function orderByToPredicate(orderBy, recordType, alias, objectInfoMap, joins) {
8303
8427
  function pathForKey(key) {
8304
8428
  switch (key) {
8305
8429
  case 'ApiName':
8306
- return '$.apiName';
8430
+ return JSON_EXTRACT_PATH_INGESTION_APINAME;
8307
8431
  case 'WeakEtag':
8308
8432
  return '$.weakEtag';
8309
8433
  case 'Id':
@@ -8325,7 +8449,7 @@ function scopeToJoins(scope = '', settings) {
8325
8449
  {
8326
8450
  type: PredicateType.single,
8327
8451
  alias: 'ServiceAppointment_AssignedResource',
8328
- leftPath: '$.apiName',
8452
+ leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
8329
8453
  operator: '=',
8330
8454
  value: 'AssignedResource',
8331
8455
  dataType: 'String',
@@ -8346,7 +8470,7 @@ function scopeToJoins(scope = '', settings) {
8346
8470
  {
8347
8471
  type: PredicateType.single,
8348
8472
  alias: 'ServiceAppointment_AssignedResource_ServiceResource',
8349
- leftPath: '$.apiName',
8473
+ leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
8350
8474
  operator: '=',
8351
8475
  value: 'ServiceResource',
8352
8476
  dataType: 'String',
@@ -8451,14 +8575,21 @@ function addResolversToSchema(schema, polyFields) {
8451
8575
  // Fields of the `RecordQuery` type are the record queries for the entity types
8452
8576
  // supported for the org
8453
8577
  for (const recordQuery of fields) {
8454
- recordQuery.resolve = function recordConnectionResolver(record, args) {
8578
+ recordQuery.resolve = async function recordConnectionResolver(record, args, { query }, info) {
8579
+ const { name: currentFieldName } = recordQuery;
8580
+ let ingestionTimestamp = 0;
8581
+ if (excludeStaleRecordsGate.isOpen({ fallback: false })) {
8582
+ // at our record query we fetch each ingestion time stamp and pass it down to each lower resolver to query against
8583
+ ingestionTimestamp = await fetchIngestionTimeStampFromDatabase(currentFieldName, info, args, query);
8584
+ }
8455
8585
  // In the SF schema, the relevant arguments are passed into RecordQuery fields, but actually used
8456
8586
  // down in the edge resolvers. For this resolver, we can just return what was passed in
8457
8587
  // to make it available to the next execution step
8458
8588
  return {
8459
8589
  parentArgs: args,
8460
8590
  parentRecord: record,
8461
- currentFieldName: recordQuery.name,
8591
+ currentFieldName,
8592
+ ingestionTimestamp,
8462
8593
  };
8463
8594
  };
8464
8595
  }
@@ -8504,10 +8635,11 @@ function addResolversToSchema(schema, polyFields) {
8504
8635
  // }
8505
8636
  for (const field of fields) {
8506
8637
  if (field.name === 'node') {
8507
- field.resolve = function nodeResolver(record, _args, { seenRecordIds }) {
8638
+ field.resolve = function nodeResolver(obj, _args, { seenRecordIds }) {
8639
+ const { record, ingestionTimestamp } = obj;
8508
8640
  const recordRepresentation = parse$3(record);
8509
8641
  seenRecordIds.add(recordRepresentation.id);
8510
- return recordRepresentation;
8642
+ return { recordRepresentation, ingestionTimestamp };
8511
8643
  };
8512
8644
  }
8513
8645
  }
@@ -8530,40 +8662,40 @@ function addResolversToSchema(schema, polyFields) {
8530
8662
  for (const field of fields) {
8531
8663
  switch (field.name) {
8532
8664
  case 'Id':
8533
- field.resolve = (record) => record.id;
8665
+ field.resolve = ({ recordRepresentation: record }) => record.id;
8534
8666
  break;
8535
8667
  case 'ApiName':
8536
- field.resolve = (record) => record.apiName;
8668
+ field.resolve = ({ recordRepresentation: record }) => record.apiName;
8537
8669
  break;
8538
8670
  case 'WeakEtag':
8539
- field.resolve = (record) => record.weakEtag;
8671
+ field.resolve = ({ recordRepresentation: record }) => record.weakEtag;
8540
8672
  break;
8541
8673
  case '_drafts':
8542
- field.resolve = (record) => {
8674
+ field.resolve = ({ recordRepresentation: record, }) => {
8543
8675
  return record.drafts ? record.drafts : null;
8544
8676
  };
8545
8677
  break;
8546
8678
  case 'LastModifiedById':
8547
- field.resolve = (record) => {
8679
+ field.resolve = ({ recordRepresentation: record }) => {
8548
8680
  return record.lastModifiedById
8549
8681
  ? { value: record.lastModifiedById }
8550
8682
  : null;
8551
8683
  };
8552
8684
  break;
8553
8685
  case 'LastModifiedDate':
8554
- field.resolve = (record) => {
8686
+ field.resolve = ({ recordRepresentation: record }) => {
8555
8687
  return record.lastModifiedDate
8556
8688
  ? { value: record.lastModifiedDate }
8557
8689
  : null;
8558
8690
  };
8559
8691
  break;
8560
8692
  case 'SystemModstamp':
8561
- field.resolve = (record) => {
8693
+ field.resolve = ({ recordRepresentation: record }) => {
8562
8694
  return record.systemModstamp ? { value: record.systemModstamp } : null;
8563
8695
  };
8564
8696
  break;
8565
8697
  case 'RecordTypeId':
8566
- field.resolve = (record) => {
8698
+ field.resolve = ({ recordRepresentation: record }) => {
8567
8699
  return record.recordTypeId ? { value: record.recordTypeId } : null;
8568
8700
  };
8569
8701
  break;
@@ -8575,7 +8707,17 @@ function addResolversToSchema(schema, polyFields) {
8575
8707
  .getInterfaces()
8576
8708
  .find((iface) => iface.name === 'Record')) ||
8577
8709
  (recordFieldType && recordFieldType.name === 'Record')) {
8578
- field.resolve = function relationResolver(record, _args, { Record, seenRecordIds }) {
8710
+ field.resolve = async function relationResolver(obj, _args, { Record, seenRecordIds }) {
8711
+ const fetchRecordOrNull = async (key) => {
8712
+ const recordRepresentation = await Record.load(key);
8713
+ return recordRepresentation !== null
8714
+ ? {
8715
+ recordRepresentation,
8716
+ ingestionTimestamp,
8717
+ }
8718
+ : null;
8719
+ };
8720
+ const { recordRepresentation: record, ingestionTimestamp } = obj;
8579
8721
  const fieldName = field.name.endsWith('__r')
8580
8722
  ? field.name.replace('__r', '__c')
8581
8723
  : field.name;
@@ -8585,26 +8727,28 @@ function addResolversToSchema(schema, polyFields) {
8585
8727
  if (!id)
8586
8728
  return null;
8587
8729
  if (id['__ref'] !== undefined) {
8588
- return Record.load(record.fields[`${field.name}Id`].value);
8730
+ return fetchRecordOrNull(record.fields[`${field.name}Id`].value);
8589
8731
  }
8590
8732
  seenRecordIds.add(id);
8591
- return Record.load(id);
8733
+ return fetchRecordOrNull(id);
8592
8734
  };
8593
8735
  }
8594
8736
  else if (isObjectType(recordFieldType) &&
8595
8737
  field.type.name.endsWith('Connection')) {
8596
8738
  // spanning field to a connection
8597
- field.resolve = (record, args, { seenRecordIds }) => {
8598
- seenRecordIds.add(record.id);
8739
+ field.resolve = async ({ recordRepresentation, ingestionTimestamp }, args, { seenRecordIds }) => {
8740
+ seenRecordIds.add(recordRepresentation.id);
8741
+ const { name: currentFieldName } = field;
8599
8742
  return {
8600
8743
  parentArgs: args,
8601
- parentRecord: record,
8602
- currentFieldName: field.name,
8744
+ parentRecord: recordRepresentation,
8745
+ currentFieldName,
8746
+ ingestionTimestamp,
8603
8747
  };
8604
8748
  };
8605
8749
  }
8606
8750
  else {
8607
- field.resolve = function recordFieldResolver(record) {
8751
+ field.resolve = function recordFieldResolver({ recordRepresentation: record, }) {
8608
8752
  return record.fields[field.name] || null;
8609
8753
  };
8610
8754
  }
@@ -8616,7 +8760,7 @@ function addResolversToSchema(schema, polyFields) {
8616
8760
  if (recordInterface !== undefined && baseRecord !== undefined) {
8617
8761
  // Applys 'resolveType' of GraphQLInterfaceType to 'Record' interface. Since all the heterogenous types are named as 'apiName', the type with same name as the loaded record 'apiName' property is the type wanted.
8618
8762
  // GraphQL executor would match InLineFragment' condition with type and keeps the deeper level field resolving going.
8619
- recordInterface.resolveType = function (value) {
8763
+ recordInterface.resolveType = function ({ recordRepresentation: value, }) {
8620
8764
  const targetType = polyTypes.find((type) => type.name === value.apiName);
8621
8765
  return targetType === undefined ? baseRecord : targetType;
8622
8766
  };
@@ -8624,7 +8768,7 @@ function addResolversToSchema(schema, polyFields) {
8624
8768
  return schema;
8625
8769
  }
8626
8770
  async function connectionEdgeResolver(obj, _args, context) {
8627
- const { parentArgs = {}, parentRecord, currentFieldName } = obj;
8771
+ const { parentArgs = {}, parentRecord, currentFieldName, ingestionTimestamp } = obj;
8628
8772
  const { query, objectInfos, draftFunctions } = context;
8629
8773
  let joins = [];
8630
8774
  let alias = currentFieldName;
@@ -8655,10 +8799,19 @@ async function connectionEdgeResolver(obj, _args, context) {
8655
8799
  predicates,
8656
8800
  orderBy: orderByToPredicate(parentArgs.orderBy, alias, alias, context.objectInfos),
8657
8801
  limit: parentArgs.first,
8802
+ ingestionTimestamp,
8658
8803
  };
8659
8804
  const { sql, bindings } = buildQuery(queryConfig);
8660
8805
  const results = await query(sql, bindings);
8661
- return results.rows.map((row) => row[0]);
8806
+ //map each sql result with the ingestion timestamp to pass it down a level
8807
+ return results.rows
8808
+ .map((row) => row[0])
8809
+ .map((record) => {
8810
+ return {
8811
+ record,
8812
+ ingestionTimestamp,
8813
+ };
8814
+ });
8662
8815
  }
8663
8816
  /**
8664
8817
  * Converts a childRelationship into a predicate
@@ -8684,6 +8837,62 @@ function isRecordType(type) {
8684
8837
  const interfaces = type.getInterfaces();
8685
8838
  return Boolean(interfaces.find((iface) => iface.name === 'Record'));
8686
8839
  }
8840
+ /**
8841
+ * Builds the top level record query key based on AST data
8842
+ * @param operation
8843
+ * @param variables
8844
+ * @param argumentNodes
8845
+ * @param currentFieldName
8846
+ * @returns
8847
+ */
8848
+ function buildKeyStringForRecordQuery(operation, variables, argumentNodes, currentFieldName) {
8849
+ const queryKey = buildQueryTypeStringKey({
8850
+ luvio: {},
8851
+ keyPrefix: 'UiApi',
8852
+ schemaName: 'uiapi',
8853
+ queryTypeName: 'Query',
8854
+ operationNode: operation,
8855
+ variables,
8856
+ fragmentMap: {},
8857
+ });
8858
+ const filteredArgumentNodes = assign$3([], argumentNodes).filter((node) => node.name.value !== 'first' && node.name.value !== 'after');
8859
+ const argumentString = filteredArgumentNodes.length > 0
8860
+ ? '__' + serializeFieldArguments(filteredArgumentNodes, variables)
8861
+ : '';
8862
+ return `${queryKey}__uiapi__query__${currentFieldName}${argumentString}`;
8863
+ }
8864
+ /**
8865
+ * fetches a query level ingestion time stamp from the L2 cache
8866
+ * if no query has been seen then the timestamp is 0
8867
+ * @param apiName
8868
+ * @param info
8869
+ * @param args
8870
+ * @param query
8871
+ * @returns
8872
+ */
8873
+ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
8874
+ const { operation, variableValues } = info;
8875
+ // if we cannot find the query key in the database then default to 0 as we assume we have not seen the query
8876
+ // and all the data is not stale
8877
+ let ingestionTimestamp = 0;
8878
+ if (info.fieldNodes.length > 0 && info.fieldNodes[0].arguments !== undefined) {
8879
+ const key = buildKeyStringForRecordQuery(operation,
8880
+ // join varables passed from query to the argument variables given from the AST
8881
+ { ...variableValues, ...args }, info.fieldNodes[0].arguments, apiName);
8882
+ const sql = `
8883
+ SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
8884
+ FROM lds_data
8885
+ WHERE key IS ?
8886
+ `;
8887
+ const results = await query(sql, [key]);
8888
+ const [timestamp] = results.rows.map((row) => row[0]);
8889
+ if (timestamp !== null && typeof timestamp === 'number') {
8890
+ //go back 10 ms to adjust for margin of error when top level query is stored and when raml objects are stored
8891
+ ingestionTimestamp = timestamp - 10;
8892
+ }
8893
+ }
8894
+ return ingestionTimestamp;
8895
+ }
8687
8896
 
8688
8897
  var uiapiSchemaString = "scalar String\nscalar DateTime\nscalar Currency\nscalar ID\nscalar Boolean\nscalar Longitude\nscalar Float\nscalar MultiPicklist\nscalar Base64\nscalar Url\nscalar PhoneNumber\nscalar Email\nscalar TextArea\nscalar Latitude\nscalar Picklist\nscalar RichTextArea\nscalar EncryptedString\nscalar Double\nscalar Long\nscalar JSON\nscalar Time\nscalar Int\nscalar Percent\nscalar LongTextArea\nscalar Date\ntype PercentAggregate implements FieldValue {\n value: Percent\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: PercentValue\n min: PercentValue\n sum: PercentValue\n}\n\ntype StringAggregate implements FieldValue {\n value: String\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n label: String\n max: StringValue\n min: StringValue\n}\n\ntype Query {\n uiapi: UIAPI!\n rateLimit: RateLimit\n}\n\ninput EmailOperators {\n eq: Email\n ne: Email\n like: Email\n lt: Email\n gt: Email\n lte: Email\n gte: Email\n in: [Email]\n nin: [Email]\n}\n\ninput PolymorphicParentRelationshipRecordOrderBy @generic {\n RecordOrderBy: RecordOrderBy @fieldCategory\n}\n\ninput DoubleOperators {\n eq: Double\n ne: Double\n lt: Double\n gt: Double\n lte: Double\n gte: Double\n in: [Double]\n nin: [Double]\n}\n\ntype DateOnlyAggregation {\n value: Date\n format: String\n}\n\ntype DateAggregate implements FieldValue {\n value: Date\n displayValue: String\n calendarMonth: DateFunctionAggregation\n calendarQuarter: DateFunctionAggregation\n calendarYear: DateFunctionAggregation\n count: LongValue\n countDistinct: LongValue\n dayInMonth: DateFunctionAggregation\n dayInWeek: DateFunctionAggregation\n dayInYear: DateFunctionAggregation\n fiscalMonth: DateFunctionAggregation\n fiscalQuarter: DateFunctionAggregation\n fiscalYear: DateFunctionAggregation\n format: String\n grouping: IntValue\n max: DateValue\n min: DateValue\n weekInMonth: DateFunctionAggregation\n weekInYear: DateFunctionAggregation\n}\n\ninput PolymorphicParentRelationshipGroupBy @generic {\n RecordGroupBy: RecordGroupBy @fieldCategory\n}\n\nenum GroupByFunction {\n DAY_IN_WEEK\n DAY_IN_MONTH\n DAY_IN_YEAR\n WEEK_IN_MONTH\n WEEK_IN_YEAR\n CALENDAR_MONTH\n CALENDAR_QUARTER\n CALENDAR_YEAR\n FISCAL_MONTH\n FISCAL_QUARTER\n FISCAL_YEAR\n DAY_ONLY\n HOUR_IN_DAY\n}\n\ntype RecordTypeInfo {\n available: Boolean!\n defaultRecordTypeMapping: Boolean!\n master: Boolean!\n name: String\n recordTypeId: ID\n}\n\ntype BooleanValue implements FieldValue {\n value: Boolean\n displayValue: String\n}\n\ntype ReferenceToInfo {\n ApiName: String!\n nameFields: [String]!\n objectInfo: ObjectInfo\n}\n\ninterface FieldValue {\n displayValue: String\n}\n\ntype LongitudeValue implements FieldValue {\n value: Longitude\n displayValue: String\n}\n\ntype StringValue implements FieldValue {\n value: String\n displayValue: String\n label: String\n}\n\ntype IntValue implements FieldValue {\n value: Int\n displayValue: String\n format: String\n}\n\ntype UrlValue implements FieldValue {\n value: Url\n displayValue: String\n}\n\ninput IdOperators {\n eq: ID\n ne: ID\n lt: ID\n gt: ID\n lte: ID\n gte: ID\n in: [ID]\n nin: [ID]\n inq: JoinInput\n ninq: JoinInput\n}\n\ntype LongAggregate implements FieldValue {\n value: Long\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n grouping: IntValue\n max: LongValue\n min: LongValue\n sum: LongValue\n}\n\ntype PhoneNumberAggregate implements FieldValue {\n value: PhoneNumber\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: PhoneNumberValue\n min: PhoneNumberValue\n}\n\ninput TimeOperators {\n eq: Time\n ne: Time\n lt: Time\n gt: Time\n lte: Time\n gte: Time\n in: [Time]\n nin: [Time]\n}\n\ntype PicklistValue implements FieldValue {\n value: Picklist\n displayValue: String\n label: String\n}\n\ntype CurrencyAggregate implements FieldValue {\n value: Currency\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: CurrencyValue\n min: CurrencyValue\n sum: CurrencyValue\n}\n\ntype RelatedListInfo {\n childApiName: String!\n relatedListName: String!\n label: String!\n displayColumns: [ListColumn!]!\n orderedByInfo: [ListOrder!]!\n parentApiName: String!\n fieldApiName: String!\n}\n\ninput StringOperators {\n eq: String\n ne: String\n like: String\n lt: String\n gt: String\n lte: String\n gte: String\n in: [String]\n nin: [String]\n}\n\ntype UIAPI {\n query: RecordQuery!\n aggregate: RecordQueryAggregate!\n objectInfos(apiNames: [String]): [ObjectInfo]\n relatedListByName(parentApiName: String!, relatedListName: String!): RelatedListInfo\n}\n\ninput MultiPicklistOperators {\n eq: MultiPicklist\n ne: MultiPicklist\n includes: [MultiPicklist]\n excludes: [MultiPicklist]\n}\n\ntype DateTimeAggregate implements FieldValue {\n value: DateTime\n displayValue: String\n calendarMonth: DateFunctionAggregation\n calendarQuarter: DateFunctionAggregation\n calendarYear: DateFunctionAggregation\n count: LongValue\n countDistinct: LongValue\n dayInMonth: DateFunctionAggregation\n dayInWeek: DateFunctionAggregation\n dayInYear: DateFunctionAggregation\n dayOnly: DateOnlyAggregation\n fiscalMonth: DateFunctionAggregation\n fiscalQuarter: DateFunctionAggregation\n fiscalYear: DateFunctionAggregation\n format: String\n hourInDay: DateFunctionAggregation\n max: DateTimeValue\n min: DateTimeValue\n weekInMonth: DateFunctionAggregation\n weekInYear: DateFunctionAggregation\n}\n\ninput BooleanOperators {\n eq: Boolean\n ne: Boolean\n}\n\ntype EmailAggregate implements FieldValue {\n value: Email\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: EmailValue\n min: EmailValue\n}\n\ninput GroupByDateFunction {\n function: GroupByFunction\n}\n\ntype RichTextAreaValue implements FieldValue {\n value: RichTextArea\n displayValue: String\n}\n\ntype MultiPicklistValue implements FieldValue {\n value: MultiPicklist\n displayValue: String\n label: String\n}\n\ntype TimeAggregate implements FieldValue {\n value: Time\n displayValue: String\n format: String\n hourInDay: DateFunctionAggregation\n}\n\ntype __Type {\n kind: __TypeKind!\n name: String\n description: String\n fields(includeDeprecated: Boolean = false): [__Field!]\n interfaces: [__Type!]\n possibleTypes: [__Type!]\n enumValues(includeDeprecated: Boolean = false): [__EnumValue!]\n inputFields: [__InputValue!]\n ofType: __Type\n}\n\ntype ListColumn {\n fieldApiName: String!\n label: String!\n lookupId: String\n sortable: Boolean\n}\n\ntype LatitudeAggregate implements FieldValue {\n value: Latitude\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n max: LatitudeValue\n min: LatitudeValue\n sum: DoubleValue\n}\n\ninput CurrencyOperators {\n eq: Currency\n ne: Currency\n lt: Currency\n gt: Currency\n lte: Currency\n gte: Currency\n in: [Currency]\n nin: [Currency]\n}\n\ninput DistanceInput {\n latitude: Latitude!\n longitude: Longitude!\n}\n\nunion PolymorphicParentRelationship @generic = RecordRepresentation\n\ntype LongTextAreaValue implements FieldValue {\n value: LongTextArea\n displayValue: String\n}\n\ntype LatitudeValue implements FieldValue {\n value: Latitude\n displayValue: String\n}\n\ninput OrderByClause {\n order: ResultOrder\n nulls: NullOrder\n}\n\ninput GroupByClause {\n group: Boolean\n}\n\ntype RecordAggregateConnection @generic {\n edges: [RecordAggregateEdge]\n pageInfo: PageInfo!\n totalCount: Int!\n}\n\ntype LongitudeAggregate implements FieldValue {\n value: Longitude\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n max: LongitudeValue\n min: LongitudeValue\n sum: DoubleValue\n}\n\ntype RecordEdge @generic {\n node: RecordRepresentation\n cursor: String!\n}\n\ntype DateValue implements FieldValue {\n value: Date\n displayValue: String\n format: String\n}\n\ninput URLOperators {\n eq: Url\n ne: Url\n like: Url\n lt: Url\n gt: Url\n lte: Url\n gte: Url\n in: [Url]\n nin: [Url]\n}\n\ninput LongOperators {\n eq: Long\n ne: Long\n lt: Long\n gt: Long\n lte: Long\n gte: Long\n in: [Long]\n nin: [Long]\n}\n\nenum DataType {\n STRING\n TEXTAREA\n PHONE\n EMAIL\n URL\n ENCRYPTEDSTRING\n BOOLEAN\n CURRENCY\n INT\n LONG\n DOUBLE\n PERCENT\n DATETIME\n TIME\n DATE\n REFERENCE\n PICKLIST\n MULTIPICKLIST\n ADDRESS\n LOCATION\n BASE64\n COMPLEXVALUE\n COMBOBOX\n JSON\n JUNCTIONIDLIST\n ANYTYPE\n}\n\nenum NullOrder {\n LAST\n FIRST\n}\n\ntype PhoneNumberValue implements FieldValue {\n value: PhoneNumber\n displayValue: String\n}\n\n# Cannot have empty enum\n# enum RecordScope @generic {\n# }\n\ntype DoubleAggregate implements FieldValue {\n value: Double\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: DoubleValue\n min: DoubleValue\n sum: DoubleValue\n}\n\ntype __Field {\n name: String!\n description: String\n args: [__InputValue!]!\n type: __Type!\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\ninput DateOperators {\n eq: DateInput\n ne: DateInput\n lt: DateInput\n gt: DateInput\n lte: DateInput\n gte: DateInput\n in: [DateInput]\n nin: [DateInput]\n DAY_IN_WEEK: DateFunctionInput\n DAY_IN_MONTH: DateFunctionInput\n DAY_IN_YEAR: DateFunctionInput\n WEEK_IN_MONTH: DateFunctionInput\n WEEK_IN_YEAR: DateFunctionInput\n CALENDAR_MONTH: DateFunctionInput\n CALENDAR_QUARTER: DateFunctionInput\n CALENDAR_YEAR: DateFunctionInput\n FISCAL_MONTH: DateFunctionInput\n FISCAL_QUARTER: DateFunctionInput\n FISCAL_YEAR: DateFunctionInput\n}\n\ninput GeolocationInput {\n latitude: Latitude!\n longitude: Longitude!\n radius: Float!\n unit: Unit!\n}\n\ninput JoinInput {\n Record: RecordFilter @fieldCategory\n ApiName: String\n}\n\ninput TextAreaOperators {\n eq: TextArea\n ne: TextArea\n like: TextArea\n lt: TextArea\n gt: TextArea\n lte: TextArea\n gte: TextArea\n in: [TextArea]\n nin: [TextArea]\n}\n\ntype TextAreaValue implements FieldValue {\n value: TextArea\n displayValue: String\n}\n\ninput PercentOperators {\n eq: Percent\n ne: Percent\n lt: Percent\n gt: Percent\n lte: Percent\n gte: Percent\n in: [Percent]\n nin: [Percent]\n}\n\ntype DoubleValue implements FieldValue {\n value: Double\n displayValue: String\n format: String\n}\n\ntype IDAggregate implements FieldValue {\n value: ID\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: IDValue\n min: IDValue\n}\n\ntype __InputValue {\n name: String!\n description: String\n type: __Type!\n defaultValue: String\n}\n\ntype RecordAggregateEdge @generic {\n node: RecordResult\n cursor: String!\n}\n\ntype __Directive {\n name: String\n description: String\n locations: [__DirectiveLocation!]\n args: [__InputValue!]!\n}\n\ntype ThemeInfo {\n color: String\n iconUrl: String\n}\n\ntype UrlAggregate implements FieldValue {\n value: Url\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: UrlValue\n min: UrlValue\n}\n\nenum DateLiteral {\n THIS_WEEK\n THIS_FISCAL_QUARTER\n NEXT_YEAR\n TODAY\n LAST_WEEK\n LAST_YEAR\n YESTERDAY\n NEXT_MONTH\n NEXT_FISCAL_YEAR\n NEXT_WEEK\n NEXT_90_DAYS\n LAST_FISCAL_QUARTER\n LAST_FISCAL_YEAR\n THIS_YEAR\n THIS_MONTH\n THIS_QUARTER\n LAST_90_DAYS\n NEXT_FISCAL_QUARTER\n THIS_FISCAL_YEAR\n TOMORROW\n NEXT_QUARTER\n LAST_MONTH\n LAST_QUARTER\n}\n\ntype __EnumValue {\n name: String!\n description: String\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\ntype RecordRepresentation implements Record @generic{\n Id: ID!\n ApiName: String!\n WeakEtag: Long!\n DisplayValue: String\n LastModifiedById: IDValue\n LastModifiedDate: DateTimeValue\n SystemModstamp: DateTimeValue\n RecordTypeId(fallback: Boolean): IDValue\n IntValue: IntValue @fieldCategory\n StringValue: StringValue @fieldCategory\n BooleanValue: BooleanValue @fieldCategory\n IDValue: IDValue @fieldCategory\n DateTimeValue: DateTimeValue @fieldCategory\n TimeValue: TimeValue @fieldCategory\n DateValue: DateValue @fieldCategory\n TextAreaValue: TextAreaValue @fieldCategory\n LongTextAreaValue: LongTextAreaValue @fieldCategory\n RichTextAreaValue: RichTextAreaValue @fieldCategory\n PhoneNumberValue: PhoneNumberValue @fieldCategory\n EmailValue: EmailValue @fieldCategory\n UrlValue: UrlValue @fieldCategory\n EncryptedStringValue: EncryptedStringValue @fieldCategory\n CurrencyValue: CurrencyValue @fieldCategory\n LongitudeValue: LongitudeValue @fieldCategory\n LatitudeValue: LatitudeValue @fieldCategory\n PicklistValue: PicklistValue @fieldCategory\n MultiPicklistValue: MultiPicklistValue @fieldCategory\n LongValue: LongValue @fieldCategory\n DoubleValue: DoubleValue @fieldCategory\n PercentValue: PercentValue @fieldCategory\n Base64Value: Base64Value @fieldCategory\n JSONValue: JSONValue @fieldCategory\n parentRelationship: RecordRepresentation @fieldCategory\n polymorphicParentRelationship: PolymorphicParentRelationship @fieldCategory\n childRelationship(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy): RecordConnection @fieldCategory\n}\n\ntype IDValue implements FieldValue {\n value: ID\n displayValue: String\n}\n\nenum Unit {\n MI\n KM\n}\n\ninput OrderByGeolocationClause {\n distance: DistanceInput\n order: ResultOrder\n nulls: NullOrder\n}\n\ntype TextAreaAggregate implements FieldValue {\n value: TextArea\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: TextAreaValue\n min: TextAreaValue\n}\n\nenum GroupByType {\n GROUP_BY\n ROLLUP\n CUBE\n}\n\nenum ResultOrder {\n DESC\n ASC\n}\n\ninput RecordOrderBy @generic {\n orderableField: OrderByClause @fieldCategory\n orderableGeolocationField: OrderByGeolocationClause @fieldCategory\n orderableParentRelationship: RecordOrderBy @fieldCategory\n orderablePolymorphicParentRelationship: PolymorphicParentRelationshipRecordOrderBy @fieldCategory\n}\n\ninput PicklistOperators {\n eq: Picklist\n ne: Picklist\n in: [Picklist]\n nin: [Picklist]\n}\n\ninput RecordFilter @generic {\n and: [RecordFilter]\n or: [RecordFilter]\n not: RecordFilter\n parentRelationshipRecordFilter: RecordFilter @fieldCategory\n polymorphicParentRelationshipRecordFilter: PolymorphicParentRelationshipRecordFilter @fieldCategory\n IntegerOperator: IntegerOperators @fieldCategory\n LongOperator: LongOperators @fieldCategory\n StringOperator: StringOperators @fieldCategory\n DoubleOperator: DoubleOperators @fieldCategory\n PercentOperator: PercentOperators @fieldCategory\n LongitudeOperator: LongitudeOperators @fieldCategory\n LatitudeOperator: LatitudeOperators @fieldCategory\n EmailOperator: EmailOperators @fieldCategory\n TextAreaOperator: TextAreaOperators @fieldCategory\n LongTextAreaOperator: LongTextAreaOperators @fieldCategory\n URLOperator: URLOperators @fieldCategory\n PhoneNumberOperator: PhoneNumberOperators @fieldCategory\n BooleanOperator: BooleanOperators @fieldCategory\n IdOperator: IdOperators @fieldCategory\n CurrencyOperator: CurrencyOperators @fieldCategory\n TimeOperator: TimeOperators @fieldCategory\n DateOperator: DateOperators @fieldCategory\n DateTimeOperator: DateTimeOperators @fieldCategory\n PicklistOperator: PicklistOperators @fieldCategory\n MultiPicklistOperator: MultiPicklistOperators @fieldCategory\n GeolocationOperator: GeolocationOperators @fieldCategory\n}\n\ntype TimeValue implements FieldValue {\n value: Time\n displayValue: String\n format: String\n}\n\ninput GeolocationOperators {\n lt: GeolocationInput\n gt: GeolocationInput\n}\n\ntype PicklistAggregate implements FieldValue {\n value: Picklist\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n label: String\n max: PicklistValue\n min: PicklistValue\n}\n\ninput LatitudeOperators {\n eq: Latitude\n ne: Latitude\n lt: Latitude\n gt: Latitude\n lte: Latitude\n gte: Latitude\n in: [Latitude]\n nin: [Latitude]\n}\n\ntype DateTimeValue implements FieldValue {\n value: DateTime\n displayValue: String\n format: String\n}\n\nenum __DirectiveLocation {\n QUERY\n MUTATION\n FIELD\n FRAGMENT_DEFINITION\n FRAGMENT_SPREAD\n INLINE_FRAGMENT\n SCHEMA\n SCALAR\n OBJECT\n FIELD_DEFINITION\n ARGUMENT_DEFINITION\n INTERFACE\n UNION\n ENUM\n ENUM_VALUE\n INPUT_OBJECT\n INPUT_FIELD_DEFINITION\n}\n\ntype IntAggregate implements FieldValue {\n value: Int\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n grouping: IntValue\n max: IntValue\n min: IntValue\n sum: LongValue\n}\n\ntype ListOrder {\n fieldApiName: String!\n sortDirection: ResultOrder\n}\n\ntype RecordAggregate @generic {\n ApiName: String!\n BooleanAggregate: BooleanAggregate @fieldCategory\n CurrencyAggregate: CurrencyAggregate @fieldCategory\n DateAggregate: DateAggregate @fieldCategory\n DoubleAggregate: DoubleAggregate @fieldCategory\n EmailAggregate: EmailAggregate @fieldCategory\n IDAggregate: IDAggregate @fieldCategory\n IntAggregate: IntAggregate @fieldCategory\n LatitudeAggregate: LatitudeAggregate @fieldCategory\n LongitudeAggregate: LongitudeAggregate @fieldCategory\n LongAggregate: LongAggregate @fieldCategory\n PercentAggregate: PercentAggregate @fieldCategory\n PhoneNumberAggregate: PhoneNumberAggregate @fieldCategory\n PicklistAggregate: PicklistAggregate @fieldCategory\n StringAggregate: StringAggregate @fieldCategory\n TextAreaAggregate: TextAreaAggregate @fieldCategory\n TimeAggregate: TimeAggregate @fieldCategory\n UrlAggregate: UrlAggregate @fieldCategory\n}\n\ntype JSONValue implements FieldValue {\n value: JSON\n displayValue: String\n}\n\ntype EmailValue implements FieldValue {\n value: Email\n displayValue: String\n}\n\ntype LongValue implements FieldValue {\n value: Long\n displayValue: String\n format: String\n}\n\ninput DateFunctionInput {\n value: LongOperators\n convertTimezoneValue: LongOperators\n}\n\ntype DependentField {\n controllingField: String!\n dependentFields: [String]!\n}\n\ninput LongTextAreaOperators {\n eq: LongTextArea\n ne: LongTextArea\n like: LongTextArea\n lt: LongTextArea\n gt: LongTextArea\n lte: LongTextArea\n gte: LongTextArea\n in: [LongTextArea]\n nin: [LongTextArea]\n}\n\nenum __TypeKind {\n SCALAR\n OBJECT\n INTERFACE\n UNION\n ENUM\n INPUT_OBJECT\n LIST\n NON_NULL\n}\n\ntype PercentValue implements FieldValue {\n value: Percent\n displayValue: String\n format: String\n}\n\ninput DateTimeOperators {\n eq: DateTimeInput\n ne: DateTimeInput\n lt: DateTimeInput\n gt: DateTimeInput\n lte: DateTimeInput\n gte: DateTimeInput\n in: [DateTimeInput]\n nin: [DateTimeInput]\n DAY_IN_WEEK: DateFunctionInput\n DAY_IN_MONTH: DateFunctionInput\n DAY_IN_YEAR: DateFunctionInput\n WEEK_IN_MONTH: DateFunctionInput\n WEEK_IN_YEAR: DateFunctionInput\n CALENDAR_MONTH: DateFunctionInput\n CALENDAR_QUARTER: DateFunctionInput\n CALENDAR_YEAR: DateFunctionInput\n FISCAL_MONTH: DateFunctionInput\n FISCAL_QUARTER: DateFunctionInput\n FISCAL_YEAR: DateFunctionInput\n DAY_ONLY: DateTimeFunctionInput\n HOUR_IN_DAY: DateFunctionInput\n}\n\ntype BooleanAggregate implements FieldValue {\n value: Boolean\n displayValue: String\n grouping: IntValue\n}\n\ntype RecordQueryAggregate {\n recordQueryAggregate(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy, scope: String, groupBy: RecordGroupBy): RecordAggregateConnection @fieldCategory\n}\n\ntype RecordConnection @generic {\n edges: [RecordEdge]\n pageInfo: PageInfo!\n totalCount: Int!\n}\n\ntype FilteredLookupInfo {\n controllingFields: [String]!\n dependent: Boolean!\n optionalFilter: Boolean!\n}\n\ninput PhoneNumberOperators {\n eq: PhoneNumber\n ne: PhoneNumber\n like: PhoneNumber\n lt: PhoneNumber\n gt: PhoneNumber\n lte: PhoneNumber\n gte: PhoneNumber\n in: [PhoneNumber]\n nin: [PhoneNumber]\n}\n\ntype ObjectInfo {\n ApiName: String!\n childRelationships: [ChildRelationship]!\n createable: Boolean!\n custom: Boolean!\n defaultRecordTypeId: ID\n deletable: Boolean!\n dependentFields: [DependentField]!\n feedEnabled: Boolean!\n fields: [Field]!\n keyPrefix: String\n label: String\n labelPlural: String\n layoutable: Boolean!\n mruEnabled: Boolean!\n nameFields: [String]!\n queryable: Boolean!\n recordTypeInfos: [RecordTypeInfo]!\n searchable: Boolean!\n themeInfo: ThemeInfo\n updateable: Boolean!\n}\n\ninput LongitudeOperators {\n eq: Longitude\n ne: Longitude\n lt: Longitude\n gt: Longitude\n lte: Longitude\n gte: Longitude\n in: [Longitude]\n nin: [Longitude]\n}\n\ntype Field {\n ApiName: String!\n calculated: Boolean!\n compound: Boolean!\n compoundComponentName: String\n compoundFieldName: String\n controllerName: String\n controllingFields: [String]!\n createable: Boolean!\n custom: Boolean!\n dataType: DataType\n extraTypeInfo: FieldExtraTypeInfo\n filterable: Boolean!\n filteredLookupInfo: FilteredLookupInfo\n highScaleNumber: Boolean!\n htmlFormatted: Boolean!\n inlineHelpText: String\n label: String\n nameField: Boolean!\n polymorphicForeignKey: Boolean!\n precision: Int\n reference: Boolean!\n referenceTargetField: String\n referenceToInfos: [ReferenceToInfo]!\n relationshipName: String\n required: Boolean!\n scale: Int\n searchPrefilterable: Boolean\n sortable: Boolean!\n updateable: Boolean!\n}\n\nenum FieldExtraTypeInfo {\n IMAGE_URL\n EXTERNAL_LOOKUP\n INDIRECT_LOOKUP\n PERSONNAME\n SWITCHABLE_PERSONNAME\n PLAINTEXTAREA\n RICHTEXTAREA\n}\n\ntype RateLimit {\n cost: Long\n limit: Long\n remaining: Long\n resetAt: DateTime\n}\n\ninput DateRange {\n last_n_days: Int\n next_n_days: Int\n last_n_weeks: Int\n next_n_weeks: Int\n last_n_months: Int\n next_n_months: Int\n last_n_quarters: Int\n next_n_quarters: Int\n last_n_fiscal_quarters: Int\n next_n_fiscal_quarters: Int\n last_n_years: Int\n next_n_years: Int\n last_n_fiscal_years: Int\n next_n_fiscal_years: Int\n n_days_ago: Int\n n_weeks_ago: Int\n n_months_ago: Int\n n_quarters_ago: Int\n n_years_ago: Int\n n_fiscal_quarters_ago: Int\n n_fiscal_years_ago: Int\n}\n\ninput DateTimeFunctionInput {\n value: DateTimePrimitiveOperators\n convertTimezoneValue: DateTimePrimitiveOperators\n}\n\ntype Base64Value implements FieldValue {\n value: Base64\n displayValue: String\n}\n\ninput IntegerOperators {\n eq: Int\n ne: Int\n lt: Int\n gt: Int\n lte: Int\n gte: Int\n in: [Int]\n nin: [Int]\n}\n\ntype EncryptedStringValue implements FieldValue {\n value: EncryptedString\n displayValue: String\n}\n\ninterface Record {\n Id: ID!\n ApiName: String!\n WeakEtag: Long!\n DisplayValue: String\n LastModifiedById: IDValue\n LastModifiedDate: DateTimeValue\n SystemModstamp: DateTimeValue\n RecordTypeId(fallback: Boolean): IDValue\n}\n\ninput PolymorphicParentRelationshipRecordFilter @generic {\n RecordFilter: RecordFilter @fieldCategory\n}\n\ntype __Schema {\n types: [__Type!]!\n queryType: __Type!\n mutationType: __Type\n directives: [__Directive!]!\n subscriptionType: __Type\n}\n\ninput DateTimeInput {\n value: DateTime\n literal: DateLiteral\n range: DateRange\n}\n\ninput DateTimePrimitiveOperators {\n eq: DateTime\n ne: DateTime\n lt: DateTime\n gt: DateTime\n lte: DateTime\n gte: DateTime\n in: [DateTime]\n nin: [DateTime]\n}\n\ntype ChildRelationship {\n childObjectApiName: String!\n fieldName: String\n junctionIdListNames: [String]!\n junctionReferenceTo: [String]!\n relationshipName: String\n objectInfo: ObjectInfo\n}\n\ntype RecordResult @generic {\n aggregate: RecordAggregate\n}\n\ntype PageInfo {\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n startCursor: String\n endCursor: String\n}\n\ntype CurrencyValue implements FieldValue {\n value: Currency\n displayValue: String\n format: String\n}\n\ninput DateInput {\n value: Date\n literal: DateLiteral\n range: DateRange\n}\n\ninput RecordGroupBy @generic {\n groupableField: GroupByClause @fieldCategory\n groupableDateField: GroupByDateFunction @fieldCategory\n groupableParentRelationship: RecordGroupBy @fieldCategory\n groupablePolymorphicParentRelationship: PolymorphicParentRelationshipGroupBy @fieldCategory\n type: GroupByType = GROUP_BY\n}\n\ntype DateFunctionAggregation {\n value: Long\n format: String\n}\n\ntype RecordQuery {\n # scope should be type RecordScope but it cannot currently be used\n recordQuery(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy, scope: String): RecordConnection @fieldCategory\n}\n\ndirective @generic on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT\ndirective @fieldCategory on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE\ndirective @category on FIELD\n\n\n\n\n\n\n\n";
8689
8898
 
@@ -15767,4 +15976,4 @@ register({
15767
15976
  });
15768
15977
 
15769
15978
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15770
- // version: 1.152.4-cef16daed
15979
+ // version: 1.154.0-3a36aab7e
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.152.4",
3
+ "version": "1.154.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
@@ -57,16 +57,16 @@
57
57
  {
58
58
  "path": "./dist/main.js",
59
59
  "maxSize": {
60
- "none": "675 kB",
61
- "min": "275 kB",
60
+ "none": "700 kB",
61
+ "min": "280 kB",
62
62
  "compressed": "110 kB"
63
63
  }
64
64
  },
65
65
  {
66
66
  "path": "./sfdc/main.js",
67
67
  "maxSize": {
68
- "none": "675 kB",
69
- "min": "275 kB",
68
+ "none": "700 kB",
69
+ "min": "280 kB",
70
70
  "compressed": "110 kB"
71
71
  }
72
72
  }
package/sfdc/main.js CHANGED
@@ -521,7 +521,7 @@ function isDeprecatedDurableStoreEntry(durableRecord) {
521
521
  const DefaultDurableSegment = 'DEFAULT';
522
522
  const RedirectDurableSegment = 'REDIRECT_KEYS';
523
523
 
524
- const { keys: keys$6, create: create$5, assign: assign$4, freeze: freeze$1 } = Object;
524
+ const { keys: keys$6, create: create$5, assign: assign$5, freeze: freeze$1 } = Object;
525
525
 
526
526
  //Durable store error instrumentation key
527
527
  const DURABLE_STORE_ERROR = 'durable-store-error';
@@ -4892,7 +4892,7 @@ function createDraftSynthesisErrorResponse(message = 'failed to synthesize draft
4892
4892
  return new DraftErrorFetchResponse(HttpStatusCode.BadRequest, error);
4893
4893
  }
4894
4894
 
4895
- const { keys: keys$4, create: create$4, assign: assign$3, values: values$2 } = Object;
4895
+ const { keys: keys$4, create: create$4, assign: assign$4, values: values$2 } = Object;
4896
4896
  const { stringify: stringify$4, parse: parse$4 } = JSON;
4897
4897
  const { isArray: isArray$3 } = Array;
4898
4898
 
@@ -6471,6 +6471,104 @@ function isEntryDurableRecordRepresentation(entry, key) {
6471
6471
  entry.data.__type === undefined);
6472
6472
  }
6473
6473
 
6474
+ function serializeFieldArguments(argumentNodes, variables) {
6475
+ const mutableArgumentNodes = Object.assign([], argumentNodes);
6476
+ return `args__(${mutableArgumentNodes
6477
+ .sort((a, b) => {
6478
+ const aName = a.name.value.toUpperCase();
6479
+ const bName = b.name.value.toUpperCase();
6480
+ return aName < bName ? -1 : aName > bName ? 1 : 0;
6481
+ })
6482
+ .map((node) => serializeArgNode(node, variables))
6483
+ .join('::')})`;
6484
+ }
6485
+ function serializeArgNode(argumentNode, variables) {
6486
+ const argName = argumentNode.name.value;
6487
+ return `${argName}:${serializeValueNode(argumentNode.value, variables)}`;
6488
+ }
6489
+ function serializeValueNode(valueNode, variables) {
6490
+ switch (valueNode.kind) {
6491
+ case 'BooleanValue':
6492
+ return valueNode.value + '';
6493
+ case 'IntValue':
6494
+ case 'FloatValue':
6495
+ case 'EnumValue':
6496
+ case 'StringValue':
6497
+ return valueNode.value;
6498
+ case 'ListValue': {
6499
+ const mutableValueNodeList = Object.assign([], valueNode.values);
6500
+ return mutableValueNodeList
6501
+ .sort((a, b) => {
6502
+ const aVal = serializeValueNode(a, variables).toUpperCase();
6503
+ const bVal = serializeValueNode(b, variables).toUpperCase();
6504
+ return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
6505
+ })
6506
+ .map((val, i) => `${serializeValueNode(val, variables)}[${i}]`)
6507
+ .join(',');
6508
+ }
6509
+ case 'Variable': {
6510
+ const variableValue = variables[valueNode.name.value];
6511
+ return typeof variableValue === 'string'
6512
+ ? variableValue
6513
+ : JSON.stringify(variableValue);
6514
+ }
6515
+ case 'NullValue':
6516
+ return 'null';
6517
+ case 'ObjectValue': {
6518
+ const mutableFieldNodeList = Object.assign([], valueNode.fields);
6519
+ return mutableFieldNodeList
6520
+ .sort((a, b) => {
6521
+ const aName = a.name.value.toUpperCase();
6522
+ const bName = b.name.value.toUpperCase();
6523
+ return aName < bName ? -1 : aName > bName ? 1 : 0;
6524
+ })
6525
+ .map((field) => field.name.value + ':' + serializeValueNode(field.value, variables))
6526
+ .join(',');
6527
+ }
6528
+ }
6529
+ }
6530
+
6531
+ function serializeOperationNode(operationNode, variables, fragmentMap) {
6532
+ return `${serializeSelectionSet(operationNode.selectionSet, variables, fragmentMap)}`;
6533
+ }
6534
+ function serializeSelectionSet(selectionSetNode, variables, fragmentMap) {
6535
+ return `${selectionSetNode.selections
6536
+ .map((selection) => serializeSelectionNode(selection, variables, fragmentMap))
6537
+ .join()}`;
6538
+ }
6539
+ /**
6540
+ *
6541
+ * @description This function takes a GraphQL SelectionNode from an AST and serializes it in a stable way, so we can
6542
+ * use it for property names and Store keys.
6543
+ * @param selectionNode
6544
+ * @param variables
6545
+ * @param fragmentMap
6546
+ * @returns string
6547
+ */
6548
+ function serializeSelectionNode(selectionNode, variables, fragmentMap) {
6549
+ switch (selectionNode.kind) {
6550
+ case 'Field': {
6551
+ const hasArguments = selectionNode.arguments !== undefined && selectionNode.arguments.length > 0;
6552
+ const argumentSuffix = hasArguments
6553
+ ? `__${serializeFieldArguments(selectionNode.arguments, variables)}`
6554
+ : '';
6555
+ return `${selectionNode.name.value}${argumentSuffix}`;
6556
+ }
6557
+ case 'FragmentSpread': {
6558
+ const fragment = fragmentMap[selectionNode.name.value];
6559
+ return fragment === undefined
6560
+ ? selectionNode.name.value
6561
+ : serializeSelectionSet(fragment.selectionSet, variables, fragmentMap);
6562
+ }
6563
+ case 'InlineFragment':
6564
+ return serializeSelectionSet(selectionNode.selectionSet, variables, fragmentMap);
6565
+ }
6566
+ }
6567
+ function buildQueryTypeStringKey(args) {
6568
+ const { keyPrefix, schemaName, queryTypeName, operationNode, variables, fragmentMap } = args;
6569
+ return `${keyPrefix}::${schemaName}::${queryTypeName}[${serializeOperationNode(operationNode, variables, fragmentMap)}]`;
6570
+ }
6571
+
6474
6572
  /**
6475
6573
  * Copyright (c) 2022, Salesforce, Inc.,
6476
6574
  * All rights reserved.
@@ -6604,7 +6702,7 @@ function isArrayLike(x) {
6604
6702
  (x.length === 0 || (x.length > 0 && Object.prototype.hasOwnProperty.call(x, x.length - 1))));
6605
6703
  }
6606
6704
 
6607
- const { create: create$3, keys: keys$3, values: values$1, entries: entries$2 } = Object;
6705
+ const { create: create$3, keys: keys$3, values: values$1, entries: entries$2, assign: assign$3 } = Object;
6608
6706
  const { stringify: stringify$3, parse: parse$3 } = JSON;
6609
6707
  const { isArray: isArray$2 } = Array;
6610
6708
 
@@ -7048,6 +7146,10 @@ function dateRangesFrom(dateRange, input, dateFunction) {
7048
7146
  }
7049
7147
  }
7050
7148
 
7149
+ const JSON_EXTRACT_PATH_INGESTION_TIMESTAMP = '$.ingestionTimestamp';
7150
+ const JSON_EXTRACT_PATH_INGESTION_APINAME = '$.apiName';
7151
+ const JSON_EXTRACT_PATH_DRAFTS = '$.drafts';
7152
+
7051
7153
  const MultiPickListValueSeparator = ';';
7052
7154
  function filterToPredicates(where, recordType, alias, objectInfoMap, joins, draftFunctions) {
7053
7155
  if (!where)
@@ -7109,7 +7211,7 @@ function filterToPredicates(where, recordType, alias, objectInfoMap, joins, draf
7109
7211
  return [
7110
7212
  {
7111
7213
  alias: childAlias,
7112
- leftPath: '$.apiName',
7214
+ leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
7113
7215
  operator: '=',
7114
7216
  value: entityName,
7115
7217
  dataType: 'String',
@@ -7570,11 +7672,18 @@ function buildQuery(config) {
7570
7672
  const joins = buildJoins(config);
7571
7673
  const predicates = buildPredicates(config);
7572
7674
  const orderBy = buildOrderBy(config);
7675
+ const staleRecordsSql = excludeStaleRecordsGate.isOpen({ fallback: false })
7676
+ ? `AND (
7677
+ json_extract("${config.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ?
7678
+ OR json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL
7679
+ )`
7680
+ : '';
7573
7681
  const sql = `
7574
7682
  SELECT "${config.alias}".data
7575
7683
  FROM lds_data "${config.alias}" ${joins.sql}
7576
7684
  WHERE "${config.alias}".key like 'UiApi::RecordRepresentation:%'
7577
- AND json_extract("${config.alias}".data, '$.apiName') = ?
7685
+ AND json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_INGESTION_APINAME}') = ?
7686
+ ${staleRecordsSql}
7578
7687
  ${predicates.sql}
7579
7688
  ${orderBy.sql}
7580
7689
  LIMIT ?
@@ -7587,6 +7696,7 @@ function buildQuery(config) {
7587
7696
  ...joins.bindings,
7588
7697
  // the api name for the main record type
7589
7698
  config.alias,
7699
+ ...(excludeStaleRecordsGate.isOpen({ fallback: false }) ? [config.ingestionTimestamp] : []),
7590
7700
  // where clause and parent scope bindings
7591
7701
  ...predicates.bindings,
7592
7702
  // limit binding
@@ -7612,19 +7722,33 @@ function buildJoins(config) {
7612
7722
  if (allJoins.length === 0)
7613
7723
  return { sql, bindings };
7614
7724
  sql = allJoins.reduce((joinAccumulator, join) => {
7725
+ let timestampAdded = false;
7615
7726
  const joinConditions = join.conditions.reduce((conditionAccumulator, condition) => {
7616
7727
  let joined_sql;
7728
+ const joinMetadataTimestamp = excludeStaleRecordsGate.isOpen({ fallback: false })
7729
+ ? ` AND (json_extract("${join.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ? OR json_extract("${join.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL)`
7730
+ : '';
7617
7731
  // predicate on a value, use the newly joined table
7618
7732
  if ('type' in condition) {
7619
7733
  const { sql, binding } = predicateToSQL(condition, join.alias);
7620
- joined_sql = ` AND ${sql}`;
7734
+ joined_sql = ` AND ${sql}${timestampAdded ? '' : joinMetadataTimestamp}`;
7621
7735
  bindings.push(...binding);
7736
+ if (excludeStaleRecordsGate.isOpen({ fallback: false }) &&
7737
+ timestampAdded === false) {
7738
+ bindings.push(config.ingestionTimestamp);
7739
+ timestampAdded = true;
7740
+ }
7622
7741
  }
7623
7742
  else {
7624
7743
  // predicate on a path
7625
7744
  const left = ` AND json_extract("${join.to}".data, '${condition.leftPath}')`;
7626
7745
  const right = `json_extract("${join.alias}".data, '${condition.rightPath}')`;
7627
- joined_sql = `${left} = ${right}`;
7746
+ joined_sql = `${left} = ${right}${timestampAdded ? '' : joinMetadataTimestamp}`;
7747
+ if (excludeStaleRecordsGate.isOpen({ fallback: false }) &&
7748
+ timestampAdded === false) {
7749
+ bindings.push(config.ingestionTimestamp);
7750
+ timestampAdded = true;
7751
+ }
7628
7752
  }
7629
7753
  conditionAccumulator += joined_sql;
7630
7754
  return conditionAccumulator;
@@ -8303,7 +8427,7 @@ function orderByToPredicate(orderBy, recordType, alias, objectInfoMap, joins) {
8303
8427
  function pathForKey(key) {
8304
8428
  switch (key) {
8305
8429
  case 'ApiName':
8306
- return '$.apiName';
8430
+ return JSON_EXTRACT_PATH_INGESTION_APINAME;
8307
8431
  case 'WeakEtag':
8308
8432
  return '$.weakEtag';
8309
8433
  case 'Id':
@@ -8325,7 +8449,7 @@ function scopeToJoins(scope = '', settings) {
8325
8449
  {
8326
8450
  type: PredicateType.single,
8327
8451
  alias: 'ServiceAppointment_AssignedResource',
8328
- leftPath: '$.apiName',
8452
+ leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
8329
8453
  operator: '=',
8330
8454
  value: 'AssignedResource',
8331
8455
  dataType: 'String',
@@ -8346,7 +8470,7 @@ function scopeToJoins(scope = '', settings) {
8346
8470
  {
8347
8471
  type: PredicateType.single,
8348
8472
  alias: 'ServiceAppointment_AssignedResource_ServiceResource',
8349
- leftPath: '$.apiName',
8473
+ leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
8350
8474
  operator: '=',
8351
8475
  value: 'ServiceResource',
8352
8476
  dataType: 'String',
@@ -8451,14 +8575,21 @@ function addResolversToSchema(schema, polyFields) {
8451
8575
  // Fields of the `RecordQuery` type are the record queries for the entity types
8452
8576
  // supported for the org
8453
8577
  for (const recordQuery of fields) {
8454
- recordQuery.resolve = function recordConnectionResolver(record, args) {
8578
+ recordQuery.resolve = async function recordConnectionResolver(record, args, { query }, info) {
8579
+ const { name: currentFieldName } = recordQuery;
8580
+ let ingestionTimestamp = 0;
8581
+ if (excludeStaleRecordsGate.isOpen({ fallback: false })) {
8582
+ // at our record query we fetch each ingestion time stamp and pass it down to each lower resolver to query against
8583
+ ingestionTimestamp = await fetchIngestionTimeStampFromDatabase(currentFieldName, info, args, query);
8584
+ }
8455
8585
  // In the SF schema, the relevant arguments are passed into RecordQuery fields, but actually used
8456
8586
  // down in the edge resolvers. For this resolver, we can just return what was passed in
8457
8587
  // to make it available to the next execution step
8458
8588
  return {
8459
8589
  parentArgs: args,
8460
8590
  parentRecord: record,
8461
- currentFieldName: recordQuery.name,
8591
+ currentFieldName,
8592
+ ingestionTimestamp,
8462
8593
  };
8463
8594
  };
8464
8595
  }
@@ -8504,10 +8635,11 @@ function addResolversToSchema(schema, polyFields) {
8504
8635
  // }
8505
8636
  for (const field of fields) {
8506
8637
  if (field.name === 'node') {
8507
- field.resolve = function nodeResolver(record, _args, { seenRecordIds }) {
8638
+ field.resolve = function nodeResolver(obj, _args, { seenRecordIds }) {
8639
+ const { record, ingestionTimestamp } = obj;
8508
8640
  const recordRepresentation = parse$3(record);
8509
8641
  seenRecordIds.add(recordRepresentation.id);
8510
- return recordRepresentation;
8642
+ return { recordRepresentation, ingestionTimestamp };
8511
8643
  };
8512
8644
  }
8513
8645
  }
@@ -8530,40 +8662,40 @@ function addResolversToSchema(schema, polyFields) {
8530
8662
  for (const field of fields) {
8531
8663
  switch (field.name) {
8532
8664
  case 'Id':
8533
- field.resolve = (record) => record.id;
8665
+ field.resolve = ({ recordRepresentation: record }) => record.id;
8534
8666
  break;
8535
8667
  case 'ApiName':
8536
- field.resolve = (record) => record.apiName;
8668
+ field.resolve = ({ recordRepresentation: record }) => record.apiName;
8537
8669
  break;
8538
8670
  case 'WeakEtag':
8539
- field.resolve = (record) => record.weakEtag;
8671
+ field.resolve = ({ recordRepresentation: record }) => record.weakEtag;
8540
8672
  break;
8541
8673
  case '_drafts':
8542
- field.resolve = (record) => {
8674
+ field.resolve = ({ recordRepresentation: record, }) => {
8543
8675
  return record.drafts ? record.drafts : null;
8544
8676
  };
8545
8677
  break;
8546
8678
  case 'LastModifiedById':
8547
- field.resolve = (record) => {
8679
+ field.resolve = ({ recordRepresentation: record }) => {
8548
8680
  return record.lastModifiedById
8549
8681
  ? { value: record.lastModifiedById }
8550
8682
  : null;
8551
8683
  };
8552
8684
  break;
8553
8685
  case 'LastModifiedDate':
8554
- field.resolve = (record) => {
8686
+ field.resolve = ({ recordRepresentation: record }) => {
8555
8687
  return record.lastModifiedDate
8556
8688
  ? { value: record.lastModifiedDate }
8557
8689
  : null;
8558
8690
  };
8559
8691
  break;
8560
8692
  case 'SystemModstamp':
8561
- field.resolve = (record) => {
8693
+ field.resolve = ({ recordRepresentation: record }) => {
8562
8694
  return record.systemModstamp ? { value: record.systemModstamp } : null;
8563
8695
  };
8564
8696
  break;
8565
8697
  case 'RecordTypeId':
8566
- field.resolve = (record) => {
8698
+ field.resolve = ({ recordRepresentation: record }) => {
8567
8699
  return record.recordTypeId ? { value: record.recordTypeId } : null;
8568
8700
  };
8569
8701
  break;
@@ -8575,7 +8707,17 @@ function addResolversToSchema(schema, polyFields) {
8575
8707
  .getInterfaces()
8576
8708
  .find((iface) => iface.name === 'Record')) ||
8577
8709
  (recordFieldType && recordFieldType.name === 'Record')) {
8578
- field.resolve = function relationResolver(record, _args, { Record, seenRecordIds }) {
8710
+ field.resolve = async function relationResolver(obj, _args, { Record, seenRecordIds }) {
8711
+ const fetchRecordOrNull = async (key) => {
8712
+ const recordRepresentation = await Record.load(key);
8713
+ return recordRepresentation !== null
8714
+ ? {
8715
+ recordRepresentation,
8716
+ ingestionTimestamp,
8717
+ }
8718
+ : null;
8719
+ };
8720
+ const { recordRepresentation: record, ingestionTimestamp } = obj;
8579
8721
  const fieldName = field.name.endsWith('__r')
8580
8722
  ? field.name.replace('__r', '__c')
8581
8723
  : field.name;
@@ -8585,26 +8727,28 @@ function addResolversToSchema(schema, polyFields) {
8585
8727
  if (!id)
8586
8728
  return null;
8587
8729
  if (id['__ref'] !== undefined) {
8588
- return Record.load(record.fields[`${field.name}Id`].value);
8730
+ return fetchRecordOrNull(record.fields[`${field.name}Id`].value);
8589
8731
  }
8590
8732
  seenRecordIds.add(id);
8591
- return Record.load(id);
8733
+ return fetchRecordOrNull(id);
8592
8734
  };
8593
8735
  }
8594
8736
  else if (isObjectType(recordFieldType) &&
8595
8737
  field.type.name.endsWith('Connection')) {
8596
8738
  // spanning field to a connection
8597
- field.resolve = (record, args, { seenRecordIds }) => {
8598
- seenRecordIds.add(record.id);
8739
+ field.resolve = async ({ recordRepresentation, ingestionTimestamp }, args, { seenRecordIds }) => {
8740
+ seenRecordIds.add(recordRepresentation.id);
8741
+ const { name: currentFieldName } = field;
8599
8742
  return {
8600
8743
  parentArgs: args,
8601
- parentRecord: record,
8602
- currentFieldName: field.name,
8744
+ parentRecord: recordRepresentation,
8745
+ currentFieldName,
8746
+ ingestionTimestamp,
8603
8747
  };
8604
8748
  };
8605
8749
  }
8606
8750
  else {
8607
- field.resolve = function recordFieldResolver(record) {
8751
+ field.resolve = function recordFieldResolver({ recordRepresentation: record, }) {
8608
8752
  return record.fields[field.name] || null;
8609
8753
  };
8610
8754
  }
@@ -8616,7 +8760,7 @@ function addResolversToSchema(schema, polyFields) {
8616
8760
  if (recordInterface !== undefined && baseRecord !== undefined) {
8617
8761
  // Applys 'resolveType' of GraphQLInterfaceType to 'Record' interface. Since all the heterogenous types are named as 'apiName', the type with same name as the loaded record 'apiName' property is the type wanted.
8618
8762
  // GraphQL executor would match InLineFragment' condition with type and keeps the deeper level field resolving going.
8619
- recordInterface.resolveType = function (value) {
8763
+ recordInterface.resolveType = function ({ recordRepresentation: value, }) {
8620
8764
  const targetType = polyTypes.find((type) => type.name === value.apiName);
8621
8765
  return targetType === undefined ? baseRecord : targetType;
8622
8766
  };
@@ -8624,7 +8768,7 @@ function addResolversToSchema(schema, polyFields) {
8624
8768
  return schema;
8625
8769
  }
8626
8770
  async function connectionEdgeResolver(obj, _args, context) {
8627
- const { parentArgs = {}, parentRecord, currentFieldName } = obj;
8771
+ const { parentArgs = {}, parentRecord, currentFieldName, ingestionTimestamp } = obj;
8628
8772
  const { query, objectInfos, draftFunctions } = context;
8629
8773
  let joins = [];
8630
8774
  let alias = currentFieldName;
@@ -8655,10 +8799,19 @@ async function connectionEdgeResolver(obj, _args, context) {
8655
8799
  predicates,
8656
8800
  orderBy: orderByToPredicate(parentArgs.orderBy, alias, alias, context.objectInfos),
8657
8801
  limit: parentArgs.first,
8802
+ ingestionTimestamp,
8658
8803
  };
8659
8804
  const { sql, bindings } = buildQuery(queryConfig);
8660
8805
  const results = await query(sql, bindings);
8661
- return results.rows.map((row) => row[0]);
8806
+ //map each sql result with the ingestion timestamp to pass it down a level
8807
+ return results.rows
8808
+ .map((row) => row[0])
8809
+ .map((record) => {
8810
+ return {
8811
+ record,
8812
+ ingestionTimestamp,
8813
+ };
8814
+ });
8662
8815
  }
8663
8816
  /**
8664
8817
  * Converts a childRelationship into a predicate
@@ -8684,6 +8837,62 @@ function isRecordType(type) {
8684
8837
  const interfaces = type.getInterfaces();
8685
8838
  return Boolean(interfaces.find((iface) => iface.name === 'Record'));
8686
8839
  }
8840
+ /**
8841
+ * Builds the top level record query key based on AST data
8842
+ * @param operation
8843
+ * @param variables
8844
+ * @param argumentNodes
8845
+ * @param currentFieldName
8846
+ * @returns
8847
+ */
8848
+ function buildKeyStringForRecordQuery(operation, variables, argumentNodes, currentFieldName) {
8849
+ const queryKey = buildQueryTypeStringKey({
8850
+ luvio: {},
8851
+ keyPrefix: 'UiApi',
8852
+ schemaName: 'uiapi',
8853
+ queryTypeName: 'Query',
8854
+ operationNode: operation,
8855
+ variables,
8856
+ fragmentMap: {},
8857
+ });
8858
+ const filteredArgumentNodes = assign$3([], argumentNodes).filter((node) => node.name.value !== 'first' && node.name.value !== 'after');
8859
+ const argumentString = filteredArgumentNodes.length > 0
8860
+ ? '__' + serializeFieldArguments(filteredArgumentNodes, variables)
8861
+ : '';
8862
+ return `${queryKey}__uiapi__query__${currentFieldName}${argumentString}`;
8863
+ }
8864
+ /**
8865
+ * fetches a query level ingestion time stamp from the L2 cache
8866
+ * if no query has been seen then the timestamp is 0
8867
+ * @param apiName
8868
+ * @param info
8869
+ * @param args
8870
+ * @param query
8871
+ * @returns
8872
+ */
8873
+ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
8874
+ const { operation, variableValues } = info;
8875
+ // if we cannot find the query key in the database then default to 0 as we assume we have not seen the query
8876
+ // and all the data is not stale
8877
+ let ingestionTimestamp = 0;
8878
+ if (info.fieldNodes.length > 0 && info.fieldNodes[0].arguments !== undefined) {
8879
+ const key = buildKeyStringForRecordQuery(operation,
8880
+ // join varables passed from query to the argument variables given from the AST
8881
+ { ...variableValues, ...args }, info.fieldNodes[0].arguments, apiName);
8882
+ const sql = `
8883
+ SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
8884
+ FROM lds_data
8885
+ WHERE key IS ?
8886
+ `;
8887
+ const results = await query(sql, [key]);
8888
+ const [timestamp] = results.rows.map((row) => row[0]);
8889
+ if (timestamp !== null && typeof timestamp === 'number') {
8890
+ //go back 10 ms to adjust for margin of error when top level query is stored and when raml objects are stored
8891
+ ingestionTimestamp = timestamp - 10;
8892
+ }
8893
+ }
8894
+ return ingestionTimestamp;
8895
+ }
8687
8896
 
8688
8897
  var uiapiSchemaString = "scalar String\nscalar DateTime\nscalar Currency\nscalar ID\nscalar Boolean\nscalar Longitude\nscalar Float\nscalar MultiPicklist\nscalar Base64\nscalar Url\nscalar PhoneNumber\nscalar Email\nscalar TextArea\nscalar Latitude\nscalar Picklist\nscalar RichTextArea\nscalar EncryptedString\nscalar Double\nscalar Long\nscalar JSON\nscalar Time\nscalar Int\nscalar Percent\nscalar LongTextArea\nscalar Date\ntype PercentAggregate implements FieldValue {\n value: Percent\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: PercentValue\n min: PercentValue\n sum: PercentValue\n}\n\ntype StringAggregate implements FieldValue {\n value: String\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n label: String\n max: StringValue\n min: StringValue\n}\n\ntype Query {\n uiapi: UIAPI!\n rateLimit: RateLimit\n}\n\ninput EmailOperators {\n eq: Email\n ne: Email\n like: Email\n lt: Email\n gt: Email\n lte: Email\n gte: Email\n in: [Email]\n nin: [Email]\n}\n\ninput PolymorphicParentRelationshipRecordOrderBy @generic {\n RecordOrderBy: RecordOrderBy @fieldCategory\n}\n\ninput DoubleOperators {\n eq: Double\n ne: Double\n lt: Double\n gt: Double\n lte: Double\n gte: Double\n in: [Double]\n nin: [Double]\n}\n\ntype DateOnlyAggregation {\n value: Date\n format: String\n}\n\ntype DateAggregate implements FieldValue {\n value: Date\n displayValue: String\n calendarMonth: DateFunctionAggregation\n calendarQuarter: DateFunctionAggregation\n calendarYear: DateFunctionAggregation\n count: LongValue\n countDistinct: LongValue\n dayInMonth: DateFunctionAggregation\n dayInWeek: DateFunctionAggregation\n dayInYear: DateFunctionAggregation\n fiscalMonth: DateFunctionAggregation\n fiscalQuarter: DateFunctionAggregation\n fiscalYear: DateFunctionAggregation\n format: String\n grouping: IntValue\n max: DateValue\n min: DateValue\n weekInMonth: DateFunctionAggregation\n weekInYear: DateFunctionAggregation\n}\n\ninput PolymorphicParentRelationshipGroupBy @generic {\n RecordGroupBy: RecordGroupBy @fieldCategory\n}\n\nenum GroupByFunction {\n DAY_IN_WEEK\n DAY_IN_MONTH\n DAY_IN_YEAR\n WEEK_IN_MONTH\n WEEK_IN_YEAR\n CALENDAR_MONTH\n CALENDAR_QUARTER\n CALENDAR_YEAR\n FISCAL_MONTH\n FISCAL_QUARTER\n FISCAL_YEAR\n DAY_ONLY\n HOUR_IN_DAY\n}\n\ntype RecordTypeInfo {\n available: Boolean!\n defaultRecordTypeMapping: Boolean!\n master: Boolean!\n name: String\n recordTypeId: ID\n}\n\ntype BooleanValue implements FieldValue {\n value: Boolean\n displayValue: String\n}\n\ntype ReferenceToInfo {\n ApiName: String!\n nameFields: [String]!\n objectInfo: ObjectInfo\n}\n\ninterface FieldValue {\n displayValue: String\n}\n\ntype LongitudeValue implements FieldValue {\n value: Longitude\n displayValue: String\n}\n\ntype StringValue implements FieldValue {\n value: String\n displayValue: String\n label: String\n}\n\ntype IntValue implements FieldValue {\n value: Int\n displayValue: String\n format: String\n}\n\ntype UrlValue implements FieldValue {\n value: Url\n displayValue: String\n}\n\ninput IdOperators {\n eq: ID\n ne: ID\n lt: ID\n gt: ID\n lte: ID\n gte: ID\n in: [ID]\n nin: [ID]\n inq: JoinInput\n ninq: JoinInput\n}\n\ntype LongAggregate implements FieldValue {\n value: Long\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n grouping: IntValue\n max: LongValue\n min: LongValue\n sum: LongValue\n}\n\ntype PhoneNumberAggregate implements FieldValue {\n value: PhoneNumber\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: PhoneNumberValue\n min: PhoneNumberValue\n}\n\ninput TimeOperators {\n eq: Time\n ne: Time\n lt: Time\n gt: Time\n lte: Time\n gte: Time\n in: [Time]\n nin: [Time]\n}\n\ntype PicklistValue implements FieldValue {\n value: Picklist\n displayValue: String\n label: String\n}\n\ntype CurrencyAggregate implements FieldValue {\n value: Currency\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: CurrencyValue\n min: CurrencyValue\n sum: CurrencyValue\n}\n\ntype RelatedListInfo {\n childApiName: String!\n relatedListName: String!\n label: String!\n displayColumns: [ListColumn!]!\n orderedByInfo: [ListOrder!]!\n parentApiName: String!\n fieldApiName: String!\n}\n\ninput StringOperators {\n eq: String\n ne: String\n like: String\n lt: String\n gt: String\n lte: String\n gte: String\n in: [String]\n nin: [String]\n}\n\ntype UIAPI {\n query: RecordQuery!\n aggregate: RecordQueryAggregate!\n objectInfos(apiNames: [String]): [ObjectInfo]\n relatedListByName(parentApiName: String!, relatedListName: String!): RelatedListInfo\n}\n\ninput MultiPicklistOperators {\n eq: MultiPicklist\n ne: MultiPicklist\n includes: [MultiPicklist]\n excludes: [MultiPicklist]\n}\n\ntype DateTimeAggregate implements FieldValue {\n value: DateTime\n displayValue: String\n calendarMonth: DateFunctionAggregation\n calendarQuarter: DateFunctionAggregation\n calendarYear: DateFunctionAggregation\n count: LongValue\n countDistinct: LongValue\n dayInMonth: DateFunctionAggregation\n dayInWeek: DateFunctionAggregation\n dayInYear: DateFunctionAggregation\n dayOnly: DateOnlyAggregation\n fiscalMonth: DateFunctionAggregation\n fiscalQuarter: DateFunctionAggregation\n fiscalYear: DateFunctionAggregation\n format: String\n hourInDay: DateFunctionAggregation\n max: DateTimeValue\n min: DateTimeValue\n weekInMonth: DateFunctionAggregation\n weekInYear: DateFunctionAggregation\n}\n\ninput BooleanOperators {\n eq: Boolean\n ne: Boolean\n}\n\ntype EmailAggregate implements FieldValue {\n value: Email\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: EmailValue\n min: EmailValue\n}\n\ninput GroupByDateFunction {\n function: GroupByFunction\n}\n\ntype RichTextAreaValue implements FieldValue {\n value: RichTextArea\n displayValue: String\n}\n\ntype MultiPicklistValue implements FieldValue {\n value: MultiPicklist\n displayValue: String\n label: String\n}\n\ntype TimeAggregate implements FieldValue {\n value: Time\n displayValue: String\n format: String\n hourInDay: DateFunctionAggregation\n}\n\ntype __Type {\n kind: __TypeKind!\n name: String\n description: String\n fields(includeDeprecated: Boolean = false): [__Field!]\n interfaces: [__Type!]\n possibleTypes: [__Type!]\n enumValues(includeDeprecated: Boolean = false): [__EnumValue!]\n inputFields: [__InputValue!]\n ofType: __Type\n}\n\ntype ListColumn {\n fieldApiName: String!\n label: String!\n lookupId: String\n sortable: Boolean\n}\n\ntype LatitudeAggregate implements FieldValue {\n value: Latitude\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n max: LatitudeValue\n min: LatitudeValue\n sum: DoubleValue\n}\n\ninput CurrencyOperators {\n eq: Currency\n ne: Currency\n lt: Currency\n gt: Currency\n lte: Currency\n gte: Currency\n in: [Currency]\n nin: [Currency]\n}\n\ninput DistanceInput {\n latitude: Latitude!\n longitude: Longitude!\n}\n\nunion PolymorphicParentRelationship @generic = RecordRepresentation\n\ntype LongTextAreaValue implements FieldValue {\n value: LongTextArea\n displayValue: String\n}\n\ntype LatitudeValue implements FieldValue {\n value: Latitude\n displayValue: String\n}\n\ninput OrderByClause {\n order: ResultOrder\n nulls: NullOrder\n}\n\ninput GroupByClause {\n group: Boolean\n}\n\ntype RecordAggregateConnection @generic {\n edges: [RecordAggregateEdge]\n pageInfo: PageInfo!\n totalCount: Int!\n}\n\ntype LongitudeAggregate implements FieldValue {\n value: Longitude\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n max: LongitudeValue\n min: LongitudeValue\n sum: DoubleValue\n}\n\ntype RecordEdge @generic {\n node: RecordRepresentation\n cursor: String!\n}\n\ntype DateValue implements FieldValue {\n value: Date\n displayValue: String\n format: String\n}\n\ninput URLOperators {\n eq: Url\n ne: Url\n like: Url\n lt: Url\n gt: Url\n lte: Url\n gte: Url\n in: [Url]\n nin: [Url]\n}\n\ninput LongOperators {\n eq: Long\n ne: Long\n lt: Long\n gt: Long\n lte: Long\n gte: Long\n in: [Long]\n nin: [Long]\n}\n\nenum DataType {\n STRING\n TEXTAREA\n PHONE\n EMAIL\n URL\n ENCRYPTEDSTRING\n BOOLEAN\n CURRENCY\n INT\n LONG\n DOUBLE\n PERCENT\n DATETIME\n TIME\n DATE\n REFERENCE\n PICKLIST\n MULTIPICKLIST\n ADDRESS\n LOCATION\n BASE64\n COMPLEXVALUE\n COMBOBOX\n JSON\n JUNCTIONIDLIST\n ANYTYPE\n}\n\nenum NullOrder {\n LAST\n FIRST\n}\n\ntype PhoneNumberValue implements FieldValue {\n value: PhoneNumber\n displayValue: String\n}\n\n# Cannot have empty enum\n# enum RecordScope @generic {\n# }\n\ntype DoubleAggregate implements FieldValue {\n value: Double\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: DoubleValue\n min: DoubleValue\n sum: DoubleValue\n}\n\ntype __Field {\n name: String!\n description: String\n args: [__InputValue!]!\n type: __Type!\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\ninput DateOperators {\n eq: DateInput\n ne: DateInput\n lt: DateInput\n gt: DateInput\n lte: DateInput\n gte: DateInput\n in: [DateInput]\n nin: [DateInput]\n DAY_IN_WEEK: DateFunctionInput\n DAY_IN_MONTH: DateFunctionInput\n DAY_IN_YEAR: DateFunctionInput\n WEEK_IN_MONTH: DateFunctionInput\n WEEK_IN_YEAR: DateFunctionInput\n CALENDAR_MONTH: DateFunctionInput\n CALENDAR_QUARTER: DateFunctionInput\n CALENDAR_YEAR: DateFunctionInput\n FISCAL_MONTH: DateFunctionInput\n FISCAL_QUARTER: DateFunctionInput\n FISCAL_YEAR: DateFunctionInput\n}\n\ninput GeolocationInput {\n latitude: Latitude!\n longitude: Longitude!\n radius: Float!\n unit: Unit!\n}\n\ninput JoinInput {\n Record: RecordFilter @fieldCategory\n ApiName: String\n}\n\ninput TextAreaOperators {\n eq: TextArea\n ne: TextArea\n like: TextArea\n lt: TextArea\n gt: TextArea\n lte: TextArea\n gte: TextArea\n in: [TextArea]\n nin: [TextArea]\n}\n\ntype TextAreaValue implements FieldValue {\n value: TextArea\n displayValue: String\n}\n\ninput PercentOperators {\n eq: Percent\n ne: Percent\n lt: Percent\n gt: Percent\n lte: Percent\n gte: Percent\n in: [Percent]\n nin: [Percent]\n}\n\ntype DoubleValue implements FieldValue {\n value: Double\n displayValue: String\n format: String\n}\n\ntype IDAggregate implements FieldValue {\n value: ID\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: IDValue\n min: IDValue\n}\n\ntype __InputValue {\n name: String!\n description: String\n type: __Type!\n defaultValue: String\n}\n\ntype RecordAggregateEdge @generic {\n node: RecordResult\n cursor: String!\n}\n\ntype __Directive {\n name: String\n description: String\n locations: [__DirectiveLocation!]\n args: [__InputValue!]!\n}\n\ntype ThemeInfo {\n color: String\n iconUrl: String\n}\n\ntype UrlAggregate implements FieldValue {\n value: Url\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: UrlValue\n min: UrlValue\n}\n\nenum DateLiteral {\n THIS_WEEK\n THIS_FISCAL_QUARTER\n NEXT_YEAR\n TODAY\n LAST_WEEK\n LAST_YEAR\n YESTERDAY\n NEXT_MONTH\n NEXT_FISCAL_YEAR\n NEXT_WEEK\n NEXT_90_DAYS\n LAST_FISCAL_QUARTER\n LAST_FISCAL_YEAR\n THIS_YEAR\n THIS_MONTH\n THIS_QUARTER\n LAST_90_DAYS\n NEXT_FISCAL_QUARTER\n THIS_FISCAL_YEAR\n TOMORROW\n NEXT_QUARTER\n LAST_MONTH\n LAST_QUARTER\n}\n\ntype __EnumValue {\n name: String!\n description: String\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\ntype RecordRepresentation implements Record @generic{\n Id: ID!\n ApiName: String!\n WeakEtag: Long!\n DisplayValue: String\n LastModifiedById: IDValue\n LastModifiedDate: DateTimeValue\n SystemModstamp: DateTimeValue\n RecordTypeId(fallback: Boolean): IDValue\n IntValue: IntValue @fieldCategory\n StringValue: StringValue @fieldCategory\n BooleanValue: BooleanValue @fieldCategory\n IDValue: IDValue @fieldCategory\n DateTimeValue: DateTimeValue @fieldCategory\n TimeValue: TimeValue @fieldCategory\n DateValue: DateValue @fieldCategory\n TextAreaValue: TextAreaValue @fieldCategory\n LongTextAreaValue: LongTextAreaValue @fieldCategory\n RichTextAreaValue: RichTextAreaValue @fieldCategory\n PhoneNumberValue: PhoneNumberValue @fieldCategory\n EmailValue: EmailValue @fieldCategory\n UrlValue: UrlValue @fieldCategory\n EncryptedStringValue: EncryptedStringValue @fieldCategory\n CurrencyValue: CurrencyValue @fieldCategory\n LongitudeValue: LongitudeValue @fieldCategory\n LatitudeValue: LatitudeValue @fieldCategory\n PicklistValue: PicklistValue @fieldCategory\n MultiPicklistValue: MultiPicklistValue @fieldCategory\n LongValue: LongValue @fieldCategory\n DoubleValue: DoubleValue @fieldCategory\n PercentValue: PercentValue @fieldCategory\n Base64Value: Base64Value @fieldCategory\n JSONValue: JSONValue @fieldCategory\n parentRelationship: RecordRepresentation @fieldCategory\n polymorphicParentRelationship: PolymorphicParentRelationship @fieldCategory\n childRelationship(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy): RecordConnection @fieldCategory\n}\n\ntype IDValue implements FieldValue {\n value: ID\n displayValue: String\n}\n\nenum Unit {\n MI\n KM\n}\n\ninput OrderByGeolocationClause {\n distance: DistanceInput\n order: ResultOrder\n nulls: NullOrder\n}\n\ntype TextAreaAggregate implements FieldValue {\n value: TextArea\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: TextAreaValue\n min: TextAreaValue\n}\n\nenum GroupByType {\n GROUP_BY\n ROLLUP\n CUBE\n}\n\nenum ResultOrder {\n DESC\n ASC\n}\n\ninput RecordOrderBy @generic {\n orderableField: OrderByClause @fieldCategory\n orderableGeolocationField: OrderByGeolocationClause @fieldCategory\n orderableParentRelationship: RecordOrderBy @fieldCategory\n orderablePolymorphicParentRelationship: PolymorphicParentRelationshipRecordOrderBy @fieldCategory\n}\n\ninput PicklistOperators {\n eq: Picklist\n ne: Picklist\n in: [Picklist]\n nin: [Picklist]\n}\n\ninput RecordFilter @generic {\n and: [RecordFilter]\n or: [RecordFilter]\n not: RecordFilter\n parentRelationshipRecordFilter: RecordFilter @fieldCategory\n polymorphicParentRelationshipRecordFilter: PolymorphicParentRelationshipRecordFilter @fieldCategory\n IntegerOperator: IntegerOperators @fieldCategory\n LongOperator: LongOperators @fieldCategory\n StringOperator: StringOperators @fieldCategory\n DoubleOperator: DoubleOperators @fieldCategory\n PercentOperator: PercentOperators @fieldCategory\n LongitudeOperator: LongitudeOperators @fieldCategory\n LatitudeOperator: LatitudeOperators @fieldCategory\n EmailOperator: EmailOperators @fieldCategory\n TextAreaOperator: TextAreaOperators @fieldCategory\n LongTextAreaOperator: LongTextAreaOperators @fieldCategory\n URLOperator: URLOperators @fieldCategory\n PhoneNumberOperator: PhoneNumberOperators @fieldCategory\n BooleanOperator: BooleanOperators @fieldCategory\n IdOperator: IdOperators @fieldCategory\n CurrencyOperator: CurrencyOperators @fieldCategory\n TimeOperator: TimeOperators @fieldCategory\n DateOperator: DateOperators @fieldCategory\n DateTimeOperator: DateTimeOperators @fieldCategory\n PicklistOperator: PicklistOperators @fieldCategory\n MultiPicklistOperator: MultiPicklistOperators @fieldCategory\n GeolocationOperator: GeolocationOperators @fieldCategory\n}\n\ntype TimeValue implements FieldValue {\n value: Time\n displayValue: String\n format: String\n}\n\ninput GeolocationOperators {\n lt: GeolocationInput\n gt: GeolocationInput\n}\n\ntype PicklistAggregate implements FieldValue {\n value: Picklist\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n label: String\n max: PicklistValue\n min: PicklistValue\n}\n\ninput LatitudeOperators {\n eq: Latitude\n ne: Latitude\n lt: Latitude\n gt: Latitude\n lte: Latitude\n gte: Latitude\n in: [Latitude]\n nin: [Latitude]\n}\n\ntype DateTimeValue implements FieldValue {\n value: DateTime\n displayValue: String\n format: String\n}\n\nenum __DirectiveLocation {\n QUERY\n MUTATION\n FIELD\n FRAGMENT_DEFINITION\n FRAGMENT_SPREAD\n INLINE_FRAGMENT\n SCHEMA\n SCALAR\n OBJECT\n FIELD_DEFINITION\n ARGUMENT_DEFINITION\n INTERFACE\n UNION\n ENUM\n ENUM_VALUE\n INPUT_OBJECT\n INPUT_FIELD_DEFINITION\n}\n\ntype IntAggregate implements FieldValue {\n value: Int\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n grouping: IntValue\n max: IntValue\n min: IntValue\n sum: LongValue\n}\n\ntype ListOrder {\n fieldApiName: String!\n sortDirection: ResultOrder\n}\n\ntype RecordAggregate @generic {\n ApiName: String!\n BooleanAggregate: BooleanAggregate @fieldCategory\n CurrencyAggregate: CurrencyAggregate @fieldCategory\n DateAggregate: DateAggregate @fieldCategory\n DoubleAggregate: DoubleAggregate @fieldCategory\n EmailAggregate: EmailAggregate @fieldCategory\n IDAggregate: IDAggregate @fieldCategory\n IntAggregate: IntAggregate @fieldCategory\n LatitudeAggregate: LatitudeAggregate @fieldCategory\n LongitudeAggregate: LongitudeAggregate @fieldCategory\n LongAggregate: LongAggregate @fieldCategory\n PercentAggregate: PercentAggregate @fieldCategory\n PhoneNumberAggregate: PhoneNumberAggregate @fieldCategory\n PicklistAggregate: PicklistAggregate @fieldCategory\n StringAggregate: StringAggregate @fieldCategory\n TextAreaAggregate: TextAreaAggregate @fieldCategory\n TimeAggregate: TimeAggregate @fieldCategory\n UrlAggregate: UrlAggregate @fieldCategory\n}\n\ntype JSONValue implements FieldValue {\n value: JSON\n displayValue: String\n}\n\ntype EmailValue implements FieldValue {\n value: Email\n displayValue: String\n}\n\ntype LongValue implements FieldValue {\n value: Long\n displayValue: String\n format: String\n}\n\ninput DateFunctionInput {\n value: LongOperators\n convertTimezoneValue: LongOperators\n}\n\ntype DependentField {\n controllingField: String!\n dependentFields: [String]!\n}\n\ninput LongTextAreaOperators {\n eq: LongTextArea\n ne: LongTextArea\n like: LongTextArea\n lt: LongTextArea\n gt: LongTextArea\n lte: LongTextArea\n gte: LongTextArea\n in: [LongTextArea]\n nin: [LongTextArea]\n}\n\nenum __TypeKind {\n SCALAR\n OBJECT\n INTERFACE\n UNION\n ENUM\n INPUT_OBJECT\n LIST\n NON_NULL\n}\n\ntype PercentValue implements FieldValue {\n value: Percent\n displayValue: String\n format: String\n}\n\ninput DateTimeOperators {\n eq: DateTimeInput\n ne: DateTimeInput\n lt: DateTimeInput\n gt: DateTimeInput\n lte: DateTimeInput\n gte: DateTimeInput\n in: [DateTimeInput]\n nin: [DateTimeInput]\n DAY_IN_WEEK: DateFunctionInput\n DAY_IN_MONTH: DateFunctionInput\n DAY_IN_YEAR: DateFunctionInput\n WEEK_IN_MONTH: DateFunctionInput\n WEEK_IN_YEAR: DateFunctionInput\n CALENDAR_MONTH: DateFunctionInput\n CALENDAR_QUARTER: DateFunctionInput\n CALENDAR_YEAR: DateFunctionInput\n FISCAL_MONTH: DateFunctionInput\n FISCAL_QUARTER: DateFunctionInput\n FISCAL_YEAR: DateFunctionInput\n DAY_ONLY: DateTimeFunctionInput\n HOUR_IN_DAY: DateFunctionInput\n}\n\ntype BooleanAggregate implements FieldValue {\n value: Boolean\n displayValue: String\n grouping: IntValue\n}\n\ntype RecordQueryAggregate {\n recordQueryAggregate(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy, scope: String, groupBy: RecordGroupBy): RecordAggregateConnection @fieldCategory\n}\n\ntype RecordConnection @generic {\n edges: [RecordEdge]\n pageInfo: PageInfo!\n totalCount: Int!\n}\n\ntype FilteredLookupInfo {\n controllingFields: [String]!\n dependent: Boolean!\n optionalFilter: Boolean!\n}\n\ninput PhoneNumberOperators {\n eq: PhoneNumber\n ne: PhoneNumber\n like: PhoneNumber\n lt: PhoneNumber\n gt: PhoneNumber\n lte: PhoneNumber\n gte: PhoneNumber\n in: [PhoneNumber]\n nin: [PhoneNumber]\n}\n\ntype ObjectInfo {\n ApiName: String!\n childRelationships: [ChildRelationship]!\n createable: Boolean!\n custom: Boolean!\n defaultRecordTypeId: ID\n deletable: Boolean!\n dependentFields: [DependentField]!\n feedEnabled: Boolean!\n fields: [Field]!\n keyPrefix: String\n label: String\n labelPlural: String\n layoutable: Boolean!\n mruEnabled: Boolean!\n nameFields: [String]!\n queryable: Boolean!\n recordTypeInfos: [RecordTypeInfo]!\n searchable: Boolean!\n themeInfo: ThemeInfo\n updateable: Boolean!\n}\n\ninput LongitudeOperators {\n eq: Longitude\n ne: Longitude\n lt: Longitude\n gt: Longitude\n lte: Longitude\n gte: Longitude\n in: [Longitude]\n nin: [Longitude]\n}\n\ntype Field {\n ApiName: String!\n calculated: Boolean!\n compound: Boolean!\n compoundComponentName: String\n compoundFieldName: String\n controllerName: String\n controllingFields: [String]!\n createable: Boolean!\n custom: Boolean!\n dataType: DataType\n extraTypeInfo: FieldExtraTypeInfo\n filterable: Boolean!\n filteredLookupInfo: FilteredLookupInfo\n highScaleNumber: Boolean!\n htmlFormatted: Boolean!\n inlineHelpText: String\n label: String\n nameField: Boolean!\n polymorphicForeignKey: Boolean!\n precision: Int\n reference: Boolean!\n referenceTargetField: String\n referenceToInfos: [ReferenceToInfo]!\n relationshipName: String\n required: Boolean!\n scale: Int\n searchPrefilterable: Boolean\n sortable: Boolean!\n updateable: Boolean!\n}\n\nenum FieldExtraTypeInfo {\n IMAGE_URL\n EXTERNAL_LOOKUP\n INDIRECT_LOOKUP\n PERSONNAME\n SWITCHABLE_PERSONNAME\n PLAINTEXTAREA\n RICHTEXTAREA\n}\n\ntype RateLimit {\n cost: Long\n limit: Long\n remaining: Long\n resetAt: DateTime\n}\n\ninput DateRange {\n last_n_days: Int\n next_n_days: Int\n last_n_weeks: Int\n next_n_weeks: Int\n last_n_months: Int\n next_n_months: Int\n last_n_quarters: Int\n next_n_quarters: Int\n last_n_fiscal_quarters: Int\n next_n_fiscal_quarters: Int\n last_n_years: Int\n next_n_years: Int\n last_n_fiscal_years: Int\n next_n_fiscal_years: Int\n n_days_ago: Int\n n_weeks_ago: Int\n n_months_ago: Int\n n_quarters_ago: Int\n n_years_ago: Int\n n_fiscal_quarters_ago: Int\n n_fiscal_years_ago: Int\n}\n\ninput DateTimeFunctionInput {\n value: DateTimePrimitiveOperators\n convertTimezoneValue: DateTimePrimitiveOperators\n}\n\ntype Base64Value implements FieldValue {\n value: Base64\n displayValue: String\n}\n\ninput IntegerOperators {\n eq: Int\n ne: Int\n lt: Int\n gt: Int\n lte: Int\n gte: Int\n in: [Int]\n nin: [Int]\n}\n\ntype EncryptedStringValue implements FieldValue {\n value: EncryptedString\n displayValue: String\n}\n\ninterface Record {\n Id: ID!\n ApiName: String!\n WeakEtag: Long!\n DisplayValue: String\n LastModifiedById: IDValue\n LastModifiedDate: DateTimeValue\n SystemModstamp: DateTimeValue\n RecordTypeId(fallback: Boolean): IDValue\n}\n\ninput PolymorphicParentRelationshipRecordFilter @generic {\n RecordFilter: RecordFilter @fieldCategory\n}\n\ntype __Schema {\n types: [__Type!]!\n queryType: __Type!\n mutationType: __Type\n directives: [__Directive!]!\n subscriptionType: __Type\n}\n\ninput DateTimeInput {\n value: DateTime\n literal: DateLiteral\n range: DateRange\n}\n\ninput DateTimePrimitiveOperators {\n eq: DateTime\n ne: DateTime\n lt: DateTime\n gt: DateTime\n lte: DateTime\n gte: DateTime\n in: [DateTime]\n nin: [DateTime]\n}\n\ntype ChildRelationship {\n childObjectApiName: String!\n fieldName: String\n junctionIdListNames: [String]!\n junctionReferenceTo: [String]!\n relationshipName: String\n objectInfo: ObjectInfo\n}\n\ntype RecordResult @generic {\n aggregate: RecordAggregate\n}\n\ntype PageInfo {\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n startCursor: String\n endCursor: String\n}\n\ntype CurrencyValue implements FieldValue {\n value: Currency\n displayValue: String\n format: String\n}\n\ninput DateInput {\n value: Date\n literal: DateLiteral\n range: DateRange\n}\n\ninput RecordGroupBy @generic {\n groupableField: GroupByClause @fieldCategory\n groupableDateField: GroupByDateFunction @fieldCategory\n groupableParentRelationship: RecordGroupBy @fieldCategory\n groupablePolymorphicParentRelationship: PolymorphicParentRelationshipGroupBy @fieldCategory\n type: GroupByType = GROUP_BY\n}\n\ntype DateFunctionAggregation {\n value: Long\n format: String\n}\n\ntype RecordQuery {\n # scope should be type RecordScope but it cannot currently be used\n recordQuery(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy, scope: String): RecordConnection @fieldCategory\n}\n\ndirective @generic on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT\ndirective @fieldCategory on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE\ndirective @category on FIELD\n\n\n\n\n\n\n\n";
8689
8898
 
@@ -15767,4 +15976,4 @@ register({
15767
15976
  });
15768
15977
 
15769
15978
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15770
- // version: 1.152.4-cef16daed
15979
+ // version: 1.154.0-3a36aab7e