@isograph/react 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.turbo/turbo-compile-typescript.log +4 -0
  2. package/dist/core/FragmentReference.d.ts +25 -6
  3. package/dist/core/FragmentReference.d.ts.map +1 -0
  4. package/dist/core/FragmentReference.js +3 -13
  5. package/dist/core/IsographEnvironment.d.ts +34 -26
  6. package/dist/core/IsographEnvironment.d.ts.map +1 -0
  7. package/dist/core/IsographEnvironment.js +19 -22
  8. package/dist/core/PromiseWrapper.d.ts +4 -4
  9. package/dist/core/PromiseWrapper.d.ts.map +1 -0
  10. package/dist/core/PromiseWrapper.js +9 -9
  11. package/dist/core/areEqualWithDeepComparison.d.ts +5 -3
  12. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
  13. package/dist/core/areEqualWithDeepComparison.js +89 -39
  14. package/dist/core/cache.d.ts +20 -13
  15. package/dist/core/cache.d.ts.map +1 -0
  16. package/dist/core/cache.js +205 -128
  17. package/dist/core/check.d.ts +22 -0
  18. package/dist/core/check.d.ts.map +1 -0
  19. package/dist/core/check.js +127 -0
  20. package/dist/core/componentCache.d.ts +2 -2
  21. package/dist/core/componentCache.d.ts.map +1 -0
  22. package/dist/core/componentCache.js +28 -32
  23. package/dist/core/entrypoint.d.ts +31 -15
  24. package/dist/core/entrypoint.d.ts.map +1 -0
  25. package/dist/core/entrypoint.js +1 -2
  26. package/dist/core/garbageCollection.d.ts +6 -5
  27. package/dist/core/garbageCollection.d.ts.map +1 -0
  28. package/dist/core/garbageCollection.js +49 -16
  29. package/dist/core/logging.d.ts +68 -0
  30. package/dist/core/logging.d.ts.map +1 -0
  31. package/dist/core/logging.js +22 -0
  32. package/dist/core/makeNetworkRequest.d.ts +6 -3
  33. package/dist/core/makeNetworkRequest.d.ts.map +1 -0
  34. package/dist/core/makeNetworkRequest.js +160 -19
  35. package/dist/core/read.d.ts +25 -5
  36. package/dist/core/read.d.ts.map +1 -0
  37. package/dist/core/read.js +416 -259
  38. package/dist/core/reader.d.ts +31 -15
  39. package/dist/core/reader.d.ts.map +1 -0
  40. package/dist/core/startUpdate.d.ts +5 -0
  41. package/dist/core/startUpdate.d.ts.map +1 -0
  42. package/dist/core/startUpdate.js +15 -0
  43. package/dist/core/util.d.ts +5 -0
  44. package/dist/core/util.d.ts.map +1 -0
  45. package/dist/index.d.ts +19 -14
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +11 -2
  48. package/dist/loadable-hooks/useClientSideDefer.d.ts +9 -3
  49. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
  50. package/dist/loadable-hooks/useClientSideDefer.js +6 -8
  51. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +27 -0
  52. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
  53. package/dist/loadable-hooks/useConnectionSpecPagination.js +162 -0
  54. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +2 -2
  55. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
  56. package/dist/loadable-hooks/useImperativeExposedMutationField.js +1 -2
  57. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +13 -7
  58. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
  59. package/dist/loadable-hooks/useImperativeLoadableField.js +4 -5
  60. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +13 -26
  61. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
  62. package/dist/loadable-hooks/useSkipLimitPagination.js +93 -47
  63. package/dist/react/FragmentReader.d.ts +6 -4
  64. package/dist/react/FragmentReader.d.ts.map +1 -0
  65. package/dist/react/FragmentReader.js +4 -2
  66. package/dist/react/IsographEnvironmentProvider.d.ts +1 -0
  67. package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
  68. package/dist/react/IsographEnvironmentProvider.js +3 -3
  69. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
  70. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
  71. package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
  72. package/dist/react/useImperativeReference.d.ts +8 -6
  73. package/dist/react/useImperativeReference.d.ts.map +1 -0
  74. package/dist/react/useImperativeReference.js +6 -8
  75. package/dist/react/useLazyReference.d.ts +5 -3
  76. package/dist/react/useLazyReference.d.ts.map +1 -0
  77. package/dist/react/useLazyReference.js +34 -6
  78. package/dist/react/useReadAndSubscribe.d.ts +6 -3
  79. package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
  80. package/dist/react/useReadAndSubscribe.js +13 -10
  81. package/dist/react/useRerenderOnChange.d.ts +7 -2
  82. package/dist/react/useRerenderOnChange.d.ts.map +1 -0
  83. package/dist/react/useRerenderOnChange.js +3 -4
  84. package/dist/react/useResult.d.ts +4 -3
  85. package/dist/react/useResult.d.ts.map +1 -0
  86. package/dist/react/useResult.js +14 -9
  87. package/isograph.config.json +8 -0
  88. package/package.json +14 -9
  89. package/{src/tests/schema.graphql → schema.graphql} +1 -0
  90. package/src/core/FragmentReference.ts +44 -17
  91. package/src/core/IsographEnvironment.ts +67 -50
  92. package/src/core/PromiseWrapper.ts +3 -3
  93. package/src/core/areEqualWithDeepComparison.ts +95 -41
  94. package/src/core/cache.ts +316 -169
  95. package/src/core/check.ts +212 -0
  96. package/src/core/componentCache.ts +40 -46
  97. package/src/core/entrypoint.ts +41 -16
  98. package/src/core/garbageCollection.ts +77 -26
  99. package/src/core/logging.ts +118 -0
  100. package/src/core/makeNetworkRequest.ts +249 -20
  101. package/src/core/read.ts +658 -368
  102. package/src/core/reader.ts +61 -21
  103. package/src/core/startUpdate.ts +28 -0
  104. package/src/core/util.ts +8 -0
  105. package/src/index.ts +94 -8
  106. package/src/loadable-hooks/useClientSideDefer.ts +48 -17
  107. package/src/loadable-hooks/useConnectionSpecPagination.ts +344 -0
  108. package/src/loadable-hooks/useImperativeExposedMutationField.ts +1 -1
  109. package/src/loadable-hooks/useImperativeLoadableField.ts +36 -12
  110. package/src/loadable-hooks/useSkipLimitPagination.ts +253 -94
  111. package/src/react/FragmentReader.tsx +15 -6
  112. package/src/react/IsographEnvironmentProvider.tsx +1 -1
  113. package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
  114. package/src/react/useImperativeReference.ts +50 -18
  115. package/src/react/useLazyReference.ts +79 -11
  116. package/src/react/useReadAndSubscribe.ts +33 -10
  117. package/src/react/useRerenderOnChange.ts +7 -2
  118. package/src/react/useResult.ts +30 -9
  119. package/src/tests/__isograph/Query/meName/entrypoint.ts +10 -29
  120. package/src/tests/__isograph/Query/meName/normalization_ast.ts +25 -0
  121. package/src/tests/__isograph/Query/meName/param_type.ts +5 -2
  122. package/src/tests/__isograph/Query/meName/query_text.ts +6 -0
  123. package/src/tests/__isograph/Query/meName/resolver_reader.ts +5 -0
  124. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +10 -65
  125. package/src/tests/__isograph/Query/meNameSuccessor/normalization_ast.ts +56 -0
  126. package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +9 -6
  127. package/src/tests/__isograph/Query/meNameSuccessor/query_text.ts +13 -0
  128. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +10 -0
  129. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +10 -28
  130. package/src/tests/__isograph/Query/nodeField/normalization_ast.ts +30 -0
  131. package/src/tests/__isograph/Query/nodeField/param_type.ts +7 -3
  132. package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
  133. package/src/tests/__isograph/Query/nodeField/query_text.ts +6 -0
  134. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +5 -0
  135. package/src/tests/__isograph/Query/subquery/entrypoint.ts +28 -0
  136. package/src/tests/__isograph/Query/subquery/normalization_ast.ts +38 -0
  137. package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
  138. package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
  139. package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
  140. package/src/tests/__isograph/Query/subquery/query_text.ts +8 -0
  141. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +52 -0
  142. package/src/tests/__isograph/iso.ts +24 -12
  143. package/src/tests/garbageCollection.test.ts +53 -45
  144. package/src/tests/meNameSuccessor.ts +8 -3
  145. package/src/tests/nodeQuery.ts +7 -4
  146. package/src/tests/normalizeData.test.ts +120 -0
  147. package/src/tests/tsconfig.json +3 -3
  148. package/tsconfig.json +2 -2
  149. package/tsconfig.pkg.json +7 -3
  150. package/vitest.config.ts +20 -0
  151. package/src/tests/isograph.config.json +0 -7
@@ -1,28 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SECOND_SPLIT_KEY = exports.FIRST_SPLIT_KEY = exports.getParentRecordKey = exports.onNextChange = exports.subscribe = exports.subscribeToAnyChange = exports.normalizeData = exports.getOrCreateCacheForArtifact = exports.stableCopy = exports.getOrCreateItemInSuspenseCache = void 0;
3
+ exports.THIRD_SPLIT_KEY = exports.SECOND_SPLIT_KEY = exports.FIRST_SPLIT_KEY = exports.TYPENAME_FIELD_NAME = void 0;
4
+ exports.getOrCreateItemInSuspenseCache = getOrCreateItemInSuspenseCache;
5
+ exports.stableCopy = stableCopy;
6
+ exports.getOrCreateCacheForArtifact = getOrCreateCacheForArtifact;
7
+ exports.normalizeData = normalizeData;
8
+ exports.subscribeToAnyChange = subscribeToAnyChange;
9
+ exports.subscribeToAnyChangesToRecord = subscribeToAnyChangesToRecord;
10
+ exports.subscribe = subscribe;
11
+ exports.onNextChangeToRecord = onNextChangeToRecord;
12
+ exports.insertIfNotExists = insertIfNotExists;
13
+ exports.getParentRecordKey = getParentRecordKey;
4
14
  const react_disposable_state_1 = require("@isograph/react-disposable-state");
5
- const IsographEnvironment_1 = require("./IsographEnvironment");
6
- const read_1 = require("./read");
7
15
  const areEqualWithDeepComparison_1 = require("./areEqualWithDeepComparison");
16
+ const IsographEnvironment_1 = require("./IsographEnvironment");
17
+ const logging_1 = require("./logging");
8
18
  const makeNetworkRequest_1 = require("./makeNetworkRequest");
9
19
  const PromiseWrapper_1 = require("./PromiseWrapper");
10
- const TYPENAME_FIELD_NAME = '__typename';
20
+ const read_1 = require("./read");
21
+ exports.TYPENAME_FIELD_NAME = '__typename';
11
22
  function getOrCreateItemInSuspenseCache(environment, index, factory) {
12
- // @ts-expect-error
13
- if (typeof window !== 'undefined' && window.__LOG) {
14
- console.log('getting cache for', {
15
- index,
16
- cache: Object.keys(environment.fragmentCache),
17
- found: !!environment.fragmentCache[index],
18
- });
19
- }
20
23
  if (environment.fragmentCache[index] == null) {
21
24
  environment.fragmentCache[index] = new react_disposable_state_1.ParentCache(factory);
22
25
  }
23
26
  return environment.fragmentCache[index];
24
27
  }
25
- exports.getOrCreateItemInSuspenseCache = getOrCreateItemInSuspenseCache;
26
28
  /**
27
29
  * Creates a copy of the provided value, ensuring any nested objects have their
28
30
  * keys sorted such that equivalent values would have identical JSON.stringify
@@ -44,11 +46,11 @@ function stableCopy(value) {
44
46
  }
45
47
  return stable;
46
48
  }
47
- exports.stableCopy = stableCopy;
48
- function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
49
- const cacheKey = entrypoint.queryText + JSON.stringify(stableCopy(variables));
49
+ function getOrCreateCacheForArtifact(environment, entrypoint, variables, fetchOptions) {
50
+ const cacheKey = entrypoint.networkRequestInfo.queryText +
51
+ JSON.stringify(stableCopy(variables));
50
52
  const factory = () => {
51
- const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, entrypoint, variables);
53
+ const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.maybeMakeNetworkRequest)(environment, entrypoint, variables, fetchOptions);
52
54
  const itemCleanupPair = [
53
55
  {
54
56
  kind: 'FragmentReference',
@@ -57,7 +59,7 @@ function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
57
59
  readerArtifact: entrypoint.readerWithRefetchQueries.readerArtifact,
58
60
  nestedRefetchQueries: entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
59
61
  }),
60
- root: IsographEnvironment_1.ROOT_ID,
62
+ root: { __link: IsographEnvironment_1.ROOT_ID, __typename: entrypoint.concreteType },
61
63
  variables,
62
64
  networkRequest: networkRequest,
63
65
  },
@@ -67,26 +69,27 @@ function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
67
69
  };
68
70
  return getOrCreateItemInSuspenseCache(environment, cacheKey, factory);
69
71
  }
70
- exports.getOrCreateCacheForArtifact = getOrCreateCacheForArtifact;
71
- function normalizeData(environment, normalizationAst, networkResponse, variables, nestedRefetchQueries) {
72
- const encounteredIds = new Set();
73
- // @ts-expect-error
74
- if (typeof window !== 'undefined' && window.__LOG) {
75
- console.log('about to normalize', normalizationAst, networkResponse, variables);
76
- }
77
- normalizeDataIntoRecord(environment, normalizationAst, networkResponse, environment.store.__ROOT, IsographEnvironment_1.ROOT_ID, variables, nestedRefetchQueries, encounteredIds);
78
- // @ts-expect-error
79
- if (typeof window !== 'undefined' && window.__LOG) {
80
- console.log('after normalization', {
81
- store: environment.store,
82
- encounteredIds,
83
- environment,
84
- });
85
- }
72
+ function normalizeData(environment, normalizationAst, networkResponse, variables, nestedRefetchQueries, root) {
73
+ var _a, _b;
74
+ var _c, _d, _e;
75
+ const encounteredIds = new Map();
76
+ (0, logging_1.logMessage)(environment, () => ({
77
+ kind: 'AboutToNormalize',
78
+ normalizationAst,
79
+ networkResponse,
80
+ variables,
81
+ }));
82
+ const recordsById = ((_a = (_c = environment.store)[_d = root.__typename]) !== null && _a !== void 0 ? _a : (_c[_d] = {}));
83
+ const newStoreRecord = ((_b = recordsById[_e = root.__link]) !== null && _b !== void 0 ? _b : (recordsById[_e] = {}));
84
+ normalizeDataIntoRecord(environment, normalizationAst, networkResponse, newStoreRecord, root, variables, nestedRefetchQueries, encounteredIds);
85
+ (0, logging_1.logMessage)(environment, () => ({
86
+ kind: 'AfterNormalization',
87
+ store: environment.store,
88
+ encounteredIds,
89
+ }));
86
90
  callSubscriptions(environment, encounteredIds);
87
91
  return encounteredIds;
88
92
  }
89
- exports.normalizeData = normalizeData;
90
93
  function subscribeToAnyChange(environment, callback) {
91
94
  const subscription = {
92
95
  kind: 'AnyRecords',
@@ -95,30 +98,35 @@ function subscribeToAnyChange(environment, callback) {
95
98
  environment.subscriptions.add(subscription);
96
99
  return () => environment.subscriptions.delete(subscription);
97
100
  }
98
- exports.subscribeToAnyChange = subscribeToAnyChange;
101
+ function subscribeToAnyChangesToRecord(environment, recordLink, callback) {
102
+ const subscription = {
103
+ kind: 'AnyChangesToRecord',
104
+ recordLink,
105
+ callback,
106
+ };
107
+ environment.subscriptions.add(subscription);
108
+ return () => environment.subscriptions.delete(subscription);
109
+ }
99
110
  // TODO we should re-read and call callback if the value has changed
100
- function subscribe(environment, encounteredDataAndRecords, fragmentReference, callback) {
111
+ function subscribe(environment, encounteredDataAndRecords, fragmentReference, callback, readerAst) {
101
112
  const fragmentSubscription = {
102
113
  kind: 'FragmentSubscription',
103
114
  callback,
104
115
  encounteredDataAndRecords,
105
116
  fragmentReference,
117
+ readerAst,
106
118
  };
107
- // @ts-expect-error
108
119
  environment.subscriptions.add(fragmentSubscription);
109
- // @ts-expect-error
110
120
  return () => environment.subscriptions.delete(fragmentSubscription);
111
121
  }
112
- exports.subscribe = subscribe;
113
- function onNextChange(environment) {
122
+ function onNextChangeToRecord(environment, recordLink) {
114
123
  return new Promise((resolve) => {
115
- const unsubscribe = subscribeToAnyChange(environment, () => {
124
+ const unsubscribe = subscribeToAnyChangesToRecord(environment, recordLink, () => {
116
125
  unsubscribe();
117
126
  resolve();
118
127
  });
119
128
  });
120
129
  }
121
- exports.onNextChange = onNextChange;
122
130
  // Calls to readButDoNotEvaluate can suspend (i.e. throw a promise).
123
131
  // Maybe in the future, they will be able to throw errors.
124
132
  //
@@ -134,6 +142,7 @@ function withErrorHandling(f) {
134
142
  }
135
143
  function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
136
144
  environment.subscriptions.forEach(withErrorHandling((subscription) => {
145
+ var _a;
137
146
  switch (subscription.kind) {
138
147
  case 'FragmentSubscription': {
139
148
  // TODO if there are multiple components subscribed to the same
@@ -157,32 +166,30 @@ function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
157
166
  suspendIfInFlight: false,
158
167
  throwOnNetworkError: false,
159
168
  });
160
- if (!(0, areEqualWithDeepComparison_1.areEqualObjectsWithDeepComparison)(subscription.encounteredDataAndRecords.item, newEncounteredDataAndRecords.item)) {
161
- // @ts-expect-error
162
- if (typeof window !== 'undefined' && window.__LOG) {
163
- console.log('Deep equality - No', {
164
- fragmentReference: subscription.fragmentReference,
165
- old: subscription.encounteredDataAndRecords.item,
166
- new: newEncounteredDataAndRecords.item,
167
- });
168
- }
169
- // TODO deep compare values
169
+ const mergedItem = (0, areEqualWithDeepComparison_1.mergeObjectsUsingReaderAst)(subscription.readerAst, subscription.encounteredDataAndRecords.item, newEncounteredDataAndRecords.item);
170
+ (0, logging_1.logMessage)(environment, () => ({
171
+ kind: 'DeepEqualityCheck',
172
+ fragmentReference: subscription.fragmentReference,
173
+ old: subscription.encounteredDataAndRecords.item,
174
+ new: newEncounteredDataAndRecords.item,
175
+ deeplyEqual: mergedItem === subscription.encounteredDataAndRecords.item,
176
+ }));
177
+ if (mergedItem !== subscription.encounteredDataAndRecords.item) {
170
178
  subscription.callback(newEncounteredDataAndRecords);
171
179
  }
172
- else {
173
- // @ts-expect-error
174
- if (typeof window !== 'undefined' && window.__LOG) {
175
- console.log('Deep equality - Yes', {
176
- fragmentReference: subscription.fragmentReference,
177
- old: subscription.encounteredDataAndRecords.item,
178
- });
179
- }
180
- }
181
180
  }
182
181
  return;
183
182
  }
184
183
  case 'AnyRecords': {
185
- return subscription.callback();
184
+ subscription.callback();
185
+ return;
186
+ }
187
+ case 'AnyChangesToRecord': {
188
+ if ((_a = recordsEncounteredWhenNormalizing
189
+ .get(subscription.recordLink.__typename)) === null || _a === void 0 ? void 0 : _a.has(subscription.recordLink.__link)) {
190
+ subscription.callback();
191
+ }
192
+ return;
186
193
  }
187
194
  default: {
188
195
  // Ensure we have covered all variants
@@ -193,7 +200,20 @@ function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
193
200
  }
194
201
  }));
195
202
  }
196
- function hasOverlappingIds(set1, set2) {
203
+ function hasOverlappingIds(ids1, ids2) {
204
+ for (const [typeName, set1] of ids1.entries()) {
205
+ const set2 = ids2.get(typeName);
206
+ if (set2 === undefined) {
207
+ continue;
208
+ }
209
+ if (isNotDisjointFrom(set1, set2)) {
210
+ return true;
211
+ }
212
+ }
213
+ return false;
214
+ }
215
+ // TODO use a polyfill library
216
+ function isNotDisjointFrom(set1, set2) {
197
217
  for (const id of set1) {
198
218
  if (set2.has(id)) {
199
219
  return true;
@@ -204,7 +224,7 @@ function hasOverlappingIds(set1, set2) {
204
224
  /**
205
225
  * Mutate targetParentRecord according to the normalizationAst and networkResponseParentRecord.
206
226
  */
207
- function normalizeDataIntoRecord(environment, normalizationAst, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
227
+ function normalizeDataIntoRecord(environment, normalizationAst, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds) {
208
228
  let recordHasBeenUpdated = false;
209
229
  for (const normalizationNode of normalizationAst) {
210
230
  switch (normalizationNode.kind) {
@@ -215,13 +235,13 @@ function normalizeDataIntoRecord(environment, normalizationAst, networkResponseP
215
235
  break;
216
236
  }
217
237
  case 'Linked': {
218
- const linkedFieldResultedInChange = normalizeLinkedField(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
238
+ const linkedFieldResultedInChange = normalizeLinkedField(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds);
219
239
  recordHasBeenUpdated =
220
240
  recordHasBeenUpdated || linkedFieldResultedInChange;
221
241
  break;
222
242
  }
223
243
  case 'InlineFragment': {
224
- const inlineFragmentResultedInChange = normalizeInlineFragment(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
244
+ const inlineFragmentResultedInChange = normalizeInlineFragment(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds);
225
245
  recordHasBeenUpdated =
226
246
  recordHasBeenUpdated || inlineFragmentResultedInChange;
227
247
  break;
@@ -235,10 +255,19 @@ function normalizeDataIntoRecord(environment, normalizationAst, networkResponseP
235
255
  }
236
256
  }
237
257
  if (recordHasBeenUpdated) {
238
- mutableEncounteredIds.add(targetParentRecordId);
258
+ let encounteredRecordsIds = insertIfNotExists(mutableEncounteredIds, targetParentRecordLink.__typename);
259
+ encounteredRecordsIds.add(targetParentRecordLink.__link);
239
260
  }
240
261
  return recordHasBeenUpdated;
241
262
  }
263
+ function insertIfNotExists(map, key) {
264
+ let result = map.get(key);
265
+ if (result === undefined) {
266
+ result = new Set();
267
+ map.set(key, result);
268
+ }
269
+ return result;
270
+ }
242
271
  function normalizeScalarField(astNode, networkResponseParentRecord, targetStoreRecord, variables) {
243
272
  const networkResponseKey = getNetworkResponseKey(astNode);
244
273
  const networkResponseData = networkResponseParentRecord[networkResponseKey];
@@ -256,7 +285,8 @@ function normalizeScalarField(astNode, networkResponseParentRecord, targetStoreR
256
285
  /**
257
286
  * Mutate targetParentRecord with a given linked field ast node.
258
287
  */
259
- function normalizeLinkedField(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
288
+ function normalizeLinkedField(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds) {
289
+ var _a, _b;
260
290
  const networkResponseKey = getNetworkResponseKey(astNode);
261
291
  const networkResponseData = networkResponseParentRecord[networkResponseKey];
262
292
  const parentRecordKey = getParentRecordKey(astNode, variables);
@@ -265,7 +295,8 @@ function normalizeLinkedField(environment, astNode, networkResponseParentRecord,
265
295
  targetParentRecord[parentRecordKey] = null;
266
296
  return existingValue !== null;
267
297
  }
268
- if (isScalarButNotEmptyArray(networkResponseData)) {
298
+ if (isScalarOrEmptyArray(networkResponseData) &&
299
+ !isNullOrEmptyArray(networkResponseData)) {
269
300
  throw new Error('Unexpected scalar network response when normalizing a linked field');
270
301
  }
271
302
  if (Array.isArray(networkResponseData)) {
@@ -273,43 +304,61 @@ function normalizeLinkedField(environment, astNode, networkResponseParentRecord,
273
304
  const dataIds = [];
274
305
  for (let i = 0; i < networkResponseData.length; i++) {
275
306
  const networkResponseObject = networkResponseData[i];
276
- const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseObject, targetParentRecordId, variables, i, nestedRefetchQueries, mutableEncounteredIds);
277
- dataIds.push({ __link: newStoreRecordId });
307
+ if (networkResponseObject == null) {
308
+ dataIds.push(null);
309
+ continue;
310
+ }
311
+ const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseObject, targetParentRecordLink, variables, i, nestedRefetchQueries, mutableEncounteredIds);
312
+ const __typename = (_a = astNode.concreteType) !== null && _a !== void 0 ? _a : networkResponseObject[exports.TYPENAME_FIELD_NAME];
313
+ if (__typename == null) {
314
+ throw new Error('Unexpected missing __typename in network response when normalizing a linked field. ' +
315
+ 'This is indicative of a bug in Isograph.');
316
+ }
317
+ dataIds.push({
318
+ __link: newStoreRecordId,
319
+ __typename,
320
+ });
278
321
  }
279
322
  targetParentRecord[parentRecordKey] = dataIds;
280
323
  return !dataIdsAreTheSame(existingValue, dataIds);
281
324
  }
282
325
  else {
283
- const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordId, variables, null, nestedRefetchQueries, mutableEncounteredIds);
326
+ const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordLink, variables, null, nestedRefetchQueries, mutableEncounteredIds);
327
+ let __typename = (_b = astNode.concreteType) !== null && _b !== void 0 ? _b : networkResponseData[exports.TYPENAME_FIELD_NAME];
328
+ if (__typename == null) {
329
+ throw new Error('Unexpected missing __typename in network response when normalizing a linked field. ' +
330
+ 'This is indicative of a bug in Isograph.');
331
+ }
284
332
  targetParentRecord[parentRecordKey] = {
285
333
  __link: newStoreRecordId,
334
+ __typename,
286
335
  };
287
336
  const link = (0, IsographEnvironment_1.getLink)(existingValue);
288
- return (link === null || link === void 0 ? void 0 : link.__link) !== newStoreRecordId;
337
+ return (link === null || link === void 0 ? void 0 : link.__link) !== newStoreRecordId || link.__typename !== __typename;
289
338
  }
290
339
  }
291
340
  /**
292
341
  * Mutate targetParentRecord with a given linked field ast node.
293
342
  */
294
- function normalizeInlineFragment(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
343
+ function normalizeInlineFragment(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds) {
295
344
  const typeToRefineTo = astNode.type;
296
- if (networkResponseParentRecord[TYPENAME_FIELD_NAME] === typeToRefineTo) {
297
- const hasBeenModified = normalizeDataIntoRecord(environment, astNode.selections, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
345
+ if (networkResponseParentRecord[exports.TYPENAME_FIELD_NAME] === typeToRefineTo) {
346
+ const hasBeenModified = normalizeDataIntoRecord(environment, astNode.selections, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds);
298
347
  return hasBeenModified;
299
348
  }
300
349
  return false;
301
350
  }
302
351
  function dataIdsAreTheSame(existingValue, newDataIds) {
352
+ var _a, _b;
303
353
  if (Array.isArray(existingValue)) {
304
354
  if (newDataIds.length !== existingValue.length) {
305
355
  return false;
306
356
  }
307
357
  for (let i = 0; i < newDataIds.length; i++) {
308
358
  const maybeLink = (0, IsographEnvironment_1.getLink)(existingValue[i]);
309
- if (maybeLink !== null) {
310
- if (newDataIds[i].__link !== maybeLink.__link) {
311
- return false;
312
- }
359
+ if (((_a = newDataIds[i]) === null || _a === void 0 ? void 0 : _a.__link) !== (maybeLink === null || maybeLink === void 0 ? void 0 : maybeLink.__link) ||
360
+ ((_b = newDataIds[i]) === null || _b === void 0 ? void 0 : _b.__typename) !== (maybeLink === null || maybeLink === void 0 ? void 0 : maybeLink.__typename)) {
361
+ return false;
313
362
  }
314
363
  }
315
364
  return true;
@@ -318,12 +367,18 @@ function dataIdsAreTheSame(existingValue, newDataIds) {
318
367
  return false;
319
368
  }
320
369
  }
321
- function normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordId, variables, index, nestedRefetchQueries, mutableEncounteredIds) {
322
- var _a;
323
- const newStoreRecordId = getDataIdOfNetworkResponse(targetParentRecordId, networkResponseData, astNode, variables, index);
324
- const newStoreRecord = (_a = environment.store[newStoreRecordId]) !== null && _a !== void 0 ? _a : {};
325
- environment.store[newStoreRecordId] = newStoreRecord;
326
- normalizeDataIntoRecord(environment, astNode.selections, networkResponseData, newStoreRecord, newStoreRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
370
+ function normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordLink, variables, index, nestedRefetchQueries, mutableEncounteredIds) {
371
+ var _a, _b, _c;
372
+ var _d;
373
+ const newStoreRecordId = getDataIdOfNetworkResponse(targetParentRecordLink, networkResponseData, astNode, variables, index);
374
+ const __typename = (_a = astNode.concreteType) !== null && _a !== void 0 ? _a : networkResponseData[exports.TYPENAME_FIELD_NAME];
375
+ if (__typename == null) {
376
+ throw new Error('Unexpected missing __typename in network response object. ' +
377
+ 'This is indicative of a bug in Isograph.');
378
+ }
379
+ const recordsById = ((_b = (_d = environment.store)[__typename]) !== null && _b !== void 0 ? _b : (_d[__typename] = {}));
380
+ const newStoreRecord = ((_c = recordsById[newStoreRecordId]) !== null && _c !== void 0 ? _c : (recordsById[newStoreRecordId] = {}));
381
+ normalizeDataIntoRecord(environment, astNode.selections, networkResponseData, newStoreRecord, { __link: newStoreRecordId, __typename: __typename }, variables, nestedRefetchQueries, mutableEncounteredIds);
327
382
  return newStoreRecordId;
328
383
  }
329
384
  function isScalarOrEmptyArray(data) {
@@ -332,24 +387,20 @@ function isScalarOrEmptyArray(data) {
332
387
  // This is maybe fixed in a new version of Typescript??
333
388
  return data.every((x) => isScalarOrEmptyArray(x));
334
389
  }
335
- const isScalarValue = typeof data === 'string' ||
390
+ const isScalarValue = data === null ||
391
+ typeof data === 'string' ||
336
392
  typeof data === 'number' ||
337
393
  typeof data === 'boolean';
338
394
  return isScalarValue;
339
395
  }
340
- function isScalarButNotEmptyArray(data) {
341
- // N.B. empty arrays count as empty arrays of linked fields.
396
+ function isNullOrEmptyArray(data) {
342
397
  if (Array.isArray(data)) {
343
398
  if (data.length === 0) {
344
- return false;
399
+ return true;
345
400
  }
346
- // This is maybe fixed in a new version of Typescript??
347
- return data.every((x) => isScalarOrEmptyArray(x));
401
+ return data.every((x) => isNullOrEmptyArray(x));
348
402
  }
349
- const isScalarValue = typeof data === 'string' ||
350
- typeof data === 'number' ||
351
- typeof data === 'boolean';
352
- return isScalarValue;
403
+ return data === null;
353
404
  }
354
405
  function getParentRecordKey(astNode, variables) {
355
406
  let parentRecordKey = astNode.fieldName;
@@ -361,10 +412,18 @@ function getParentRecordKey(astNode, variables) {
361
412
  }
362
413
  return parentRecordKey;
363
414
  }
364
- exports.getParentRecordKey = getParentRecordKey;
365
415
  function getStoreKeyChunkForArgumentValue(argumentValue, variables) {
366
416
  var _a;
367
417
  switch (argumentValue.kind) {
418
+ case 'Object': {
419
+ return Object.fromEntries(argumentValue.value.map(([argumentName, argumentValue]) => {
420
+ return [
421
+ argumentName,
422
+ // substitute variables
423
+ getStoreKeyChunkForArgumentValue(argumentValue, variables),
424
+ ];
425
+ }));
426
+ }
368
427
  case 'Literal': {
369
428
  return argumentValue.value;
370
429
  }
@@ -387,57 +446,75 @@ function getStoreKeyChunkForArgumentValue(argumentValue, variables) {
387
446
  }
388
447
  }
389
448
  function getStoreKeyChunkForArgument(argument, variables) {
390
- const chunk = getStoreKeyChunkForArgumentValue(argument[1], variables);
449
+ let chunk = getStoreKeyChunkForArgumentValue(argument[1], variables);
450
+ if (typeof chunk === 'object') {
451
+ chunk = JSON.stringify(stableCopy(chunk));
452
+ }
391
453
  return `${exports.FIRST_SPLIT_KEY}${argument[0]}${exports.SECOND_SPLIT_KEY}${chunk}`;
392
454
  }
393
455
  function getNetworkResponseKey(astNode) {
394
456
  let networkResponseKey = astNode.fieldName;
395
457
  const fieldParameters = astNode.arguments;
396
458
  if (fieldParameters != null) {
397
- for (const fieldParameter of fieldParameters) {
398
- const [argumentName, argumentValue] = fieldParameter;
399
- let argumentValueChunk;
400
- switch (argumentValue.kind) {
401
- case 'Literal': {
402
- argumentValueChunk = 'l_' + argumentValue.value;
403
- break;
404
- }
405
- case 'Variable': {
406
- argumentValueChunk = 'v_' + argumentValue.name;
407
- break;
408
- }
409
- case 'String': {
410
- argumentValueChunk = 's_' + argumentValue.value;
411
- break;
412
- }
413
- case 'Enum': {
414
- argumentValueChunk = 'e_' + argumentValue.value;
415
- break;
416
- }
417
- default: {
418
- // Ensure we have covered all variants
419
- let _ = argumentValue;
420
- _;
421
- throw new Error('Unexpected case');
422
- }
423
- }
459
+ for (const [argumentName, argumentValue] of fieldParameters) {
460
+ let argumentValueChunk = getArgumentValueChunk(argumentValue);
424
461
  networkResponseKey += `${exports.FIRST_SPLIT_KEY}${argumentName}${exports.SECOND_SPLIT_KEY}${argumentValueChunk}`;
425
462
  }
426
463
  }
427
464
  return networkResponseKey;
428
465
  }
466
+ function getArgumentValueChunk(argumentValue) {
467
+ switch (argumentValue.kind) {
468
+ case 'Object': {
469
+ return ('o_' +
470
+ argumentValue.value
471
+ .map(([argumentName, argumentValue]) => {
472
+ return (argumentName +
473
+ exports.THIRD_SPLIT_KEY +
474
+ getArgumentValueChunk(argumentValue));
475
+ })
476
+ .join('_') +
477
+ '_c');
478
+ }
479
+ case 'Literal': {
480
+ return 'l_' + argumentValue.value;
481
+ }
482
+ case 'Variable': {
483
+ return 'v_' + argumentValue.name;
484
+ }
485
+ case 'String': {
486
+ // replace all non-word characters (alphanumeric & underscore) with underscores
487
+ return 's_' + argumentValue.value.replaceAll(/\W/g, '_');
488
+ }
489
+ case 'Enum': {
490
+ return 'e_' + argumentValue.value;
491
+ }
492
+ default: {
493
+ // Ensure we have covered all variants
494
+ let _ = argumentValue;
495
+ _;
496
+ throw new Error('Unexpected case');
497
+ }
498
+ }
499
+ }
429
500
  // an alias might be pullRequests____first___first____after___cursor
430
501
  exports.FIRST_SPLIT_KEY = '____';
431
502
  exports.SECOND_SPLIT_KEY = '___';
503
+ exports.THIRD_SPLIT_KEY = '__';
432
504
  // Returns a key to look up an item in the store
433
- function getDataIdOfNetworkResponse(parentRecordId, dataToNormalize, astNode, variables, index) {
505
+ function getDataIdOfNetworkResponse(parentRecordLink, dataToNormalize, astNode, variables, index) {
506
+ // If we are dealing with nested Query, use __ROOT as id
507
+ // TODO do not hard code this value here
508
+ if (astNode.concreteType === 'Query') {
509
+ return IsographEnvironment_1.ROOT_ID;
510
+ }
434
511
  // Check whether the dataToNormalize has an id field. If so, that is the key.
435
512
  // If not, we construct an id from the parentRecordId and the field parameters.
436
513
  const dataId = dataToNormalize.id;
437
514
  if (dataId != null) {
438
515
  return dataId;
439
516
  }
440
- let storeKey = `${parentRecordId}.${astNode.fieldName}`;
517
+ let storeKey = `${parentRecordLink.__typename}:${parentRecordLink.__link}.${astNode.fieldName}`;
441
518
  if (index != null) {
442
519
  storeKey += `.${index}`;
443
520
  }
@@ -0,0 +1,22 @@
1
+ import { NormalizationAstNodes } from './entrypoint';
2
+ import { Variables } from './FragmentReference';
3
+ import { IsographEnvironment, Link } from './IsographEnvironment';
4
+ export type ShouldFetch = RequiredShouldFetch | 'IfNecessary';
5
+ export type RequiredShouldFetch = 'Yes' | 'No';
6
+ export declare const DEFAULT_SHOULD_FETCH_VALUE: ShouldFetch;
7
+ export type FetchOptions<TReadOutData> = {
8
+ shouldFetch?: ShouldFetch;
9
+ onComplete?: (data: TReadOutData) => void;
10
+ onError?: () => void;
11
+ };
12
+ export type RequiredFetchOptions<TReadOutData> = {
13
+ shouldFetch: RequiredShouldFetch;
14
+ } & FetchOptions<TReadOutData>;
15
+ export type CheckResult = {
16
+ kind: 'EnoughData';
17
+ } | {
18
+ kind: 'MissingData';
19
+ record: Link;
20
+ };
21
+ export declare function check(environment: IsographEnvironment, normalizationAst: NormalizationAstNodes, variables: Variables, root: Link): CheckResult;
22
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/core/check.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAEL,mBAAmB,EACnB,IAAI,EAEL,MAAM,uBAAuB,CAAC;AAG/B,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,aAAa,CAAC;AAC9D,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,IAAI,CAAC;AAE/C,eAAO,MAAM,0BAA0B,EAAE,WAA2B,CAAC;AAErE,MAAM,MAAM,YAAY,CAAC,YAAY,IAAI;IACvC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,YAAY,IAAI;IAC/C,WAAW,EAAE,mBAAmB,CAAC;CAClC,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAE/B,MAAM,MAAM,WAAW,GACnB;IACE,IAAI,EAAE,YAAY,CAAC;CACpB,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,IAAI,CAAC;CACd,CAAC;AAEN,wBAAgB,KAAK,CACnB,WAAW,EAAE,mBAAmB,EAChC,gBAAgB,EAAE,qBAAqB,EACvC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,GACT,WAAW,CAgBb"}