@isograph/react 0.0.0-main-6d83b6a2 → 0.0.0-main-f524690b
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/cache.js +8 -3
- package/package.json +3 -3
- package/src/cache.ts +48 -21
- package/src/componentCache.ts +5 -1
- package/src/index.tsx +97 -26
package/dist/cache.js
CHANGED
@@ -138,7 +138,8 @@ function normalizeScalarField(astNode, networkResponseParentRecord, targetStoreR
|
|
138
138
|
const networkResponseKey = getNetworkResponseKey(astNode);
|
139
139
|
const networkResponseData = networkResponseParentRecord[networkResponseKey];
|
140
140
|
const parentRecordKey = getParentRecordKey(astNode, variables);
|
141
|
-
if (networkResponseData == null ||
|
141
|
+
if (networkResponseData == null ||
|
142
|
+
isScalarOrEmptyArray(networkResponseData)) {
|
142
143
|
targetStoreRecord[parentRecordKey] = networkResponseData;
|
143
144
|
}
|
144
145
|
else {
|
@@ -190,7 +191,9 @@ function isScalarOrEmptyArray(data) {
|
|
190
191
|
// This is maybe fixed in a new version of Typescript??
|
191
192
|
return data.every((x) => isScalarOrEmptyArray(x));
|
192
193
|
}
|
193
|
-
const isScalarValue = typeof data === 'string' ||
|
194
|
+
const isScalarValue = typeof data === 'string' ||
|
195
|
+
typeof data === 'number' ||
|
196
|
+
typeof data === 'boolean';
|
194
197
|
return isScalarValue;
|
195
198
|
}
|
196
199
|
function isScalarButNotEmptyArray(data) {
|
@@ -202,7 +205,9 @@ function isScalarButNotEmptyArray(data) {
|
|
202
205
|
// This is maybe fixed in a new version of Typescript??
|
203
206
|
return data.every((x) => isScalarOrEmptyArray(x));
|
204
207
|
}
|
205
|
-
const isScalarValue = typeof data === 'string' ||
|
208
|
+
const isScalarValue = typeof data === 'string' ||
|
209
|
+
typeof data === 'number' ||
|
210
|
+
typeof data === 'boolean';
|
206
211
|
return isScalarValue;
|
207
212
|
}
|
208
213
|
function getParentRecordKey(astNode, variables) {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@isograph/react",
|
3
|
-
"version": "0.0.0-main-
|
3
|
+
"version": "0.0.0-main-f524690b",
|
4
4
|
"description": "Use Isograph with React",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"types": "dist/index.d.ts",
|
@@ -15,8 +15,8 @@
|
|
15
15
|
"prepack": "yarn run test && yarn run compile"
|
16
16
|
},
|
17
17
|
"dependencies": {
|
18
|
-
"@isograph/disposable-types": "0.0.0-main-
|
19
|
-
"@isograph/react-disposable-state": "0.0.0-main-
|
18
|
+
"@isograph/disposable-types": "0.0.0-main-f524690b",
|
19
|
+
"@isograph/react-disposable-state": "0.0.0-main-f524690b",
|
20
20
|
"react": "^18.2.0"
|
21
21
|
},
|
22
22
|
"devDependencies": {
|
package/src/cache.ts
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
Factory,
|
3
|
+
ItemCleanupPair,
|
4
|
+
ParentCache,
|
5
|
+
} from '@isograph/react-disposable-state';
|
2
6
|
import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
|
3
7
|
import {
|
4
8
|
Argument,
|
@@ -20,7 +24,10 @@ declare global {
|
|
20
24
|
|
21
25
|
const cache: { [index: string]: ParentCache<any> } = {};
|
22
26
|
|
23
|
-
function getOrCreateCache<T>(
|
27
|
+
function getOrCreateCache<T>(
|
28
|
+
index: string,
|
29
|
+
factory: Factory<T>,
|
30
|
+
): ParentCache<T> {
|
24
31
|
if (typeof window !== 'undefined' && window.__LOG) {
|
25
32
|
console.log('getting cache for', {
|
26
33
|
index,
|
@@ -64,7 +71,8 @@ export function getOrCreateCacheForArtifact<T>(
|
|
64
71
|
variables: object,
|
65
72
|
): ParentCache<PromiseWrapper<T>> {
|
66
73
|
const cacheKey = artifact.queryText + JSON.stringify(stableCopy(variables));
|
67
|
-
const factory: Factory<PromiseWrapper<T>> = () =>
|
74
|
+
const factory: Factory<PromiseWrapper<T>> = () =>
|
75
|
+
makeNetworkRequest<T>(artifact, variables);
|
68
76
|
return getOrCreateCache<PromiseWrapper<T>>(cacheKey, factory);
|
69
77
|
}
|
70
78
|
|
@@ -86,18 +94,20 @@ export function makeNetworkRequest<T>(
|
|
86
94
|
throw new Error('Network must be set before makeNetworkRequest is called');
|
87
95
|
}
|
88
96
|
|
89
|
-
const promise = network(artifact.queryText, variables).then(
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
97
|
+
const promise = network(artifact.queryText, variables).then(
|
98
|
+
(networkResponse) => {
|
99
|
+
if (typeof window !== 'undefined' && window.__LOG) {
|
100
|
+
console.log('network response', artifact);
|
101
|
+
}
|
102
|
+
normalizeData(
|
103
|
+
artifact.normalizationAst,
|
104
|
+
networkResponse.data,
|
105
|
+
variables,
|
106
|
+
artifact.nestedRefetchQueries,
|
107
|
+
);
|
108
|
+
return networkResponse.data;
|
109
|
+
},
|
110
|
+
);
|
101
111
|
|
102
112
|
const wrapper = wrapPromise(promise);
|
103
113
|
|
@@ -173,7 +183,12 @@ function normalizeData(
|
|
173
183
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[],
|
174
184
|
) {
|
175
185
|
if (typeof window !== 'undefined' && window.__LOG) {
|
176
|
-
console.log(
|
186
|
+
console.log(
|
187
|
+
'about to normalize',
|
188
|
+
normalizationAst,
|
189
|
+
networkResponse,
|
190
|
+
variables,
|
191
|
+
);
|
177
192
|
}
|
178
193
|
normalizeDataIntoRecord(
|
179
194
|
normalizationAst,
|
@@ -256,7 +271,10 @@ function normalizeScalarField(
|
|
256
271
|
const networkResponseData = networkResponseParentRecord[networkResponseKey];
|
257
272
|
const parentRecordKey = getParentRecordKey(astNode, variables);
|
258
273
|
|
259
|
-
if (
|
274
|
+
if (
|
275
|
+
networkResponseData == null ||
|
276
|
+
isScalarOrEmptyArray(networkResponseData)
|
277
|
+
) {
|
260
278
|
targetStoreRecord[parentRecordKey] = networkResponseData;
|
261
279
|
} else {
|
262
280
|
throw new Error('Unexpected object array when normalizing scalar');
|
@@ -284,7 +302,9 @@ function normalizeLinkedField(
|
|
284
302
|
}
|
285
303
|
|
286
304
|
if (isScalarButNotEmptyArray(networkResponseData)) {
|
287
|
-
throw new Error(
|
305
|
+
throw new Error(
|
306
|
+
'Unexpected scalar network response when normalizing a linked field',
|
307
|
+
);
|
288
308
|
}
|
289
309
|
|
290
310
|
if (Array.isArray(networkResponseData)) {
|
@@ -358,7 +378,9 @@ function isScalarOrEmptyArray(
|
|
358
378
|
return (data as any).every((x: any) => isScalarOrEmptyArray(x));
|
359
379
|
}
|
360
380
|
const isScalarValue =
|
361
|
-
typeof data === 'string' ||
|
381
|
+
typeof data === 'string' ||
|
382
|
+
typeof data === 'number' ||
|
383
|
+
typeof data === 'boolean';
|
362
384
|
return isScalarValue;
|
363
385
|
}
|
364
386
|
|
@@ -374,7 +396,9 @@ function isScalarButNotEmptyArray(
|
|
374
396
|
return (data as any).every((x: any) => isScalarOrEmptyArray(x));
|
375
397
|
}
|
376
398
|
const isScalarValue =
|
377
|
-
typeof data === 'string' ||
|
399
|
+
typeof data === 'string' ||
|
400
|
+
typeof data === 'number' ||
|
401
|
+
typeof data === 'boolean';
|
378
402
|
return isScalarValue;
|
379
403
|
}
|
380
404
|
|
@@ -418,7 +442,10 @@ function getStoreKeyChunkForArgumentValue(
|
|
418
442
|
}
|
419
443
|
}
|
420
444
|
|
421
|
-
function getStoreKeyChunkForArgument(
|
445
|
+
function getStoreKeyChunkForArgument(
|
446
|
+
argument: Argument,
|
447
|
+
variables: { [index: string]: string },
|
448
|
+
) {
|
422
449
|
const chunk = getStoreKeyChunkForArgumentValue(argument[1], variables);
|
423
450
|
return `${FIRST_SPLIT_KEY}${argument[0]}${SECOND_SPLIT_KEY}${chunk}`;
|
424
451
|
}
|
package/src/componentCache.ts
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
ReaderArtifact,
|
3
|
+
RefetchQueryArtifactWrapper,
|
4
|
+
readButDoNotEvaluate,
|
5
|
+
} from './index';
|
2
6
|
import { DataId, stableCopy } from './cache';
|
3
7
|
|
4
8
|
type ComponentName = string;
|
package/src/index.tsx
CHANGED
@@ -26,15 +26,27 @@ export {
|
|
26
26
|
export { iso } from './iso';
|
27
27
|
|
28
28
|
// This type should be treated as an opaque type.
|
29
|
-
export type IsographEntrypoint<
|
29
|
+
export type IsographEntrypoint<
|
30
|
+
TReadFromStore extends Object,
|
31
|
+
TResolverProps,
|
32
|
+
TResolverResult,
|
33
|
+
> = {
|
30
34
|
kind: 'Entrypoint';
|
31
35
|
queryText: string;
|
32
36
|
normalizationAst: NormalizationAst;
|
33
|
-
readerArtifact: ReaderArtifact<
|
37
|
+
readerArtifact: ReaderArtifact<
|
38
|
+
TReadFromStore,
|
39
|
+
TResolverProps,
|
40
|
+
TResolverResult
|
41
|
+
>;
|
34
42
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[];
|
35
43
|
};
|
36
44
|
|
37
|
-
export type ReaderArtifact<
|
45
|
+
export type ReaderArtifact<
|
46
|
+
TReadFromStore extends Object,
|
47
|
+
TResolverProps,
|
48
|
+
TResolverResult,
|
49
|
+
> = {
|
38
50
|
kind: 'ReaderArtifact';
|
39
51
|
readerAst: ReaderAst<TReadFromStore>;
|
40
52
|
resolver: (data: TResolverProps) => TResolverResult;
|
@@ -96,7 +108,9 @@ export type ReaderMutationField = {
|
|
96
108
|
allowedVariables: string[];
|
97
109
|
};
|
98
110
|
|
99
|
-
export type NormalizationAstNode =
|
111
|
+
export type NormalizationAstNode =
|
112
|
+
| NormalizationScalarField
|
113
|
+
| NormalizationLinkedField;
|
100
114
|
// @ts-ignore
|
101
115
|
export type NormalizationAst = NormalizationAstNode[];
|
102
116
|
|
@@ -139,24 +153,45 @@ export type ArgumentValue =
|
|
139
153
|
value: any;
|
140
154
|
};
|
141
155
|
|
142
|
-
export type FragmentReference<
|
156
|
+
export type FragmentReference<
|
157
|
+
TReadFromStore extends Object,
|
158
|
+
TResolverProps,
|
159
|
+
TResolverResult,
|
160
|
+
> = {
|
143
161
|
kind: 'FragmentReference';
|
144
|
-
readerArtifact: ReaderArtifact<
|
162
|
+
readerArtifact: ReaderArtifact<
|
163
|
+
TReadFromStore,
|
164
|
+
TResolverProps,
|
165
|
+
TResolverResult
|
166
|
+
>;
|
145
167
|
root: DataId;
|
146
168
|
variables: { [index: string]: string } | null;
|
147
169
|
// TODO: We should instead have ReaderAst<TResolverProps>
|
148
170
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[];
|
149
171
|
};
|
150
172
|
|
151
|
-
function assertIsEntrypoint<
|
152
|
-
|
153
|
-
|
173
|
+
function assertIsEntrypoint<
|
174
|
+
TReadFromStore extends Object,
|
175
|
+
TResolverProps,
|
176
|
+
TResolverResult,
|
177
|
+
>(
|
178
|
+
value:
|
179
|
+
| IsographEntrypoint<TReadFromStore, TResolverProps, TResolverResult>
|
180
|
+
| typeof iso,
|
181
|
+
): asserts value is IsographEntrypoint<
|
182
|
+
TReadFromStore,
|
183
|
+
TResolverProps,
|
184
|
+
TResolverResult
|
185
|
+
> {
|
154
186
|
if (typeof value === 'function') throw new Error('Not a string');
|
155
187
|
}
|
156
188
|
|
157
|
-
type ExtractTReadFromStore<Type> =
|
158
|
-
|
159
|
-
type
|
189
|
+
type ExtractTReadFromStore<Type> =
|
190
|
+
Type extends IsographEntrypoint<infer X, any, any> ? X : never;
|
191
|
+
type ExtractResolverProps<Type> =
|
192
|
+
Type extends IsographEntrypoint<any, infer X, any> ? X : never;
|
193
|
+
type ExtractResolverResult<Type> =
|
194
|
+
Type extends IsographEntrypoint<any, any, infer X> ? X : never;
|
160
195
|
// Note: we cannot write TEntrypoint extends IsographEntrypoint<any, any, any>, or else
|
161
196
|
// if we do not explicitly pass a type, the read out type will be any.
|
162
197
|
// We cannot write TEntrypoint extends IsographEntrypoint<never, never, never>, or else
|
@@ -185,7 +220,8 @@ export function useLazyReference<TEntrypoint>(
|
|
185
220
|
|
186
221
|
// TODO add comment explaining why we never use this value
|
187
222
|
// @ts-ignore
|
188
|
-
const data =
|
223
|
+
const data =
|
224
|
+
useLazyDisposableState<PromiseWrapper<TResolverResult>>(cache).state;
|
189
225
|
|
190
226
|
return {
|
191
227
|
queryReference: {
|
@@ -198,8 +234,16 @@ export function useLazyReference<TEntrypoint>(
|
|
198
234
|
};
|
199
235
|
}
|
200
236
|
|
201
|
-
export function read<
|
202
|
-
|
237
|
+
export function read<
|
238
|
+
TReadFromStore extends Object,
|
239
|
+
TResolverProps,
|
240
|
+
TResolverResult,
|
241
|
+
>(
|
242
|
+
fragmentReference: FragmentReference<
|
243
|
+
TReadFromStore,
|
244
|
+
TResolverProps,
|
245
|
+
TResolverResult
|
246
|
+
>,
|
203
247
|
): TResolverResult {
|
204
248
|
const variant = fragmentReference.readerArtifact.variant;
|
205
249
|
if (variant.kind === 'Eager') {
|
@@ -313,7 +357,12 @@ function readData<TReadFromStore>(
|
|
313
357
|
results.push(null);
|
314
358
|
continue;
|
315
359
|
}
|
316
|
-
const result = readData(
|
360
|
+
const result = readData(
|
361
|
+
field.selections,
|
362
|
+
link.__link,
|
363
|
+
variables,
|
364
|
+
nestedRefetchQueries,
|
365
|
+
);
|
317
366
|
if (result.kind === 'MissingData') {
|
318
367
|
return {
|
319
368
|
kind: 'MissingData',
|
@@ -361,7 +410,12 @@ function readData<TReadFromStore>(
|
|
361
410
|
break;
|
362
411
|
}
|
363
412
|
const targetId = link.__link;
|
364
|
-
const data = readData(
|
413
|
+
const data = readData(
|
414
|
+
field.selections,
|
415
|
+
targetId,
|
416
|
+
variables,
|
417
|
+
nestedRefetchQueries,
|
418
|
+
);
|
365
419
|
if (data.kind === 'MissingData') {
|
366
420
|
return {
|
367
421
|
kind: 'MissingData',
|
@@ -398,12 +452,15 @@ function readData<TReadFromStore>(
|
|
398
452
|
const refetchQueryArtifact = refetchQuery.artifact;
|
399
453
|
const allowedVariables = refetchQuery.allowedVariables;
|
400
454
|
|
401
|
-
target[field.alias] = field.readerArtifact.resolver(
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
455
|
+
target[field.alias] = field.readerArtifact.resolver(
|
456
|
+
refetchQueryArtifact,
|
457
|
+
{
|
458
|
+
...data.data,
|
459
|
+
// TODO continue from here
|
460
|
+
// variables need to be filtered for what we need just for the refetch query
|
461
|
+
...filterVariables(variables, allowedVariables),
|
462
|
+
},
|
463
|
+
);
|
407
464
|
}
|
408
465
|
break;
|
409
466
|
}
|
@@ -490,9 +547,21 @@ function missingFieldHandler(
|
|
490
547
|
variables: { [index: string]: any } | null,
|
491
548
|
): Link | undefined {
|
492
549
|
if (customMissingFieldHandler != null) {
|
493
|
-
return customMissingFieldHandler(
|
550
|
+
return customMissingFieldHandler(
|
551
|
+
storeRecord,
|
552
|
+
root,
|
553
|
+
fieldName,
|
554
|
+
arguments_,
|
555
|
+
variables,
|
556
|
+
);
|
494
557
|
} else {
|
495
|
-
return defaultMissingFieldHandler(
|
558
|
+
return defaultMissingFieldHandler(
|
559
|
+
storeRecord,
|
560
|
+
root,
|
561
|
+
fieldName,
|
562
|
+
arguments_,
|
563
|
+
variables,
|
564
|
+
);
|
496
565
|
}
|
497
566
|
}
|
498
567
|
|
@@ -514,7 +583,9 @@ export function defaultMissingFieldHandler(
|
|
514
583
|
}
|
515
584
|
}
|
516
585
|
|
517
|
-
export function setMissingFieldHandler(
|
586
|
+
export function setMissingFieldHandler(
|
587
|
+
handler: typeof defaultMissingFieldHandler,
|
588
|
+
) {
|
518
589
|
customMissingFieldHandler = handler;
|
519
590
|
}
|
520
591
|
|