@isograph/react 0.0.0-main-90264a00 → 0.0.0-main-2fbc0faf
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.
- package/dist/EntrypointReader.d.ts +2 -1
- package/dist/EntrypointReader.js +2 -2
- package/dist/FragmentReference.d.ts +13 -0
- package/dist/FragmentReference.js +2 -0
- package/dist/IsographEnvironment.d.ts +1 -0
- package/dist/IsographEnvironment.js +14 -1
- package/dist/cache.d.ts +2 -1
- package/dist/componentCache.d.ts +2 -1
- package/dist/componentCache.js +2 -2
- package/dist/entrypoint.d.ts +3 -1
- package/dist/garbageCollection.d.ts +1 -1
- package/dist/garbageCollection.js +2 -3
- package/dist/index.d.ts +5 -38
- package/dist/index.js +21 -294
- package/dist/read.d.ts +4 -0
- package/dist/read.js +233 -0
- package/dist/reader.d.ts +1 -1
- package/dist/useImperativeReference.d.ts +2 -1
- package/dist/useImperativeReference.js +6 -4
- package/dist/useLazyReference.d.ts +7 -0
- package/dist/useLazyReference.js +30 -0
- package/dist/useResult.d.ts +2 -0
- package/dist/useResult.js +18 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.js +2 -0
- package/package.json +3 -3
- package/src/EntrypointReader.tsx +3 -6
- package/src/FragmentReference.ts +18 -0
- package/src/IsographEnvironment.tsx +13 -0
- package/src/cache.tsx +9 -11
- package/src/componentCache.ts +3 -5
- package/src/entrypoint.ts +6 -1
- package/src/garbageCollection.ts +2 -1
- package/src/index.tsx +11 -467
- package/src/read.ts +338 -0
- package/src/reader.ts +1 -1
- package/src/useImperativeReference.ts +6 -6
- package/src/useLazyReference.ts +58 -0
- package/src/useResult.ts +20 -0
- package/src/util.ts +15 -0
package/src/index.tsx
CHANGED
@@ -1,29 +1,3 @@
|
|
1
|
-
import {
|
2
|
-
getOrCreateCacheForArtifact,
|
3
|
-
onNextChange,
|
4
|
-
getParentRecordKey,
|
5
|
-
subscribe,
|
6
|
-
} from './cache';
|
7
|
-
import { useLazyDisposableState } from '@isograph/react-disposable-state';
|
8
|
-
import { type PromiseWrapper } from './PromiseWrapper';
|
9
|
-
import { getOrCreateCachedComponent } from './componentCache';
|
10
|
-
import {
|
11
|
-
DataId,
|
12
|
-
DataTypeValue,
|
13
|
-
IsographEnvironment,
|
14
|
-
Link,
|
15
|
-
ROOT_ID,
|
16
|
-
defaultMissingFieldHandler,
|
17
|
-
} from './IsographEnvironment';
|
18
|
-
import { useEffect, useState } from 'react';
|
19
|
-
import { useIsographEnvironment } from './IsographEnvironmentProvider';
|
20
|
-
import { ReaderArtifact, ReaderAst } from './reader';
|
21
|
-
import {
|
22
|
-
IsographEntrypoint,
|
23
|
-
RefetchQueryArtifactWrapper,
|
24
|
-
assertIsEntrypoint,
|
25
|
-
} from './entrypoint';
|
26
|
-
|
27
1
|
export {
|
28
2
|
retainQuery,
|
29
3
|
unretainQuery,
|
@@ -73,444 +47,14 @@ export {
|
|
73
47
|
RefetchQueryArtifact,
|
74
48
|
RefetchQueryArtifactWrapper,
|
75
49
|
} from './entrypoint';
|
76
|
-
|
77
|
-
export
|
78
|
-
|
79
|
-
|
80
|
-
export
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
}
|
88
|
-
| {
|
89
|
-
kind: 'Literal';
|
90
|
-
value: any;
|
91
|
-
};
|
92
|
-
|
93
|
-
// TODO type this better
|
94
|
-
type Variable = any;
|
95
|
-
|
96
|
-
export type FragmentReference<
|
97
|
-
TReadFromStore extends Object,
|
98
|
-
TResolverResult,
|
99
|
-
> = {
|
100
|
-
kind: 'FragmentReference';
|
101
|
-
readerArtifact: ReaderArtifact<TReadFromStore, TResolverResult>;
|
102
|
-
root: DataId;
|
103
|
-
variables: { [index: string]: Variable } | null;
|
104
|
-
// TODO: We should instead have ReaderAst<TResolverProps>
|
105
|
-
nestedRefetchQueries: RefetchQueryArtifactWrapper[];
|
106
|
-
};
|
107
|
-
|
108
|
-
export type ExtractReadFromStore<Type> =
|
109
|
-
Type extends IsographEntrypoint<infer X, any> ? X : never;
|
110
|
-
export type ExtractResolverResult<Type> =
|
111
|
-
Type extends IsographEntrypoint<any, infer X> ? X : never;
|
112
|
-
// Note: we cannot write TEntrypoint extends IsographEntrypoint<any, any, any>, or else
|
113
|
-
// if we do not explicitly pass a type, the read out type will be any.
|
114
|
-
// We cannot write TEntrypoint extends IsographEntrypoint<never, never, never>, or else
|
115
|
-
// any actual Entrypoint we pass will not be valid.
|
116
|
-
export function useLazyReference<TEntrypoint>(
|
117
|
-
entrypoint:
|
118
|
-
| TEntrypoint
|
119
|
-
// Temporarily, we need to allow useLazyReference to take the result of calling
|
120
|
-
// iso(`...`). At runtime, we confirm that the passed-in `iso` literal is actually
|
121
|
-
// an entrypoint.
|
122
|
-
| ((_: any) => any),
|
123
|
-
variables: { [key: string]: Variable },
|
124
|
-
): {
|
125
|
-
queryReference: FragmentReference<
|
126
|
-
ExtractReadFromStore<TEntrypoint>,
|
127
|
-
ExtractResolverResult<TEntrypoint>
|
128
|
-
>;
|
129
|
-
} {
|
130
|
-
const environment = useIsographEnvironment();
|
131
|
-
assertIsEntrypoint<
|
132
|
-
ExtractReadFromStore<TEntrypoint>,
|
133
|
-
ExtractResolverResult<TEntrypoint>
|
134
|
-
>(entrypoint);
|
135
|
-
const cache = getOrCreateCacheForArtifact<ExtractResolverResult<TEntrypoint>>(
|
136
|
-
environment,
|
137
|
-
entrypoint,
|
138
|
-
variables,
|
139
|
-
);
|
140
|
-
|
141
|
-
// TODO add comment explaining why we never use this value
|
142
|
-
// @ts-ignore
|
143
|
-
const data =
|
144
|
-
useLazyDisposableState<PromiseWrapper<ExtractResolverResult<TEntrypoint>>>(
|
145
|
-
cache,
|
146
|
-
).state;
|
147
|
-
|
148
|
-
return {
|
149
|
-
queryReference: {
|
150
|
-
kind: 'FragmentReference',
|
151
|
-
readerArtifact: entrypoint.readerArtifact,
|
152
|
-
root: ROOT_ID,
|
153
|
-
variables,
|
154
|
-
nestedRefetchQueries: entrypoint.nestedRefetchQueries,
|
155
|
-
},
|
156
|
-
};
|
157
|
-
}
|
158
|
-
|
159
|
-
export function useResult<TReadFromStore extends Object, TResolverResult>(
|
160
|
-
fragmentReference: FragmentReference<TReadFromStore, TResolverResult>,
|
161
|
-
): TResolverResult {
|
162
|
-
const environment = useIsographEnvironment();
|
163
|
-
|
164
|
-
const [, setState] = useState<object | void>();
|
165
|
-
useEffect(() => {
|
166
|
-
return subscribe(environment, () => {
|
167
|
-
return setState({});
|
168
|
-
});
|
169
|
-
}, []);
|
170
|
-
|
171
|
-
return read(environment, fragmentReference);
|
172
|
-
}
|
173
|
-
|
174
|
-
export function read<TReadFromStore extends Object, TResolverResult>(
|
175
|
-
environment: IsographEnvironment,
|
176
|
-
fragmentReference: FragmentReference<TReadFromStore, TResolverResult>,
|
177
|
-
): TResolverResult {
|
178
|
-
const variant = fragmentReference.readerArtifact.variant;
|
179
|
-
if (variant.kind === 'Eager') {
|
180
|
-
const data = readData(
|
181
|
-
environment,
|
182
|
-
fragmentReference.readerArtifact.readerAst,
|
183
|
-
fragmentReference.root,
|
184
|
-
fragmentReference.variables ?? {},
|
185
|
-
fragmentReference.nestedRefetchQueries,
|
186
|
-
);
|
187
|
-
if (data.kind === 'MissingData') {
|
188
|
-
throw onNextChange(environment);
|
189
|
-
} else {
|
190
|
-
// @ts-expect-error This not properly typed yet
|
191
|
-
return fragmentReference.readerArtifact.resolver(data.data);
|
192
|
-
}
|
193
|
-
} else if (variant.kind === 'Component') {
|
194
|
-
// @ts-ignore
|
195
|
-
return getOrCreateCachedComponent(
|
196
|
-
environment,
|
197
|
-
fragmentReference.root,
|
198
|
-
variant.componentName,
|
199
|
-
fragmentReference.readerArtifact,
|
200
|
-
fragmentReference.variables ?? {},
|
201
|
-
fragmentReference.nestedRefetchQueries,
|
202
|
-
);
|
203
|
-
}
|
204
|
-
// Why can't Typescript realize that this is unreachable??
|
205
|
-
throw new Error('This is unreachable');
|
206
|
-
}
|
207
|
-
|
208
|
-
export function readButDoNotEvaluate<TReadFromStore extends Object>(
|
209
|
-
environment: IsographEnvironment,
|
210
|
-
reference: FragmentReference<TReadFromStore, unknown>,
|
211
|
-
): TReadFromStore {
|
212
|
-
const response = readData(
|
213
|
-
environment,
|
214
|
-
reference.readerArtifact.readerAst,
|
215
|
-
reference.root,
|
216
|
-
reference.variables ?? {},
|
217
|
-
reference.nestedRefetchQueries,
|
218
|
-
);
|
219
|
-
if (typeof window !== 'undefined' && window.__LOG) {
|
220
|
-
console.log('done reading', { response });
|
221
|
-
}
|
222
|
-
if (response.kind === 'MissingData') {
|
223
|
-
throw onNextChange(environment);
|
224
|
-
} else {
|
225
|
-
return response.data;
|
226
|
-
}
|
227
|
-
}
|
228
|
-
|
229
|
-
type ReadDataResult<TReadFromStore> =
|
230
|
-
| {
|
231
|
-
kind: 'Success';
|
232
|
-
data: TReadFromStore;
|
233
|
-
}
|
234
|
-
| {
|
235
|
-
kind: 'MissingData';
|
236
|
-
reason: string;
|
237
|
-
nestedReason?: ReadDataResult<unknown>;
|
238
|
-
};
|
239
|
-
|
240
|
-
function readData<TReadFromStore>(
|
241
|
-
environment: IsographEnvironment,
|
242
|
-
ast: ReaderAst<TReadFromStore>,
|
243
|
-
root: DataId,
|
244
|
-
variables: { [index: string]: string },
|
245
|
-
nestedRefetchQueries: RefetchQueryArtifactWrapper[],
|
246
|
-
): ReadDataResult<TReadFromStore> {
|
247
|
-
let storeRecord = environment.store[root];
|
248
|
-
if (storeRecord === undefined) {
|
249
|
-
return { kind: 'MissingData', reason: 'No record for root ' + root };
|
250
|
-
}
|
251
|
-
|
252
|
-
if (storeRecord === null) {
|
253
|
-
return { kind: 'Success', data: null as any };
|
254
|
-
}
|
255
|
-
|
256
|
-
let target: { [index: string]: any } = {};
|
257
|
-
|
258
|
-
for (const field of ast) {
|
259
|
-
switch (field.kind) {
|
260
|
-
case 'Scalar': {
|
261
|
-
const storeRecordName = getParentRecordKey(field, variables);
|
262
|
-
const value = storeRecord[storeRecordName];
|
263
|
-
// TODO consider making scalars into discriminated unions. This probably has
|
264
|
-
// to happen for when we handle errors.
|
265
|
-
if (value === undefined) {
|
266
|
-
return {
|
267
|
-
kind: 'MissingData',
|
268
|
-
reason: 'No value for ' + storeRecordName + ' on root ' + root,
|
269
|
-
};
|
270
|
-
}
|
271
|
-
target[field.alias ?? field.fieldName] = value;
|
272
|
-
break;
|
273
|
-
}
|
274
|
-
case 'Linked': {
|
275
|
-
const storeRecordName = getParentRecordKey(field, variables);
|
276
|
-
const value = storeRecord[storeRecordName];
|
277
|
-
if (Array.isArray(value)) {
|
278
|
-
const results = [];
|
279
|
-
for (const item of value) {
|
280
|
-
const link = assertLink(item);
|
281
|
-
if (link === undefined) {
|
282
|
-
return {
|
283
|
-
kind: 'MissingData',
|
284
|
-
reason:
|
285
|
-
'No link for ' +
|
286
|
-
storeRecordName +
|
287
|
-
' on root ' +
|
288
|
-
root +
|
289
|
-
'. Link is ' +
|
290
|
-
JSON.stringify(item),
|
291
|
-
};
|
292
|
-
} else if (link === null) {
|
293
|
-
results.push(null);
|
294
|
-
continue;
|
295
|
-
}
|
296
|
-
const result = readData(
|
297
|
-
environment,
|
298
|
-
field.selections,
|
299
|
-
link.__link,
|
300
|
-
variables,
|
301
|
-
nestedRefetchQueries,
|
302
|
-
);
|
303
|
-
if (result.kind === 'MissingData') {
|
304
|
-
return {
|
305
|
-
kind: 'MissingData',
|
306
|
-
reason:
|
307
|
-
'Missing data for ' +
|
308
|
-
storeRecordName +
|
309
|
-
' on root ' +
|
310
|
-
root +
|
311
|
-
'. Link is ' +
|
312
|
-
JSON.stringify(item),
|
313
|
-
nestedReason: result,
|
314
|
-
};
|
315
|
-
}
|
316
|
-
results.push(result.data);
|
317
|
-
}
|
318
|
-
target[field.alias ?? field.fieldName] = results;
|
319
|
-
break;
|
320
|
-
}
|
321
|
-
let link = assertLink(value);
|
322
|
-
if (link === undefined) {
|
323
|
-
// TODO make this configurable, and also generated and derived from the schema
|
324
|
-
const missingFieldHandler =
|
325
|
-
environment.missingFieldHandler ?? defaultMissingFieldHandler;
|
326
|
-
const altLink = missingFieldHandler(
|
327
|
-
storeRecord,
|
328
|
-
root,
|
329
|
-
field.fieldName,
|
330
|
-
field.arguments,
|
331
|
-
variables,
|
332
|
-
);
|
333
|
-
if (altLink === undefined) {
|
334
|
-
return {
|
335
|
-
kind: 'MissingData',
|
336
|
-
reason:
|
337
|
-
'No link for ' +
|
338
|
-
storeRecordName +
|
339
|
-
' on root ' +
|
340
|
-
root +
|
341
|
-
'. Link is ' +
|
342
|
-
JSON.stringify(value),
|
343
|
-
};
|
344
|
-
} else {
|
345
|
-
link = altLink;
|
346
|
-
}
|
347
|
-
} else if (link === null) {
|
348
|
-
target[field.alias ?? field.fieldName] = null;
|
349
|
-
break;
|
350
|
-
}
|
351
|
-
const targetId = link.__link;
|
352
|
-
const data = readData(
|
353
|
-
environment,
|
354
|
-
field.selections,
|
355
|
-
targetId,
|
356
|
-
variables,
|
357
|
-
nestedRefetchQueries,
|
358
|
-
);
|
359
|
-
if (data.kind === 'MissingData') {
|
360
|
-
return {
|
361
|
-
kind: 'MissingData',
|
362
|
-
reason: 'Missing data for ' + storeRecordName + ' on root ' + root,
|
363
|
-
nestedReason: data,
|
364
|
-
};
|
365
|
-
}
|
366
|
-
target[field.alias ?? field.fieldName] = data.data;
|
367
|
-
break;
|
368
|
-
}
|
369
|
-
case 'RefetchField': {
|
370
|
-
const data = readData(
|
371
|
-
environment,
|
372
|
-
field.readerArtifact.readerAst,
|
373
|
-
root,
|
374
|
-
variables,
|
375
|
-
// Refetch fields just read the id, and don't need refetch query artifacts
|
376
|
-
[],
|
377
|
-
);
|
378
|
-
if (typeof window !== 'undefined' && window.__LOG) {
|
379
|
-
console.log('refetch field data', data, field);
|
380
|
-
}
|
381
|
-
if (data.kind === 'MissingData') {
|
382
|
-
return {
|
383
|
-
kind: 'MissingData',
|
384
|
-
reason: 'Missing data for ' + field.alias + ' on root ' + root,
|
385
|
-
nestedReason: data,
|
386
|
-
};
|
387
|
-
} else {
|
388
|
-
const refetchQueryIndex = field.refetchQuery;
|
389
|
-
if (refetchQueryIndex == null) {
|
390
|
-
throw new Error('refetchQuery is null in RefetchField');
|
391
|
-
}
|
392
|
-
const refetchQuery = nestedRefetchQueries[refetchQueryIndex];
|
393
|
-
const refetchQueryArtifact = refetchQuery.artifact;
|
394
|
-
const allowedVariables = refetchQuery.allowedVariables;
|
395
|
-
|
396
|
-
target[field.alias] = field.readerArtifact.resolver(
|
397
|
-
environment,
|
398
|
-
// resolvers for refetch fields take 3 args, and this is not reflected in types
|
399
|
-
refetchQueryArtifact,
|
400
|
-
// @ts-expect-error
|
401
|
-
{
|
402
|
-
...data.data,
|
403
|
-
// TODO continue from here
|
404
|
-
// variables need to be filtered for what we need just for the refetch query
|
405
|
-
...filterVariables(variables, allowedVariables),
|
406
|
-
},
|
407
|
-
);
|
408
|
-
}
|
409
|
-
break;
|
410
|
-
}
|
411
|
-
case 'MutationField': {
|
412
|
-
const data = readData(
|
413
|
-
environment,
|
414
|
-
field.readerArtifact.readerAst,
|
415
|
-
root,
|
416
|
-
variables,
|
417
|
-
// Refetch fields just read the id, and don't need refetch query artifacts
|
418
|
-
[],
|
419
|
-
);
|
420
|
-
if (typeof window !== 'undefined' && window.__LOG) {
|
421
|
-
console.log('refetch field data', data, field);
|
422
|
-
}
|
423
|
-
if (data.kind === 'MissingData') {
|
424
|
-
return {
|
425
|
-
kind: 'MissingData',
|
426
|
-
reason: 'Missing data for ' + field.alias + ' on root ' + root,
|
427
|
-
nestedReason: data,
|
428
|
-
};
|
429
|
-
} else {
|
430
|
-
const refetchQueryIndex = field.refetchQuery;
|
431
|
-
if (refetchQueryIndex == null) {
|
432
|
-
throw new Error('refetchQuery is null in MutationField');
|
433
|
-
}
|
434
|
-
const refetchQuery = nestedRefetchQueries[refetchQueryIndex];
|
435
|
-
const refetchQueryArtifact = refetchQuery.artifact;
|
436
|
-
const allowedVariables = refetchQuery.allowedVariables;
|
437
|
-
|
438
|
-
target[field.alias] = field.readerArtifact.resolver(
|
439
|
-
environment,
|
440
|
-
refetchQueryArtifact,
|
441
|
-
// @ts-expect-error
|
442
|
-
data.data,
|
443
|
-
filterVariables(variables, allowedVariables),
|
444
|
-
);
|
445
|
-
}
|
446
|
-
break;
|
447
|
-
}
|
448
|
-
case 'Resolver': {
|
449
|
-
const usedRefetchQueries = field.usedRefetchQueries;
|
450
|
-
const resolverRefetchQueries = usedRefetchQueries.map(
|
451
|
-
(index) => nestedRefetchQueries[index],
|
452
|
-
);
|
453
|
-
|
454
|
-
const variant = field.readerArtifact.variant;
|
455
|
-
if (variant.kind === 'Eager') {
|
456
|
-
const data = readData(
|
457
|
-
environment,
|
458
|
-
field.readerArtifact.readerAst,
|
459
|
-
root,
|
460
|
-
variables,
|
461
|
-
resolverRefetchQueries,
|
462
|
-
);
|
463
|
-
if (data.kind === 'MissingData') {
|
464
|
-
return {
|
465
|
-
kind: 'MissingData',
|
466
|
-
reason: 'Missing data for ' + field.alias + ' on root ' + root,
|
467
|
-
nestedReason: data,
|
468
|
-
};
|
469
|
-
} else {
|
470
|
-
// @ts-expect-error
|
471
|
-
target[field.alias] = field.readerArtifact.resolver(data.data);
|
472
|
-
}
|
473
|
-
} else if (variant.kind === 'Component') {
|
474
|
-
target[field.alias] = getOrCreateCachedComponent(
|
475
|
-
environment,
|
476
|
-
root,
|
477
|
-
variant.componentName,
|
478
|
-
field.readerArtifact,
|
479
|
-
variables,
|
480
|
-
resolverRefetchQueries,
|
481
|
-
);
|
482
|
-
}
|
483
|
-
break;
|
484
|
-
}
|
485
|
-
}
|
486
|
-
}
|
487
|
-
return { kind: 'Success', data: target as any };
|
488
|
-
}
|
489
|
-
|
490
|
-
export function assertLink(link: DataTypeValue): Link | null {
|
491
|
-
if (Array.isArray(link)) {
|
492
|
-
throw new Error('Unexpected array');
|
493
|
-
}
|
494
|
-
if (link == null) {
|
495
|
-
return null;
|
496
|
-
}
|
497
|
-
if (typeof link === 'object') {
|
498
|
-
return link;
|
499
|
-
}
|
500
|
-
throw new Error('Invalid link');
|
501
|
-
}
|
502
|
-
|
503
|
-
export type IsographComponentProps<TDataType, TOtherProps = Object> = {
|
504
|
-
data: TDataType;
|
505
|
-
} & TOtherProps;
|
506
|
-
|
507
|
-
function filterVariables(
|
508
|
-
variables: { [index: string]: string },
|
509
|
-
allowedVariables: string[],
|
510
|
-
): { [index: string]: string } {
|
511
|
-
const result: { [index: string]: string } = {};
|
512
|
-
for (const key of allowedVariables) {
|
513
|
-
result[key] = variables[key];
|
514
|
-
}
|
515
|
-
return result;
|
516
|
-
}
|
50
|
+
export { read, readButDoNotEvaluate } from './read';
|
51
|
+
export { useResult } from './useResult';
|
52
|
+
export { type FragmentReference } from './FragmentReference';
|
53
|
+
export { useLazyReference } from './useLazyReference';
|
54
|
+
export {
|
55
|
+
ExtractSecondParam,
|
56
|
+
Argument,
|
57
|
+
ArgumentName,
|
58
|
+
ArgumentValue,
|
59
|
+
Arguments,
|
60
|
+
} from './util';
|