@isograph/react 0.0.0-main-dc9ca2f6 → 0.0.0-main-905800be
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 +7 -18
- package/dist/IsographEnvironment.js +9 -117
- package/dist/IsographEnvironmentProvider.d.ts +10 -0
- package/dist/IsographEnvironmentProvider.js +42 -0
- package/dist/cache.js +4 -3
- package/dist/entrypoint.d.ts +32 -0
- package/dist/entrypoint.js +8 -0
- package/dist/garbageCollection.d.ts +11 -0
- package/dist/garbageCollection.js +75 -0
- package/dist/index.d.ts +9 -82
- package/dist/index.js +21 -26
- package/dist/reader.d.ts +47 -0
- package/dist/reader.js +3 -0
- package/package.json +3 -3
- package/src/IsographEnvironment.tsx +16 -151
- package/src/IsographEnvironmentProvider.tsx +33 -0
- package/src/cache.tsx +5 -3
- package/src/entrypoint.ts +59 -0
- package/src/garbageCollection.ts +120 -0
- package/src/index.tsx +45 -145
- package/src/reader.ts +65 -0
package/dist/reader.js
ADDED
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-905800be",
|
|
4
4
|
"description": "Use Isograph with React",
|
|
5
5
|
"homepage": "https://isograph.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"prepack": "yarn run test && yarn run compile"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@isograph/disposable-types": "0.0.0-main-
|
|
20
|
-
"@isograph/react-disposable-state": "0.0.0-main-
|
|
19
|
+
"@isograph/disposable-types": "0.0.0-main-905800be",
|
|
20
|
+
"@isograph/react-disposable-state": "0.0.0-main-905800be",
|
|
21
21
|
"react": "^18.2.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
@@ -1,11 +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
|
-
import {
|
|
5
|
-
import { getParentRecordKey } from './cache';
|
|
6
|
-
|
|
7
|
-
export const IsographEnvironmentContext =
|
|
8
|
-
createContext<IsographEnvironment | null>(null);
|
|
2
|
+
import { RetainedQuery } from './garbageCollection';
|
|
9
3
|
|
|
10
4
|
type ComponentName = string;
|
|
11
5
|
type StringifiedArgs = string;
|
|
@@ -18,11 +12,6 @@ type ComponentCache = {
|
|
|
18
12
|
export type Subscriptions = Set<() => void>;
|
|
19
13
|
type SuspenseCache = { [index: string]: ParentCache<any> };
|
|
20
14
|
|
|
21
|
-
export type RetainedQuery = {
|
|
22
|
-
normalizationAst: NormalizationAst;
|
|
23
|
-
variables: {};
|
|
24
|
-
};
|
|
25
|
-
|
|
26
15
|
export type IsographEnvironment = {
|
|
27
16
|
store: IsographStore;
|
|
28
17
|
networkFunction: IsographNetworkFunction;
|
|
@@ -51,6 +40,7 @@ export type IsographNetworkFunction = (
|
|
|
51
40
|
export type Link = {
|
|
52
41
|
__link: DataId;
|
|
53
42
|
};
|
|
43
|
+
|
|
54
44
|
export type DataTypeValue =
|
|
55
45
|
// N.B. undefined is here to support optional id's, but
|
|
56
46
|
// undefined should not *actually* be present in the store.
|
|
@@ -81,33 +71,6 @@ export type IsographStore = {
|
|
|
81
71
|
__ROOT: StoreRecord;
|
|
82
72
|
};
|
|
83
73
|
|
|
84
|
-
export type IsographEnvironmentProviderProps = {
|
|
85
|
-
environment: IsographEnvironment;
|
|
86
|
-
children: ReactNode;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export function IsographEnvironmentProvider({
|
|
90
|
-
environment,
|
|
91
|
-
children,
|
|
92
|
-
}: IsographEnvironmentProviderProps) {
|
|
93
|
-
return (
|
|
94
|
-
<IsographEnvironmentContext.Provider value={environment}>
|
|
95
|
-
{children}
|
|
96
|
-
</IsographEnvironmentContext.Provider>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function useIsographEnvironment(): IsographEnvironment {
|
|
101
|
-
const context = useContext(IsographEnvironmentContext);
|
|
102
|
-
if (context == null) {
|
|
103
|
-
throw new Error(
|
|
104
|
-
'Unexpected null environment context. Make sure to render ' +
|
|
105
|
-
'this component within an IsographEnvironmentProvider component',
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
return context;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
74
|
const DEFAULT_GC_BUFFER_SIZE = 10;
|
|
112
75
|
export function createIsographEnvironment(
|
|
113
76
|
store: IsographStore,
|
|
@@ -133,118 +96,20 @@ export function createIsographStore() {
|
|
|
133
96
|
};
|
|
134
97
|
}
|
|
135
98
|
|
|
136
|
-
export function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function recordReachableIds(
|
|
154
|
-
store: IsographStore,
|
|
155
|
-
retainedQuery: RetainedQuery,
|
|
156
|
-
mutableRetainedIds: Set<DataId>,
|
|
157
|
-
) {
|
|
158
|
-
recordReachableIdsFromRecord(
|
|
159
|
-
store,
|
|
160
|
-
store[ROOT_ID],
|
|
161
|
-
mutableRetainedIds,
|
|
162
|
-
retainedQuery.normalizationAst,
|
|
163
|
-
retainedQuery.variables,
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function getLinkedId(data: Exclude<DataTypeValue, null | void>): string {
|
|
168
|
-
// @ts-expect-error
|
|
169
|
-
if (data.__link != null) {
|
|
170
|
-
// @ts-expect-error
|
|
171
|
-
return data.__link;
|
|
172
|
-
} else {
|
|
173
|
-
throw new Error('Record in an invalid state');
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function recordReachableIdsFromRecord(
|
|
178
|
-
store: IsographStore,
|
|
179
|
-
currentRecord: StoreRecord,
|
|
180
|
-
mutableRetainedIds: Set<DataId>,
|
|
181
|
-
selections: NormalizationAst,
|
|
182
|
-
variables: { [index: string]: string } | null,
|
|
183
|
-
) {
|
|
184
|
-
for (const selection of selections) {
|
|
185
|
-
switch (selection.kind) {
|
|
186
|
-
case 'Linked':
|
|
187
|
-
const linkKey = getParentRecordKey(selection, variables ?? {});
|
|
188
|
-
const linkedFieldOrFields = currentRecord[linkKey];
|
|
189
|
-
|
|
190
|
-
const ids = [];
|
|
191
|
-
if (Array.isArray(linkedFieldOrFields)) {
|
|
192
|
-
for (const link of linkedFieldOrFields) {
|
|
193
|
-
if (link != null) {
|
|
194
|
-
const id = getLinkedId(link);
|
|
195
|
-
ids.push(id);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
} else {
|
|
199
|
-
if (linkedFieldOrFields != null) {
|
|
200
|
-
const id = getLinkedId(linkedFieldOrFields);
|
|
201
|
-
ids.push(id);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
for (const nextRecordId of ids) {
|
|
206
|
-
const nextRecord = store[nextRecordId];
|
|
207
|
-
if (nextRecord != null) {
|
|
208
|
-
mutableRetainedIds.add(nextRecordId);
|
|
209
|
-
recordReachableIdsFromRecord(
|
|
210
|
-
store,
|
|
211
|
-
nextRecord,
|
|
212
|
-
mutableRetainedIds,
|
|
213
|
-
selection.selections,
|
|
214
|
-
variables,
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
continue;
|
|
220
|
-
case 'Scalar':
|
|
221
|
-
continue;
|
|
99
|
+
export function defaultMissingFieldHandler(
|
|
100
|
+
_storeRecord: StoreRecord,
|
|
101
|
+
_root: DataId,
|
|
102
|
+
fieldName: string,
|
|
103
|
+
arguments_: { [index: string]: any } | null,
|
|
104
|
+
variables: { [index: string]: any } | null,
|
|
105
|
+
): Link | undefined {
|
|
106
|
+
if (fieldName === 'node' || fieldName === 'user') {
|
|
107
|
+
const variable = arguments_?.['id'];
|
|
108
|
+
const value = variables?.[variable];
|
|
109
|
+
|
|
110
|
+
// TODO can we handle explicit nulls here too? Probably, after wrapping in objects
|
|
111
|
+
if (value != null) {
|
|
112
|
+
return { __link: value };
|
|
222
113
|
}
|
|
223
114
|
}
|
|
224
115
|
}
|
|
225
|
-
|
|
226
|
-
type DidUnretainSomeQuery = boolean;
|
|
227
|
-
export function unretainQuery(
|
|
228
|
-
environment: IsographEnvironment,
|
|
229
|
-
retainedQuery: RetainedQuery,
|
|
230
|
-
): DidUnretainSomeQuery {
|
|
231
|
-
environment.retainedQueries.delete(retainedQuery);
|
|
232
|
-
environment.gcBuffer.push(retainedQuery);
|
|
233
|
-
|
|
234
|
-
if (environment.gcBuffer.length > environment.gcBufferSize) {
|
|
235
|
-
environment.gcBuffer.shift();
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export function retainQuery(
|
|
243
|
-
environment: IsographEnvironment,
|
|
244
|
-
queryToRetain: RetainedQuery,
|
|
245
|
-
) {
|
|
246
|
-
environment.retainedQueries.add(queryToRetain);
|
|
247
|
-
// TODO can we remove this query from the buffer somehow?
|
|
248
|
-
// We are relying on === equality, but we really should be comparing
|
|
249
|
-
// id + variables
|
|
250
|
-
}
|
|
@@ -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
|
@@ -21,11 +21,13 @@ import {
|
|
|
21
21
|
StoreRecord,
|
|
22
22
|
Link,
|
|
23
23
|
type IsographEnvironment,
|
|
24
|
-
|
|
24
|
+
} from './IsographEnvironment';
|
|
25
|
+
import {
|
|
25
26
|
RetainedQuery,
|
|
26
|
-
|
|
27
|
+
garbageCollectEnvironment,
|
|
27
28
|
retainQuery,
|
|
28
|
-
|
|
29
|
+
unretainQuery,
|
|
30
|
+
} from './garbageCollection';
|
|
29
31
|
|
|
30
32
|
declare global {
|
|
31
33
|
interface Window {
|
|
@@ -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,120 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataId,
|
|
3
|
+
IsographEnvironment,
|
|
4
|
+
IsographStore,
|
|
5
|
+
ROOT_ID,
|
|
6
|
+
StoreRecord,
|
|
7
|
+
} from './IsographEnvironment';
|
|
8
|
+
import { NormalizationAst, assertLink } from './index';
|
|
9
|
+
import { getParentRecordKey } from './cache';
|
|
10
|
+
|
|
11
|
+
export type RetainedQuery = {
|
|
12
|
+
normalizationAst: NormalizationAst;
|
|
13
|
+
variables: {};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type DidUnretainSomeQuery = boolean;
|
|
17
|
+
export function unretainQuery(
|
|
18
|
+
environment: IsographEnvironment,
|
|
19
|
+
retainedQuery: RetainedQuery,
|
|
20
|
+
): DidUnretainSomeQuery {
|
|
21
|
+
environment.retainedQueries.delete(retainedQuery);
|
|
22
|
+
environment.gcBuffer.push(retainedQuery);
|
|
23
|
+
|
|
24
|
+
if (environment.gcBuffer.length > environment.gcBufferSize) {
|
|
25
|
+
environment.gcBuffer.shift();
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function retainQuery(
|
|
33
|
+
environment: IsographEnvironment,
|
|
34
|
+
queryToRetain: RetainedQuery,
|
|
35
|
+
) {
|
|
36
|
+
environment.retainedQueries.add(queryToRetain);
|
|
37
|
+
// TODO can we remove this query from the buffer somehow?
|
|
38
|
+
// We are relying on === equality, but we really should be comparing
|
|
39
|
+
// id + variables
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function garbageCollectEnvironment(environment: IsographEnvironment) {
|
|
43
|
+
const retainedIds = new Set<DataId>([ROOT_ID]);
|
|
44
|
+
|
|
45
|
+
for (const query of environment.retainedQueries) {
|
|
46
|
+
recordReachableIds(environment.store, query, retainedIds);
|
|
47
|
+
}
|
|
48
|
+
for (const query of environment.gcBuffer) {
|
|
49
|
+
recordReachableIds(environment.store, query, retainedIds);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const dataId in environment.store) {
|
|
53
|
+
if (!retainedIds.has(dataId)) {
|
|
54
|
+
delete environment.store[dataId];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function recordReachableIds(
|
|
60
|
+
store: IsographStore,
|
|
61
|
+
retainedQuery: RetainedQuery,
|
|
62
|
+
mutableRetainedIds: Set<DataId>,
|
|
63
|
+
) {
|
|
64
|
+
recordReachableIdsFromRecord(
|
|
65
|
+
store,
|
|
66
|
+
store[ROOT_ID],
|
|
67
|
+
mutableRetainedIds,
|
|
68
|
+
retainedQuery.normalizationAst,
|
|
69
|
+
retainedQuery.variables,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function recordReachableIdsFromRecord(
|
|
74
|
+
store: IsographStore,
|
|
75
|
+
currentRecord: StoreRecord,
|
|
76
|
+
mutableRetainedIds: Set<DataId>,
|
|
77
|
+
selections: NormalizationAst,
|
|
78
|
+
variables: { [index: string]: string } | null,
|
|
79
|
+
) {
|
|
80
|
+
for (const selection of selections) {
|
|
81
|
+
switch (selection.kind) {
|
|
82
|
+
case 'Linked':
|
|
83
|
+
const linkKey = getParentRecordKey(selection, variables ?? {});
|
|
84
|
+
const linkedFieldOrFields = currentRecord[linkKey];
|
|
85
|
+
|
|
86
|
+
const ids = [];
|
|
87
|
+
if (Array.isArray(linkedFieldOrFields)) {
|
|
88
|
+
for (const maybeLink of linkedFieldOrFields) {
|
|
89
|
+
const link = assertLink(maybeLink);
|
|
90
|
+
if (link != null) {
|
|
91
|
+
ids.push(link.__link);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
const link = assertLink(linkedFieldOrFields);
|
|
96
|
+
if (link != null) {
|
|
97
|
+
ids.push(link.__link);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const nextRecordId of ids) {
|
|
102
|
+
const nextRecord = store[nextRecordId];
|
|
103
|
+
if (nextRecord != null) {
|
|
104
|
+
mutableRetainedIds.add(nextRecordId);
|
|
105
|
+
recordReachableIdsFromRecord(
|
|
106
|
+
store,
|
|
107
|
+
nextRecord,
|
|
108
|
+
mutableRetainedIds,
|
|
109
|
+
selection.selections,
|
|
110
|
+
variables,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
continue;
|
|
116
|
+
case 'Scalar':
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -13,141 +13,70 @@ import {
|
|
|
13
13
|
IsographEnvironment,
|
|
14
14
|
Link,
|
|
15
15
|
ROOT_ID,
|
|
16
|
-
|
|
17
|
-
useIsographEnvironment,
|
|
16
|
+
defaultMissingFieldHandler,
|
|
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,
|
|
46
|
+
defaultMissingFieldHandler,
|
|
38
47
|
} from './IsographEnvironment';
|
|
48
|
+
export {
|
|
49
|
+
IsographEnvironmentProvider,
|
|
50
|
+
useIsographEnvironment,
|
|
51
|
+
type IsographEnvironmentProviderProps,
|
|
52
|
+
} from './IsographEnvironmentProvider';
|
|
39
53
|
export { useImperativeReference } from './useImperativeReference';
|
|
40
54
|
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[];
|
|
55
|
+
export {
|
|
56
|
+
type ReaderArtifact,
|
|
57
|
+
ReaderAst,
|
|
58
|
+
ReaderAstNode,
|
|
59
|
+
ReaderLinkedField,
|
|
60
|
+
ReaderMutationField,
|
|
61
|
+
ReaderRefetchField,
|
|
62
|
+
ReaderResolverField,
|
|
63
|
+
ReaderResolverVariant,
|
|
64
|
+
ReaderScalarField,
|
|
65
|
+
} from './reader';
|
|
66
|
+
export {
|
|
67
|
+
NormalizationAst,
|
|
68
|
+
NormalizationAstNode,
|
|
69
|
+
NormalizationLinkedField,
|
|
70
|
+
NormalizationScalarField,
|
|
71
|
+
IsographEntrypoint,
|
|
72
|
+
assertIsEntrypoint,
|
|
73
|
+
RefetchQueryArtifact,
|
|
74
|
+
RefetchQueryArtifactWrapper,
|
|
75
|
+
} from './entrypoint';
|
|
72
76
|
|
|
73
77
|
export type ExtractSecondParam<T extends (arg1: any, arg2: any) => any> =
|
|
74
78
|
T extends (arg1: any, arg2: infer P) => any ? P : never;
|
|
75
79
|
|
|
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
|
-
export type NormalizationAst = NormalizationAstNode[];
|
|
124
|
-
|
|
125
|
-
export type NormalizationScalarField = {
|
|
126
|
-
kind: 'Scalar';
|
|
127
|
-
fieldName: string;
|
|
128
|
-
arguments: Arguments | null;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export type NormalizationLinkedField = {
|
|
132
|
-
kind: 'Linked';
|
|
133
|
-
fieldName: string;
|
|
134
|
-
arguments: Arguments | null;
|
|
135
|
-
selections: NormalizationAst;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// This is more like an entrypoint, but one specifically for a refetch query/mutation
|
|
139
|
-
export type RefetchQueryArtifact = {
|
|
140
|
-
kind: 'RefetchQuery';
|
|
141
|
-
queryText: string;
|
|
142
|
-
normalizationAst: NormalizationAst;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
// TODO rename
|
|
146
|
-
export type RefetchQueryArtifactWrapper = {
|
|
147
|
-
artifact: RefetchQueryArtifact;
|
|
148
|
-
allowedVariables: string[];
|
|
149
|
-
};
|
|
150
|
-
|
|
151
80
|
export type Arguments = Argument[];
|
|
152
81
|
export type Argument = [ArgumentName, ArgumentValue];
|
|
153
82
|
export type ArgumentName = string;
|
|
@@ -176,17 +105,6 @@ export type FragmentReference<
|
|
|
176
105
|
nestedRefetchQueries: RefetchQueryArtifactWrapper[];
|
|
177
106
|
};
|
|
178
107
|
|
|
179
|
-
function assertIsEntrypoint<TReadFromStore extends Object, TResolverResult>(
|
|
180
|
-
value:
|
|
181
|
-
| IsographEntrypoint<TReadFromStore, TResolverResult>
|
|
182
|
-
| ((_: any) => any)
|
|
183
|
-
// Temporarily, allow any here. Once we automatically provide
|
|
184
|
-
// types to entrypoints, we probably don't need this.
|
|
185
|
-
| any,
|
|
186
|
-
): asserts value is IsographEntrypoint<TReadFromStore, TResolverResult> {
|
|
187
|
-
if (typeof value === 'function') throw new Error('Not a string');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
108
|
export type ExtractReadFromStore<Type> =
|
|
191
109
|
Type extends IsographEntrypoint<infer X, any> ? X : never;
|
|
192
110
|
export type ExtractResolverResult<Type> =
|
|
@@ -569,34 +487,16 @@ function readData<TReadFromStore>(
|
|
|
569
487
|
return { kind: 'Success', data: target as any };
|
|
570
488
|
}
|
|
571
489
|
|
|
572
|
-
export function
|
|
573
|
-
_storeRecord: StoreRecord,
|
|
574
|
-
_root: DataId,
|
|
575
|
-
fieldName: string,
|
|
576
|
-
arguments_: { [index: string]: any } | null,
|
|
577
|
-
variables: { [index: string]: any } | null,
|
|
578
|
-
): Link | undefined {
|
|
579
|
-
if (fieldName === 'node' || fieldName === 'user') {
|
|
580
|
-
const variable = arguments_?.['id'];
|
|
581
|
-
const value = variables?.[variable];
|
|
582
|
-
|
|
583
|
-
// TODO can we handle explicit nulls here too? Probably, after wrapping in objects
|
|
584
|
-
if (value != null) {
|
|
585
|
-
return { __link: value };
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function assertLink(link: DataTypeValue): Link | undefined | null {
|
|
490
|
+
export function assertLink(link: DataTypeValue): Link | null {
|
|
591
491
|
if (Array.isArray(link)) {
|
|
592
492
|
throw new Error('Unexpected array');
|
|
593
493
|
}
|
|
494
|
+
if (link == null) {
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
594
497
|
if (typeof link === 'object') {
|
|
595
498
|
return link;
|
|
596
499
|
}
|
|
597
|
-
if (link === undefined) {
|
|
598
|
-
return undefined;
|
|
599
|
-
}
|
|
600
500
|
throw new Error('Invalid link');
|
|
601
501
|
}
|
|
602
502
|
|