@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.
@@ -31,12 +31,15 @@
31
31
  const { isArray: isArray$9 } = Array;
32
32
  const { push: push$5, indexOf, slice: slice$2 } = Array.prototype;
33
33
  const { parse: parse$a, stringify: stringify$a } = JSON;
34
+ const WeakSetCtor = WeakSet;
34
35
 
36
+ const deeplyFrozen = new WeakSetCtor();
35
37
  function deepFreeze(value) {
36
- // No need to freeze primitives
37
- if (typeof value !== 'object' || value === null) {
38
+ // No need to freeze primitives or already frozen stuff
39
+ if (typeof value !== 'object' || value === null || deeplyFrozen.has(value)) {
38
40
  return;
39
41
  }
42
+ deeplyFrozen.add(value);
40
43
  if (isArray$9(value)) {
41
44
  for (let i = 0, len = value.length; i < len; i += 1) {
42
45
  deepFreeze(value[i]);
@@ -1733,6 +1736,10 @@
1733
1736
  }
1734
1737
  }
1735
1738
  markVisited(canonicalKey) {
1739
+ if (typeof canonicalKey === 'string') {
1740
+ this.fallbackStringKeyInMemoryStore.markVisited(canonicalKey);
1741
+ return;
1742
+ }
1736
1743
  const { visitedIdsSet, reverseRedirectKeysMap } = this;
1737
1744
  let redirectKey = canonicalKey;
1738
1745
  // mark all redirects leading up to the canonical key as visited so
@@ -2044,7 +2051,7 @@
2044
2051
  if (isStoreRecordError$1(linked)) {
2045
2052
  return new GraphNodeError(this.store, linked);
2046
2053
  }
2047
- return new GraphNode(this.store, linked);
2054
+ return new GraphNode(this.store, linked, __ref);
2048
2055
  }
2049
2056
  linkData() {
2050
2057
  return this.data.data;
@@ -2054,10 +2061,11 @@
2054
2061
  }
2055
2062
  }
2056
2063
  class GraphNode {
2057
- constructor(store, data) {
2064
+ constructor(store, data, storeKey) {
2058
2065
  this.type = GraphNodeType$1.Node;
2059
2066
  this.store = store;
2060
2067
  this.data = data;
2068
+ this.storeKey = storeKey;
2061
2069
  }
2062
2070
  object(propertyName) {
2063
2071
  const value = this.data[propertyName];
@@ -2067,7 +2075,8 @@
2067
2075
  if (typeof value !== 'object' || value === null) {
2068
2076
  throw new Error(`Cannot walk to path ${String(propertyName)}. "${String(propertyName)}" is a scalar: "${value}"`);
2069
2077
  }
2070
- return new GraphNode(this.store, value);
2078
+ // We're walking to an object property on the current store record, pass the storeKey down.
2079
+ return new GraphNode(this.store, value, this.storeKey);
2071
2080
  }
2072
2081
  link(propertyName) {
2073
2082
  const value = this.data[propertyName];
@@ -2097,6 +2106,8 @@
2097
2106
  }
2098
2107
  write(propertyName, value) {
2099
2108
  this.data[propertyName] = value;
2109
+ const canonicalKey = this.store.getCanonicalRecordId(this.storeKey);
2110
+ this.store.markVisited(canonicalKey);
2100
2111
  }
2101
2112
  isUndefined(propertyName) {
2102
2113
  return this.data[propertyName] === undefined;
@@ -2281,6 +2292,34 @@
2281
2292
  const FRAGMENT_READ_RESULT_MISSING = {
2282
2293
  state: FragmentReadResultState$1.Missing,
2283
2294
  };
2295
+ function resolveLink$1(reader, storeLink, version) {
2296
+ const { StoreLinkStateValues } = reader;
2297
+ const linkState = reader.getLinkState(storeLink);
2298
+ switch (linkState.state) {
2299
+ case StoreLinkStateValues.RefNotPresent:
2300
+ case StoreLinkStateValues.NotPresent:
2301
+ case StoreLinkStateValues.Missing:
2302
+ reader.markMissingLink(storeLink.__ref);
2303
+ reader.markMissing();
2304
+ return;
2305
+ case StoreLinkStateValues.Pending:
2306
+ reader.markPending();
2307
+ return;
2308
+ case StoreLinkStateValues.Null:
2309
+ return;
2310
+ }
2311
+ const { key: __ref } = linkState;
2312
+ return reader.read({
2313
+ recordId: __ref,
2314
+ node: {
2315
+ kind: 'Fragment',
2316
+ private: [],
2317
+ opaque: true,
2318
+ version,
2319
+ },
2320
+ variables: {},
2321
+ });
2322
+ }
2284
2323
  class Reader {
2285
2324
  constructor(store, variables, refresh, baseSnapshot, ttlStrategy) {
2286
2325
  this.store = store;
@@ -3231,9 +3270,9 @@
3231
3270
  if (value === undefined) {
3232
3271
  return null;
3233
3272
  }
3234
- return this.wrapNormalizedGraphNode(value, store);
3273
+ return this.wrapNormalizedGraphNode(value, key, store);
3235
3274
  }
3236
- wrapNormalizedGraphNode(normalized, storeOverride) {
3275
+ wrapNormalizedGraphNode(normalized, key, storeOverride) {
3237
3276
  if (normalized === null) {
3238
3277
  return null;
3239
3278
  }
@@ -3241,7 +3280,7 @@
3241
3280
  if (isStoreRecordError$1(normalized)) {
3242
3281
  return new GraphNodeError(store, normalized);
3243
3282
  }
3244
- return new GraphNode(store, normalized);
3283
+ return new GraphNode(store, normalized, key);
3245
3284
  }
3246
3285
  withContext(adapter, options) {
3247
3286
  const { contextId, onContextLoaded } = options;
@@ -3536,8 +3575,8 @@
3536
3575
  getNode(key) {
3537
3576
  return this.environment.getNode(key);
3538
3577
  }
3539
- wrapNormalizedGraphNode(normalized) {
3540
- return this.environment.wrapNormalizedGraphNode(normalized);
3578
+ wrapNormalizedGraphNode(normalized, key) {
3579
+ return this.environment.wrapNormalizedGraphNode(normalized, key);
3541
3580
  }
3542
3581
  instrument(paramsBuilder) {
3543
3582
  const { instrument } = this.options;
@@ -3847,7 +3886,7 @@
3847
3886
  }
3848
3887
  return resourceParams;
3849
3888
  }
3850
- // engine version: 0.145.2-6a13677c
3889
+ // engine version: 0.146.0-dev5-a2ec6e3f
3851
3890
 
3852
3891
  /**
3853
3892
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -3974,7 +4013,7 @@
3974
4013
  }
3975
4014
  callbacks.push(callback);
3976
4015
  }
3977
- // version: 1.229.0-dev1-f69d054a9
4016
+ // version: 1.229.0-dev10-bc9ef2513
3978
4017
 
3979
4018
  // TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
3980
4019
  function instrumentAdapter$1(createFunction, _metadata) {
@@ -15435,7 +15474,7 @@
15435
15474
  }
15436
15475
  return superResult;
15437
15476
  }
15438
- // version: 1.229.0-dev1-f69d054a9
15477
+ // version: 1.229.0-dev10-bc9ef2513
15439
15478
 
15440
15479
  function unwrap(data) {
15441
15480
  // The lwc-luvio bindings import a function from lwc called "unwrap".
@@ -15536,14 +15575,15 @@
15536
15575
  return undefined;
15537
15576
  });
15538
15577
  }
15539
- const { isArray: isArray$8 } = Array;
15540
- const { stringify: stringify$9 } = JSON;
15541
15578
 
15542
15579
  function isPromise$1(value) {
15543
15580
  // check for Thenable due to test frameworks using custom Promise impls
15544
15581
  return value.then !== undefined;
15545
15582
  }
15546
15583
 
15584
+ const { isArray: isArray$8 } = Array;
15585
+ const { stringify: stringify$9 } = JSON;
15586
+
15547
15587
  /**
15548
15588
  * (Re)throws an error after adding a prefix to the message.
15549
15589
  *
@@ -16358,7 +16398,7 @@
16358
16398
  const { apiFamily, name } = metadata;
16359
16399
  return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
16360
16400
  }
16361
- // version: 1.229.0-dev1-f69d054a9
16401
+ // version: 1.229.0-dev10-bc9ef2513
16362
16402
 
16363
16403
  /**
16364
16404
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -16457,11 +16497,11 @@
16457
16497
  TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
16458
16498
  TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
16459
16499
  })(TypeCheckShapes || (TypeCheckShapes = {}));
16460
- // engine version: 0.145.2-6a13677c
16500
+ // engine version: 0.146.0-dev5-a2ec6e3f
16461
16501
 
16462
16502
  const { keys: ObjectKeys$3, create: ObjectCreate$3 } = Object;
16463
16503
 
16464
- const { assign: assign$9, create: create$9, freeze: freeze$4, keys: keys$b } = Object;
16504
+ const { assign: assign$9, create: create$9, freeze: freeze$4, isFrozen: isFrozen$2, keys: keys$b } = Object;
16465
16505
 
16466
16506
  ObjectCreate$3(null);
16467
16507
 
@@ -16831,7 +16871,7 @@
16831
16871
  }
16832
16872
  const keyPrefix$1 = 'UiApi';
16833
16873
 
16834
- const { assign: assign$8, create: create$8, freeze: freeze$3, keys: keys$a } = Object;
16874
+ const { assign: assign$8, create: create$8, freeze: freeze$3, isFrozen: isFrozen$1, keys: keys$a } = Object;
16835
16875
  const { hasOwnProperty: hasOwnProperty$1 } = Object.prototype;
16836
16876
  const { split, endsWith } = String.prototype;
16837
16877
  const { isArray: isArray$7 } = Array;
@@ -19800,7 +19840,7 @@
19800
19840
  extractTrackedFieldsToTrie(spanningLink.data.__ref, spanning, next, config, spanningVisitedRecordIds, depth + 1);
19801
19841
  // For a spanning record that is detected to be a circular reference, we add the field along with Id and Name.
19802
19842
  // It's possible for spanning record lookup fields to sometimes be circular, and sometimes not - depending on the value of the lookup field.
19803
- // For more information on scenarios that caused this fix: https://salesforce.quip.com/OvzNAh3eNIWY
19843
+ // For more information on scenarios that caused this fix: search "LDS Recursive Spanning Fields Problem" in Quip
19804
19844
  if (keys$a(next.children).length === 0) {
19805
19845
  addScalarFieldId(next);
19806
19846
  addScalarFieldName(next);
@@ -19953,7 +19993,11 @@
19953
19993
  }
19954
19994
  const link = fieldValueRepresentation.link(fieldName);
19955
19995
  const resolved = link.follow();
19956
- if (isGraphNode(resolved) && resolved.isScalar('value') && path.length > 0) {
19996
+ if (isGraphNode(resolved) &&
19997
+ resolved.isScalar('value') &&
19998
+ path.length > 0 &&
19999
+ // TODO [W-14082782]: temporary fix
20000
+ !isFrozen$1(link.data)) {
19957
20001
  const linkState = link.linkData();
19958
20002
  const fields = linkState === undefined ? [] : linkState.fields;
19959
20003
  link.writeLinkData({
@@ -19981,22 +20025,12 @@
19981
20025
  const fieldValueRepresentation = record.object('fields');
19982
20026
  const fieldName = path.shift();
19983
20027
  if (fieldValueRepresentation.isUndefined(fieldName) === true) {
19984
- // TODO [W-6900046]: remove cast, make RecordRepresentationNormalized['fields'] accept
19985
- // an undefined/non-present __ref if isMissing is present
19986
- fieldValueRepresentation.write(fieldName, {
19987
- __ref: undefined,
19988
- isMissing: true,
19989
- });
20028
+ writeMissingFieldToStore(fieldValueRepresentation, fieldName);
19990
20029
  return;
19991
20030
  }
19992
20031
  const link = fieldValueRepresentation.link(fieldName);
19993
20032
  if (link.isPending()) {
19994
- // TODO [W-6900046]: remove cast, make RecordRepresentationNormalized['fields'] accept
19995
- // an undefined/non-present __ref if isMissing is present
19996
- fieldValueRepresentation.write(fieldName, {
19997
- __ref: undefined,
19998
- isMissing: true,
19999
- });
20033
+ writeMissingFieldToStore(fieldValueRepresentation, fieldName);
20000
20034
  }
20001
20035
  else if (path.length > 0 && link.isMissing() === false) {
20002
20036
  const fieldValue = link.follow();
@@ -20012,6 +20046,19 @@
20012
20046
  }
20013
20047
  }
20014
20048
  }
20049
+ /**
20050
+ * Graph Node Directly modifies store entries, which is generally a non-starter.
20051
+ * Until we can refactor this mess, you need to use this function to safely mark the RecordRepresentation
20052
+ * as a seenId in the store when you perform this mutation.
20053
+ */
20054
+ function writeMissingFieldToStore(field, fieldName) {
20055
+ // TODO [W-6900046]: remove cast, make RecordRepresentationNormalized['fields'] accept
20056
+ // an undefined/non-present __ref if isMissing is present
20057
+ field.write(fieldName, {
20058
+ __ref: undefined,
20059
+ isMissing: true,
20060
+ });
20061
+ }
20015
20062
  /**
20016
20063
  * Tells you if an objectApiName is supported by UI API or not.
20017
20064
  * Note: Luvio does not currently support all the entities, the list is limited to UI API supported entities
@@ -20143,8 +20190,11 @@
20143
20190
  return existing;
20144
20191
  }
20145
20192
  function mergeRecordConflict(luvio, incoming, existing, recordConflictMap) {
20146
- const incomingNode = luvio.wrapNormalizedGraphNode(incoming);
20147
- const existingNode = luvio.wrapNormalizedGraphNode(existing);
20193
+ const recordKey = keyBuilder$1U(luvio, {
20194
+ recordId: incoming.id,
20195
+ });
20196
+ const incomingNode = luvio.wrapNormalizedGraphNode(incoming, recordKey);
20197
+ const existingNode = luvio.wrapNormalizedGraphNode(existing, recordKey);
20148
20198
  const incomingTrackedFieldsTrieRoot = {
20149
20199
  name: incoming.apiName,
20150
20200
  children: {},
@@ -20153,9 +20203,6 @@
20153
20203
  name: existing.apiName,
20154
20204
  children: {},
20155
20205
  };
20156
- const recordKey = keyBuilder$1U(luvio, {
20157
- recordId: incoming.id,
20158
- });
20159
20206
  const trackedFieldsConfig = {
20160
20207
  maxDepth: configurationForRestAdapters$1.getTrackedFieldDepthOnCacheMergeConflict(),
20161
20208
  onlyFetchLeafNodeIdAndName: configurationForRestAdapters$1.getTrackedFieldLeafNodeIdAndNameOnly(),
@@ -24866,7 +24913,7 @@
24866
24913
  // Temp fix until we can mimic the server behavior for non-layoutable entities.
24867
24914
  let layoutMap = {};
24868
24915
  if (hasOwnProperty$1.call(layouts, apiName)) {
24869
- layoutMap = layouts[apiName][recordTypeId];
24916
+ layoutMap = layouts[apiName][recordTypeId] || {};
24870
24917
  }
24871
24918
  return {
24872
24919
  layoutMap,
@@ -25044,18 +25091,28 @@
25044
25091
  * These are intermediate lookups to check if the record is in the L2 cache
25045
25092
  * @param {Luvio} luvio
25046
25093
  * @param {GetRecordLayoutTypeConfig} config
25047
- * @param {BuildCachedSnapshot<BuildSnapshotContext} cachedSnapshot
25094
+ * @param {BuildCachedSnapshot<BuildSnapshotContext>} cachedSnapshot
25048
25095
  */
25049
25096
  function makeCacheOnlySnapshot(luvio, config, adapterContext, cachedSnapshot) {
25050
- return luvio.applyCachePolicy({
25051
- cachePolicy: {
25052
- // only looking in the cache so we can check for L2 data offline
25053
- type: 'only-if-cached',
25054
- },
25055
- }, { config, luvio, adapterContext }, cachedSnapshot,
25056
- // this won't be invoked since we're requesting only-if-cached
25097
+ return luvio.applyCachePolicy(
25098
+ // Pass empty context so environment will use its default cache-policy
25099
+ {}, { config, luvio, adapterContext }, cachedSnapshot,
25100
+ // disallow hitting the network by returning a gateway timeout
25057
25101
  () => {
25058
- throw Error('buildNetworkSnapshot should not be called for only-if-cached policy');
25102
+ return new Promise((resolve) => {
25103
+ resolve({
25104
+ state: 'Error',
25105
+ data: undefined,
25106
+ error: {
25107
+ body: undefined,
25108
+ headers: {},
25109
+ ok: false,
25110
+ status: 504,
25111
+ statusText: 'Gateway Timeout',
25112
+ errorType: 'fetchResponse',
25113
+ },
25114
+ });
25115
+ });
25059
25116
  });
25060
25117
  }
25061
25118
  /**
@@ -25266,7 +25323,7 @@
25266
25323
  const responsePromises = [];
25267
25324
  for (let i = 0, len = entries.length; i < len; i++) {
25268
25325
  const { key, record } = entries[i];
25269
- const node = luvio.wrapNormalizedGraphNode(record);
25326
+ const node = luvio.wrapNormalizedGraphNode(record, key);
25270
25327
  const optionalFields = getTrackedFields(key, node, {
25271
25328
  maxDepth: configurationForRestAdapters$1.getTrackedFieldDepthOnNotifyChange(),
25272
25329
  onlyFetchLeafNodeIdAndName: configurationForRestAdapters$1.getTrackedFieldLeafNodeIdAndNameOnly(),
@@ -41164,7 +41221,16 @@
41164
41221
  throttle(60, 60000, createLDSAdapter(luvio, 'notifyListInfoUpdateAvailable', notifyUpdateAvailableFactory$1));
41165
41222
  throttle(60, 60000, createLDSAdapter(luvio, 'notifyQuickActionDefaultsUpdateAvailable', notifyUpdateAvailableFactory));
41166
41223
  });
41167
- // version: 1.229.0-dev1-5b6d3db67
41224
+ // version: 1.229.0-dev10-abb060196
41225
+
41226
+ var ldsIdempotencyWriteDisabled = {
41227
+ isOpen: function (e) {
41228
+ return e.fallback;
41229
+ },
41230
+ hasError: function () {
41231
+ return !0;
41232
+ },
41233
+ };
41168
41234
 
41169
41235
  var caseSensitiveUserId = '005B0000000GR4OIAW';
41170
41236
 
@@ -41795,6 +41861,9 @@
41795
41861
  }
41796
41862
 
41797
41863
  function isStoreEntryError(storeRecord) {
41864
+ if (!storeRecord || typeof storeRecord !== 'object') {
41865
+ return false;
41866
+ }
41798
41867
  return storeRecord.__type === 'error';
41799
41868
  }
41800
41869
 
@@ -42379,12 +42448,12 @@
42379
42448
  }
42380
42449
  return environment.getNode(key, ingestStagingStore);
42381
42450
  };
42382
- const wrapNormalizedGraphNode = function (normalized) {
42451
+ const wrapNormalizedGraphNode = function (normalized, key) {
42383
42452
  validateNotDisposed();
42384
42453
  if (ingestStagingStore === null) {
42385
42454
  ingestStagingStore = buildIngestStagingStore(environment);
42386
42455
  }
42387
- return environment.wrapNormalizedGraphNode(normalized, ingestStagingStore);
42456
+ return environment.wrapNormalizedGraphNode(normalized, key, ingestStagingStore);
42388
42457
  };
42389
42458
  const rebuildSnapshot = function (snapshot, onRebuild) {
42390
42459
  validateNotDisposed();
@@ -44023,6 +44092,7 @@
44023
44092
  'Email',
44024
44093
  'TextArea',
44025
44094
  'Percent',
44095
+ 'EncryptedString',
44026
44096
  ].includes(type);
44027
44097
  }
44028
44098
 
@@ -45503,8 +45573,12 @@
45503
45573
  // If there is no metadata for this query or it somehow lacks a timestamp
45504
45574
  // skip setting the root timestamp
45505
45575
  if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
45506
- // subtract 10ms from timestamp to account for ingestion processing time
45507
- input.rootTimestamp = queryMetadata.ingestionTimestamp - 10;
45576
+ const timestamp = Number(queryMetadata.ingestionTimestamp);
45577
+ if (!isNaN(timestamp)) {
45578
+ // adjust the timestamp to account for ingestion processing time
45579
+ // 30s is used because this is the default record TTL
45580
+ input.rootTimestamp = timestamp - 30000;
45581
+ }
45508
45582
  }
45509
45583
  }
45510
45584
  return recordQuery(selection, alias, apiName, [], input);
@@ -46317,8 +46391,12 @@
46317
46391
 
46318
46392
  const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
46319
46393
  const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
46394
+ const ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED = 'IDEMPOTENCY_FEATURE_NOT_ENABLED';
46395
+ const ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED = 'IDEMPOTENCY_NOT_SUPPORTED';
46320
46396
  const ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER = 'IDEMPOTENCY_KEY_USED_DIFFERENT_USER';
46321
46397
  const ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST = 'IDEMPOTENCY_CONCURRENT_REQUEST';
46398
+ const ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED = 'IDEMPOTENCY_KEY_ALREADY_USED';
46399
+ const ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR = 'IDEMPOTENCY_BACKEND_OPERATION_ERROR';
46322
46400
  /**
46323
46401
  * Get the retry after in milliseconds from the response headers, undefined if not specified.
46324
46402
  * The header could have two different format.
@@ -46348,7 +46426,9 @@
46348
46426
  const dispatchResourceRequest = async function (resourceRequest, _context) {
46349
46427
  const resourceRequestCopy = clone$1(resourceRequest);
46350
46428
  resourceRequestCopy.headers = resourceRequestCopy.headers || {};
46351
- resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
46429
+ if (handler.hasIdempotencySupport()) {
46430
+ resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
46431
+ }
46352
46432
  // enable return extra fields for record creation and record update http call
46353
46433
  if (resourceRequest.basePath === '/ui-api/records' &&
46354
46434
  (resourceRequest.method === 'post' || resourceRequest.method === 'patch')) {
@@ -47170,6 +47250,12 @@
47170
47250
  // the luvio store redirect table, during which a new draft might be enqueued
47171
47251
  // which would not see a necessary mapping.
47172
47252
  this.ephemeralRedirects = {};
47253
+ // determined by Server setup.
47254
+ this.isIdempotencySupported = true;
47255
+ // idempotency write flag set by lds
47256
+ this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
47257
+ fallback: false,
47258
+ });
47173
47259
  }
47174
47260
  enqueue(data) {
47175
47261
  return this.draftQueue.enqueue(this.handlerId, data);
@@ -47199,21 +47285,43 @@
47199
47285
  retryDelayInMs = getRetryAfterInMs(response.headers);
47200
47286
  shouldRetry = true;
47201
47287
  break;
47202
- case HttpStatusCode$1.ServerError:
47288
+ case HttpStatusCode$1.ServerError: {
47203
47289
  shouldRetry = true;
47290
+ if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR)) {
47291
+ this.isIdempotencySupported = false;
47292
+ retryDelayInMs = 0;
47293
+ actionDataChanged = true;
47294
+ }
47204
47295
  break;
47296
+ }
47205
47297
  case 409 /* IdempotentWriteSpecificHttpStatusCode.Conflict */: {
47206
- const errorCode = response.body[0].errorCode;
47207
- if (errorCode === ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER) {
47208
- updatedAction.data.headers = updatedAction.data.headers || {};
47209
- updatedAction.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
47298
+ if (this.isUiApiErrors(response.body)) {
47299
+ const errorCode = response.body[0].errorCode;
47300
+ if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER)) {
47301
+ retryDelayInMs = 0;
47302
+ actionDataChanged = true;
47303
+ }
47304
+ else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
47305
+ retryDelayInMs = getRetryAfterInMs(response.headers);
47306
+ }
47307
+ shouldRetry = true;
47308
+ }
47309
+ break;
47310
+ }
47311
+ case HttpStatusCode$1.BadRequest: {
47312
+ if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED, ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED)) {
47210
47313
  retryDelayInMs = 0;
47211
47314
  actionDataChanged = true;
47315
+ shouldRetry = true;
47212
47316
  }
47213
- else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
47214
- retryDelayInMs = getRetryAfterInMs(response.headers);
47317
+ break;
47318
+ }
47319
+ case 422 /* IdempotentWriteSpecificHttpStatusCode.UnProcessableEntity */: {
47320
+ if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED)) {
47321
+ retryDelayInMs = 0;
47322
+ actionDataChanged = true;
47323
+ shouldRetry = true;
47215
47324
  }
47216
- shouldRetry = true;
47217
47325
  break;
47218
47326
  }
47219
47327
  }
@@ -47232,6 +47340,27 @@
47232
47340
  return ProcessActionResult.NETWORK_ERROR;
47233
47341
  }
47234
47342
  }
47343
+ // 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.
47344
+ handleIdempotencyServerError(responseBody, action, updateIdempotencyKey, ...targetErrorCodes) {
47345
+ if (this.isUiApiErrors(responseBody)) {
47346
+ const errorCode = responseBody[0].errorCode;
47347
+ if (targetErrorCodes.includes(errorCode)) {
47348
+ action.data.headers = action.data.headers || {};
47349
+ if (updateIdempotencyKey) {
47350
+ action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
47351
+ }
47352
+ else {
47353
+ delete action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY];
47354
+ }
47355
+ return true;
47356
+ }
47357
+ }
47358
+ return false;
47359
+ }
47360
+ // 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.
47361
+ isUiApiErrors(body) {
47362
+ return body !== undefined && Array.isArray(body) && body.length > 0 && body[0].errorCode;
47363
+ }
47235
47364
  async buildPendingAction(request, queue) {
47236
47365
  const targetId = await this.getIdFromRequest(request);
47237
47366
  if (targetId === undefined) {
@@ -47445,6 +47574,10 @@
47445
47574
  ...targetData,
47446
47575
  body: this.mergeRequestBody(targetBody, sourceBody),
47447
47576
  };
47577
+ // Updates Idempotency key if target has one
47578
+ if (targetData.headers && targetData.headers[HTTP_HEADER_IDEMPOTENCY_KEY]) {
47579
+ merged.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
47580
+ }
47448
47581
  // overlay metadata
47449
47582
  merged.metadata = { ...targetMetadata, ...sourceMetadata };
47450
47583
  // put status back to pending to auto upload if queue is active and targed is at the head.
@@ -47481,6 +47614,9 @@
47481
47614
  getDraftIdsFromAction(action) {
47482
47615
  return [action.targetId];
47483
47616
  }
47617
+ hasIdempotencySupport() {
47618
+ return this.isIdempotencySupported && !this.isLdsIdempotencyWriteDisabled;
47619
+ }
47484
47620
  async ingestResponses(responses, action) {
47485
47621
  const luvio = this.getLuvio();
47486
47622
  await luvio.handleSuccessResponse(() => {
@@ -49286,6 +49422,10 @@
49286
49422
  return node.kind === 'OperationDefinition';
49287
49423
  }
49288
49424
 
49425
+ const POLYMORPHIC_PARENT_RELATIONSHIP = 'polymorphicParentRelationship';
49426
+ const PARENT_RELATIONSHIP = 'parentRelationship';
49427
+ const CHILD_RELATIONSHIP = 'childRelationship';
49428
+ const RECORD_QUERY = 'recordQuery';
49289
49429
  function requestsDraftsField(recordFieldNode) {
49290
49430
  if (!recordFieldNode.selectionSet)
49291
49431
  return false;
@@ -49301,18 +49441,41 @@
49301
49441
  directive.arguments
49302
49442
  .map((argument) => argument.value)
49303
49443
  .filter(isStringValueNode)
49304
- .some((categoryName) => categoryName.value === 'recordQuery'));
49444
+ .some((categoryName) => categoryName.value === RECORD_QUERY));
49305
49445
  });
49306
49446
  }
49307
49447
  return false;
49308
49448
  }
49309
- // finds field with 'recordQuery' and 'childRelationship' directive
49310
- function findNearestRecordQuery(ancestors) {
49311
- const recordQueryAncester = findNearestAncesterPath(ancestors, true).node;
49312
- return recordQueryAncester === undefined ? undefined : recordQueryAncester;
49449
+ // finds connection field with 'recordQuery' and 'childRelationship' directive.
49450
+ function findNearestConnection(ancestors) {
49451
+ const connectionAncestor = findNearestAncesterPath(ancestors, true).node;
49452
+ return connectionAncestor === undefined ? undefined : connectionAncestor;
49453
+ }
49454
+ // convinient method to find nearest connection with its path
49455
+ function findNearestConnectionWithPath(ancestors) {
49456
+ const closestAncestorPath = findNearestAncesterPath(ancestors, true);
49457
+ let connection = undefined;
49458
+ let connectionPath = undefined;
49459
+ if (closestAncestorPath.parentIndex > 0) {
49460
+ const connectionAncestor = closestAncestorPath.node;
49461
+ const connectionAncestors = ancestors.slice(0, closestAncestorPath.parentIndex);
49462
+ connection =
49463
+ connectionAncestor === undefined ? undefined : connectionAncestor;
49464
+ if (connection !== undefined) {
49465
+ const ancesterPath = findAncesterPath(connectionAncestors);
49466
+ connectionPath =
49467
+ ancesterPath === ''
49468
+ ? connection.name.value
49469
+ : `${ancesterPath}#${connection.name.value}`;
49470
+ }
49471
+ }
49472
+ return {
49473
+ connection,
49474
+ path: connectionPath,
49475
+ };
49313
49476
  }
49314
- // 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.
49315
- function findNearestAncesterPath(ancestors, recordQueryOnly) {
49477
+ // 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.
49478
+ function findNearestAncesterPath(ancestors, connectionOnly) {
49316
49479
  let recordQueryPath = { node: undefined, parentIndex: -1 };
49317
49480
  let relationship = '';
49318
49481
  for (let i = ancestors.length - 1; i >= 0; i--) {
@@ -49326,9 +49489,11 @@
49326
49489
  continue;
49327
49490
  for (let arg of directive.arguments) {
49328
49491
  if (arg.value &&
49329
- (arg.value.value === 'recordQuery' ||
49330
- arg.value.value === 'childRelationship' ||
49331
- (!recordQueryOnly && arg.value.value === 'parentRelationship'))) {
49492
+ (arg.value.value === RECORD_QUERY ||
49493
+ arg.value.value === CHILD_RELATIONSHIP ||
49494
+ (!connectionOnly &&
49495
+ (arg.value.value === PARENT_RELATIONSHIP ||
49496
+ arg.value.value === POLYMORPHIC_PARENT_RELATIONSHIP)))) {
49332
49497
  recordQueryPath = { node: node, parentIndex: i };
49333
49498
  relationship = arg.value.value;
49334
49499
  break;
@@ -49343,17 +49508,19 @@
49343
49508
  //checks if nearest ancester could be an inline fragment
49344
49509
  if (recordQueryPath.node !== undefined &&
49345
49510
  recordQueryPath.node.selectionSet &&
49346
- relationship === 'parentRelationship') {
49347
- //
49348
- if (recordQueryPath.node.selectionSet.selections.every(isInlineFragmentNode)) {
49349
- //
49350
- const inlineFragmentLoc = recordQueryPath.parentIndex + 2;
49351
- if (inlineFragmentLoc < ancestors.length && ancestors[inlineFragmentLoc]) {
49511
+ (relationship === PARENT_RELATIONSHIP || relationship === POLYMORPHIC_PARENT_RELATIONSHIP)) {
49512
+ // InlineFragment is usually 3 steps aways from its FieldNode parent within ancester hierarchy if it exists. The below search
49513
+ // is applied to adapt to future AST structure change
49514
+ let parentIndex = recordQueryPath.parentIndex + 1;
49515
+ while (parentIndex < ancestors.length) {
49516
+ if (isInlineFragmentNode(ancestors[parentIndex])) {
49352
49517
  recordQueryPath = {
49353
- node: ancestors[inlineFragmentLoc],
49354
- parentIndex: inlineFragmentLoc,
49518
+ node: ancestors[parentIndex],
49519
+ parentIndex,
49355
49520
  };
49521
+ break;
49356
49522
  }
49523
+ parentIndex++;
49357
49524
  }
49358
49525
  }
49359
49526
  return recordQueryPath;
@@ -49377,7 +49544,7 @@
49377
49544
  ? sectionPath
49378
49545
  : sectionPath === ''
49379
49546
  ? path
49380
- : `${sectionPath}_${path}`;
49547
+ : `${sectionPath}#${path}`;
49381
49548
  }
49382
49549
  }
49383
49550
  boundaryIndex = parentIndex;
@@ -49435,9 +49602,9 @@
49435
49602
  const relationships = args
49436
49603
  .map((arg) => arg.value)
49437
49604
  .filter(isStringValueNode)
49438
- .filter((valueNode) => valueNode.value === 'childRelationship' ||
49439
- valueNode.value === 'parentRelationship' ||
49440
- valueNode.value === 'polymorphicParentRelationship')
49605
+ .filter((valueNode) => valueNode.value === CHILD_RELATIONSHIP ||
49606
+ valueNode.value === PARENT_RELATIONSHIP ||
49607
+ valueNode.value === POLYMORPHIC_PARENT_RELATIONSHIP)
49441
49608
  .map((relationshipNode) => relationshipNode.value);
49442
49609
  if (relationships.length > 0) {
49443
49610
  return relationships[0];
@@ -49494,8 +49661,8 @@
49494
49661
  */
49495
49662
  function isParentRelationship(node) {
49496
49663
  return (node &&
49497
- (isRelationship(node, 'parentRelationship') ||
49498
- isRelationship(node, 'polymorphicParentRelationship')));
49664
+ (isRelationship(node, PARENT_RELATIONSHIP) ||
49665
+ isRelationship(node, POLYMORPHIC_PARENT_RELATIONSHIP)));
49499
49666
  }
49500
49667
  /*
49501
49668
  checks if the InlineFragment spans
@@ -49801,6 +49968,26 @@
49801
49968
  return values$1(objectInfo.fields).find((field) => field.apiName === fieldName ||
49802
49969
  (field.dataType === 'Reference' && field.relationshipName === fieldName));
49803
49970
  }
49971
+ async function readIngestionTimestampForKey(key, query) {
49972
+ let ingestionTimestamp = 0;
49973
+ const sql = `
49974
+ SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
49975
+ FROM lds_data
49976
+ WHERE key IS ?
49977
+ `;
49978
+ const results = await query(sql, [key]);
49979
+ const [timestamp] = results.rows.map((row) => row[0]);
49980
+ if (timestamp !== null) {
49981
+ const numericalTimestamp = Number(timestamp);
49982
+ if (isNaN(numericalTimestamp)) {
49983
+ return ingestionTimestamp;
49984
+ }
49985
+ // adjust the timestamp to account for ingestion processing time
49986
+ // 30s is used because this is the default record TTL
49987
+ ingestionTimestamp = numericalTimestamp - 30000;
49988
+ }
49989
+ return ingestionTimestamp;
49990
+ }
49804
49991
 
49805
49992
  function findSpanningField(name) {
49806
49993
  return (field) => {
@@ -50320,17 +50507,7 @@
50320
50507
  const key = buildKeyStringForRecordQuery(operation,
50321
50508
  // join varables passed from query to the argument variables given from the AST
50322
50509
  { ...variableValues, ...args }, info.fieldNodes[0].arguments, apiName);
50323
- const sql = `
50324
- SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}')
50325
- FROM lds_data
50326
- WHERE key IS ?
50327
- `;
50328
- const results = await query(sql, [key]);
50329
- const [timestamp] = results.rows.map((row) => row[0]);
50330
- if (timestamp !== null && typeof timestamp === 'number') {
50331
- //go back 10 ms to adjust for margin of error when top level query is stored and when raml objects are stored
50332
- ingestionTimestamp = timestamp - 10;
50333
- }
50510
+ return readIngestionTimestampForKey(key, query);
50334
50511
  }
50335
50512
  return ingestionTimestamp;
50336
50513
  }
@@ -50378,26 +50555,20 @@
50378
50555
  let recordConnections = ``;
50379
50556
  const polymorphicFieldTypeNames = new Set();
50380
50557
  let typedScalars = new Set();
50558
+ let parentRelationshipFields = new Set();
50381
50559
  for (const objectInfo of values$1(objectInfos)) {
50382
50560
  const { apiName, childRelationships } = objectInfo;
50383
50561
  let fields = ``;
50384
50562
  typedScalars.add(`${apiName}_Filter`);
50385
50563
  typedScalars.add(`${apiName}_OrderBy`);
50386
- for (const childRelationship of childRelationships) {
50387
- const { childObjectApiName } = childRelationship;
50388
- // Only add the relationship if there is relevant objectinfos for it,
50389
- // otherwise we'd be defining types we cannot satisfy and aren't referenced in
50390
- // the query.
50391
- if (objectInfos[childObjectApiName] !== undefined) {
50392
- fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
50393
- typedScalars.add(`${childObjectApiName}_Filter`);
50394
- typedScalars.add(`${childObjectApiName}_OrderBy`);
50395
- }
50396
- }
50397
50564
  for (const field of values$1(objectInfo.fields)) {
50398
50565
  if (!fieldsStaticallyAdded.includes(field.apiName)) {
50399
50566
  fields += `${field.apiName}: ${dataTypeToType(field.dataType, field.apiName)}\n`;
50400
50567
  }
50568
+ //handles parent relationship
50569
+ if (field.relationshipName === null) {
50570
+ continue;
50571
+ }
50401
50572
  // For spanning parent relationships with no union types
50402
50573
  if (field.referenceToInfos.length === 1) {
50403
50574
  const [relation] = field.referenceToInfos;
@@ -50405,11 +50576,13 @@
50405
50576
  // otherwise we'd be defining types we cannot satisfy and aren't referenced in
50406
50577
  // the query.
50407
50578
  if (objectInfos[relation.apiName] !== undefined) {
50579
+ parentRelationshipFields.add(field.relationshipName);
50408
50580
  fields += `${field.relationshipName}: ${relation.apiName}\n`;
50409
50581
  }
50410
50582
  // For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
50411
50583
  }
50412
50584
  else if (field.referenceToInfos.length > 1) {
50585
+ parentRelationshipFields.add(field.relationshipName);
50413
50586
  fields += `${field.relationshipName}: Record\n`;
50414
50587
  for (const relation of field.referenceToInfos) {
50415
50588
  if (objectInfos[relation.apiName] !== undefined) {
@@ -50418,6 +50591,20 @@
50418
50591
  }
50419
50592
  }
50420
50593
  }
50594
+ // handles child relationship
50595
+ for (const childRelationship of childRelationships) {
50596
+ const { childObjectApiName } = childRelationship;
50597
+ // Only add the relationship if there is relevant objectinfos for it,
50598
+ // otherwise we'd be defining types we cannot satisfy and aren't referenced in
50599
+ // the query.
50600
+ // 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
50601
+ if (objectInfos[childObjectApiName] !== undefined &&
50602
+ !parentRelationshipFields.has(childRelationship.relationshipName)) {
50603
+ fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
50604
+ typedScalars.add(`${childObjectApiName}_Filter`);
50605
+ typedScalars.add(`${childObjectApiName}_OrderBy`);
50606
+ }
50607
+ }
50421
50608
  recordQueries += `${apiName}(first: Int, where: ${apiName}_Filter, orderBy: ${apiName}_OrderBy, scope: SupportedScopes): ${apiName}Connection\n`;
50422
50609
  const isServiceAppointment = apiName === 'ServiceAppointment';
50423
50610
  recordConnections += /* GraphQL */ `
@@ -50492,6 +50679,8 @@
50492
50679
  return 'PercentValue';
50493
50680
  case 'Int':
50494
50681
  return 'IntValue';
50682
+ case 'EncryptedString':
50683
+ return 'EncryptedStringValue';
50495
50684
  // ! do the rest of the custom types
50496
50685
  default:
50497
50686
  return 'String';
@@ -50577,7 +50766,7 @@
50577
50766
  },
50578
50767
  value: {
50579
50768
  kind: Kind.STRING,
50580
- value: 'parentRelationship',
50769
+ value: PARENT_RELATIONSHIP,
50581
50770
  block: false,
50582
50771
  },
50583
50772
  },
@@ -50591,8 +50780,8 @@
50591
50780
  // example 2 'ServiceAppointment' -> ['Owner']; 'Owner' -> ['User', 'Group']
50592
50781
  const objectNodeInfoTree = {};
50593
50782
  // save the field path to apiName map
50594
- // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account']; 'ServiceAppointment_Account_Owner' -> ['User']
50595
- const objectInfoApiMap = {};
50783
+ // example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#ccount' -> ['Account']; 'ServiceAppointment#Account#Owner' -> ['User']
50784
+ const pathToObjectApiNamesMap = {};
50596
50785
  let startNodes = new Set();
50597
50786
  let totalNodes = new Set();
50598
50787
  let objectInfos = {};
@@ -50606,11 +50795,11 @@
50606
50795
  visit(originalAST, {
50607
50796
  Argument: {
50608
50797
  enter(node, key, parent, path, ancestors) {
50609
- const recordQueryNode = findNearestRecordQuery(ancestors);
50610
- if (!recordQueryNode)
50798
+ const { connection: recordConnectionNode, path: ancesterPath } = findNearestConnectionWithPath(ancestors);
50799
+ if (!recordConnectionNode || !ancesterPath)
50611
50800
  return;
50612
- if (!objectNodeInfoTree[recordQueryNode.name.value]) {
50613
- objectNodeInfoTree[recordQueryNode.name.value] = [];
50801
+ if (!objectNodeInfoTree[ancesterPath]) {
50802
+ objectNodeInfoTree[ancesterPath] = [];
50614
50803
  }
50615
50804
  switch (node.name.value) {
50616
50805
  case 'orderBy':
@@ -50618,12 +50807,12 @@
50618
50807
  if (node.value.kind !== 'ObjectValue') {
50619
50808
  return;
50620
50809
  }
50621
- totalNodes.add(recordQueryNode.name.value);
50810
+ totalNodes.add(ancesterPath);
50622
50811
  // 'childRelationship' node is not taken as the startNode of the 'NodeInfoTree' graph. The field scanning will construct the graph which lead here.
50623
- if (isRecordQuery(recordQueryNode)) {
50624
- startNodes.add(recordQueryNode.name.value);
50812
+ if (isRecordQuery(recordConnectionNode)) {
50813
+ startNodes.add(recordConnectionNode.name.value);
50625
50814
  }
50626
- growObjectFieldTree(objectNodeInfoTree, recordQueryNode.name.value, node.value, totalNodes, startNodes);
50815
+ growObjectFieldTree(objectNodeInfoTree, ancesterPath, node.value, totalNodes, startNodes);
50627
50816
  break;
50628
50817
  case 'scope':
50629
50818
  if (!isScopeArgumentNodeWithType(node, 'ASSIGNEDTOME', variables)) {
@@ -50638,17 +50827,16 @@
50638
50827
  name: 'ServiceResources',
50639
50828
  });
50640
50829
  }
50641
- if (objectNodeInfoTree['ServiceResources'] === undefined) {
50642
- objectNodeInfoTree['ServiceResources'] = [];
50643
- }
50644
- if (!objectNodeInfoTree['ServiceResources'].some((child) => child.name === 'ServiceResource')) {
50645
- objectNodeInfoTree['ServiceResources'].push({
50646
- relation: 'parent',
50647
- name: 'ServiceResource',
50648
- });
50830
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources'] === undefined) {
50831
+ objectNodeInfoTree['ServiceAppointment#ServiceResources'] = [
50832
+ {
50833
+ relation: 'parent',
50834
+ name: 'ServiceResource',
50835
+ },
50836
+ ];
50649
50837
  }
50650
- if (objectNodeInfoTree['ServiceResource'] === undefined) {
50651
- objectNodeInfoTree['ServiceResource'] = [];
50838
+ if (objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] === undefined) {
50839
+ objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] = [];
50652
50840
  }
50653
50841
  break;
50654
50842
  default:
@@ -50662,7 +50850,7 @@
50662
50850
  return;
50663
50851
  if (!node.selectionSet)
50664
50852
  return;
50665
- const recordQueryField = findNearestRecordQuery(ancestors);
50853
+ const recordQueryField = findNearestConnection(ancestors);
50666
50854
  //only injects fields for 'recordQuery' field. ignores the 'childRelationship' field since it will be traversed as the child of the 'recordQuery'
50667
50855
  if (isRecordQuery(recordQueryField) && recordQueryField) {
50668
50856
  totalNodes.add(recordQueryField.name.value);
@@ -50673,21 +50861,21 @@
50673
50861
  },
50674
50862
  });
50675
50863
  if (objectInfoService && startNodes.size > 0) {
50676
- objectInfos = await resolveObjectInfos(objectNodeInfoTree, objectInfoApiMap, startNodes, objectInfoService);
50864
+ objectInfos = await resolveObjectInfos(objectNodeInfoTree, pathToObjectApiNamesMap, startNodes, objectInfoService);
50677
50865
  }
50678
50866
  // read pass; gather whats needed
50679
50867
  visit(originalAST, {
50680
50868
  Argument: {
50681
50869
  leave(node, key, parent, path, ancestors) {
50682
- const recordQueryField = findNearestRecordQuery(ancestors);
50870
+ const recordQueryField = findNearestConnection(ancestors);
50683
50871
  if (!recordQueryField)
50684
50872
  return;
50685
50873
  const ancestorPath = findAncesterPath(ancestors);
50686
50874
  if (!inlineFragmentSelections[ancestorPath]) {
50687
50875
  inlineFragmentSelections[ancestorPath] = [];
50688
50876
  }
50689
- const recordQueryApiName = objectInfoApiMap[ancestorPath]
50690
- ? objectInfoApiMap[ancestorPath][0]
50877
+ const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
50878
+ ? pathToObjectApiNamesMap[ancestorPath][0]
50691
50879
  : recordQueryField.name.value;
50692
50880
  // The record node acts as the reference. The duplicated field in the record node is not injected
50693
50881
  const recordReferenceNode = [recordQueryField]
@@ -50701,7 +50889,7 @@
50701
50889
  case 'scope':
50702
50890
  // Hanle 'MINE' field
50703
50891
  if (isScopeArgumentNodeWithType(node, 'MINE', variables)) {
50704
- if (isMineScopeAvailable(ancestorPath, objectInfoApiMap, objectInfos)) {
50892
+ if (isMineScopeAvailable(ancestorPath, pathToObjectApiNamesMap, objectInfos)) {
50705
50893
  // 'typeConditon' is added when the 'InlineFragmentNode' is appended at the write pass
50706
50894
  inlineFragmentSelections[ancestorPath].push(...mineFragmentSelections);
50707
50895
  }
@@ -50727,7 +50915,7 @@
50727
50915
  case 'where': {
50728
50916
  inlineFragmentSelections[ancestorPath] = [
50729
50917
  ...inlineFragmentSelections[ancestorPath],
50730
- ...injectFilter(node, idState, ancestorPath, objectInfos, objectInfoApiMap, draftFunctions, recordReferenceNode),
50918
+ ...injectFilter(node, idState, ancestorPath, false, objectInfos, pathToObjectApiNamesMap, draftFunctions, recordReferenceNode),
50731
50919
  ];
50732
50920
  break;
50733
50921
  }
@@ -50743,7 +50931,7 @@
50743
50931
  if (!node.selectionSet)
50744
50932
  return;
50745
50933
  // it could be 'recordQuery' or 'childRelationship'
50746
- const recordQueryField = findNearestRecordQuery(ancestors);
50934
+ const recordQueryField = findNearestConnection(ancestors);
50747
50935
  if (!recordQueryField)
50748
50936
  return;
50749
50937
  const ancestorPath = findAncesterPath(ancestors);
@@ -50755,7 +50943,7 @@
50755
50943
  spanningSelections.push(selection);
50756
50944
  }
50757
50945
  }
50758
- const injectedFields = injectFields(spanningSelections, node, ancestors, objectInfos, objectInfoApiMap);
50946
+ const injectedFields = injectFields(spanningSelections, node, ancestorPath, ancestors, objectInfos, pathToObjectApiNamesMap);
50759
50947
  const mergedInjectedFields = mergeSelectionNodes$1(inlineFragmentSelections[ancestorPath], injectedFields);
50760
50948
  inlineFragmentSelections[ancestorPath] = mergedInjectedFields;
50761
50949
  },
@@ -50768,7 +50956,7 @@
50768
50956
  // removes 'ServicesResources' query field node if 'assignedtome' scope shows up
50769
50957
  if (assignedtomeQueryFieldNode !== undefined &&
50770
50958
  node.name.value === 'ServiceResources') {
50771
- const serviceResourcesAncestor = findNearestRecordQuery(ancestors);
50959
+ const serviceResourcesAncestor = findNearestConnection(ancestors);
50772
50960
  if (serviceResourcesAncestor === assignedtomeQueryFieldNode) {
50773
50961
  return null;
50774
50962
  }
@@ -50777,7 +50965,7 @@
50777
50965
  return;
50778
50966
  if (!node.selectionSet)
50779
50967
  return;
50780
- const recordQueryField = findNearestRecordQuery(ancestors);
50968
+ const recordQueryField = findNearestConnection(ancestors);
50781
50969
  if (!recordQueryField)
50782
50970
  return;
50783
50971
  const ancestorPath = findAncesterPath(ancestors);
@@ -50786,8 +50974,8 @@
50786
50974
  return;
50787
50975
  //const recordQueryPath = findAncesterPath(ancestors);
50788
50976
  // '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.
50789
- const recordQueryApiName = objectInfoApiMap[ancestorPath]
50790
- ? objectInfoApiMap[ancestorPath][0]
50977
+ const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
50978
+ ? pathToObjectApiNamesMap[ancestorPath][0]
50791
50979
  : recordQueryField.name.value;
50792
50980
  const nodeWithFragments = {
50793
50981
  ...node,
@@ -50824,7 +51012,7 @@
50824
51012
  if (node.name.value === 'where') {
50825
51013
  const ancestorPath = findAncesterPath(ancestors);
50826
51014
  if (idState.paths.includes(ancestorPath)) {
50827
- const apiName = objectInfoApiMap[ancestorPath][0];
51015
+ const apiName = pathToObjectApiNamesMap[ancestorPath][0];
50828
51016
  const objectInfo = objectInfos[apiName];
50829
51017
  const swappedIdFilter = swapIdField(node.value, objectInfo, false, idState, draftFunctions);
50830
51018
  return {
@@ -50890,8 +51078,8 @@
50890
51078
  };
50891
51079
  }
50892
51080
  }
50893
- function isMineScopeAvailable(apiNamePath, objectInfoApiMap, objectInfos) {
50894
- const apiName = objectInfoApiMap[apiNamePath];
51081
+ function isMineScopeAvailable(apiNamePath, pathToObjectApiNamesMap, objectInfos) {
51082
+ const apiName = pathToObjectApiNamesMap[apiNamePath];
50895
51083
  if (!apiName)
50896
51084
  return false;
50897
51085
  const objectInfo = objectInfos[apiName[0]];
@@ -50980,15 +51168,16 @@
50980
51168
  }
50981
51169
  // example: 'Account'
50982
51170
  const childNode = objectFieldNode.name.value;
51171
+ const childNodepath = `${parentNode}#${childNode}`;
50983
51172
  if (!tree[parentNode].some((child) => child.name === childNode)) {
50984
51173
  tree[parentNode].push({
50985
51174
  relation: 'parent',
50986
51175
  name: childNode,
50987
51176
  });
50988
- totalNodes.add(childNode);
51177
+ totalNodes.add(childNodepath);
50989
51178
  }
50990
51179
  // recursively go to deeper level of filter.
50991
- growObjectFieldTree(tree, childNode, objectFieldNode.value, totalNodes, startNodes);
51180
+ growObjectFieldTree(tree, childNodepath, objectFieldNode.value, totalNodes, startNodes);
50992
51181
  }
50993
51182
  }
50994
51183
  }
@@ -51023,19 +51212,20 @@
51023
51212
  }
51024
51213
  if (!tree[parentSectionPath].some((field) => field.name === fieldName)) {
51025
51214
  tree[parentSectionPath].push({
51026
- relation: relationType === 'parentRelationship' ||
51027
- relationType === 'polymorphicParentRelationship'
51215
+ relation: relationType === PARENT_RELATIONSHIP ||
51216
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
51028
51217
  ? 'parent'
51029
51218
  : 'child',
51030
51219
  name: fieldName,
51031
51220
  });
51032
- totalNodes.add(fieldName);
51221
+ totalNodes.add(`${parentSectionPath}#${fieldName}`);
51033
51222
  }
51034
51223
  if (entryNode.selectionSet && entryNode.selectionSet.selections) {
51035
51224
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
51036
51225
  // recursively build the traversal tree
51037
51226
  for (const child of childNodes) {
51038
- growFieldTree(tree, fieldName, child, entryNode, totalNodes, startNodes);
51227
+ const path = `${parentSectionPath}#${fieldName}`;
51228
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
51039
51229
  }
51040
51230
  }
51041
51231
  }
@@ -51065,23 +51255,23 @@
51065
51255
  }
51066
51256
  if (!tree[parentSectionPath].some((field) => field.name === conditionName)) {
51067
51257
  tree[parentSectionPath].push({
51068
- relation: relationType === 'parentRelationship' ||
51069
- relationType === 'polymorphicParentRelationship'
51258
+ relation: relationType === PARENT_RELATIONSHIP ||
51259
+ relationType === POLYMORPHIC_PARENT_RELATIONSHIP
51070
51260
  ? 'parent'
51071
51261
  : 'child',
51072
51262
  name: conditionName,
51073
51263
  });
51074
- totalNodes.add(conditionName);
51264
+ const path = `${parentSectionPath}#${conditionName}`;
51265
+ totalNodes.add(path);
51075
51266
  }
51076
51267
  }
51077
51268
  }
51078
51269
  // dive deep immediately for 'InlineFragment'
51079
51270
  const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
51271
+ const path = `${parentSectionPath}${entryNode.typeCondition ? '#' + entryNode.typeCondition.name.value : ''}`;
51080
51272
  // Navigates into InLineFragment
51081
51273
  for (const child of childNodes) {
51082
- growFieldTree(tree, entryNode.typeCondition
51083
- ? entryNode.typeCondition.name.value
51084
- : parentSectionPath, child, entryNode, totalNodes, startNodes);
51274
+ growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
51085
51275
  }
51086
51276
  }
51087
51277
  }
@@ -51093,7 +51283,7 @@
51093
51283
  * @param startNodes start nodes of the tree. It can be used to fetch ObjectInfo immediately
51094
51284
  * @param path
51095
51285
  */
51096
- async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes, objectInfoService) {
51286
+ async function resolveObjectInfos(objectInfotree, pathToObjectApiNamesMap, startNodes, objectInfoService) {
51097
51287
  let objectInfos;
51098
51288
  try {
51099
51289
  objectInfos = await objectInfoService.getObjectInfos(Array.from(startNodes));
@@ -51107,9 +51297,9 @@
51107
51297
  throw new Error(`Unable to resolve ObjectInfo(s) for ${Array.from(startNodes)}`);
51108
51298
  }
51109
51299
  for (const startNode of startNodes) {
51110
- objectInfoApiMap[startNode] = [startNode];
51300
+ pathToObjectApiNamesMap[startNode] = [startNode];
51111
51301
  const children = objectInfotree[startNode];
51112
- const subObjectInfoMap = await fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfos, children, startNode, objectInfoService);
51302
+ const subObjectInfoMap = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfos, children, startNode, objectInfoService);
51113
51303
  objectInfos = { ...objectInfos, ...subObjectInfoMap };
51114
51304
  }
51115
51305
  return objectInfos;
@@ -51117,15 +51307,15 @@
51117
51307
  // example 1: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Account']
51118
51308
  // example 2: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Owner'], this example has 2 apiName for the node 'Owner'
51119
51309
  // example 3: 'parentPath': 'ServiceAppointment_Owner', 'nodesAtSameLevel': ['User', 'Group']
51120
- async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap, nodesAtSameLevel, parentPath, objectInfoService) {
51121
- const objectInfoApiNames = objectInfoApiMap[parentPath];
51310
+ async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfoMap, nodesAtSameLevel, parentPath, objectInfoService) {
51311
+ const objectInfoApiNames = pathToObjectApiNamesMap[parentPath];
51122
51312
  if (!objectInfoApiNames) {
51123
51313
  // eslint-disable-next-line
51124
51314
  throw new Error(`Object Info does not exist for ${parentPath}`);
51125
51315
  }
51126
51316
  const validObjectInfoNodes = [];
51127
51317
  let updatedObjectInfoMap = {};
51128
- // InlineFragment and polymorphic field support fits into this scenario ObjectInfoApiMap Entry: 'ServiceAppointment_Owner' -> ['User', 'Group']; ServiceAppointment_Owner_User' -> ['User']
51318
+ // InlineFragment and polymorphic field support fits into this scenario pathToObjectApiNamesMap Entry: 'ServiceAppointment#Owner' -> ['User', 'Group']; ServiceAppointment#Owner#User' -> ['User']
51129
51319
  if (objectInfoApiNames.length > 0 &&
51130
51320
  nodesAtSameLevel.length > 0 &&
51131
51321
  objectInfoApiNames.includes(nodesAtSameLevel[0].name)) {
@@ -51137,8 +51327,8 @@
51137
51327
  // eslint-disable-next-line
51138
51328
  throw new Error(`Condition ${field.name} does not exists for ${parentPath}`);
51139
51329
  }
51140
- const path = `${parentPath}_${field.name}`;
51141
- objectInfoApiMap[path] = [field.name];
51330
+ const path = `${parentPath}#${field.name}`;
51331
+ pathToObjectApiNamesMap[path] = [field.name];
51142
51332
  }
51143
51333
  validObjectInfoNodes.push(...nodesAtSameLevel);
51144
51334
  updatedObjectInfoMap = { ...objectInfoMap };
@@ -51153,7 +51343,7 @@
51153
51343
  let apiNames = [];
51154
51344
  for (const nodeInfo of nodesAtSameLevel) {
51155
51345
  const field = nodeInfo.name;
51156
- const path = `${parentPath}_${field}`;
51346
+ const path = `${parentPath}#${field}`;
51157
51347
  // Handle 'parentRelationship'
51158
51348
  if (nodeInfo.relation === 'parent') {
51159
51349
  const relationshipId = referenceIdFieldForRelationship(field);
@@ -51171,21 +51361,21 @@
51171
51361
  }
51172
51362
  }
51173
51363
  // This is a polymorphic field
51174
- if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[field]) {
51364
+ if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[path]) {
51175
51365
  // Fields needs to expand and heterogenous entity ObjectInfo needs to be fetched
51176
- const referencedNodeInfos = objectInfotree[field];
51366
+ const referencedNodeInfos = objectInfotree[path];
51177
51367
  const requestedApiNames = referencedNodeInfos.map((referenceNodeInfo) => referenceNodeInfo.name);
51178
51368
  // 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.
51179
- if (requestedApiNames.length > 0 && objectInfotree[field]) {
51369
+ if (requestedApiNames.length > 0 && objectInfotree[path]) {
51180
51370
  fieldDefinition.referenceToInfos
51181
51371
  .filter((referenceToInfo) => requestedApiNames.includes(referenceToInfo.apiName))
51182
51372
  .forEach((ref) => {
51183
- if (!objectInfoApiMap[path]) {
51184
- objectInfoApiMap[path] = [];
51373
+ if (!pathToObjectApiNamesMap[path]) {
51374
+ pathToObjectApiNamesMap[path] = [];
51185
51375
  }
51186
51376
  // 'ServiceAppointment_Owner' ->['User', 'Group']
51187
- if (!objectInfoApiMap[path].includes(ref.apiName)) {
51188
- objectInfoApiMap[path].push(ref.apiName);
51377
+ if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
51378
+ pathToObjectApiNamesMap[path].push(ref.apiName);
51189
51379
  }
51190
51380
  if (!apiNames.includes(ref.apiName)) {
51191
51381
  apiNames.push(ref.apiName);
@@ -51195,11 +51385,11 @@
51195
51385
  }
51196
51386
  else if (fieldDefinition.referenceToInfos.length === 1) {
51197
51387
  const ref = fieldDefinition.referenceToInfos[0];
51198
- if (!objectInfoApiMap[path]) {
51199
- objectInfoApiMap[path] = [];
51388
+ if (!pathToObjectApiNamesMap[path]) {
51389
+ pathToObjectApiNamesMap[path] = [];
51200
51390
  }
51201
- if (!objectInfoApiMap[path].includes(ref.apiName)) {
51202
- objectInfoApiMap[path].push(ref.apiName);
51391
+ if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
51392
+ pathToObjectApiNamesMap[path].push(ref.apiName);
51203
51393
  }
51204
51394
  if (!apiNames.includes(ref.apiName)) {
51205
51395
  apiNames.push(ref.apiName);
@@ -51210,11 +51400,11 @@
51210
51400
  // handles 'childRelationship'
51211
51401
  const childRelationship = parentObjectInfo.childRelationships.find((childRelationship) => childRelationship.relationshipName === field);
51212
51402
  if (childRelationship) {
51213
- if (!objectInfoApiMap[path]) {
51214
- objectInfoApiMap[path] = [];
51403
+ if (!pathToObjectApiNamesMap[path]) {
51404
+ pathToObjectApiNamesMap[path] = [];
51215
51405
  }
51216
- if (!objectInfoApiMap[path].includes(childRelationship.childObjectApiName)) {
51217
- objectInfoApiMap[path].push(childRelationship.childObjectApiName);
51406
+ if (!pathToObjectApiNamesMap[path].includes(childRelationship.childObjectApiName)) {
51407
+ pathToObjectApiNamesMap[path].push(childRelationship.childObjectApiName);
51218
51408
  }
51219
51409
  if (!apiNames.includes(childRelationship.childObjectApiName)) {
51220
51410
  apiNames.push(childRelationship.childObjectApiName);
@@ -51236,10 +51426,10 @@
51236
51426
  }
51237
51427
  for (const nodeInfo of validObjectInfoNodes) {
51238
51428
  const field = nodeInfo.name;
51239
- const subLevelFields = objectInfotree[field];
51240
- const path = `${parentPath}_${field}`;
51429
+ const path = `${parentPath}#${field}`;
51430
+ const subLevelFields = objectInfotree[path];
51241
51431
  if (subLevelFields && subLevelFields.length > 0) {
51242
- const subObjectInfos = await fetchObjectInfos(objectInfotree, objectInfoApiMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
51432
+ const subObjectInfos = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
51243
51433
  updatedObjectInfoMap = { ...updatedObjectInfoMap, ...subObjectInfos };
51244
51434
  }
51245
51435
  }
@@ -51254,27 +51444,29 @@
51254
51444
  * 'path' and 'queryNode' is 1 level above the 'filterNode'
51255
51445
  * @param filterNode filter node which needs to be injected. For example, 'State' ObjectFieldNode within filter 'where: { State: { eq: "Nova Scotia" }}'
51256
51446
  * @param idState ID state will be updated to determine if the ID fields in AST need to be swapped. The swapping happens later.
51257
- * @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
51447
+ * @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
51448
+ * @param isParentPolymorphic true if parent points to a polymorphic field.
51258
51449
  * @param queryNode referece FieldNode which provides the information if 'filterNode' exist in it nor not.
51259
51450
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
51260
- * @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'].
51451
+ * @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'].
51261
51452
  * @param draftFunctions functions for working with record ids that may be draft-created ids
51262
51453
  * @returns an array of nodes with injected fields
51263
51454
  */
51264
- function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode) {
51455
+ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode) {
51265
51456
  const injectedSelections = [];
51457
+ let isPolymorphicField = false;
51266
51458
  switch (filterNode.kind) {
51267
51459
  case Kind.ARGUMENT:
51268
51460
  if (filterNode.value.kind !== 'ObjectValue')
51269
51461
  return [];
51270
51462
  filterNode.value.fields.forEach((objectFieldNode) => {
51271
- let subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
51463
+ let subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
51272
51464
  for (const subResult of subResults) {
51273
51465
  mergeOrAddToGroup(injectedSelections, subResult);
51274
51466
  }
51275
51467
  // multiple Ids might need to be swapped. remember their paths for faster write.
51276
51468
  if (idState.swapNeeded) {
51277
- idState.paths.push(path);
51469
+ idState.paths.push(parentPath);
51278
51470
  }
51279
51471
  });
51280
51472
  return injectedSelections;
@@ -51283,7 +51475,7 @@
51283
51475
  case Kind.LIST: {
51284
51476
  filterNode.value.values.filter(isObjectValueNode).forEach((objectValueNode) => {
51285
51477
  objectValueNode.fields.forEach((objectFieldNode) => {
51286
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
51478
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
51287
51479
  for (const subResult of subResults) {
51288
51480
  mergeOrAddToGroup(injectedSelections, subResult);
51289
51481
  }
@@ -51294,7 +51486,7 @@
51294
51486
  case Kind.OBJECT: {
51295
51487
  if (filterNode.name.value === 'not') {
51296
51488
  filterNode.value.fields.forEach((objectFieldNode) => {
51297
- const subResults = injectFilter(objectFieldNode, idState, path, objectInfos, objectInfoApiMap, draftFunctions, queryNode);
51489
+ const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
51298
51490
  for (const subResult of subResults) {
51299
51491
  mergeOrAddToGroup(injectedSelections, subResult);
51300
51492
  }
@@ -51304,15 +51496,15 @@
51304
51496
  let apiNames = [];
51305
51497
  let isScalarField = false;
51306
51498
  //It is possible that this is a polymorphic field
51307
- apiNames = objectInfoApiMap[path];
51308
- // example: path: 'ServiceAppointment_LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
51499
+ apiNames = pathToObjectApiNamesMap[parentPath];
51500
+ // example: path: 'ServiceAppointment#LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
51309
51501
  if (apiNames === undefined) {
51310
51502
  isScalarField = true;
51311
51503
  }
51312
51504
  else {
51313
51505
  if (apiNames.some((apiName) => objectInfos[apiName] === undefined)) {
51314
51506
  // eslint-disable-next-line
51315
- throw new Error(`ObjectInfo is missing for ${path}`);
51507
+ throw new Error(`ObjectInfo is missing for ${parentPath}`);
51316
51508
  }
51317
51509
  }
51318
51510
  if (isScalarField) {
@@ -51334,29 +51526,19 @@
51334
51526
  }
51335
51527
  });
51336
51528
  let isSpanning = false;
51529
+ // if true, current node is a polymorphic concrete type node. For example, field node `User` under `Owner`
51337
51530
  let isInlineFragment = false;
51338
- let isPolymorphicField = false;
51339
51531
  let isTypeNameExisting = false;
51340
51532
  let curPath;
51341
51533
  let fieldName = filterNode.name.value;
51342
- curPath = `${path}_${fieldName}`;
51343
- if (objectInfoApiMap[curPath] && objectInfoApiMap[curPath].length > 0) {
51534
+ curPath = `${parentPath}#${fieldName}`;
51535
+ if (pathToObjectApiNamesMap[curPath] &&
51536
+ pathToObjectApiNamesMap[curPath].length > 0) {
51344
51537
  isSpanning = true;
51345
- if (objectInfoApiMap[curPath].length === 1) {
51346
- if (objectInfoApiMap[path] &&
51347
- objectInfoApiMap[path].length >= 1 &&
51348
- objectInfoApiMap[path].includes(objectInfoApiMap[curPath][0])) {
51349
- isInlineFragment = true;
51350
- }
51351
- }
51352
- // Checks if the current filter node is a polymorphic field. 'ServiceAppointment_Owner' --> ['User']; 'ServiceAppointment_Owner_User' --> ['User']
51353
- const childApiName = objectInfoApiMap[curPath][0];
51354
- const trialApiNames = objectInfoApiMap[`${curPath}_${childApiName}`];
51355
- if (trialApiNames !== undefined &&
51356
- trialApiNames.length === 1 &&
51357
- trialApiNames[0] === childApiName) {
51358
- isPolymorphicField = true;
51359
- }
51538
+ isInlineFragment =
51539
+ isParentPolymorphic &&
51540
+ pathToObjectApiNamesMap[curPath].length === 1;
51541
+ isPolymorphicField = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
51360
51542
  }
51361
51543
  // 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]
51362
51544
  if (isInlineFragment) {
@@ -51400,23 +51582,25 @@
51400
51582
  throw new Error(`Field ${fieldName} does not exist in ${apiNames[0]}`);
51401
51583
  }
51402
51584
  }
51403
- const objectInfoName = objectInfoApiMap[curPath] !== undefined
51404
- ? objectInfoApiMap[curPath][0]
51405
- : objectInfoApiMap[path][0];
51585
+ const objectInfoName = pathToObjectApiNamesMap[curPath] !== undefined
51586
+ ? pathToObjectApiNamesMap[curPath][0]
51587
+ : pathToObjectApiNamesMap[parentPath][0];
51406
51588
  const isIdField = isFieldAnIdField(filterNode.name.value, objectInfos[objectInfoName]);
51407
51589
  if (!isIdField) {
51408
51590
  let subSelectionNodes = [];
51409
51591
  let subFieldsHasId = false;
51410
- filterNode.value.fields.forEach((subFieldNode) => {
51411
- // Check if the filter field has the 'Id'
51412
- if (isFieldAnIdField(subFieldNode.name.value, objectInfos[objectInfoName])) {
51413
- subFieldsHasId = true;
51414
- updateIDInfo(subFieldNode, idState, draftFunctions);
51415
- }
51416
- // try injecting the fields within predicate no matter it has relation or not.
51417
- let subResults = injectFilter(subFieldNode, idState, curPath, objectInfos, objectInfoApiMap, draftFunctions, existingFields ? existingFields[0] : undefined);
51418
- subSelectionNodes = subSelectionNodes.concat(subResults);
51419
- });
51592
+ if (isSpanning) {
51593
+ filterNode.value.fields.forEach((subFieldNode) => {
51594
+ // Check if the filter field has the 'Id'
51595
+ if (isFieldAnIdField(subFieldNode.name.value, objectInfos[objectInfoName])) {
51596
+ subFieldsHasId = true;
51597
+ updateIDInfo(subFieldNode, idState, draftFunctions);
51598
+ }
51599
+ // try injecting the fields within predicate no matter it has relation or not.
51600
+ let subResults = injectFilter(subFieldNode, idState, curPath, isPolymorphicField, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
51601
+ subSelectionNodes = subSelectionNodes.concat(subResults);
51602
+ });
51603
+ }
51420
51604
  if (!subFieldsHasId) {
51421
51605
  // Check if the query field has the 'Id'
51422
51606
  const existingIdsInQuery = existingFields &&
@@ -51445,6 +51629,7 @@
51445
51629
  }
51446
51630
  //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
51447
51631
  if (!existingFields ||
51632
+ existingFields.length === 0 ||
51448
51633
  subSelectionNodes.length > 0 ||
51449
51634
  (isSpanning && !subFieldsHasId) ||
51450
51635
  (isInlineFragment && !isTypeNameExisting)) {
@@ -51575,6 +51760,44 @@
51575
51760
  }
51576
51761
  group.push(element);
51577
51762
  }
51763
+ // checks if the path points to a polymorphic field. For example, for the below `pathToObjectApiNamesMap`
51764
+ // {
51765
+ // 'ServiceAppointment' -> ['ServiceAppointment']
51766
+ // 'ServiceAppointment#Owner' --> ['User'],
51767
+ // 'ServiceAppointment#Owner#User' --> ['User']
51768
+ // }
51769
+ // path `ServiceAppointment#Owner` points to a polymorphic field, but path `ServiceAppointment#Owner#User` does not.
51770
+ function isPolymorphicFieldPath(path, pathToObjectApiNamesMap, objectInfos) {
51771
+ const lastSegmentIndex = path.lastIndexOf('#');
51772
+ if (lastSegmentIndex < 0) {
51773
+ return false;
51774
+ }
51775
+ const lastSegment = path.slice(lastSegmentIndex + 1);
51776
+ const parentApiPath = path.slice(0, lastSegmentIndex);
51777
+ if (pathToObjectApiNamesMap[parentApiPath] === undefined) {
51778
+ return false;
51779
+ }
51780
+ const parentObjectApiNames = pathToObjectApiNamesMap[parentApiPath];
51781
+ // 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`.
51782
+ // 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.
51783
+ // Below are the entries in `pathToObjectApiNamesMap`
51784
+ // {
51785
+ // `ServiceAppointmen`t: [`ServiceAppointment`],
51786
+ // `ServiceAppointment#Owner`: [`User`, `Group`],
51787
+ // `ServiceAppointment#Owner#User`: [`User`],
51788
+ // `ServiceAppointment#Owner#Group`: [`Group`],
51789
+ // }
51790
+ if (parentObjectApiNames.length !== 1) {
51791
+ return false;
51792
+ }
51793
+ const parentObjectInfo = objectInfos[parentObjectApiNames[0]];
51794
+ const relationshipField = referenceIdFieldForRelationship(lastSegment);
51795
+ let fieldDefinition = parentObjectInfo.fields[relationshipField];
51796
+ if (fieldDefinition === undefined) {
51797
+ return false;
51798
+ }
51799
+ return fieldDefinition.polymorphicForeignKey;
51800
+ }
51578
51801
  function isFieldAnIdField(fieldName, objectInfo) {
51579
51802
  if (fieldName === 'Id')
51580
51803
  return true;
@@ -51627,10 +51850,10 @@
51627
51850
  * @param parentNode parent node of param 1
51628
51851
  * @param ancestors ancester of param 1
51629
51852
  * @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
51630
- * @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'].
51853
+ * @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'].
51631
51854
  * @return injected SelectionNodes used to construct the InlineFragment.
51632
51855
  */
51633
- function injectFields(selections, parentNode, ancestors, objectInfos, objectInfoApiMap) {
51856
+ function injectFields(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
51634
51857
  /**
51635
51858
  * 1 parentship can return 2 FieldNode which need to be flattened
51636
51859
  * Concact: { ** Contact { ** ContactId {
@@ -51648,6 +51871,10 @@
51648
51871
  if (!selection.selectionSet) {
51649
51872
  return selection;
51650
51873
  }
51874
+ const segment = isFieldNode(selection)
51875
+ ? selection.name.value
51876
+ : selection.typeCondition.name.value;
51877
+ const curPath = `${parentPath}#${segment}`;
51651
51878
  const spanningSubSelections = [];
51652
51879
  for (const subSelection of selection.selectionSet.selections) {
51653
51880
  if (isFieldSpanning(subSelection, selection)) {
@@ -51655,7 +51882,7 @@
51655
51882
  }
51656
51883
  }
51657
51884
  // Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
51658
- const subInjectedSelections = injectFields(spanningSubSelections, selection, ancestors, objectInfos, objectInfoApiMap);
51885
+ const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
51659
51886
  if (!selection.selectionSet) {
51660
51887
  return selection;
51661
51888
  }
@@ -51698,7 +51925,7 @@
51698
51925
  }
51699
51926
  }
51700
51927
  // For polymorphic fields, the Id field is excluded.
51701
- const excludeId = selection.selectionSet.selections.every(isInlineFragmentNode);
51928
+ const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
51702
51929
  const idSelection = [];
51703
51930
  if (!excludeId && !hasIdAlready) {
51704
51931
  idSelection.push({
@@ -51709,8 +51936,8 @@
51709
51936
  },
51710
51937
  });
51711
51938
  }
51712
- // 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,
51713
- // please reference 'removeSyntheticFields'.
51939
+ // 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
51940
+ // `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
51714
51941
  if (isInlineFragmentNode(selection) &&
51715
51942
  !selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
51716
51943
  idSelection.push({
@@ -51744,7 +51971,7 @@
51744
51971
  if (isFieldNode(parentNode) && parentNode.selectionSet && parentNode.name.value === 'node') {
51745
51972
  if (parentNode.selectionSet.selections
51746
51973
  .filter(isFieldOrInlineFragmentNode)
51747
- .some((selectionNode) => isRelationship(selectionNode, 'childRelationship'))) {
51974
+ .some((selectionNode) => isRelationship(selectionNode, CHILD_RELATIONSHIP))) {
51748
51975
  if (!parentNode.selectionSet.selections
51749
51976
  .filter(isFieldNode)
51750
51977
  .some((sibling) => sibling.name.value === 'Id')) {
@@ -51763,15 +51990,15 @@
51763
51990
  if (parentInfo.parentIndex >= 0) {
51764
51991
  // example node { TimeSheetEntries { edges { node { Id }}}}
51765
51992
  const parent = parentInfo.node;
51766
- if (isRelationship(parent, 'childRelationship')) {
51993
+ if (isRelationship(parent, CHILD_RELATIONSHIP)) {
51767
51994
  const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
51768
51995
  // path : "TimeSheet"
51769
51996
  const grandParentPath = findAncesterPath(unVisitedAncestors);
51770
- if (objectInfoApiMap &&
51771
- objectInfoApiMap[grandParentPath] &&
51997
+ if (pathToObjectApiNamesMap &&
51998
+ pathToObjectApiNamesMap[grandParentPath] &&
51772
51999
  objectInfos &&
51773
- objectInfos[objectInfoApiMap[grandParentPath][0]]) {
51774
- const grandParentObjectInfo = objectInfos[objectInfoApiMap[grandParentPath][0]];
52000
+ objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
52001
+ const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
51775
52002
  // exmaple "TimeSheetEntries"
51776
52003
  const parentFieldName = parent.name.value;
51777
52004
  const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
@@ -51892,7 +52119,7 @@
51892
52119
  },
51893
52120
  value: {
51894
52121
  kind: 'StringValue',
51895
- value: 'childRelationship',
52122
+ value: CHILD_RELATIONSHIP,
51896
52123
  block: false,
51897
52124
  },
51898
52125
  },
@@ -51984,7 +52211,7 @@
51984
52211
  },
51985
52212
  value: {
51986
52213
  kind: 'StringValue',
51987
- value: 'parentRelationship',
52214
+ value: PARENT_RELATIONSHIP,
51988
52215
  block: false,
51989
52216
  },
51990
52217
  },
@@ -52121,7 +52348,9 @@
52121
52348
  jsonOutput[fieldName] = null;
52122
52349
  return;
52123
52350
  }
52124
- jsonOutput[fieldName] = {};
52351
+ if (jsonOutput[fieldName] === undefined) {
52352
+ jsonOutput[fieldName] = {};
52353
+ }
52125
52354
  createUserJsonOutput(selection, jsonInput[fieldName], jsonOutput[fieldName]);
52126
52355
  }
52127
52356
  else {
@@ -52853,34 +53082,42 @@
52853
53082
  }
52854
53083
  const { dataType, relationshipName, referenceToInfos } = fieldInfo;
52855
53084
  const draftFieldValue = record.fields[draftField].value;
52856
- if (dataType === 'Reference' && relationshipName !== null && draftFieldValue !== null) {
52857
- if (typeof draftFieldValue !== 'string') {
52858
- throw Error('reference field value is not a string');
53085
+ if (dataType === 'Reference' && relationshipName !== null) {
53086
+ if (draftFieldValue === null) {
53087
+ recordFields[relationshipName] = {
53088
+ displayValue: null,
53089
+ value: null,
53090
+ };
52859
53091
  }
52860
- const key = getRecordKeyForId(luvio, draftFieldValue);
52861
- const referencedRecord = referencedRecords.get(key);
52862
- recordFields[relationshipName] = {
52863
- displayValue: null,
52864
- value: createLink$2(key),
52865
- };
52866
- // for custom objects, we select the 'Name' field
52867
- // otherwise we check the object info for name fields.
52868
- //if there are multiple we select 'Name' if it exists, otherwise the first one
52869
- if (referencedRecord !== undefined && referenceToInfos.length > 0) {
52870
- let nameField;
52871
- const referenceToInfo = referenceToInfos[0];
52872
- const nameFields = referenceToInfo.nameFields;
52873
- if (nameFields.length !== 0) {
52874
- nameField = nameFields.find((x) => x === 'Name');
52875
- if (nameField === undefined) {
52876
- nameField = nameFields[0];
52877
- }
53092
+ else {
53093
+ if (typeof draftFieldValue !== 'string') {
53094
+ throw Error('reference field value is not a string');
52878
53095
  }
52879
- if (nameField !== undefined) {
52880
- const nameFieldRef = referencedRecord.fields[nameField];
52881
- if (nameFieldRef) {
52882
- recordFields[relationshipName].displayValue =
52883
- (_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
53096
+ const key = getRecordKeyForId(luvio, draftFieldValue);
53097
+ const referencedRecord = referencedRecords.get(key);
53098
+ recordFields[relationshipName] = {
53099
+ displayValue: null,
53100
+ value: createLink$2(key),
53101
+ };
53102
+ // for custom objects, we select the 'Name' field
53103
+ // otherwise we check the object info for name fields.
53104
+ //if there are multiple we select 'Name' if it exists, otherwise the first one
53105
+ if (referencedRecord !== undefined && referenceToInfos.length > 0) {
53106
+ let nameField;
53107
+ const referenceToInfo = referenceToInfos[0];
53108
+ const nameFields = referenceToInfo.nameFields;
53109
+ if (nameFields.length !== 0) {
53110
+ nameField = nameFields.find((x) => x === 'Name');
53111
+ if (nameField === undefined) {
53112
+ nameField = nameFields[0];
53113
+ }
53114
+ }
53115
+ if (nameField !== undefined) {
53116
+ const nameFieldRef = referencedRecord.fields[nameField];
53117
+ if (nameFieldRef) {
53118
+ recordFields[relationshipName].displayValue =
53119
+ (_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
53120
+ }
52884
53121
  }
52885
53122
  }
52886
53123
  }
@@ -53173,17 +53410,8 @@
53173
53410
  };
53174
53411
  for (const fieldName of keys$3(recordWithSpanningRefLinks.fields)) {
53175
53412
  const fieldKey = buildRecordFieldStoreKey(key, fieldName);
53176
- if (this.collectedFields[fieldKey] !== undefined) {
53177
- const fieldData = recordWithSpanningRefLinks.fields[fieldName];
53178
- normalizedRecord.fields[fieldName] = { __ref: fieldKey };
53179
- publishData(fieldKey, fieldData);
53180
- }
53181
- else if (recordWithSpanningRefLinks.fields[fieldName] &&
53182
- recordWithSpanningRefLinks.fields[fieldName].value &&
53183
- recordWithSpanningRefLinks.fields[fieldName].value.__ref !== undefined) {
53184
- normalizedRecord.fields[fieldName] = { __ref: fieldKey };
53185
- publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
53186
- }
53413
+ normalizedRecord.fields[fieldName] = { __ref: fieldKey };
53414
+ publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
53187
53415
  }
53188
53416
  // publish the normalized record
53189
53417
  publishData(key, normalizedRecord);
@@ -54103,7 +54331,7 @@
54103
54331
  // Fulfilled snapshot (this only happens in this code path if
54104
54332
  // the error is network error or 504), otherwise we spread over
54105
54333
  // the non-eval'ed snapshot (which will be either Fulfilled or Stale)
54106
- return nonEvaluatedSnapshot.state === 'Error'
54334
+ const resultSnapshot = nonEvaluatedSnapshot.state === 'Error'
54107
54335
  ? createLocalEvalSnapshot(gqlResult, seenRecords, recordId, rebuildWithLocalEval)
54108
54336
  : {
54109
54337
  ...nonEvaluatedSnapshot,
@@ -54112,6 +54340,22 @@
54112
54340
  seenRecords,
54113
54341
  rebuildWithLocalEval,
54114
54342
  };
54343
+ const { refresh, state } = resultSnapshot;
54344
+ if (state !== 'Error' && refresh) {
54345
+ // after refreshing a graphql snapshot, we want to force a rebuild regardless
54346
+ // of if the call failed or not or if the data changed or not because we want
54347
+ // to make sure any potential new drafts are picked up
54348
+ resultSnapshot.refresh = {
54349
+ ...refresh,
54350
+ resolve: (config) => {
54351
+ return refresh.resolve(config).finally(() => {
54352
+ luvio.storePublish(resultSnapshot.recordId, undefined);
54353
+ luvio.storeBroadcast();
54354
+ });
54355
+ },
54356
+ };
54357
+ }
54358
+ return resultSnapshot;
54115
54359
  };
54116
54360
  }
54117
54361
 
@@ -55165,7 +55409,10 @@
55165
55409
  * @returns the merged record
55166
55410
  */
55167
55411
  function mergeAggregateUiResponse(response, mergeFunc) {
55168
- const { body } = response;
55412
+ if (response.ok === false) {
55413
+ return response;
55414
+ }
55415
+ const body = response.body;
55169
55416
  try {
55170
55417
  if (body === null ||
55171
55418
  body === undefined ||
@@ -56996,6 +57243,9 @@
56996
57243
  this.ldsRecordRefresher = config.ldsRecordRefresher;
56997
57244
  this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
56998
57245
  this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
57246
+ if (this.useBatchGQL) {
57247
+ this.batchSize = this.batchSize / DEFAULT_GQL_QUERY_BATCH_SIZE;
57248
+ }
56999
57249
  this.conflictPool = new ConflictPool(config.store, this.objectInfoLoader);
57000
57250
  }
57001
57251
  // function that enqueues priming work
@@ -57852,7 +58102,7 @@
57852
58102
  id: '@salesforce/lds-network-adapter',
57853
58103
  instrument: instrument$1,
57854
58104
  });
57855
- // version: 1.229.0-dev1-f69d054a9
58105
+ // version: 1.229.0-dev10-bc9ef2513
57856
58106
 
57857
58107
  const { create: create$2, keys: keys$2 } = Object;
57858
58108
  const { stringify: stringify$1, parse: parse$1 } = JSON;
@@ -71176,7 +71426,7 @@
71176
71426
  }
71177
71427
  }
71178
71428
 
71179
- const { assign, create: create$1, freeze: freeze$1, keys: keys$1 } = Object;
71429
+ const { assign, create: create$1, freeze: freeze$1, isFrozen, keys: keys$1 } = Object;
71180
71430
  const { isArray: isArray$1 } = Array;
71181
71431
  const { concat, filter, includes, push, reduce } = Array.prototype;
71182
71432
 
@@ -72615,6 +72865,7 @@
72615
72865
  }
72616
72866
  if (fieldData === null) {
72617
72867
  reader.assignScalar(requestedFieldName, sink, fieldData);
72868
+ reader.exitPath();
72618
72869
  return sink;
72619
72870
  }
72620
72871
  const fieldType = getFieldType(sel);
@@ -72640,17 +72891,8 @@
72640
72891
  return sink;
72641
72892
  }
72642
72893
  function selectTypeLink(sel, fieldData, reader, key, sink, variables, fragments, version, selectFn, isCursorConnection) {
72643
- const resolvedLink = reader.read({
72644
- recordId: fieldData.__ref,
72645
- node: {
72646
- kind: 'Fragment',
72647
- private: [],
72648
- opaque: true,
72649
- version,
72650
- },
72651
- variables: {}
72652
- });
72653
- if (resolvedLink.data !== undefined) {
72894
+ const resolvedLink = resolveLink$1(reader, fieldData, version);
72895
+ if (resolvedLink && resolvedLink.data !== undefined) {
72654
72896
  if (isCursorConnection) {
72655
72897
  selectTypeLinkWithPagination(resolvedLink, sel, fieldData, reader, key, sink, variables, fragments, selectFn);
72656
72898
  }
@@ -73226,19 +73468,11 @@
73226
73468
  }
73227
73469
  case 'PolymorphicParentRelationship':
73228
73470
  case 'RecordRepresentation': {
73229
- const spanningFieldLink = reader.read({
73230
- recordId: fieldData.__ref,
73231
- node: {
73232
- kind: 'Fragment',
73233
- private: [],
73234
- opaque: true,
73235
- version: VERSION$f,
73236
- },
73237
- variables: {},
73238
- });
73239
- reader.markSeenId(fieldData.__ref);
73240
- const resolvedSpanningFieldValue = spanningFieldLink.data;
73471
+ const spanningFieldLink = resolveLink$1(reader, fieldData, VERSION$f);
73472
+ const resolvedSpanningFieldValue = spanningFieldLink && spanningFieldLink.data;
73473
+ const fieldDataRef = fieldData.__ref;
73241
73474
  if (resolvedSpanningFieldValue !== undefined) {
73475
+ reader.markSeenId(fieldDataRef);
73242
73476
  const { value: spanningFieldResult } = resolvedSpanningFieldValue;
73243
73477
  // Handle null values - graphql will return it at the field level, not return nested { value: null }
73244
73478
  if (spanningFieldResult === null || typeof spanningFieldResult !== 'object') {
@@ -73262,7 +73496,9 @@
73262
73496
  }
73263
73497
  }
73264
73498
  else {
73265
- reader.markMissingLink(fieldData.__ref);
73499
+ if (fieldDataRef !== undefined) {
73500
+ reader.markMissingLink(fieldDataRef);
73501
+ }
73266
73502
  reader.markMissing();
73267
73503
  }
73268
73504
  break;
@@ -76298,7 +76534,7 @@
76298
76534
  configuration: { ...configurationForGraphQLAdapters },
76299
76535
  instrument,
76300
76536
  });
76301
- // version: 1.229.0-dev1-5b6d3db67
76537
+ // version: 1.229.0-dev10-abb060196
76302
76538
 
76303
76539
  // On core the unstable adapters are re-exported with different names,
76304
76540
 
@@ -78545,7 +78781,7 @@
78545
78781
  unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
78546
78782
  graphQLImperative = ldsAdapter;
78547
78783
  });
78548
- // version: 1.229.0-dev1-5b6d3db67
78784
+ // version: 1.229.0-dev10-abb060196
78549
78785
 
78550
78786
  var gqlApi = /*#__PURE__*/Object.freeze({
78551
78787
  __proto__: null,
@@ -79276,4 +79512,4 @@
79276
79512
  Object.defineProperty(exports, '__esModule', { value: true });
79277
79513
 
79278
79514
  }));
79279
- // version: 1.229.0-dev1-f69d054a9
79515
+ // version: 1.229.0-dev10-bc9ef2513