@isograph/react 0.0.0-main-4ef7c123

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/index.js ADDED
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getRefReaderForName = exports.setMissingFieldHandler = exports.defaultMissingFieldHandler = exports.readButDoNotEvaluate = exports.read = exports.useLazyReference = exports.iso = exports.isoFetch = exports.subscribe = exports.makeNetworkRequest = exports.setNetwork = void 0;
7
+ const cache_1 = require("./cache");
8
+ const react_disposable_state_1 = require("@isograph/react-disposable-state");
9
+ const react_1 = __importDefault(require("react"));
10
+ var cache_2 = require("./cache");
11
+ Object.defineProperty(exports, "setNetwork", { enumerable: true, get: function () { return cache_2.setNetwork; } });
12
+ Object.defineProperty(exports, "makeNetworkRequest", { enumerable: true, get: function () { return cache_2.makeNetworkRequest; } });
13
+ Object.defineProperty(exports, "subscribe", { enumerable: true, get: function () { return cache_2.subscribe; } });
14
+ function isoFetch(_text) {
15
+ return void 0;
16
+ }
17
+ exports.isoFetch = isoFetch;
18
+ function iso(_queryText) {
19
+ // The name `identity` here is a bit of a double entendre.
20
+ // First, it is the identity function, constrained to operate
21
+ // on a very specific type. Thus, the value of b Declare`...`(
22
+ // someFunction) is someFunction. But furthermore, if one
23
+ // write b Declare`...` and passes no function, the resolver itself
24
+ // is the identity function. At that point, the types
25
+ // TResolverParameter and TResolverReturn must be identical.
26
+ return function identity(x) {
27
+ return x;
28
+ };
29
+ }
30
+ exports.iso = iso;
31
+ function useLazyReference(fetchableResolverArtifact, variables) {
32
+ // Typechecking fails here... TODO investigate
33
+ const cache = (0, cache_1.getOrCreateCacheForArtifact)(fetchableResolverArtifact, variables);
34
+ // TODO add comment explaining why we never use this value
35
+ // @ts-ignore
36
+ const data =
37
+ // @ts-ignore
38
+ (0, react_disposable_state_1.useLazyDisposableState)(cache).state;
39
+ return {
40
+ queryReference: {
41
+ kind: "FragmentReference",
42
+ readerArtifact: fetchableResolverArtifact.readerArtifact,
43
+ root: cache_1.ROOT_ID,
44
+ variables,
45
+ nestedRefetchQueries: fetchableResolverArtifact.nestedRefetchQueries,
46
+ },
47
+ };
48
+ }
49
+ exports.useLazyReference = useLazyReference;
50
+ function read(fragmentReference) {
51
+ var _a;
52
+ const variant = fragmentReference.readerArtifact.variant;
53
+ if (variant.kind === "Eager") {
54
+ const data = readData(fragmentReference.readerArtifact.readerAst, fragmentReference.root, (_a = fragmentReference.variables) !== null && _a !== void 0 ? _a : {}, fragmentReference.nestedRefetchQueries);
55
+ if (data.kind === "MissingData") {
56
+ throw (0, cache_1.onNextChange)();
57
+ }
58
+ else {
59
+ return fragmentReference.readerArtifact.resolver(data.data);
60
+ }
61
+ }
62
+ else if (variant.kind === "Component") {
63
+ return (additionalRuntimeProps) => {
64
+ // TODO also incorporate the typename
65
+ const RefReaderForName = getRefReaderForName(variant.componentName);
66
+ // TODO do not create a new reference on every render?
67
+ return (react_1.default.createElement(RefReaderForName, { reference: {
68
+ kind: "FragmentReference",
69
+ readerArtifact: fragmentReference.readerArtifact,
70
+ root: fragmentReference.root,
71
+ variables: fragmentReference.variables,
72
+ nestedRefetchQueries: fragmentReference.nestedRefetchQueries,
73
+ }, additionalRuntimeProps: additionalRuntimeProps }));
74
+ };
75
+ }
76
+ // Why can't Typescript realize that this is unreachable??
77
+ throw new Error("This is unreachable");
78
+ }
79
+ exports.read = read;
80
+ function readButDoNotEvaluate(reference) {
81
+ var _a;
82
+ const response = readData(reference.readerArtifact.readerAst, reference.root, (_a = reference.variables) !== null && _a !== void 0 ? _a : {}, reference.nestedRefetchQueries);
83
+ console.log("done reading but not evaluating", { response });
84
+ if (response.kind === "MissingData") {
85
+ throw (0, cache_1.onNextChange)();
86
+ }
87
+ else {
88
+ return response.data;
89
+ }
90
+ }
91
+ exports.readButDoNotEvaluate = readButDoNotEvaluate;
92
+ function readData(ast, root, variables, nestedRefetchQueries) {
93
+ var _a, _b, _c, _d;
94
+ let storeRecord = cache_1.store[root];
95
+ if (storeRecord === undefined) {
96
+ return { kind: "MissingData", reason: "No record for root " + root };
97
+ }
98
+ if (storeRecord === null) {
99
+ return { kind: "Success", data: null };
100
+ }
101
+ let target = {};
102
+ for (const field of ast) {
103
+ switch (field.kind) {
104
+ case "Scalar": {
105
+ const storeRecordName = (0, cache_1.getParentRecordKey)(field, variables);
106
+ const value = storeRecord[storeRecordName];
107
+ // TODO consider making scalars into discriminated unions. This probably has
108
+ // to happen for when we handle errors.
109
+ if (value === undefined) {
110
+ return {
111
+ kind: "MissingData",
112
+ reason: "No value for " + storeRecordName + " on root " + root,
113
+ };
114
+ }
115
+ target[(_a = field.alias) !== null && _a !== void 0 ? _a : field.fieldName] = value;
116
+ break;
117
+ }
118
+ case "Linked": {
119
+ const storeRecordName = (0, cache_1.getParentRecordKey)(field, variables);
120
+ const value = storeRecord[storeRecordName];
121
+ if (Array.isArray(value)) {
122
+ const results = [];
123
+ for (const item of value) {
124
+ const link = assertLink(item);
125
+ if (link === undefined) {
126
+ return {
127
+ kind: "MissingData",
128
+ reason: "No link for " +
129
+ storeRecordName +
130
+ " on root " +
131
+ root +
132
+ ". Link is " +
133
+ JSON.stringify(item),
134
+ };
135
+ }
136
+ else if (link === null) {
137
+ results.push(null);
138
+ continue;
139
+ }
140
+ const result = readData(field.selections, link.__link, variables, nestedRefetchQueries);
141
+ if (result.kind === "MissingData") {
142
+ return {
143
+ kind: "MissingData",
144
+ reason: "Missing data for " +
145
+ storeRecordName +
146
+ " on root " +
147
+ root +
148
+ ". Link is " +
149
+ JSON.stringify(item),
150
+ nestedReason: result,
151
+ };
152
+ }
153
+ results.push(result.data);
154
+ }
155
+ target[(_b = field.alias) !== null && _b !== void 0 ? _b : field.fieldName] = results;
156
+ break;
157
+ }
158
+ let link = assertLink(value);
159
+ if (link === undefined) {
160
+ // TODO make this configurable, and also generated and derived from the schema
161
+ const altLink = missingFieldHandler(storeRecord, root, field.fieldName, field.arguments, variables);
162
+ if (altLink === undefined) {
163
+ return {
164
+ kind: "MissingData",
165
+ reason: "No link for " +
166
+ storeRecordName +
167
+ " on root " +
168
+ root +
169
+ ". Link is " +
170
+ JSON.stringify(value),
171
+ };
172
+ }
173
+ else {
174
+ link = altLink;
175
+ }
176
+ }
177
+ else if (link === null) {
178
+ target[(_c = field.alias) !== null && _c !== void 0 ? _c : field.fieldName] = null;
179
+ break;
180
+ }
181
+ const targetId = link.__link;
182
+ const data = readData(field.selections, targetId, variables, nestedRefetchQueries);
183
+ if (data.kind === "MissingData") {
184
+ return {
185
+ kind: "MissingData",
186
+ reason: "Missing data for " + storeRecordName + " on root " + root,
187
+ nestedReason: data,
188
+ };
189
+ }
190
+ target[(_d = field.alias) !== null && _d !== void 0 ? _d : field.fieldName] = data.data;
191
+ break;
192
+ }
193
+ case "RefetchField": {
194
+ const data = readData(field.readerArtifact.readerAst, root, variables,
195
+ // Refetch fields just read the id, and don't need refetch query artifacts
196
+ []);
197
+ console.log("refetch field data", data, field);
198
+ if (data.kind === "MissingData") {
199
+ return {
200
+ kind: "MissingData",
201
+ reason: "Missing data for " + field.alias + " on root " + root,
202
+ nestedReason: data,
203
+ };
204
+ }
205
+ else {
206
+ const refetchQueryIndex = field.refetchQuery;
207
+ if (refetchQueryIndex == null) {
208
+ throw new Error("refetchQuery is null in RefetchField");
209
+ }
210
+ const refetchQuery = nestedRefetchQueries[refetchQueryIndex];
211
+ const refetchQueryArtifact = refetchQuery.artifact;
212
+ const allowedVariables = refetchQuery.allowedVariables;
213
+ target[field.alias] = field.readerArtifact.resolver(refetchQueryArtifact, Object.assign(Object.assign({}, data.data), filterVariables(variables, allowedVariables)));
214
+ }
215
+ break;
216
+ }
217
+ case "MutationField": {
218
+ const data = readData(field.readerArtifact.readerAst, root, variables,
219
+ // Refetch fields just read the id, and don't need refetch query artifacts
220
+ []);
221
+ console.log("refetch field data", data, field);
222
+ if (data.kind === "MissingData") {
223
+ return {
224
+ kind: "MissingData",
225
+ reason: "Missing data for " + field.alias + " on root " + root,
226
+ nestedReason: data,
227
+ };
228
+ }
229
+ else {
230
+ const refetchQueryIndex = field.refetchQuery;
231
+ if (refetchQueryIndex == null) {
232
+ throw new Error("refetchQuery is null in MutationField");
233
+ }
234
+ const refetchQuery = nestedRefetchQueries[refetchQueryIndex];
235
+ const refetchQueryArtifact = refetchQuery.artifact;
236
+ const allowedVariables = refetchQuery.allowedVariables;
237
+ target[field.alias] = field.readerArtifact.resolver(refetchQueryArtifact, data.data, filterVariables(variables, allowedVariables));
238
+ }
239
+ break;
240
+ }
241
+ case "Resolver": {
242
+ const usedRefetchQueries = field.usedRefetchQueries;
243
+ const resolverRefetchQueries = usedRefetchQueries.map((index) => nestedRefetchQueries[index]);
244
+ const variant = field.readerArtifact.variant;
245
+ if (variant.kind === "Eager") {
246
+ const data = readData(field.readerArtifact.readerAst, root, variables, resolverRefetchQueries);
247
+ if (data.kind === "MissingData") {
248
+ return {
249
+ kind: "MissingData",
250
+ reason: "Missing data for " + field.alias + " on root " + root,
251
+ nestedReason: data,
252
+ };
253
+ }
254
+ else {
255
+ target[field.alias] = field.readerArtifact.resolver(data.data);
256
+ }
257
+ }
258
+ else if (variant.kind === "Component") {
259
+ target[field.alias] = (additionalRuntimeProps) => {
260
+ // TODO also incorporate the typename
261
+ const RefReaderForName = getRefReaderForName(variant.componentName);
262
+ // TODO do not create a new reference on every render?
263
+ return (react_1.default.createElement(RefReaderForName, { reference: {
264
+ kind: "FragmentReference",
265
+ readerArtifact: field.readerArtifact,
266
+ root,
267
+ variables,
268
+ nestedRefetchQueries: resolverRefetchQueries,
269
+ }, additionalRuntimeProps: additionalRuntimeProps }));
270
+ };
271
+ }
272
+ break;
273
+ }
274
+ }
275
+ }
276
+ return { kind: "Success", data: target };
277
+ }
278
+ let customMissingFieldHandler = null;
279
+ function missingFieldHandler(storeRecord, root, fieldName, arguments_, variables) {
280
+ if (customMissingFieldHandler != null) {
281
+ return customMissingFieldHandler(storeRecord, root, fieldName, arguments_, variables);
282
+ }
283
+ else {
284
+ return defaultMissingFieldHandler(storeRecord, root, fieldName, arguments_, variables);
285
+ }
286
+ }
287
+ function defaultMissingFieldHandler(storeRecord, root, fieldName, arguments_, variables) {
288
+ if (fieldName === "node" || fieldName === "user") {
289
+ const variable = arguments_ === null || arguments_ === void 0 ? void 0 : arguments_["id"];
290
+ const value = variables === null || variables === void 0 ? void 0 : variables[variable];
291
+ // TODO can we handle explicit nulls here too? Probably, after wrapping in objects
292
+ if (value != null) {
293
+ return { __link: value };
294
+ }
295
+ }
296
+ }
297
+ exports.defaultMissingFieldHandler = defaultMissingFieldHandler;
298
+ function setMissingFieldHandler(handler) {
299
+ customMissingFieldHandler = handler;
300
+ }
301
+ exports.setMissingFieldHandler = setMissingFieldHandler;
302
+ function assertLink(link) {
303
+ if (Array.isArray(link)) {
304
+ throw new Error("Unexpected array");
305
+ }
306
+ if (typeof link === "object") {
307
+ return link;
308
+ }
309
+ if (link === undefined) {
310
+ return undefined;
311
+ }
312
+ throw new Error("Invalid link");
313
+ }
314
+ const refReaders = {};
315
+ function getRefReaderForName(name) {
316
+ if (refReaders[name] == null) {
317
+ function Component({ reference, additionalRuntimeProps, }) {
318
+ const data = readButDoNotEvaluate(reference);
319
+ return reference.readerArtifact.resolver(Object.assign({ data }, additionalRuntimeProps));
320
+ }
321
+ Component.displayName = `${name} @component`;
322
+ refReaders[name] = Component;
323
+ }
324
+ return refReaders[name];
325
+ }
326
+ exports.getRefReaderForName = getRefReaderForName;
327
+ function filterVariables(variables, allowedVariables) {
328
+ const result = {};
329
+ for (const key of allowedVariables) {
330
+ result[key] = variables[key];
331
+ }
332
+ return result;
333
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@isograph/react",
3
+ "version": "0.0.0-main-4ef7c123",
4
+ "description": "Use Isograph with React",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "author": "Isograph Labs",
8
+ "license": "MIT",
9
+ "scripts": {
10
+ "compile": "rm -rf dist/* && (tsc -p tsconfig.pkg.json || exit 0)",
11
+ "compile-watch": "tsc -p tsconfig.pkg.json --watch",
12
+ "test": "echo no tests yet",
13
+ "test-watch": "vitest watch",
14
+ "coverage": "vitest run --coverage",
15
+ "prepack": "yarn run test && yarn run compile"
16
+ },
17
+ "dependencies": {
18
+ "@isograph/disposable-types": "0.0.0-main-4ef7c123",
19
+ "@isograph/react-disposable-state": "0.0.0-main-4ef7c123",
20
+ "react": "^18.2.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^18.0.31",
24
+ "react-test-renderer": "^18.2.0",
25
+ "vitest": "^0.29.8",
26
+ "typescript": "^5.0.3"
27
+ }
28
+ }
@@ -0,0 +1,29 @@
1
+ const NOT_SET: Symbol = Symbol("NOT_SET");
2
+ type NotSet = typeof NOT_SET;
3
+
4
+ /**
5
+ * Invariant:
6
+ * Before the promise is resolved, value becomes non-null.
7
+ */
8
+ export type PromiseWrapper<T> = {
9
+ promise: Promise<T>;
10
+ value: Exclude<T, NotSet> | NotSet;
11
+ };
12
+
13
+ export function wrapPromise<T>(promise: Promise<T>): PromiseWrapper<T> {
14
+ // TODO confirm suspense works if the promise is already resolved.
15
+ const wrapper: PromiseWrapper<T> = { promise, value: NOT_SET };
16
+ promise.then((v) => {
17
+ // T is assignable to Exclude<T, Symbol> | Symbol
18
+ wrapper.value = v as any;
19
+ });
20
+ return wrapper;
21
+ }
22
+
23
+ export function useReadPromise<T>(p: PromiseWrapper<T>): T {
24
+ if (p.value !== NOT_SET) {
25
+ // Safety: p.value is either NOT_SET or an actual value.
26
+ return p.value as any;
27
+ }
28
+ throw p.promise;
29
+ }