@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
package/dist/core/read.js CHANGED
@@ -1,23 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getNetworkRequestOptionsWithDefaults = exports.readButDoNotEvaluate = void 0;
3
+ exports.readButDoNotEvaluate = readButDoNotEvaluate;
4
+ exports.readLoadablySelectedFieldData = readLoadablySelectedFieldData;
5
+ exports.readResolverFieldData = readResolverFieldData;
6
+ exports.readScalarFieldData = readScalarFieldData;
7
+ exports.readLinkedFieldData = readLinkedFieldData;
8
+ exports.getNetworkRequestOptionsWithDefaults = getNetworkRequestOptionsWithDefaults;
9
+ exports.readImperativelyLoadedField = readImperativelyLoadedField;
4
10
  const cache_1 = require("./cache");
5
11
  const componentCache_1 = require("./componentCache");
6
12
  const IsographEnvironment_1 = require("./IsographEnvironment");
13
+ const logging_1 = require("./logging");
7
14
  const makeNetworkRequest_1 = require("./makeNetworkRequest");
8
15
  const PromiseWrapper_1 = require("./PromiseWrapper");
16
+ const startUpdate_1 = require("./startUpdate");
9
17
  function readButDoNotEvaluate(environment, fragmentReference, networkRequestOptions) {
10
18
  var _a;
11
- const mutableEncounteredRecords = new Set();
19
+ const mutableEncounteredRecords = new Map();
20
+ // TODO consider moving this to the outside
12
21
  const readerWithRefetchQueries = (0, PromiseWrapper_1.readPromise)(fragmentReference.readerWithRefetchQueries);
13
22
  const response = readData(environment, readerWithRefetchQueries.readerArtifact.readerAst, fragmentReference.root, (_a = fragmentReference.variables) !== null && _a !== void 0 ? _a : {}, readerWithRefetchQueries.nestedRefetchQueries, fragmentReference.networkRequest, networkRequestOptions, mutableEncounteredRecords);
14
- // @ts-expect-error
15
- if (typeof window !== 'undefined' && window.__LOG) {
16
- console.log('done reading', { response });
17
- }
23
+ (0, logging_1.logMessage)(environment, () => ({
24
+ kind: 'DoneReading',
25
+ response,
26
+ fieldName: readerWithRefetchQueries.readerArtifact.fieldName,
27
+ root: fragmentReference.root,
28
+ }));
18
29
  if (response.kind === 'MissingData') {
19
30
  // There are two cases here that we care about:
20
- // 1. the network request is in flight, we haven't suspend on it, and we want
31
+ // 1. the network request is in flight, we haven't suspended on it, and we want
21
32
  // to throw if it errors out. So, networkRequestOptions.suspendIfInFlight === false
22
33
  // and networkRequestOptions.throwOnNetworkError === true.
23
34
  // 2. everything else
@@ -27,13 +38,22 @@ function readButDoNotEvaluate(environment, fragmentReference, networkRequestOpti
27
38
  // will not resolve.
28
39
  if (!networkRequestOptions.suspendIfInFlight &&
29
40
  networkRequestOptions.throwOnNetworkError) {
30
- // TODO assert that the network request state is not Err
41
+ // What are we doing here? If the network response has errored out, we can do
42
+ // two things: throw a rejected promise, or throw an error. Both work identically
43
+ // in the browser. However, during initial SSR on NextJS, throwing a rejected
44
+ // promise results in an infinite loop (including re-issuing the query until the
45
+ // process OOM's or something.) Hence, we throw an error.
46
+ // TODO investigate why we cannot check against NOT_SET here and we have to cast
47
+ const result = fragmentReference.networkRequest.result;
48
+ if (result.kind === 'Err') {
49
+ throw new Error('NetworkError', { cause: result.error });
50
+ }
31
51
  throw new Promise((resolve, reject) => {
32
- (0, cache_1.onNextChange)(environment).then(resolve);
52
+ (0, cache_1.onNextChangeToRecord)(environment, response.recordLink).then(resolve);
33
53
  fragmentReference.networkRequest.promise.catch(reject);
34
54
  });
35
55
  }
36
- throw (0, cache_1.onNextChange)(environment);
56
+ throw (0, cache_1.onNextChangeToRecord)(environment, response.recordLink);
37
57
  }
38
58
  else {
39
59
  return {
@@ -42,286 +62,69 @@ function readButDoNotEvaluate(environment, fragmentReference, networkRequestOpti
42
62
  };
43
63
  }
44
64
  }
45
- exports.readButDoNotEvaluate = readButDoNotEvaluate;
46
65
  function readData(environment, ast, root, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords) {
47
- var _a, _b, _c, _d, _e;
48
- mutableEncounteredRecords.add(root);
49
- let storeRecord = environment.store[root];
66
+ var _a, _b, _c;
67
+ const encounteredIds = (0, cache_1.insertIfNotExists)(mutableEncounteredRecords, root.__typename);
68
+ encounteredIds.add(root.__link);
69
+ let storeRecord = (_a = environment.store[root.__typename]) === null || _a === void 0 ? void 0 : _a[root.__link];
50
70
  if (storeRecord === undefined) {
51
71
  return {
52
72
  kind: 'MissingData',
53
- reason: 'No record for root ' + root,
73
+ reason: 'No record for root ' + root.__link,
74
+ recordLink: root,
54
75
  };
55
76
  }
56
77
  if (storeRecord === null) {
57
78
  return {
58
79
  kind: 'Success',
59
80
  data: null,
60
- encounteredRecords: mutableEncounteredRecords,
61
81
  };
62
82
  }
63
83
  let target = {};
64
84
  for (const field of ast) {
65
85
  switch (field.kind) {
66
86
  case 'Scalar': {
67
- const storeRecordName = (0, cache_1.getParentRecordKey)(field, variables);
68
- const value = storeRecord[storeRecordName];
69
- // TODO consider making scalars into discriminated unions. This probably has
70
- // to happen for when we handle errors.
71
- if (value === undefined) {
72
- return {
73
- kind: 'MissingData',
74
- reason: 'No value for ' + storeRecordName + ' on root ' + root,
75
- };
87
+ const data = readScalarFieldData(field, storeRecord, root, variables);
88
+ if (data.kind === 'MissingData') {
89
+ return data;
76
90
  }
77
- target[(_a = field.alias) !== null && _a !== void 0 ? _a : field.fieldName] = value;
91
+ target[(_b = field.alias) !== null && _b !== void 0 ? _b : field.fieldName] = data.data;
92
+ break;
93
+ }
94
+ case 'Link': {
95
+ target[field.alias] = root;
78
96
  break;
79
97
  }
80
98
  case 'Linked': {
81
- const storeRecordName = (0, cache_1.getParentRecordKey)(field, variables);
82
- const value = storeRecord[storeRecordName];
83
- if (Array.isArray(value)) {
84
- const results = [];
85
- for (const item of value) {
86
- const link = (0, IsographEnvironment_1.assertLink)(item);
87
- if (link === undefined) {
88
- return {
89
- kind: 'MissingData',
90
- reason: 'No link for ' +
91
- storeRecordName +
92
- ' on root ' +
93
- root +
94
- '. Link is ' +
95
- JSON.stringify(item),
96
- };
97
- }
98
- else if (link === null) {
99
- results.push(null);
100
- continue;
101
- }
102
- const result = readData(environment, field.selections, link.__link, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
103
- if (result.kind === 'MissingData') {
104
- return {
105
- kind: 'MissingData',
106
- reason: 'Missing data for ' +
107
- storeRecordName +
108
- ' on root ' +
109
- root +
110
- '. Link is ' +
111
- JSON.stringify(item),
112
- nestedReason: result,
113
- };
114
- }
115
- results.push(result.data);
116
- }
117
- target[(_b = field.alias) !== null && _b !== void 0 ? _b : field.fieldName] = results;
118
- break;
119
- }
120
- let link = (0, IsographEnvironment_1.assertLink)(value);
121
- if (link === undefined) {
122
- // TODO make this configurable, and also generated and derived from the schema
123
- const missingFieldHandler = (_c = environment.missingFieldHandler) !== null && _c !== void 0 ? _c : IsographEnvironment_1.defaultMissingFieldHandler;
124
- const altLink = missingFieldHandler(storeRecord, root, field.fieldName, field.arguments, variables);
125
- if (altLink === undefined) {
126
- return {
127
- kind: 'MissingData',
128
- reason: 'No link for ' +
129
- storeRecordName +
130
- ' on root ' +
131
- root +
132
- '. Link is ' +
133
- JSON.stringify(value),
134
- };
135
- }
136
- else {
137
- link = altLink;
138
- }
139
- }
140
- else if (link === null) {
141
- target[(_d = field.alias) !== null && _d !== void 0 ? _d : field.fieldName] = null;
142
- break;
143
- }
144
- const targetId = link.__link;
145
- const data = readData(environment, field.selections, targetId, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
99
+ const data = readLinkedFieldData(environment, field, storeRecord, root, variables, networkRequest, (ast, root) => readData(environment, ast, root, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords));
146
100
  if (data.kind === 'MissingData') {
147
- return {
148
- kind: 'MissingData',
149
- reason: 'Missing data for ' + storeRecordName + ' on root ' + root,
150
- nestedReason: data,
151
- };
101
+ return data;
152
102
  }
153
- target[(_e = field.alias) !== null && _e !== void 0 ? _e : field.fieldName] = data.data;
103
+ target[(_c = field.alias) !== null && _c !== void 0 ? _c : field.fieldName] = data.data;
154
104
  break;
155
105
  }
156
106
  case 'ImperativelyLoadedField': {
157
- // First, we read the data using the refetch reader AST (i.e. read out the
158
- // id field).
159
- const data = readData(environment, field.refetchReaderArtifact.readerAst, root, variables,
160
- // Refetch fields just read the id, and don't need refetch query artifacts
161
- [],
162
- // This is probably indicative of the fact that we are doing redundant checks
163
- // on the status of this network request...
164
- networkRequest, networkRequestOptions, mutableEncounteredRecords);
107
+ const data = readImperativelyLoadedField(environment, field, root, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
165
108
  if (data.kind === 'MissingData') {
166
- return {
167
- kind: 'MissingData',
168
- reason: 'Missing data for ' + field.alias + ' on root ' + root,
169
- nestedReason: data,
170
- };
171
- }
172
- else {
173
- const refetchQueryIndex = field.refetchQuery;
174
- if (refetchQueryIndex == null) {
175
- throw new Error('refetchQuery is null in RefetchField');
176
- }
177
- const refetchQuery = nestedRefetchQueries[refetchQueryIndex];
178
- const refetchQueryArtifact = refetchQuery.artifact;
179
- const allowedVariables = refetchQuery.allowedVariables;
180
- // Second, we allow the user to call the resolver, which will ultimately
181
- // use the resolver reader AST to get the resolver parameters.
182
- target[field.alias] = (args) => [
183
- // Stable id
184
- root + '__' + field.name,
185
- // Fetcher
186
- field.refetchReaderArtifact.resolver(environment, refetchQueryArtifact, data.data, filterVariables(Object.assign(Object.assign({}, args), variables), allowedVariables), root,
187
- // TODO these params should be removed
188
- null, []),
189
- ];
109
+ return data;
190
110
  }
111
+ target[field.alias] = data.data;
191
112
  break;
192
113
  }
193
114
  case 'Resolver': {
194
- const usedRefetchQueries = field.usedRefetchQueries;
195
- const resolverRefetchQueries = usedRefetchQueries.map((index) => nestedRefetchQueries[index]);
196
- switch (field.readerArtifact.kind) {
197
- case 'EagerReaderArtifact': {
198
- const data = readData(environment, field.readerArtifact.readerAst, root, variables, resolverRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
199
- if (data.kind === 'MissingData') {
200
- return {
201
- kind: 'MissingData',
202
- reason: 'Missing data for ' + field.alias + ' on root ' + root,
203
- nestedReason: data,
204
- };
205
- }
206
- else {
207
- target[field.alias] = field.readerArtifact.resolver(data.data);
208
- }
209
- break;
210
- }
211
- case 'ComponentReaderArtifact': {
212
- target[field.alias] = (0, componentCache_1.getOrCreateCachedComponent)(environment, field.readerArtifact.componentName, {
213
- kind: 'FragmentReference',
214
- readerWithRefetchQueries: (0, PromiseWrapper_1.wrapResolvedValue)({
215
- kind: 'ReaderWithRefetchQueries',
216
- readerArtifact: field.readerArtifact,
217
- nestedRefetchQueries: resolverRefetchQueries,
218
- }),
219
- root,
220
- variables: generateChildVariableMap(variables, field.arguments),
221
- networkRequest,
222
- }, networkRequestOptions);
223
- break;
224
- }
225
- default: {
226
- let _ = field.readerArtifact;
227
- _;
228
- throw new Error('Unexpected kind');
229
- }
115
+ const data = readResolverFieldData(environment, field, root, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
116
+ if (data.kind === 'MissingData') {
117
+ return data;
230
118
  }
119
+ target[field.alias] = data.data;
231
120
  break;
232
121
  }
233
122
  case 'LoadablySelectedField': {
234
- const refetchReaderParams = readData(environment, field.refetchReaderAst, root, variables,
235
- // Refetch fields just read the id, and don't need refetch query artifacts
236
- [], networkRequest, networkRequestOptions, mutableEncounteredRecords);
237
- if (refetchReaderParams.kind === 'MissingData') {
238
- return {
239
- kind: 'MissingData',
240
- reason: 'Missing data for ' + field.alias + ' on root ' + root,
241
- nestedReason: refetchReaderParams,
242
- };
243
- }
244
- else {
245
- target[field.alias] = (args) => {
246
- // TODO we should use the reader AST for this
247
- const includeReadOutData = (variables, readOutData) => {
248
- variables.id = readOutData.id;
249
- return variables;
250
- };
251
- const localVariables = includeReadOutData(args !== null && args !== void 0 ? args : {}, refetchReaderParams.data);
252
- writeQueryArgsToVariables(localVariables, field.queryArguments, variables);
253
- return [
254
- // Stable id
255
- root +
256
- '/' +
257
- field.name +
258
- '/' +
259
- stableStringifyArgs(localVariables),
260
- // Fetcher
261
- () => {
262
- const fragmentReferenceAndDisposeFromEntrypoint = (entrypoint) => {
263
- const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, entrypoint, localVariables);
264
- const fragmentReference = {
265
- kind: 'FragmentReference',
266
- readerWithRefetchQueries: (0, PromiseWrapper_1.wrapResolvedValue)({
267
- kind: 'ReaderWithRefetchQueries',
268
- readerArtifact: entrypoint.readerWithRefetchQueries.readerArtifact,
269
- nestedRefetchQueries: entrypoint.readerWithRefetchQueries
270
- .nestedRefetchQueries,
271
- }),
272
- // TODO localVariables is not guaranteed to have an id field
273
- root: localVariables.id,
274
- variables: localVariables,
275
- networkRequest,
276
- };
277
- return [fragmentReference, disposeNetworkRequest];
278
- };
279
- if (field.entrypoint.kind === 'Entrypoint') {
280
- return fragmentReferenceAndDisposeFromEntrypoint(field.entrypoint);
281
- }
282
- else {
283
- const isographArtifactPromiseWrapper = (0, IsographEnvironment_1.getOrLoadIsographArtifact)(environment, field.entrypoint.typeAndField, field.entrypoint.loader);
284
- const state = (0, PromiseWrapper_1.getPromiseState)(isographArtifactPromiseWrapper);
285
- if (state.kind === 'Ok') {
286
- return fragmentReferenceAndDisposeFromEntrypoint(state.value);
287
- }
288
- else {
289
- // Promise is pending or thrown
290
- let entrypointLoaderState = { kind: 'EntrypointNotLoaded' };
291
- const networkRequest = (0, PromiseWrapper_1.wrapPromise)(isographArtifactPromiseWrapper.promise.then((entrypoint) => {
292
- if (entrypointLoaderState.kind === 'EntrypointNotLoaded') {
293
- const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, entrypoint, localVariables);
294
- entrypointLoaderState = {
295
- kind: 'NetworkRequestStarted',
296
- disposeNetworkRequest,
297
- };
298
- return networkRequest.promise;
299
- }
300
- }));
301
- const readerWithRefetchPromise = isographArtifactPromiseWrapper.promise.then((entrypoint) => entrypoint.readerWithRefetchQueries);
302
- const fragmentReference = {
303
- kind: 'FragmentReference',
304
- readerWithRefetchQueries: (0, PromiseWrapper_1.wrapPromise)(readerWithRefetchPromise),
305
- // TODO localVariables is not guaranteed to have an id field
306
- root: localVariables.id,
307
- variables: localVariables,
308
- networkRequest,
309
- };
310
- return [
311
- fragmentReference,
312
- () => {
313
- if (entrypointLoaderState.kind === 'NetworkRequestStarted') {
314
- entrypointLoaderState.disposeNetworkRequest();
315
- }
316
- entrypointLoaderState = { kind: 'Disposed' };
317
- },
318
- ];
319
- }
320
- }
321
- },
322
- ];
323
- };
123
+ const data = readLoadablySelectedFieldData(environment, field, root, variables, networkRequest, networkRequestOptions, mutableEncounteredRecords);
124
+ if (data.kind === 'MissingData') {
125
+ return data;
324
126
  }
127
+ target[field.alias] = data.data;
325
128
  break;
326
129
  }
327
130
  default: {
@@ -335,7 +138,104 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, netwo
335
138
  return {
336
139
  kind: 'Success',
337
140
  data: target,
338
- encounteredRecords: mutableEncounteredRecords,
141
+ };
142
+ }
143
+ function readLoadablySelectedFieldData(environment, field, root, variables, networkRequest, networkRequestOptions, mutableEncounteredRecords) {
144
+ const refetchReaderParams = readData(environment, field.refetchReaderAst, root, variables,
145
+ // Refetch fields just read the id, and don't need refetch query artifacts
146
+ [], networkRequest, networkRequestOptions, mutableEncounteredRecords);
147
+ if (refetchReaderParams.kind === 'MissingData') {
148
+ return {
149
+ kind: 'MissingData',
150
+ reason: 'Missing data for ' + field.alias + ' on root ' + root.__link,
151
+ nestedReason: refetchReaderParams,
152
+ recordLink: refetchReaderParams.recordLink,
153
+ };
154
+ }
155
+ return {
156
+ kind: 'Success',
157
+ data: (args,
158
+ // TODO get the associated type for FetchOptions from the loadably selected field
159
+ fetchOptions) => {
160
+ // TODO we should use the reader AST for this
161
+ const includeReadOutData = (variables, readOutData) => {
162
+ variables.id = readOutData.id;
163
+ return variables;
164
+ };
165
+ const localVariables = includeReadOutData(args !== null && args !== void 0 ? args : {}, refetchReaderParams.data);
166
+ writeQueryArgsToVariables(localVariables, field.queryArguments, variables);
167
+ return [
168
+ // Stable id
169
+ root.__typename +
170
+ ':' +
171
+ root.__link +
172
+ '/' +
173
+ field.name +
174
+ '/' +
175
+ stableStringifyArgs(localVariables),
176
+ // Fetcher
177
+ () => {
178
+ const fragmentReferenceAndDisposeFromEntrypoint = (entrypoint) => {
179
+ const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.maybeMakeNetworkRequest)(environment, entrypoint, localVariables, fetchOptions);
180
+ const fragmentReference = {
181
+ kind: 'FragmentReference',
182
+ readerWithRefetchQueries: (0, PromiseWrapper_1.wrapResolvedValue)({
183
+ kind: 'ReaderWithRefetchQueries',
184
+ readerArtifact: entrypoint.readerWithRefetchQueries.readerArtifact,
185
+ nestedRefetchQueries: entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
186
+ }),
187
+ // TODO localVariables is not guaranteed to have an id field
188
+ root,
189
+ variables: localVariables,
190
+ networkRequest,
191
+ };
192
+ return [fragmentReference, disposeNetworkRequest];
193
+ };
194
+ if (field.entrypoint.kind === 'Entrypoint') {
195
+ return fragmentReferenceAndDisposeFromEntrypoint(field.entrypoint);
196
+ }
197
+ else {
198
+ const isographArtifactPromiseWrapper = (0, IsographEnvironment_1.getOrLoadIsographArtifact)(environment, field.entrypoint.typeAndField, field.entrypoint.loader);
199
+ const state = (0, PromiseWrapper_1.getPromiseState)(isographArtifactPromiseWrapper);
200
+ if (state.kind === 'Ok') {
201
+ return fragmentReferenceAndDisposeFromEntrypoint(state.value);
202
+ }
203
+ else {
204
+ // Promise is pending or thrown
205
+ let entrypointLoaderState = { kind: 'EntrypointNotLoaded' };
206
+ const networkRequest = (0, PromiseWrapper_1.wrapPromise)(isographArtifactPromiseWrapper.promise.then((entrypoint) => {
207
+ if (entrypointLoaderState.kind === 'EntrypointNotLoaded') {
208
+ const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.maybeMakeNetworkRequest)(environment, entrypoint, localVariables, fetchOptions);
209
+ entrypointLoaderState = {
210
+ kind: 'NetworkRequestStarted',
211
+ disposeNetworkRequest,
212
+ };
213
+ return networkRequest.promise;
214
+ }
215
+ }));
216
+ const readerWithRefetchPromise = isographArtifactPromiseWrapper.promise.then((entrypoint) => entrypoint.readerWithRefetchQueries);
217
+ const fragmentReference = {
218
+ kind: 'FragmentReference',
219
+ readerWithRefetchQueries: (0, PromiseWrapper_1.wrapPromise)(readerWithRefetchPromise),
220
+ // TODO localVariables is not guaranteed to have an id field
221
+ root,
222
+ variables: localVariables,
223
+ networkRequest,
224
+ };
225
+ return [
226
+ fragmentReference,
227
+ () => {
228
+ if (entrypointLoaderState.kind === 'NetworkRequestStarted') {
229
+ entrypointLoaderState.disposeNetworkRequest();
230
+ }
231
+ entrypointLoaderState = { kind: 'Disposed' };
232
+ },
233
+ ];
234
+ }
235
+ }
236
+ },
237
+ ];
238
+ },
339
239
  };
340
240
  }
341
241
  function filterVariables(variables, allowedVariables) {
@@ -352,8 +252,16 @@ function generateChildVariableMap(variables, fieldArguments) {
352
252
  }
353
253
  const childVars = {};
354
254
  for (const [name, value] of fieldArguments) {
355
- if (value.kind === 'Variable') {
356
- childVars[name] = variables[value.name];
255
+ if (value.kind === 'Object') {
256
+ childVars[name] = generateChildVariableMap(variables, value.value);
257
+ }
258
+ else if (value.kind === 'Variable') {
259
+ const variable = variables[value.name];
260
+ // Variable could be null if it was not provided but has a default case,
261
+ // so we allow the loop to continue rather than throwing an error.
262
+ if (variable != null) {
263
+ childVars[name] = variable;
264
+ }
357
265
  }
358
266
  else {
359
267
  childVars[name] = value.value;
@@ -367,6 +275,10 @@ function writeQueryArgsToVariables(targetVariables, queryArgs, variables) {
367
275
  }
368
276
  for (const [name, argType] of queryArgs) {
369
277
  switch (argType.kind) {
278
+ case 'Object': {
279
+ writeQueryArgsToVariables((targetVariables[name] = {}), argType.value, variables);
280
+ break;
281
+ }
370
282
  case 'Variable': {
371
283
  targetVariables[name] = variables[argType.name];
372
284
  break;
@@ -391,6 +303,212 @@ function writeQueryArgsToVariables(targetVariables, queryArgs, variables) {
391
303
  }
392
304
  }
393
305
  }
306
+ function readResolverFieldData(environment, field, root, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords) {
307
+ const usedRefetchQueries = field.usedRefetchQueries;
308
+ const resolverRefetchQueries = usedRefetchQueries.map((index) => {
309
+ const resolverRefetchQuery = nestedRefetchQueries[index];
310
+ if (resolverRefetchQuery == null) {
311
+ throw new Error('resolverRefetchQuery is null in Resolver. This is indicative of a bug in Isograph.');
312
+ }
313
+ return resolverRefetchQuery;
314
+ });
315
+ const readerWithRefetchQueries = {
316
+ kind: 'ReaderWithRefetchQueries',
317
+ readerArtifact: field.readerArtifact,
318
+ nestedRefetchQueries: resolverRefetchQueries,
319
+ };
320
+ const fragment = {
321
+ kind: 'FragmentReference',
322
+ readerWithRefetchQueries: (0, PromiseWrapper_1.wrapResolvedValue)(readerWithRefetchQueries),
323
+ root,
324
+ variables: generateChildVariableMap(variables, field.arguments),
325
+ networkRequest,
326
+ };
327
+ switch (field.readerArtifact.kind) {
328
+ case 'EagerReaderArtifact': {
329
+ const data = readData(environment, field.readerArtifact.readerAst, root, generateChildVariableMap(variables, field.arguments), resolverRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
330
+ if (data.kind === 'MissingData') {
331
+ return {
332
+ kind: 'MissingData',
333
+ reason: 'Missing data for ' + field.alias + ' on root ' + root.__link,
334
+ nestedReason: data,
335
+ recordLink: data.recordLink,
336
+ };
337
+ }
338
+ const firstParameter = {
339
+ data: data.data,
340
+ parameters: variables,
341
+ startUpdate: field.readerArtifact.hasUpdatable
342
+ ? (0, startUpdate_1.getOrCreateCachedStartUpdate)(environment, fragment, readerWithRefetchQueries.readerArtifact.fieldName)
343
+ : undefined,
344
+ };
345
+ return {
346
+ kind: 'Success',
347
+ data: field.readerArtifact.resolver(firstParameter),
348
+ };
349
+ }
350
+ case 'ComponentReaderArtifact': {
351
+ return {
352
+ kind: 'Success',
353
+ data: (0, componentCache_1.getOrCreateCachedComponent)(environment, field.readerArtifact.fieldName, fragment, networkRequestOptions),
354
+ };
355
+ }
356
+ default: {
357
+ let _ = field.readerArtifact;
358
+ _;
359
+ throw new Error('Unexpected kind');
360
+ }
361
+ }
362
+ }
363
+ function readScalarFieldData(field, storeRecord, root, variables) {
364
+ const storeRecordName = (0, cache_1.getParentRecordKey)(field, variables);
365
+ const value = storeRecord[storeRecordName];
366
+ // TODO consider making scalars into discriminated unions. This probably has
367
+ // to happen for when we handle errors.
368
+ if (value === undefined) {
369
+ return {
370
+ kind: 'MissingData',
371
+ reason: 'No value for ' + storeRecordName + ' on root ' + root.__link,
372
+ recordLink: root,
373
+ };
374
+ }
375
+ return { kind: 'Success', data: value };
376
+ }
377
+ function readLinkedFieldData(environment, field, storeRecord, root, variables, networkRequest, readData) {
378
+ const storeRecordName = (0, cache_1.getParentRecordKey)(field, variables);
379
+ const value = storeRecord[storeRecordName];
380
+ if (Array.isArray(value)) {
381
+ const results = [];
382
+ for (const item of value) {
383
+ const link = (0, IsographEnvironment_1.assertLink)(item);
384
+ if (link === undefined) {
385
+ return {
386
+ kind: 'MissingData',
387
+ reason: 'No link for ' +
388
+ storeRecordName +
389
+ ' on root ' +
390
+ root.__link +
391
+ '. Link is ' +
392
+ JSON.stringify(item),
393
+ recordLink: root,
394
+ };
395
+ }
396
+ else if (link === null) {
397
+ results.push(null);
398
+ continue;
399
+ }
400
+ const result = readData(field.selections, link);
401
+ if (result.kind === 'MissingData') {
402
+ return {
403
+ kind: 'MissingData',
404
+ reason: 'Missing data for ' +
405
+ storeRecordName +
406
+ ' on root ' +
407
+ root.__link +
408
+ '. Link is ' +
409
+ JSON.stringify(item),
410
+ nestedReason: result,
411
+ recordLink: result.recordLink,
412
+ };
413
+ }
414
+ results.push(result.data);
415
+ }
416
+ return {
417
+ kind: 'Success',
418
+ data: results,
419
+ };
420
+ }
421
+ let link = (0, IsographEnvironment_1.assertLink)(value);
422
+ if (field.condition) {
423
+ const data = readData(field.condition.readerAst, root);
424
+ if (data.kind === 'MissingData') {
425
+ return {
426
+ kind: 'MissingData',
427
+ reason: 'Missing data for ' + storeRecordName + ' on root ' + root.__link,
428
+ nestedReason: data,
429
+ recordLink: data.recordLink,
430
+ };
431
+ }
432
+ const readerWithRefetchQueries = {
433
+ kind: 'ReaderWithRefetchQueries',
434
+ readerArtifact: field.condition,
435
+ // TODO this is wrong
436
+ // should map field.condition.usedRefetchQueries
437
+ // but it doesn't exist
438
+ nestedRefetchQueries: [],
439
+ };
440
+ const fragment = {
441
+ kind: 'FragmentReference',
442
+ readerWithRefetchQueries: (0, PromiseWrapper_1.wrapResolvedValue)(readerWithRefetchQueries),
443
+ root,
444
+ variables: generateChildVariableMap(variables,
445
+ // TODO this is wrong
446
+ // should use field.condition.variables
447
+ // but it doesn't exist
448
+ []),
449
+ networkRequest,
450
+ };
451
+ const condition = field.condition.resolver(Object.assign({ data: data.data, parameters: {} }, (field.condition.hasUpdatable
452
+ ? {
453
+ startUpdate: (0, startUpdate_1.getOrCreateCachedStartUpdate)(environment, fragment, readerWithRefetchQueries.readerArtifact.fieldName),
454
+ }
455
+ : undefined)));
456
+ if (condition === true) {
457
+ link = root;
458
+ }
459
+ else if (condition === false) {
460
+ link = null;
461
+ }
462
+ else {
463
+ link = condition;
464
+ }
465
+ }
466
+ if (link === undefined) {
467
+ // TODO make this configurable, and also generated and derived from the schema
468
+ const missingFieldHandler = environment.missingFieldHandler;
469
+ const altLink = missingFieldHandler === null || missingFieldHandler === void 0 ? void 0 : missingFieldHandler(storeRecord, root, field.fieldName, field.arguments, variables);
470
+ (0, logging_1.logMessage)(environment, () => ({
471
+ kind: 'MissingFieldHandlerCalled',
472
+ root,
473
+ storeRecord,
474
+ fieldName: field.fieldName,
475
+ arguments: field.arguments,
476
+ variables,
477
+ }));
478
+ if (altLink === undefined) {
479
+ return {
480
+ kind: 'MissingData',
481
+ reason: 'No link for ' +
482
+ storeRecordName +
483
+ ' on root ' +
484
+ root.__link +
485
+ '. Link is ' +
486
+ JSON.stringify(value),
487
+ recordLink: root,
488
+ };
489
+ }
490
+ else {
491
+ link = altLink;
492
+ }
493
+ }
494
+ else if (link === null) {
495
+ return {
496
+ kind: 'Success',
497
+ data: null,
498
+ };
499
+ }
500
+ const targetId = link;
501
+ const data = readData(field.selections, targetId);
502
+ if (data.kind === 'MissingData') {
503
+ return {
504
+ kind: 'MissingData',
505
+ reason: 'Missing data for ' + storeRecordName + ' on root ' + root.__link,
506
+ nestedReason: data,
507
+ recordLink: data.recordLink,
508
+ };
509
+ }
510
+ return data;
511
+ }
394
512
  function getNetworkRequestOptionsWithDefaults(networkRequestOptions) {
395
513
  var _a, _b;
396
514
  return {
@@ -398,7 +516,6 @@ function getNetworkRequestOptionsWithDefaults(networkRequestOptions) {
398
516
  throwOnNetworkError: (_b = networkRequestOptions === null || networkRequestOptions === void 0 ? void 0 : networkRequestOptions.throwOnNetworkError) !== null && _b !== void 0 ? _b : true,
399
517
  };
400
518
  }
401
- exports.getNetworkRequestOptionsWithDefaults = getNetworkRequestOptionsWithDefaults;
402
519
  // TODO use a description of the params for this?
403
520
  // TODO call stableStringifyArgs on the variable values, as well.
404
521
  // This doesn't matter for now, since we are just using primitive values
@@ -413,3 +530,43 @@ function stableStringifyArgs(args) {
413
530
  }
414
531
  return s;
415
532
  }
533
+ function readImperativelyLoadedField(environment, field, root, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords) {
534
+ // First, we read the data using the refetch reader AST (i.e. read out the
535
+ // id field).
536
+ const data = readData(environment, field.refetchReaderArtifact.readerAst, root, variables,
537
+ // Refetch fields just read the id, and don't need refetch query artifacts
538
+ [],
539
+ // This is probably indicative of the fact that we are doing redundant checks
540
+ // on the status of this network request...
541
+ networkRequest, networkRequestOptions, mutableEncounteredRecords);
542
+ if (data.kind === 'MissingData') {
543
+ return {
544
+ kind: 'MissingData',
545
+ reason: 'Missing data for ' + field.alias + ' on root ' + root.__link,
546
+ nestedReason: data,
547
+ recordLink: data.recordLink,
548
+ };
549
+ }
550
+ else {
551
+ const refetchQueryIndex = field.refetchQuery;
552
+ const refetchQuery = nestedRefetchQueries[refetchQueryIndex];
553
+ if (refetchQuery == null) {
554
+ throw new Error('refetchQuery is null in RefetchField. This is indicative of a bug in Isograph.');
555
+ }
556
+ const refetchQueryArtifact = refetchQuery.artifact;
557
+ const allowedVariables = refetchQuery.allowedVariables;
558
+ // Second, we allow the user to call the resolver, which will ultimately
559
+ // use the resolver reader AST to get the resolver parameters.
560
+ return {
561
+ kind: 'Success',
562
+ data: (args) => [
563
+ // Stable id
564
+ root.__link + '__' + field.name,
565
+ // Fetcher
566
+ field.refetchReaderArtifact.resolver(environment, refetchQueryArtifact, data.data, filterVariables(Object.assign(Object.assign({}, args), variables), allowedVariables), root,
567
+ // TODO these params should be removed
568
+ null, []),
569
+ ],
570
+ };
571
+ }
572
+ }