@isograph/react 0.3.1 → 0.4.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 (119) hide show
  1. package/.turbo/turbo-compile-libs.log +5 -0
  2. package/dist/core/FragmentReference.d.ts +5 -5
  3. package/dist/core/FragmentReference.d.ts.map +1 -1
  4. package/dist/core/IsographEnvironment.d.ts +14 -9
  5. package/dist/core/IsographEnvironment.d.ts.map +1 -1
  6. package/dist/core/PromiseWrapper.d.ts +4 -4
  7. package/dist/core/PromiseWrapper.d.ts.map +1 -1
  8. package/dist/core/PromiseWrapper.js +2 -9
  9. package/dist/core/areEqualWithDeepComparison.d.ts +1 -3
  10. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -1
  11. package/dist/core/areEqualWithDeepComparison.js +0 -2
  12. package/dist/core/brand.d.ts +2 -0
  13. package/dist/core/brand.d.ts.map +1 -0
  14. package/dist/core/brand.js +2 -0
  15. package/dist/core/cache.d.ts +7 -6
  16. package/dist/core/cache.d.ts.map +1 -1
  17. package/dist/core/cache.js +46 -28
  18. package/dist/core/check.d.ts +3 -3
  19. package/dist/core/check.d.ts.map +1 -1
  20. package/dist/core/componentCache.d.ts.map +1 -1
  21. package/dist/core/componentCache.js +1 -1
  22. package/dist/core/entrypoint.d.ts +20 -2
  23. package/dist/core/entrypoint.d.ts.map +1 -1
  24. package/dist/core/garbageCollection.d.ts +2 -2
  25. package/dist/core/garbageCollection.d.ts.map +1 -1
  26. package/dist/core/logging.d.ts +13 -4
  27. package/dist/core/logging.d.ts.map +1 -1
  28. package/dist/core/makeNetworkRequest.d.ts +3 -3
  29. package/dist/core/makeNetworkRequest.d.ts.map +1 -1
  30. package/dist/core/makeNetworkRequest.js +15 -15
  31. package/dist/core/read.d.ts +8 -8
  32. package/dist/core/read.d.ts.map +1 -1
  33. package/dist/core/read.js +98 -29
  34. package/dist/core/reader.d.ts +12 -8
  35. package/dist/core/reader.d.ts.map +1 -1
  36. package/dist/core/startUpdate.d.ts +7 -4
  37. package/dist/core/startUpdate.d.ts.map +1 -1
  38. package/dist/core/startUpdate.js +153 -5
  39. package/dist/index.d.ts +8 -5
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +8 -1
  42. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -1
  43. package/dist/loadable-hooks/useConnectionSpecPagination.js +1 -1
  44. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -1
  45. package/dist/loadable-hooks/useSkipLimitPagination.js +1 -1
  46. package/dist/react/FragmentReader.d.ts +7 -13
  47. package/dist/react/FragmentReader.d.ts.map +1 -1
  48. package/dist/react/FragmentReader.js +3 -30
  49. package/dist/react/FragmentRenderer.d.ts +15 -0
  50. package/dist/react/FragmentRenderer.d.ts.map +1 -0
  51. package/dist/react/FragmentRenderer.js +35 -0
  52. package/dist/react/LoadableFieldReader.d.ts +12 -0
  53. package/dist/react/LoadableFieldReader.d.ts.map +1 -0
  54. package/dist/react/LoadableFieldReader.js +10 -0
  55. package/dist/react/LoadableFieldRenderer.d.ts +13 -0
  56. package/dist/react/LoadableFieldRenderer.d.ts.map +1 -0
  57. package/dist/react/LoadableFieldRenderer.js +37 -0
  58. package/dist/react/useImperativeReference.d.ts.map +1 -1
  59. package/dist/react/useImperativeReference.js +6 -6
  60. package/dist/react/useResult.d.ts.map +1 -1
  61. package/dist/react/useResult.js +1 -1
  62. package/package.json +4 -5
  63. package/src/core/FragmentReference.ts +16 -7
  64. package/src/core/IsographEnvironment.ts +19 -9
  65. package/src/core/PromiseWrapper.ts +13 -16
  66. package/src/core/areEqualWithDeepComparison.ts +5 -5
  67. package/src/core/brand.ts +18 -0
  68. package/src/core/cache.ts +50 -43
  69. package/src/core/check.ts +4 -4
  70. package/src/core/componentCache.ts +5 -1
  71. package/src/core/entrypoint.ts +32 -5
  72. package/src/core/garbageCollection.ts +3 -3
  73. package/src/core/logging.ts +16 -4
  74. package/src/core/makeNetworkRequest.ts +48 -23
  75. package/src/core/read.ts +153 -48
  76. package/src/core/reader.ts +11 -7
  77. package/src/core/startUpdate.ts +313 -7
  78. package/src/index.ts +11 -3
  79. package/src/loadable-hooks/useConnectionSpecPagination.ts +1 -0
  80. package/src/loadable-hooks/useSkipLimitPagination.ts +1 -0
  81. package/src/react/FragmentReader.tsx +23 -39
  82. package/src/react/FragmentRenderer.tsx +46 -0
  83. package/src/react/LoadableFieldReader.tsx +40 -0
  84. package/src/react/LoadableFieldRenderer.tsx +41 -0
  85. package/src/react/useImperativeReference.ts +9 -8
  86. package/src/react/useResult.ts +1 -0
  87. package/src/tests/__isograph/Economist/link/output_type.ts +2 -0
  88. package/src/tests/__isograph/Node/asEconomist/resolver_reader.ts +28 -0
  89. package/src/tests/__isograph/Node/link/output_type.ts +3 -0
  90. package/src/tests/__isograph/Query/linkedUpdate/entrypoint.ts +31 -0
  91. package/src/tests/__isograph/Query/linkedUpdate/normalization_ast.ts +95 -0
  92. package/src/tests/__isograph/Query/linkedUpdate/output_type.ts +3 -0
  93. package/src/tests/__isograph/Query/linkedUpdate/param_type.ts +51 -0
  94. package/src/tests/__isograph/Query/linkedUpdate/query_text.ts +20 -0
  95. package/src/tests/__isograph/Query/linkedUpdate/resolver_reader.ts +93 -0
  96. package/src/tests/__isograph/Query/meName/entrypoint.ts +4 -1
  97. package/src/tests/__isograph/Query/meName/query_text.ts +1 -1
  98. package/src/tests/__isograph/Query/meName/resolver_reader.ts +1 -0
  99. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +4 -1
  100. package/src/tests/__isograph/Query/meNameSuccessor/query_text.ts +1 -1
  101. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +3 -0
  102. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +4 -1
  103. package/src/tests/__isograph/Query/nodeField/query_text.ts +1 -1
  104. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +1 -0
  105. package/src/tests/__isograph/Query/startUpdate/entrypoint.ts +31 -0
  106. package/src/tests/__isograph/Query/startUpdate/normalization_ast.ts +51 -0
  107. package/src/tests/__isograph/Query/startUpdate/output_type.ts +3 -0
  108. package/src/tests/__isograph/Query/startUpdate/param_type.ts +26 -0
  109. package/src/tests/__isograph/Query/startUpdate/parameters_type.ts +3 -0
  110. package/src/tests/__isograph/Query/startUpdate/query_text.ts +11 -0
  111. package/src/tests/__isograph/Query/startUpdate/resolver_reader.ts +55 -0
  112. package/src/tests/__isograph/Query/subquery/entrypoint.ts +4 -1
  113. package/src/tests/__isograph/Query/subquery/query_text.ts +1 -1
  114. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +2 -0
  115. package/src/tests/__isograph/iso.ts +21 -1
  116. package/src/tests/__isograph/tsconfig.json +8 -0
  117. package/src/tests/normalizeData.test.ts +0 -1
  118. package/src/tests/startUpdate.test.ts +205 -0
  119. package/.turbo/turbo-compile-typescript.log +0 -4
@@ -1,28 +1,334 @@
1
+ import {
2
+ callSubscriptions,
3
+ getParentRecordKey,
4
+ insertEmptySetIfMissing,
5
+ type EncounteredIds,
6
+ } from './cache';
7
+ import type { RefetchQueryNormalizationArtifactWrapper } from './entrypoint';
1
8
  import {
2
9
  stableIdForFragmentReference,
10
+ type ExtractParameters,
3
11
  type ExtractStartUpdate,
12
+ type ExtractUpdatableData,
4
13
  type FragmentReference,
5
14
  type UnknownTReadFromStore,
6
15
  } from './FragmentReference';
7
- import type { IsographEnvironment } from './IsographEnvironment';
16
+ import {
17
+ assertLink,
18
+ type IsographEnvironment,
19
+ type StoreLink,
20
+ } from './IsographEnvironment';
21
+ import { logMessage } from './logging';
22
+ import { readPromise, type PromiseWrapper } from './PromiseWrapper';
23
+ import {
24
+ readImperativelyLoadedField,
25
+ readLinkedFieldData,
26
+ readLoadablySelectedFieldData,
27
+ readResolverFieldData,
28
+ readScalarFieldData,
29
+ type NetworkRequestReaderOptions,
30
+ type ReadDataResultSuccess,
31
+ } from './read';
32
+ import type { ReaderAst } from './reader';
8
33
 
9
34
  export function getOrCreateCachedStartUpdate<
10
35
  TReadFromStore extends UnknownTReadFromStore,
11
36
  >(
12
37
  environment: IsographEnvironment,
13
- fragmentReference: FragmentReference<TReadFromStore, any>,
38
+ fragmentReference: FragmentReference<TReadFromStore, unknown>,
14
39
  eagerResolverName: string,
40
+ networkRequestOptions: NetworkRequestReaderOptions,
15
41
  ): ExtractStartUpdate<TReadFromStore> {
16
42
  return (environment.eagerReaderCache[
17
43
  stableIdForFragmentReference(fragmentReference, eagerResolverName)
18
- ] ??= createStartUpdate(environment, fragmentReference));
44
+ ] ??= createStartUpdate(
45
+ environment,
46
+ fragmentReference,
47
+ networkRequestOptions,
48
+ ));
19
49
  }
20
50
 
21
51
  export function createStartUpdate<TReadFromStore extends UnknownTReadFromStore>(
22
- _environment: IsographEnvironment,
23
- _fragmentReference: FragmentReference<TReadFromStore, any>,
52
+ environment: IsographEnvironment,
53
+ fragmentReference: FragmentReference<TReadFromStore, unknown>,
54
+ networkRequestOptions: NetworkRequestReaderOptions,
24
55
  ): ExtractStartUpdate<TReadFromStore> {
25
- return (_updater) => {
26
- // TODO start update
56
+ return (updater) => {
57
+ let mutableUpdatedIds: EncounteredIds = new Map();
58
+
59
+ let data = createUpdatableProxy(
60
+ environment,
61
+ fragmentReference,
62
+ networkRequestOptions,
63
+ mutableUpdatedIds,
64
+ );
65
+
66
+ try {
67
+ updater(data);
68
+ } catch (e) {
69
+ logMessage(environment, () => ({
70
+ kind: 'StartUpdateError',
71
+ error: e,
72
+ }));
73
+ throw e;
74
+ } finally {
75
+ logMessage(environment, () => ({
76
+ kind: 'StartUpdateComplete',
77
+ updatedIds: mutableUpdatedIds,
78
+ }));
79
+ callSubscriptions(environment, mutableUpdatedIds);
80
+ }
81
+ };
82
+ }
83
+
84
+ export function createUpdatableProxy<
85
+ TReadFromStore extends UnknownTReadFromStore,
86
+ >(
87
+ environment: IsographEnvironment,
88
+ fragmentReference: FragmentReference<TReadFromStore, unknown>,
89
+ networkRequestOptions: NetworkRequestReaderOptions,
90
+ mutableUpdatedIds: EncounteredIds,
91
+ ): ExtractUpdatableData<TReadFromStore> {
92
+ const readerWithRefetchQueries = readPromise(
93
+ fragmentReference.readerWithRefetchQueries,
94
+ );
95
+
96
+ return readUpdatableData(
97
+ environment,
98
+ readerWithRefetchQueries.readerArtifact.readerAst,
99
+ fragmentReference.root,
100
+ fragmentReference.variables ?? {},
101
+ readerWithRefetchQueries.nestedRefetchQueries,
102
+ fragmentReference.networkRequest,
103
+ networkRequestOptions,
104
+ {
105
+ lastInvalidated: 0,
106
+ },
107
+ mutableUpdatedIds,
108
+ ).data;
109
+ }
110
+
111
+ type MutableInvalidationState = {
112
+ lastInvalidated: number;
113
+ };
114
+
115
+ function defineCachedProperty<T>(
116
+ target: T,
117
+ property: PropertyKey,
118
+ mutableState: MutableInvalidationState,
119
+ get: () => any,
120
+ set?: (v: any) => void,
121
+ ) {
122
+ let value:
123
+ | { kind: 'Set'; value: T; validatedAt: number }
124
+ | {
125
+ kind: 'NotSet';
126
+ } = {
127
+ kind: 'NotSet',
128
+ };
129
+
130
+ Object.defineProperty(target, property, {
131
+ configurable: false,
132
+ enumerable: true,
133
+ get: () => {
134
+ if (
135
+ value.kind === 'NotSet' ||
136
+ value.validatedAt < mutableState.lastInvalidated
137
+ ) {
138
+ value = {
139
+ kind: 'Set',
140
+ value: get(),
141
+ validatedAt: mutableState.lastInvalidated,
142
+ };
143
+ }
144
+ return value.value;
145
+ },
146
+ ...(set && {
147
+ set: (newValue) => {
148
+ set(newValue);
149
+ mutableState.lastInvalidated++;
150
+ },
151
+ }),
152
+ });
153
+ }
154
+
155
+ function readUpdatableData<TReadFromStore extends UnknownTReadFromStore>(
156
+ environment: IsographEnvironment,
157
+ ast: ReaderAst<TReadFromStore>,
158
+ root: StoreLink,
159
+ variables: ExtractParameters<TReadFromStore>,
160
+ nestedRefetchQueries: RefetchQueryNormalizationArtifactWrapper[],
161
+ networkRequest: PromiseWrapper<void, any>,
162
+ networkRequestOptions: NetworkRequestReaderOptions,
163
+ mutableState: MutableInvalidationState,
164
+ mutableUpdatedIds: EncounteredIds,
165
+ ): ReadDataResultSuccess<ExtractUpdatableData<TReadFromStore>> {
166
+ let storeRecord = environment.store[root.__typename]?.[root.__link];
167
+ if (storeRecord == null) {
168
+ return {
169
+ kind: 'Success',
170
+ data: null as any,
171
+ };
172
+ }
173
+
174
+ let target: { [index: string]: any } = {};
175
+
176
+ for (const field of ast) {
177
+ switch (field.kind) {
178
+ case 'Scalar': {
179
+ const storeRecordName = getParentRecordKey(field, variables);
180
+
181
+ defineCachedProperty(
182
+ target,
183
+ field.alias ?? field.fieldName,
184
+ mutableState,
185
+ () => {
186
+ const data = readScalarFieldData(
187
+ field,
188
+ storeRecord,
189
+ root,
190
+ variables,
191
+ );
192
+ if (data.kind === 'MissingData') {
193
+ throw new Error(data.reason);
194
+ }
195
+ return data.data;
196
+ },
197
+ field.isUpdatable
198
+ ? (newValue) => {
199
+ storeRecord[storeRecordName] = newValue;
200
+ const updatedIds = insertEmptySetIfMissing(
201
+ mutableUpdatedIds,
202
+ root.__typename,
203
+ );
204
+ updatedIds.add(root.__link);
205
+ }
206
+ : undefined,
207
+ );
208
+ break;
209
+ }
210
+ case 'Linked': {
211
+ const storeRecordName = getParentRecordKey(field, variables);
212
+ defineCachedProperty(
213
+ target,
214
+ field.alias ?? field.fieldName,
215
+ mutableState,
216
+ () => {
217
+ const data = readLinkedFieldData(
218
+ environment,
219
+ field,
220
+ storeRecord,
221
+ root,
222
+ variables,
223
+ nestedRefetchQueries,
224
+ networkRequest,
225
+ networkRequestOptions,
226
+ (ast, root) =>
227
+ readUpdatableData(
228
+ environment,
229
+ ast,
230
+ root,
231
+ variables,
232
+ nestedRefetchQueries,
233
+ networkRequest,
234
+ networkRequestOptions,
235
+ mutableState,
236
+ mutableUpdatedIds,
237
+ ),
238
+ );
239
+ if (data.kind === 'MissingData') {
240
+ throw new Error(data.reason);
241
+ }
242
+ return data.data;
243
+ },
244
+ 'isUpdatable' in field && field.isUpdatable
245
+ ? (newValue) => {
246
+ if (Array.isArray(newValue)) {
247
+ storeRecord[storeRecordName] = newValue.map((node) =>
248
+ assertLink(node?.link),
249
+ );
250
+ } else {
251
+ storeRecord[storeRecordName] = assertLink(newValue?.link);
252
+ }
253
+ const updatedIds = insertEmptySetIfMissing(
254
+ mutableUpdatedIds,
255
+ root.__typename,
256
+ );
257
+ updatedIds.add(root.__link);
258
+ }
259
+ : undefined,
260
+ );
261
+ break;
262
+ }
263
+ case 'ImperativelyLoadedField': {
264
+ defineCachedProperty(target, field.alias, mutableState, () => {
265
+ const data = readImperativelyLoadedField(
266
+ environment,
267
+ field,
268
+ root,
269
+ variables,
270
+ nestedRefetchQueries,
271
+ networkRequest,
272
+ networkRequestOptions,
273
+ new Map(),
274
+ );
275
+ if (data.kind === 'MissingData') {
276
+ throw new Error(data.reason);
277
+ }
278
+ return data.data;
279
+ });
280
+ break;
281
+ }
282
+ case 'Resolver': {
283
+ defineCachedProperty(target, field.alias, mutableState, () => {
284
+ const data = readResolverFieldData(
285
+ environment,
286
+ field,
287
+ root,
288
+ variables,
289
+ nestedRefetchQueries,
290
+ networkRequest,
291
+ networkRequestOptions,
292
+ new Map(),
293
+ );
294
+ if (data.kind === 'MissingData') {
295
+ throw new Error(data.reason);
296
+ }
297
+ return data.data;
298
+ });
299
+ break;
300
+ }
301
+ case 'LoadablySelectedField': {
302
+ defineCachedProperty(target, field.alias, mutableState, () => {
303
+ const data = readLoadablySelectedFieldData(
304
+ environment,
305
+ field,
306
+ root,
307
+ variables,
308
+ networkRequest,
309
+ networkRequestOptions,
310
+ new Map(),
311
+ );
312
+ if (data.kind === 'MissingData') {
313
+ throw new Error(data.reason);
314
+ }
315
+ return data.data;
316
+ });
317
+ break;
318
+ }
319
+ case 'Link': {
320
+ target[field.alias] = root;
321
+ break;
322
+ }
323
+ default: {
324
+ field satisfies never;
325
+ throw new Error('Unexpected case.');
326
+ }
327
+ }
328
+ }
329
+
330
+ return {
331
+ kind: 'Success',
332
+ data: target as any,
27
333
  };
28
334
  }
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export {
18
18
  NOT_SET,
19
19
  } from './core/PromiseWrapper';
20
20
  export {
21
+ callSubscriptions,
21
22
  subscribe,
22
23
  normalizeData,
23
24
  type NetworkResponseObject,
@@ -34,6 +35,7 @@ export {
34
35
  type IsographNetworkFunction,
35
36
  type IsographStore,
36
37
  type MissingFieldHandler,
38
+ type StoreLink,
37
39
  type Link,
38
40
  type StoreRecord,
39
41
  type CacheMap,
@@ -63,7 +65,7 @@ export {
63
65
  type StableId,
64
66
  type ResolverFirstParameter,
65
67
  type ReaderImperativelyLoadedField,
66
- type ReaderLoadableField,
68
+ type LoadablySelectedField as ReaderLoadableField,
67
69
  type ReaderLinkField,
68
70
  type StartUpdate,
69
71
  } from './core/reader';
@@ -75,6 +77,9 @@ export {
75
77
  type NormalizationLinkedField,
76
78
  type NormalizationScalarField,
77
79
  type IsographEntrypoint,
80
+ type IsographOperation,
81
+ type IsographPersistedOperation,
82
+ type IsographPersistedOperationExtraInfo,
78
83
  assertIsEntrypoint,
79
84
  type RefetchQueryNormalizationArtifact,
80
85
  type RefetchQueryNormalizationArtifactWrapper,
@@ -137,9 +142,12 @@ export {
137
142
  type UseImperativeReferenceResult,
138
143
  } from './react/useImperativeReference';
139
144
  export {
140
- FragmentReader,
145
+ FragmentRenderer,
141
146
  type IsExactlyIntrinsicAttributes,
142
- } from './react/FragmentReader';
147
+ } from './react/FragmentRenderer';
148
+ export { FragmentReader } from './react/FragmentReader';
149
+ export { LoadableFieldReader } from './react/LoadableFieldReader';
150
+ export { LoadableFieldRenderer } from './react/LoadableFieldRenderer';
143
151
  export { useResult } from './react/useResult';
144
152
  export {
145
153
  useReadAndSubscribe,
@@ -135,6 +135,7 @@ export function useConnectionSpecPagination<
135
135
  environment,
136
136
  fragmentReference,
137
137
  readerWithRefetchQueries.readerArtifact.fieldName,
138
+ networkRequestOptions,
138
139
  ),
139
140
  }
140
141
  : undefined),
@@ -129,6 +129,7 @@ export function useSkipLimitPagination<
129
129
  environment,
130
130
  fragmentReference,
131
131
  readerWithRefetchQueries.readerArtifact.kind,
132
+ networkRequestOptions,
132
133
  ),
133
134
  }
134
135
  : undefined),
@@ -1,43 +1,27 @@
1
- import * as React from 'react';
2
- import { ExtractReadFromStore, IsographEntrypoint } from '../core/entrypoint';
3
- import { FragmentReference } from '../core/FragmentReference';
4
- import { NetworkRequestReaderOptions } from '../core/read';
1
+ import {
2
+ type ExtractReadFromStore,
3
+ type IsographEntrypoint,
4
+ } from '../core/entrypoint';
5
+ import { type FragmentReference } from '../core/FragmentReference';
6
+ import { type NetworkRequestReaderOptions } from '../core/read';
5
7
  import { useResult } from './useResult';
6
8
 
7
- export type IsExactlyIntrinsicAttributes<T> = T extends JSX.IntrinsicAttributes
8
- ? JSX.IntrinsicAttributes extends T
9
- ? true
10
- : false
11
- : false;
12
-
13
9
  export function FragmentReader<
14
- TProps extends Record<any, any>,
15
- TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>, any>,
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} />;
10
+ TResult,
11
+ TEntrypoint extends IsographEntrypoint<any, TResult, any>,
12
+ TChildrenResult,
13
+ >({
14
+ fragmentReference,
15
+ networkRequestOptions,
16
+ children,
17
+ }: {
18
+ fragmentReference: FragmentReference<
19
+ ExtractReadFromStore<TEntrypoint>,
20
+ TResult
21
+ >;
22
+ networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
23
+ children: (data: TResult) => TChildrenResult;
24
+ }): TChildrenResult {
25
+ const result = useResult(fragmentReference, networkRequestOptions);
26
+ return children(result);
43
27
  }
@@ -0,0 +1,46 @@
1
+ import * as React from 'react';
2
+ import {
3
+ type ExtractReadFromStore,
4
+ type IsographEntrypoint,
5
+ } from '../core/entrypoint';
6
+ import { type FragmentReference } from '../core/FragmentReference';
7
+ import { type NetworkRequestReaderOptions } from '../core/read';
8
+ import { useResult } from './useResult';
9
+
10
+ export type IsExactlyIntrinsicAttributes<T> = T extends JSX.IntrinsicAttributes
11
+ ? JSX.IntrinsicAttributes extends T
12
+ ? true
13
+ : false
14
+ : false;
15
+
16
+ export function FragmentRenderer<
17
+ TProps extends Record<any, any>,
18
+ TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>, any>,
19
+ >(
20
+ props: IsExactlyIntrinsicAttributes<TProps> extends true
21
+ ? {
22
+ fragmentReference: FragmentReference<
23
+ ExtractReadFromStore<TEntrypoint>,
24
+ React.FC<TProps>
25
+ >;
26
+ additionalProps?: Record<PropertyKey, never>;
27
+ networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
28
+ }
29
+ : {
30
+ fragmentReference: FragmentReference<
31
+ ExtractReadFromStore<TEntrypoint>,
32
+ React.FC<TProps>
33
+ >;
34
+ additionalProps: Omit<TProps, keyof JSX.IntrinsicAttributes>;
35
+ networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
36
+ },
37
+ ): React.ReactNode {
38
+ const Component = useResult(
39
+ props.fragmentReference,
40
+ props.networkRequestOptions,
41
+ );
42
+ // TypeScript is not understanding that if additionalProps is Record<PropertyKey, never>,
43
+ // it means that TProps === JSX.IntrinsicAttributes.
44
+ // @ts-expect-error
45
+ return <Component {...props.additionalProps} />;
46
+ }
@@ -0,0 +1,40 @@
1
+ import { type FetchOptions } from '../core/check';
2
+ import {
3
+ ExtractParameters,
4
+ type UnknownTReadFromStore,
5
+ } from '../core/FragmentReference';
6
+ import { type NetworkRequestReaderOptions } from '../core/read';
7
+ import { type LoadableField } from '../core/reader';
8
+ import { useClientSideDefer } from '../loadable-hooks/useClientSideDefer';
9
+ import { useResult } from './useResult';
10
+
11
+ export function LoadableFieldReader<
12
+ TReadFromStore extends UnknownTReadFromStore,
13
+ TResult,
14
+ TProvidedArgs extends object,
15
+ TChildrenResult,
16
+ >(props: {
17
+ loadableField: LoadableField<
18
+ TReadFromStore,
19
+ TResult,
20
+ Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>
21
+ >;
22
+ // TODO we can improve this to not require args if its an empty object
23
+ args: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>;
24
+ fetchOptions?: FetchOptions<TResult>;
25
+ networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
26
+ children: (arg: TResult) => TChildrenResult;
27
+ }): TChildrenResult {
28
+ const { fragmentReference } = useClientSideDefer(
29
+ props.loadableField,
30
+ props.args,
31
+ props.fetchOptions,
32
+ );
33
+
34
+ const readOutFragmentData = useResult(
35
+ fragmentReference,
36
+ props.networkRequestOptions,
37
+ );
38
+
39
+ return props.children(readOutFragmentData);
40
+ }
@@ -0,0 +1,41 @@
1
+ import * as React from 'react';
2
+ import { type FetchOptions } from '../core/check';
3
+ import {
4
+ ExtractParameters,
5
+ type UnknownTReadFromStore,
6
+ } from '../core/FragmentReference';
7
+ import { type NetworkRequestReaderOptions } from '../core/read';
8
+ import { type LoadableField } from '../core/reader';
9
+ import { useClientSideDefer } from '../loadable-hooks/useClientSideDefer';
10
+ import { useResult } from './useResult';
11
+
12
+ export function LoadableFieldRenderer<
13
+ TReadFromStore extends UnknownTReadFromStore,
14
+ TProvidedArgs extends object,
15
+ TChildrenResult,
16
+ TProps,
17
+ >(props: {
18
+ loadableField: LoadableField<
19
+ TReadFromStore,
20
+ React.FC<TProps>,
21
+ Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>
22
+ >;
23
+ // TODO we can improve this to not require args if its an empty object
24
+ args: Omit<ExtractParameters<TReadFromStore>, keyof TProvidedArgs>;
25
+ fetchOptions?: FetchOptions<React.FC<TProps>>;
26
+ networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
27
+ additionalProps: Omit<TProps, keyof JSX.IntrinsicAttributes>;
28
+ }): TChildrenResult {
29
+ const { fragmentReference } = useClientSideDefer(
30
+ props.loadableField,
31
+ props.args,
32
+ props.fetchOptions,
33
+ );
34
+
35
+ const Component = useResult(fragmentReference, props.networkRequestOptions);
36
+
37
+ // TODO we probably can figure out a way to convince TypeScript of
38
+ // the validity of this.
39
+ // @ts-expect-error
40
+ return <Component {...props.additionalProps} />;
41
+ }
@@ -15,7 +15,7 @@ import {
15
15
  } from '../core/FragmentReference';
16
16
  import { ROOT_ID } from '../core/IsographEnvironment';
17
17
  import { maybeMakeNetworkRequest } from '../core/makeNetworkRequest';
18
- import { wrapResolvedValue } from '../core/PromiseWrapper';
18
+ import { wrapPromise, wrapResolvedValue } from '../core/PromiseWrapper';
19
19
  import { useIsographEnvironment } from './IsographEnvironmentProvider';
20
20
 
21
21
  export type UseImperativeReferenceResult<
@@ -61,21 +61,22 @@ export function useImperativeReference<
61
61
  variables: ExtractParameters<TReadFromStore>,
62
62
  fetchOptions?: FetchOptions<TClientFieldValue>,
63
63
  ) => {
64
+ const readerWithRefetchQueries =
65
+ entrypoint.readerWithRefetchQueries.kind ===
66
+ 'ReaderWithRefetchQueriesLoader'
67
+ ? wrapPromise(entrypoint.readerWithRefetchQueries.loader())
68
+ : wrapResolvedValue(entrypoint.readerWithRefetchQueries);
64
69
  const [networkRequest, disposeNetworkRequest] = maybeMakeNetworkRequest(
65
70
  environment,
66
71
  entrypoint,
67
72
  variables,
68
- fetchOptions,
73
+ readerWithRefetchQueries,
74
+ fetchOptions ?? null,
69
75
  );
70
76
  setState([
71
77
  {
72
78
  kind: 'FragmentReference',
73
- readerWithRefetchQueries: wrapResolvedValue({
74
- kind: 'ReaderWithRefetchQueries',
75
- readerArtifact: entrypoint.readerWithRefetchQueries.readerArtifact,
76
- nestedRefetchQueries:
77
- entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
78
- }),
79
+ readerWithRefetchQueries,
79
80
  root: { __link: ROOT_ID, __typename: entrypoint.concreteType },
80
81
  variables,
81
82
  networkRequest,
@@ -61,6 +61,7 @@ export function useResult<
61
61
  environment,
62
62
  fragmentReference,
63
63
  readerWithRefetchQueries.readerArtifact.fieldName,
64
+ networkRequestOptions,
64
65
  ),
65
66
  }
66
67
  : undefined),
@@ -0,0 +1,2 @@
1
+ import type { Link } from '@isograph/react';
2
+ export type Economist__link__output_type = Link<"Economist">;
@@ -0,0 +1,28 @@
1
+ import type { EagerReaderArtifact, ReaderAst, Link } from '@isograph/react';
2
+
3
+ const readerAst: ReaderAst<{ data: any, parameters: Record<PropertyKey, never> }> = [
4
+ {
5
+ kind: "Scalar",
6
+ fieldName: "__typename",
7
+ alias: null,
8
+ arguments: null,
9
+ isUpdatable: false,
10
+ },
11
+ {
12
+ kind: "Link",
13
+ alias: "link",
14
+ },
15
+ ];
16
+
17
+ const artifact: EagerReaderArtifact<
18
+ { data: any, parameters: Record<PropertyKey, never> },
19
+ Link<"Economist"> | null
20
+ > = {
21
+ kind: "EagerReaderArtifact",
22
+ fieldName: "Node.asEconomist",
23
+ resolver: ({ data }) => data.__typename === "Economist" ? data.link : null,
24
+ readerAst,
25
+ hasUpdatable: false,
26
+ };
27
+
28
+ export default artifact;
@@ -0,0 +1,3 @@
1
+ import type { Link } from '@isograph/react';
2
+ export type Node__link__output_type =
3
+ | Link<"Economist">;