@isograph/react 0.0.0-main-945e49cc → 0.0.0-main-1bfd646b
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/IsographEnvironment.d.ts +5 -9
- package/dist/IsographEnvironment.js +5 -40
- package/dist/IsographEnvironmentProvider.d.ts +10 -0
- package/dist/IsographEnvironmentProvider.js +42 -0
- package/dist/cache.js +39 -11
- package/dist/entrypoint.d.ts +32 -0
- package/dist/entrypoint.js +8 -0
- package/dist/garbageCollection.d.ts +11 -0
- package/dist/garbageCollection.js +84 -0
- package/dist/index.d.ts +7 -76
- package/dist/index.js +15 -11
- package/dist/reader.d.ts +47 -0
- package/dist/reader.js +3 -0
- package/package.json +3 -3
- package/src/IsographEnvironment.tsx +9 -32
- package/src/IsographEnvironmentProvider.tsx +33 -0
- package/src/cache.tsx +69 -10
- package/src/entrypoint.ts +59 -0
- package/src/garbageCollection.ts +131 -0
- package/src/index.tsx +39 -123
- package/src/reader.ts +65 -0
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { ReactNode, createContext, useContext } from 'react';
|
|
2
|
-
import * as React from 'react';
|
|
3
1
|
import { ParentCache } from '@isograph/isograph-react-disposable-state';
|
|
4
|
-
|
|
5
|
-
export const IsographEnvironmentContext =
|
|
6
|
-
createContext<IsographEnvironment | null>(null);
|
|
2
|
+
import { RetainedQuery } from './garbageCollection';
|
|
7
3
|
|
|
8
4
|
type ComponentName = string;
|
|
9
5
|
type StringifiedArgs = string;
|
|
@@ -23,6 +19,9 @@ export type IsographEnvironment = {
|
|
|
23
19
|
componentCache: ComponentCache;
|
|
24
20
|
subscriptions: Subscriptions;
|
|
25
21
|
suspenseCache: SuspenseCache;
|
|
22
|
+
retainedQueries: Set<RetainedQuery>;
|
|
23
|
+
gcBuffer: Array<RetainedQuery>;
|
|
24
|
+
gcBufferSize: number;
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
export type MissingFieldHandler = (
|
|
@@ -41,6 +40,7 @@ export type IsographNetworkFunction = (
|
|
|
41
40
|
export type Link = {
|
|
42
41
|
__link: DataId;
|
|
43
42
|
};
|
|
43
|
+
|
|
44
44
|
export type DataTypeValue =
|
|
45
45
|
// N.B. undefined is here to support optional id's, but
|
|
46
46
|
// undefined should not *actually* be present in the store.
|
|
@@ -71,33 +71,7 @@ export type IsographStore = {
|
|
|
71
71
|
__ROOT: StoreRecord;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
environment: IsographEnvironment;
|
|
76
|
-
children: ReactNode;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export function IsographEnvironmentProvider({
|
|
80
|
-
environment,
|
|
81
|
-
children,
|
|
82
|
-
}: IsographEnvironmentProviderProps) {
|
|
83
|
-
return (
|
|
84
|
-
<IsographEnvironmentContext.Provider value={environment}>
|
|
85
|
-
{children}
|
|
86
|
-
</IsographEnvironmentContext.Provider>
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function useIsographEnvironment(): IsographEnvironment {
|
|
91
|
-
const context = useContext(IsographEnvironmentContext);
|
|
92
|
-
if (context == null) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
'Unexpected null environment context. Make sure to render ' +
|
|
95
|
-
'this component within an IsographEnvironmentProvider component',
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
return context;
|
|
99
|
-
}
|
|
100
|
-
|
|
74
|
+
const DEFAULT_GC_BUFFER_SIZE = 10;
|
|
101
75
|
export function createIsographEnvironment(
|
|
102
76
|
store: IsographStore,
|
|
103
77
|
networkFunction: IsographNetworkFunction,
|
|
@@ -110,6 +84,9 @@ export function createIsographEnvironment(
|
|
|
110
84
|
componentCache: {},
|
|
111
85
|
subscriptions: new Set(),
|
|
112
86
|
suspenseCache: {},
|
|
87
|
+
retainedQueries: new Set(),
|
|
88
|
+
gcBuffer: [],
|
|
89
|
+
gcBufferSize: DEFAULT_GC_BUFFER_SIZE,
|
|
113
90
|
};
|
|
114
91
|
}
|
|
115
92
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode, createContext, useContext } from 'react';
|
|
3
|
+
import { type IsographEnvironment } from './IsographEnvironment';
|
|
4
|
+
|
|
5
|
+
export const IsographEnvironmentContext =
|
|
6
|
+
createContext<IsographEnvironment | null>(null);
|
|
7
|
+
|
|
8
|
+
export type IsographEnvironmentProviderProps = {
|
|
9
|
+
environment: IsographEnvironment;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function IsographEnvironmentProvider({
|
|
14
|
+
environment,
|
|
15
|
+
children,
|
|
16
|
+
}: IsographEnvironmentProviderProps) {
|
|
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
|
+
}
|
package/src/cache.tsx
CHANGED
|
@@ -22,6 +22,12 @@ import {
|
|
|
22
22
|
Link,
|
|
23
23
|
type IsographEnvironment,
|
|
24
24
|
} from './IsographEnvironment';
|
|
25
|
+
import {
|
|
26
|
+
RetainedQuery,
|
|
27
|
+
garbageCollectEnvironment,
|
|
28
|
+
retainQuery,
|
|
29
|
+
unretainQuery,
|
|
30
|
+
} from './garbageCollection';
|
|
25
31
|
|
|
26
32
|
declare global {
|
|
27
33
|
interface Window {
|
|
@@ -83,6 +89,18 @@ export function getOrCreateCacheForArtifact<T>(
|
|
|
83
89
|
return getOrCreateCache<PromiseWrapper<T>>(environment, cacheKey, factory);
|
|
84
90
|
}
|
|
85
91
|
|
|
92
|
+
type NetworkRequestStatus =
|
|
93
|
+
| {
|
|
94
|
+
kind: 'UndisposedIncomplete';
|
|
95
|
+
}
|
|
96
|
+
| {
|
|
97
|
+
kind: 'Disposed';
|
|
98
|
+
}
|
|
99
|
+
| {
|
|
100
|
+
kind: 'UndisposedComplete';
|
|
101
|
+
retainedQuery: RetainedQuery;
|
|
102
|
+
};
|
|
103
|
+
|
|
86
104
|
export function makeNetworkRequest<T>(
|
|
87
105
|
environment: IsographEnvironment,
|
|
88
106
|
artifact: IsoResolver,
|
|
@@ -91,20 +109,37 @@ export function makeNetworkRequest<T>(
|
|
|
91
109
|
if (typeof window !== 'undefined' && window.__LOG) {
|
|
92
110
|
console.log('make network request', artifact, variables);
|
|
93
111
|
}
|
|
112
|
+
let status: NetworkRequestStatus = {
|
|
113
|
+
kind: 'UndisposedIncomplete',
|
|
114
|
+
};
|
|
115
|
+
// This should be an observable, not a promise
|
|
94
116
|
const promise = environment
|
|
95
117
|
.networkFunction(artifact.queryText, variables)
|
|
96
118
|
.then((networkResponse) => {
|
|
97
119
|
if (typeof window !== 'undefined' && window.__LOG) {
|
|
98
120
|
console.log('network response', artifact, artifact);
|
|
99
121
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
|
|
123
|
+
if (status.kind === 'UndisposedIncomplete') {
|
|
124
|
+
normalizeData(
|
|
125
|
+
environment,
|
|
126
|
+
artifact.normalizationAst,
|
|
127
|
+
networkResponse.data,
|
|
128
|
+
variables,
|
|
129
|
+
artifact.nestedRefetchQueries,
|
|
130
|
+
);
|
|
131
|
+
const retainedQuery = {
|
|
132
|
+
normalizationAst: artifact.normalizationAst,
|
|
133
|
+
variables,
|
|
134
|
+
};
|
|
135
|
+
status = {
|
|
136
|
+
kind: 'UndisposedComplete',
|
|
137
|
+
retainedQuery,
|
|
138
|
+
};
|
|
139
|
+
retainQuery(environment, retainedQuery);
|
|
140
|
+
}
|
|
141
|
+
// TODO return null
|
|
142
|
+
return networkResponse;
|
|
108
143
|
});
|
|
109
144
|
|
|
110
145
|
const wrapper = wrapPromise(promise);
|
|
@@ -112,7 +147,18 @@ export function makeNetworkRequest<T>(
|
|
|
112
147
|
const response: ItemCleanupPair<PromiseWrapper<T>> = [
|
|
113
148
|
wrapper,
|
|
114
149
|
() => {
|
|
115
|
-
|
|
150
|
+
if (status.kind === 'UndisposedComplete') {
|
|
151
|
+
const didUnretainSomeQuery = unretainQuery(
|
|
152
|
+
environment,
|
|
153
|
+
status.retainedQuery,
|
|
154
|
+
);
|
|
155
|
+
if (didUnretainSomeQuery) {
|
|
156
|
+
garbageCollectEnvironment(environment);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
status = {
|
|
160
|
+
kind: 'Disposed',
|
|
161
|
+
};
|
|
116
162
|
},
|
|
117
163
|
];
|
|
118
164
|
return response;
|
|
@@ -138,7 +184,9 @@ function normalizeData(
|
|
|
138
184
|
networkResponse: NetworkResponseObject,
|
|
139
185
|
variables: Object,
|
|
140
186
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[],
|
|
141
|
-
) {
|
|
187
|
+
): Set<DataId> {
|
|
188
|
+
const encounteredIds = new Set<DataId>();
|
|
189
|
+
|
|
142
190
|
if (typeof window !== 'undefined' && window.__LOG) {
|
|
143
191
|
console.log(
|
|
144
192
|
'about to normalize',
|
|
@@ -155,11 +203,13 @@ function normalizeData(
|
|
|
155
203
|
ROOT_ID,
|
|
156
204
|
variables as any,
|
|
157
205
|
nestedRefetchQueries,
|
|
206
|
+
encounteredIds,
|
|
158
207
|
);
|
|
159
208
|
if (typeof window !== 'undefined' && window.__LOG) {
|
|
160
209
|
console.log('after normalization', { store: environment.store });
|
|
161
210
|
}
|
|
162
211
|
callSubscriptions(environment);
|
|
212
|
+
return encounteredIds;
|
|
163
213
|
}
|
|
164
214
|
|
|
165
215
|
export function subscribe(
|
|
@@ -194,7 +244,9 @@ function normalizeDataIntoRecord(
|
|
|
194
244
|
targetParentRecordId: DataId,
|
|
195
245
|
variables: { [index: string]: string },
|
|
196
246
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[],
|
|
247
|
+
mutableEncounteredIds: Set<DataId>,
|
|
197
248
|
) {
|
|
249
|
+
mutableEncounteredIds.add(targetParentRecordId);
|
|
198
250
|
for (const normalizationNode of normalizationAst) {
|
|
199
251
|
switch (normalizationNode.kind) {
|
|
200
252
|
case 'Scalar': {
|
|
@@ -215,6 +267,7 @@ function normalizeDataIntoRecord(
|
|
|
215
267
|
targetParentRecordId,
|
|
216
268
|
variables,
|
|
217
269
|
nestedRefetchQueries,
|
|
270
|
+
mutableEncounteredIds,
|
|
218
271
|
);
|
|
219
272
|
break;
|
|
220
273
|
}
|
|
@@ -253,6 +306,7 @@ function normalizeLinkedField(
|
|
|
253
306
|
targetParentRecordId: DataId,
|
|
254
307
|
variables: { [index: string]: string },
|
|
255
308
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[],
|
|
309
|
+
mutableEncounteredIds: Set<DataId>,
|
|
256
310
|
) {
|
|
257
311
|
const networkResponseKey = getNetworkResponseKey(astNode);
|
|
258
312
|
const networkResponseData = networkResponseParentRecord[networkResponseKey];
|
|
@@ -282,7 +336,9 @@ function normalizeLinkedField(
|
|
|
282
336
|
variables,
|
|
283
337
|
i,
|
|
284
338
|
nestedRefetchQueries,
|
|
339
|
+
mutableEncounteredIds,
|
|
285
340
|
);
|
|
341
|
+
|
|
286
342
|
dataIds.push({ __link: newStoreRecordId });
|
|
287
343
|
}
|
|
288
344
|
targetParentRecord[parentRecordKey] = dataIds;
|
|
@@ -295,6 +351,7 @@ function normalizeLinkedField(
|
|
|
295
351
|
variables,
|
|
296
352
|
null,
|
|
297
353
|
nestedRefetchQueries,
|
|
354
|
+
mutableEncounteredIds,
|
|
298
355
|
);
|
|
299
356
|
targetParentRecord[parentRecordKey] = {
|
|
300
357
|
__link: newStoreRecordId,
|
|
@@ -310,6 +367,7 @@ function normalizeNetworkResponseObject(
|
|
|
310
367
|
variables: { [index: string]: string },
|
|
311
368
|
index: number | null,
|
|
312
369
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[],
|
|
370
|
+
mutableEncounteredIds: Set<DataId>,
|
|
313
371
|
): DataId /* The id of the modified or newly created item */ {
|
|
314
372
|
const newStoreRecordId = getDataIdOfNetworkResponse(
|
|
315
373
|
targetParentRecordId,
|
|
@@ -330,6 +388,7 @@ function normalizeNetworkResponseObject(
|
|
|
330
388
|
newStoreRecordId,
|
|
331
389
|
variables,
|
|
332
390
|
nestedRefetchQueries,
|
|
391
|
+
mutableEncounteredIds,
|
|
333
392
|
);
|
|
334
393
|
|
|
335
394
|
return newStoreRecordId;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Arguments } from './index';
|
|
2
|
+
import { ReaderArtifact } from './reader';
|
|
3
|
+
|
|
4
|
+
// This type should be treated as an opaque type.
|
|
5
|
+
export type IsographEntrypoint<
|
|
6
|
+
TReadFromStore extends Object,
|
|
7
|
+
TResolverResult,
|
|
8
|
+
> = {
|
|
9
|
+
kind: 'Entrypoint';
|
|
10
|
+
queryText: string;
|
|
11
|
+
normalizationAst: NormalizationAst;
|
|
12
|
+
readerArtifact: ReaderArtifact<TReadFromStore, TResolverResult>;
|
|
13
|
+
nestedRefetchQueries: RefetchQueryArtifactWrapper[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type NormalizationAstNode =
|
|
17
|
+
| NormalizationScalarField
|
|
18
|
+
| NormalizationLinkedField;
|
|
19
|
+
export type NormalizationAst = NormalizationAstNode[];
|
|
20
|
+
|
|
21
|
+
export type NormalizationScalarField = {
|
|
22
|
+
kind: 'Scalar';
|
|
23
|
+
fieldName: string;
|
|
24
|
+
arguments: Arguments | null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type NormalizationLinkedField = {
|
|
28
|
+
kind: 'Linked';
|
|
29
|
+
fieldName: string;
|
|
30
|
+
arguments: Arguments | null;
|
|
31
|
+
selections: NormalizationAst;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// This is more like an entrypoint, but one specifically for a refetch query/mutation
|
|
35
|
+
export type RefetchQueryArtifact = {
|
|
36
|
+
kind: 'RefetchQuery';
|
|
37
|
+
queryText: string;
|
|
38
|
+
normalizationAst: NormalizationAst;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// TODO rename
|
|
42
|
+
export type RefetchQueryArtifactWrapper = {
|
|
43
|
+
artifact: RefetchQueryArtifact;
|
|
44
|
+
allowedVariables: string[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function assertIsEntrypoint<
|
|
48
|
+
TReadFromStore extends Object,
|
|
49
|
+
TResolverResult,
|
|
50
|
+
>(
|
|
51
|
+
value:
|
|
52
|
+
| IsographEntrypoint<TReadFromStore, TResolverResult>
|
|
53
|
+
| ((_: any) => any)
|
|
54
|
+
// Temporarily, allow any here. Once we automatically provide
|
|
55
|
+
// types to entrypoints, we probably don't need this.
|
|
56
|
+
| any,
|
|
57
|
+
): asserts value is IsographEntrypoint<TReadFromStore, TResolverResult> {
|
|
58
|
+
if (typeof value === 'function') throw new Error('Not a string');
|
|
59
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataId,
|
|
3
|
+
DataTypeValue,
|
|
4
|
+
IsographEnvironment,
|
|
5
|
+
IsographStore,
|
|
6
|
+
ROOT_ID,
|
|
7
|
+
StoreRecord,
|
|
8
|
+
} from './IsographEnvironment';
|
|
9
|
+
import { NormalizationAst } from './index';
|
|
10
|
+
import { getParentRecordKey } from './cache';
|
|
11
|
+
|
|
12
|
+
export type RetainedQuery = {
|
|
13
|
+
normalizationAst: NormalizationAst;
|
|
14
|
+
variables: {};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type DidUnretainSomeQuery = boolean;
|
|
18
|
+
export function unretainQuery(
|
|
19
|
+
environment: IsographEnvironment,
|
|
20
|
+
retainedQuery: RetainedQuery,
|
|
21
|
+
): DidUnretainSomeQuery {
|
|
22
|
+
environment.retainedQueries.delete(retainedQuery);
|
|
23
|
+
environment.gcBuffer.push(retainedQuery);
|
|
24
|
+
|
|
25
|
+
if (environment.gcBuffer.length > environment.gcBufferSize) {
|
|
26
|
+
environment.gcBuffer.shift();
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function retainQuery(
|
|
34
|
+
environment: IsographEnvironment,
|
|
35
|
+
queryToRetain: RetainedQuery,
|
|
36
|
+
) {
|
|
37
|
+
environment.retainedQueries.add(queryToRetain);
|
|
38
|
+
// TODO can we remove this query from the buffer somehow?
|
|
39
|
+
// We are relying on === equality, but we really should be comparing
|
|
40
|
+
// id + variables
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function garbageCollectEnvironment(environment: IsographEnvironment) {
|
|
44
|
+
const retainedIds = new Set<DataId>([ROOT_ID]);
|
|
45
|
+
|
|
46
|
+
for (const query of environment.retainedQueries) {
|
|
47
|
+
recordReachableIds(environment.store, query, retainedIds);
|
|
48
|
+
}
|
|
49
|
+
for (const query of environment.gcBuffer) {
|
|
50
|
+
recordReachableIds(environment.store, query, retainedIds);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const dataId in environment.store) {
|
|
54
|
+
if (!retainedIds.has(dataId)) {
|
|
55
|
+
delete environment.store[dataId];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function recordReachableIds(
|
|
61
|
+
store: IsographStore,
|
|
62
|
+
retainedQuery: RetainedQuery,
|
|
63
|
+
mutableRetainedIds: Set<DataId>,
|
|
64
|
+
) {
|
|
65
|
+
recordReachableIdsFromRecord(
|
|
66
|
+
store,
|
|
67
|
+
store[ROOT_ID],
|
|
68
|
+
mutableRetainedIds,
|
|
69
|
+
retainedQuery.normalizationAst,
|
|
70
|
+
retainedQuery.variables,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getLinkedId(data: Exclude<DataTypeValue, null | void>): string {
|
|
75
|
+
// @ts-expect-error
|
|
76
|
+
if (data.__link != null) {
|
|
77
|
+
// @ts-expect-error
|
|
78
|
+
return data.__link;
|
|
79
|
+
} else {
|
|
80
|
+
throw new Error('Record in an invalid state');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function recordReachableIdsFromRecord(
|
|
85
|
+
store: IsographStore,
|
|
86
|
+
currentRecord: StoreRecord,
|
|
87
|
+
mutableRetainedIds: Set<DataId>,
|
|
88
|
+
selections: NormalizationAst,
|
|
89
|
+
variables: { [index: string]: string } | null,
|
|
90
|
+
) {
|
|
91
|
+
for (const selection of selections) {
|
|
92
|
+
switch (selection.kind) {
|
|
93
|
+
case 'Linked':
|
|
94
|
+
const linkKey = getParentRecordKey(selection, variables ?? {});
|
|
95
|
+
const linkedFieldOrFields = currentRecord[linkKey];
|
|
96
|
+
|
|
97
|
+
const ids = [];
|
|
98
|
+
if (Array.isArray(linkedFieldOrFields)) {
|
|
99
|
+
for (const link of linkedFieldOrFields) {
|
|
100
|
+
if (link != null) {
|
|
101
|
+
const id = getLinkedId(link);
|
|
102
|
+
ids.push(id);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
if (linkedFieldOrFields != null) {
|
|
107
|
+
const id = getLinkedId(linkedFieldOrFields);
|
|
108
|
+
ids.push(id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const nextRecordId of ids) {
|
|
113
|
+
const nextRecord = store[nextRecordId];
|
|
114
|
+
if (nextRecord != null) {
|
|
115
|
+
mutableRetainedIds.add(nextRecordId);
|
|
116
|
+
recordReachableIdsFromRecord(
|
|
117
|
+
store,
|
|
118
|
+
nextRecord,
|
|
119
|
+
mutableRetainedIds,
|
|
120
|
+
selection.selections,
|
|
121
|
+
variables,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
continue;
|
|
127
|
+
case 'Scalar':
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -14,141 +14,68 @@ import {
|
|
|
14
14
|
Link,
|
|
15
15
|
ROOT_ID,
|
|
16
16
|
StoreRecord,
|
|
17
|
-
useIsographEnvironment,
|
|
18
17
|
} from './IsographEnvironment';
|
|
19
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';
|
|
20
26
|
|
|
27
|
+
export {
|
|
28
|
+
retainQuery,
|
|
29
|
+
unretainQuery,
|
|
30
|
+
type RetainedQuery,
|
|
31
|
+
garbageCollectEnvironment,
|
|
32
|
+
} from './garbageCollection';
|
|
21
33
|
export { type PromiseWrapper } from './PromiseWrapper';
|
|
22
34
|
export { makeNetworkRequest, subscribe } from './cache';
|
|
23
35
|
export {
|
|
24
|
-
IsographEnvironmentContext,
|
|
25
36
|
ROOT_ID,
|
|
26
37
|
type DataId,
|
|
27
38
|
type DataTypeValue,
|
|
28
39
|
type IsographEnvironment,
|
|
29
|
-
IsographEnvironmentProvider,
|
|
30
|
-
type IsographEnvironmentProviderProps,
|
|
31
40
|
type IsographNetworkFunction,
|
|
32
41
|
type IsographStore,
|
|
33
42
|
type Link,
|
|
34
43
|
type StoreRecord,
|
|
35
|
-
useIsographEnvironment,
|
|
36
44
|
createIsographEnvironment,
|
|
37
45
|
createIsographStore,
|
|
38
46
|
} from './IsographEnvironment';
|
|
47
|
+
export {
|
|
48
|
+
IsographEnvironmentProvider,
|
|
49
|
+
useIsographEnvironment,
|
|
50
|
+
type IsographEnvironmentProviderProps,
|
|
51
|
+
} from './IsographEnvironmentProvider';
|
|
39
52
|
export { useImperativeReference } from './useImperativeReference';
|
|
40
53
|
export { EntrypointReader } from './EntrypointReader';
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export type ReaderAstNode =
|
|
64
|
-
| ReaderScalarField
|
|
65
|
-
| ReaderLinkedField
|
|
66
|
-
| ReaderResolverField
|
|
67
|
-
| ReaderRefetchField
|
|
68
|
-
| ReaderMutationField;
|
|
69
|
-
|
|
70
|
-
// @ts-ignore
|
|
71
|
-
export type ReaderAst<TReadFromStore> = ReaderAstNode[];
|
|
54
|
+
export {
|
|
55
|
+
type ReaderArtifact,
|
|
56
|
+
ReaderAst,
|
|
57
|
+
ReaderAstNode,
|
|
58
|
+
ReaderLinkedField,
|
|
59
|
+
ReaderMutationField,
|
|
60
|
+
ReaderRefetchField,
|
|
61
|
+
ReaderResolverField,
|
|
62
|
+
ReaderResolverVariant,
|
|
63
|
+
ReaderScalarField,
|
|
64
|
+
} from './reader';
|
|
65
|
+
export {
|
|
66
|
+
NormalizationAst,
|
|
67
|
+
NormalizationAstNode,
|
|
68
|
+
NormalizationLinkedField,
|
|
69
|
+
NormalizationScalarField,
|
|
70
|
+
IsographEntrypoint,
|
|
71
|
+
assertIsEntrypoint,
|
|
72
|
+
RefetchQueryArtifact,
|
|
73
|
+
RefetchQueryArtifactWrapper,
|
|
74
|
+
} from './entrypoint';
|
|
72
75
|
|
|
73
76
|
export type ExtractSecondParam<T extends (arg1: any, arg2: any) => any> =
|
|
74
77
|
T extends (arg1: any, arg2: infer P) => any ? P : never;
|
|
75
78
|
|
|
76
|
-
export type ReaderScalarField = {
|
|
77
|
-
kind: 'Scalar';
|
|
78
|
-
fieldName: string;
|
|
79
|
-
alias: string | null;
|
|
80
|
-
arguments: Arguments | null;
|
|
81
|
-
};
|
|
82
|
-
export type ReaderLinkedField = {
|
|
83
|
-
kind: 'Linked';
|
|
84
|
-
fieldName: string;
|
|
85
|
-
alias: string | null;
|
|
86
|
-
selections: ReaderAst<unknown>;
|
|
87
|
-
arguments: Arguments | null;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export type ReaderResolverVariant =
|
|
91
|
-
| { kind: 'Eager' }
|
|
92
|
-
// componentName is the component's cacheKey for getRefReaderByName
|
|
93
|
-
// and is the type + field concatenated
|
|
94
|
-
| { kind: 'Component'; componentName: string };
|
|
95
|
-
|
|
96
|
-
export type ReaderResolverField = {
|
|
97
|
-
kind: 'Resolver';
|
|
98
|
-
alias: string;
|
|
99
|
-
readerArtifact: ReaderArtifact<any, any>;
|
|
100
|
-
arguments: Arguments | null;
|
|
101
|
-
usedRefetchQueries: number[];
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export type ReaderRefetchField = {
|
|
105
|
-
kind: 'RefetchField';
|
|
106
|
-
alias: string;
|
|
107
|
-
// TODO this bad modeling. A refetch field cannot have variant: "Component" (I think)
|
|
108
|
-
readerArtifact: ReaderArtifact<any, any>;
|
|
109
|
-
refetchQuery: number;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export type ReaderMutationField = {
|
|
113
|
-
kind: 'MutationField';
|
|
114
|
-
alias: string;
|
|
115
|
-
// TODO this bad modeling. A mutation field cannot have variant: "Component" (I think)
|
|
116
|
-
readerArtifact: ReaderArtifact<any, any>;
|
|
117
|
-
refetchQuery: number;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
export type NormalizationAstNode =
|
|
121
|
-
| NormalizationScalarField
|
|
122
|
-
| NormalizationLinkedField;
|
|
123
|
-
// @ts-ignore
|
|
124
|
-
export type NormalizationAst = NormalizationAstNode[];
|
|
125
|
-
|
|
126
|
-
export type NormalizationScalarField = {
|
|
127
|
-
kind: 'Scalar';
|
|
128
|
-
fieldName: string;
|
|
129
|
-
arguments: Arguments | null;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export type NormalizationLinkedField = {
|
|
133
|
-
kind: 'Linked';
|
|
134
|
-
fieldName: string;
|
|
135
|
-
arguments: Arguments | null;
|
|
136
|
-
selections: NormalizationAst;
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// This is more like an entrypoint, but one specifically for a refetch query/mutation
|
|
140
|
-
export type RefetchQueryArtifact = {
|
|
141
|
-
kind: 'RefetchQuery';
|
|
142
|
-
queryText: string;
|
|
143
|
-
normalizationAst: NormalizationAst;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
// TODO rename
|
|
147
|
-
export type RefetchQueryArtifactWrapper = {
|
|
148
|
-
artifact: RefetchQueryArtifact;
|
|
149
|
-
allowedVariables: string[];
|
|
150
|
-
};
|
|
151
|
-
|
|
152
79
|
export type Arguments = Argument[];
|
|
153
80
|
export type Argument = [ArgumentName, ArgumentValue];
|
|
154
81
|
export type ArgumentName = string;
|
|
@@ -177,17 +104,6 @@ export type FragmentReference<
|
|
|
177
104
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[];
|
|
178
105
|
};
|
|
179
106
|
|
|
180
|
-
function assertIsEntrypoint<TReadFromStore extends Object, TResolverResult>(
|
|
181
|
-
value:
|
|
182
|
-
| IsographEntrypoint<TReadFromStore, TResolverResult>
|
|
183
|
-
| ((_: any) => any)
|
|
184
|
-
// Temporarily, allow any here. Once we automatically provide
|
|
185
|
-
// types to entrypoints, we probably don't need this.
|
|
186
|
-
| any,
|
|
187
|
-
): asserts value is IsographEntrypoint<TReadFromStore, TResolverResult> {
|
|
188
|
-
if (typeof value === 'function') throw new Error('Not a string');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
107
|
export type ExtractReadFromStore<Type> =
|
|
192
108
|
Type extends IsographEntrypoint<infer X, any> ? X : never;
|
|
193
109
|
export type ExtractResolverResult<Type> =
|