@isograph/react 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.turbo/turbo-compile-typescript.log +4 -0
  2. package/dist/core/FragmentReference.d.ts +25 -6
  3. package/dist/core/FragmentReference.d.ts.map +1 -0
  4. package/dist/core/FragmentReference.js +3 -13
  5. package/dist/core/IsographEnvironment.d.ts +34 -26
  6. package/dist/core/IsographEnvironment.d.ts.map +1 -0
  7. package/dist/core/IsographEnvironment.js +19 -22
  8. package/dist/core/PromiseWrapper.d.ts +4 -4
  9. package/dist/core/PromiseWrapper.d.ts.map +1 -0
  10. package/dist/core/PromiseWrapper.js +9 -9
  11. package/dist/core/areEqualWithDeepComparison.d.ts +5 -3
  12. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
  13. package/dist/core/areEqualWithDeepComparison.js +89 -39
  14. package/dist/core/cache.d.ts +20 -13
  15. package/dist/core/cache.d.ts.map +1 -0
  16. package/dist/core/cache.js +205 -128
  17. package/dist/core/check.d.ts +22 -0
  18. package/dist/core/check.d.ts.map +1 -0
  19. package/dist/core/check.js +127 -0
  20. package/dist/core/componentCache.d.ts +2 -2
  21. package/dist/core/componentCache.d.ts.map +1 -0
  22. package/dist/core/componentCache.js +28 -32
  23. package/dist/core/entrypoint.d.ts +31 -15
  24. package/dist/core/entrypoint.d.ts.map +1 -0
  25. package/dist/core/entrypoint.js +1 -2
  26. package/dist/core/garbageCollection.d.ts +6 -5
  27. package/dist/core/garbageCollection.d.ts.map +1 -0
  28. package/dist/core/garbageCollection.js +49 -16
  29. package/dist/core/logging.d.ts +68 -0
  30. package/dist/core/logging.d.ts.map +1 -0
  31. package/dist/core/logging.js +22 -0
  32. package/dist/core/makeNetworkRequest.d.ts +6 -3
  33. package/dist/core/makeNetworkRequest.d.ts.map +1 -0
  34. package/dist/core/makeNetworkRequest.js +160 -19
  35. package/dist/core/read.d.ts +25 -5
  36. package/dist/core/read.d.ts.map +1 -0
  37. package/dist/core/read.js +416 -259
  38. package/dist/core/reader.d.ts +31 -15
  39. package/dist/core/reader.d.ts.map +1 -0
  40. package/dist/core/startUpdate.d.ts +5 -0
  41. package/dist/core/startUpdate.d.ts.map +1 -0
  42. package/dist/core/startUpdate.js +15 -0
  43. package/dist/core/util.d.ts +5 -0
  44. package/dist/core/util.d.ts.map +1 -0
  45. package/dist/index.d.ts +19 -14
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +11 -2
  48. package/dist/loadable-hooks/useClientSideDefer.d.ts +9 -3
  49. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
  50. package/dist/loadable-hooks/useClientSideDefer.js +6 -8
  51. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +27 -0
  52. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
  53. package/dist/loadable-hooks/useConnectionSpecPagination.js +162 -0
  54. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +2 -2
  55. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
  56. package/dist/loadable-hooks/useImperativeExposedMutationField.js +1 -2
  57. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +13 -7
  58. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
  59. package/dist/loadable-hooks/useImperativeLoadableField.js +4 -5
  60. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +13 -26
  61. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
  62. package/dist/loadable-hooks/useSkipLimitPagination.js +93 -47
  63. package/dist/react/FragmentReader.d.ts +6 -4
  64. package/dist/react/FragmentReader.d.ts.map +1 -0
  65. package/dist/react/FragmentReader.js +4 -2
  66. package/dist/react/IsographEnvironmentProvider.d.ts +1 -0
  67. package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
  68. package/dist/react/IsographEnvironmentProvider.js +3 -3
  69. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
  70. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
  71. package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
  72. package/dist/react/useImperativeReference.d.ts +8 -6
  73. package/dist/react/useImperativeReference.d.ts.map +1 -0
  74. package/dist/react/useImperativeReference.js +6 -8
  75. package/dist/react/useLazyReference.d.ts +5 -3
  76. package/dist/react/useLazyReference.d.ts.map +1 -0
  77. package/dist/react/useLazyReference.js +34 -6
  78. package/dist/react/useReadAndSubscribe.d.ts +6 -3
  79. package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
  80. package/dist/react/useReadAndSubscribe.js +13 -10
  81. package/dist/react/useRerenderOnChange.d.ts +7 -2
  82. package/dist/react/useRerenderOnChange.d.ts.map +1 -0
  83. package/dist/react/useRerenderOnChange.js +3 -4
  84. package/dist/react/useResult.d.ts +4 -3
  85. package/dist/react/useResult.d.ts.map +1 -0
  86. package/dist/react/useResult.js +14 -9
  87. package/isograph.config.json +8 -0
  88. package/package.json +14 -9
  89. package/{src/tests/schema.graphql → schema.graphql} +1 -0
  90. package/src/core/FragmentReference.ts +44 -17
  91. package/src/core/IsographEnvironment.ts +67 -50
  92. package/src/core/PromiseWrapper.ts +3 -3
  93. package/src/core/areEqualWithDeepComparison.ts +95 -41
  94. package/src/core/cache.ts +316 -169
  95. package/src/core/check.ts +212 -0
  96. package/src/core/componentCache.ts +40 -46
  97. package/src/core/entrypoint.ts +41 -16
  98. package/src/core/garbageCollection.ts +77 -26
  99. package/src/core/logging.ts +118 -0
  100. package/src/core/makeNetworkRequest.ts +249 -20
  101. package/src/core/read.ts +658 -368
  102. package/src/core/reader.ts +61 -21
  103. package/src/core/startUpdate.ts +28 -0
  104. package/src/core/util.ts +8 -0
  105. package/src/index.ts +94 -8
  106. package/src/loadable-hooks/useClientSideDefer.ts +48 -17
  107. package/src/loadable-hooks/useConnectionSpecPagination.ts +344 -0
  108. package/src/loadable-hooks/useImperativeExposedMutationField.ts +1 -1
  109. package/src/loadable-hooks/useImperativeLoadableField.ts +36 -12
  110. package/src/loadable-hooks/useSkipLimitPagination.ts +253 -94
  111. package/src/react/FragmentReader.tsx +15 -6
  112. package/src/react/IsographEnvironmentProvider.tsx +1 -1
  113. package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
  114. package/src/react/useImperativeReference.ts +50 -18
  115. package/src/react/useLazyReference.ts +79 -11
  116. package/src/react/useReadAndSubscribe.ts +33 -10
  117. package/src/react/useRerenderOnChange.ts +7 -2
  118. package/src/react/useResult.ts +30 -9
  119. package/src/tests/__isograph/Query/meName/entrypoint.ts +10 -29
  120. package/src/tests/__isograph/Query/meName/normalization_ast.ts +25 -0
  121. package/src/tests/__isograph/Query/meName/param_type.ts +5 -2
  122. package/src/tests/__isograph/Query/meName/query_text.ts +6 -0
  123. package/src/tests/__isograph/Query/meName/resolver_reader.ts +5 -0
  124. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +10 -65
  125. package/src/tests/__isograph/Query/meNameSuccessor/normalization_ast.ts +56 -0
  126. package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +9 -6
  127. package/src/tests/__isograph/Query/meNameSuccessor/query_text.ts +13 -0
  128. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +10 -0
  129. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +10 -28
  130. package/src/tests/__isograph/Query/nodeField/normalization_ast.ts +30 -0
  131. package/src/tests/__isograph/Query/nodeField/param_type.ts +7 -3
  132. package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
  133. package/src/tests/__isograph/Query/nodeField/query_text.ts +6 -0
  134. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +5 -0
  135. package/src/tests/__isograph/Query/subquery/entrypoint.ts +28 -0
  136. package/src/tests/__isograph/Query/subquery/normalization_ast.ts +38 -0
  137. package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
  138. package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
  139. package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
  140. package/src/tests/__isograph/Query/subquery/query_text.ts +8 -0
  141. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +52 -0
  142. package/src/tests/__isograph/iso.ts +24 -12
  143. package/src/tests/garbageCollection.test.ts +53 -45
  144. package/src/tests/meNameSuccessor.ts +8 -3
  145. package/src/tests/nodeQuery.ts +7 -4
  146. package/src/tests/normalizeData.test.ts +120 -0
  147. package/src/tests/tsconfig.json +3 -3
  148. package/tsconfig.json +2 -2
  149. package/tsconfig.pkg.json +7 -3
  150. package/vitest.config.ts +20 -0
  151. package/src/tests/isograph.config.json +0 -7
@@ -1,9 +1,4 @@
1
- import { LoadableField } from '../core/reader';
2
- import { useIsographEnvironment } from '../react/IsographEnvironmentProvider';
3
1
  import { ItemCleanupPair } from '@isograph/disposable-types';
4
- import { FragmentReference } from '../core/FragmentReference';
5
- import { maybeUnwrapNetworkRequest } from '../react/useResult';
6
- import { readButDoNotEvaluate } from '../core/read';
7
2
  import {
8
3
  UNASSIGNED_STATE,
9
4
  useUpdatableDisposableState,
@@ -12,32 +7,60 @@ import {
12
7
  createReferenceCountedPointer,
13
8
  ReferenceCountedPointer,
14
9
  } from '@isograph/reference-counted-pointer';
10
+ import { useState } from 'react';
11
+ import { subscribeToAnyChange } from '../core/cache';
12
+ import { FetchOptions } from '../core/check';
13
+ import {
14
+ FragmentReference,
15
+ type UnknownTReadFromStore,
16
+ } from '../core/FragmentReference';
15
17
  import { getPromiseState, readPromise } from '../core/PromiseWrapper';
18
+ import {
19
+ readButDoNotEvaluate,
20
+ type WithEncounteredRecords,
21
+ } from '../core/read';
22
+ import { LoadableField, type ReaderAst } from '../core/reader';
23
+ import { getOrCreateCachedStartUpdate } from '../core/startUpdate';
24
+ import { useIsographEnvironment } from '../react/IsographEnvironmentProvider';
25
+ import { useSubscribeToMultiple } from '../react/useReadAndSubscribe';
26
+ import { maybeUnwrapNetworkRequest } from '../react/useResult';
16
27
 
17
- type SkipOrLimit = 'skip' | 'limit';
18
- type OmitSkipLimit<TArgs> = keyof Omit<TArgs, SkipOrLimit> extends never
19
- ? void | Record<string, never>
20
- : Omit<TArgs, SkipOrLimit>;
21
-
22
- type UseSkipLimitReturnValue<TArgs, TItem> =
28
+ export type UseSkipLimitReturnValue<
29
+ TReadFromStore extends UnknownTReadFromStore,
30
+ TItem,
31
+ > =
23
32
  | {
24
33
  readonly kind: 'Complete';
25
- readonly fetchMore: (args: OmitSkipLimit<TArgs>, count: number) => void;
34
+ readonly fetchMore: (
35
+ count: number,
36
+ fetchOptions?: FetchOptions<ReadonlyArray<TItem>>,
37
+ ) => void;
26
38
  readonly results: ReadonlyArray<TItem>;
27
39
  }
28
40
  | {
29
41
  readonly kind: 'Pending';
30
42
  readonly results: ReadonlyArray<TItem>;
31
- readonly pendingFragment: FragmentReference<any, ReadonlyArray<TItem>>;
43
+ readonly pendingFragment: FragmentReference<
44
+ TReadFromStore,
45
+ ReadonlyArray<TItem>
46
+ >;
32
47
  };
33
48
 
34
- type ArrayFragmentReference<TItem> = FragmentReference<
35
- any,
36
- ReadonlyArray<TItem>
37
- >;
49
+ type ArrayFragmentReference<
50
+ TReadFromStore extends UnknownTReadFromStore,
51
+ TItem,
52
+ > = FragmentReference<TReadFromStore, ReadonlyArray<TItem>>;
53
+
54
+ type LoadedFragmentReferences<
55
+ TReadFromStore extends { parameters: object; data: object },
56
+ TItem,
57
+ > = ReadonlyArray<LoadedFragmentReference<TReadFromStore, TItem>>;
38
58
 
39
- type LoadedFragmentReferences<TItem> = ReadonlyArray<
40
- ItemCleanupPair<ReferenceCountedPointer<ArrayFragmentReference<TItem>>>
59
+ type LoadedFragmentReference<
60
+ TReadFromStore extends { parameters: object; data: object },
61
+ TItem,
62
+ > = ItemCleanupPair<
63
+ ReferenceCountedPointer<ArrayFragmentReference<TReadFromStore, TItem>>
41
64
  >;
42
65
 
43
66
  function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): ReadonlyArray<T> {
@@ -50,95 +73,145 @@ function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): ReadonlyArray<T> {
50
73
  return outArray;
51
74
  }
52
75
 
53
- /**
54
- * accepts a loadableField that accepts skip and limit arguments
55
- * and returns:
56
- * - a fetchMore function that, when called, triggers a network
57
- * request for additional data, and
58
- * - the data received so far.
59
- *
60
- * This hook will suspend if any network request is in flight.
61
- *
62
- * Calling fetchMore before the hook mounts is a no-op.
63
- *
64
- * NOTE: this hook does not subscribe to changes. This is a known
65
- * issue. If you are running into this issue, reach out on GitHub/
66
- * Twitter, and we'll fix the issue.
67
- */
76
+ export type UseSkipLimitPaginationArgs = {
77
+ skip: number;
78
+ limit: number;
79
+ };
80
+
68
81
  export function useSkipLimitPagination<
69
- TArgs extends {
70
- skip: number | void | null;
71
- limit: number | void | null;
72
- },
73
82
  TItem,
83
+ TReadFromStore extends UnknownTReadFromStore,
74
84
  >(
75
- loadableField: LoadableField<TArgs, Array<TItem>>,
76
- ): UseSkipLimitReturnValue<TArgs, TItem> {
85
+ loadableField: LoadableField<
86
+ TReadFromStore,
87
+ ReadonlyArray<TItem>,
88
+ UseSkipLimitPaginationArgs
89
+ >,
90
+ initialState?: {
91
+ skip?: number | void | null;
92
+ },
93
+ ): UseSkipLimitReturnValue<TReadFromStore, TItem> {
77
94
  const networkRequestOptions = {
78
95
  suspendIfInFlight: true,
79
96
  throwOnNetworkError: true,
80
97
  };
81
98
  const { state, setState } =
82
- useUpdatableDisposableState<LoadedFragmentReferences<TItem>>();
99
+ useUpdatableDisposableState<
100
+ LoadedFragmentReferences<TReadFromStore, TItem>
101
+ >();
83
102
 
84
103
  const environment = useIsographEnvironment();
85
104
 
105
+ // TODO move this out of useSkipLimitPagination, and pass environment and networkRequestOptions
106
+ // as parameters (or recreate networkRequestOptions)
86
107
  function readCompletedFragmentReferences(
87
- completedReferences: ReadonlyArray<
88
- ItemCleanupPair<ReferenceCountedPointer<ArrayFragmentReference<TItem>>>
89
- >,
108
+ completedReferences: ArrayFragmentReference<TReadFromStore, TItem>[],
90
109
  ) {
91
- // In general, this will not suspend. But it could, if there is missing data.
92
- // A better version of this hook would not do any reading here.
93
- const results = completedReferences.map(([pointer]) => {
94
- const fragmentReference = pointer.getItemIfNotDisposed();
95
- if (fragmentReference == null) {
110
+ const results = completedReferences.map((fragmentReference, i) => {
111
+ const readerWithRefetchQueries = readPromise(
112
+ fragmentReference.readerWithRefetchQueries,
113
+ );
114
+
115
+ // invariant: readOutDataAndRecords.length === completedReferences.length
116
+ const data = readOutDataAndRecords[i]?.item;
117
+ if (data == null) {
96
118
  throw new Error(
97
- 'FragmentReference is unexpectedly disposed. \
98
- This is indicative of a bug in Isograph.',
119
+ 'Parameter data is unexpectedly null. This is indicative of a bug in Isograph.',
99
120
  );
100
121
  }
101
122
 
102
- maybeUnwrapNetworkRequest(
103
- fragmentReference.networkRequest,
104
- networkRequestOptions,
105
- );
106
- const data = readButDoNotEvaluate(
107
- environment,
108
- fragmentReference,
109
- networkRequestOptions,
110
- );
123
+ const firstParameter = {
124
+ data,
125
+ parameters: fragmentReference.variables,
126
+ ...(readerWithRefetchQueries.readerArtifact.hasUpdatable
127
+ ? {
128
+ startUpdate: getOrCreateCachedStartUpdate(
129
+ environment,
130
+ fragmentReference,
131
+ readerWithRefetchQueries.readerArtifact.kind,
132
+ ),
133
+ }
134
+ : undefined),
135
+ };
111
136
 
112
- const readerWithRefetchQueries = readPromise(
113
- fragmentReference.readerWithRefetchQueries,
114
- );
137
+ if (
138
+ readerWithRefetchQueries.readerArtifact.kind !== 'EagerReaderArtifact'
139
+ ) {
140
+ throw new Error(
141
+ `@loadable field of kind "${readerWithRefetchQueries.readerArtifact.kind}" is not supported by useSkipLimitPagination`,
142
+ );
143
+ }
115
144
 
116
- return readerWithRefetchQueries.readerArtifact.resolver(
117
- data.item,
118
- undefined,
119
- ) as ReadonlyArray<any>;
145
+ return readerWithRefetchQueries.readerArtifact.resolver(firstParameter);
120
146
  });
121
147
 
122
148
  const items = flatten(results);
123
149
  return items;
124
150
  }
125
151
 
152
+ function subscribeCompletedFragmentReferences(
153
+ completedReferences: ArrayFragmentReference<TReadFromStore, TItem>[],
154
+ ) {
155
+ return completedReferences.map(
156
+ (
157
+ fragmentReference,
158
+ i,
159
+ ): {
160
+ records: WithEncounteredRecords<TReadFromStore>;
161
+ callback: (
162
+ updatedRecords: WithEncounteredRecords<TReadFromStore>,
163
+ ) => void;
164
+ fragmentReference: ArrayFragmentReference<TReadFromStore, TItem>;
165
+ readerAst: ReaderAst<TItem>;
166
+ } => {
167
+ maybeUnwrapNetworkRequest(
168
+ fragmentReference.networkRequest,
169
+ networkRequestOptions,
170
+ );
171
+
172
+ const readerWithRefetchQueries = readPromise(
173
+ fragmentReference.readerWithRefetchQueries,
174
+ );
175
+
176
+ const records = readOutDataAndRecords[i];
177
+ if (records == null) {
178
+ throw new Error(
179
+ 'subscribeCompletedFragmentReferences records is unexpectedly null',
180
+ );
181
+ }
182
+
183
+ return {
184
+ fragmentReference,
185
+ readerAst: readerWithRefetchQueries.readerArtifact.readerAst,
186
+ records,
187
+ callback(_data) {
188
+ rerender({});
189
+ },
190
+ };
191
+ },
192
+ );
193
+ }
194
+
126
195
  const getFetchMore =
127
196
  (loadedSoFar: number) =>
128
- (args: OmitSkipLimit<TArgs>, count: number): void => {
129
- // @ts-expect-error
130
- const loadedField = loadableField({
131
- ...args,
132
- skip: loadedSoFar,
133
- limit: count,
134
- })[1]();
197
+ (
198
+ count: number,
199
+ fetchOptions?: FetchOptions<ReadonlyArray<TItem>>,
200
+ ): void => {
201
+ const loadedField = loadableField(
202
+ {
203
+ skip: loadedSoFar,
204
+ limit: count,
205
+ },
206
+ fetchOptions ?? {},
207
+ )[1]();
135
208
  const newPointer = createReferenceCountedPointer(loadedField);
136
209
  const clonedPointers = loadedReferences.map(([refCountedPointer]) => {
137
210
  const clonedRefCountedPointer = refCountedPointer.cloneIfNotDisposed();
138
211
  if (clonedRefCountedPointer == null) {
139
212
  throw new Error(
140
213
  'This reference counted pointer has already been disposed. \
141
- This is indicative of a bug in useSkipLimitPagination.',
214
+ This is indicative of a bug in useSkipLimitPagination.',
142
215
  );
143
216
  }
144
217
  return clonedRefCountedPointer;
@@ -148,7 +221,9 @@ export function useSkipLimitPagination<
148
221
  const totalItemCleanupPair: ItemCleanupPair<
149
222
  ReadonlyArray<
150
223
  ItemCleanupPair<
151
- ReferenceCountedPointer<ArrayFragmentReference<TItem>>
224
+ ReferenceCountedPointer<
225
+ ArrayFragmentReference<TReadFromStore, TItem>
226
+ >
152
227
  >
153
228
  >
154
229
  > = [
@@ -163,33 +238,73 @@ export function useSkipLimitPagination<
163
238
  setState(totalItemCleanupPair);
164
239
  };
165
240
 
241
+ const [, rerender] = useState({});
242
+
166
243
  const loadedReferences = state === UNASSIGNED_STATE ? [] : state;
167
- if (loadedReferences.length === 0) {
168
- return {
169
- kind: 'Complete',
170
- fetchMore: getFetchMore(0),
171
- results: [],
172
- };
173
- }
174
244
 
175
- const mostRecentItem = loadedReferences[loadedReferences.length - 1];
176
- const mostRecentFragmentReference = mostRecentItem[0].getItemIfNotDisposed();
177
- if (mostRecentFragmentReference === null) {
245
+ const mostRecentItem:
246
+ | LoadedFragmentReference<TReadFromStore, TItem>
247
+ | undefined = loadedReferences[loadedReferences.length - 1];
248
+ const mostRecentFragmentReference =
249
+ mostRecentItem?.[0].getItemIfNotDisposed();
250
+
251
+ if (mostRecentItem && mostRecentFragmentReference === null) {
178
252
  throw new Error(
179
253
  'FragmentReference is unexpectedly disposed. \
180
- This is indicative of a bug in Isograph.',
254
+ This is indicative of a bug in Isograph.',
181
255
  );
182
256
  }
183
257
 
184
- const networkRequestStatus = getPromiseState(
185
- mostRecentFragmentReference.networkRequest,
258
+ const networkRequestStatus =
259
+ mostRecentFragmentReference &&
260
+ getPromiseState(mostRecentFragmentReference.networkRequest);
261
+
262
+ const slicedFragmentReferences =
263
+ networkRequestStatus?.kind === 'Ok'
264
+ ? loadedReferences
265
+ : loadedReferences.slice(0, loadedReferences.length - 1);
266
+
267
+ const completedFragmentReferences = slicedFragmentReferences.map(
268
+ ([pointer]) => {
269
+ const fragmentReference = pointer.getItemIfNotDisposed();
270
+ if (fragmentReference == null) {
271
+ throw new Error(
272
+ 'FragmentReference is unexpectedly disposed. \
273
+ This is indicative of a bug in Isograph.',
274
+ );
275
+ }
276
+ return fragmentReference;
277
+ },
186
278
  );
279
+
280
+ const readOutDataAndRecords = completedFragmentReferences.map(
281
+ (fragmentReference) =>
282
+ readButDoNotEvaluate(
283
+ environment,
284
+ fragmentReference,
285
+ networkRequestOptions,
286
+ ),
287
+ );
288
+
289
+ useSubscribeToMultiple<TReadFromStore>(
290
+ subscribeCompletedFragmentReferences(completedFragmentReferences),
291
+ );
292
+
293
+ if (!networkRequestStatus) {
294
+ return {
295
+ kind: 'Complete',
296
+ fetchMore: getFetchMore(initialState?.skip ?? 0),
297
+ results: [],
298
+ };
299
+ }
300
+
187
301
  switch (networkRequestStatus.kind) {
188
302
  case 'Pending': {
189
- const completedFragmentReferences = loadedReferences.slice(
190
- 0,
191
- loadedReferences.length - 1,
192
- );
303
+ const unsubscribe = subscribeToAnyChange(environment, () => {
304
+ unsubscribe();
305
+ rerender({});
306
+ });
307
+
193
308
  return {
194
309
  kind: 'Pending',
195
310
  pendingFragment: mostRecentFragmentReference,
@@ -200,7 +315,9 @@ export function useSkipLimitPagination<
200
315
  throw networkRequestStatus.error;
201
316
  }
202
317
  case 'Ok': {
203
- const results = readCompletedFragmentReferences(loadedReferences);
318
+ const results = readCompletedFragmentReferences(
319
+ completedFragmentReferences,
320
+ );
204
321
  return {
205
322
  kind: 'Complete',
206
323
  results,
@@ -209,3 +326,45 @@ export function useSkipLimitPagination<
209
326
  }
210
327
  }
211
328
  }
329
+
330
+ // @ts-ignore
331
+ function tsTests() {
332
+ type Parameters = {
333
+ readonly search: string;
334
+ readonly skip: number;
335
+ readonly limit: number;
336
+ };
337
+
338
+ let basicLoadable!: LoadableField<
339
+ {
340
+ readonly data: object;
341
+ readonly parameters: Omit<Parameters, 'search'>;
342
+ },
343
+ object[]
344
+ >;
345
+
346
+ useSkipLimitPagination(basicLoadable);
347
+ useSkipLimitPagination(basicLoadable, {});
348
+ useSkipLimitPagination(basicLoadable, { skip: 10 });
349
+
350
+ let unprovidedSearchLoadable!: LoadableField<
351
+ {
352
+ readonly data: object;
353
+ readonly parameters: Parameters;
354
+ },
355
+ object[]
356
+ >;
357
+ // @ts-expect-error
358
+ useSkipLimitPagination(unprovidedSearchLoadable);
359
+
360
+ let providedSearchLoadable!: LoadableField<
361
+ {
362
+ readonly data: object;
363
+ readonly parameters: Parameters;
364
+ },
365
+ object[],
366
+ Omit<Parameters, 'search'>
367
+ >;
368
+
369
+ useSkipLimitPagination(providedSearchLoadable);
370
+ }
@@ -1,20 +1,26 @@
1
1
  import * as React from 'react';
2
2
  import { ExtractReadFromStore, IsographEntrypoint } from '../core/entrypoint';
3
3
  import { FragmentReference } from '../core/FragmentReference';
4
- import { useResult } from './useResult';
5
4
  import { NetworkRequestReaderOptions } from '../core/read';
5
+ import { useResult } from './useResult';
6
+
7
+ export type IsExactlyIntrinsicAttributes<T> = T extends JSX.IntrinsicAttributes
8
+ ? JSX.IntrinsicAttributes extends T
9
+ ? true
10
+ : false
11
+ : false;
6
12
 
7
13
  export function FragmentReader<
8
14
  TProps extends Record<any, any>,
9
- TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>>,
15
+ TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>, any>,
10
16
  >(
11
- props: TProps extends Record<string, never>
17
+ props: IsExactlyIntrinsicAttributes<TProps> extends true
12
18
  ? {
13
19
  fragmentReference: FragmentReference<
14
20
  ExtractReadFromStore<TEntrypoint>,
15
- React.FC<{}>
21
+ React.FC<TProps>
16
22
  >;
17
- additionalProps?: TProps;
23
+ additionalProps?: Record<PropertyKey, never>;
18
24
  networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
19
25
  }
20
26
  : {
@@ -22,7 +28,7 @@ export function FragmentReader<
22
28
  ExtractReadFromStore<TEntrypoint>,
23
29
  React.FC<TProps>
24
30
  >;
25
- additionalProps: TProps;
31
+ additionalProps: Omit<TProps, keyof JSX.IntrinsicAttributes>;
26
32
  networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
27
33
  },
28
34
  ): React.ReactNode {
@@ -30,5 +36,8 @@ export function FragmentReader<
30
36
  props.fragmentReference,
31
37
  props.networkRequestOptions,
32
38
  );
39
+ // TypeScript is not understanding that if additionalProps is Record<PropertyKey, never>,
40
+ // it means that TProps === JSX.IntrinsicAttributes.
41
+ // @ts-expect-error
33
42
  return <Component {...props.additionalProps} />;
34
43
  }
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { ReactNode, createContext, useContext } from 'react';
2
+ import { createContext, ReactNode, useContext } from 'react';
3
3
  import { type IsographEnvironment } from '../core/IsographEnvironment';
4
4
 
5
5
  export const IsographEnvironmentContext =
@@ -0,0 +1,17 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ /**
4
+ * This is a function that will render a component only after it commits.
5
+ * It should not be used in production. It's useful as a way to debug issues
6
+ * with NextJS, where an indefinite suspense causes the server to hang
7
+ * forever and never complete the original request.
8
+ */
9
+ export function RenderAfterCommit__DO_NOT_USE({
10
+ children,
11
+ }: {
12
+ children: React.ReactNode;
13
+ }) {
14
+ const [show, setShow] = useState(false);
15
+ useEffect(() => setShow(true), []);
16
+ return show ? children : null;
17
+ }
@@ -1,39 +1,71 @@
1
1
  import {
2
- UnassignedState,
2
+ UNASSIGNED_STATE,
3
3
  useUpdatableDisposableState,
4
4
  } from '@isograph/react-disposable-state';
5
- import { IsographEntrypoint } from '../core/entrypoint';
6
- import { FragmentReference, Variables } from '../core/FragmentReference';
7
- import { useIsographEnvironment } from './IsographEnvironmentProvider';
5
+ import { FetchOptions, type RequiredFetchOptions } from '../core/check';
6
+ import {
7
+ IsographEntrypoint,
8
+ type NormalizationAst,
9
+ type NormalizationAstLoader,
10
+ } from '../core/entrypoint';
11
+ import {
12
+ ExtractParameters,
13
+ FragmentReference,
14
+ type UnknownTReadFromStore,
15
+ } from '../core/FragmentReference';
8
16
  import { ROOT_ID } from '../core/IsographEnvironment';
9
- import { makeNetworkRequest } from '../core/makeNetworkRequest';
17
+ import { maybeMakeNetworkRequest } from '../core/makeNetworkRequest';
10
18
  import { wrapResolvedValue } from '../core/PromiseWrapper';
19
+ import { useIsographEnvironment } from './IsographEnvironmentProvider';
11
20
 
12
- // TODO rename this to useImperativelyLoadedEntrypoint
21
+ export type UseImperativeReferenceResult<
22
+ TReadFromStore extends UnknownTReadFromStore,
23
+ TClientFieldValue,
24
+ TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
25
+ > = {
26
+ fragmentReference: FragmentReference<
27
+ TReadFromStore,
28
+ TClientFieldValue
29
+ > | null;
30
+ loadFragmentReference: (
31
+ variables: ExtractParameters<TReadFromStore>,
32
+ ...[fetchOptions]: NormalizationAstLoader extends TNormalizationAst
33
+ ? [fetchOptions: RequiredFetchOptions<TClientFieldValue>]
34
+ : [fetchOptions?: FetchOptions<TClientFieldValue>]
35
+ ) => void;
36
+ };
13
37
 
14
38
  export function useImperativeReference<
15
- TReadFromStore extends Object,
39
+ TReadFromStore extends UnknownTReadFromStore,
16
40
  TClientFieldValue,
41
+ TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
17
42
  >(
18
- entrypoint: IsographEntrypoint<TReadFromStore, TClientFieldValue>,
19
- ): {
20
- fragmentReference:
21
- | FragmentReference<TReadFromStore, TClientFieldValue>
22
- | UnassignedState;
23
- loadFragmentReference: (variables: Variables) => void;
24
- } {
43
+ entrypoint: IsographEntrypoint<
44
+ TReadFromStore,
45
+ TClientFieldValue,
46
+ TNormalizationAst
47
+ >,
48
+ ): UseImperativeReferenceResult<
49
+ TReadFromStore,
50
+ TClientFieldValue,
51
+ TNormalizationAst
52
+ > {
25
53
  const { state, setState } =
26
54
  useUpdatableDisposableState<
27
55
  FragmentReference<TReadFromStore, TClientFieldValue>
28
56
  >();
29
57
  const environment = useIsographEnvironment();
30
58
  return {
31
- fragmentReference: state,
32
- loadFragmentReference: (variables: Variables) => {
33
- const [networkRequest, disposeNetworkRequest] = makeNetworkRequest(
59
+ fragmentReference: state !== UNASSIGNED_STATE ? state : null,
60
+ loadFragmentReference: (
61
+ variables: ExtractParameters<TReadFromStore>,
62
+ fetchOptions?: FetchOptions<TClientFieldValue>,
63
+ ) => {
64
+ const [networkRequest, disposeNetworkRequest] = maybeMakeNetworkRequest(
34
65
  environment,
35
66
  entrypoint,
36
67
  variables,
68
+ fetchOptions,
37
69
  );
38
70
  setState([
39
71
  {
@@ -44,7 +76,7 @@ export function useImperativeReference<
44
76
  nestedRefetchQueries:
45
77
  entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
46
78
  }),
47
- root: ROOT_ID,
79
+ root: { __link: ROOT_ID, __typename: entrypoint.concreteType },
48
80
  variables,
49
81
  networkRequest,
50
82
  },