@isograph/react 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/dist/core/FragmentReference.d.ts +25 -0
  2. package/dist/core/FragmentReference.d.ts.map +1 -0
  3. package/dist/core/FragmentReference.js +16 -0
  4. package/dist/core/IsographEnvironment.d.ts +89 -0
  5. package/dist/core/IsographEnvironment.d.ts.map +1 -0
  6. package/dist/core/IsographEnvironment.js +65 -0
  7. package/dist/core/PromiseWrapper.d.ts +28 -0
  8. package/dist/core/PromiseWrapper.d.ts.map +1 -0
  9. package/dist/core/PromiseWrapper.js +57 -0
  10. package/dist/core/areEqualWithDeepComparison.d.ts +5 -0
  11. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
  12. package/dist/core/areEqualWithDeepComparison.js +95 -0
  13. package/dist/core/cache.d.ts +44 -0
  14. package/dist/core/cache.d.ts.map +1 -0
  15. package/dist/core/cache.js +514 -0
  16. package/dist/core/check.d.ts +18 -0
  17. package/dist/core/check.d.ts.map +1 -0
  18. package/dist/core/check.js +127 -0
  19. package/dist/core/componentCache.d.ts +5 -0
  20. package/dist/core/componentCache.d.ts.map +1 -0
  21. package/dist/core/componentCache.js +38 -0
  22. package/dist/core/entrypoint.d.ts +69 -0
  23. package/dist/core/entrypoint.d.ts.map +1 -0
  24. package/dist/core/entrypoint.js +7 -0
  25. package/dist/core/garbageCollection.d.ts +13 -0
  26. package/dist/core/garbageCollection.d.ts.map +1 -0
  27. package/dist/core/garbageCollection.js +107 -0
  28. package/dist/core/logging.d.ts +69 -0
  29. package/dist/core/logging.d.ts.map +1 -0
  30. package/dist/core/logging.js +19 -0
  31. package/dist/core/makeNetworkRequest.d.ts +9 -0
  32. package/dist/core/makeNetworkRequest.d.ts.map +1 -0
  33. package/dist/core/makeNetworkRequest.js +118 -0
  34. package/dist/core/read.d.ts +27 -0
  35. package/dist/core/read.d.ts.map +1 -0
  36. package/dist/core/read.js +478 -0
  37. package/dist/core/reader.d.ts +87 -0
  38. package/dist/core/reader.d.ts.map +1 -0
  39. package/dist/core/reader.js +2 -0
  40. package/dist/core/util.d.ts +19 -0
  41. package/dist/core/util.d.ts.map +1 -0
  42. package/dist/core/util.js +2 -0
  43. package/dist/index.d.ts +26 -120
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +57 -306
  46. package/dist/loadable-hooks/useClientSideDefer.d.ts +16 -0
  47. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
  48. package/dist/loadable-hooks/useClientSideDefer.js +13 -0
  49. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +34 -0
  50. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
  51. package/dist/loadable-hooks/useConnectionSpecPagination.js +160 -0
  52. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +6 -0
  53. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
  54. package/dist/loadable-hooks/useImperativeExposedMutationField.js +14 -0
  55. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +17 -0
  56. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
  57. package/dist/loadable-hooks/useImperativeLoadableField.js +14 -0
  58. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +27 -0
  59. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
  60. package/dist/loadable-hooks/useSkipLimitPagination.js +162 -0
  61. package/dist/react/FragmentReader.d.ts +16 -0
  62. package/dist/react/FragmentReader.d.ts.map +1 -0
  63. package/dist/{EntrypointReader.js → react/FragmentReader.js} +7 -5
  64. package/dist/react/IsographEnvironmentProvider.d.ts +11 -0
  65. package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
  66. package/dist/{IsographEnvironment.js → react/IsographEnvironmentProvider.js} +4 -22
  67. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
  68. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
  69. package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
  70. package/dist/react/useImperativeReference.d.ts +12 -0
  71. package/dist/react/useImperativeReference.d.ts.map +1 -0
  72. package/dist/react/useImperativeReference.js +35 -0
  73. package/dist/react/useLazyReference.d.ts +10 -0
  74. package/dist/react/useLazyReference.d.ts.map +1 -0
  75. package/dist/react/useLazyReference.js +21 -0
  76. package/dist/react/useReadAndSubscribe.d.ts +20 -0
  77. package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
  78. package/dist/react/useReadAndSubscribe.js +40 -0
  79. package/dist/react/useRerenderOnChange.d.ts +8 -0
  80. package/dist/react/useRerenderOnChange.d.ts.map +1 -0
  81. package/dist/react/useRerenderOnChange.js +22 -0
  82. package/dist/react/useResult.d.ts +9 -0
  83. package/dist/react/useResult.d.ts.map +1 -0
  84. package/dist/react/useResult.js +39 -0
  85. package/docs/how-useLazyReference-works.md +117 -0
  86. package/isograph.config.json +7 -0
  87. package/package.json +18 -9
  88. package/schema.graphql +17 -0
  89. package/src/core/FragmentReference.ts +49 -0
  90. package/src/core/IsographEnvironment.ts +192 -0
  91. package/src/core/PromiseWrapper.ts +86 -0
  92. package/src/core/areEqualWithDeepComparison.ts +112 -0
  93. package/src/core/cache.ts +835 -0
  94. package/src/core/check.ts +207 -0
  95. package/src/core/componentCache.ts +62 -0
  96. package/src/core/entrypoint.ts +106 -0
  97. package/src/core/garbageCollection.ts +173 -0
  98. package/src/core/logging.ts +116 -0
  99. package/src/core/makeNetworkRequest.ts +175 -0
  100. package/src/core/read.ts +722 -0
  101. package/src/core/reader.ts +160 -0
  102. package/src/core/util.ts +27 -0
  103. package/src/index.ts +99 -0
  104. package/src/loadable-hooks/useClientSideDefer.ts +58 -0
  105. package/src/loadable-hooks/useConnectionSpecPagination.ts +331 -0
  106. package/src/loadable-hooks/useImperativeExposedMutationField.ts +17 -0
  107. package/src/loadable-hooks/useImperativeLoadableField.ts +52 -0
  108. package/src/loadable-hooks/useSkipLimitPagination.ts +352 -0
  109. package/src/react/FragmentReader.tsx +43 -0
  110. package/src/react/IsographEnvironmentProvider.tsx +33 -0
  111. package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
  112. package/src/react/useImperativeReference.ts +68 -0
  113. package/src/react/useLazyReference.ts +42 -0
  114. package/src/react/useReadAndSubscribe.ts +81 -0
  115. package/src/react/useRerenderOnChange.ts +38 -0
  116. package/src/react/useResult.ts +73 -0
  117. package/src/tests/__isograph/Query/meName/entrypoint.ts +52 -0
  118. package/src/tests/__isograph/Query/meName/output_type.ts +3 -0
  119. package/src/tests/__isograph/Query/meName/param_type.ts +9 -0
  120. package/src/tests/__isograph/Query/meName/resolver_reader.ts +33 -0
  121. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +90 -0
  122. package/src/tests/__isograph/Query/meNameSuccessor/output_type.ts +3 -0
  123. package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +14 -0
  124. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +57 -0
  125. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +57 -0
  126. package/src/tests/__isograph/Query/nodeField/output_type.ts +3 -0
  127. package/src/tests/__isograph/Query/nodeField/param_type.ts +10 -0
  128. package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
  129. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +38 -0
  130. package/src/tests/__isograph/Query/subquery/entrypoint.ts +67 -0
  131. package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
  132. package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
  133. package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
  134. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +47 -0
  135. package/src/tests/__isograph/iso.ts +99 -0
  136. package/src/tests/garbageCollection.test.ts +142 -0
  137. package/src/tests/meNameSuccessor.ts +25 -0
  138. package/src/tests/nodeQuery.ts +19 -0
  139. package/src/tests/normalizeData.test.ts +120 -0
  140. package/src/tests/tsconfig.json +21 -0
  141. package/tsconfig.json +6 -0
  142. package/tsconfig.pkg.json +7 -1
  143. package/vitest.config.ts +20 -0
  144. package/dist/EntrypointReader.d.ts +0 -6
  145. package/dist/IsographEnvironment.d.ts +0 -56
  146. package/dist/PromiseWrapper.d.ts +0 -13
  147. package/dist/PromiseWrapper.js +0 -22
  148. package/dist/cache.d.ts +0 -26
  149. package/dist/cache.js +0 -274
  150. package/dist/componentCache.d.ts +0 -6
  151. package/dist/componentCache.js +0 -31
  152. package/dist/useImperativeReference.d.ts +0 -8
  153. package/dist/useImperativeReference.js +0 -28
  154. package/src/EntrypointReader.tsx +0 -20
  155. package/src/IsographEnvironment.tsx +0 -120
  156. package/src/PromiseWrapper.ts +0 -29
  157. package/src/cache.tsx +0 -484
  158. package/src/componentCache.ts +0 -41
  159. package/src/index.tsx +0 -617
  160. package/src/useImperativeReference.ts +0 -58
@@ -0,0 +1,52 @@
1
+ import {
2
+ ExtractParameters,
3
+ FragmentReference,
4
+ } from '../core/FragmentReference';
5
+ import {
6
+ UnassignedState,
7
+ useUpdatableDisposableState,
8
+ } from '@isograph/react-disposable-state';
9
+ import { LoadableField } from '../core/reader';
10
+ import { FetchOptions } from '../core/check';
11
+
12
+ type UseImperativeLoadableFieldReturn<
13
+ TReadFromStore extends { data: object; parameters: object },
14
+ TResult,
15
+ TProvidedArgs extends object,
16
+ > = {
17
+ fragmentReference:
18
+ | FragmentReference<TReadFromStore, TResult>
19
+ | UnassignedState;
20
+ loadField: (
21
+ // TODO this should be void iff all args are provided by the query, like in
22
+ // useClientSideDefer.
23
+ args: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs> | void,
24
+ fetchOptions?: FetchOptions,
25
+ ) => void;
26
+ };
27
+
28
+ export function useImperativeLoadableField<
29
+ TReadFromStore extends { data: object; parameters: object },
30
+ TResult,
31
+ TProvidedArgs extends object,
32
+ >(
33
+ loadableField: LoadableField<
34
+ TReadFromStore,
35
+ TResult,
36
+ Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>
37
+ >,
38
+ ): UseImperativeLoadableFieldReturn<TReadFromStore, TResult, TProvidedArgs> {
39
+ const { state, setState } =
40
+ useUpdatableDisposableState<FragmentReference<TReadFromStore, TResult>>();
41
+
42
+ return {
43
+ loadField: (
44
+ args: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs> | void,
45
+ fetchOptions?: FetchOptions,
46
+ ) => {
47
+ const [_id, loader] = loadableField(args, fetchOptions ?? {});
48
+ setState(loader());
49
+ },
50
+ fragmentReference: state,
51
+ };
52
+ }
@@ -0,0 +1,352 @@
1
+ import { LoadableField, type ReaderAst } from '../core/reader';
2
+ import { useIsographEnvironment } from '../react/IsographEnvironmentProvider';
3
+ 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
+ import { subscribeToAnyChange } from '../core/cache';
8
+ import { useState } from 'react';
9
+ import {
10
+ UNASSIGNED_STATE,
11
+ useUpdatableDisposableState,
12
+ } from '@isograph/react-disposable-state';
13
+ import {
14
+ createReferenceCountedPointer,
15
+ ReferenceCountedPointer,
16
+ } from '@isograph/reference-counted-pointer';
17
+ import { getPromiseState, readPromise } from '../core/PromiseWrapper';
18
+ import { type WithEncounteredRecords } from '../core/read';
19
+ import { useSubscribeToMultiple } from '../react/useReadAndSubscribe';
20
+ import { FetchOptions } from '../core/check';
21
+
22
+ type UseSkipLimitReturnValue<
23
+ TReadFromStore extends { data: object; parameters: object },
24
+ TItem,
25
+ > =
26
+ | {
27
+ readonly kind: 'Complete';
28
+ readonly fetchMore: (count: number, fetchOptions?: FetchOptions) => void;
29
+ readonly results: ReadonlyArray<TItem>;
30
+ }
31
+ | {
32
+ readonly kind: 'Pending';
33
+ readonly results: ReadonlyArray<TItem>;
34
+ readonly pendingFragment: FragmentReference<
35
+ TReadFromStore,
36
+ ReadonlyArray<TItem>
37
+ >;
38
+ };
39
+
40
+ type ArrayFragmentReference<
41
+ TReadFromStore extends { parameters: object; data: object },
42
+ TItem,
43
+ > = FragmentReference<TReadFromStore, ReadonlyArray<TItem>>;
44
+
45
+ type LoadedFragmentReferences<
46
+ TReadFromStore extends { parameters: object; data: object },
47
+ TItem,
48
+ > = ReadonlyArray<LoadedFragmentReference<TReadFromStore, TItem>>;
49
+
50
+ type LoadedFragmentReference<
51
+ TReadFromStore extends { parameters: object; data: object },
52
+ TItem,
53
+ > = ItemCleanupPair<
54
+ ReferenceCountedPointer<ArrayFragmentReference<TReadFromStore, TItem>>
55
+ >;
56
+
57
+ function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): ReadonlyArray<T> {
58
+ let outArray: Array<T> = [];
59
+ for (const subarr of arr) {
60
+ for (const item of subarr) {
61
+ outArray.push(item);
62
+ }
63
+ }
64
+ return outArray;
65
+ }
66
+
67
+ type UseSkipLimitPaginationArgs = {
68
+ skip: number;
69
+ limit: number;
70
+ };
71
+
72
+ export function useSkipLimitPagination<
73
+ TItem,
74
+ TReadFromStore extends {
75
+ parameters: object;
76
+ data: object;
77
+ },
78
+ >(
79
+ loadableField: LoadableField<
80
+ TReadFromStore,
81
+ ReadonlyArray<TItem>,
82
+ UseSkipLimitPaginationArgs
83
+ >,
84
+ initialState?: {
85
+ skip?: number | void | null;
86
+ },
87
+ ): UseSkipLimitReturnValue<TReadFromStore, TItem> {
88
+ const networkRequestOptions = {
89
+ suspendIfInFlight: true,
90
+ throwOnNetworkError: true,
91
+ };
92
+ const { state, setState } =
93
+ useUpdatableDisposableState<
94
+ LoadedFragmentReferences<TReadFromStore, TItem>
95
+ >();
96
+
97
+ const environment = useIsographEnvironment();
98
+
99
+ // TODO move this out of useSkipLimitPagination, and pass environment and networkRequestOptions
100
+ // as parameters (or recreate networkRequestOptions)
101
+ function readCompletedFragmentReferences(
102
+ completedReferences: ArrayFragmentReference<TReadFromStore, TItem>[],
103
+ ) {
104
+ const results = completedReferences.map((fragmentReference, i) => {
105
+ const readerWithRefetchQueries = readPromise(
106
+ fragmentReference.readerWithRefetchQueries,
107
+ );
108
+
109
+ // invariant: readOutDataAndRecords.length === completedReferences.length
110
+ const data = readOutDataAndRecords[i]?.item;
111
+ if (data == null) {
112
+ throw new Error(
113
+ 'Parameter data is unexpectedly null. This is indicative of a bug in Isograph.',
114
+ );
115
+ }
116
+
117
+ const firstParameter = {
118
+ data,
119
+ parameters: fragmentReference.variables,
120
+ };
121
+
122
+ if (
123
+ readerWithRefetchQueries.readerArtifact.kind !== 'EagerReaderArtifact'
124
+ ) {
125
+ throw new Error(
126
+ `@loadable field of kind "${readerWithRefetchQueries.readerArtifact.kind}" is not supported by useSkipLimitPagination`,
127
+ );
128
+ }
129
+
130
+ return readerWithRefetchQueries.readerArtifact.resolver(firstParameter);
131
+ });
132
+
133
+ const items = flatten(results);
134
+ return items;
135
+ }
136
+
137
+ function subscribeCompletedFragmentReferences(
138
+ completedReferences: ArrayFragmentReference<TReadFromStore, TItem>[],
139
+ ) {
140
+ return completedReferences.map(
141
+ (
142
+ fragmentReference,
143
+ i,
144
+ ): {
145
+ records: WithEncounteredRecords<TReadFromStore>;
146
+ callback: (
147
+ updatedRecords: WithEncounteredRecords<TReadFromStore>,
148
+ ) => void;
149
+ fragmentReference: ArrayFragmentReference<TReadFromStore, TItem>;
150
+ readerAst: ReaderAst<TItem>;
151
+ } => {
152
+ maybeUnwrapNetworkRequest(
153
+ fragmentReference.networkRequest,
154
+ networkRequestOptions,
155
+ );
156
+
157
+ const readerWithRefetchQueries = readPromise(
158
+ fragmentReference.readerWithRefetchQueries,
159
+ );
160
+
161
+ const records = readOutDataAndRecords[i];
162
+ if (records == null) {
163
+ throw new Error(
164
+ 'subscribeCompletedFragmentReferences records is unexpectedly null',
165
+ );
166
+ }
167
+
168
+ return {
169
+ fragmentReference,
170
+ readerAst: readerWithRefetchQueries.readerArtifact.readerAst,
171
+ records,
172
+ callback(_data) {
173
+ rerender({});
174
+ },
175
+ };
176
+ },
177
+ );
178
+ }
179
+
180
+ const getFetchMore =
181
+ (loadedSoFar: number) =>
182
+ (count: number, fetchOptions?: FetchOptions): void => {
183
+ const loadedField = loadableField(
184
+ {
185
+ skip: loadedSoFar,
186
+ limit: count,
187
+ },
188
+ fetchOptions ?? {},
189
+ )[1]();
190
+ const newPointer = createReferenceCountedPointer(loadedField);
191
+ const clonedPointers = loadedReferences.map(([refCountedPointer]) => {
192
+ const clonedRefCountedPointer = refCountedPointer.cloneIfNotDisposed();
193
+ if (clonedRefCountedPointer == null) {
194
+ throw new Error(
195
+ 'This reference counted pointer has already been disposed. \
196
+ This is indicative of a bug in useSkipLimitPagination.',
197
+ );
198
+ }
199
+ return clonedRefCountedPointer;
200
+ });
201
+ clonedPointers.push(newPointer);
202
+
203
+ const totalItemCleanupPair: ItemCleanupPair<
204
+ ReadonlyArray<
205
+ ItemCleanupPair<
206
+ ReferenceCountedPointer<
207
+ ArrayFragmentReference<TReadFromStore, TItem>
208
+ >
209
+ >
210
+ >
211
+ > = [
212
+ clonedPointers,
213
+ () => {
214
+ clonedPointers.forEach(([, dispose]) => {
215
+ dispose();
216
+ });
217
+ },
218
+ ];
219
+
220
+ setState(totalItemCleanupPair);
221
+ };
222
+
223
+ const [, rerender] = useState({});
224
+
225
+ const loadedReferences = state === UNASSIGNED_STATE ? [] : state;
226
+
227
+ const mostRecentItem:
228
+ | LoadedFragmentReference<TReadFromStore, TItem>
229
+ | undefined = loadedReferences[loadedReferences.length - 1];
230
+ const mostRecentFragmentReference =
231
+ mostRecentItem?.[0].getItemIfNotDisposed();
232
+
233
+ if (mostRecentItem && mostRecentFragmentReference === null) {
234
+ throw new Error(
235
+ 'FragmentReference is unexpectedly disposed. \
236
+ This is indicative of a bug in Isograph.',
237
+ );
238
+ }
239
+
240
+ const networkRequestStatus =
241
+ mostRecentFragmentReference &&
242
+ getPromiseState(mostRecentFragmentReference.networkRequest);
243
+
244
+ const slicedFragmentReferences =
245
+ networkRequestStatus?.kind === 'Ok'
246
+ ? loadedReferences
247
+ : loadedReferences.slice(0, loadedReferences.length - 1);
248
+
249
+ const completedFragmentReferences = slicedFragmentReferences.map(
250
+ ([pointer]) => {
251
+ const fragmentReference = pointer.getItemIfNotDisposed();
252
+ if (fragmentReference == null) {
253
+ throw new Error(
254
+ 'FragmentReference is unexpectedly disposed. \
255
+ This is indicative of a bug in Isograph.',
256
+ );
257
+ }
258
+ return fragmentReference;
259
+ },
260
+ );
261
+
262
+ const readOutDataAndRecords = completedFragmentReferences.map(
263
+ (fragmentReference) =>
264
+ readButDoNotEvaluate(
265
+ environment,
266
+ fragmentReference,
267
+ networkRequestOptions,
268
+ ),
269
+ );
270
+
271
+ useSubscribeToMultiple<TReadFromStore>(
272
+ subscribeCompletedFragmentReferences(completedFragmentReferences),
273
+ );
274
+
275
+ if (!networkRequestStatus) {
276
+ return {
277
+ kind: 'Complete',
278
+ fetchMore: getFetchMore(initialState?.skip ?? 0),
279
+ results: [],
280
+ };
281
+ }
282
+
283
+ switch (networkRequestStatus.kind) {
284
+ case 'Pending': {
285
+ const unsubscribe = subscribeToAnyChange(environment, () => {
286
+ unsubscribe();
287
+ rerender({});
288
+ });
289
+
290
+ return {
291
+ kind: 'Pending',
292
+ pendingFragment: mostRecentFragmentReference,
293
+ results: readCompletedFragmentReferences(completedFragmentReferences),
294
+ };
295
+ }
296
+ case 'Err': {
297
+ throw networkRequestStatus.error;
298
+ }
299
+ case 'Ok': {
300
+ const results = readCompletedFragmentReferences(
301
+ completedFragmentReferences,
302
+ );
303
+ return {
304
+ kind: 'Complete',
305
+ results,
306
+ fetchMore: getFetchMore(results.length),
307
+ };
308
+ }
309
+ }
310
+ }
311
+
312
+ // @ts-ignore
313
+ function tsTests() {
314
+ type Parameters = {
315
+ readonly search: string;
316
+ readonly skip: number;
317
+ readonly limit: number;
318
+ };
319
+
320
+ let basicLoadable!: LoadableField<
321
+ {
322
+ readonly data: object;
323
+ readonly parameters: Omit<Parameters, 'search'>;
324
+ },
325
+ object[]
326
+ >;
327
+
328
+ useSkipLimitPagination(basicLoadable);
329
+ useSkipLimitPagination(basicLoadable, {});
330
+ useSkipLimitPagination(basicLoadable, { skip: 10 });
331
+
332
+ let unprovidedSearchLoadable!: LoadableField<
333
+ {
334
+ readonly data: object;
335
+ readonly parameters: Parameters;
336
+ },
337
+ object[]
338
+ >;
339
+ // @ts-expect-error
340
+ useSkipLimitPagination(unprovidedSearchLoadable);
341
+
342
+ let providedSearchLoadable!: LoadableField<
343
+ {
344
+ readonly data: object;
345
+ readonly parameters: Parameters;
346
+ },
347
+ object[],
348
+ Omit<Parameters, 'search'>
349
+ >;
350
+
351
+ useSkipLimitPagination(providedSearchLoadable);
352
+ }
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { ExtractReadFromStore, IsographEntrypoint } from '../core/entrypoint';
3
+ import { FragmentReference } from '../core/FragmentReference';
4
+ import { useResult } from './useResult';
5
+ import { NetworkRequestReaderOptions } from '../core/read';
6
+
7
+ type IsExactlyIntrinsicAttributes<T> = T extends JSX.IntrinsicAttributes
8
+ ? JSX.IntrinsicAttributes extends T
9
+ ? true
10
+ : false
11
+ : false;
12
+
13
+ export function FragmentReader<
14
+ TProps extends Record<any, any>,
15
+ TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>>,
16
+ >(
17
+ props: IsExactlyIntrinsicAttributes<TProps> extends true
18
+ ? {
19
+ fragmentReference: FragmentReference<
20
+ ExtractReadFromStore<TEntrypoint>,
21
+ React.FC<TProps>
22
+ >;
23
+ additionalProps?: Record<PropertyKey, never>;
24
+ networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
25
+ }
26
+ : {
27
+ fragmentReference: FragmentReference<
28
+ ExtractReadFromStore<TEntrypoint>,
29
+ React.FC<TProps>
30
+ >;
31
+ additionalProps: Omit<TProps, keyof JSX.IntrinsicAttributes>;
32
+ networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
33
+ },
34
+ ): React.ReactNode {
35
+ const Component = useResult(
36
+ props.fragmentReference,
37
+ props.networkRequestOptions,
38
+ );
39
+ // TypeScript is not understanding that if additionalProps is Record<PropertyKey, never>,
40
+ // it means that TProps === JSX.IntrinsicAttributes.
41
+ // @ts-expect-error
42
+ return <Component {...props.additionalProps} />;
43
+ }
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { ReactNode, createContext, useContext } from 'react';
3
+ import { type IsographEnvironment } from '../core/IsographEnvironment';
4
+
5
+ export const IsographEnvironmentContext =
6
+ createContext<IsographEnvironment | null>(null);
7
+
8
+ export type IsographEnvironmentProviderProps = {
9
+ readonly environment: IsographEnvironment;
10
+ readonly children: ReactNode;
11
+ };
12
+
13
+ export function IsographEnvironmentProvider({
14
+ environment,
15
+ children,
16
+ }: IsographEnvironmentProviderProps): React.ReactElement {
17
+ return (
18
+ <IsographEnvironmentContext.Provider value={environment}>
19
+ {children}
20
+ </IsographEnvironmentContext.Provider>
21
+ );
22
+ }
23
+
24
+ export function useIsographEnvironment(): IsographEnvironment {
25
+ const context = useContext(IsographEnvironmentContext);
26
+ if (context == null) {
27
+ throw new Error(
28
+ 'Unexpected null environment context. Make sure to render ' +
29
+ 'this component within an IsographEnvironmentProvider component',
30
+ );
31
+ }
32
+ return context;
33
+ }
@@ -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
+ }
@@ -0,0 +1,68 @@
1
+ import {
2
+ UnassignedState,
3
+ useUpdatableDisposableState,
4
+ } from '@isograph/react-disposable-state';
5
+ import { IsographEntrypoint } from '../core/entrypoint';
6
+ import {
7
+ FragmentReference,
8
+ ExtractParameters,
9
+ } from '../core/FragmentReference';
10
+ import { useIsographEnvironment } from './IsographEnvironmentProvider';
11
+ import { ROOT_ID } from '../core/IsographEnvironment';
12
+ import { maybeMakeNetworkRequest } from '../core/makeNetworkRequest';
13
+ import { wrapResolvedValue } from '../core/PromiseWrapper';
14
+ import { FetchOptions } from '../core/check';
15
+
16
+ // TODO rename this to useImperativelyLoadedEntrypoint
17
+
18
+ export function useImperativeReference<
19
+ TReadFromStore extends { parameters: object; data: object },
20
+ TClientFieldValue,
21
+ >(
22
+ entrypoint: IsographEntrypoint<TReadFromStore, TClientFieldValue>,
23
+ ): {
24
+ fragmentReference:
25
+ | FragmentReference<TReadFromStore, TClientFieldValue>
26
+ | UnassignedState;
27
+ loadFragmentReference: (
28
+ variables: ExtractParameters<TReadFromStore>,
29
+ fetchOptions?: FetchOptions,
30
+ ) => void;
31
+ } {
32
+ const { state, setState } =
33
+ useUpdatableDisposableState<
34
+ FragmentReference<TReadFromStore, TClientFieldValue>
35
+ >();
36
+ const environment = useIsographEnvironment();
37
+ return {
38
+ fragmentReference: state,
39
+ loadFragmentReference: (
40
+ variables: ExtractParameters<TReadFromStore>,
41
+ fetchOptions?: FetchOptions,
42
+ ) => {
43
+ const [networkRequest, disposeNetworkRequest] = maybeMakeNetworkRequest(
44
+ environment,
45
+ entrypoint,
46
+ variables,
47
+ fetchOptions,
48
+ );
49
+ setState([
50
+ {
51
+ kind: 'FragmentReference',
52
+ readerWithRefetchQueries: wrapResolvedValue({
53
+ kind: 'ReaderWithRefetchQueries',
54
+ readerArtifact: entrypoint.readerWithRefetchQueries.readerArtifact,
55
+ nestedRefetchQueries:
56
+ entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
57
+ }),
58
+ root: { __link: ROOT_ID, __typename: entrypoint.concreteType },
59
+ variables,
60
+ networkRequest,
61
+ },
62
+ () => {
63
+ disposeNetworkRequest();
64
+ },
65
+ ]);
66
+ },
67
+ };
68
+ }
@@ -0,0 +1,42 @@
1
+ import {
2
+ FragmentReference,
3
+ ExtractParameters,
4
+ } from '../core/FragmentReference';
5
+ import { useIsographEnvironment } from './IsographEnvironmentProvider';
6
+ import { IsographEntrypoint } from '../core/entrypoint';
7
+ import { getOrCreateCacheForArtifact } from '../core/cache';
8
+ import { useLazyDisposableState } from '@isograph/react-disposable-state';
9
+ import { logMessage } from '../core/logging';
10
+ import { FetchOptions } from '../core/check';
11
+
12
+ export function useLazyReference<
13
+ TReadFromStore extends { parameters: object; data: object },
14
+ TClientFieldValue,
15
+ >(
16
+ entrypoint: IsographEntrypoint<TReadFromStore, TClientFieldValue>,
17
+ variables: ExtractParameters<TReadFromStore>,
18
+ fetchOptions?: FetchOptions,
19
+ ): {
20
+ fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue>;
21
+ } {
22
+ const environment = useIsographEnvironment();
23
+
24
+ if (entrypoint?.kind !== 'Entrypoint') {
25
+ // TODO have a separate error logger
26
+ logMessage(environment, {
27
+ kind: 'NonEntrypointReceived',
28
+ entrypoint,
29
+ });
30
+ }
31
+
32
+ const cache = getOrCreateCacheForArtifact(
33
+ environment,
34
+ entrypoint,
35
+ variables,
36
+ fetchOptions,
37
+ );
38
+
39
+ return {
40
+ fragmentReference: useLazyDisposableState(cache).state,
41
+ };
42
+ }
@@ -0,0 +1,81 @@
1
+ import { useEffect, useState } from 'react';
2
+ import {
3
+ FragmentReference,
4
+ stableIdForFragmentReference,
5
+ ExtractData,
6
+ } from '../core/FragmentReference';
7
+ import {
8
+ NetworkRequestReaderOptions,
9
+ readButDoNotEvaluate,
10
+ WithEncounteredRecords,
11
+ } from '../core/read';
12
+ import { useRerenderOnChange } from './useRerenderOnChange';
13
+ import { useIsographEnvironment } from './IsographEnvironmentProvider';
14
+ import { subscribe } from '../core/cache';
15
+ import type { ReaderAst } from '../core/reader';
16
+
17
+ /**
18
+ * Read the data from a fragment reference and subscribe to updates.
19
+ */
20
+ export function useReadAndSubscribe<
21
+ TReadFromStore extends { parameters: object; data: object },
22
+ >(
23
+ fragmentReference: FragmentReference<TReadFromStore, any>,
24
+ networkRequestOptions: NetworkRequestReaderOptions,
25
+ readerAst: ReaderAst<TReadFromStore>,
26
+ ): ExtractData<TReadFromStore> {
27
+ const environment = useIsographEnvironment();
28
+ const [readOutDataAndRecords, setReadOutDataAndRecords] = useState(() =>
29
+ readButDoNotEvaluate(environment, fragmentReference, networkRequestOptions),
30
+ );
31
+ useRerenderOnChange(
32
+ readOutDataAndRecords,
33
+ fragmentReference,
34
+ setReadOutDataAndRecords,
35
+ readerAst,
36
+ );
37
+ return readOutDataAndRecords.item;
38
+ }
39
+
40
+ export function useSubscribeToMultiple<
41
+ TReadFromStore extends { parameters: object; data: object },
42
+ >(
43
+ items: ReadonlyArray<{
44
+ records: WithEncounteredRecords<TReadFromStore>;
45
+ callback: (updatedRecords: WithEncounteredRecords<TReadFromStore>) => void;
46
+ fragmentReference: FragmentReference<TReadFromStore, any>;
47
+ readerAst: ReaderAst<TReadFromStore>;
48
+ }>,
49
+ ) {
50
+ const environment = useIsographEnvironment();
51
+ useEffect(
52
+ () => {
53
+ const cleanupFns = items.map(
54
+ ({ records, callback, fragmentReference, readerAst }) => {
55
+ return subscribe(
56
+ environment,
57
+ records,
58
+ fragmentReference,
59
+ callback,
60
+ readerAst,
61
+ );
62
+ },
63
+ );
64
+ return () => {
65
+ cleanupFns.forEach((loader) => {
66
+ loader();
67
+ });
68
+ };
69
+ },
70
+ // By analogy to useReadAndSubscribe, we can have an empty dependency array?
71
+ // Maybe callback has to be depended on. I don't know!
72
+ // TODO find out
73
+ [
74
+ items
75
+ .map(({ fragmentReference }) =>
76
+ stableIdForFragmentReference(fragmentReference),
77
+ )
78
+ .join('.'),
79
+ ],
80
+ );
81
+ }