@isograph/react 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/core/FragmentReference.d.ts +14 -4
  2. package/dist/core/FragmentReference.d.ts.map +1 -0
  3. package/dist/core/FragmentReference.js +2 -3
  4. package/dist/core/IsographEnvironment.d.ts +28 -10
  5. package/dist/core/IsographEnvironment.d.ts.map +1 -0
  6. package/dist/core/IsographEnvironment.js +15 -22
  7. package/dist/core/PromiseWrapper.d.ts +1 -0
  8. package/dist/core/PromiseWrapper.d.ts.map +1 -0
  9. package/dist/core/PromiseWrapper.js +4 -5
  10. package/dist/core/areEqualWithDeepComparison.d.ts +5 -3
  11. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
  12. package/dist/core/areEqualWithDeepComparison.js +73 -39
  13. package/dist/core/cache.d.ts +26 -10
  14. package/dist/core/cache.d.ts.map +1 -0
  15. package/dist/core/cache.js +160 -98
  16. package/dist/core/check.d.ts +18 -0
  17. package/dist/core/check.d.ts.map +1 -0
  18. package/dist/core/check.js +127 -0
  19. package/dist/core/componentCache.d.ts +1 -1
  20. package/dist/core/componentCache.d.ts.map +1 -0
  21. package/dist/core/componentCache.js +14 -14
  22. package/dist/core/entrypoint.d.ts +27 -8
  23. package/dist/core/entrypoint.d.ts.map +1 -0
  24. package/dist/core/entrypoint.js +1 -2
  25. package/dist/core/garbageCollection.d.ts +3 -1
  26. package/dist/core/garbageCollection.d.ts.map +1 -0
  27. package/dist/core/garbageCollection.js +48 -15
  28. package/dist/core/logging.d.ts +69 -0
  29. package/dist/core/logging.d.ts.map +1 -0
  30. package/dist/core/logging.js +19 -0
  31. package/dist/core/makeNetworkRequest.d.ts +4 -1
  32. package/dist/core/makeNetworkRequest.d.ts.map +1 -0
  33. package/dist/core/makeNetworkRequest.js +71 -15
  34. package/dist/core/read.d.ts +20 -5
  35. package/dist/core/read.d.ts.map +1 -0
  36. package/dist/core/read.js +104 -41
  37. package/dist/core/reader.d.ts +34 -10
  38. package/dist/core/reader.d.ts.map +1 -0
  39. package/dist/core/util.d.ts +2 -0
  40. package/dist/core/util.d.ts.map +1 -0
  41. package/dist/index.d.ts +10 -5
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +10 -2
  44. package/dist/loadable-hooks/useClientSideDefer.d.ts +15 -3
  45. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
  46. package/dist/loadable-hooks/useClientSideDefer.js +4 -6
  47. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +34 -0
  48. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
  49. package/dist/loadable-hooks/useConnectionSpecPagination.js +160 -0
  50. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +1 -0
  51. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
  52. package/dist/loadable-hooks/useImperativeExposedMutationField.js +1 -2
  53. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +13 -5
  54. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
  55. package/dist/loadable-hooks/useImperativeLoadableField.js +3 -4
  56. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +18 -24
  57. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
  58. package/dist/loadable-hooks/useSkipLimitPagination.js +88 -44
  59. package/dist/react/FragmentReader.d.ts +7 -4
  60. package/dist/react/FragmentReader.d.ts.map +1 -0
  61. package/dist/react/FragmentReader.js +4 -2
  62. package/dist/react/IsographEnvironmentProvider.d.ts +1 -0
  63. package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
  64. package/dist/react/IsographEnvironmentProvider.js +3 -3
  65. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
  66. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
  67. package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
  68. package/dist/react/useImperativeReference.d.ts +8 -3
  69. package/dist/react/useImperativeReference.d.ts.map +1 -0
  70. package/dist/react/useImperativeReference.js +4 -5
  71. package/dist/react/useLazyReference.d.ts +7 -2
  72. package/dist/react/useLazyReference.d.ts.map +1 -0
  73. package/dist/react/useLazyReference.js +11 -4
  74. package/dist/react/useReadAndSubscribe.d.ts +12 -3
  75. package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
  76. package/dist/react/useReadAndSubscribe.js +6 -7
  77. package/dist/react/useRerenderOnChange.d.ts +6 -1
  78. package/dist/react/useRerenderOnChange.d.ts.map +1 -0
  79. package/dist/react/useRerenderOnChange.js +3 -4
  80. package/dist/react/useResult.d.ts +5 -1
  81. package/dist/react/useResult.d.ts.map +1 -0
  82. package/dist/react/useResult.js +8 -5
  83. package/{src/tests/isograph.config.json → isograph.config.json} +1 -1
  84. package/package.json +12 -8
  85. package/{src/tests/schema.graphql → schema.graphql} +1 -0
  86. package/src/core/FragmentReference.ts +17 -5
  87. package/src/core/IsographEnvironment.ts +38 -29
  88. package/src/core/areEqualWithDeepComparison.ts +76 -42
  89. package/src/core/cache.ts +237 -123
  90. package/src/core/check.ts +207 -0
  91. package/src/core/componentCache.ts +18 -17
  92. package/src/core/entrypoint.ts +15 -8
  93. package/src/core/garbageCollection.ts +71 -20
  94. package/src/core/logging.ts +116 -0
  95. package/src/core/makeNetworkRequest.ts +89 -13
  96. package/src/core/read.ts +162 -55
  97. package/src/core/reader.ts +40 -13
  98. package/src/core/util.ts +4 -0
  99. package/src/index.ts +14 -1
  100. package/src/loadable-hooks/useClientSideDefer.ts +45 -15
  101. package/src/loadable-hooks/useConnectionSpecPagination.ts +331 -0
  102. package/src/loadable-hooks/useImperativeLoadableField.ts +36 -10
  103. package/src/loadable-hooks/useSkipLimitPagination.ts +231 -90
  104. package/src/react/FragmentReader.tsx +13 -4
  105. package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
  106. package/src/react/useImperativeReference.ts +18 -7
  107. package/src/react/useLazyReference.ts +24 -4
  108. package/src/react/useReadAndSubscribe.ts +20 -5
  109. package/src/react/useRerenderOnChange.ts +6 -1
  110. package/src/react/useResult.ts +10 -2
  111. package/src/tests/__isograph/Query/meName/entrypoint.ts +7 -2
  112. package/src/tests/__isograph/Query/meName/param_type.ts +5 -2
  113. package/src/tests/__isograph/Query/meName/resolver_reader.ts +1 -0
  114. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +9 -2
  115. package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +9 -6
  116. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +3 -0
  117. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +13 -2
  118. package/src/tests/__isograph/Query/nodeField/param_type.ts +7 -3
  119. package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
  120. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +1 -0
  121. package/src/tests/__isograph/Query/subquery/entrypoint.ts +67 -0
  122. package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
  123. package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
  124. package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
  125. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +47 -0
  126. package/src/tests/__isograph/iso.ts +22 -11
  127. package/src/tests/garbageCollection.test.ts +45 -39
  128. package/src/tests/meNameSuccessor.ts +8 -3
  129. package/src/tests/nodeQuery.ts +6 -4
  130. package/src/tests/normalizeData.test.ts +120 -0
  131. package/src/tests/tsconfig.json +3 -3
  132. package/tsconfig.json +2 -2
  133. package/tsconfig.pkg.json +6 -1
  134. package/vitest.config.ts +20 -0
@@ -1,28 +1,37 @@
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.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
15
  const IsographEnvironment_1 = require("./IsographEnvironment");
6
16
  const read_1 = require("./read");
7
17
  const areEqualWithDeepComparison_1 = require("./areEqualWithDeepComparison");
8
18
  const makeNetworkRequest_1 = require("./makeNetworkRequest");
9
19
  const PromiseWrapper_1 = require("./PromiseWrapper");
10
- const TYPENAME_FIELD_NAME = '__typename';
20
+ const logging_1 = require("./logging");
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
- }
23
+ // TODO this is probably a useless message, we should remove it
24
+ (0, logging_1.logMessage)(environment, {
25
+ kind: 'GettingSuspenseCacheItem',
26
+ index,
27
+ availableCacheItems: Object.keys(environment.fragmentCache),
28
+ found: !!environment.fragmentCache[index],
29
+ });
20
30
  if (environment.fragmentCache[index] == null) {
21
31
  environment.fragmentCache[index] = new react_disposable_state_1.ParentCache(factory);
22
32
  }
23
33
  return environment.fragmentCache[index];
24
34
  }
25
- exports.getOrCreateItemInSuspenseCache = getOrCreateItemInSuspenseCache;
26
35
  /**
27
36
  * Creates a copy of the provided value, ensuring any nested objects have their
28
37
  * keys sorted such that equivalent values would have identical JSON.stringify
@@ -44,11 +53,11 @@ function stableCopy(value) {
44
53
  }
45
54
  return stable;
46
55
  }
47
- exports.stableCopy = stableCopy;
48
- function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
49
- const cacheKey = entrypoint.queryText + JSON.stringify(stableCopy(variables));
56
+ function getOrCreateCacheForArtifact(environment, entrypoint, variables, fetchOptions) {
57
+ const cacheKey = entrypoint.networkRequestInfo.queryText +
58
+ JSON.stringify(stableCopy(variables));
50
59
  const factory = () => {
51
- const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, entrypoint, variables);
60
+ const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.maybeMakeNetworkRequest)(environment, entrypoint, variables, fetchOptions);
52
61
  const itemCleanupPair = [
53
62
  {
54
63
  kind: 'FragmentReference',
@@ -57,7 +66,7 @@ function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
57
66
  readerArtifact: entrypoint.readerWithRefetchQueries.readerArtifact,
58
67
  nestedRefetchQueries: entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
59
68
  }),
60
- root: IsographEnvironment_1.ROOT_ID,
69
+ root: { __link: IsographEnvironment_1.ROOT_ID, __typename: entrypoint.concreteType },
61
70
  variables,
62
71
  networkRequest: networkRequest,
63
72
  },
@@ -67,26 +76,27 @@ function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
67
76
  };
68
77
  return getOrCreateItemInSuspenseCache(environment, cacheKey, factory);
69
78
  }
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
- }
79
+ function normalizeData(environment, normalizationAst, networkResponse, variables, nestedRefetchQueries, root) {
80
+ var _a, _b;
81
+ var _c, _d, _e;
82
+ const encounteredIds = new Map();
83
+ (0, logging_1.logMessage)(environment, {
84
+ kind: 'AboutToNormalize',
85
+ normalizationAst,
86
+ networkResponse,
87
+ variables,
88
+ });
89
+ const recordsById = ((_a = (_c = environment.store)[_d = root.__typename]) !== null && _a !== void 0 ? _a : (_c[_d] = {}));
90
+ const newStoreRecord = ((_b = recordsById[_e = root.__link]) !== null && _b !== void 0 ? _b : (recordsById[_e] = {}));
91
+ normalizeDataIntoRecord(environment, normalizationAst, networkResponse, newStoreRecord, root, variables, nestedRefetchQueries, encounteredIds);
92
+ (0, logging_1.logMessage)(environment, {
93
+ kind: 'AfterNormalization',
94
+ store: environment.store,
95
+ encounteredIds,
96
+ });
86
97
  callSubscriptions(environment, encounteredIds);
87
98
  return encounteredIds;
88
99
  }
89
- exports.normalizeData = normalizeData;
90
100
  function subscribeToAnyChange(environment, callback) {
91
101
  const subscription = {
92
102
  kind: 'AnyRecords',
@@ -95,30 +105,35 @@ function subscribeToAnyChange(environment, callback) {
95
105
  environment.subscriptions.add(subscription);
96
106
  return () => environment.subscriptions.delete(subscription);
97
107
  }
98
- exports.subscribeToAnyChange = subscribeToAnyChange;
108
+ function subscribeToAnyChangesToRecord(environment, recordLink, callback) {
109
+ const subscription = {
110
+ kind: 'AnyChangesToRecord',
111
+ recordLink,
112
+ callback,
113
+ };
114
+ environment.subscriptions.add(subscription);
115
+ return () => environment.subscriptions.delete(subscription);
116
+ }
99
117
  // TODO we should re-read and call callback if the value has changed
100
- function subscribe(environment, encounteredDataAndRecords, fragmentReference, callback) {
118
+ function subscribe(environment, encounteredDataAndRecords, fragmentReference, callback, readerAst) {
101
119
  const fragmentSubscription = {
102
120
  kind: 'FragmentSubscription',
103
121
  callback,
104
122
  encounteredDataAndRecords,
105
123
  fragmentReference,
124
+ readerAst,
106
125
  };
107
- // @ts-expect-error
108
126
  environment.subscriptions.add(fragmentSubscription);
109
- // @ts-expect-error
110
127
  return () => environment.subscriptions.delete(fragmentSubscription);
111
128
  }
112
- exports.subscribe = subscribe;
113
- function onNextChange(environment) {
129
+ function onNextChangeToRecord(environment, recordLink) {
114
130
  return new Promise((resolve) => {
115
- const unsubscribe = subscribeToAnyChange(environment, () => {
131
+ const unsubscribe = subscribeToAnyChangesToRecord(environment, recordLink, () => {
116
132
  unsubscribe();
117
133
  resolve();
118
134
  });
119
135
  });
120
136
  }
121
- exports.onNextChange = onNextChange;
122
137
  // Calls to readButDoNotEvaluate can suspend (i.e. throw a promise).
123
138
  // Maybe in the future, they will be able to throw errors.
124
139
  //
@@ -134,6 +149,7 @@ function withErrorHandling(f) {
134
149
  }
135
150
  function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
136
151
  environment.subscriptions.forEach(withErrorHandling((subscription) => {
152
+ var _a;
137
153
  switch (subscription.kind) {
138
154
  case 'FragmentSubscription': {
139
155
  // TODO if there are multiple components subscribed to the same
@@ -157,32 +173,30 @@ function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
157
173
  suspendIfInFlight: false,
158
174
  throwOnNetworkError: false,
159
175
  });
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
176
+ const mergedItem = (0, areEqualWithDeepComparison_1.mergeObjectsUsingReaderAst)(subscription.readerAst, subscription.encounteredDataAndRecords.item, newEncounteredDataAndRecords.item);
177
+ (0, logging_1.logMessage)(environment, {
178
+ kind: 'DeepEqualityCheck',
179
+ fragmentReference: subscription.fragmentReference,
180
+ old: subscription.encounteredDataAndRecords.item,
181
+ new: newEncounteredDataAndRecords.item,
182
+ deeplyEqual: mergedItem === subscription.encounteredDataAndRecords.item,
183
+ });
184
+ if (mergedItem !== subscription.encounteredDataAndRecords.item) {
170
185
  subscription.callback(newEncounteredDataAndRecords);
171
186
  }
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
187
  }
182
188
  return;
183
189
  }
184
190
  case 'AnyRecords': {
185
- return subscription.callback();
191
+ subscription.callback();
192
+ return;
193
+ }
194
+ case 'AnyChangesToRecord': {
195
+ if ((_a = recordsEncounteredWhenNormalizing
196
+ .get(subscription.recordLink.__typename)) === null || _a === void 0 ? void 0 : _a.has(subscription.recordLink.__link)) {
197
+ subscription.callback();
198
+ }
199
+ return;
186
200
  }
187
201
  default: {
188
202
  // Ensure we have covered all variants
@@ -193,7 +207,20 @@ function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
193
207
  }
194
208
  }));
195
209
  }
196
- function hasOverlappingIds(set1, set2) {
210
+ function hasOverlappingIds(ids1, ids2) {
211
+ for (const [typeName, set1] of ids1.entries()) {
212
+ const set2 = ids2.get(typeName);
213
+ if (set2 === undefined) {
214
+ continue;
215
+ }
216
+ if (isNotDisjointFrom(set1, set2)) {
217
+ return true;
218
+ }
219
+ }
220
+ return false;
221
+ }
222
+ // TODO use a polyfill library
223
+ function isNotDisjointFrom(set1, set2) {
197
224
  for (const id of set1) {
198
225
  if (set2.has(id)) {
199
226
  return true;
@@ -204,7 +231,7 @@ function hasOverlappingIds(set1, set2) {
204
231
  /**
205
232
  * Mutate targetParentRecord according to the normalizationAst and networkResponseParentRecord.
206
233
  */
207
- function normalizeDataIntoRecord(environment, normalizationAst, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
234
+ function normalizeDataIntoRecord(environment, normalizationAst, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds) {
208
235
  let recordHasBeenUpdated = false;
209
236
  for (const normalizationNode of normalizationAst) {
210
237
  switch (normalizationNode.kind) {
@@ -215,13 +242,13 @@ function normalizeDataIntoRecord(environment, normalizationAst, networkResponseP
215
242
  break;
216
243
  }
217
244
  case 'Linked': {
218
- const linkedFieldResultedInChange = normalizeLinkedField(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
245
+ const linkedFieldResultedInChange = normalizeLinkedField(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds);
219
246
  recordHasBeenUpdated =
220
247
  recordHasBeenUpdated || linkedFieldResultedInChange;
221
248
  break;
222
249
  }
223
250
  case 'InlineFragment': {
224
- const inlineFragmentResultedInChange = normalizeInlineFragment(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
251
+ const inlineFragmentResultedInChange = normalizeInlineFragment(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds);
225
252
  recordHasBeenUpdated =
226
253
  recordHasBeenUpdated || inlineFragmentResultedInChange;
227
254
  break;
@@ -235,10 +262,19 @@ function normalizeDataIntoRecord(environment, normalizationAst, networkResponseP
235
262
  }
236
263
  }
237
264
  if (recordHasBeenUpdated) {
238
- mutableEncounteredIds.add(targetParentRecordId);
265
+ let encounteredRecordsIds = insertIfNotExists(mutableEncounteredIds, targetParentRecordLink.__typename);
266
+ encounteredRecordsIds.add(targetParentRecordLink.__link);
239
267
  }
240
268
  return recordHasBeenUpdated;
241
269
  }
270
+ function insertIfNotExists(map, key) {
271
+ let result = map.get(key);
272
+ if (result === undefined) {
273
+ result = new Set();
274
+ map.set(key, result);
275
+ }
276
+ return result;
277
+ }
242
278
  function normalizeScalarField(astNode, networkResponseParentRecord, targetStoreRecord, variables) {
243
279
  const networkResponseKey = getNetworkResponseKey(astNode);
244
280
  const networkResponseData = networkResponseParentRecord[networkResponseKey];
@@ -256,7 +292,8 @@ function normalizeScalarField(astNode, networkResponseParentRecord, targetStoreR
256
292
  /**
257
293
  * Mutate targetParentRecord with a given linked field ast node.
258
294
  */
259
- function normalizeLinkedField(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
295
+ function normalizeLinkedField(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds) {
296
+ var _a, _b;
260
297
  const networkResponseKey = getNetworkResponseKey(astNode);
261
298
  const networkResponseData = networkResponseParentRecord[networkResponseKey];
262
299
  const parentRecordKey = getParentRecordKey(astNode, variables);
@@ -265,7 +302,8 @@ function normalizeLinkedField(environment, astNode, networkResponseParentRecord,
265
302
  targetParentRecord[parentRecordKey] = null;
266
303
  return existingValue !== null;
267
304
  }
268
- if (isScalarButNotEmptyArray(networkResponseData)) {
305
+ if (isScalarOrEmptyArray(networkResponseData) &&
306
+ !isNullOrEmptyArray(networkResponseData)) {
269
307
  throw new Error('Unexpected scalar network response when normalizing a linked field');
270
308
  }
271
309
  if (Array.isArray(networkResponseData)) {
@@ -273,43 +311,61 @@ function normalizeLinkedField(environment, astNode, networkResponseParentRecord,
273
311
  const dataIds = [];
274
312
  for (let i = 0; i < networkResponseData.length; i++) {
275
313
  const networkResponseObject = networkResponseData[i];
276
- const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseObject, targetParentRecordId, variables, i, nestedRefetchQueries, mutableEncounteredIds);
277
- dataIds.push({ __link: newStoreRecordId });
314
+ if (networkResponseObject == null) {
315
+ dataIds.push(null);
316
+ continue;
317
+ }
318
+ const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseObject, targetParentRecordLink, variables, i, nestedRefetchQueries, mutableEncounteredIds);
319
+ const __typename = (_a = astNode.concreteType) !== null && _a !== void 0 ? _a : networkResponseObject[exports.TYPENAME_FIELD_NAME];
320
+ if (__typename == null) {
321
+ throw new Error('Unexpected missing __typename in network response when normalizing a linked field. ' +
322
+ 'This is indicative of a bug in Isograph.');
323
+ }
324
+ dataIds.push({
325
+ __link: newStoreRecordId,
326
+ __typename,
327
+ });
278
328
  }
279
329
  targetParentRecord[parentRecordKey] = dataIds;
280
330
  return !dataIdsAreTheSame(existingValue, dataIds);
281
331
  }
282
332
  else {
283
- const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordId, variables, null, nestedRefetchQueries, mutableEncounteredIds);
333
+ const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordLink, variables, null, nestedRefetchQueries, mutableEncounteredIds);
334
+ let __typename = (_b = astNode.concreteType) !== null && _b !== void 0 ? _b : networkResponseData[exports.TYPENAME_FIELD_NAME];
335
+ if (__typename == null) {
336
+ throw new Error('Unexpected missing __typename in network response when normalizing a linked field. ' +
337
+ 'This is indicative of a bug in Isograph.');
338
+ }
284
339
  targetParentRecord[parentRecordKey] = {
285
340
  __link: newStoreRecordId,
341
+ __typename,
286
342
  };
287
343
  const link = (0, IsographEnvironment_1.getLink)(existingValue);
288
- return (link === null || link === void 0 ? void 0 : link.__link) !== newStoreRecordId;
344
+ return (link === null || link === void 0 ? void 0 : link.__link) !== newStoreRecordId || link.__typename !== __typename;
289
345
  }
290
346
  }
291
347
  /**
292
348
  * Mutate targetParentRecord with a given linked field ast node.
293
349
  */
294
- function normalizeInlineFragment(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
350
+ function normalizeInlineFragment(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds) {
295
351
  const typeToRefineTo = astNode.type;
296
- if (networkResponseParentRecord[TYPENAME_FIELD_NAME] === typeToRefineTo) {
297
- const hasBeenModified = normalizeDataIntoRecord(environment, astNode.selections, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
352
+ if (networkResponseParentRecord[exports.TYPENAME_FIELD_NAME] === typeToRefineTo) {
353
+ const hasBeenModified = normalizeDataIntoRecord(environment, astNode.selections, networkResponseParentRecord, targetParentRecord, targetParentRecordLink, variables, nestedRefetchQueries, mutableEncounteredIds);
298
354
  return hasBeenModified;
299
355
  }
300
356
  return false;
301
357
  }
302
358
  function dataIdsAreTheSame(existingValue, newDataIds) {
359
+ var _a, _b;
303
360
  if (Array.isArray(existingValue)) {
304
361
  if (newDataIds.length !== existingValue.length) {
305
362
  return false;
306
363
  }
307
364
  for (let i = 0; i < newDataIds.length; i++) {
308
365
  const maybeLink = (0, IsographEnvironment_1.getLink)(existingValue[i]);
309
- if (maybeLink !== null) {
310
- if (newDataIds[i].__link !== maybeLink.__link) {
311
- return false;
312
- }
366
+ if (((_a = newDataIds[i]) === null || _a === void 0 ? void 0 : _a.__link) !== (maybeLink === null || maybeLink === void 0 ? void 0 : maybeLink.__link) ||
367
+ ((_b = newDataIds[i]) === null || _b === void 0 ? void 0 : _b.__typename) !== (maybeLink === null || maybeLink === void 0 ? void 0 : maybeLink.__typename)) {
368
+ return false;
313
369
  }
314
370
  }
315
371
  return true;
@@ -318,12 +374,18 @@ function dataIdsAreTheSame(existingValue, newDataIds) {
318
374
  return false;
319
375
  }
320
376
  }
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);
377
+ function normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordLink, variables, index, nestedRefetchQueries, mutableEncounteredIds) {
378
+ var _a, _b, _c;
379
+ var _d;
380
+ const newStoreRecordId = getDataIdOfNetworkResponse(targetParentRecordLink, networkResponseData, astNode, variables, index);
381
+ const __typename = (_a = astNode.concreteType) !== null && _a !== void 0 ? _a : networkResponseData[exports.TYPENAME_FIELD_NAME];
382
+ if (__typename == null) {
383
+ throw new Error('Unexpected missing __typename in network response object. ' +
384
+ 'This is indicative of a bug in Isograph.');
385
+ }
386
+ const recordsById = ((_b = (_d = environment.store)[__typename]) !== null && _b !== void 0 ? _b : (_d[__typename] = {}));
387
+ const newStoreRecord = ((_c = recordsById[newStoreRecordId]) !== null && _c !== void 0 ? _c : (recordsById[newStoreRecordId] = {}));
388
+ normalizeDataIntoRecord(environment, astNode.selections, networkResponseData, newStoreRecord, { __link: newStoreRecordId, __typename: __typename }, variables, nestedRefetchQueries, mutableEncounteredIds);
327
389
  return newStoreRecordId;
328
390
  }
329
391
  function isScalarOrEmptyArray(data) {
@@ -332,24 +394,20 @@ function isScalarOrEmptyArray(data) {
332
394
  // This is maybe fixed in a new version of Typescript??
333
395
  return data.every((x) => isScalarOrEmptyArray(x));
334
396
  }
335
- const isScalarValue = typeof data === 'string' ||
397
+ const isScalarValue = data === null ||
398
+ typeof data === 'string' ||
336
399
  typeof data === 'number' ||
337
400
  typeof data === 'boolean';
338
401
  return isScalarValue;
339
402
  }
340
- function isScalarButNotEmptyArray(data) {
341
- // N.B. empty arrays count as empty arrays of linked fields.
403
+ function isNullOrEmptyArray(data) {
342
404
  if (Array.isArray(data)) {
343
405
  if (data.length === 0) {
344
- return false;
406
+ return true;
345
407
  }
346
- // This is maybe fixed in a new version of Typescript??
347
- return data.every((x) => isScalarOrEmptyArray(x));
408
+ return data.every((x) => isNullOrEmptyArray(x));
348
409
  }
349
- const isScalarValue = typeof data === 'string' ||
350
- typeof data === 'number' ||
351
- typeof data === 'boolean';
352
- return isScalarValue;
410
+ return data === null;
353
411
  }
354
412
  function getParentRecordKey(astNode, variables) {
355
413
  let parentRecordKey = astNode.fieldName;
@@ -361,7 +419,6 @@ function getParentRecordKey(astNode, variables) {
361
419
  }
362
420
  return parentRecordKey;
363
421
  }
364
- exports.getParentRecordKey = getParentRecordKey;
365
422
  function getStoreKeyChunkForArgumentValue(argumentValue, variables) {
366
423
  var _a;
367
424
  switch (argumentValue.kind) {
@@ -430,14 +487,19 @@ function getNetworkResponseKey(astNode) {
430
487
  exports.FIRST_SPLIT_KEY = '____';
431
488
  exports.SECOND_SPLIT_KEY = '___';
432
489
  // Returns a key to look up an item in the store
433
- function getDataIdOfNetworkResponse(parentRecordId, dataToNormalize, astNode, variables, index) {
490
+ function getDataIdOfNetworkResponse(parentRecordLink, dataToNormalize, astNode, variables, index) {
491
+ // If we are dealing with nested Query, use __ROOT as id
492
+ // TODO do not hard code this value here
493
+ if (astNode.concreteType === 'Query') {
494
+ return IsographEnvironment_1.ROOT_ID;
495
+ }
434
496
  // Check whether the dataToNormalize has an id field. If so, that is the key.
435
497
  // If not, we construct an id from the parentRecordId and the field parameters.
436
498
  const dataId = dataToNormalize.id;
437
499
  if (dataId != null) {
438
500
  return dataId;
439
501
  }
440
- let storeKey = `${parentRecordId}.${astNode.fieldName}`;
502
+ let storeKey = `${parentRecordLink.__typename}:${parentRecordLink.__link}.${astNode.fieldName}`;
441
503
  if (index != null) {
442
504
  storeKey += `.${index}`;
443
505
  }
@@ -0,0 +1,18 @@
1
+ import { NormalizationAst } from './entrypoint';
2
+ import { Variables } from './FragmentReference';
3
+ import { IsographEnvironment, Link } from './IsographEnvironment';
4
+ export type ShouldFetch = 'Yes' | 'No' | 'IfNecessary';
5
+ export declare const DEFAULT_SHOULD_FETCH_VALUE: ShouldFetch;
6
+ export type FetchOptions = {
7
+ shouldFetch?: ShouldFetch;
8
+ onComplete?: () => void;
9
+ onError?: () => void;
10
+ };
11
+ export type CheckResult = {
12
+ kind: 'EnoughData';
13
+ } | {
14
+ kind: 'MissingData';
15
+ record: Link;
16
+ };
17
+ export declare function check(environment: IsographEnvironment, normalizationAst: NormalizationAst, variables: Variables, root: Link): CheckResult;
18
+ //# 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,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAEL,mBAAmB,EACnB,IAAI,EAEL,MAAM,uBAAuB,CAAC;AAG/B,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,IAAI,GAAG,aAAa,CAAC;AAEvD,eAAO,MAAM,0BAA0B,EAAE,WAA2B,CAAC;AAErE,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,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,gBAAgB,EAClC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,GACT,WAAW,CAgBb"}
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SHOULD_FETCH_VALUE = void 0;
4
+ exports.check = check;
5
+ const cache_1 = require("./cache");
6
+ const IsographEnvironment_1 = require("./IsographEnvironment");
7
+ const logging_1 = require("./logging");
8
+ exports.DEFAULT_SHOULD_FETCH_VALUE = 'IfNecessary';
9
+ function check(environment, normalizationAst, variables, root) {
10
+ var _a, _b;
11
+ var _c, _d, _e;
12
+ const recordsById = ((_a = (_c = environment.store)[_d = root.__typename]) !== null && _a !== void 0 ? _a : (_c[_d] = {}));
13
+ const newStoreRecord = ((_b = recordsById[_e = root.__link]) !== null && _b !== void 0 ? _b : (recordsById[_e] = {}));
14
+ const checkResult = checkFromRecord(environment, normalizationAst, variables, newStoreRecord, root);
15
+ (0, logging_1.logMessage)(environment, {
16
+ kind: 'EnvironmentCheck',
17
+ result: checkResult,
18
+ });
19
+ return checkResult;
20
+ }
21
+ function checkFromRecord(environment, normalizationAst, variables, record, recordLink) {
22
+ var _a, _b;
23
+ normalizationAstLoop: for (const normalizationAstNode of normalizationAst) {
24
+ switch (normalizationAstNode.kind) {
25
+ case 'Scalar': {
26
+ const parentRecordKey = (0, cache_1.getParentRecordKey)(normalizationAstNode, variables);
27
+ const scalarValue = record[parentRecordKey];
28
+ // null means the value is known to be missing, so it must
29
+ // be exactly undefined
30
+ if (scalarValue === undefined) {
31
+ return {
32
+ kind: 'MissingData',
33
+ record: recordLink,
34
+ };
35
+ }
36
+ continue normalizationAstLoop;
37
+ }
38
+ case 'Linked': {
39
+ const parentRecordKey = (0, cache_1.getParentRecordKey)(normalizationAstNode, variables);
40
+ const linkedValue = record[parentRecordKey];
41
+ if (linkedValue === undefined) {
42
+ return {
43
+ kind: 'MissingData',
44
+ record: recordLink,
45
+ };
46
+ }
47
+ else if (linkedValue === null) {
48
+ continue;
49
+ }
50
+ else if (Array.isArray(linkedValue)) {
51
+ arrayItemsLoop: for (const item of linkedValue) {
52
+ const link = (0, IsographEnvironment_1.getLink)(item);
53
+ if (link === null) {
54
+ throw new Error('Unexpected non-link in the Isograph store. ' +
55
+ 'This is indicative of a bug in Isograph.');
56
+ }
57
+ const linkedRecord = (_a = environment.store[link.__typename]) === null || _a === void 0 ? void 0 : _a[link.__link];
58
+ if (linkedRecord === undefined) {
59
+ return {
60
+ kind: 'MissingData',
61
+ record: link,
62
+ };
63
+ }
64
+ else if (linkedRecord === null) {
65
+ continue arrayItemsLoop;
66
+ }
67
+ else {
68
+ // TODO in __DEV__ assert linkedRecord is an object
69
+ const result = checkFromRecord(environment, normalizationAstNode.selections, variables, linkedRecord, link);
70
+ if (result.kind === 'MissingData') {
71
+ return result;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ else {
77
+ const link = (0, IsographEnvironment_1.getLink)(linkedValue);
78
+ if (link === null) {
79
+ throw new Error('Unexpected non-link in the Isograph store. ' +
80
+ 'This is indicative of a bug in Isograph.');
81
+ }
82
+ const linkedRecord = (_b = environment.store[link.__typename]) === null || _b === void 0 ? void 0 : _b[link.__link];
83
+ if (linkedRecord === undefined) {
84
+ return {
85
+ kind: 'MissingData',
86
+ record: link,
87
+ };
88
+ }
89
+ else if (linkedRecord === null) {
90
+ continue normalizationAstLoop;
91
+ }
92
+ else {
93
+ // TODO in __DEV__ assert linkedRecord is an object
94
+ const result = checkFromRecord(environment, normalizationAstNode.selections, variables, linkedRecord, link);
95
+ if (result.kind === 'MissingData') {
96
+ return result;
97
+ }
98
+ }
99
+ }
100
+ continue normalizationAstLoop;
101
+ }
102
+ case 'InlineFragment': {
103
+ const existingRecordTypename = record['__typename'];
104
+ if (existingRecordTypename == null ||
105
+ existingRecordTypename !== normalizationAstNode.type) {
106
+ return {
107
+ kind: 'MissingData',
108
+ record: recordLink,
109
+ };
110
+ }
111
+ const result = checkFromRecord(environment, normalizationAstNode.selections, variables, record, recordLink);
112
+ if (result.kind === 'MissingData') {
113
+ return result;
114
+ }
115
+ continue normalizationAstLoop;
116
+ }
117
+ default: {
118
+ let _ = normalizationAstNode;
119
+ _;
120
+ throw new Error('Unexpected case. This is indicative of a bug in Isograph.');
121
+ }
122
+ }
123
+ }
124
+ return {
125
+ kind: 'EnoughData',
126
+ };
127
+ }
@@ -1,5 +1,5 @@
1
- /// <reference types="react" />
2
1
  import { IsographEnvironment } from './IsographEnvironment';
3
2
  import { FragmentReference } from './FragmentReference';
4
3
  import { NetworkRequestReaderOptions } from './read';
5
4
  export declare function getOrCreateCachedComponent(environment: IsographEnvironment, componentName: string, fragmentReference: FragmentReference<any, any>, networkRequestOptions: NetworkRequestReaderOptions): React.FC<any>;
5
+ //# sourceMappingURL=componentCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"componentCache.d.ts","sourceRoot":"","sources":["../../src/core/componentCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,2BAA2B,EAAE,MAAM,QAAQ,CAAC;AAIrD,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,mBAAmB,EAChC,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,EAC9C,qBAAqB,EAAE,2BAA2B,GACjD,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAgDf"}