@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.
@@ -0,0 +1,13 @@
1
+ declare const NOT_SET: Symbol;
2
+ type NotSet = typeof NOT_SET;
3
+ /**
4
+ * Invariant:
5
+ * Before the promise is resolved, value becomes non-null.
6
+ */
7
+ export type PromiseWrapper<T> = {
8
+ promise: Promise<T>;
9
+ value: Exclude<T, NotSet> | NotSet;
10
+ };
11
+ export declare function wrapPromise<T>(promise: Promise<T>): PromiseWrapper<T>;
12
+ export declare function useReadPromise<T>(p: PromiseWrapper<T>): T;
13
+ export {};
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useReadPromise = exports.wrapPromise = void 0;
4
+ const NOT_SET = Symbol("NOT_SET");
5
+ function wrapPromise(promise) {
6
+ // TODO confirm suspense works if the promise is already resolved.
7
+ const wrapper = { promise, value: NOT_SET };
8
+ promise.then((v) => {
9
+ // T is assignable to Exclude<T, Symbol> | Symbol
10
+ wrapper.value = v;
11
+ });
12
+ return wrapper;
13
+ }
14
+ exports.wrapPromise = wrapPromise;
15
+ function useReadPromise(p) {
16
+ if (p.value !== NOT_SET) {
17
+ // Safety: p.value is either NOT_SET or an actual value.
18
+ return p.value;
19
+ }
20
+ throw p.promise;
21
+ }
22
+ exports.useReadPromise = useReadPromise;
@@ -0,0 +1,30 @@
1
+ import { ItemCleanupPair, ParentCache } from "@isograph/react-disposable-state";
2
+ import { PromiseWrapper } from "./PromiseWrapper";
3
+ import { IsographFetchableResolver, NormalizationLinkedField, NormalizationScalarField, ReaderLinkedField, ReaderScalarField } from "./index";
4
+ type IsoResolver = IsographFetchableResolver<any, any, any>;
5
+ export declare function getOrCreateCacheForArtifact<T>(artifact: IsoResolver, variables: object): ParentCache<PromiseWrapper<T>>;
6
+ declare let network: ((queryText: string, variables: object) => Promise<any>) | null;
7
+ export declare function setNetwork(newNetwork: typeof network): void;
8
+ export declare function makeNetworkRequest<T>(artifact: IsoResolver, variables: object): ItemCleanupPair<PromiseWrapper<T>>;
9
+ export type Link = {
10
+ __link: DataId;
11
+ };
12
+ export type DataTypeValue = undefined | number | boolean | string | null | Link | DataTypeValue[];
13
+ export type StoreRecord = {
14
+ [index: DataId | string]: DataTypeValue;
15
+ id?: DataId;
16
+ };
17
+ export type DataId = string;
18
+ export declare const ROOT_ID: DataId & "__ROOT";
19
+ export declare const store: {
20
+ [index: DataId]: StoreRecord | null;
21
+ __ROOT: StoreRecord;
22
+ };
23
+ export declare function subscribe(callback: () => void): () => void;
24
+ export declare function onNextChange(): Promise<void>;
25
+ export declare function getParentRecordKey(astNode: NormalizationLinkedField | NormalizationScalarField | ReaderLinkedField | ReaderScalarField, variables: {
26
+ [index: string]: string;
27
+ }): string;
28
+ export declare const FIRST_SPLIT_KEY = "____";
29
+ export declare const SECOND_SPLIT_KEY = "___";
30
+ export {};
package/dist/cache.js ADDED
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SECOND_SPLIT_KEY = exports.FIRST_SPLIT_KEY = exports.getParentRecordKey = exports.onNextChange = exports.subscribe = exports.store = exports.ROOT_ID = exports.makeNetworkRequest = exports.setNetwork = exports.getOrCreateCacheForArtifact = void 0;
4
+ const react_disposable_state_1 = require("@isograph/react-disposable-state");
5
+ const PromiseWrapper_1 = require("./PromiseWrapper");
6
+ const cache = {};
7
+ function getOrCreateCache(index, factory) {
8
+ console.log("getting cache for", {
9
+ index,
10
+ cache: Object.keys(cache),
11
+ found: !!cache[index],
12
+ });
13
+ if (cache[index] == null) {
14
+ cache[index] = new react_disposable_state_1.ParentCache(factory);
15
+ }
16
+ return cache[index];
17
+ }
18
+ /**
19
+ * Creates a copy of the provided value, ensuring any nested objects have their
20
+ * keys sorted such that equivalent values would have identical JSON.stringify
21
+ * results.
22
+ */
23
+ function stableCopy(value) {
24
+ if (!value || typeof value !== "object") {
25
+ return value;
26
+ }
27
+ if (Array.isArray(value)) {
28
+ // @ts-ignore
29
+ return value.map(stableCopy);
30
+ }
31
+ const keys = Object.keys(value).sort();
32
+ const stable = {};
33
+ for (let i = 0; i < keys.length; i++) {
34
+ // @ts-ignore
35
+ stable[keys[i]] = stableCopy(value[keys[i]]);
36
+ }
37
+ return stable;
38
+ }
39
+ function getOrCreateCacheForArtifact(artifact, variables) {
40
+ const cacheKey = artifact.queryText + JSON.stringify(stableCopy(variables));
41
+ const factory = () => makeNetworkRequest(artifact, variables);
42
+ return getOrCreateCache(cacheKey, factory);
43
+ }
44
+ exports.getOrCreateCacheForArtifact = getOrCreateCacheForArtifact;
45
+ let network;
46
+ // This is a hack until we store this in context somehow
47
+ function setNetwork(newNetwork) {
48
+ network = newNetwork;
49
+ }
50
+ exports.setNetwork = setNetwork;
51
+ function makeNetworkRequest(artifact, variables) {
52
+ console.log("make network request", artifact, variables);
53
+ if (network == null) {
54
+ throw new Error("Network must be set before makeNetworkRequest is called");
55
+ }
56
+ const promise = network(artifact.queryText, variables).then((networkResponse) => {
57
+ console.log("network response", artifact);
58
+ normalizeData(artifact.normalizationAst, networkResponse.data, variables, artifact.nestedRefetchQueries);
59
+ return networkResponse.data;
60
+ });
61
+ const wrapper = (0, PromiseWrapper_1.wrapPromise)(promise);
62
+ const response = [
63
+ wrapper,
64
+ () => {
65
+ // delete from cache
66
+ },
67
+ ];
68
+ return response;
69
+ }
70
+ exports.makeNetworkRequest = makeNetworkRequest;
71
+ exports.ROOT_ID = "__ROOT";
72
+ exports.store = {
73
+ __ROOT: {},
74
+ };
75
+ function normalizeData(normalizationAst, networkResponse, variables, nestedRefetchQueries) {
76
+ console.log("about to normalize", normalizationAst, networkResponse, variables);
77
+ normalizeDataIntoRecord(normalizationAst, networkResponse, exports.store.__ROOT, exports.ROOT_ID, variables, nestedRefetchQueries);
78
+ console.log("after normalization", { store: exports.store });
79
+ callSubscriptions();
80
+ }
81
+ function subscribe(callback) {
82
+ subscriptions.add(callback);
83
+ return () => subscriptions.delete(callback);
84
+ }
85
+ exports.subscribe = subscribe;
86
+ function onNextChange() {
87
+ return new Promise((resolve) => {
88
+ const unsubscribe = subscribe(() => {
89
+ unsubscribe();
90
+ resolve();
91
+ });
92
+ });
93
+ }
94
+ exports.onNextChange = onNextChange;
95
+ const subscriptions = new Set();
96
+ function callSubscriptions() {
97
+ subscriptions.forEach((callback) => callback());
98
+ }
99
+ /**
100
+ * Mutate targetParentRecord according to the normalizationAst and networkResponseParentRecord.
101
+ */
102
+ function normalizeDataIntoRecord(normalizationAst, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries) {
103
+ for (const normalizationNode of normalizationAst) {
104
+ switch (normalizationNode.kind) {
105
+ case "Scalar": {
106
+ normalizeScalarField(normalizationNode, networkResponseParentRecord, targetParentRecord, variables);
107
+ break;
108
+ }
109
+ case "Linked": {
110
+ normalizeLinkedField(normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries);
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ function normalizeScalarField(astNode, networkResponseParentRecord, targetStoreRecord, variables) {
117
+ const networkResponseKey = getNetworkResponseKey(astNode);
118
+ const networkResponseData = networkResponseParentRecord[networkResponseKey];
119
+ const parentRecordKey = getParentRecordKey(astNode, variables);
120
+ if (networkResponseData == null ||
121
+ isScalarOrEmptyArray(networkResponseData)) {
122
+ targetStoreRecord[parentRecordKey] = networkResponseData;
123
+ }
124
+ else {
125
+ throw new Error("Unexpected object array when normalizing scalar");
126
+ }
127
+ }
128
+ /**
129
+ * Mutate targetParentRecord with a given linked field ast node.
130
+ */
131
+ function normalizeLinkedField(astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries) {
132
+ const networkResponseKey = getNetworkResponseKey(astNode);
133
+ const networkResponseData = networkResponseParentRecord[networkResponseKey];
134
+ const parentRecordKey = getParentRecordKey(astNode, variables);
135
+ if (networkResponseData == null) {
136
+ targetParentRecord[parentRecordKey] = null;
137
+ return;
138
+ }
139
+ if (isScalarButNotEmptyArray(networkResponseData)) {
140
+ throw new Error("Unexpected scalar network response when normalizing a linked field");
141
+ }
142
+ if (Array.isArray(networkResponseData)) {
143
+ // TODO check astNode.plural or the like
144
+ const dataIds = [];
145
+ for (let i = 0; i < networkResponseData.length; i++) {
146
+ const networkResponseObject = networkResponseData[i];
147
+ const newStoreRecordId = normalizeNetworkResponseObject(astNode, networkResponseObject, targetParentRecordId, variables, i, nestedRefetchQueries);
148
+ dataIds.push({ __link: newStoreRecordId });
149
+ }
150
+ targetParentRecord[parentRecordKey] = dataIds;
151
+ }
152
+ else {
153
+ const newStoreRecordId = normalizeNetworkResponseObject(astNode, networkResponseData, targetParentRecordId, variables, null, nestedRefetchQueries);
154
+ targetParentRecord[parentRecordKey] = {
155
+ __link: newStoreRecordId,
156
+ };
157
+ }
158
+ }
159
+ function normalizeNetworkResponseObject(astNode, networkResponseData, targetParentRecordId, variables, index, nestedRefetchQueries) {
160
+ var _a;
161
+ const newStoreRecordId = getDataIdOfNetworkResponse(targetParentRecordId, networkResponseData, astNode, variables, index);
162
+ const newStoreRecord = (_a = exports.store[newStoreRecordId]) !== null && _a !== void 0 ? _a : {};
163
+ exports.store[newStoreRecordId] = newStoreRecord;
164
+ normalizeDataIntoRecord(astNode.selections, networkResponseData, newStoreRecord, newStoreRecordId, variables, nestedRefetchQueries);
165
+ return newStoreRecordId;
166
+ }
167
+ function isScalarOrEmptyArray(data) {
168
+ // N.B. empty arrays count as empty arrays of scalar fields.
169
+ if (Array.isArray(data)) {
170
+ // This is maybe fixed in a new version of Typescript??
171
+ return data.every((x) => isScalarOrEmptyArray(x));
172
+ }
173
+ const isScalarValue = typeof data === "string" ||
174
+ typeof data === "number" ||
175
+ typeof data === "boolean";
176
+ return isScalarValue;
177
+ }
178
+ function isScalarButNotEmptyArray(data) {
179
+ // N.B. empty arrays count as empty arrays of linked fields.
180
+ if (Array.isArray(data)) {
181
+ if (data.length === 0) {
182
+ return false;
183
+ }
184
+ // This is maybe fixed in a new version of Typescript??
185
+ return data.every((x) => isScalarOrEmptyArray(x));
186
+ }
187
+ const isScalarValue = typeof data === "string" ||
188
+ typeof data === "number" ||
189
+ typeof data === "boolean";
190
+ return isScalarValue;
191
+ }
192
+ function getParentRecordKey(astNode, variables) {
193
+ let parentRecordKey = astNode.fieldName;
194
+ const fieldParameters = astNode.arguments;
195
+ if (fieldParameters != null) {
196
+ for (const fieldParameter of fieldParameters) {
197
+ const { argumentName, variableName } = fieldParameter;
198
+ const valueToUse = variables[variableName];
199
+ parentRecordKey += `${exports.FIRST_SPLIT_KEY}${argumentName}${exports.SECOND_SPLIT_KEY}${valueToUse}`;
200
+ }
201
+ }
202
+ return parentRecordKey;
203
+ }
204
+ exports.getParentRecordKey = getParentRecordKey;
205
+ function getNetworkResponseKey(astNode) {
206
+ let networkResponseKey = astNode.fieldName;
207
+ const fieldParameters = astNode.arguments;
208
+ if (fieldParameters != null) {
209
+ for (const fieldParameter of fieldParameters) {
210
+ const { argumentName, variableName } = fieldParameter;
211
+ networkResponseKey += `${exports.FIRST_SPLIT_KEY}${argumentName}${exports.SECOND_SPLIT_KEY}${variableName}`;
212
+ }
213
+ }
214
+ return networkResponseKey;
215
+ }
216
+ // an alias might be pullRequests____first___first____after___cursor
217
+ exports.FIRST_SPLIT_KEY = "____";
218
+ exports.SECOND_SPLIT_KEY = "___";
219
+ // Returns a key to look up an item in the store
220
+ function getDataIdOfNetworkResponse(parentRecordId, dataToNormalize, astNode, variables, index) {
221
+ // Check whether the dataToNormalize has an id field. If so, that is the key.
222
+ // If not, we construct an id from the parentRecordId and the field parameters.
223
+ const dataId = dataToNormalize.id;
224
+ if (dataId != null) {
225
+ return dataId;
226
+ }
227
+ let storeKey = `${parentRecordId}.${astNode.fieldName}`;
228
+ if (index != null) {
229
+ storeKey += `.${index}`;
230
+ }
231
+ const fieldParameters = astNode.arguments;
232
+ if (fieldParameters == null) {
233
+ return storeKey;
234
+ }
235
+ for (const fieldParameter of fieldParameters) {
236
+ const { argumentName, variableName } = fieldParameter;
237
+ const valueToUse = variables[variableName];
238
+ storeKey += `${exports.FIRST_SPLIT_KEY}${argumentName}${exports.SECOND_SPLIT_KEY}${valueToUse}`;
239
+ }
240
+ return storeKey;
241
+ }
@@ -0,0 +1,109 @@
1
+ import { DataId, StoreRecord, Link } from "./cache";
2
+ export { setNetwork, makeNetworkRequest, subscribe, DataId, Link, StoreRecord, } from "./cache";
3
+ export type IsographFetchableResolver<TReadFromStore extends Object, TResolverProps, TResolverResult> = {
4
+ kind: "FetchableResolver";
5
+ queryText: string;
6
+ normalizationAst: NormalizationAst;
7
+ readerArtifact: ReaderArtifact<TReadFromStore, TResolverProps, TResolverResult>;
8
+ nestedRefetchQueries: RefetchQueryArtifactWrapper[];
9
+ };
10
+ export type ReaderArtifact<TReadFromStore extends Object, TResolverProps, TResolverResult> = {
11
+ kind: "ReaderArtifact";
12
+ readerAst: ReaderAst<TReadFromStore>;
13
+ resolver: (data: TResolverProps) => TResolverResult;
14
+ variant: ReaderResolverVariant;
15
+ };
16
+ export type ReaderAstNode = ReaderScalarField | ReaderLinkedField | ReaderResolverField | ReaderRefetchField | ReaderMutationField;
17
+ export type ReaderAst<TReadFromStore> = ReaderAstNode[];
18
+ export type ReaderScalarField = {
19
+ kind: "Scalar";
20
+ fieldName: string;
21
+ alias: string | null;
22
+ arguments: Arguments | null;
23
+ };
24
+ export type ReaderLinkedField = {
25
+ kind: "Linked";
26
+ fieldName: string;
27
+ alias: string | null;
28
+ selections: ReaderAst<unknown>;
29
+ arguments: Arguments | null;
30
+ };
31
+ export type ReaderResolverVariant = {
32
+ kind: "Eager";
33
+ } | {
34
+ kind: "Component";
35
+ componentName: string;
36
+ };
37
+ export type ReaderResolverField = {
38
+ kind: "Resolver";
39
+ alias: string;
40
+ readerArtifact: ReaderArtifact<any, any, any>;
41
+ arguments: Arguments | null;
42
+ usedRefetchQueries: number[];
43
+ };
44
+ export type ReaderRefetchField = {
45
+ kind: "RefetchField";
46
+ alias: string;
47
+ readerArtifact: ReaderArtifact<any, any, any>;
48
+ refetchQuery: number;
49
+ };
50
+ export type ReaderMutationField = {
51
+ kind: "MutationField";
52
+ alias: string;
53
+ readerArtifact: ReaderArtifact<any, any, any>;
54
+ refetchQuery: number;
55
+ allowedVariables: string[];
56
+ };
57
+ export type NormalizationAstNode = NormalizationScalarField | NormalizationLinkedField;
58
+ export type NormalizationAst = NormalizationAstNode[];
59
+ export type NormalizationScalarField = {
60
+ kind: "Scalar";
61
+ fieldName: string;
62
+ arguments: Arguments | null;
63
+ };
64
+ export type NormalizationLinkedField = {
65
+ kind: "Linked";
66
+ fieldName: string;
67
+ arguments: Arguments | null;
68
+ selections: NormalizationAst;
69
+ };
70
+ export type RefetchQueryArtifact = {
71
+ kind: "RefetchQuery";
72
+ queryText: string;
73
+ normalizationAst: NormalizationAst;
74
+ };
75
+ export type RefetchQueryArtifactWrapper = {
76
+ artifact: RefetchQueryArtifact;
77
+ allowedVariables: string[];
78
+ };
79
+ export type Arguments = Argument[];
80
+ export type Argument = {
81
+ argumentName: string;
82
+ variableName: string;
83
+ };
84
+ export type FragmentReference<TReadFromStore extends Object, TResolverProps, TResolverResult> = {
85
+ kind: "FragmentReference";
86
+ readerArtifact: ReaderArtifact<TReadFromStore, TResolverProps, TResolverResult>;
87
+ root: DataId;
88
+ variables: {
89
+ [index: string]: string;
90
+ } | null;
91
+ nestedRefetchQueries: RefetchQueryArtifactWrapper[];
92
+ };
93
+ export declare function isoFetch<T extends IsographFetchableResolver<any, any, any>>(_text: TemplateStringsArray): T;
94
+ export declare function iso<TResolverParameter, TResolverReturn = TResolverParameter>(_queryText: TemplateStringsArray): (x: ((param: TResolverParameter) => TResolverReturn) | void) => (param: TResolverParameter) => TResolverReturn;
95
+ export declare function useLazyReference<TReadFromStore extends Object, TResolverProps, TResolverResult>(fetchableResolverArtifact: IsographFetchableResolver<TReadFromStore, TResolverProps, TResolverResult>, variables: object): {
96
+ queryReference: FragmentReference<TReadFromStore, TResolverProps, TResolverResult>;
97
+ };
98
+ export declare function read<TReadFromStore extends Object, TResolverProps, TResolverResult>(fragmentReference: FragmentReference<TReadFromStore, TResolverProps, TResolverResult>): TResolverResult;
99
+ export declare function readButDoNotEvaluate<TReadFromStore extends Object>(reference: FragmentReference<TReadFromStore, unknown, unknown>): TReadFromStore;
100
+ export declare function defaultMissingFieldHandler(storeRecord: StoreRecord, root: DataId, fieldName: string, arguments_: {
101
+ [index: string]: any;
102
+ } | null, variables: {
103
+ [index: string]: any;
104
+ } | null): Link | undefined;
105
+ export declare function setMissingFieldHandler(handler: typeof defaultMissingFieldHandler): void;
106
+ export declare function getRefReaderForName(name: string): any;
107
+ export type IsographComponentProps<TDataType, TOtherProps = Object> = {
108
+ data: TDataType;
109
+ } & TOtherProps;