@isograph/react 0.0.0-main-5141cf39 → 0.0.0-main-7f95f0cd

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 (45) hide show
  1. package/.turbo/turbo-compile-libs.log +1 -1
  2. package/dist/core/cache.d.ts +1 -1
  3. package/dist/core/cache.d.ts.map +1 -1
  4. package/dist/core/check.d.ts +8 -4
  5. package/dist/core/check.d.ts.map +1 -1
  6. package/dist/core/makeNetworkRequest.d.ts +2 -2
  7. package/dist/core/makeNetworkRequest.d.ts.map +1 -1
  8. package/dist/core/makeNetworkRequest.js +72 -23
  9. package/dist/core/optimisticProxy.d.ts +13 -5
  10. package/dist/core/optimisticProxy.d.ts.map +1 -1
  11. package/dist/core/optimisticProxy.js +35 -8
  12. package/dist/core/reader.d.ts +1 -1
  13. package/dist/core/reader.d.ts.map +1 -1
  14. package/dist/loadable-hooks/useClientSideDefer.d.ts +2 -2
  15. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -1
  16. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +1 -1
  17. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -1
  18. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +1 -1
  19. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -1
  20. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +1 -1
  21. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -1
  22. package/dist/react/LoadableFieldReader.d.ts +1 -1
  23. package/dist/react/LoadableFieldReader.d.ts.map +1 -1
  24. package/dist/react/LoadableFieldRenderer.d.ts +1 -1
  25. package/dist/react/LoadableFieldRenderer.d.ts.map +1 -1
  26. package/dist/react/useImperativeReference.d.ts +3 -3
  27. package/dist/react/useImperativeReference.d.ts.map +1 -1
  28. package/dist/react/useLazyReference.d.ts +1 -1
  29. package/dist/react/useLazyReference.d.ts.map +1 -1
  30. package/package.json +4 -4
  31. package/src/core/cache.ts +1 -1
  32. package/src/core/check.ts +10 -4
  33. package/src/core/makeNetworkRequest.ts +180 -43
  34. package/src/core/optimisticProxy.ts +67 -14
  35. package/src/core/read.ts +2 -2
  36. package/src/core/reader.ts +1 -1
  37. package/src/loadable-hooks/useClientSideDefer.ts +2 -2
  38. package/src/loadable-hooks/useConnectionSpecPagination.ts +5 -2
  39. package/src/loadable-hooks/useImperativeLoadableField.ts +2 -2
  40. package/src/loadable-hooks/useSkipLimitPagination.ts +2 -2
  41. package/src/react/LoadableFieldReader.tsx +1 -1
  42. package/src/react/LoadableFieldRenderer.tsx +1 -1
  43. package/src/react/useImperativeReference.ts +5 -3
  44. package/src/react/useLazyReference.ts +1 -1
  45. package/src/tests/optimisticProxy.test.ts +19 -19
@@ -1 +1 @@
1
- {"version":3,"file":"useLazyReference.d.ts","sourceRoot":"","sources":["../../src/react/useLazyReference.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,qBAAqB,EAC3B,MAAM,2BAA2B,CAAC;AAInC,wBAAgB,gBAAgB,CAC9B,cAAc,SAAS,qBAAqB,EAC5C,iBAAiB,EACjB,iBAAiB,SAAS,gBAAgB,GAAG,sBAAsB,EACnE,gBAAgB,SAAS,qBAAqB,EAE9C,UAAU,EAAE,kBAAkB,CAC5B,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,CACjB,EACD,SAAS,EAAE,iBAAiB,CAAC,cAAc,CAAC,EAC5C,GAAG,CAAC,YAAY,CAAC,EAAE,iBAAiB,SAAS,sBAAsB,GAC/D,CAAC,YAAY,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,CAAC,GACvD,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC,GACnD,gBAAgB,GAAG,sBAAsB,SAAS,iBAAiB,GAClE,OAAO,GACP;IACE,iBAAiB,EAAE,iBAAiB,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;CACzE,CAqBJ"}
1
+ {"version":3,"file":"useLazyReference.d.ts","sourceRoot":"","sources":["../../src/react/useLazyReference.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,qBAAqB,EAC3B,MAAM,2BAA2B,CAAC;AAInC,wBAAgB,gBAAgB,CAC9B,cAAc,SAAS,qBAAqB,EAC5C,iBAAiB,EACjB,iBAAiB,SAAS,gBAAgB,GAAG,sBAAsB,EACnE,gBAAgB,SAAS,qBAAqB,EAE9C,UAAU,EAAE,kBAAkB,CAC5B,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,CACjB,EACD,SAAS,EAAE,iBAAiB,CAAC,cAAc,CAAC,EAC5C,GAAG,CAAC,YAAY,CAAC,EAAE,iBAAiB,SAAS,sBAAsB,GAC/D,CAAC,YAAY,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,CAAC,GACvD,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,GACrE,gBAAgB,GAAG,sBAAsB,SAAS,iBAAiB,GAClE,OAAO,GACP;IACE,iBAAiB,EAAE,iBAAiB,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;CACzE,CAqBJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isograph/react",
3
- "version": "0.0.0-main-5141cf39",
3
+ "version": "0.0.0-main-7f95f0cd",
4
4
  "description": "Use Isograph with React",
5
5
  "homepage": "https://isograph.dev",
6
6
  "main": "dist/index.js",
@@ -8,9 +8,9 @@
8
8
  "author": "Isograph Labs",
9
9
  "license": "MIT",
10
10
  "dependencies": {
11
- "@isograph/disposable-types": "0.0.0-main-5141cf39",
12
- "@isograph/reference-counted-pointer": "0.0.0-main-5141cf39",
13
- "@isograph/react-disposable-state": "0.0.0-main-5141cf39"
11
+ "@isograph/disposable-types": "0.0.0-main-7f95f0cd",
12
+ "@isograph/reference-counted-pointer": "0.0.0-main-7f95f0cd",
13
+ "@isograph/react-disposable-state": "0.0.0-main-7f95f0cd"
14
14
  },
15
15
  "peerDependencies": {
16
16
  "react": "^18.0.0 || ^19.0.0"
package/src/core/cache.ts CHANGED
@@ -100,7 +100,7 @@ export function getOrCreateCacheForArtifact<
100
100
  TRawResponseType
101
101
  >,
102
102
  variables: ExtractParameters<TReadFromStore>,
103
- fetchOptions?: FetchOptions<TClientFieldValue>,
103
+ fetchOptions?: FetchOptions<TClientFieldValue, TRawResponseType>,
104
104
  ): ParentCache<FragmentReference<TReadFromStore, TClientFieldValue>> {
105
105
  let cacheKey = '';
106
106
  switch (entrypoint.networkRequestInfo.operation.kind) {
package/src/core/check.ts CHANGED
@@ -15,15 +15,21 @@ export type RequiredShouldFetch = 'Yes' | 'No';
15
15
 
16
16
  export const DEFAULT_SHOULD_FETCH_VALUE: ShouldFetch = 'IfNecessary';
17
17
 
18
- export type FetchOptions<TReadOutData> = {
19
- shouldFetch?: ShouldFetch;
18
+ type FetchOptionsShared<TReadOutData> = {
20
19
  onComplete?: (data: TReadOutData) => void;
21
20
  onError?: () => void;
22
21
  };
23
22
 
24
- export type RequiredFetchOptions<TReadOutData> = {
23
+ export interface FetchOptions<TReadOutData, TRawResponseType>
24
+ extends FetchOptionsShared<TReadOutData> {
25
+ shouldFetch?: ShouldFetch;
26
+ optimisticNetworkResponse?: TRawResponseType;
27
+ }
28
+
29
+ export interface RequiredFetchOptions<TReadOutData>
30
+ extends FetchOptionsShared<TReadOutData> {
25
31
  shouldFetch: RequiredShouldFetch;
26
- } & FetchOptions<TReadOutData>;
32
+ }
27
33
 
28
34
  export type CheckResult =
29
35
  | {
@@ -27,7 +27,13 @@ import {
27
27
  } from './garbageCollection';
28
28
  import { IsographEnvironment, ROOT_ID, StoreLink } from './IsographEnvironment';
29
29
  import { logMessage } from './logging';
30
- import { addNetworkResponseStoreLayer } from './optimisticProxy';
30
+ import {
31
+ addNetworkResponseStoreLayer,
32
+ addOptimisticNetworkResponseStoreLayer,
33
+ revertOptimisticStoreLayerAndMaybeReplace,
34
+ type OptimisticStoreLayer,
35
+ type StoreLayerWithData,
36
+ } from './optimisticProxy';
31
37
  import {
32
38
  AnyError,
33
39
  PromiseWrapper,
@@ -58,7 +64,7 @@ export function maybeMakeNetworkRequest<
58
64
  readerWithRefetchQueries: PromiseWrapper<
59
65
  ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>
60
66
  > | null,
61
- fetchOptions: FetchOptions<TClientFieldValue> | null,
67
+ fetchOptions: FetchOptions<TClientFieldValue, TRawResponseType> | null,
62
68
  ): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
63
69
  switch (fetchOptions?.shouldFetch ?? DEFAULT_SHOULD_FETCH_VALUE) {
64
70
  case 'Yes': {
@@ -83,7 +89,8 @@ export function maybeMakeNetworkRequest<
83
89
  'NormalizationAstLoader'
84
90
  ) {
85
91
  throw new Error(
86
- '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.',
87
94
  );
88
95
  }
89
96
  const result = check(
@@ -131,8 +138,10 @@ export function retainQueryWithoutMakingNetworkRequest<
131
138
  >,
132
139
  variables: ExtractParameters<TReadFromStore>,
133
140
  ): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
134
- let status: NetworkRequestStatus = {
135
- kind: 'Undisposed',
141
+ let status:
142
+ | NetworkRequestStatusUndisposedComplete
143
+ | NetworkRequestStatusDisposed = {
144
+ kind: 'UndisposedComplete',
136
145
  retainedQuery: fetchNormalizationAstAndRetainArtifact(
137
146
  environment,
138
147
  artifact,
@@ -142,12 +151,9 @@ export function retainQueryWithoutMakingNetworkRequest<
142
151
  return [
143
152
  wrapResolvedValue(undefined),
144
153
  () => {
145
- if (status.kind === 'Undisposed') {
146
- unretainAndGarbageCollect(environment, status);
154
+ if (status.kind !== 'Disposed') {
155
+ status = unretainAndGarbageCollect(environment, status);
147
156
  }
148
- status = {
149
- kind: 'Disposed',
150
- };
151
157
  },
152
158
  ];
153
159
  }
@@ -171,18 +177,27 @@ export function makeNetworkRequest<
171
177
  readerWithRefetchQueries: PromiseWrapper<
172
178
  ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>
173
179
  > | null,
174
- fetchOptions: FetchOptions<TClientFieldValue> | null,
180
+ fetchOptions: FetchOptions<TClientFieldValue, TRawResponseType> | null,
175
181
  ): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
176
182
  // TODO this should be a DataId and stored in the store
177
183
  const myNetworkRequestId = networkRequestId + '';
178
184
  networkRequestId++;
179
185
  let status: NetworkRequestStatus = {
180
- kind: 'Undisposed',
186
+ kind: 'UndisposedIncomplete',
181
187
  retainedQuery: fetchNormalizationAstAndRetainArtifact(
182
188
  environment,
183
189
  artifact,
184
190
  variables,
185
191
  ),
192
+ optimistic:
193
+ fetchOptions?.optimisticNetworkResponse != null
194
+ ? makeOptimisticUpdate(
195
+ environment,
196
+ artifact,
197
+ variables,
198
+ fetchOptions?.optimisticNetworkResponse,
199
+ )
200
+ : null,
186
201
  };
187
202
 
188
203
  logMessage(environment, () => ({
@@ -218,26 +233,50 @@ export function makeNetworkRequest<
218
233
  }
219
234
 
220
235
  const root = { __link: ROOT_ID, __typename: artifact.concreteType };
221
- if (status.kind === 'Undisposed') {
222
- const encounteredIds: EncounteredIds = new Map();
223
- environment.store = addNetworkResponseStoreLayer(environment.store);
224
- normalizeData(
225
- environment,
226
- environment.store,
227
- normalizationAst.selections,
228
- networkResponse.data ?? {},
229
- variables,
230
- root,
231
- encounteredIds,
232
- );
233
236
 
234
- logMessage(environment, () => ({
235
- kind: 'AfterNormalization',
236
- store: environment.store,
237
- encounteredIds: encounteredIds,
238
- }));
237
+ if (status.kind === 'UndisposedIncomplete') {
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
+ }));
239
272
 
240
- callSubscriptions(environment, encounteredIds);
273
+ callSubscriptions(environment, encounteredIds);
274
+
275
+ status = {
276
+ kind: 'UndisposedComplete',
277
+ retainedQuery: status.retainedQuery,
278
+ };
279
+ }
241
280
  }
242
281
 
243
282
  const onComplete = fetchOptions?.onComplete;
@@ -268,6 +307,16 @@ export function makeNetworkRequest<
268
307
  try {
269
308
  fetchOptions?.onError?.();
270
309
  } catch {}
310
+
311
+ if (status.kind === 'UndisposedIncomplete') {
312
+ status =
313
+ revertOptimisticStoreLayerAndMaybeReplaceIfUndisposedIncomplete(
314
+ environment,
315
+ status,
316
+ null,
317
+ );
318
+ }
319
+
271
320
  throw e;
272
321
  });
273
322
 
@@ -276,27 +325,41 @@ export function makeNetworkRequest<
276
325
  const response: ItemCleanupPair<PromiseWrapper<void, AnyError>> = [
277
326
  wrapper,
278
327
  () => {
279
- if (status.kind === 'Undisposed') {
280
- unretainAndGarbageCollect(environment, status);
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);
281
338
  }
282
- status = {
283
- kind: 'Disposed',
284
- };
285
339
  },
286
340
  ];
287
341
  return response;
288
342
  }
289
343
 
290
- type NetworkRequestStatusUndisposed = {
291
- readonly kind: 'Undisposed';
344
+ type NetworkRequestStatusUndisposedIncomplete = {
345
+ readonly kind: 'UndisposedIncomplete';
292
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';
293
357
  };
294
358
 
295
359
  type NetworkRequestStatus =
296
- | NetworkRequestStatusUndisposed
297
- | {
298
- readonly kind: 'Disposed';
299
- };
360
+ | NetworkRequestStatusUndisposedIncomplete
361
+ | NetworkRequestStatusUndisposedComplete
362
+ | NetworkRequestStatusDisposed;
300
363
 
301
364
  function readDataForOnComplete<
302
365
  TReadFromStore extends UnknownTReadFromStore,
@@ -438,12 +501,86 @@ function fetchNormalizationAstAndRetainArtifact<
438
501
  return retainedQuery;
439
502
  }
440
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
+
441
574
  function unretainAndGarbageCollect(
442
575
  environment: IsographEnvironment,
443
- status: NetworkRequestStatusUndisposed,
444
- ) {
576
+ status: NetworkRequestStatusUndisposedComplete,
577
+ ): NetworkRequestStatusDisposed {
445
578
  const didUnretainSomeQuery = unretainQuery(environment, status.retainedQuery);
446
579
  if (didUnretainSomeQuery) {
447
580
  garbageCollectEnvironment(environment);
448
581
  }
582
+
583
+ return {
584
+ kind: 'Disposed',
585
+ };
449
586
  }
@@ -122,8 +122,28 @@ export type StartUpdateStoreLayer = {
122
122
  startUpdate: DataUpdate<StartUpdateStoreLayer | BaseStoreLayer>;
123
123
  };
124
124
 
125
- export type OptimisticStoreLayer = {
126
- readonly kind: 'OptimisticStoreLayer';
125
+ export type OptimisticStoreLayer =
126
+ | OptimisticUpdaterStoreLayer
127
+ | OptimisticNetworkResponseStoreLayer;
128
+
129
+ export type OptimisticUpdaterStoreLayer = {
130
+ readonly kind: 'OptimisticUpdaterStoreLayer';
131
+ childStoreLayer:
132
+ | OptimisticStoreLayer
133
+ | StartUpdateStoreLayer
134
+ | NetworkResponseStoreLayer
135
+ | null;
136
+ parentStoreLayer:
137
+ | OptimisticStoreLayer
138
+ | StartUpdateStoreLayer
139
+ | NetworkResponseStoreLayer
140
+ | BaseStoreLayer;
141
+ data: StoreLayerData;
142
+ readonly startUpdate: DataUpdate<OptimisticUpdaterStoreLayer>;
143
+ };
144
+
145
+ export type OptimisticNetworkResponseStoreLayer = {
146
+ readonly kind: 'OptimisticNetworkResponseStoreLayer';
127
147
  childStoreLayer:
128
148
  | OptimisticStoreLayer
129
149
  | StartUpdateStoreLayer
@@ -135,7 +155,6 @@ export type OptimisticStoreLayer = {
135
155
  | NetworkResponseStoreLayer
136
156
  | BaseStoreLayer;
137
157
  data: StoreLayerData;
138
- readonly startUpdate: DataUpdate<OptimisticStoreLayer>;
139
158
  };
140
159
 
141
160
  export function addNetworkResponseStoreLayer(
@@ -147,7 +166,8 @@ export function addNetworkResponseStoreLayer(
147
166
  return parent;
148
167
  }
149
168
  case 'StartUpdateStoreLayer':
150
- case 'OptimisticStoreLayer': {
169
+ case 'OptimisticNetworkResponseStoreLayer':
170
+ case 'OptimisticUpdaterStoreLayer': {
151
171
  const node: NetworkResponseStoreLayer = {
152
172
  kind: 'NetworkResponseStoreLayer',
153
173
  parentStoreLayer: parent,
@@ -205,7 +225,8 @@ export function addStartUpdateStoreLayer(
205
225
  return node;
206
226
  }
207
227
  case 'NetworkResponseStoreLayer':
208
- case 'OptimisticStoreLayer': {
228
+ case 'OptimisticNetworkResponseStoreLayer':
229
+ case 'OptimisticUpdaterStoreLayer': {
209
230
  const node: StartUpdateStoreLayer = {
210
231
  kind: 'StartUpdateStoreLayer',
211
232
  parentStoreLayer: parent,
@@ -225,17 +246,18 @@ export function addStartUpdateStoreLayer(
225
246
  }
226
247
  }
227
248
 
228
- export function addOptimisticStoreLayer(
249
+ export function addOptimisticUpdaterStoreLayer(
229
250
  parent: StoreLayer,
230
- startUpdate: OptimisticStoreLayer['startUpdate'],
231
- ): OptimisticStoreLayer {
251
+ startUpdate: OptimisticUpdaterStoreLayer['startUpdate'],
252
+ ): OptimisticUpdaterStoreLayer {
232
253
  switch (parent.kind) {
233
254
  case 'BaseStoreLayer':
234
255
  case 'StartUpdateStoreLayer':
235
256
  case 'NetworkResponseStoreLayer':
236
- case 'OptimisticStoreLayer': {
237
- const node: OptimisticStoreLayer = {
238
- kind: 'OptimisticStoreLayer',
257
+ case 'OptimisticNetworkResponseStoreLayer':
258
+ case 'OptimisticUpdaterStoreLayer': {
259
+ const node: OptimisticUpdaterStoreLayer = {
260
+ kind: 'OptimisticUpdaterStoreLayer',
239
261
  parentStoreLayer: parent,
240
262
  childStoreLayer: null,
241
263
  data: {},
@@ -254,6 +276,33 @@ export function addOptimisticStoreLayer(
254
276
  }
255
277
  }
256
278
 
279
+ export function addOptimisticNetworkResponseStoreLayer(
280
+ parent: StoreLayer,
281
+ ): OptimisticNetworkResponseStoreLayer {
282
+ switch (parent.kind) {
283
+ case 'BaseStoreLayer':
284
+ case 'StartUpdateStoreLayer':
285
+ case 'NetworkResponseStoreLayer':
286
+ case 'OptimisticNetworkResponseStoreLayer':
287
+ case 'OptimisticUpdaterStoreLayer': {
288
+ const node: OptimisticNetworkResponseStoreLayer = {
289
+ kind: 'OptimisticNetworkResponseStoreLayer',
290
+ parentStoreLayer: parent,
291
+ childStoreLayer: null,
292
+ data: {},
293
+ };
294
+
295
+ parent.childStoreLayer = node;
296
+
297
+ return node;
298
+ }
299
+ default: {
300
+ parent satisfies never;
301
+ throw new Error('Unreachable. This is a bug in Isograph.');
302
+ }
303
+ }
304
+ }
305
+
257
306
  /**
258
307
  * Merge storeLayerToMerge, and its children, into baseStoreLayer.
259
308
  * We can merge until we reach a revertible layer (i.e. an optimistic layer).
@@ -273,7 +322,7 @@ function mergeLayersWithDataIntoBaseLayer(
273
322
  ) {
274
323
  while (
275
324
  storeLayerToMerge &&
276
- storeLayerToMerge.kind !== 'OptimisticStoreLayer'
325
+ storeLayerToMerge.kind !== 'OptimisticUpdaterStoreLayer'
277
326
  ) {
278
327
  mergeDataLayer(baseStoreLayer.data, storeLayerToMerge.data);
279
328
  storeLayerToMerge = storeLayerToMerge.childStoreLayer;
@@ -309,6 +358,7 @@ function reexecuteUpdatesAndMergeData(
309
358
  while (storeLayer !== null) {
310
359
  mergeDataLayer(oldMergedData, storeLayer.data);
311
360
  switch (storeLayer.kind) {
361
+ case 'OptimisticNetworkResponseStoreLayer':
312
362
  case 'NetworkResponseStoreLayer':
313
363
  break;
314
364
  case 'StartUpdateStoreLayer': {
@@ -316,7 +366,7 @@ function reexecuteUpdatesAndMergeData(
316
366
  storeLayer.startUpdate(storeLayer);
317
367
  break;
318
368
  }
319
- case 'OptimisticStoreLayer': {
369
+ case 'OptimisticUpdaterStoreLayer': {
320
370
  storeLayer.data = {};
321
371
  storeLayer.startUpdate(storeLayer);
322
372
  break;
@@ -467,7 +517,10 @@ export type StoreLayer =
467
517
  | StartUpdateStoreLayer
468
518
  | BaseStoreLayer;
469
519
 
470
- export type StoreLayerWithData = BaseStoreLayer | NetworkResponseStoreLayer;
520
+ export type StoreLayerWithData =
521
+ | BaseStoreLayer
522
+ | NetworkResponseStoreLayer
523
+ | OptimisticNetworkResponseStoreLayer;
471
524
 
472
525
  function compareData(
473
526
  oldData: StoreLayerData,
package/src/core/read.ts CHANGED
@@ -316,7 +316,7 @@ export function readLoadablySelectedFieldData(
316
316
  data: (
317
317
  args: any,
318
318
  // TODO get the associated type for FetchOptions from the loadably selected field
319
- fetchOptions?: FetchOptions<any>,
319
+ fetchOptions?: FetchOptions<any, never>,
320
320
  ) => {
321
321
  // TODO we should use the reader AST for this
322
322
  const includeReadOutData = (variables: any, readOutData: any) => {
@@ -919,7 +919,7 @@ export function readClientPointerData(
919
919
  data: (
920
920
  args: any,
921
921
  // TODO get the associated type for FetchOptions from the loadably selected field
922
- fetchOptions?: FetchOptions<any>,
922
+ fetchOptions?: FetchOptions<any, never>,
923
923
  ) => {
924
924
  const includeReadOutData = (variables: any, readOutData: any) => {
925
925
  variables.id = readOutData.id;
@@ -177,5 +177,5 @@ export type LoadableField<
177
177
  // user-facing API. Users should only interact with LoadableFields via APIs
178
178
  // like useClientSideDefer. These APIs should have a nullable fetchOptions
179
179
  // parameter, and provide a default value ({}) to the LoadableField.
180
- fetchOptions: FetchOptions<TResult>,
180
+ fetchOptions: FetchOptions<TResult, never>,
181
181
  ) => [StableId, Factory<FragmentReference<TReadFromStore, TResult>>];
@@ -30,11 +30,11 @@ export function useClientSideDefer<
30
30
  >
31
31
  ? [
32
32
  args?: ArgsWithoutProvidedArgs<TReadFromStore, TProvidedArgs>,
33
- fetchOptions?: FetchOptions<TResult>,
33
+ fetchOptions?: FetchOptions<TResult, never>,
34
34
  ]
35
35
  : [
36
36
  args: ArgsWithoutProvidedArgs<TReadFromStore, TProvidedArgs>,
37
- fetchOptions?: FetchOptions<TResult>,
37
+ fetchOptions?: FetchOptions<TResult, never>,
38
38
  ]
39
39
  ): { fragmentReference: FragmentReference<TReadFromStore, TResult> } {
40
40
  const [args, fetchOptions] = maybeRequiredArgs;
@@ -38,7 +38,7 @@ export type UsePaginationReturnValue<
38
38
  kind: 'HasMoreRecords';
39
39
  fetchMore: (
40
40
  count: number,
41
- fetchOptions?: FetchOptions<Connection<TItem>>,
41
+ fetchOptions?: FetchOptions<Connection<TItem>, never>,
42
42
  ) => void;
43
43
  results: ReadonlyArray<TItem>;
44
44
  }
@@ -210,7 +210,10 @@ export function useConnectionSpecPagination<
210
210
 
211
211
  const getFetchMore =
212
212
  (after: string | null) =>
213
- (count: number, fetchOptions?: FetchOptions<Connection<TItem>>): void => {
213
+ (
214
+ count: number,
215
+ fetchOptions?: FetchOptions<Connection<TItem>, never>,
216
+ ): void => {
214
217
  const loadedField = loadableField(
215
218
  {
216
219
  after: after,
@@ -19,7 +19,7 @@ export type UseImperativeLoadableFieldReturn<
19
19
  // TODO this should be void iff all args are provided by the query, like in
20
20
  // useClientSideDefer.
21
21
  args: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs> | void,
22
- fetchOptions?: FetchOptions<TResult>,
22
+ fetchOptions?: FetchOptions<TResult, never>,
23
23
  ) => void;
24
24
  };
25
25
 
@@ -40,7 +40,7 @@ export function useImperativeLoadableField<
40
40
  return {
41
41
  loadFragmentReference: (
42
42
  args: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs> | void,
43
- fetchOptions?: FetchOptions<TResult>,
43
+ fetchOptions?: FetchOptions<TResult, never>,
44
44
  ) => {
45
45
  const [_id, loader] = loadableField(args, fetchOptions ?? {});
46
46
  setState(loader());
@@ -33,7 +33,7 @@ export type UseSkipLimitReturnValue<
33
33
  readonly kind: 'Complete';
34
34
  readonly fetchMore: (
35
35
  count: number,
36
- fetchOptions?: FetchOptions<ReadonlyArray<TItem>>,
36
+ fetchOptions?: FetchOptions<ReadonlyArray<TItem>, never>,
37
37
  ) => void;
38
38
  readonly results: ReadonlyArray<TItem>;
39
39
  }
@@ -196,7 +196,7 @@ export function useSkipLimitPagination<
196
196
  (loadedSoFar: number) =>
197
197
  (
198
198
  count: number,
199
- fetchOptions?: FetchOptions<ReadonlyArray<TItem>>,
199
+ fetchOptions?: FetchOptions<ReadonlyArray<TItem>, never>,
200
200
  ): void => {
201
201
  const loadedField = loadableField(
202
202
  {
@@ -38,7 +38,7 @@ export function LoadableFieldReader<
38
38
  TResult,
39
39
  Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>
40
40
  >;
41
- fetchOptions?: FetchOptions<TResult>;
41
+ fetchOptions?: FetchOptions<TResult, never>;
42
42
  networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
43
43
  children: (arg: TResult) => TChildrenResult;
44
44
  } & MaybeRequiredArgs<TReadFromStore, TProvidedArgs>,
@@ -38,7 +38,7 @@ export function LoadableFieldRenderer<
38
38
  React.FC<TProps>,
39
39
  Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>
40
40
  >;
41
- fetchOptions?: FetchOptions<React.FC<TProps>>;
41
+ fetchOptions?: FetchOptions<React.FC<TProps>, never>;
42
42
  networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
43
43
  additionalProps: Omit<TProps, keyof JSX.IntrinsicAttributes>;
44
44
  } & MaybeRequiredArgs<TReadFromStore, TProvidedArgs>,