@isograph/react 0.2.0 → 0.3.1
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/.turbo/turbo-compile-typescript.log +4 -0
- package/dist/core/FragmentReference.d.ts +25 -6
- package/dist/core/FragmentReference.d.ts.map +1 -0
- package/dist/core/FragmentReference.js +3 -13
- package/dist/core/IsographEnvironment.d.ts +34 -26
- package/dist/core/IsographEnvironment.d.ts.map +1 -0
- package/dist/core/IsographEnvironment.js +19 -22
- package/dist/core/PromiseWrapper.d.ts +4 -4
- package/dist/core/PromiseWrapper.d.ts.map +1 -0
- package/dist/core/PromiseWrapper.js +9 -9
- package/dist/core/areEqualWithDeepComparison.d.ts +5 -3
- package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
- package/dist/core/areEqualWithDeepComparison.js +89 -39
- package/dist/core/cache.d.ts +20 -13
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +205 -128
- package/dist/core/check.d.ts +22 -0
- package/dist/core/check.d.ts.map +1 -0
- package/dist/core/check.js +127 -0
- package/dist/core/componentCache.d.ts +2 -2
- package/dist/core/componentCache.d.ts.map +1 -0
- package/dist/core/componentCache.js +28 -32
- package/dist/core/entrypoint.d.ts +31 -15
- package/dist/core/entrypoint.d.ts.map +1 -0
- package/dist/core/entrypoint.js +1 -2
- package/dist/core/garbageCollection.d.ts +6 -5
- package/dist/core/garbageCollection.d.ts.map +1 -0
- package/dist/core/garbageCollection.js +49 -16
- package/dist/core/logging.d.ts +68 -0
- package/dist/core/logging.d.ts.map +1 -0
- package/dist/core/logging.js +22 -0
- package/dist/core/makeNetworkRequest.d.ts +6 -3
- package/dist/core/makeNetworkRequest.d.ts.map +1 -0
- package/dist/core/makeNetworkRequest.js +160 -19
- package/dist/core/read.d.ts +25 -5
- package/dist/core/read.d.ts.map +1 -0
- package/dist/core/read.js +416 -259
- package/dist/core/reader.d.ts +31 -15
- package/dist/core/reader.d.ts.map +1 -0
- package/dist/core/startUpdate.d.ts +5 -0
- package/dist/core/startUpdate.d.ts.map +1 -0
- package/dist/core/startUpdate.js +15 -0
- package/dist/core/util.d.ts +5 -0
- package/dist/core/util.d.ts.map +1 -0
- package/dist/index.d.ts +19 -14
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -2
- package/dist/loadable-hooks/useClientSideDefer.d.ts +9 -3
- package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
- package/dist/loadable-hooks/useClientSideDefer.js +6 -8
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +27 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.js +162 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +2 -2
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.js +1 -2
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts +13 -7
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
- package/dist/loadable-hooks/useImperativeLoadableField.js +4 -5
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts +13 -26
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
- package/dist/loadable-hooks/useSkipLimitPagination.js +93 -47
- package/dist/react/FragmentReader.d.ts +6 -4
- package/dist/react/FragmentReader.d.ts.map +1 -0
- package/dist/react/FragmentReader.js +4 -2
- package/dist/react/IsographEnvironmentProvider.d.ts +1 -0
- package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
- package/dist/react/IsographEnvironmentProvider.js +3 -3
- package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
- package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
- package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
- package/dist/react/useImperativeReference.d.ts +8 -6
- package/dist/react/useImperativeReference.d.ts.map +1 -0
- package/dist/react/useImperativeReference.js +6 -8
- package/dist/react/useLazyReference.d.ts +5 -3
- package/dist/react/useLazyReference.d.ts.map +1 -0
- package/dist/react/useLazyReference.js +34 -6
- package/dist/react/useReadAndSubscribe.d.ts +6 -3
- package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
- package/dist/react/useReadAndSubscribe.js +13 -10
- package/dist/react/useRerenderOnChange.d.ts +7 -2
- package/dist/react/useRerenderOnChange.d.ts.map +1 -0
- package/dist/react/useRerenderOnChange.js +3 -4
- package/dist/react/useResult.d.ts +4 -3
- package/dist/react/useResult.d.ts.map +1 -0
- package/dist/react/useResult.js +14 -9
- package/isograph.config.json +8 -0
- package/package.json +14 -9
- package/{src/tests/schema.graphql → schema.graphql} +1 -0
- package/src/core/FragmentReference.ts +44 -17
- package/src/core/IsographEnvironment.ts +67 -50
- package/src/core/PromiseWrapper.ts +3 -3
- package/src/core/areEqualWithDeepComparison.ts +95 -41
- package/src/core/cache.ts +316 -169
- package/src/core/check.ts +212 -0
- package/src/core/componentCache.ts +40 -46
- package/src/core/entrypoint.ts +41 -16
- package/src/core/garbageCollection.ts +77 -26
- package/src/core/logging.ts +118 -0
- package/src/core/makeNetworkRequest.ts +249 -20
- package/src/core/read.ts +658 -368
- package/src/core/reader.ts +61 -21
- package/src/core/startUpdate.ts +28 -0
- package/src/core/util.ts +8 -0
- package/src/index.ts +94 -8
- package/src/loadable-hooks/useClientSideDefer.ts +48 -17
- package/src/loadable-hooks/useConnectionSpecPagination.ts +344 -0
- package/src/loadable-hooks/useImperativeExposedMutationField.ts +1 -1
- package/src/loadable-hooks/useImperativeLoadableField.ts +36 -12
- package/src/loadable-hooks/useSkipLimitPagination.ts +253 -94
- package/src/react/FragmentReader.tsx +15 -6
- package/src/react/IsographEnvironmentProvider.tsx +1 -1
- package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
- package/src/react/useImperativeReference.ts +50 -18
- package/src/react/useLazyReference.ts +79 -11
- package/src/react/useReadAndSubscribe.ts +33 -10
- package/src/react/useRerenderOnChange.ts +7 -2
- package/src/react/useResult.ts +30 -9
- package/src/tests/__isograph/Query/meName/entrypoint.ts +10 -29
- package/src/tests/__isograph/Query/meName/normalization_ast.ts +25 -0
- package/src/tests/__isograph/Query/meName/param_type.ts +5 -2
- package/src/tests/__isograph/Query/meName/query_text.ts +6 -0
- package/src/tests/__isograph/Query/meName/resolver_reader.ts +5 -0
- package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +10 -65
- package/src/tests/__isograph/Query/meNameSuccessor/normalization_ast.ts +56 -0
- package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +9 -6
- package/src/tests/__isograph/Query/meNameSuccessor/query_text.ts +13 -0
- package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +10 -0
- package/src/tests/__isograph/Query/nodeField/entrypoint.ts +10 -28
- package/src/tests/__isograph/Query/nodeField/normalization_ast.ts +30 -0
- package/src/tests/__isograph/Query/nodeField/param_type.ts +7 -3
- package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
- package/src/tests/__isograph/Query/nodeField/query_text.ts +6 -0
- package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +5 -0
- package/src/tests/__isograph/Query/subquery/entrypoint.ts +28 -0
- package/src/tests/__isograph/Query/subquery/normalization_ast.ts +38 -0
- package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
- package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
- package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
- package/src/tests/__isograph/Query/subquery/query_text.ts +8 -0
- package/src/tests/__isograph/Query/subquery/resolver_reader.ts +52 -0
- package/src/tests/__isograph/iso.ts +24 -12
- package/src/tests/garbageCollection.test.ts +53 -45
- package/src/tests/meNameSuccessor.ts +8 -3
- package/src/tests/nodeQuery.ts +7 -4
- package/src/tests/normalizeData.test.ts +120 -0
- package/src/tests/tsconfig.json +3 -3
- package/tsconfig.json +2 -2
- package/tsconfig.pkg.json +7 -3
- package/vitest.config.ts +20 -0
- package/src/tests/isograph.config.json +0 -7
@@ -0,0 +1,212 @@
|
|
1
|
+
import { getParentRecordKey } from './cache';
|
2
|
+
import { NormalizationAstNodes } from './entrypoint';
|
3
|
+
import { Variables } from './FragmentReference';
|
4
|
+
import {
|
5
|
+
getLink,
|
6
|
+
IsographEnvironment,
|
7
|
+
Link,
|
8
|
+
StoreRecord,
|
9
|
+
} from './IsographEnvironment';
|
10
|
+
import { logMessage } from './logging';
|
11
|
+
|
12
|
+
export type ShouldFetch = RequiredShouldFetch | 'IfNecessary';
|
13
|
+
export type RequiredShouldFetch = 'Yes' | 'No';
|
14
|
+
|
15
|
+
export const DEFAULT_SHOULD_FETCH_VALUE: ShouldFetch = 'IfNecessary';
|
16
|
+
|
17
|
+
export type FetchOptions<TReadOutData> = {
|
18
|
+
shouldFetch?: ShouldFetch;
|
19
|
+
onComplete?: (data: TReadOutData) => void;
|
20
|
+
onError?: () => void;
|
21
|
+
};
|
22
|
+
|
23
|
+
export type RequiredFetchOptions<TReadOutData> = {
|
24
|
+
shouldFetch: RequiredShouldFetch;
|
25
|
+
} & FetchOptions<TReadOutData>;
|
26
|
+
|
27
|
+
export type CheckResult =
|
28
|
+
| {
|
29
|
+
kind: 'EnoughData';
|
30
|
+
}
|
31
|
+
| {
|
32
|
+
kind: 'MissingData';
|
33
|
+
record: Link;
|
34
|
+
};
|
35
|
+
|
36
|
+
export function check(
|
37
|
+
environment: IsographEnvironment,
|
38
|
+
normalizationAst: NormalizationAstNodes,
|
39
|
+
variables: Variables,
|
40
|
+
root: Link,
|
41
|
+
): CheckResult {
|
42
|
+
const recordsById = (environment.store[root.__typename] ??= {});
|
43
|
+
const newStoreRecord = (recordsById[root.__link] ??= {});
|
44
|
+
|
45
|
+
const checkResult = checkFromRecord(
|
46
|
+
environment,
|
47
|
+
normalizationAst,
|
48
|
+
variables,
|
49
|
+
newStoreRecord,
|
50
|
+
root,
|
51
|
+
);
|
52
|
+
logMessage(environment, () => ({
|
53
|
+
kind: 'EnvironmentCheck',
|
54
|
+
result: checkResult,
|
55
|
+
}));
|
56
|
+
return checkResult;
|
57
|
+
}
|
58
|
+
|
59
|
+
function checkFromRecord(
|
60
|
+
environment: IsographEnvironment,
|
61
|
+
normalizationAst: NormalizationAstNodes,
|
62
|
+
variables: Variables,
|
63
|
+
record: StoreRecord,
|
64
|
+
recordLink: Link,
|
65
|
+
): CheckResult {
|
66
|
+
normalizationAstLoop: for (const normalizationAstNode of normalizationAst) {
|
67
|
+
switch (normalizationAstNode.kind) {
|
68
|
+
case 'Scalar': {
|
69
|
+
const parentRecordKey = getParentRecordKey(
|
70
|
+
normalizationAstNode,
|
71
|
+
variables,
|
72
|
+
);
|
73
|
+
const scalarValue = record[parentRecordKey];
|
74
|
+
|
75
|
+
// null means the value is known to be missing, so it must
|
76
|
+
// be exactly undefined
|
77
|
+
if (scalarValue === undefined) {
|
78
|
+
return {
|
79
|
+
kind: 'MissingData',
|
80
|
+
record: recordLink,
|
81
|
+
};
|
82
|
+
}
|
83
|
+
continue normalizationAstLoop;
|
84
|
+
}
|
85
|
+
case 'Linked': {
|
86
|
+
const parentRecordKey = getParentRecordKey(
|
87
|
+
normalizationAstNode,
|
88
|
+
variables,
|
89
|
+
);
|
90
|
+
|
91
|
+
const linkedValue = record[parentRecordKey];
|
92
|
+
|
93
|
+
if (linkedValue === undefined) {
|
94
|
+
return {
|
95
|
+
kind: 'MissingData',
|
96
|
+
record: recordLink,
|
97
|
+
};
|
98
|
+
} else if (linkedValue === null) {
|
99
|
+
continue;
|
100
|
+
} else if (Array.isArray(linkedValue)) {
|
101
|
+
arrayItemsLoop: for (const item of linkedValue) {
|
102
|
+
const link = getLink(item);
|
103
|
+
if (link === null) {
|
104
|
+
throw new Error(
|
105
|
+
'Unexpected non-link in the Isograph store. ' +
|
106
|
+
'This is indicative of a bug in Isograph.',
|
107
|
+
);
|
108
|
+
}
|
109
|
+
|
110
|
+
const linkedRecord =
|
111
|
+
environment.store[link.__typename]?.[link.__link];
|
112
|
+
|
113
|
+
if (linkedRecord === undefined) {
|
114
|
+
return {
|
115
|
+
kind: 'MissingData',
|
116
|
+
record: link,
|
117
|
+
};
|
118
|
+
} else if (linkedRecord === null) {
|
119
|
+
continue arrayItemsLoop;
|
120
|
+
} else {
|
121
|
+
// TODO in __DEV__ assert linkedRecord is an object
|
122
|
+
const result = checkFromRecord(
|
123
|
+
environment,
|
124
|
+
normalizationAstNode.selections,
|
125
|
+
variables,
|
126
|
+
linkedRecord,
|
127
|
+
link,
|
128
|
+
);
|
129
|
+
|
130
|
+
if (result.kind === 'MissingData') {
|
131
|
+
return result;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
} else {
|
136
|
+
const link = getLink(linkedValue);
|
137
|
+
if (link === null) {
|
138
|
+
throw new Error(
|
139
|
+
'Unexpected non-link in the Isograph store. ' +
|
140
|
+
'This is indicative of a bug in Isograph.',
|
141
|
+
);
|
142
|
+
}
|
143
|
+
|
144
|
+
const linkedRecord =
|
145
|
+
environment.store[link.__typename]?.[link.__link];
|
146
|
+
|
147
|
+
if (linkedRecord === undefined) {
|
148
|
+
return {
|
149
|
+
kind: 'MissingData',
|
150
|
+
record: link,
|
151
|
+
};
|
152
|
+
} else if (linkedRecord === null) {
|
153
|
+
continue normalizationAstLoop;
|
154
|
+
} else {
|
155
|
+
// TODO in __DEV__ assert linkedRecord is an object
|
156
|
+
const result = checkFromRecord(
|
157
|
+
environment,
|
158
|
+
normalizationAstNode.selections,
|
159
|
+
variables,
|
160
|
+
linkedRecord,
|
161
|
+
link,
|
162
|
+
);
|
163
|
+
|
164
|
+
if (result.kind === 'MissingData') {
|
165
|
+
return result;
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
continue normalizationAstLoop;
|
171
|
+
}
|
172
|
+
case 'InlineFragment': {
|
173
|
+
const existingRecordTypename = record['__typename'];
|
174
|
+
|
175
|
+
if (
|
176
|
+
existingRecordTypename == null ||
|
177
|
+
existingRecordTypename !== normalizationAstNode.type
|
178
|
+
) {
|
179
|
+
return {
|
180
|
+
kind: 'MissingData',
|
181
|
+
record: recordLink,
|
182
|
+
};
|
183
|
+
}
|
184
|
+
|
185
|
+
const result = checkFromRecord(
|
186
|
+
environment,
|
187
|
+
normalizationAstNode.selections,
|
188
|
+
variables,
|
189
|
+
record,
|
190
|
+
recordLink,
|
191
|
+
);
|
192
|
+
|
193
|
+
if (result.kind === 'MissingData') {
|
194
|
+
return result;
|
195
|
+
}
|
196
|
+
|
197
|
+
continue normalizationAstLoop;
|
198
|
+
}
|
199
|
+
default: {
|
200
|
+
let _: never = normalizationAstNode;
|
201
|
+
_;
|
202
|
+
throw new Error(
|
203
|
+
'Unexpected case. This is indicative of a bug in Isograph.',
|
204
|
+
);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
return {
|
210
|
+
kind: 'EnoughData',
|
211
|
+
};
|
212
|
+
}
|
@@ -1,9 +1,13 @@
|
|
1
|
-
import { stableCopy } from './cache';
|
2
|
-
import { IsographEnvironment } from './IsographEnvironment';
|
3
|
-
import { FragmentReference } from './FragmentReference';
|
4
1
|
import { useReadAndSubscribe } from '../react/useReadAndSubscribe';
|
5
|
-
import {
|
2
|
+
import {
|
3
|
+
FragmentReference,
|
4
|
+
stableIdForFragmentReference,
|
5
|
+
} from './FragmentReference';
|
6
|
+
import { IsographEnvironment } from './IsographEnvironment';
|
7
|
+
import { logMessage } from './logging';
|
6
8
|
import { readPromise } from './PromiseWrapper';
|
9
|
+
import { NetworkRequestReaderOptions } from './read';
|
10
|
+
import { createStartUpdate } from './startUpdate';
|
7
11
|
|
8
12
|
export function getOrCreateCachedComponent(
|
9
13
|
environment: IsographEnvironment,
|
@@ -11,51 +15,41 @@ export function getOrCreateCachedComponent(
|
|
11
15
|
fragmentReference: FragmentReference<any, any>,
|
12
16
|
networkRequestOptions: NetworkRequestReaderOptions,
|
13
17
|
): React.FC<any> {
|
14
|
-
//
|
15
|
-
|
16
|
-
// time.
|
17
|
-
const cachedComponentsById = environment.componentCache;
|
18
|
-
|
19
|
-
cachedComponentsById[fragmentReference.root] =
|
20
|
-
cachedComponentsById[fragmentReference.root] ?? {};
|
21
|
-
const componentsByName = cachedComponentsById[fragmentReference.root];
|
22
|
-
|
23
|
-
componentsByName[componentName] = componentsByName[componentName] ?? {};
|
24
|
-
const byArgs = componentsByName[componentName];
|
18
|
+
// We create startUpdate outside of component to make it stable
|
19
|
+
const startUpdate = createStartUpdate(environment, fragmentReference);
|
25
20
|
|
26
|
-
|
27
|
-
|
28
|
-
)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
const data = useReadAndSubscribe(
|
34
|
-
fragmentReference,
|
35
|
-
networkRequestOptions,
|
36
|
-
);
|
21
|
+
return (environment.componentCache[
|
22
|
+
stableIdForFragmentReference(fragmentReference, componentName)
|
23
|
+
] ??= (() => {
|
24
|
+
function Component(additionalRuntimeProps: { [key: string]: any }) {
|
25
|
+
const readerWithRefetchQueries = readPromise(
|
26
|
+
fragmentReference.readerWithRefetchQueries,
|
27
|
+
);
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
' ' +
|
44
|
-
fragmentReference.root,
|
45
|
-
);
|
46
|
-
}
|
29
|
+
const data = useReadAndSubscribe(
|
30
|
+
fragmentReference,
|
31
|
+
networkRequestOptions,
|
32
|
+
readerWithRefetchQueries.readerArtifact.readerAst,
|
33
|
+
);
|
47
34
|
|
48
|
-
|
49
|
-
|
50
|
-
|
35
|
+
logMessage(environment, () => ({
|
36
|
+
kind: 'ComponentRerendered',
|
37
|
+
componentName,
|
38
|
+
rootLink: fragmentReference.root,
|
39
|
+
}));
|
51
40
|
|
52
|
-
|
41
|
+
return readerWithRefetchQueries.readerArtifact.resolver(
|
42
|
+
{
|
53
43
|
data,
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
44
|
+
parameters: fragmentReference.variables,
|
45
|
+
startUpdate: readerWithRefetchQueries.readerArtifact.hasUpdatable
|
46
|
+
? startUpdate
|
47
|
+
: undefined,
|
48
|
+
},
|
49
|
+
additionalRuntimeProps,
|
50
|
+
);
|
51
|
+
}
|
52
|
+
Component.displayName = `${componentName} (id: ${fragmentReference.root}) @component`;
|
53
|
+
return Component;
|
54
|
+
})());
|
61
55
|
}
|
package/src/core/entrypoint.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
import type { UnknownTReadFromStore } from './FragmentReference';
|
2
|
+
import type { TypeName } from './IsographEnvironment';
|
1
3
|
import { TopLevelReaderArtifact } from './reader';
|
2
4
|
import { Arguments } from './util';
|
3
5
|
|
4
6
|
export type ReaderWithRefetchQueries<
|
5
|
-
TReadFromStore extends
|
7
|
+
TReadFromStore extends UnknownTReadFromStore,
|
6
8
|
TClientFieldValue,
|
7
9
|
> = {
|
8
10
|
readonly kind: 'ReaderWithRefetchQueries';
|
@@ -15,28 +17,34 @@ export type ReaderWithRefetchQueries<
|
|
15
17
|
readonly nestedRefetchQueries: RefetchQueryNormalizationArtifactWrapper[];
|
16
18
|
};
|
17
19
|
|
20
|
+
export type NetworkRequestInfo<TNormalizationAst> = {
|
21
|
+
readonly kind: 'NetworkRequestInfo';
|
22
|
+
readonly queryText: string;
|
23
|
+
readonly normalizationAst: TNormalizationAst;
|
24
|
+
};
|
18
25
|
// This type should be treated as an opaque type.
|
19
26
|
export type IsographEntrypoint<
|
20
|
-
TReadFromStore extends
|
27
|
+
TReadFromStore extends UnknownTReadFromStore,
|
21
28
|
TClientFieldValue,
|
29
|
+
TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
|
22
30
|
> = {
|
23
31
|
readonly kind: 'Entrypoint';
|
24
|
-
readonly
|
25
|
-
readonly normalizationAst: NormalizationAst;
|
32
|
+
readonly networkRequestInfo: NetworkRequestInfo<TNormalizationAst>;
|
26
33
|
readonly readerWithRefetchQueries: ReaderWithRefetchQueries<
|
27
34
|
TReadFromStore,
|
28
35
|
TClientFieldValue
|
29
36
|
>;
|
37
|
+
readonly concreteType: TypeName;
|
30
38
|
};
|
31
39
|
|
32
40
|
export type IsographEntrypointLoader<
|
33
|
-
TReadFromStore extends
|
41
|
+
TReadFromStore extends UnknownTReadFromStore,
|
34
42
|
TClientFieldValue,
|
35
43
|
> = {
|
36
44
|
readonly kind: 'EntrypointLoader';
|
37
45
|
readonly typeAndField: string;
|
38
46
|
readonly loader: () => Promise<
|
39
|
-
IsographEntrypoint<TReadFromStore, TClientFieldValue>
|
47
|
+
IsographEntrypoint<TReadFromStore, TClientFieldValue, NormalizationAst>
|
40
48
|
>;
|
41
49
|
};
|
42
50
|
|
@@ -44,7 +52,18 @@ export type NormalizationAstNode =
|
|
44
52
|
| NormalizationScalarField
|
45
53
|
| NormalizationLinkedField
|
46
54
|
| NormalizationInlineFragment;
|
47
|
-
|
55
|
+
|
56
|
+
export type NormalizationAstNodes = ReadonlyArray<NormalizationAstNode>;
|
57
|
+
|
58
|
+
export type NormalizationAst = {
|
59
|
+
readonly kind: 'NormalizationAst';
|
60
|
+
readonly selections: NormalizationAstNodes;
|
61
|
+
};
|
62
|
+
|
63
|
+
export type NormalizationAstLoader = {
|
64
|
+
readonly kind: 'NormalizationAstLoader';
|
65
|
+
readonly loader: () => Promise<NormalizationAst>;
|
66
|
+
};
|
48
67
|
|
49
68
|
export type NormalizationScalarField = {
|
50
69
|
readonly kind: 'Scalar';
|
@@ -56,20 +75,21 @@ export type NormalizationLinkedField = {
|
|
56
75
|
readonly kind: 'Linked';
|
57
76
|
readonly fieldName: string;
|
58
77
|
readonly arguments: Arguments | null;
|
59
|
-
readonly selections:
|
78
|
+
readonly selections: NormalizationAstNodes;
|
79
|
+
readonly concreteType: TypeName | null;
|
60
80
|
};
|
61
81
|
|
62
82
|
export type NormalizationInlineFragment = {
|
63
83
|
readonly kind: 'InlineFragment';
|
64
84
|
readonly type: string;
|
65
|
-
readonly selections:
|
85
|
+
readonly selections: NormalizationAstNodes;
|
66
86
|
};
|
67
87
|
|
68
88
|
// This is more like an entrypoint, but one specifically for a refetch query/mutation
|
69
89
|
export type RefetchQueryNormalizationArtifact = {
|
70
90
|
readonly kind: 'RefetchQuery';
|
71
|
-
readonly
|
72
|
-
readonly
|
91
|
+
readonly networkRequestInfo: NetworkRequestInfo<NormalizationAst>;
|
92
|
+
readonly concreteType: TypeName;
|
73
93
|
};
|
74
94
|
|
75
95
|
// TODO rename
|
@@ -79,21 +99,26 @@ export type RefetchQueryNormalizationArtifactWrapper = {
|
|
79
99
|
};
|
80
100
|
|
81
101
|
export function assertIsEntrypoint<
|
82
|
-
TReadFromStore extends
|
102
|
+
TReadFromStore extends UnknownTReadFromStore,
|
83
103
|
TClientFieldValue,
|
104
|
+
TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
|
84
105
|
>(
|
85
106
|
value:
|
86
|
-
| IsographEntrypoint<TReadFromStore, TClientFieldValue>
|
107
|
+
| IsographEntrypoint<TReadFromStore, TClientFieldValue, TNormalizationAst>
|
87
108
|
| ((_: any) => any)
|
88
109
|
// Temporarily, allow any here. Once we automatically provide
|
89
110
|
// types to entrypoints, we probably don't need this.
|
90
111
|
| any,
|
91
|
-
): asserts value is IsographEntrypoint<
|
112
|
+
): asserts value is IsographEntrypoint<
|
113
|
+
TReadFromStore,
|
114
|
+
TClientFieldValue,
|
115
|
+
TNormalizationAst
|
116
|
+
> {
|
92
117
|
if (typeof value === 'function') throw new Error('Not a string');
|
93
118
|
}
|
94
119
|
|
95
120
|
export type ExtractReadFromStore<Type> =
|
96
|
-
Type extends IsographEntrypoint<infer X, any> ? X : never;
|
121
|
+
Type extends IsographEntrypoint<infer X, any, any> ? X : never;
|
97
122
|
export type ExtractResolverResult<Type> =
|
98
|
-
Type extends IsographEntrypoint<any, infer X> ? X : never;
|
123
|
+
Type extends IsographEntrypoint<any, infer X, any> ? X : never;
|
99
124
|
export type ExtractProps<Type> = Type extends React.FC<infer X> ? X : never;
|
@@ -1,21 +1,23 @@
|
|
1
|
+
import { getParentRecordKey } from './cache';
|
2
|
+
import { NormalizationAstNodes } from './entrypoint';
|
1
3
|
import { Variables } from './FragmentReference';
|
2
4
|
import {
|
5
|
+
assertLink,
|
3
6
|
DataId,
|
4
7
|
IsographEnvironment,
|
5
8
|
IsographStore,
|
6
|
-
ROOT_ID,
|
7
9
|
StoreRecord,
|
8
|
-
|
10
|
+
type Link,
|
11
|
+
type TypeName,
|
9
12
|
} from './IsographEnvironment';
|
10
|
-
import { getParentRecordKey } from './cache';
|
11
|
-
import { NormalizationAst } from './entrypoint';
|
12
13
|
|
13
14
|
export type RetainedQuery = {
|
14
|
-
readonly normalizationAst:
|
15
|
+
readonly normalizationAst: NormalizationAstNodes;
|
15
16
|
readonly variables: {};
|
17
|
+
readonly root: Link;
|
16
18
|
};
|
17
19
|
|
18
|
-
type DidUnretainSomeQuery = boolean;
|
20
|
+
export type DidUnretainSomeQuery = boolean;
|
19
21
|
export function unretainQuery(
|
20
22
|
environment: IsographEnvironment,
|
21
23
|
retainedQuery: RetainedQuery,
|
@@ -42,7 +44,7 @@ export function retainQuery(
|
|
42
44
|
}
|
43
45
|
|
44
46
|
export function garbageCollectEnvironment(environment: IsographEnvironment) {
|
45
|
-
const retainedIds =
|
47
|
+
const retainedIds: RetainedIds = {};
|
46
48
|
|
47
49
|
for (const query of environment.retainedQueries) {
|
48
50
|
recordReachableIds(environment.store, query, retainedIds);
|
@@ -51,32 +53,62 @@ export function garbageCollectEnvironment(environment: IsographEnvironment) {
|
|
51
53
|
recordReachableIds(environment.store, query, retainedIds);
|
52
54
|
}
|
53
55
|
|
54
|
-
for (const
|
55
|
-
|
56
|
-
|
56
|
+
for (const typeName in environment.store) {
|
57
|
+
const dataById = environment.store[typeName];
|
58
|
+
if (dataById == null) continue;
|
59
|
+
const retainedTypeIds = retainedIds[typeName];
|
60
|
+
|
61
|
+
// delete all objects
|
62
|
+
if (retainedTypeIds == undefined || retainedTypeIds.size == 0) {
|
63
|
+
delete environment.store[typeName];
|
64
|
+
continue;
|
65
|
+
}
|
66
|
+
|
67
|
+
for (const dataId in dataById) {
|
68
|
+
if (!retainedTypeIds.has(dataId)) {
|
69
|
+
delete dataById[dataId];
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
if (Object.keys(dataById).length === 0) {
|
74
|
+
delete environment.store[typeName];
|
57
75
|
}
|
58
76
|
}
|
59
77
|
}
|
60
78
|
|
79
|
+
interface RetainedIds {
|
80
|
+
[typeName: TypeName]: Set<DataId>;
|
81
|
+
}
|
82
|
+
|
61
83
|
function recordReachableIds(
|
62
84
|
store: IsographStore,
|
63
85
|
retainedQuery: RetainedQuery,
|
64
|
-
mutableRetainedIds:
|
86
|
+
mutableRetainedIds: RetainedIds,
|
65
87
|
) {
|
66
|
-
|
67
|
-
store
|
68
|
-
|
69
|
-
|
70
|
-
retainedQuery.
|
71
|
-
|
72
|
-
);
|
88
|
+
const record =
|
89
|
+
store[retainedQuery.root.__typename]?.[retainedQuery.root.__link];
|
90
|
+
|
91
|
+
const retainedRecordsIds = (mutableRetainedIds[
|
92
|
+
retainedQuery.root.__typename
|
93
|
+
] ??= new Set());
|
94
|
+
retainedRecordsIds.add(retainedQuery.root.__link);
|
95
|
+
|
96
|
+
if (record) {
|
97
|
+
recordReachableIdsFromRecord(
|
98
|
+
store,
|
99
|
+
record,
|
100
|
+
mutableRetainedIds,
|
101
|
+
retainedQuery.normalizationAst,
|
102
|
+
retainedQuery.variables,
|
103
|
+
);
|
104
|
+
}
|
73
105
|
}
|
74
106
|
|
75
107
|
function recordReachableIdsFromRecord(
|
76
108
|
store: IsographStore,
|
77
109
|
currentRecord: StoreRecord,
|
78
|
-
mutableRetainedIds:
|
79
|
-
selections:
|
110
|
+
mutableRetainedIds: RetainedIds,
|
111
|
+
selections: NormalizationAstNodes,
|
80
112
|
variables: Variables | null,
|
81
113
|
) {
|
82
114
|
for (const selection of selections) {
|
@@ -85,25 +117,44 @@ function recordReachableIdsFromRecord(
|
|
85
117
|
const linkKey = getParentRecordKey(selection, variables ?? {});
|
86
118
|
const linkedFieldOrFields = currentRecord[linkKey];
|
87
119
|
|
88
|
-
const
|
120
|
+
const links: Link[] = [];
|
89
121
|
if (Array.isArray(linkedFieldOrFields)) {
|
90
122
|
for (const maybeLink of linkedFieldOrFields) {
|
91
123
|
const link = assertLink(maybeLink);
|
92
124
|
if (link != null) {
|
93
|
-
|
125
|
+
links.push(link);
|
94
126
|
}
|
95
127
|
}
|
96
128
|
} else {
|
97
129
|
const link = assertLink(linkedFieldOrFields);
|
98
130
|
if (link != null) {
|
99
|
-
|
131
|
+
links.push(link);
|
100
132
|
}
|
101
133
|
}
|
102
134
|
|
103
|
-
|
104
|
-
|
135
|
+
let typeStore =
|
136
|
+
selection.concreteType !== null
|
137
|
+
? store[selection.concreteType]
|
138
|
+
: null;
|
139
|
+
|
140
|
+
if (typeStore == null && selection.concreteType !== null) {
|
141
|
+
continue;
|
142
|
+
}
|
143
|
+
|
144
|
+
for (const nextRecordLink of links) {
|
145
|
+
let __typename = nextRecordLink.__typename;
|
146
|
+
|
147
|
+
const resolvedTypeStore = typeStore ?? store[__typename];
|
148
|
+
|
149
|
+
if (resolvedTypeStore == null) {
|
150
|
+
continue;
|
151
|
+
}
|
152
|
+
|
153
|
+
const nextRecord = resolvedTypeStore[nextRecordLink.__link];
|
105
154
|
if (nextRecord != null) {
|
106
|
-
mutableRetainedIds
|
155
|
+
const retainedRecordsIds = (mutableRetainedIds[__typename] ??=
|
156
|
+
new Set());
|
157
|
+
retainedRecordsIds.add(nextRecordLink.__link);
|
107
158
|
recordReachableIdsFromRecord(
|
108
159
|
store,
|
109
160
|
nextRecord,
|