@salesforce/lds-worker-api 1.229.0-dev1 → 1.229.0-dev10

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.
@@ -25,12 +25,15 @@ const { hasOwnProperty: hasOwnProperty$3 } = Object.prototype;
25
25
  const { isArray: isArray$9 } = Array;
26
26
  const { push: push$5, indexOf, slice: slice$2 } = Array.prototype;
27
27
  const { parse: parse$a, stringify: stringify$a } = JSON;
28
+ const WeakSetCtor = WeakSet;
28
29
 
30
+ const deeplyFrozen = new WeakSetCtor();
29
31
  function deepFreeze(value) {
30
- // No need to freeze primitives
31
- if (typeof value !== 'object' || value === null) {
32
+ // No need to freeze primitives or already frozen stuff
33
+ if (typeof value !== 'object' || value === null || deeplyFrozen.has(value)) {
32
34
  return;
33
35
  }
36
+ deeplyFrozen.add(value);
34
37
  if (isArray$9(value)) {
35
38
  for (let i = 0, len = value.length; i < len; i += 1) {
36
39
  deepFreeze(value[i]);
@@ -1727,6 +1730,10 @@ class InMemoryStore {
1727
1730
  }
1728
1731
  }
1729
1732
  markVisited(canonicalKey) {
1733
+ if (typeof canonicalKey === 'string') {
1734
+ this.fallbackStringKeyInMemoryStore.markVisited(canonicalKey);
1735
+ return;
1736
+ }
1730
1737
  const { visitedIdsSet, reverseRedirectKeysMap } = this;
1731
1738
  let redirectKey = canonicalKey;
1732
1739
  // mark all redirects leading up to the canonical key as visited so
@@ -2038,7 +2045,7 @@ class GraphLink {
2038
2045
  if (isStoreRecordError$1(linked)) {
2039
2046
  return new GraphNodeError(this.store, linked);
2040
2047
  }
2041
- return new GraphNode(this.store, linked);
2048
+ return new GraphNode(this.store, linked, __ref);
2042
2049
  }
2043
2050
  linkData() {
2044
2051
  return this.data.data;
@@ -2048,10 +2055,11 @@ class GraphLink {
2048
2055
  }
2049
2056
  }
2050
2057
  class GraphNode {
2051
- constructor(store, data) {
2058
+ constructor(store, data, storeKey) {
2052
2059
  this.type = GraphNodeType$1.Node;
2053
2060
  this.store = store;
2054
2061
  this.data = data;
2062
+ this.storeKey = storeKey;
2055
2063
  }
2056
2064
  object(propertyName) {
2057
2065
  const value = this.data[propertyName];
@@ -2061,7 +2069,8 @@ class GraphNode {
2061
2069
  if (typeof value !== 'object' || value === null) {
2062
2070
  throw new Error(`Cannot walk to path ${String(propertyName)}. "${String(propertyName)}" is a scalar: "${value}"`);
2063
2071
  }
2064
- return new GraphNode(this.store, value);
2072
+ // We're walking to an object property on the current store record, pass the storeKey down.
2073
+ return new GraphNode(this.store, value, this.storeKey);
2065
2074
  }
2066
2075
  link(propertyName) {
2067
2076
  const value = this.data[propertyName];
@@ -2091,6 +2100,8 @@ class GraphNode {
2091
2100
  }
2092
2101
  write(propertyName, value) {
2093
2102
  this.data[propertyName] = value;
2103
+ const canonicalKey = this.store.getCanonicalRecordId(this.storeKey);
2104
+ this.store.markVisited(canonicalKey);
2094
2105
  }
2095
2106
  isUndefined(propertyName) {
2096
2107
  return this.data[propertyName] === undefined;
@@ -2275,6 +2286,34 @@ var FragmentReadResultState$1;
2275
2286
  const FRAGMENT_READ_RESULT_MISSING = {
2276
2287
  state: FragmentReadResultState$1.Missing,
2277
2288
  };
2289
+ function resolveLink$1(reader, storeLink, version) {
2290
+ const { StoreLinkStateValues } = reader;
2291
+ const linkState = reader.getLinkState(storeLink);
2292
+ switch (linkState.state) {
2293
+ case StoreLinkStateValues.RefNotPresent:
2294
+ case StoreLinkStateValues.NotPresent:
2295
+ case StoreLinkStateValues.Missing:
2296
+ reader.markMissingLink(storeLink.__ref);
2297
+ reader.markMissing();
2298
+ return;
2299
+ case StoreLinkStateValues.Pending:
2300
+ reader.markPending();
2301
+ return;
2302
+ case StoreLinkStateValues.Null:
2303
+ return;
2304
+ }
2305
+ const { key: __ref } = linkState;
2306
+ return reader.read({
2307
+ recordId: __ref,
2308
+ node: {
2309
+ kind: 'Fragment',
2310
+ private: [],
2311
+ opaque: true,
2312
+ version,
2313
+ },
2314
+ variables: {},
2315
+ });
2316
+ }
2278
2317
  class Reader {
2279
2318
  constructor(store, variables, refresh, baseSnapshot, ttlStrategy) {
2280
2319
  this.store = store;
@@ -3225,9 +3264,9 @@ class Environment {
3225
3264
  if (value === undefined) {
3226
3265
  return null;
3227
3266
  }
3228
- return this.wrapNormalizedGraphNode(value, store);
3267
+ return this.wrapNormalizedGraphNode(value, key, store);
3229
3268
  }
3230
- wrapNormalizedGraphNode(normalized, storeOverride) {
3269
+ wrapNormalizedGraphNode(normalized, key, storeOverride) {
3231
3270
  if (normalized === null) {
3232
3271
  return null;
3233
3272
  }
@@ -3235,7 +3274,7 @@ class Environment {
3235
3274
  if (isStoreRecordError$1(normalized)) {
3236
3275
  return new GraphNodeError(store, normalized);
3237
3276
  }
3238
- return new GraphNode(store, normalized);
3277
+ return new GraphNode(store, normalized, key);
3239
3278
  }
3240
3279
  withContext(adapter, options) {
3241
3280
  const { contextId, onContextLoaded } = options;
@@ -3530,8 +3569,8 @@ class Luvio {
3530
3569
  getNode(key) {
3531
3570
  return this.environment.getNode(key);
3532
3571
  }
3533
- wrapNormalizedGraphNode(normalized) {
3534
- return this.environment.wrapNormalizedGraphNode(normalized);
3572
+ wrapNormalizedGraphNode(normalized, key) {
3573
+ return this.environment.wrapNormalizedGraphNode(normalized, key);
3535
3574
  }
3536
3575
  instrument(paramsBuilder) {
3537
3576
  const { instrument } = this.options;
@@ -3841,7 +3880,7 @@ function createResourceParamsImpl(config, configMetadata) {
3841
3880
  }
3842
3881
  return resourceParams;
3843
3882
  }
3844
- // engine version: 0.145.2-6a13677c
3883
+ // engine version: 0.146.0-dev5-a2ec6e3f
3845
3884
 
3846
3885
  /**
3847
3886
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -3968,7 +4007,7 @@ function withDefaultLuvio(callback) {
3968
4007
  }
3969
4008
  callbacks.push(callback);
3970
4009
  }
3971
- // version: 1.229.0-dev1-f69d054a9
4010
+ // version: 1.229.0-dev10-bc9ef2513
3972
4011
 
3973
4012
  // TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
3974
4013
  function instrumentAdapter$1(createFunction, _metadata) {
@@ -15429,7 +15468,7 @@ function gql(literals, ...subs) {
15429
15468
  }
15430
15469
  return superResult;
15431
15470
  }
15432
- // version: 1.229.0-dev1-f69d054a9
15471
+ // version: 1.229.0-dev10-bc9ef2513
15433
15472
 
15434
15473
  function unwrap(data) {
15435
15474
  // The lwc-luvio bindings import a function from lwc called "unwrap".
@@ -15530,14 +15569,15 @@ function refreshData(data, dataToTuple, luvio) {
15530
15569
  return undefined;
15531
15570
  });
15532
15571
  }
15533
- const { isArray: isArray$8 } = Array;
15534
- const { stringify: stringify$9 } = JSON;
15535
15572
 
15536
15573
  function isPromise$1(value) {
15537
15574
  // check for Thenable due to test frameworks using custom Promise impls
15538
15575
  return value.then !== undefined;
15539
15576
  }
15540
15577
 
15578
+ const { isArray: isArray$8 } = Array;
15579
+ const { stringify: stringify$9 } = JSON;
15580
+
15541
15581
  /**
15542
15582
  * (Re)throws an error after adding a prefix to the message.
15543
15583
  *
@@ -16352,7 +16392,7 @@ function createGraphQLWireAdapterConstructor(luvio, adapter, metadata, astResolv
16352
16392
  const { apiFamily, name } = metadata;
16353
16393
  return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
16354
16394
  }
16355
- // version: 1.229.0-dev1-f69d054a9
16395
+ // version: 1.229.0-dev10-bc9ef2513
16356
16396
 
16357
16397
  /**
16358
16398
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -16451,11 +16491,11 @@ var TypeCheckShapes;
16451
16491
  TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
16452
16492
  TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
16453
16493
  })(TypeCheckShapes || (TypeCheckShapes = {}));
16454
- // engine version: 0.145.2-6a13677c
16494
+ // engine version: 0.146.0-dev5-a2ec6e3f
16455
16495
 
16456
16496
  const { keys: ObjectKeys$3, create: ObjectCreate$3 } = Object;
16457
16497
 
16458
- const { assign: assign$9, create: create$9, freeze: freeze$4, keys: keys$b } = Object;
16498
+ const { assign: assign$9, create: create$9, freeze: freeze$4, isFrozen: isFrozen$2, keys: keys$b } = Object;
16459
16499
 
16460
16500
  ObjectCreate$3(null);
16461
16501
 
@@ -16825,7 +16865,7 @@ function buildAdapterValidationConfig$1(displayName, paramsMeta) {
16825
16865
  }
16826
16866
  const keyPrefix$1 = 'UiApi';
16827
16867
 
16828
- const { assign: assign$8, create: create$8, freeze: freeze$3, keys: keys$a } = Object;
16868
+ const { assign: assign$8, create: create$8, freeze: freeze$3, isFrozen: isFrozen$1, keys: keys$a } = Object;
16829
16869
  const { hasOwnProperty: hasOwnProperty$1 } = Object.prototype;
16830
16870
  const { split, endsWith } = String.prototype;
16831
16871
  const { isArray: isArray$7 } = Array;
@@ -19794,7 +19834,7 @@ function extractTrackedFieldsToTrie(recordId, node, root, config, visitedRecordI
19794
19834
  extractTrackedFieldsToTrie(spanningLink.data.__ref, spanning, next, config, spanningVisitedRecordIds, depth + 1);
19795
19835
  // For a spanning record that is detected to be a circular reference, we add the field along with Id and Name.
19796
19836
  // It's possible for spanning record lookup fields to sometimes be circular, and sometimes not - depending on the value of the lookup field.
19797
- // For more information on scenarios that caused this fix: https://salesforce.quip.com/OvzNAh3eNIWY
19837
+ // For more information on scenarios that caused this fix: search "LDS Recursive Spanning Fields Problem" in Quip
19798
19838
  if (keys$a(next.children).length === 0) {
19799
19839
  addScalarFieldId(next);
19800
19840
  addScalarFieldName(next);
@@ -19947,7 +19987,11 @@ function markNulledOutPath(record, path) {
19947
19987
  }
19948
19988
  const link = fieldValueRepresentation.link(fieldName);
19949
19989
  const resolved = link.follow();
19950
- if (isGraphNode(resolved) && resolved.isScalar('value') && path.length > 0) {
19990
+ if (isGraphNode(resolved) &&
19991
+ resolved.isScalar('value') &&
19992
+ path.length > 0 &&
19993
+ // TODO [W-14082782]: temporary fix
19994
+ !isFrozen$1(link.data)) {
19951
19995
  const linkState = link.linkData();
19952
19996
  const fields = linkState === undefined ? [] : linkState.fields;
19953
19997
  link.writeLinkData({
@@ -19975,22 +20019,12 @@ function _markMissingPath(record, path) {
19975
20019
  const fieldValueRepresentation = record.object('fields');
19976
20020
  const fieldName = path.shift();
19977
20021
  if (fieldValueRepresentation.isUndefined(fieldName) === true) {
19978
- // TODO [W-6900046]: remove cast, make RecordRepresentationNormalized['fields'] accept
19979
- // an undefined/non-present __ref if isMissing is present
19980
- fieldValueRepresentation.write(fieldName, {
19981
- __ref: undefined,
19982
- isMissing: true,
19983
- });
20022
+ writeMissingFieldToStore(fieldValueRepresentation, fieldName);
19984
20023
  return;
19985
20024
  }
19986
20025
  const link = fieldValueRepresentation.link(fieldName);
19987
20026
  if (link.isPending()) {
19988
- // TODO [W-6900046]: remove cast, make RecordRepresentationNormalized['fields'] accept
19989
- // an undefined/non-present __ref if isMissing is present
19990
- fieldValueRepresentation.write(fieldName, {
19991
- __ref: undefined,
19992
- isMissing: true,
19993
- });
20027
+ writeMissingFieldToStore(fieldValueRepresentation, fieldName);
19994
20028
  }
19995
20029
  else if (path.length > 0 && link.isMissing() === false) {
19996
20030
  const fieldValue = link.follow();
@@ -20006,6 +20040,19 @@ function _markMissingPath(record, path) {
20006
20040
  }
20007
20041
  }
20008
20042
  }
20043
+ /**
20044
+ * Graph Node Directly modifies store entries, which is generally a non-starter.
20045
+ * Until we can refactor this mess, you need to use this function to safely mark the RecordRepresentation
20046
+ * as a seenId in the store when you perform this mutation.
20047
+ */
20048
+ function writeMissingFieldToStore(field, fieldName) {
20049
+ // TODO [W-6900046]: remove cast, make RecordRepresentationNormalized['fields'] accept
20050
+ // an undefined/non-present __ref if isMissing is present
20051
+ field.write(fieldName, {
20052
+ __ref: undefined,
20053
+ isMissing: true,
20054
+ });
20055
+ }
20009
20056
  /**
20010
20057
  * Tells you if an objectApiName is supported by UI API or not.
20011
20058
  * Note: Luvio does not currently support all the entities, the list is limited to UI API supported entities
@@ -20137,8 +20184,11 @@ function mergeAndRefreshLowerVersionRecord(luvio, incoming, existing, incomingTr
20137
20184
  return existing;
20138
20185
  }
20139
20186
  function mergeRecordConflict(luvio, incoming, existing, recordConflictMap) {
20140
- const incomingNode = luvio.wrapNormalizedGraphNode(incoming);
20141
- const existingNode = luvio.wrapNormalizedGraphNode(existing);
20187
+ const recordKey = keyBuilder$1U(luvio, {
20188
+ recordId: incoming.id,
20189
+ });
20190
+ const incomingNode = luvio.wrapNormalizedGraphNode(incoming, recordKey);
20191
+ const existingNode = luvio.wrapNormalizedGraphNode(existing, recordKey);
20142
20192
  const incomingTrackedFieldsTrieRoot = {
20143
20193
  name: incoming.apiName,
20144
20194
  children: {},
@@ -20147,9 +20197,6 @@ function mergeRecordConflict(luvio, incoming, existing, recordConflictMap) {
20147
20197
  name: existing.apiName,
20148
20198
  children: {},
20149
20199
  };
20150
- const recordKey = keyBuilder$1U(luvio, {
20151
- recordId: incoming.id,
20152
- });
20153
20200
  const trackedFieldsConfig = {
20154
20201
  maxDepth: configurationForRestAdapters$1.getTrackedFieldDepthOnCacheMergeConflict(),
20155
20202
  onlyFetchLeafNodeIdAndName: configurationForRestAdapters$1.getTrackedFieldLeafNodeIdAndNameOnly(),
@@ -24860,7 +24907,7 @@ function getLayoutMapAndObjectInfo(recordId, data) {
24860
24907
  // Temp fix until we can mimic the server behavior for non-layoutable entities.
24861
24908
  let layoutMap = {};
24862
24909
  if (hasOwnProperty$1.call(layouts, apiName)) {
24863
- layoutMap = layouts[apiName][recordTypeId];
24910
+ layoutMap = layouts[apiName][recordTypeId] || {};
24864
24911
  }
24865
24912
  return {
24866
24913
  layoutMap,
@@ -25038,18 +25085,28 @@ const buildLayoutModeCacheSnapshot = (apiName, recordTypeId, layoutType, mode) =
25038
25085
  * These are intermediate lookups to check if the record is in the L2 cache
25039
25086
  * @param {Luvio} luvio
25040
25087
  * @param {GetRecordLayoutTypeConfig} config
25041
- * @param {BuildCachedSnapshot<BuildSnapshotContext} cachedSnapshot
25088
+ * @param {BuildCachedSnapshot<BuildSnapshotContext>} cachedSnapshot
25042
25089
  */
25043
25090
  function makeCacheOnlySnapshot(luvio, config, adapterContext, cachedSnapshot) {
25044
- return luvio.applyCachePolicy({
25045
- cachePolicy: {
25046
- // only looking in the cache so we can check for L2 data offline
25047
- type: 'only-if-cached',
25048
- },
25049
- }, { config, luvio, adapterContext }, cachedSnapshot,
25050
- // this won't be invoked since we're requesting only-if-cached
25091
+ return luvio.applyCachePolicy(
25092
+ // Pass empty context so environment will use its default cache-policy
25093
+ {}, { config, luvio, adapterContext }, cachedSnapshot,
25094
+ // disallow hitting the network by returning a gateway timeout
25051
25095
  () => {
25052
- throw Error('buildNetworkSnapshot should not be called for only-if-cached policy');
25096
+ return new Promise((resolve) => {
25097
+ resolve({
25098
+ state: 'Error',
25099
+ data: undefined,
25100
+ error: {
25101
+ body: undefined,
25102
+ headers: {},
25103
+ ok: false,
25104
+ status: 504,
25105
+ statusText: 'Gateway Timeout',
25106
+ errorType: 'fetchResponse',
25107
+ },
25108
+ });
25109
+ });
25053
25110
  });
25054
25111
  }
25055
25112
  /**
@@ -25260,7 +25317,7 @@ const notifyChangeFactory = (luvio) => {
25260
25317
  const responsePromises = [];
25261
25318
  for (let i = 0, len = entries.length; i < len; i++) {
25262
25319
  const { key, record } = entries[i];
25263
- const node = luvio.wrapNormalizedGraphNode(record);
25320
+ const node = luvio.wrapNormalizedGraphNode(record, key);
25264
25321
  const optionalFields = getTrackedFields(key, node, {
25265
25322
  maxDepth: configurationForRestAdapters$1.getTrackedFieldDepthOnNotifyChange(),
25266
25323
  onlyFetchLeafNodeIdAndName: configurationForRestAdapters$1.getTrackedFieldLeafNodeIdAndNameOnly(),
@@ -41158,7 +41215,16 @@ withDefaultLuvio((luvio) => {
41158
41215
  throttle(60, 60000, createLDSAdapter(luvio, 'notifyListInfoUpdateAvailable', notifyUpdateAvailableFactory$1));
41159
41216
  throttle(60, 60000, createLDSAdapter(luvio, 'notifyQuickActionDefaultsUpdateAvailable', notifyUpdateAvailableFactory));
41160
41217
  });
41161
- // version: 1.229.0-dev1-5b6d3db67
41218
+ // version: 1.229.0-dev10-abb060196
41219
+
41220
+ var ldsIdempotencyWriteDisabled = {
41221
+ isOpen: function (e) {
41222
+ return e.fallback;
41223
+ },
41224
+ hasError: function () {
41225
+ return !0;
41226
+ },
41227
+ };
41162
41228
 
41163
41229
  var caseSensitiveUserId = '005B0000000GR4OIAW';
41164
41230
 
@@ -41789,6 +41855,9 @@ function handleDurableStoreRejection(instrument) {
41789
41855
  }
41790
41856
 
41791
41857
  function isStoreEntryError(storeRecord) {
41858
+ if (!storeRecord || typeof storeRecord !== 'object') {
41859
+ return false;
41860
+ }
41792
41861
  return storeRecord.__type === 'error';
41793
41862
  }
41794
41863
 
@@ -42373,12 +42442,12 @@ function makeDurable(environment, { durableStore, instrumentation }) {
42373
42442
  }
42374
42443
  return environment.getNode(key, ingestStagingStore);
42375
42444
  };
42376
- const wrapNormalizedGraphNode = function (normalized) {
42445
+ const wrapNormalizedGraphNode = function (normalized, key) {
42377
42446
  validateNotDisposed();
42378
42447
  if (ingestStagingStore === null) {
42379
42448
  ingestStagingStore = buildIngestStagingStore(environment);
42380
42449
  }
42381
- return environment.wrapNormalizedGraphNode(normalized, ingestStagingStore);
42450
+ return environment.wrapNormalizedGraphNode(normalized, key, ingestStagingStore);
42382
42451
  };
42383
42452
  const rebuildSnapshot = function (snapshot, onRebuild) {
42384
42453
  validateNotDisposed();
@@ -44017,6 +44086,7 @@ function isScalarDataType(type) {
44017
44086
  'Email',
44018
44087
  'TextArea',
44019
44088
  'Percent',
44089
+ 'EncryptedString',
44020
44090
  ].includes(type);
44021
44091
  }
44022
44092
 
@@ -45497,8 +45567,12 @@ function rootRecordQuery(selection, input) {
45497
45567
  // If there is no metadata for this query or it somehow lacks a timestamp
45498
45568
  // skip setting the root timestamp
45499
45569
  if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
45500
- // subtract 10ms from timestamp to account for ingestion processing time
45501
- input.rootTimestamp = queryMetadata.ingestionTimestamp - 10;
45570
+ const timestamp = Number(queryMetadata.ingestionTimestamp);
45571
+ if (!isNaN(timestamp)) {
45572
+ // adjust the timestamp to account for ingestion processing time
45573
+ // 30s is used because this is the default record TTL
45574
+ input.rootTimestamp = timestamp - 30000;
45575
+ }
45502
45576
  }
45503
45577
  }
45504
45578
  return recordQuery(selection, alias, apiName, [], input);
@@ -46311,8 +46385,12 @@ function uuidv4() {
46311
46385
 
46312
46386
  const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
46313
46387
  const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
46388
+ const ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED = 'IDEMPOTENCY_FEATURE_NOT_ENABLED';
46389
+ const ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED = 'IDEMPOTENCY_NOT_SUPPORTED';
46314
46390
  const ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER = 'IDEMPOTENCY_KEY_USED_DIFFERENT_USER';
46315
46391
  const ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST = 'IDEMPOTENCY_CONCURRENT_REQUEST';
46392
+ const ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED = 'IDEMPOTENCY_KEY_ALREADY_USED';
46393
+ const ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR = 'IDEMPOTENCY_BACKEND_OPERATION_ERROR';
46316
46394
  /**
46317
46395
  * Get the retry after in milliseconds from the response headers, undefined if not specified.
46318
46396
  * The header could have two different format.
@@ -46342,7 +46420,9 @@ function buildLuvioOverrideForDraftAdapters(luvio, handler, extractTargetIdFromC
46342
46420
  const dispatchResourceRequest = async function (resourceRequest, _context) {
46343
46421
  const resourceRequestCopy = clone$1(resourceRequest);
46344
46422
  resourceRequestCopy.headers = resourceRequestCopy.headers || {};
46345
- resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
46423
+ if (handler.hasIdempotencySupport()) {
46424
+ resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
46425
+ }
46346
46426
  // enable return extra fields for record creation and record update http call
46347
46427
  if (resourceRequest.basePath === '/ui-api/records' &&
46348
46428
  (resourceRequest.method === 'post' || resourceRequest.method === 'patch')) {
@@ -47164,6 +47244,12 @@ class AbstractResourceRequestActionHandler {
47164
47244
  // the luvio store redirect table, during which a new draft might be enqueued
47165
47245
  // which would not see a necessary mapping.
47166
47246
  this.ephemeralRedirects = {};
47247
+ // determined by Server setup.
47248
+ this.isIdempotencySupported = true;
47249
+ // idempotency write flag set by lds
47250
+ this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
47251
+ fallback: false,
47252
+ });
47167
47253
  }
47168
47254
  enqueue(data) {
47169
47255
  return this.draftQueue.enqueue(this.handlerId, data);
@@ -47193,21 +47279,43 @@ class AbstractResourceRequestActionHandler {
47193
47279
  retryDelayInMs = getRetryAfterInMs(response.headers);
47194
47280
  shouldRetry = true;
47195
47281
  break;
47196
- case HttpStatusCode$1.ServerError:
47282
+ case HttpStatusCode$1.ServerError: {
47197
47283
  shouldRetry = true;
47284
+ if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR)) {
47285
+ this.isIdempotencySupported = false;
47286
+ retryDelayInMs = 0;
47287
+ actionDataChanged = true;
47288
+ }
47198
47289
  break;
47290
+ }
47199
47291
  case 409 /* IdempotentWriteSpecificHttpStatusCode.Conflict */: {
47200
- const errorCode = response.body[0].errorCode;
47201
- if (errorCode === ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER) {
47202
- updatedAction.data.headers = updatedAction.data.headers || {};
47203
- updatedAction.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
47292
+ if (this.isUiApiErrors(response.body)) {
47293
+ const errorCode = response.body[0].errorCode;
47294
+ if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER)) {
47295
+ retryDelayInMs = 0;
47296
+ actionDataChanged = true;
47297
+ }
47298
+ else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
47299
+ retryDelayInMs = getRetryAfterInMs(response.headers);
47300
+ }
47301
+ shouldRetry = true;
47302
+ }
47303
+ break;
47304
+ }
47305
+ case HttpStatusCode$1.BadRequest: {
47306
+ if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED, ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED)) {
47204
47307
  retryDelayInMs = 0;
47205
47308
  actionDataChanged = true;
47309
+ shouldRetry = true;
47206
47310
  }
47207
- else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
47208
- retryDelayInMs = getRetryAfterInMs(response.headers);
47311
+ break;
47312
+ }
47313
+ case 422 /* IdempotentWriteSpecificHttpStatusCode.UnProcessableEntity */: {
47314
+ if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED)) {
47315
+ retryDelayInMs = 0;
47316
+ actionDataChanged = true;
47317
+ shouldRetry = true;
47209
47318
  }
47210
- shouldRetry = true;
47211
47319
  break;
47212
47320
  }
47213
47321
  }
@@ -47226,6 +47334,27 @@ class AbstractResourceRequestActionHandler {
47226
47334
  return ProcessActionResult.NETWORK_ERROR;
47227
47335
  }
47228
47336
  }
47337
+ // true if response is an idempotency server error. updates or deletes idempotency key if the reponse is idempotency related error. Idempotency related error is in format of UiApiError array.
47338
+ handleIdempotencyServerError(responseBody, action, updateIdempotencyKey, ...targetErrorCodes) {
47339
+ if (this.isUiApiErrors(responseBody)) {
47340
+ const errorCode = responseBody[0].errorCode;
47341
+ if (targetErrorCodes.includes(errorCode)) {
47342
+ action.data.headers = action.data.headers || {};
47343
+ if (updateIdempotencyKey) {
47344
+ action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
47345
+ }
47346
+ else {
47347
+ delete action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY];
47348
+ }
47349
+ return true;
47350
+ }
47351
+ }
47352
+ return false;
47353
+ }
47354
+ // checks if the body is an array of UiApiError. Sometimes the body has `enhancedErrorType` field as an error indicator(one example is the field validation failure). In such case Action being processed updates to an Error Action.
47355
+ isUiApiErrors(body) {
47356
+ return body !== undefined && Array.isArray(body) && body.length > 0 && body[0].errorCode;
47357
+ }
47229
47358
  async buildPendingAction(request, queue) {
47230
47359
  const targetId = await this.getIdFromRequest(request);
47231
47360
  if (targetId === undefined) {
@@ -47439,6 +47568,10 @@ class AbstractResourceRequestActionHandler {
47439
47568
  ...targetData,
47440
47569
  body: this.mergeRequestBody(targetBody, sourceBody),
47441
47570
  };
47571
+ // Updates Idempotency key if target has one
47572
+ if (targetData.headers && targetData.headers[HTTP_HEADER_IDEMPOTENCY_KEY]) {
47573
+ merged.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
47574
+ }
47442
47575
  // overlay metadata
47443
47576
  merged.metadata = { ...targetMetadata, ...sourceMetadata };
47444
47577
  // put status back to pending to auto upload if queue is active and targed is at the head.
@@ -47475,6 +47608,9 @@ class AbstractResourceRequestActionHandler {
47475
47608
  getDraftIdsFromAction(action) {
47476
47609
  return [action.targetId];
47477
47610
  }
47611
+ hasIdempotencySupport() {
47612
+ return this.isIdempotencySupported && !this.isLdsIdempotencyWriteDisabled;
47613
+ }
47478
47614
  async ingestResponses(responses, action) {
47479
47615
  const luvio = this.getLuvio();
47480
47616
  await luvio.handleSuccessResponse(() => {
@@ -49280,6 +49416,10 @@ function isOperationDefinitionNode(node) {
49280
49416
  return node.kind === 'OperationDefinition';
49281
49417
  }
49282
49418
 
49419
+ const POLYMORPHIC_PARENT_RELATIONSHIP = 'polymorphicParentRelationship';
49420
+ const PARENT_RELATIONSHIP = 'parentRelationship';
49421
+ const CHILD_RELATIONSHIP = 'childRelationship';
49422
+ const RECORD_QUERY = 'recordQuery';
49283
49423
  function requestsDraftsField(recordFieldNode) {
49284
49424
  if (!recordFieldNode.selectionSet)
49285
49425
  return false;
@@ -49295,18 +49435,41 @@ function isRecordQuery(recordQueryField) {
49295
49435
  directive.arguments
49296
49436
  .map((argument) => argument.value)
49297
49437
  .filter(isStringValueNode)
49298
- .some((categoryName) => categoryName.value === 'recordQuery'));
49438
+ .some((categoryName) => categoryName.value === RECORD_QUERY));
49299
49439
  });
49300
49440
  }
49301
49441
  return false;
49302
49442
  }
49303
- // finds field with 'recordQuery' and 'childRelationship' directive
49304
- function findNearestRecordQuery(ancestors) {
49305
- const recordQueryAncester = findNearestAncesterPath(ancestors, true).node;
49306
- return recordQueryAncester === undefined ? undefined : recordQueryAncester;
49443
+ // finds connection field with 'recordQuery' and 'childRelationship' directive.
49444
+ function findNearestConnection(ancestors) {
49445
+ const connectionAncestor = findNearestAncesterPath(ancestors, true).node;
49446
+ return connectionAncestor === undefined ? undefined : connectionAncestor;
49447
+ }
49448
+ // convinient method to find nearest connection with its path
49449
+ function findNearestConnectionWithPath(ancestors) {
49450
+ const closestAncestorPath = findNearestAncesterPath(ancestors, true);
49451
+ let connection = undefined;
49452
+ let connectionPath = undefined;
49453
+ if (closestAncestorPath.parentIndex > 0) {
49454
+ const connectionAncestor = closestAncestorPath.node;
49455
+ const connectionAncestors = ancestors.slice(0, closestAncestorPath.parentIndex);
49456
+ connection =
49457
+ connectionAncestor === undefined ? undefined : connectionAncestor;
49458
+ if (connection !== undefined) {
49459
+ const ancesterPath = findAncesterPath(connectionAncestors);
49460
+ connectionPath =
49461
+ ancesterPath === ''
49462
+ ? connection.name.value
49463
+ : `${ancesterPath}#${connection.name.value}`;
49464
+ }
49465
+ }
49466
+ return {
49467
+ connection,
49468
+ path: connectionPath,
49469
+ };
49307
49470
  }
49308
- // finds cloeset ancester. If 'parentRelationship' is allowed, it could be 'InlineFragmentNode' since it inherits the 'parent' relationship. 'InlineFragmentNode' makes sure that only one 'apiName' returns when tree is traversed.
49309
- function findNearestAncesterPath(ancestors, recordQueryOnly) {
49471
+ // finds closest ancestor. If node with 'parentRelationship' is the ancester, the end result could be 'InlineFragmentNode' since it inherits the 'parent' relationship. 'InlineFragmentNode' makes sure that only one 'apiName' returns when tree is traversed.
49472
+ function findNearestAncesterPath(ancestors, connectionOnly) {
49310
49473
  let recordQueryPath = { node: undefined, parentIndex: -1 };
49311
49474
  let relationship = '';
49312
49475
  for (let i = ancestors.length - 1; i >= 0; i--) {
@@ -49320,9 +49483,11 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
49320
49483
  continue;
49321
49484
  for (let arg of directive.arguments) {
49322
49485
  if (arg.value &&
49323
- (arg.value.value === 'recordQuery' ||
49324
- arg.value.value === 'childRelationship' ||
49325
- (!recordQueryOnly && arg.value.value === 'parentRelationship'))) {
49486
+ (arg.value.value === RECORD_QUERY ||
49487
+ arg.value.value === CHILD_RELATIONSHIP ||
49488
+ (!connectionOnly &&
49489
+ (arg.value.value === PARENT_RELATIONSHIP ||
49490
+ arg.value.value === POLYMORPHIC_PARENT_RELATIONSHIP)))) {
49326
49491
  recordQueryPath = { node: node, parentIndex: i };
49327
49492
  relationship = arg.value.value;
49328
49493
  break;
@@ -49337,17 +49502,19 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
49337
49502
  //checks if nearest ancester could be an inline fragment
49338
49503
  if (recordQueryPath.node !== undefined &&
49339
49504
  recordQueryPath.node.selectionSet &&
49340
- relationship === 'parentRelationship') {
49341
- //
49342
- if (recordQueryPath.node.selectionSet.selections.every(isInlineFragmentNode)) {
49343
- //
49344
- const inlineFragmentLoc = recordQueryPath.parentIndex + 2;
49345
- if (inlineFragmentLoc < ancestors.length && ancestors[inlineFragmentLoc]) {
49505
+ (relationship === PARENT_RELATIONSHIP || relationship === POLYMORPHIC_PARENT_RELATIONSHIP)) {
49506
+ // InlineFragment is usually 3 steps aways from its FieldNode parent within ancester hierarchy if it exists. The below search
49507
+ // is applied to adapt to future AST structure change
49508
+ let parentIndex = recordQueryPath.parentIndex + 1;
49509
+ while (parentIndex < ancestors.length) {
49510
+ if (isInlineFragmentNode(ancestors[parentIndex])) {
49346
49511
  recordQueryPath = {
49347
- node: ancestors[inlineFragmentLoc],
49348
- parentIndex: inlineFragmentLoc,
49512
+ node: ancestors[parentIndex],
49513
+ parentIndex,
49349
49514
  };
49515
+ break;
49350
49516
  }
49517
+ parentIndex++;
49351
49518
  }
49352
49519
  }
49353
49520
  return recordQueryPath;
@@ -49371,7 +49538,7 @@ function findAncesterPath(ancesters) {
49371
49538
  ? sectionPath
49372
49539
  : sectionPath === ''
49373
49540
  ? path
49374
- : `${sectionPath}_${path}`;
49541
+ : `${sectionPath}#${path}`;
49375
49542
  }
49376
49543
  }
49377
49544
  boundaryIndex = parentIndex;
@@ -49429,9 +49596,9 @@ function getRelation(node) {
49429
49596
  const relationships = args
49430
49597
  .map((arg) => arg.value)
49431
49598
  .filter(isStringValueNode)
49432
- .filter((valueNode) => valueNode.value === 'childRelationship' ||
49433
- valueNode.value === 'parentRelationship' ||
49434
- valueNode.value === 'polymorphicParentRelationship')
49599
+ .filter((valueNode) => valueNode.value === CHILD_RELATIONSHIP ||
49600
+ valueNode.value === PARENT_RELATIONSHIP ||
49601
+ valueNode.value === POLYMORPHIC_PARENT_RELATIONSHIP)
49435
49602
  .map((relationshipNode) => relationshipNode.value);
49436
49603
  if (relationships.length > 0) {
49437
49604
  return relationships[0];
@@ -49488,8 +49655,8 @@ function isFieldSpanning(node, parentNode) {
49488
49655
  */
49489
49656
  function isParentRelationship(node) {
49490
49657
  return (node &&
49491
- (isRelationship(node, 'parentRelationship') ||
49492
- isRelationship(node, 'polymorphicParentRelationship')));
49658
+ (isRelationship(node, PARENT_RELATIONSHIP) ||
49659
+ isRelationship(node, POLYMORPHIC_PARENT_RELATIONSHIP)));
49493
49660
  }
49494
49661
  /*
49495
49662
  checks if the InlineFragment spans
@@ -49795,6 +49962,26 @@ function findFieldInfo(objectInfo, fieldName) {
49795
49962
  return values$1(objectInfo.fields).find((field) => field.apiName === fieldName ||
49796
49963
  (field.dataType === 'Reference' && field.relationshipName === fieldName));
49797
49964
  }
49965
+ async function readIngestionTimestampForKey(key, query) {
49966
+ let ingestionTimestamp = 0;
49967
+ const sql = `
49968
+ SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
49969
+ FROM lds_data
49970
+ WHERE key IS ?
49971
+ `;
49972
+ const results = await query(sql, [key]);
49973
+ const [timestamp] = results.rows.map((row) => row[0]);
49974
+ if (timestamp !== null) {
49975
+ const numericalTimestamp = Number(timestamp);
49976
+ if (isNaN(numericalTimestamp)) {
49977
+ return ingestionTimestamp;
49978
+ }
49979
+ // adjust the timestamp to account for ingestion processing time
49980
+ // 30s is used because this is the default record TTL
49981
+ ingestionTimestamp = numericalTimestamp - 30000;
49982
+ }
49983
+ return ingestionTimestamp;
49984
+ }
49798
49985
 
49799
49986
  function findSpanningField(name) {
49800
49987
  return (field) => {
@@ -50314,17 +50501,7 @@ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
50314
50501
  const key = buildKeyStringForRecordQuery(operation,
50315
50502
  // join varables passed from query to the argument variables given from the AST
50316
50503
  { ...variableValues, ...args }, info.fieldNodes[0].arguments, apiName);
50317
- const sql = `
50318
- SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
50319
- FROM lds_data
50320
- WHERE key IS ?
50321
- `;
50322
- const results = await query(sql, [key]);
50323
- const [timestamp] = results.rows.map((row) => row[0]);
50324
- if (timestamp !== null && typeof timestamp === 'number') {
50325
- //go back 10 ms to adjust for margin of error when top level query is stored and when raml objects are stored
50326
- ingestionTimestamp = timestamp - 10;
50327
- }
50504
+ return readIngestionTimestampForKey(key, query);
50328
50505
  }
50329
50506
  return ingestionTimestamp;
50330
50507
  }
@@ -50372,26 +50549,20 @@ function generateRecordQueries(objectInfos) {
50372
50549
  let recordConnections = ``;
50373
50550
  const polymorphicFieldTypeNames = new Set();
50374
50551
  let typedScalars = new Set();
50552
+ let parentRelationshipFields = new Set();
50375
50553
  for (const objectInfo of values$1(objectInfos)) {
50376
50554
  const { apiName, childRelationships } = objectInfo;
50377
50555
  let fields = ``;
50378
50556
  typedScalars.add(`${apiName}_Filter`);
50379
50557
  typedScalars.add(`${apiName}_OrderBy`);
50380
- for (const childRelationship of childRelationships) {
50381
- const { childObjectApiName } = childRelationship;
50382
- // Only add the relationship if there is relevant objectinfos for it,
50383
- // otherwise we'd be defining types we cannot satisfy and aren't referenced in
50384
- // the query.
50385
- if (objectInfos[childObjectApiName] !== undefined) {
50386
- fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
50387
- typedScalars.add(`${childObjectApiName}_Filter`);
50388
- typedScalars.add(`${childObjectApiName}_OrderBy`);
50389
- }
50390
- }
50391
50558
  for (const field of values$1(objectInfo.fields)) {
50392
50559
  if (!fieldsStaticallyAdded.includes(field.apiName)) {
50393
50560
  fields += `${field.apiName}: ${dataTypeToType(field.dataType, field.apiName)}\n`;
50394
50561
  }
50562
+ //handles parent relationship
50563
+ if (field.relationshipName === null) {
50564
+ continue;
50565
+ }
50395
50566
  // For spanning parent relationships with no union types
50396
50567
  if (field.referenceToInfos.length === 1) {
50397
50568
  const [relation] = field.referenceToInfos;
@@ -50399,11 +50570,13 @@ function generateRecordQueries(objectInfos) {
50399
50570
  // otherwise we'd be defining types we cannot satisfy and aren't referenced in
50400
50571
  // the query.
50401
50572
  if (objectInfos[relation.apiName] !== undefined) {
50573
+ parentRelationshipFields.add(field.relationshipName);
50402
50574
  fields += `${field.relationshipName}: ${relation.apiName}\n`;
50403
50575
  }
50404
50576
  // For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
50405
50577
  }
50406
50578
  else if (field.referenceToInfos.length > 1) {
50579
+ parentRelationshipFields.add(field.relationshipName);
50407
50580
  fields += `${field.relationshipName}: Record\n`;
50408
50581
  for (const relation of field.referenceToInfos) {
50409
50582
  if (objectInfos[relation.apiName] !== undefined) {
@@ -50412,6 +50585,20 @@ function generateRecordQueries(objectInfos) {
50412
50585
  }
50413
50586
  }
50414
50587
  }
50588
+ // handles child relationship
50589
+ for (const childRelationship of childRelationships) {
50590
+ const { childObjectApiName } = childRelationship;
50591
+ // Only add the relationship if there is relevant objectinfos for it,
50592
+ // otherwise we'd be defining types we cannot satisfy and aren't referenced in
50593
+ // the query.
50594
+ // If one field has both parent relationship and child relationship with the same name, the child relationship is ignored. This is how the server GQL has implemented as date of 08/07/2023
50595
+ if (objectInfos[childObjectApiName] !== undefined &&
50596
+ !parentRelationshipFields.has(childRelationship.relationshipName)) {
50597
+ fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
50598
+ typedScalars.add(`${childObjectApiName}_Filter`);
50599
+ typedScalars.add(`${childObjectApiName}_OrderBy`);
50600
+ }
50601
+ }
50415
50602
  recordQueries += `${apiName}(first: Int, where: ${apiName}_Filter, orderBy: ${apiName}_OrderBy, scope: SupportedScopes): ${apiName}Connection\n`;
50416
50603
  const isServiceAppointment = apiName === 'ServiceAppointment';
50417
50604
  recordConnections += /* GraphQL */ `
@@ -50486,6 +50673,8 @@ function dataTypeToType(objectInfoDataType, apiName) {
50486
50673
  return 'PercentValue';
50487
50674
  case 'Int':
50488
50675
  return 'IntValue';
50676
+ case 'EncryptedString':
50677
+ return 'EncryptedStringValue';
50489
50678
  // ! do the rest of the custom types
50490
50679
  default:
50491
50680
  return 'String';
@@ -50571,7 +50760,7 @@ const parentRelationshipDirective = {
50571
50760
  },
50572
50761
  value: {
50573
50762
  kind: Kind.STRING,
50574
- value: 'parentRelationship',
50763
+ value: PARENT_RELATIONSHIP,
50575
50764
  block: false,
50576
50765
  },
50577
50766
  },
@@ -50585,8 +50774,8 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50585
50774
  // example 2 'ServiceAppointment' -> ['Owner']; 'Owner' -> ['User', 'Group']
50586
50775
  const objectNodeInfoTree = {};
50587
50776
  // save the field path to apiName map
50588
- // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account']; 'ServiceAppointment_Account_Owner' -> ['User']
50589
- const objectInfoApiMap = {};
50777
+ // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#ccount' -> ['Account']; 'ServiceAppointment#Account#Owner' -> ['User']
50778
+ const pathToObjectApiNamesMap = {};
50590
50779
  let startNodes = new Set();
50591
50780
  let totalNodes = new Set();
50592
50781
  let objectInfos = {};
@@ -50600,11 +50789,11 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50600
50789
  visit(originalAST, {
50601
50790
  Argument: {
50602
50791
  enter(node, key, parent, path, ancestors) {
50603
- const recordQueryNode = findNearestRecordQuery(ancestors);
50604
- if (!recordQueryNode)
50792
+ const { connection: recordConnectionNode, path: ancesterPath } = findNearestConnectionWithPath(ancestors);
50793
+ if (!recordConnectionNode || !ancesterPath)
50605
50794
  return;
50606
- if (!objectNodeInfoTree[recordQueryNode.name.value]) {
50607
- objectNodeInfoTree[recordQueryNode.name.value] = [];
50795
+ if (!objectNodeInfoTree[ancesterPath]) {
50796
+ objectNodeInfoTree[ancesterPath] = [];
50608
50797
  }
50609
50798
  switch (node.name.value) {
50610
50799
  case 'orderBy':
@@ -50612,12 +50801,12 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50612
50801
  if (node.value.kind !== 'ObjectValue') {
50613
50802
  return;
50614
50803
  }
50615
- totalNodes.add(recordQueryNode.name.value);
50804
+ totalNodes.add(ancesterPath);
50616
50805
  // 'childRelationship' node is not taken as the startNode of the 'NodeInfoTree' graph. The field scanning will construct the graph which lead here.
50617
- if (isRecordQuery(recordQueryNode)) {
50618
- startNodes.add(recordQueryNode.name.value);
50806
+ if (isRecordQuery(recordConnectionNode)) {
50807
+ startNodes.add(recordConnectionNode.name.value);
50619
50808
  }
50620
- growObjectFieldTree(objectNodeInfoTree, recordQueryNode.name.value, node.value, totalNodes, startNodes);
50809
+ growObjectFieldTree(objectNodeInfoTree, ancesterPath, node.value, totalNodes, startNodes);
50621
50810
  break;
50622
50811
  case 'scope':
50623
50812
  if (!isScopeArgumentNodeWithType(node, 'ASSIGNEDTOME', variables)) {
@@ -50632,17 +50821,16 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50632
50821
  name: 'ServiceResources',
50633
50822
  });
50634
50823
  }
50635
- if (objectNodeInfoTree['ServiceResources'] === undefined) {
50636
- objectNodeInfoTree['ServiceResources'] = [];
50637
- }
50638
- if (!objectNodeInfoTree['ServiceResources'].some((child) => child.name === 'ServiceResource')) {
50639
- objectNodeInfoTree['ServiceResources'].push({
50640
- relation: 'parent',
50641
- name: 'ServiceResource',
50642
- });
50824
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources'] === undefined) {
50825
+ objectNodeInfoTree['ServiceAppointment#ServiceResources'] = [
50826
+ {
50827
+ relation: 'parent',
50828
+ name: 'ServiceResource',
50829
+ },
50830
+ ];
50643
50831
  }
50644
- if (objectNodeInfoTree['ServiceResource'] === undefined) {
50645
- objectNodeInfoTree['ServiceResource'] = [];
50832
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] === undefined) {
50833
+ objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] = [];
50646
50834
  }
50647
50835
  break;
50648
50836
  default:
@@ -50656,7 +50844,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50656
50844
  return;
50657
50845
  if (!node.selectionSet)
50658
50846
  return;
50659
- const recordQueryField = findNearestRecordQuery(ancestors);
50847
+ const recordQueryField = findNearestConnection(ancestors);
50660
50848
  //only injects fields for 'recordQuery' field. ignores the 'childRelationship' field since it will be traversed as the child of the 'recordQuery'
50661
50849
  if (isRecordQuery(recordQueryField) && recordQueryField) {
50662
50850
  totalNodes.add(recordQueryField.name.value);
@@ -50667,21 +50855,21 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50667
50855
  },
50668
50856
  });
50669
50857
  if (objectInfoService && startNodes.size > 0) {
50670
- objectInfos = await resolveObjectInfos(objectNodeInfoTree, objectInfoApiMap, startNodes, objectInfoService);
50858
+ objectInfos = await resolveObjectInfos(objectNodeInfoTree, pathToObjectApiNamesMap, startNodes, objectInfoService);
50671
50859
  }
50672
50860
  // read pass; gather whats needed
50673
50861
  visit(originalAST, {
50674
50862
  Argument: {
50675
50863
  leave(node, key, parent, path, ancestors) {
50676
- const recordQueryField = findNearestRecordQuery(ancestors);
50864
+ const recordQueryField = findNearestConnection(ancestors);
50677
50865
  if (!recordQueryField)
50678
50866
  return;
50679
50867
  const ancestorPath = findAncesterPath(ancestors);
50680
50868
  if (!inlineFragmentSelections[ancestorPath]) {
50681
50869
  inlineFragmentSelections[ancestorPath] = [];
50682
50870
  }
50683
- const recordQueryApiName = objectInfoApiMap[ancestorPath]
50684
- ? objectInfoApiMap[ancestorPath][0]
50871
+ const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
50872
+ ? pathToObjectApiNamesMap[ancestorPath][0]
50685
50873
  : recordQueryField.name.value;
50686
50874
  // The record node acts as the reference. The duplicated field in the record node is not injected
50687
50875
  const recordReferenceNode = [recordQueryField]
@@ -50695,7 +50883,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50695
50883
  case 'scope':
50696
50884
  // Hanle 'MINE' field
50697
50885
  if (isScopeArgumentNodeWithType(node, 'MINE', variables)) {
50698
- if (isMineScopeAvailable(ancestorPath, objectInfoApiMap, objectInfos)) {
50886
+ if (isMineScopeAvailable(ancestorPath, pathToObjectApiNamesMap, objectInfos)) {
50699
50887
  // 'typeConditon' is added when the 'InlineFragmentNode' is appended at the write pass
50700
50888
  inlineFragmentSelections[ancestorPath].push(...mineFragmentSelections);
50701
50889
  }
@@ -50721,7 +50909,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50721
50909
  case 'where': {
50722
50910
  inlineFragmentSelections[ancestorPath] = [
50723
50911
  ...inlineFragmentSelections[ancestorPath],
50724
- ...injectFilter(node, idState, ancestorPath, objectInfos, objectInfoApiMap, draftFunctions, recordReferenceNode),
50912
+ ...injectFilter(node, idState, ancestorPath, false, objectInfos, pathToObjectApiNamesMap, draftFunctions, recordReferenceNode),
50725
50913
  ];
50726
50914
  break;
50727
50915
  }
@@ -50737,7 +50925,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50737
50925
  if (!node.selectionSet)
50738
50926
  return;
50739
50927
  // it could be 'recordQuery' or 'childRelationship'
50740
- const recordQueryField = findNearestRecordQuery(ancestors);
50928
+ const recordQueryField = findNearestConnection(ancestors);
50741
50929
  if (!recordQueryField)
50742
50930
  return;
50743
50931
  const ancestorPath = findAncesterPath(ancestors);
@@ -50749,7 +50937,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50749
50937
  spanningSelections.push(selection);
50750
50938
  }
50751
50939
  }
50752
- const injectedFields = injectFields(spanningSelections, node, ancestors, objectInfos, objectInfoApiMap);
50940
+ const injectedFields = injectFields(spanningSelections, node, ancestorPath, ancestors, objectInfos, pathToObjectApiNamesMap);
50753
50941
  const mergedInjectedFields = mergeSelectionNodes$1(inlineFragmentSelections[ancestorPath], injectedFields);
50754
50942
  inlineFragmentSelections[ancestorPath] = mergedInjectedFields;
50755
50943
  },
@@ -50762,7 +50950,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50762
50950
  // removes 'ServicesResources' query field node if 'assignedtome' scope shows up
50763
50951
  if (assignedtomeQueryFieldNode !== undefined &&
50764
50952
  node.name.value === 'ServiceResources') {
50765
- const serviceResourcesAncestor = findNearestRecordQuery(ancestors);
50953
+ const serviceResourcesAncestor = findNearestConnection(ancestors);
50766
50954
  if (serviceResourcesAncestor === assignedtomeQueryFieldNode) {
50767
50955
  return null;
50768
50956
  }
@@ -50771,7 +50959,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50771
50959
  return;
50772
50960
  if (!node.selectionSet)
50773
50961
  return;
50774
- const recordQueryField = findNearestRecordQuery(ancestors);
50962
+ const recordQueryField = findNearestConnection(ancestors);
50775
50963
  if (!recordQueryField)
50776
50964
  return;
50777
50965
  const ancestorPath = findAncesterPath(ancestors);
@@ -50780,8 +50968,8 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50780
50968
  return;
50781
50969
  //const recordQueryPath = findAncesterPath(ancestors);
50782
50970
  // 'apiName' has to be at index 0 since 'node' record type could only be of 'recordQuery' or 'childRelationship'. They can not have the 'InlineFragmentNode' as its children.
50783
- const recordQueryApiName = objectInfoApiMap[ancestorPath]
50784
- ? objectInfoApiMap[ancestorPath][0]
50971
+ const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
50972
+ ? pathToObjectApiNamesMap[ancestorPath][0]
50785
50973
  : recordQueryField.name.value;
50786
50974
  const nodeWithFragments = {
50787
50975
  ...node,
@@ -50818,7 +51006,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
50818
51006
  if (node.name.value === 'where') {
50819
51007
  const ancestorPath = findAncesterPath(ancestors);
50820
51008
  if (idState.paths.includes(ancestorPath)) {
50821
- const apiName = objectInfoApiMap[ancestorPath][0];
51009
+ const apiName = pathToObjectApiNamesMap[ancestorPath][0];
50822
51010
  const objectInfo = objectInfos[apiName];
50823
51011
  const swappedIdFilter = swapIdField(node.value, objectInfo, false, idState, draftFunctions);
50824
51012
  return {
@@ -50884,8 +51072,8 @@ function swapIdField(filterFields, objectInfo, swapped, idState, draftFunctions)
50884
51072
  };
50885
51073
  }
50886
51074
  }
50887
- function isMineScopeAvailable(apiNamePath, objectInfoApiMap, objectInfos) {
50888
- const apiName = objectInfoApiMap[apiNamePath];
51075
+ function isMineScopeAvailable(apiNamePath, pathToObjectApiNamesMap, objectInfos) {
51076
+ const apiName = pathToObjectApiNamesMap[apiNamePath];
50889
51077
  if (!apiName)
50890
51078
  return false;
50891
51079
  const objectInfo = objectInfos[apiName[0]];
@@ -50974,15 +51162,16 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
50974
51162
  }
50975
51163
  // example: 'Account'
50976
51164
  const childNode = objectFieldNode.name.value;
51165
+ const childNodepath = `${parentNode}#${childNode}`;
50977
51166
  if (!tree[parentNode].some((child) => child.name === childNode)) {
50978
51167
  tree[parentNode].push({
50979
51168
  relation: 'parent',
50980
51169
  name: childNode,
50981
51170
  });
50982
- totalNodes.add(childNode);
51171
+ totalNodes.add(childNodepath);
50983
51172
  }
50984
51173
  // recursively go to deeper level of filter.
50985
- growObjectFieldTree(tree, childNode, objectFieldNode.value, totalNodes, startNodes);
51174
+ growObjectFieldTree(tree, childNodepath, objectFieldNode.value, totalNodes, startNodes);
50986
51175
  }
50987
51176
  }
50988
51177
  }
@@ -51017,19 +51206,20 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
51017
51206
  }
51018
51207
  if (!tree[parentSectionPath].some((field) => field.name === fieldName)) {
51019
51208
  tree[parentSectionPath].push({
51020
- relation: relationType === 'parentRelationship' ||
51021
- relationType === 'polymorphicParentRelationship'
51209
+ relation: relationType === PARENT_RELATIONSHIP ||
51210
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
51022
51211
  ? 'parent'
51023
51212
  : 'child',
51024
51213
  name: fieldName,
51025
51214
  });
51026
- totalNodes.add(fieldName);
51215
+ totalNodes.add(`${parentSectionPath}#${fieldName}`);
51027
51216
  }
51028
51217
  if (entryNode.selectionSet && entryNode.selectionSet.selections) {
51029
51218
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
51030
51219
  // recursively build the traversal tree
51031
51220
  for (const child of childNodes) {
51032
- growFieldTree(tree, fieldName, child, entryNode, totalNodes, startNodes);
51221
+ const path = `${parentSectionPath}#${fieldName}`;
51222
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
51033
51223
  }
51034
51224
  }
51035
51225
  }
@@ -51059,23 +51249,23 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
51059
51249
  }
51060
51250
  if (!tree[parentSectionPath].some((field) => field.name === conditionName)) {
51061
51251
  tree[parentSectionPath].push({
51062
- relation: relationType === 'parentRelationship' ||
51063
- relationType === 'polymorphicParentRelationship'
51252
+ relation: relationType === PARENT_RELATIONSHIP ||
51253
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
51064
51254
  ? 'parent'
51065
51255
  : 'child',
51066
51256
  name: conditionName,
51067
51257
  });
51068
- totalNodes.add(conditionName);
51258
+ const path = `${parentSectionPath}#${conditionName}`;
51259
+ totalNodes.add(path);
51069
51260
  }
51070
51261
  }
51071
51262
  }
51072
51263
  // dive deep immediately for 'InlineFragment'
51073
51264
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
51265
+ const path = `${parentSectionPath}${entryNode.typeCondition ? '#' + entryNode.typeCondition.name.value : ''}`;
51074
51266
  // Navigates into InLineFragment
51075
51267
  for (const child of childNodes) {
51076
- growFieldTree(tree, entryNode.typeCondition
51077
- ? entryNode.typeCondition.name.value
51078
- : parentSectionPath, child, entryNode, totalNodes, startNodes);
51268
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
51079
51269
  }
51080
51270
  }
51081
51271
  }
@@ -51087,7 +51277,7 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
51087
51277
  * @param startNodes start nodes of the tree. It can be used to fetch ObjectInfo immediately
51088
51278
  * @param path
51089
51279
  */
51090
- async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes, objectInfoService) {
51280
+ async function resolveObjectInfos(objectInfotree, pathToObjectApiNamesMap, startNodes, objectInfoService) {
51091
51281
  let objectInfos;
51092
51282
  try {
51093
51283
  objectInfos = await objectInfoService.getObjectInfos(Array.from(startNodes));
@@ -51101,9 +51291,9 @@ async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes,
51101
51291
  throw new Error(`Unable to resolve ObjectInfo(s) for ${Array.from(startNodes)}`);
51102
51292
  }
51103
51293
  for (const startNode of startNodes) {
51104
- objectInfoApiMap[startNode] = [startNode];
51294
+ pathToObjectApiNamesMap[startNode] = [startNode];
51105
51295
  const children = objectInfotree[startNode];
51106
- const subObjectInfoMap = await fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfos, children, startNode, objectInfoService);
51296
+ const subObjectInfoMap = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfos, children, startNode, objectInfoService);
51107
51297
  objectInfos = { ...objectInfos, ...subObjectInfoMap };
51108
51298
  }
51109
51299
  return objectInfos;
@@ -51111,15 +51301,15 @@ async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes,
51111
51301
  // example 1: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Account']
51112
51302
  // example 2: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Owner'], this example has 2 apiName for the node 'Owner'
51113
51303
  // example 3: 'parentPath': 'ServiceAppointment_Owner', 'nodesAtSameLevel': ['User', 'Group']
51114
- async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap, nodesAtSameLevel, parentPath, objectInfoService) {
51115
- const objectInfoApiNames = objectInfoApiMap[parentPath];
51304
+ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfoMap, nodesAtSameLevel, parentPath, objectInfoService) {
51305
+ const objectInfoApiNames = pathToObjectApiNamesMap[parentPath];
51116
51306
  if (!objectInfoApiNames) {
51117
51307
  // eslint-disable-next-line
51118
51308
  throw new Error(`Object Info does not exist for ${parentPath}`);
51119
51309
  }
51120
51310
  const validObjectInfoNodes = [];
51121
51311
  let updatedObjectInfoMap = {};
51122
- // InlineFragment and polymorphic field support fits into this scenario ObjectInfoApiMap Entry: 'ServiceAppointment_Owner' -> ['User', 'Group']; ServiceAppointment_Owner_User' -> ['User']
51312
+ // InlineFragment and polymorphic field support fits into this scenario pathToObjectApiNamesMap Entry: 'ServiceAppointment#Owner' -> ['User', 'Group']; ServiceAppointment#Owner#User' -> ['User']
51123
51313
  if (objectInfoApiNames.length > 0 &&
51124
51314
  nodesAtSameLevel.length > 0 &&
51125
51315
  objectInfoApiNames.includes(nodesAtSameLevel[0].name)) {
@@ -51131,8 +51321,8 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
51131
51321
  // eslint-disable-next-line
51132
51322
  throw new Error(`Condition ${field.name} does not exists for ${parentPath}`);
51133
51323
  }
51134
- const path = `${parentPath}_${field.name}`;
51135
- objectInfoApiMap[path] = [field.name];
51324
+ const path = `${parentPath}#${field.name}`;
51325
+ pathToObjectApiNamesMap[path] = [field.name];
51136
51326
  }
51137
51327
  validObjectInfoNodes.push(...nodesAtSameLevel);
51138
51328
  updatedObjectInfoMap = { ...objectInfoMap };
@@ -51147,7 +51337,7 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
51147
51337
  let apiNames = [];
51148
51338
  for (const nodeInfo of nodesAtSameLevel) {
51149
51339
  const field = nodeInfo.name;
51150
- const path = `${parentPath}_${field}`;
51340
+ const path = `${parentPath}#${field}`;
51151
51341
  // Handle 'parentRelationship'
51152
51342
  if (nodeInfo.relation === 'parent') {
51153
51343
  const relationshipId = referenceIdFieldForRelationship(field);
@@ -51165,21 +51355,21 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
51165
51355
  }
51166
51356
  }
51167
51357
  // This is a polymorphic field
51168
- if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[field]) {
51358
+ if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[path]) {
51169
51359
  // Fields needs to expand and heterogenous entity ObjectInfo needs to be fetched
51170
- const referencedNodeInfos = objectInfotree[field];
51360
+ const referencedNodeInfos = objectInfotree[path];
51171
51361
  const requestedApiNames = referencedNodeInfos.map((referenceNodeInfo) => referenceNodeInfo.name);
51172
51362
  // Fetches requested ObjectInfo only. Some entity's relation field could define more than 6 references. Only references show up in query need to be handled.
51173
- if (requestedApiNames.length > 0 && objectInfotree[field]) {
51363
+ if (requestedApiNames.length > 0 && objectInfotree[path]) {
51174
51364
  fieldDefinition.referenceToInfos
51175
51365
  .filter((referenceToInfo) => requestedApiNames.includes(referenceToInfo.apiName))
51176
51366
  .forEach((ref) => {
51177
- if (!objectInfoApiMap[path]) {
51178
- objectInfoApiMap[path] = [];
51367
+ if (!pathToObjectApiNamesMap[path]) {
51368
+ pathToObjectApiNamesMap[path] = [];
51179
51369
  }
51180
51370
  // 'ServiceAppointment_Owner' ->['User', 'Group']
51181
- if (!objectInfoApiMap[path].includes(ref.apiName)) {
51182
- objectInfoApiMap[path].push(ref.apiName);
51371
+ if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
51372
+ pathToObjectApiNamesMap[path].push(ref.apiName);
51183
51373
  }
51184
51374
  if (!apiNames.includes(ref.apiName)) {
51185
51375
  apiNames.push(ref.apiName);
@@ -51189,11 +51379,11 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
51189
51379
  }
51190
51380
  else if (fieldDefinition.referenceToInfos.length === 1) {
51191
51381
  const ref = fieldDefinition.referenceToInfos[0];
51192
- if (!objectInfoApiMap[path]) {
51193
- objectInfoApiMap[path] = [];
51382
+ if (!pathToObjectApiNamesMap[path]) {
51383
+ pathToObjectApiNamesMap[path] = [];
51194
51384
  }
51195
- if (!objectInfoApiMap[path].includes(ref.apiName)) {
51196
- objectInfoApiMap[path].push(ref.apiName);
51385
+ if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
51386
+ pathToObjectApiNamesMap[path].push(ref.apiName);
51197
51387
  }
51198
51388
  if (!apiNames.includes(ref.apiName)) {
51199
51389
  apiNames.push(ref.apiName);
@@ -51204,11 +51394,11 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
51204
51394
  // handles 'childRelationship'
51205
51395
  const childRelationship = parentObjectInfo.childRelationships.find((childRelationship) => childRelationship.relationshipName === field);
51206
51396
  if (childRelationship) {
51207
- if (!objectInfoApiMap[path]) {
51208
- objectInfoApiMap[path] = [];
51397
+ if (!pathToObjectApiNamesMap[path]) {
51398
+ pathToObjectApiNamesMap[path] = [];
51209
51399
  }
51210
- if (!objectInfoApiMap[path].includes(childRelationship.childObjectApiName)) {
51211
- objectInfoApiMap[path].push(childRelationship.childObjectApiName);
51400
+ if (!pathToObjectApiNamesMap[path].includes(childRelationship.childObjectApiName)) {
51401
+ pathToObjectApiNamesMap[path].push(childRelationship.childObjectApiName);
51212
51402
  }
51213
51403
  if (!apiNames.includes(childRelationship.childObjectApiName)) {
51214
51404
  apiNames.push(childRelationship.childObjectApiName);
@@ -51230,10 +51420,10 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
51230
51420
  }
51231
51421
  for (const nodeInfo of validObjectInfoNodes) {
51232
51422
  const field = nodeInfo.name;
51233
- const subLevelFields = objectInfotree[field];
51234
- const path = `${parentPath}_${field}`;
51423
+ const path = `${parentPath}#${field}`;
51424
+ const subLevelFields = objectInfotree[path];
51235
51425
  if (subLevelFields && subLevelFields.length > 0) {
51236
- const subObjectInfos = await fetchObjectInfos(objectInfotree, objectInfoApiMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
51426
+ const subObjectInfos = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
51237
51427
  updatedObjectInfoMap = { ...updatedObjectInfoMap, ...subObjectInfos };
51238
51428
  }
51239
51429
  }
@@ -51248,27 +51438,29 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
51248
51438
  * 'path' and 'queryNode' is 1 level above the 'filterNode'
51249
51439
  * @param filterNode filter node which needs to be injected. For example, 'State' ObjectFieldNode within filter 'where: { State: { eq: "Nova Scotia" }}'
51250
51440
  * @param idState ID state will be updated to determine if the ID fields in AST need to be swapped. The swapping happens later.
51251
- * @param path path to the current filterNode's parent. For example, path could be 'ServiceApointment' when filterNode is 'State'. If the path does not exist in 'objectInfoApiMap', parent node is not an field of relationship or recordQuery
51441
+ * @param parentPath path to the current filterNode's parent. For example, path could be 'ServiceApointment' when filterNode is 'State'. If the path does not exist in 'pathToObjectApiNamesMap', parent node is not an field of relationship or recordQuery
51442
+ * @param isParentPolymorphic true if parent points to a polymorphic field.
51252
51443
  * @param queryNode referece FieldNode which provides the information if 'filterNode' exist in it nor not.
51253
51444
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
51254
- * @param objectInfoApiMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account'], 'ServiceAppointment_Owner' -> ['User', 'Group'].
51445
+ * @param pathToObjectApiNamesMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account'], 'ServiceAppointment_Owner' -> ['User', 'Group'].
51255
51446
  * @param draftFunctions functions for working with record ids that may be draft-created ids
51256
51447
  * @returns an array of nodes with injected fields
51257
51448
  */
51258
- function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode) {
51449
+ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode) {
51259
51450
  const injectedSelections = [];
51451
+ let isPolymorphicField = false;
51260
51452
  switch (filterNode.kind) {
51261
51453
  case Kind.ARGUMENT:
51262
51454
  if (filterNode.value.kind !== 'ObjectValue')
51263
51455
  return [];
51264
51456
  filterNode.value.fields.forEach((objectFieldNode) => {
51265
- let subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
51457
+ let subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
51266
51458
  for (const subResult of subResults) {
51267
51459
  mergeOrAddToGroup(injectedSelections, subResult);
51268
51460
  }
51269
51461
  // multiple Ids might need to be swapped. remember their paths for faster write.
51270
51462
  if (idState.swapNeeded) {
51271
- idState.paths.push(path);
51463
+ idState.paths.push(parentPath);
51272
51464
  }
51273
51465
  });
51274
51466
  return injectedSelections;
@@ -51277,7 +51469,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
51277
51469
  case Kind.LIST: {
51278
51470
  filterNode.value.values.filter(isObjectValueNode).forEach((objectValueNode) => {
51279
51471
  objectValueNode.fields.forEach((objectFieldNode) => {
51280
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
51472
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
51281
51473
  for (const subResult of subResults) {
51282
51474
  mergeOrAddToGroup(injectedSelections, subResult);
51283
51475
  }
@@ -51288,7 +51480,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
51288
51480
  case Kind.OBJECT: {
51289
51481
  if (filterNode.name.value === 'not') {
51290
51482
  filterNode.value.fields.forEach((objectFieldNode) => {
51291
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
51483
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
51292
51484
  for (const subResult of subResults) {
51293
51485
  mergeOrAddToGroup(injectedSelections, subResult);
51294
51486
  }
@@ -51298,15 +51490,15 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
51298
51490
  let apiNames = [];
51299
51491
  let isScalarField = false;
51300
51492
  //It is possible that this is a polymorphic field
51301
- apiNames = objectInfoApiMap[path];
51302
- // example: path: 'ServiceAppointment_LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
51493
+ apiNames = pathToObjectApiNamesMap[parentPath];
51494
+ // example: path: 'ServiceAppointment#LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
51303
51495
  if (apiNames === undefined) {
51304
51496
  isScalarField = true;
51305
51497
  }
51306
51498
  else {
51307
51499
  if (apiNames.some((apiName) => objectInfos[apiName] === undefined)) {
51308
51500
  // eslint-disable-next-line
51309
- throw new Error(`ObjectInfo is missing for ${path}`);
51501
+ throw new Error(`ObjectInfo is missing for ${parentPath}`);
51310
51502
  }
51311
51503
  }
51312
51504
  if (isScalarField) {
@@ -51328,29 +51520,19 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
51328
51520
  }
51329
51521
  });
51330
51522
  let isSpanning = false;
51523
+ // if true, current node is a polymorphic concrete type node. For example, field node `User` under `Owner`
51331
51524
  let isInlineFragment = false;
51332
- let isPolymorphicField = false;
51333
51525
  let isTypeNameExisting = false;
51334
51526
  let curPath;
51335
51527
  let fieldName = filterNode.name.value;
51336
- curPath = `${path}_${fieldName}`;
51337
- if (objectInfoApiMap[curPath] && objectInfoApiMap[curPath].length > 0) {
51528
+ curPath = `${parentPath}#${fieldName}`;
51529
+ if (pathToObjectApiNamesMap[curPath] &&
51530
+ pathToObjectApiNamesMap[curPath].length > 0) {
51338
51531
  isSpanning = true;
51339
- if (objectInfoApiMap[curPath].length === 1) {
51340
- if (objectInfoApiMap[path] &&
51341
- objectInfoApiMap[path].length >= 1 &&
51342
- objectInfoApiMap[path].includes(objectInfoApiMap[curPath][0])) {
51343
- isInlineFragment = true;
51344
- }
51345
- }
51346
- // Checks if the current filter node is a polymorphic field. 'ServiceAppointment_Owner' --> ['User']; 'ServiceAppointment_Owner_User' --> ['User']
51347
- const childApiName = objectInfoApiMap[curPath][0];
51348
- const trialApiNames = objectInfoApiMap[`${curPath}_${childApiName}`];
51349
- if (trialApiNames !== undefined &&
51350
- trialApiNames.length === 1 &&
51351
- trialApiNames[0] === childApiName) {
51352
- isPolymorphicField = true;
51353
- }
51532
+ isInlineFragment =
51533
+ isParentPolymorphic &&
51534
+ pathToObjectApiNamesMap[curPath].length === 1;
51535
+ isPolymorphicField = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
51354
51536
  }
51355
51537
  // When filter node is at InLineFragment Level(a concrete entity of polymorphic field), query node is one level up. For example, ObjectFieldNode is ...{User:{...}}, queryNode is Owner:[User]
51356
51538
  if (isInlineFragment) {
@@ -51394,23 +51576,25 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
51394
51576
  throw new Error(`Field ${fieldName} does not exist in ${apiNames[0]}`);
51395
51577
  }
51396
51578
  }
51397
- const objectInfoName = objectInfoApiMap[curPath] !== undefined
51398
- ? objectInfoApiMap[curPath][0]
51399
- : objectInfoApiMap[path][0];
51579
+ const objectInfoName = pathToObjectApiNamesMap[curPath] !== undefined
51580
+ ? pathToObjectApiNamesMap[curPath][0]
51581
+ : pathToObjectApiNamesMap[parentPath][0];
51400
51582
  const isIdField = isFieldAnIdField(filterNode.name.value, objectInfos[objectInfoName]);
51401
51583
  if (!isIdField) {
51402
51584
  let subSelectionNodes = [];
51403
51585
  let subFieldsHasId = false;
51404
- filterNode.value.fields.forEach((subFieldNode) => {
51405
- // Check if the filter field has the 'Id'
51406
- if (isFieldAnIdField(subFieldNode.name.value, objectInfos[objectInfoName])) {
51407
- subFieldsHasId = true;
51408
- updateIDInfo(subFieldNode, idState, draftFunctions);
51409
- }
51410
- // try injecting the fields within predicate no matter it has relation or not.
51411
- let subResults = injectFilter(subFieldNode, idState, curPath, objectInfos, objectInfoApiMap, draftFunctions, existingFields ? existingFields[0] : undefined);
51412
- subSelectionNodes = subSelectionNodes.concat(subResults);
51413
- });
51586
+ if (isSpanning) {
51587
+ filterNode.value.fields.forEach((subFieldNode) => {
51588
+ // Check if the filter field has the 'Id'
51589
+ if (isFieldAnIdField(subFieldNode.name.value, objectInfos[objectInfoName])) {
51590
+ subFieldsHasId = true;
51591
+ updateIDInfo(subFieldNode, idState, draftFunctions);
51592
+ }
51593
+ // try injecting the fields within predicate no matter it has relation or not.
51594
+ let subResults = injectFilter(subFieldNode, idState, curPath, isPolymorphicField, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
51595
+ subSelectionNodes = subSelectionNodes.concat(subResults);
51596
+ });
51597
+ }
51414
51598
  if (!subFieldsHasId) {
51415
51599
  // Check if the query field has the 'Id'
51416
51600
  const existingIdsInQuery = existingFields &&
@@ -51439,6 +51623,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
51439
51623
  }
51440
51624
  //Inject Conditions: 1. Same field does not exist 2. Same fields has different children. 3. Filter spanning field does not have Id. 4. InLineFragment does not have the '__typename' field
51441
51625
  if (!existingFields ||
51626
+ existingFields.length === 0 ||
51442
51627
  subSelectionNodes.length > 0 ||
51443
51628
  (isSpanning && !subFieldsHasId) ||
51444
51629
  (isInlineFragment && !isTypeNameExisting)) {
@@ -51569,6 +51754,44 @@ function mergeOrAddToGroup(group, element) {
51569
51754
  }
51570
51755
  group.push(element);
51571
51756
  }
51757
+ // checks if the path points to a polymorphic field. For example, for the below `pathToObjectApiNamesMap`
51758
+ // {
51759
+ // 'ServiceAppointment' -> ['ServiceAppointment']
51760
+ // 'ServiceAppointment#Owner' --> ['User'],
51761
+ // 'ServiceAppointment#Owner#User' --> ['User']
51762
+ // }
51763
+ // path `ServiceAppointment#Owner` points to a polymorphic field, but path `ServiceAppointment#Owner#User` does not.
51764
+ function isPolymorphicFieldPath(path, pathToObjectApiNamesMap, objectInfos) {
51765
+ const lastSegmentIndex = path.lastIndexOf('#');
51766
+ if (lastSegmentIndex < 0) {
51767
+ return false;
51768
+ }
51769
+ const lastSegment = path.slice(lastSegmentIndex + 1);
51770
+ const parentApiPath = path.slice(0, lastSegmentIndex);
51771
+ if (pathToObjectApiNamesMap[parentApiPath] === undefined) {
51772
+ return false;
51773
+ }
51774
+ const parentObjectApiNames = pathToObjectApiNamesMap[parentApiPath];
51775
+ // If the last segment is a Polymorphic field, its immediate parent is a concrete object entity, which has 1 objectApiName mapped to the parent path in `pathToObjectApiNamesMap`.
51776
+ // For example, we like to check if `ServiceAppointment#Owner` path is polymorphic. The last segment is `Owner` and its parent `ServiceAppointment` has one element (which is also `ServiceAppointment`) array as its value.
51777
+ // Below are the entries in `pathToObjectApiNamesMap`
51778
+ // {
51779
+ // `ServiceAppointmen`t: [`ServiceAppointment`],
51780
+ // `ServiceAppointment#Owner`: [`User`, `Group`],
51781
+ // `ServiceAppointment#Owner#User`: [`User`],
51782
+ // `ServiceAppointment#Owner#Group`: [`Group`],
51783
+ // }
51784
+ if (parentObjectApiNames.length !== 1) {
51785
+ return false;
51786
+ }
51787
+ const parentObjectInfo = objectInfos[parentObjectApiNames[0]];
51788
+ const relationshipField = referenceIdFieldForRelationship(lastSegment);
51789
+ let fieldDefinition = parentObjectInfo.fields[relationshipField];
51790
+ if (fieldDefinition === undefined) {
51791
+ return false;
51792
+ }
51793
+ return fieldDefinition.polymorphicForeignKey;
51794
+ }
51572
51795
  function isFieldAnIdField(fieldName, objectInfo) {
51573
51796
  if (fieldName === 'Id')
51574
51797
  return true;
@@ -51621,10 +51844,10 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
51621
51844
  * @param parentNode parent node of param 1
51622
51845
  * @param ancestors ancester of param 1
51623
51846
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
51624
- * @param objectInfoApiMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account'], 'ServiceAppointment_Owner' -> ['User', 'Group'].
51847
+ * @param pathToObjectApiNamesMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#Account' -> ['Account'], 'ServiceAppointment#Owner' -> ['User', 'Group'].
51625
51848
  * @return injected SelectionNodes used to construct the InlineFragment.
51626
51849
  */
51627
- function injectFields(selections, parentNode, ancestors, objectInfos, objectInfoApiMap) {
51850
+ function injectFields(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
51628
51851
  /**
51629
51852
  * 1 parentship can return 2 FieldNode which need to be flattened
51630
51853
  * Concact: { ** Contact { ** ContactId {
@@ -51642,6 +51865,10 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
51642
51865
  if (!selection.selectionSet) {
51643
51866
  return selection;
51644
51867
  }
51868
+ const segment = isFieldNode(selection)
51869
+ ? selection.name.value
51870
+ : selection.typeCondition.name.value;
51871
+ const curPath = `${parentPath}#${segment}`;
51645
51872
  const spanningSubSelections = [];
51646
51873
  for (const subSelection of selection.selectionSet.selections) {
51647
51874
  if (isFieldSpanning(subSelection, selection)) {
@@ -51649,7 +51876,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
51649
51876
  }
51650
51877
  }
51651
51878
  // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
51652
- const subInjectedSelections = injectFields(spanningSubSelections, selection, ancestors, objectInfos, objectInfoApiMap);
51879
+ const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
51653
51880
  if (!selection.selectionSet) {
51654
51881
  return selection;
51655
51882
  }
@@ -51692,7 +51919,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
51692
51919
  }
51693
51920
  }
51694
51921
  // For polymorphic fields, the Id field is excluded.
51695
- const excludeId = selection.selectionSet.selections.every(isInlineFragmentNode);
51922
+ const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
51696
51923
  const idSelection = [];
51697
51924
  if (!excludeId && !hasIdAlready) {
51698
51925
  idSelection.push({
@@ -51703,8 +51930,8 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
51703
51930
  },
51704
51931
  });
51705
51932
  }
51706
- // Inject '__typename' for polymorphic fields. '__typename' field acts as a reference to concrete type of a polymorphic field and is used to match JSON response with AST node. For more detail,
51707
- // please reference 'removeSyntheticFields'.
51933
+ // Inject '__typename' for InlineFragment. '__typename' field acts as a reference to concrete type of a polymorphic field or a standard field in the returned GQL response, which equals to
51934
+ // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
51708
51935
  if (isInlineFragmentNode(selection) &&
51709
51936
  !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
51710
51937
  idSelection.push({
@@ -51738,7 +51965,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
51738
51965
  if (isFieldNode(parentNode) && parentNode.selectionSet && parentNode.name.value === 'node') {
51739
51966
  if (parentNode.selectionSet.selections
51740
51967
  .filter(isFieldOrInlineFragmentNode)
51741
- .some((selectionNode) => isRelationship(selectionNode, 'childRelationship'))) {
51968
+ .some((selectionNode) => isRelationship(selectionNode, CHILD_RELATIONSHIP))) {
51742
51969
  if (!parentNode.selectionSet.selections
51743
51970
  .filter(isFieldNode)
51744
51971
  .some((sibling) => sibling.name.value === 'Id')) {
@@ -51757,15 +51984,15 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
51757
51984
  if (parentInfo.parentIndex >= 0) {
51758
51985
  // example node { TimeSheetEntries { edges { node { Id }}}}
51759
51986
  const parent = parentInfo.node;
51760
- if (isRelationship(parent, 'childRelationship')) {
51987
+ if (isRelationship(parent, CHILD_RELATIONSHIP)) {
51761
51988
  const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
51762
51989
  // path : "TimeSheet"
51763
51990
  const grandParentPath = findAncesterPath(unVisitedAncestors);
51764
- if (objectInfoApiMap &&
51765
- objectInfoApiMap[grandParentPath] &&
51991
+ if (pathToObjectApiNamesMap &&
51992
+ pathToObjectApiNamesMap[grandParentPath] &&
51766
51993
  objectInfos &&
51767
- objectInfos[objectInfoApiMap[grandParentPath][0]]) {
51768
- const grandParentObjectInfo = objectInfos[objectInfoApiMap[grandParentPath][0]];
51994
+ objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
51995
+ const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
51769
51996
  // exmaple "TimeSheetEntries"
51770
51997
  const parentFieldName = parent.name.value;
51771
51998
  const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
@@ -51886,7 +52113,7 @@ const assignedToMeFragmentSelections = [
51886
52113
  },
51887
52114
  value: {
51888
52115
  kind: 'StringValue',
51889
- value: 'childRelationship',
52116
+ value: CHILD_RELATIONSHIP,
51890
52117
  block: false,
51891
52118
  },
51892
52119
  },
@@ -51978,7 +52205,7 @@ const assignedToMeFragmentSelections = [
51978
52205
  },
51979
52206
  value: {
51980
52207
  kind: 'StringValue',
51981
- value: 'parentRelationship',
52208
+ value: PARENT_RELATIONSHIP,
51982
52209
  block: false,
51983
52210
  },
51984
52211
  },
@@ -52115,7 +52342,9 @@ function handleNonArrayJsonProperty(selection, fieldName, jsonInput, jsonOutput)
52115
52342
  jsonOutput[fieldName] = null;
52116
52343
  return;
52117
52344
  }
52118
- jsonOutput[fieldName] = {};
52345
+ if (jsonOutput[fieldName] === undefined) {
52346
+ jsonOutput[fieldName] = {};
52347
+ }
52119
52348
  createUserJsonOutput(selection, jsonInput[fieldName], jsonOutput[fieldName]);
52120
52349
  }
52121
52350
  else {
@@ -52847,34 +53076,42 @@ function applyReferenceLinksToDraft(record, draftMetadata) {
52847
53076
  }
52848
53077
  const { dataType, relationshipName, referenceToInfos } = fieldInfo;
52849
53078
  const draftFieldValue = record.fields[draftField].value;
52850
- if (dataType === 'Reference' && relationshipName !== null && draftFieldValue !== null) {
52851
- if (typeof draftFieldValue !== 'string') {
52852
- throw Error('reference field value is not a string');
53079
+ if (dataType === 'Reference' && relationshipName !== null) {
53080
+ if (draftFieldValue === null) {
53081
+ recordFields[relationshipName] = {
53082
+ displayValue: null,
53083
+ value: null,
53084
+ };
52853
53085
  }
52854
- const key = getRecordKeyForId(luvio, draftFieldValue);
52855
- const referencedRecord = referencedRecords.get(key);
52856
- recordFields[relationshipName] = {
52857
- displayValue: null,
52858
- value: createLink$2(key),
52859
- };
52860
- // for custom objects, we select the 'Name' field
52861
- // otherwise we check the object info for name fields.
52862
- //if there are multiple we select 'Name' if it exists, otherwise the first one
52863
- if (referencedRecord !== undefined && referenceToInfos.length > 0) {
52864
- let nameField;
52865
- const referenceToInfo = referenceToInfos[0];
52866
- const nameFields = referenceToInfo.nameFields;
52867
- if (nameFields.length !== 0) {
52868
- nameField = nameFields.find((x) => x === 'Name');
52869
- if (nameField === undefined) {
52870
- nameField = nameFields[0];
52871
- }
53086
+ else {
53087
+ if (typeof draftFieldValue !== 'string') {
53088
+ throw Error('reference field value is not a string');
52872
53089
  }
52873
- if (nameField !== undefined) {
52874
- const nameFieldRef = referencedRecord.fields[nameField];
52875
- if (nameFieldRef) {
52876
- recordFields[relationshipName].displayValue =
52877
- (_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
53090
+ const key = getRecordKeyForId(luvio, draftFieldValue);
53091
+ const referencedRecord = referencedRecords.get(key);
53092
+ recordFields[relationshipName] = {
53093
+ displayValue: null,
53094
+ value: createLink$2(key),
53095
+ };
53096
+ // for custom objects, we select the 'Name' field
53097
+ // otherwise we check the object info for name fields.
53098
+ //if there are multiple we select 'Name' if it exists, otherwise the first one
53099
+ if (referencedRecord !== undefined && referenceToInfos.length > 0) {
53100
+ let nameField;
53101
+ const referenceToInfo = referenceToInfos[0];
53102
+ const nameFields = referenceToInfo.nameFields;
53103
+ if (nameFields.length !== 0) {
53104
+ nameField = nameFields.find((x) => x === 'Name');
53105
+ if (nameField === undefined) {
53106
+ nameField = nameFields[0];
53107
+ }
53108
+ }
53109
+ if (nameField !== undefined) {
53110
+ const nameFieldRef = referencedRecord.fields[nameField];
53111
+ if (nameFieldRef) {
53112
+ recordFields[relationshipName].displayValue =
53113
+ (_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
53114
+ }
52878
53115
  }
52879
53116
  }
52880
53117
  }
@@ -53167,17 +53404,8 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
53167
53404
  };
53168
53405
  for (const fieldName of keys$3(recordWithSpanningRefLinks.fields)) {
53169
53406
  const fieldKey = buildRecordFieldStoreKey(key, fieldName);
53170
- if (this.collectedFields[fieldKey] !== undefined) {
53171
- const fieldData = recordWithSpanningRefLinks.fields[fieldName];
53172
- normalizedRecord.fields[fieldName] = { __ref: fieldKey };
53173
- publishData(fieldKey, fieldData);
53174
- }
53175
- else if (recordWithSpanningRefLinks.fields[fieldName] &&
53176
- recordWithSpanningRefLinks.fields[fieldName].value &&
53177
- recordWithSpanningRefLinks.fields[fieldName].value.__ref !== undefined) {
53178
- normalizedRecord.fields[fieldName] = { __ref: fieldKey };
53179
- publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
53180
- }
53407
+ normalizedRecord.fields[fieldName] = { __ref: fieldKey };
53408
+ publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
53181
53409
  }
53182
53410
  // publish the normalized record
53183
53411
  publishData(key, normalizedRecord);
@@ -54097,7 +54325,7 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
54097
54325
  // Fulfilled snapshot (this only happens in this code path if
54098
54326
  // the error is network error or 504), otherwise we spread over
54099
54327
  // the non-eval'ed snapshot (which will be either Fulfilled or Stale)
54100
- return nonEvaluatedSnapshot.state === 'Error'
54328
+ const resultSnapshot = nonEvaluatedSnapshot.state === 'Error'
54101
54329
  ? createLocalEvalSnapshot(gqlResult, seenRecords, recordId, rebuildWithLocalEval)
54102
54330
  : {
54103
54331
  ...nonEvaluatedSnapshot,
@@ -54106,6 +54334,22 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
54106
54334
  seenRecords,
54107
54335
  rebuildWithLocalEval,
54108
54336
  };
54337
+ const { refresh, state } = resultSnapshot;
54338
+ if (state !== 'Error' && refresh) {
54339
+ // after refreshing a graphql snapshot, we want to force a rebuild regardless
54340
+ // of if the call failed or not or if the data changed or not because we want
54341
+ // to make sure any potential new drafts are picked up
54342
+ resultSnapshot.refresh = {
54343
+ ...refresh,
54344
+ resolve: (config) => {
54345
+ return refresh.resolve(config).finally(() => {
54346
+ luvio.storePublish(resultSnapshot.recordId, undefined);
54347
+ luvio.storeBroadcast();
54348
+ });
54349
+ },
54350
+ };
54351
+ }
54352
+ return resultSnapshot;
54109
54353
  };
54110
54354
  }
54111
54355
 
@@ -55159,7 +55403,10 @@ function isErrorResponse(response) {
55159
55403
  * @returns the merged record
55160
55404
  */
55161
55405
  function mergeAggregateUiResponse(response, mergeFunc) {
55162
- const { body } = response;
55406
+ if (response.ok === false) {
55407
+ return response;
55408
+ }
55409
+ const body = response.body;
55163
55410
  try {
55164
55411
  if (body === null ||
55165
55412
  body === undefined ||
@@ -56990,6 +57237,9 @@ class PrimingSession extends EventEmitter {
56990
57237
  this.ldsRecordRefresher = config.ldsRecordRefresher;
56991
57238
  this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
56992
57239
  this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
57240
+ if (this.useBatchGQL) {
57241
+ this.batchSize = this.batchSize / DEFAULT_GQL_QUERY_BATCH_SIZE;
57242
+ }
56993
57243
  this.conflictPool = new ConflictPool(config.store, this.objectInfoLoader);
56994
57244
  }
56995
57245
  // function that enqueues priming work
@@ -57846,7 +58096,7 @@ register({
57846
58096
  id: '@salesforce/lds-network-adapter',
57847
58097
  instrument: instrument$1,
57848
58098
  });
57849
- // version: 1.229.0-dev1-f69d054a9
58099
+ // version: 1.229.0-dev10-bc9ef2513
57850
58100
 
57851
58101
  const { create: create$2, keys: keys$2 } = Object;
57852
58102
  const { stringify: stringify$1, parse: parse$1 } = JSON;
@@ -71170,7 +71420,7 @@ function getFieldType$6(field) {
71170
71420
  }
71171
71421
  }
71172
71422
 
71173
- const { assign, create: create$1, freeze: freeze$1, keys: keys$1 } = Object;
71423
+ const { assign, create: create$1, freeze: freeze$1, isFrozen, keys: keys$1 } = Object;
71174
71424
  const { isArray: isArray$1 } = Array;
71175
71425
  const { concat, filter, includes, push, reduce } = Array.prototype;
71176
71426
 
@@ -72609,6 +72859,7 @@ function buildSelectionForField$7(source, reader, sel, variables, fragments, isC
72609
72859
  }
72610
72860
  if (fieldData === null) {
72611
72861
  reader.assignScalar(requestedFieldName, sink, fieldData);
72862
+ reader.exitPath();
72612
72863
  return sink;
72613
72864
  }
72614
72865
  const fieldType = getFieldType(sel);
@@ -72634,17 +72885,8 @@ function buildSelectionForField$7(source, reader, sel, variables, fragments, isC
72634
72885
  return sink;
72635
72886
  }
72636
72887
  function selectTypeLink(sel, fieldData, reader, key, sink, variables, fragments, version, selectFn, isCursorConnection) {
72637
- const resolvedLink = reader.read({
72638
- recordId: fieldData.__ref,
72639
- node: {
72640
- kind: 'Fragment',
72641
- private: [],
72642
- opaque: true,
72643
- version,
72644
- },
72645
- variables: {}
72646
- });
72647
- if (resolvedLink.data !== undefined) {
72888
+ const resolvedLink = resolveLink$1(reader, fieldData, version);
72889
+ if (resolvedLink && resolvedLink.data !== undefined) {
72648
72890
  if (isCursorConnection) {
72649
72891
  selectTypeLinkWithPagination(resolvedLink, sel, fieldData, reader, key, sink, variables, fragments, selectFn);
72650
72892
  }
@@ -73220,19 +73462,11 @@ function selectType$6(typename, sel, fieldData, reader, key, sink, variables, fr
73220
73462
  }
73221
73463
  case 'PolymorphicParentRelationship':
73222
73464
  case 'RecordRepresentation': {
73223
- const spanningFieldLink = reader.read({
73224
- recordId: fieldData.__ref,
73225
- node: {
73226
- kind: 'Fragment',
73227
- private: [],
73228
- opaque: true,
73229
- version: VERSION$f,
73230
- },
73231
- variables: {},
73232
- });
73233
- reader.markSeenId(fieldData.__ref);
73234
- const resolvedSpanningFieldValue = spanningFieldLink.data;
73465
+ const spanningFieldLink = resolveLink$1(reader, fieldData, VERSION$f);
73466
+ const resolvedSpanningFieldValue = spanningFieldLink && spanningFieldLink.data;
73467
+ const fieldDataRef = fieldData.__ref;
73235
73468
  if (resolvedSpanningFieldValue !== undefined) {
73469
+ reader.markSeenId(fieldDataRef);
73236
73470
  const { value: spanningFieldResult } = resolvedSpanningFieldValue;
73237
73471
  // Handle null values - graphql will return it at the field level, not return nested { value: null }
73238
73472
  if (spanningFieldResult === null || typeof spanningFieldResult !== 'object') {
@@ -73256,7 +73490,9 @@ function selectType$6(typename, sel, fieldData, reader, key, sink, variables, fr
73256
73490
  }
73257
73491
  }
73258
73492
  else {
73259
- reader.markMissingLink(fieldData.__ref);
73493
+ if (fieldDataRef !== undefined) {
73494
+ reader.markMissingLink(fieldDataRef);
73495
+ }
73260
73496
  reader.markMissing();
73261
73497
  }
73262
73498
  break;
@@ -76292,7 +76528,7 @@ register({
76292
76528
  configuration: { ...configurationForGraphQLAdapters },
76293
76529
  instrument,
76294
76530
  });
76295
- // version: 1.229.0-dev1-5b6d3db67
76531
+ // version: 1.229.0-dev10-abb060196
76296
76532
 
76297
76533
  // On core the unstable adapters are re-exported with different names,
76298
76534
 
@@ -78539,7 +78775,7 @@ withDefaultLuvio((luvio) => {
78539
78775
  unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
78540
78776
  graphQLImperative = ldsAdapter;
78541
78777
  });
78542
- // version: 1.229.0-dev1-5b6d3db67
78778
+ // version: 1.229.0-dev10-abb060196
78543
78779
 
78544
78780
  var gqlApi = /*#__PURE__*/Object.freeze({
78545
78781
  __proto__: null,
@@ -79253,4 +79489,4 @@ const { luvio } = getRuntime();
79253
79489
  setDefaultLuvio({ luvio });
79254
79490
 
79255
79491
  export { createPrimingSession, draftManager, draftQueue, executeAdapter, executeMutatingAdapter, getImperativeAdapterNames, invokeAdapter, invokeAdapterWithDraftToReplace, invokeAdapterWithMetadata, nimbusDraftQueue, registerReportObserver, setMetadataTTL, setUiApiRecordTTL, subscribeToAdapter };
79256
- // version: 1.229.0-dev1-f69d054a9
79492
+ // version: 1.229.0-dev10-bc9ef2513