@isograph/react 0.4.2 → 0.5.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 (144) hide show
  1. package/.turbo/turbo-compile-libs.log +2 -2
  2. package/dist/core/FragmentReference.d.ts +4 -2
  3. package/dist/core/FragmentReference.d.ts.map +1 -1
  4. package/dist/core/FragmentReference.js +2 -2
  5. package/dist/core/IsographEnvironment.d.ts +19 -11
  6. package/dist/core/IsographEnvironment.d.ts.map +1 -1
  7. package/dist/core/IsographEnvironment.js +27 -2
  8. package/dist/core/PromiseWrapper.d.ts +13 -7
  9. package/dist/core/PromiseWrapper.d.ts.map +1 -1
  10. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -1
  11. package/dist/core/areEqualWithDeepComparison.js +5 -0
  12. package/dist/core/brand.d.ts +17 -0
  13. package/dist/core/brand.d.ts.map +1 -1
  14. package/dist/core/cache.d.ts +10 -7
  15. package/dist/core/cache.d.ts.map +1 -1
  16. package/dist/core/cache.js +102 -74
  17. package/dist/core/check.d.ts +8 -4
  18. package/dist/core/check.d.ts.map +1 -1
  19. package/dist/core/check.js +10 -7
  20. package/dist/core/componentCache.d.ts +1 -1
  21. package/dist/core/componentCache.d.ts.map +1 -1
  22. package/dist/core/componentCache.js +6 -4
  23. package/dist/core/entrypoint.d.ts +17 -7
  24. package/dist/core/entrypoint.d.ts.map +1 -1
  25. package/dist/core/garbageCollection.d.ts +8 -2
  26. package/dist/core/garbageCollection.d.ts.map +1 -1
  27. package/dist/core/garbageCollection.js +36 -14
  28. package/dist/core/logging.d.ts +16 -3
  29. package/dist/core/logging.d.ts.map +1 -1
  30. package/dist/core/makeNetworkRequest.d.ts +4 -2
  31. package/dist/core/makeNetworkRequest.d.ts.map +1 -1
  32. package/dist/core/makeNetworkRequest.js +115 -38
  33. package/dist/core/optimisticProxy.d.ts +59 -0
  34. package/dist/core/optimisticProxy.d.ts.map +1 -0
  35. package/dist/core/optimisticProxy.js +399 -0
  36. package/dist/core/read.d.ts +3 -2
  37. package/dist/core/read.d.ts.map +1 -1
  38. package/dist/core/read.js +158 -123
  39. package/dist/core/reader.d.ts +10 -5
  40. package/dist/core/reader.d.ts.map +1 -1
  41. package/dist/core/startUpdate.d.ts +3 -2
  42. package/dist/core/startUpdate.d.ts.map +1 -1
  43. package/dist/core/startUpdate.js +35 -36
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +2 -1
  47. package/dist/loadable-hooks/useClientSideDefer.d.ts +9 -4
  48. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -1
  49. package/dist/loadable-hooks/useClientSideDefer.js +34 -1
  50. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +5 -3
  51. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -1
  52. package/dist/loadable-hooks/useConnectionSpecPagination.js +27 -13
  53. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +1 -1
  54. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -1
  55. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +1 -1
  56. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -1
  57. package/dist/loadable-hooks/useSkipLimitPagination.js +1 -1
  58. package/dist/react/FragmentReader.d.ts +2 -1
  59. package/dist/react/FragmentReader.d.ts.map +1 -1
  60. package/dist/react/FragmentRenderer.d.ts +2 -1
  61. package/dist/react/FragmentRenderer.d.ts.map +1 -1
  62. package/dist/react/LoadableFieldReader.d.ts +9 -3
  63. package/dist/react/LoadableFieldReader.d.ts.map +1 -1
  64. package/dist/react/LoadableFieldReader.js +40 -1
  65. package/dist/react/LoadableFieldRenderer.d.ts +9 -3
  66. package/dist/react/LoadableFieldRenderer.d.ts.map +1 -1
  67. package/dist/react/LoadableFieldRenderer.js +36 -1
  68. package/dist/react/useImperativeReference.d.ts +4 -3
  69. package/dist/react/useImperativeReference.d.ts.map +1 -1
  70. package/dist/react/useImperativeReference.js +3 -5
  71. package/dist/react/useLazyReference.d.ts +2 -1
  72. package/dist/react/useLazyReference.d.ts.map +1 -1
  73. package/dist/react/useReadAndSubscribe.d.ts.map +1 -1
  74. package/dist/react/useReadAndSubscribe.js +1 -3
  75. package/dist/react/useResult.d.ts.map +1 -1
  76. package/dist/react/useResult.js +6 -5
  77. package/package.json +16 -17
  78. package/src/core/FragmentReference.ts +10 -4
  79. package/src/core/IsographEnvironment.ts +59 -13
  80. package/src/core/PromiseWrapper.ts +14 -7
  81. package/src/core/areEqualWithDeepComparison.ts +5 -0
  82. package/src/core/brand.ts +18 -0
  83. package/src/core/cache.ts +186 -91
  84. package/src/core/check.ts +21 -10
  85. package/src/core/componentCache.ts +8 -4
  86. package/src/core/entrypoint.ts +35 -6
  87. package/src/core/garbageCollection.ts +61 -19
  88. package/src/core/logging.ts +15 -3
  89. package/src/core/makeNetworkRequest.ts +307 -74
  90. package/src/core/optimisticProxy.ts +563 -0
  91. package/src/core/read.ts +233 -163
  92. package/src/core/reader.ts +11 -7
  93. package/src/core/startUpdate.ts +47 -32
  94. package/src/index.ts +2 -1
  95. package/src/loadable-hooks/useClientSideDefer.ts +76 -26
  96. package/src/loadable-hooks/useConnectionSpecPagination.ts +34 -17
  97. package/src/loadable-hooks/useImperativeLoadableField.ts +2 -2
  98. package/src/loadable-hooks/useSkipLimitPagination.ts +2 -3
  99. package/src/react/FragmentReader.tsx +3 -1
  100. package/src/react/FragmentRenderer.tsx +8 -1
  101. package/src/react/LoadableFieldReader.tsx +123 -12
  102. package/src/react/LoadableFieldRenderer.tsx +122 -12
  103. package/src/react/useImperativeReference.ts +20 -11
  104. package/src/react/useLazyReference.ts +17 -6
  105. package/src/react/useReadAndSubscribe.ts +1 -8
  106. package/src/react/useResult.ts +9 -11
  107. package/src/tests/__isograph/Node/__link/output_type.ts +3 -0
  108. package/src/tests/__isograph/Node/asEconomist/resolver_reader.ts +3 -3
  109. package/src/tests/__isograph/Query/linkedUpdate/entrypoint.ts +3 -1
  110. package/src/tests/__isograph/Query/linkedUpdate/param_type.ts +4 -4
  111. package/src/tests/__isograph/Query/linkedUpdate/raw_response_type.ts +13 -0
  112. package/src/tests/__isograph/Query/linkedUpdate/resolver_reader.ts +6 -6
  113. package/src/tests/__isograph/Query/meName/entrypoint.ts +3 -1
  114. package/src/tests/__isograph/Query/meName/raw_response_type.ts +7 -0
  115. package/src/tests/__isograph/Query/meName/resolver_reader.ts +2 -2
  116. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +3 -1
  117. package/src/tests/__isograph/Query/meNameSuccessor/raw_response_type.ts +14 -0
  118. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +4 -4
  119. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +3 -1
  120. package/src/tests/__isograph/Query/nodeField/raw_response_type.ts +7 -0
  121. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +2 -2
  122. package/src/tests/__isograph/Query/normalizeUndefinedField/entrypoint.ts +33 -0
  123. package/src/tests/__isograph/Query/normalizeUndefinedField/normalization_ast.ts +25 -0
  124. package/src/tests/__isograph/Query/normalizeUndefinedField/output_type.ts +3 -0
  125. package/src/tests/__isograph/Query/normalizeUndefinedField/param_type.ts +9 -0
  126. package/src/tests/__isograph/Query/normalizeUndefinedField/query_text.ts +6 -0
  127. package/src/tests/__isograph/Query/normalizeUndefinedField/raw_response_type.ts +7 -0
  128. package/src/tests/__isograph/Query/normalizeUndefinedField/resolver_reader.ts +38 -0
  129. package/src/tests/__isograph/Query/startUpdate/entrypoint.ts +3 -1
  130. package/src/tests/__isograph/Query/startUpdate/raw_response_type.ts +8 -0
  131. package/src/tests/__isograph/Query/startUpdate/resolver_reader.ts +3 -3
  132. package/src/tests/__isograph/Query/subquery/entrypoint.ts +3 -1
  133. package/src/tests/__isograph/Query/subquery/raw_response_type.ts +9 -0
  134. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +3 -3
  135. package/src/tests/__isograph/iso.ts +11 -1
  136. package/src/tests/garbageCollection.test.ts +8 -5
  137. package/src/tests/meNameSuccessor.ts +6 -3
  138. package/src/tests/nodeQuery.ts +4 -2
  139. package/src/tests/normalizeData.test.ts +89 -15
  140. package/src/tests/optimisticProxy.test.ts +860 -0
  141. package/src/tests/startUpdate.test.ts +8 -6
  142. package/vitest.config.ts +10 -1
  143. package/src/tests/__isograph/Economist/link/output_type.ts +0 -2
  144. package/src/tests/__isograph/Node/link/output_type.ts +0 -3
@@ -1,5 +1,10 @@
1
1
  import { ItemCleanupPair } from '@isograph/disposable-types';
2
- import { normalizeData } from './cache';
2
+ import {
3
+ callSubscriptions,
4
+ normalizeData,
5
+ type EncounteredIds,
6
+ type NetworkResponseObject,
7
+ } from './cache';
3
8
  import { check, DEFAULT_SHOULD_FETCH_VALUE, FetchOptions } from './check';
4
9
  import { getOrCreateCachedComponent } from './componentCache';
5
10
  import {
@@ -22,6 +27,13 @@ import {
22
27
  } from './garbageCollection';
23
28
  import { IsographEnvironment, ROOT_ID, StoreLink } from './IsographEnvironment';
24
29
  import { logMessage } from './logging';
30
+ import {
31
+ addNetworkResponseStoreLayer,
32
+ addOptimisticNetworkResponseStoreLayer,
33
+ revertOptimisticStoreLayerAndMaybeReplace,
34
+ type OptimisticStoreLayer,
35
+ type StoreLayerWithData,
36
+ } from './optimisticProxy';
25
37
  import {
26
38
  AnyError,
27
39
  PromiseWrapper,
@@ -36,18 +48,23 @@ let networkRequestId = 0;
36
48
  export function maybeMakeNetworkRequest<
37
49
  TReadFromStore extends UnknownTReadFromStore,
38
50
  TClientFieldValue,
39
- TArtifact extends
40
- | RefetchQueryNormalizationArtifact
41
- | IsographEntrypoint<TReadFromStore, TClientFieldValue, TNormalizationAst>,
42
51
  TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
52
+ TRawResponseType extends NetworkResponseObject,
43
53
  >(
44
54
  environment: IsographEnvironment,
45
- artifact: TArtifact,
55
+ artifact:
56
+ | RefetchQueryNormalizationArtifact
57
+ | IsographEntrypoint<
58
+ TReadFromStore,
59
+ TClientFieldValue,
60
+ TNormalizationAst,
61
+ TRawResponseType
62
+ >,
46
63
  variables: ExtractParameters<TReadFromStore>,
47
64
  readerWithRefetchQueries: PromiseWrapper<
48
65
  ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>
49
66
  > | null,
50
- fetchOptions: FetchOptions<TClientFieldValue> | null,
67
+ fetchOptions: FetchOptions<TClientFieldValue, TRawResponseType> | null,
51
68
  ): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
52
69
  switch (fetchOptions?.shouldFetch ?? DEFAULT_SHOULD_FETCH_VALUE) {
53
70
  case 'Yes': {
@@ -60,7 +77,11 @@ export function maybeMakeNetworkRequest<
60
77
  );
61
78
  }
62
79
  case 'No': {
63
- return [wrapResolvedValue(undefined), () => {}];
80
+ return retainQueryWithoutMakingNetworkRequest(
81
+ environment,
82
+ artifact,
83
+ variables,
84
+ );
64
85
  }
65
86
  case 'IfNecessary': {
66
87
  if (
@@ -68,7 +89,8 @@ export function maybeMakeNetworkRequest<
68
89
  'NormalizationAstLoader'
69
90
  ) {
70
91
  throw new Error(
71
- 'Using lazy loaded normalizationAst with shouldFetch: "IfNecessary" is not supported as it will lead to slower initial load time.',
92
+ 'Using lazy loaded normalizationAst with shouldFetch: "IfNecessary" is ' +
93
+ 'not supported as it will lead to a network waterfall.',
72
94
  );
73
95
  }
74
96
  const result = check(
@@ -82,7 +104,11 @@ export function maybeMakeNetworkRequest<
82
104
  );
83
105
 
84
106
  if (result.kind === 'EnoughData') {
85
- return [wrapResolvedValue(undefined), () => {}];
107
+ return retainQueryWithoutMakingNetworkRequest(
108
+ environment,
109
+ artifact,
110
+ variables,
111
+ );
86
112
  } else {
87
113
  return makeNetworkRequest(
88
114
  environment,
@@ -96,38 +122,83 @@ export function maybeMakeNetworkRequest<
96
122
  }
97
123
  }
98
124
 
99
- function loadNormalizationAst(
100
- normalizationAst: NormalizationAstLoader | NormalizationAst,
101
- ) {
102
- switch (normalizationAst.kind) {
103
- case 'NormalizationAst': {
104
- return normalizationAst;
105
- }
106
- case 'NormalizationAstLoader': {
107
- return normalizationAst.loader();
108
- }
109
- }
125
+ export function retainQueryWithoutMakingNetworkRequest<
126
+ TReadFromStore extends UnknownTReadFromStore,
127
+ TClientFieldValue,
128
+ TRawResponseType extends NetworkResponseObject,
129
+ >(
130
+ environment: IsographEnvironment,
131
+ artifact:
132
+ | RefetchQueryNormalizationArtifact
133
+ | IsographEntrypoint<
134
+ TReadFromStore,
135
+ TClientFieldValue,
136
+ NormalizationAst | NormalizationAstLoader,
137
+ TRawResponseType
138
+ >,
139
+ variables: ExtractParameters<TReadFromStore>,
140
+ ): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
141
+ let status:
142
+ | NetworkRequestStatusUndisposedComplete
143
+ | NetworkRequestStatusDisposed = {
144
+ kind: 'UndisposedComplete',
145
+ retainedQuery: fetchNormalizationAstAndRetainArtifact(
146
+ environment,
147
+ artifact,
148
+ variables,
149
+ ),
150
+ };
151
+ return [
152
+ wrapResolvedValue(undefined),
153
+ () => {
154
+ if (status.kind !== 'Disposed') {
155
+ status = unretainAndGarbageCollect(environment, status);
156
+ }
157
+ },
158
+ ];
110
159
  }
111
160
 
112
161
  export function makeNetworkRequest<
113
162
  TReadFromStore extends UnknownTReadFromStore,
114
163
  TClientFieldValue,
115
- TArtifact extends
116
- | RefetchQueryNormalizationArtifact
117
- | IsographEntrypoint<TReadFromStore, TClientFieldValue, TNormalizationAst>,
118
164
  TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
165
+ TRawResponseType extends NetworkResponseObject,
119
166
  >(
120
167
  environment: IsographEnvironment,
121
- artifact: TArtifact,
168
+ artifact:
169
+ | RefetchQueryNormalizationArtifact
170
+ | IsographEntrypoint<
171
+ TReadFromStore,
172
+ TClientFieldValue,
173
+ TNormalizationAst,
174
+ TRawResponseType
175
+ >,
122
176
  variables: ExtractParameters<TReadFromStore>,
123
177
  readerWithRefetchQueries: PromiseWrapper<
124
178
  ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>
125
179
  > | null,
126
- fetchOptions: FetchOptions<TClientFieldValue> | null,
180
+ fetchOptions: FetchOptions<TClientFieldValue, TRawResponseType> | null,
127
181
  ): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
128
182
  // TODO this should be a DataId and stored in the store
129
183
  const myNetworkRequestId = networkRequestId + '';
130
184
  networkRequestId++;
185
+ let status: NetworkRequestStatus = {
186
+ kind: 'UndisposedIncomplete',
187
+ retainedQuery: fetchNormalizationAstAndRetainArtifact(
188
+ environment,
189
+ artifact,
190
+ variables,
191
+ ),
192
+ optimistic:
193
+ fetchOptions?.optimisticNetworkResponse != null
194
+ ? makeOptimisticUpdate(
195
+ environment,
196
+ artifact,
197
+ variables,
198
+ fetchOptions?.optimisticNetworkResponse,
199
+ )
200
+ : null,
201
+ };
131
202
 
132
203
  logMessage(environment, () => ({
133
204
  kind: 'MakeNetworkRequest',
@@ -136,16 +207,13 @@ export function makeNetworkRequest<
136
207
  networkRequestId: myNetworkRequestId,
137
208
  }));
138
209
 
139
- let status: NetworkRequestStatus = {
140
- kind: 'UndisposedIncomplete',
141
- };
142
210
  // This should be an observable, not a promise
143
211
  const promise = Promise.all([
144
212
  environment.networkFunction(
145
213
  artifact.networkRequestInfo.operation,
146
214
  variables,
147
215
  ),
148
- loadNormalizationAst(artifact.networkRequestInfo.normalizationAst),
216
+ status.retainedQuery.normalizationAst.promise,
149
217
  readerWithRefetchQueries?.promise,
150
218
  ])
151
219
  .then(([networkResponse, normalizationAst, readerWithRefetchQueries]) => {
@@ -159,30 +227,56 @@ export function makeNetworkRequest<
159
227
  try {
160
228
  fetchOptions?.onError?.();
161
229
  } catch {}
162
- throw new Error('GraphQL network response had errors', {
230
+ throw new Error('Network response had errors', {
163
231
  cause: networkResponse,
164
232
  });
165
233
  }
166
234
 
167
235
  const root = { __link: ROOT_ID, __typename: artifact.concreteType };
236
+
168
237
  if (status.kind === 'UndisposedIncomplete') {
169
- normalizeData(
170
- environment,
171
- normalizationAst.selections,
172
- networkResponse.data ?? {},
173
- variables,
174
- root,
175
- );
176
- const retainedQuery = {
177
- normalizationAst: normalizationAst.selections,
178
- variables,
179
- root,
180
- };
181
- status = {
182
- kind: 'UndisposedComplete',
183
- retainedQuery,
184
- };
185
- retainQuery(environment, retainedQuery);
238
+ if (status.optimistic != null) {
239
+ status =
240
+ revertOptimisticStoreLayerAndMaybeReplaceIfUndisposedIncomplete(
241
+ environment,
242
+ status,
243
+ (storeLayer) =>
244
+ normalizeData(
245
+ environment,
246
+ storeLayer,
247
+ normalizationAst.selections,
248
+ networkResponse.data ?? {},
249
+ variables,
250
+ root,
251
+ new Map(),
252
+ ),
253
+ );
254
+ } else {
255
+ const encounteredIds: EncounteredIds = new Map();
256
+ environment.store = addNetworkResponseStoreLayer(environment.store);
257
+ normalizeData(
258
+ environment,
259
+ environment.store,
260
+ normalizationAst.selections,
261
+ networkResponse.data ?? {},
262
+ variables,
263
+ root,
264
+ encounteredIds,
265
+ );
266
+
267
+ logMessage(environment, () => ({
268
+ kind: 'AfterNormalization',
269
+ store: environment.store,
270
+ encounteredIds: encounteredIds,
271
+ }));
272
+
273
+ callSubscriptions(environment, encounteredIds);
274
+
275
+ status = {
276
+ kind: 'UndisposedComplete',
277
+ retainedQuery: status.retainedQuery,
278
+ };
279
+ }
186
280
  }
187
281
 
188
282
  const onComplete = fetchOptions?.onComplete;
@@ -213,6 +307,16 @@ export function makeNetworkRequest<
213
307
  try {
214
308
  fetchOptions?.onError?.();
215
309
  } catch {}
310
+
311
+ if (status.kind === 'UndisposedIncomplete') {
312
+ status =
313
+ revertOptimisticStoreLayerAndMaybeReplaceIfUndisposedIncomplete(
314
+ environment,
315
+ status,
316
+ null,
317
+ );
318
+ }
319
+
216
320
  throw e;
217
321
  });
218
322
 
@@ -221,44 +325,56 @@ export function makeNetworkRequest<
221
325
  const response: ItemCleanupPair<PromiseWrapper<void, AnyError>> = [
222
326
  wrapper,
223
327
  () => {
224
- if (status.kind === 'UndisposedComplete') {
225
- const didUnretainSomeQuery = unretainQuery(
226
- environment,
227
- status.retainedQuery,
228
- );
229
- if (didUnretainSomeQuery) {
230
- garbageCollectEnvironment(environment);
231
- }
328
+ if (status.kind === 'UndisposedIncomplete') {
329
+ status =
330
+ revertOptimisticStoreLayerAndMaybeReplaceIfUndisposedIncomplete(
331
+ environment,
332
+ status,
333
+ null,
334
+ );
335
+ }
336
+ if (status.kind !== 'Disposed') {
337
+ status = unretainAndGarbageCollect(environment, status);
232
338
  }
233
- status = {
234
- kind: 'Disposed',
235
- };
236
339
  },
237
340
  ];
238
341
  return response;
239
342
  }
240
343
 
344
+ type NetworkRequestStatusUndisposedIncomplete = {
345
+ readonly kind: 'UndisposedIncomplete';
346
+ readonly retainedQuery: RetainedQuery;
347
+ readonly optimistic: OptimisticStoreLayer | null;
348
+ };
349
+
350
+ type NetworkRequestStatusUndisposedComplete = {
351
+ readonly kind: 'UndisposedComplete';
352
+ readonly retainedQuery: RetainedQuery;
353
+ };
354
+
355
+ type NetworkRequestStatusDisposed = {
356
+ readonly kind: 'Disposed';
357
+ };
358
+
241
359
  type NetworkRequestStatus =
242
- | {
243
- readonly kind: 'UndisposedIncomplete';
244
- }
245
- | {
246
- readonly kind: 'Disposed';
247
- }
248
- | {
249
- readonly kind: 'UndisposedComplete';
250
- readonly retainedQuery: RetainedQuery;
251
- };
360
+ | NetworkRequestStatusUndisposedIncomplete
361
+ | NetworkRequestStatusUndisposedComplete
362
+ | NetworkRequestStatusDisposed;
252
363
 
253
364
  function readDataForOnComplete<
254
365
  TReadFromStore extends UnknownTReadFromStore,
255
366
  TClientFieldValue,
256
- TArtifact extends
257
- | RefetchQueryNormalizationArtifact
258
- | IsographEntrypoint<TReadFromStore, TClientFieldValue, TNormalizationAst>,
259
367
  TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
368
+ TRawResponseType extends NetworkResponseObject,
260
369
  >(
261
- artifact: TArtifact,
370
+ artifact:
371
+ | RefetchQueryNormalizationArtifact
372
+ | IsographEntrypoint<
373
+ TReadFromStore,
374
+ TClientFieldValue,
375
+ TNormalizationAst,
376
+ TRawResponseType
377
+ >,
262
378
  environment: IsographEnvironment,
263
379
  root: StoreLink,
264
380
  variables: ExtractParameters<TReadFromStore>,
@@ -293,6 +409,8 @@ function readDataForOnComplete<
293
409
  readerWithRefetchQueries: wrapResolvedValue(
294
410
  resolvedReaderWithRefetchQueries,
295
411
  ),
412
+ fieldName: resolvedReaderWithRefetchQueries.readerArtifact.fieldName,
413
+ readerArtifactKind: resolvedReaderWithRefetchQueries.readerArtifact.kind,
296
414
  root,
297
415
  variables,
298
416
  networkRequest: fakeNetworkRequest,
@@ -310,7 +428,6 @@ function readDataForOnComplete<
310
428
  // TClientFieldValue which is a React.FC<...>
311
429
  return getOrCreateCachedComponent(
312
430
  environment,
313
- readerArtifact.fieldName,
314
431
  {
315
432
  kind: 'FragmentReference',
316
433
  readerWithRefetchQueries: wrapResolvedValue({
@@ -319,6 +436,8 @@ function readDataForOnComplete<
319
436
  nestedRefetchQueries:
320
437
  resolvedReaderWithRefetchQueries.nestedRefetchQueries,
321
438
  }),
439
+ fieldName: readerArtifact.fieldName,
440
+ readerArtifactKind: readerArtifact.kind,
322
441
  root,
323
442
  variables,
324
443
  networkRequest: fakeNetworkRequest,
@@ -335,7 +454,6 @@ function readDataForOnComplete<
335
454
  startUpdate: getOrCreateCachedStartUpdate(
336
455
  environment,
337
456
  fragment,
338
- resolvedReaderWithRefetchQueries.readerArtifact.fieldName,
339
457
  fakeNetworkRequestOptions,
340
458
  ),
341
459
  }
@@ -351,3 +469,118 @@ function readDataForOnComplete<
351
469
  }
352
470
  return null;
353
471
  }
472
+
473
+ function fetchNormalizationAstAndRetainArtifact<
474
+ TReadFromStore extends UnknownTReadFromStore,
475
+ TClientFieldValue,
476
+ TRawResponseType extends NetworkResponseObject,
477
+ >(
478
+ environment: IsographEnvironment,
479
+ artifact:
480
+ | RefetchQueryNormalizationArtifact
481
+ | IsographEntrypoint<
482
+ TReadFromStore,
483
+ TClientFieldValue,
484
+ NormalizationAst | NormalizationAstLoader,
485
+ TRawResponseType
486
+ >,
487
+ variables: ExtractParameters<TReadFromStore>,
488
+ ): RetainedQuery {
489
+ const normalizationAst =
490
+ artifact.networkRequestInfo.normalizationAst.kind === 'NormalizationAst'
491
+ ? wrapResolvedValue(artifact.networkRequestInfo.normalizationAst)
492
+ : wrapPromise(artifact.networkRequestInfo.normalizationAst.loader());
493
+
494
+ const root = { __link: ROOT_ID, __typename: artifact.concreteType };
495
+ const retainedQuery: RetainedQuery = {
496
+ normalizationAst: normalizationAst,
497
+ variables,
498
+ root,
499
+ };
500
+ retainQuery(environment, retainedQuery);
501
+ return retainedQuery;
502
+ }
503
+
504
+ function makeOptimisticUpdate<
505
+ TReadFromStore extends UnknownTReadFromStore,
506
+ TClientFieldValue,
507
+ TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
508
+ TRawResponseType extends NetworkResponseObject,
509
+ >(
510
+ environment: IsographEnvironment,
511
+ artifact:
512
+ | RefetchQueryNormalizationArtifact
513
+ | IsographEntrypoint<
514
+ TReadFromStore,
515
+ TClientFieldValue,
516
+ TNormalizationAst,
517
+ TRawResponseType
518
+ >,
519
+ variables: ExtractParameters<TReadFromStore>,
520
+ optimisticNetworkResponse: TRawResponseType,
521
+ ): OptimisticStoreLayer {
522
+ const root = { __link: ROOT_ID, __typename: artifact.concreteType };
523
+
524
+ if (
525
+ artifact.networkRequestInfo.normalizationAst.kind ===
526
+ 'NormalizationAstLoader'
527
+ ) {
528
+ throw new Error(
529
+ 'Using lazy loaded normalizationAst with optimisticNetworkResponse is not supported.',
530
+ );
531
+ }
532
+ const encounteredIds: EncounteredIds = new Map();
533
+ const optimistic = (environment.store =
534
+ addOptimisticNetworkResponseStoreLayer(environment.store));
535
+ normalizeData(
536
+ environment,
537
+ environment.store,
538
+ artifact.networkRequestInfo.normalizationAst.selections,
539
+ optimisticNetworkResponse,
540
+ variables,
541
+ root,
542
+ encounteredIds,
543
+ );
544
+
545
+ logMessage(environment, () => ({
546
+ kind: 'AfterNormalization',
547
+ store: environment.store,
548
+ encounteredIds: encounteredIds,
549
+ }));
550
+
551
+ callSubscriptions(environment, encounteredIds);
552
+ return optimistic;
553
+ }
554
+
555
+ function revertOptimisticStoreLayerAndMaybeReplaceIfUndisposedIncomplete(
556
+ environment: IsographEnvironment,
557
+ status: NetworkRequestStatusUndisposedIncomplete,
558
+ normalizeData: null | ((storeLayer: StoreLayerWithData) => void),
559
+ ): NetworkRequestStatusUndisposedComplete {
560
+ if (status.optimistic) {
561
+ revertOptimisticStoreLayerAndMaybeReplace(
562
+ environment,
563
+ status.optimistic,
564
+ normalizeData,
565
+ );
566
+ }
567
+
568
+ return {
569
+ kind: 'UndisposedComplete',
570
+ retainedQuery: status.retainedQuery,
571
+ };
572
+ }
573
+
574
+ function unretainAndGarbageCollect(
575
+ environment: IsographEnvironment,
576
+ status: NetworkRequestStatusUndisposedComplete,
577
+ ): NetworkRequestStatusDisposed {
578
+ const didUnretainSomeQuery = unretainQuery(environment, status.retainedQuery);
579
+ if (didUnretainSomeQuery) {
580
+ garbageCollectEnvironment(environment);
581
+ }
582
+
583
+ return {
584
+ kind: 'Disposed',
585
+ };
586
+ }