@isograph/react 0.1.1 → 0.2.0

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.
Files changed (112) hide show
  1. package/dist/core/FragmentReference.d.ts +15 -0
  2. package/dist/core/FragmentReference.js +17 -0
  3. package/dist/core/IsographEnvironment.d.ts +71 -0
  4. package/dist/core/IsographEnvironment.js +72 -0
  5. package/dist/core/PromiseWrapper.d.ts +27 -0
  6. package/dist/core/PromiseWrapper.js +58 -0
  7. package/dist/core/areEqualWithDeepComparison.d.ts +3 -0
  8. package/dist/core/areEqualWithDeepComparison.js +61 -0
  9. package/dist/core/cache.d.ts +28 -0
  10. package/dist/core/cache.js +452 -0
  11. package/dist/core/componentCache.d.ts +5 -0
  12. package/dist/core/componentCache.js +38 -0
  13. package/dist/core/entrypoint.d.ts +50 -0
  14. package/dist/core/entrypoint.js +8 -0
  15. package/dist/core/garbageCollection.d.ts +11 -0
  16. package/dist/core/garbageCollection.js +74 -0
  17. package/dist/core/makeNetworkRequest.d.ts +6 -0
  18. package/dist/core/makeNetworkRequest.js +62 -0
  19. package/dist/core/read.d.ts +12 -0
  20. package/dist/core/read.js +415 -0
  21. package/dist/core/reader.d.ts +63 -0
  22. package/dist/core/reader.js +2 -0
  23. package/dist/core/util.d.ts +17 -0
  24. package/dist/core/util.js +2 -0
  25. package/dist/index.d.ts +21 -120
  26. package/dist/index.js +49 -306
  27. package/dist/loadable-hooks/useClientSideDefer.d.ts +4 -0
  28. package/dist/loadable-hooks/useClientSideDefer.js +15 -0
  29. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +5 -0
  30. package/dist/loadable-hooks/useImperativeExposedMutationField.js +15 -0
  31. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +9 -0
  32. package/dist/loadable-hooks/useImperativeLoadableField.js +15 -0
  33. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +33 -0
  34. package/dist/loadable-hooks/useSkipLimitPagination.js +118 -0
  35. package/dist/react/FragmentReader.d.ts +13 -0
  36. package/dist/{EntrypointReader.js → react/FragmentReader.js} +5 -5
  37. package/dist/react/IsographEnvironmentProvider.d.ts +10 -0
  38. package/dist/{IsographEnvironment.js → react/IsographEnvironmentProvider.js} +2 -20
  39. package/dist/react/useImperativeReference.d.ts +7 -0
  40. package/dist/react/useImperativeReference.js +36 -0
  41. package/dist/react/useLazyReference.d.ts +5 -0
  42. package/dist/react/useLazyReference.js +14 -0
  43. package/dist/react/useReadAndSubscribe.d.ts +11 -0
  44. package/dist/react/useReadAndSubscribe.js +41 -0
  45. package/dist/react/useRerenderOnChange.d.ts +3 -0
  46. package/dist/react/useRerenderOnChange.js +23 -0
  47. package/dist/react/useResult.d.ts +5 -0
  48. package/dist/react/useResult.js +36 -0
  49. package/docs/how-useLazyReference-works.md +117 -0
  50. package/package.json +11 -6
  51. package/src/core/FragmentReference.ts +37 -0
  52. package/src/core/IsographEnvironment.ts +183 -0
  53. package/src/core/PromiseWrapper.ts +86 -0
  54. package/src/core/areEqualWithDeepComparison.ts +78 -0
  55. package/src/core/cache.ts +721 -0
  56. package/src/core/componentCache.ts +61 -0
  57. package/src/core/entrypoint.ts +99 -0
  58. package/src/core/garbageCollection.ts +122 -0
  59. package/src/core/makeNetworkRequest.ts +99 -0
  60. package/src/core/read.ts +615 -0
  61. package/src/core/reader.ts +133 -0
  62. package/src/core/util.ts +23 -0
  63. package/src/index.ts +86 -0
  64. package/src/loadable-hooks/useClientSideDefer.ts +28 -0
  65. package/src/loadable-hooks/useImperativeExposedMutationField.ts +17 -0
  66. package/src/loadable-hooks/useImperativeLoadableField.ts +26 -0
  67. package/src/loadable-hooks/useSkipLimitPagination.ts +211 -0
  68. package/src/react/FragmentReader.tsx +34 -0
  69. package/src/react/IsographEnvironmentProvider.tsx +33 -0
  70. package/src/react/useImperativeReference.ts +57 -0
  71. package/src/react/useLazyReference.ts +22 -0
  72. package/src/react/useReadAndSubscribe.ts +66 -0
  73. package/src/react/useRerenderOnChange.ts +33 -0
  74. package/src/react/useResult.ts +65 -0
  75. package/src/tests/__isograph/Query/meName/entrypoint.ts +47 -0
  76. package/src/tests/__isograph/Query/meName/output_type.ts +3 -0
  77. package/src/tests/__isograph/Query/meName/param_type.ts +6 -0
  78. package/src/tests/__isograph/Query/meName/resolver_reader.ts +32 -0
  79. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +83 -0
  80. package/src/tests/__isograph/Query/meNameSuccessor/output_type.ts +3 -0
  81. package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +11 -0
  82. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +54 -0
  83. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +46 -0
  84. package/src/tests/__isograph/Query/nodeField/output_type.ts +3 -0
  85. package/src/tests/__isograph/Query/nodeField/param_type.ts +6 -0
  86. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +37 -0
  87. package/src/tests/__isograph/iso.ts +88 -0
  88. package/src/tests/garbageCollection.test.ts +136 -0
  89. package/src/tests/isograph.config.json +7 -0
  90. package/src/tests/meNameSuccessor.ts +20 -0
  91. package/src/tests/nodeQuery.ts +17 -0
  92. package/src/tests/schema.graphql +16 -0
  93. package/src/tests/tsconfig.json +21 -0
  94. package/tsconfig.json +6 -0
  95. package/tsconfig.pkg.json +2 -1
  96. package/dist/EntrypointReader.d.ts +0 -6
  97. package/dist/IsographEnvironment.d.ts +0 -56
  98. package/dist/PromiseWrapper.d.ts +0 -13
  99. package/dist/PromiseWrapper.js +0 -22
  100. package/dist/cache.d.ts +0 -26
  101. package/dist/cache.js +0 -274
  102. package/dist/componentCache.d.ts +0 -6
  103. package/dist/componentCache.js +0 -31
  104. package/dist/useImperativeReference.d.ts +0 -8
  105. package/dist/useImperativeReference.js +0 -28
  106. package/src/EntrypointReader.tsx +0 -20
  107. package/src/IsographEnvironment.tsx +0 -120
  108. package/src/PromiseWrapper.ts +0 -29
  109. package/src/cache.tsx +0 -484
  110. package/src/componentCache.ts +0 -41
  111. package/src/index.tsx +0 -617
  112. package/src/useImperativeReference.ts +0 -58
@@ -0,0 +1,452 @@
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.subscribeToAnyChange = exports.normalizeData = exports.getOrCreateCacheForArtifact = exports.stableCopy = exports.getOrCreateItemInSuspenseCache = void 0;
4
+ const react_disposable_state_1 = require("@isograph/react-disposable-state");
5
+ const IsographEnvironment_1 = require("./IsographEnvironment");
6
+ const read_1 = require("./read");
7
+ const areEqualWithDeepComparison_1 = require("./areEqualWithDeepComparison");
8
+ const makeNetworkRequest_1 = require("./makeNetworkRequest");
9
+ const PromiseWrapper_1 = require("./PromiseWrapper");
10
+ const TYPENAME_FIELD_NAME = '__typename';
11
+ function getOrCreateItemInSuspenseCache(environment, index, factory) {
12
+ // @ts-expect-error
13
+ if (typeof window !== 'undefined' && window.__LOG) {
14
+ console.log('getting cache for', {
15
+ index,
16
+ cache: Object.keys(environment.fragmentCache),
17
+ found: !!environment.fragmentCache[index],
18
+ });
19
+ }
20
+ if (environment.fragmentCache[index] == null) {
21
+ environment.fragmentCache[index] = new react_disposable_state_1.ParentCache(factory);
22
+ }
23
+ return environment.fragmentCache[index];
24
+ }
25
+ exports.getOrCreateItemInSuspenseCache = getOrCreateItemInSuspenseCache;
26
+ /**
27
+ * Creates a copy of the provided value, ensuring any nested objects have their
28
+ * keys sorted such that equivalent values would have identical JSON.stringify
29
+ * results.
30
+ */
31
+ function stableCopy(value) {
32
+ if (!value || typeof value !== 'object') {
33
+ return value;
34
+ }
35
+ if (Array.isArray(value)) {
36
+ // @ts-ignore
37
+ return value.map(stableCopy);
38
+ }
39
+ const keys = Object.keys(value).sort();
40
+ const stable = {};
41
+ for (let i = 0; i < keys.length; i++) {
42
+ // @ts-ignore
43
+ stable[keys[i]] = stableCopy(value[keys[i]]);
44
+ }
45
+ return stable;
46
+ }
47
+ exports.stableCopy = stableCopy;
48
+ function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
49
+ const cacheKey = entrypoint.queryText + JSON.stringify(stableCopy(variables));
50
+ const factory = () => {
51
+ const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, entrypoint, variables);
52
+ const itemCleanupPair = [
53
+ {
54
+ kind: 'FragmentReference',
55
+ readerWithRefetchQueries: (0, PromiseWrapper_1.wrapResolvedValue)({
56
+ kind: 'ReaderWithRefetchQueries',
57
+ readerArtifact: entrypoint.readerWithRefetchQueries.readerArtifact,
58
+ nestedRefetchQueries: entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
59
+ }),
60
+ root: IsographEnvironment_1.ROOT_ID,
61
+ variables,
62
+ networkRequest: networkRequest,
63
+ },
64
+ disposeNetworkRequest,
65
+ ];
66
+ return itemCleanupPair;
67
+ };
68
+ return getOrCreateItemInSuspenseCache(environment, cacheKey, factory);
69
+ }
70
+ exports.getOrCreateCacheForArtifact = getOrCreateCacheForArtifact;
71
+ function normalizeData(environment, normalizationAst, networkResponse, variables, nestedRefetchQueries) {
72
+ const encounteredIds = new Set();
73
+ // @ts-expect-error
74
+ if (typeof window !== 'undefined' && window.__LOG) {
75
+ console.log('about to normalize', normalizationAst, networkResponse, variables);
76
+ }
77
+ normalizeDataIntoRecord(environment, normalizationAst, networkResponse, environment.store.__ROOT, IsographEnvironment_1.ROOT_ID, variables, nestedRefetchQueries, encounteredIds);
78
+ // @ts-expect-error
79
+ if (typeof window !== 'undefined' && window.__LOG) {
80
+ console.log('after normalization', {
81
+ store: environment.store,
82
+ encounteredIds,
83
+ environment,
84
+ });
85
+ }
86
+ callSubscriptions(environment, encounteredIds);
87
+ return encounteredIds;
88
+ }
89
+ exports.normalizeData = normalizeData;
90
+ function subscribeToAnyChange(environment, callback) {
91
+ const subscription = {
92
+ kind: 'AnyRecords',
93
+ callback,
94
+ };
95
+ environment.subscriptions.add(subscription);
96
+ return () => environment.subscriptions.delete(subscription);
97
+ }
98
+ exports.subscribeToAnyChange = subscribeToAnyChange;
99
+ // TODO we should re-read and call callback if the value has changed
100
+ function subscribe(environment, encounteredDataAndRecords, fragmentReference, callback) {
101
+ const fragmentSubscription = {
102
+ kind: 'FragmentSubscription',
103
+ callback,
104
+ encounteredDataAndRecords,
105
+ fragmentReference,
106
+ };
107
+ // @ts-expect-error
108
+ environment.subscriptions.add(fragmentSubscription);
109
+ // @ts-expect-error
110
+ return () => environment.subscriptions.delete(fragmentSubscription);
111
+ }
112
+ exports.subscribe = subscribe;
113
+ function onNextChange(environment) {
114
+ return new Promise((resolve) => {
115
+ const unsubscribe = subscribeToAnyChange(environment, () => {
116
+ unsubscribe();
117
+ resolve();
118
+ });
119
+ });
120
+ }
121
+ exports.onNextChange = onNextChange;
122
+ // Calls to readButDoNotEvaluate can suspend (i.e. throw a promise).
123
+ // Maybe in the future, they will be able to throw errors.
124
+ //
125
+ // That's probably okay to ignore. We don't, however, want to prevent
126
+ // updating other subscriptions if one subscription had missing data.
127
+ function withErrorHandling(f) {
128
+ return (t) => {
129
+ try {
130
+ return f(t);
131
+ }
132
+ catch (_a) { }
133
+ };
134
+ }
135
+ function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
136
+ environment.subscriptions.forEach(withErrorHandling((subscription) => {
137
+ switch (subscription.kind) {
138
+ case 'FragmentSubscription': {
139
+ // TODO if there are multiple components subscribed to the same
140
+ // fragment, we will call readButNotEvaluate multiple times. We
141
+ // should fix that.
142
+ if (hasOverlappingIds(recordsEncounteredWhenNormalizing, subscription.encounteredDataAndRecords.encounteredRecords)) {
143
+ const newEncounteredDataAndRecords = (0, read_1.readButDoNotEvaluate)(environment, subscription.fragmentReference,
144
+ // Is this wrong?
145
+ // Reasons to think no:
146
+ // - we are only updating the read-out value, and the network
147
+ // options only affect whether we throw.
148
+ // - the component will re-render, and re-throw on its own, anyway.
149
+ //
150
+ // Reasons to think not:
151
+ // - it seems more efficient to suspend here and not update state,
152
+ // if we expect that the component will just throw anyway
153
+ // - consistency
154
+ // - it's also weird, this is called from makeNetworkRequest, where
155
+ // we don't currently pass network request options
156
+ {
157
+ suspendIfInFlight: false,
158
+ throwOnNetworkError: false,
159
+ });
160
+ if (!(0, areEqualWithDeepComparison_1.areEqualObjectsWithDeepComparison)(subscription.encounteredDataAndRecords.item, newEncounteredDataAndRecords.item)) {
161
+ // @ts-expect-error
162
+ if (typeof window !== 'undefined' && window.__LOG) {
163
+ console.log('Deep equality - No', {
164
+ fragmentReference: subscription.fragmentReference,
165
+ old: subscription.encounteredDataAndRecords.item,
166
+ new: newEncounteredDataAndRecords.item,
167
+ });
168
+ }
169
+ // TODO deep compare values
170
+ subscription.callback(newEncounteredDataAndRecords);
171
+ }
172
+ else {
173
+ // @ts-expect-error
174
+ if (typeof window !== 'undefined' && window.__LOG) {
175
+ console.log('Deep equality - Yes', {
176
+ fragmentReference: subscription.fragmentReference,
177
+ old: subscription.encounteredDataAndRecords.item,
178
+ });
179
+ }
180
+ }
181
+ }
182
+ return;
183
+ }
184
+ case 'AnyRecords': {
185
+ return subscription.callback();
186
+ }
187
+ default: {
188
+ // Ensure we have covered all variants
189
+ const _ = subscription;
190
+ _;
191
+ throw new Error('Unexpected case');
192
+ }
193
+ }
194
+ }));
195
+ }
196
+ function hasOverlappingIds(set1, set2) {
197
+ for (const id of set1) {
198
+ if (set2.has(id)) {
199
+ return true;
200
+ }
201
+ }
202
+ return false;
203
+ }
204
+ /**
205
+ * Mutate targetParentRecord according to the normalizationAst and networkResponseParentRecord.
206
+ */
207
+ function normalizeDataIntoRecord(environment, normalizationAst, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
208
+ let recordHasBeenUpdated = false;
209
+ for (const normalizationNode of normalizationAst) {
210
+ switch (normalizationNode.kind) {
211
+ case 'Scalar': {
212
+ const scalarFieldResultedInChange = normalizeScalarField(normalizationNode, networkResponseParentRecord, targetParentRecord, variables);
213
+ recordHasBeenUpdated =
214
+ recordHasBeenUpdated || scalarFieldResultedInChange;
215
+ break;
216
+ }
217
+ case 'Linked': {
218
+ const linkedFieldResultedInChange = normalizeLinkedField(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
219
+ recordHasBeenUpdated =
220
+ recordHasBeenUpdated || linkedFieldResultedInChange;
221
+ break;
222
+ }
223
+ case 'InlineFragment': {
224
+ const inlineFragmentResultedInChange = normalizeInlineFragment(environment, normalizationNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
225
+ recordHasBeenUpdated =
226
+ recordHasBeenUpdated || inlineFragmentResultedInChange;
227
+ break;
228
+ }
229
+ default: {
230
+ // Ensure we have covered all variants
231
+ let _ = normalizationNode;
232
+ _;
233
+ throw new Error('Unexpected normalization node kind');
234
+ }
235
+ }
236
+ }
237
+ if (recordHasBeenUpdated) {
238
+ mutableEncounteredIds.add(targetParentRecordId);
239
+ }
240
+ return recordHasBeenUpdated;
241
+ }
242
+ function normalizeScalarField(astNode, networkResponseParentRecord, targetStoreRecord, variables) {
243
+ const networkResponseKey = getNetworkResponseKey(astNode);
244
+ const networkResponseData = networkResponseParentRecord[networkResponseKey];
245
+ const parentRecordKey = getParentRecordKey(astNode, variables);
246
+ if (networkResponseData == null ||
247
+ isScalarOrEmptyArray(networkResponseData)) {
248
+ const existingValue = targetStoreRecord[parentRecordKey];
249
+ targetStoreRecord[parentRecordKey] = networkResponseData;
250
+ return existingValue !== networkResponseData;
251
+ }
252
+ else {
253
+ throw new Error('Unexpected object array when normalizing scalar');
254
+ }
255
+ }
256
+ /**
257
+ * Mutate targetParentRecord with a given linked field ast node.
258
+ */
259
+ function normalizeLinkedField(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
260
+ const networkResponseKey = getNetworkResponseKey(astNode);
261
+ const networkResponseData = networkResponseParentRecord[networkResponseKey];
262
+ const parentRecordKey = getParentRecordKey(astNode, variables);
263
+ const existingValue = targetParentRecord[parentRecordKey];
264
+ if (networkResponseData == null) {
265
+ targetParentRecord[parentRecordKey] = null;
266
+ return existingValue !== null;
267
+ }
268
+ if (isScalarButNotEmptyArray(networkResponseData)) {
269
+ throw new Error('Unexpected scalar network response when normalizing a linked field');
270
+ }
271
+ if (Array.isArray(networkResponseData)) {
272
+ // TODO check astNode.plural or the like
273
+ const dataIds = [];
274
+ for (let i = 0; i < networkResponseData.length; i++) {
275
+ const networkResponseObject = networkResponseData[i];
276
+ const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseObject, targetParentRecordId, variables, i, nestedRefetchQueries, mutableEncounteredIds);
277
+ dataIds.push({ __link: newStoreRecordId });
278
+ }
279
+ targetParentRecord[parentRecordKey] = dataIds;
280
+ return !dataIdsAreTheSame(existingValue, dataIds);
281
+ }
282
+ else {
283
+ const newStoreRecordId = normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordId, variables, null, nestedRefetchQueries, mutableEncounteredIds);
284
+ targetParentRecord[parentRecordKey] = {
285
+ __link: newStoreRecordId,
286
+ };
287
+ const link = (0, IsographEnvironment_1.getLink)(existingValue);
288
+ return (link === null || link === void 0 ? void 0 : link.__link) !== newStoreRecordId;
289
+ }
290
+ }
291
+ /**
292
+ * Mutate targetParentRecord with a given linked field ast node.
293
+ */
294
+ function normalizeInlineFragment(environment, astNode, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds) {
295
+ const typeToRefineTo = astNode.type;
296
+ if (networkResponseParentRecord[TYPENAME_FIELD_NAME] === typeToRefineTo) {
297
+ const hasBeenModified = normalizeDataIntoRecord(environment, astNode.selections, networkResponseParentRecord, targetParentRecord, targetParentRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
298
+ return hasBeenModified;
299
+ }
300
+ return false;
301
+ }
302
+ function dataIdsAreTheSame(existingValue, newDataIds) {
303
+ if (Array.isArray(existingValue)) {
304
+ if (newDataIds.length !== existingValue.length) {
305
+ return false;
306
+ }
307
+ for (let i = 0; i < newDataIds.length; i++) {
308
+ const maybeLink = (0, IsographEnvironment_1.getLink)(existingValue[i]);
309
+ if (maybeLink !== null) {
310
+ if (newDataIds[i].__link !== maybeLink.__link) {
311
+ return false;
312
+ }
313
+ }
314
+ }
315
+ return true;
316
+ }
317
+ else {
318
+ return false;
319
+ }
320
+ }
321
+ function normalizeNetworkResponseObject(environment, astNode, networkResponseData, targetParentRecordId, variables, index, nestedRefetchQueries, mutableEncounteredIds) {
322
+ var _a;
323
+ const newStoreRecordId = getDataIdOfNetworkResponse(targetParentRecordId, networkResponseData, astNode, variables, index);
324
+ const newStoreRecord = (_a = environment.store[newStoreRecordId]) !== null && _a !== void 0 ? _a : {};
325
+ environment.store[newStoreRecordId] = newStoreRecord;
326
+ normalizeDataIntoRecord(environment, astNode.selections, networkResponseData, newStoreRecord, newStoreRecordId, variables, nestedRefetchQueries, mutableEncounteredIds);
327
+ return newStoreRecordId;
328
+ }
329
+ function isScalarOrEmptyArray(data) {
330
+ // N.B. empty arrays count as empty arrays of scalar fields.
331
+ if (Array.isArray(data)) {
332
+ // This is maybe fixed in a new version of Typescript??
333
+ return data.every((x) => isScalarOrEmptyArray(x));
334
+ }
335
+ const isScalarValue = typeof data === 'string' ||
336
+ typeof data === 'number' ||
337
+ typeof data === 'boolean';
338
+ return isScalarValue;
339
+ }
340
+ function isScalarButNotEmptyArray(data) {
341
+ // N.B. empty arrays count as empty arrays of linked fields.
342
+ if (Array.isArray(data)) {
343
+ if (data.length === 0) {
344
+ return false;
345
+ }
346
+ // This is maybe fixed in a new version of Typescript??
347
+ return data.every((x) => isScalarOrEmptyArray(x));
348
+ }
349
+ const isScalarValue = typeof data === 'string' ||
350
+ typeof data === 'number' ||
351
+ typeof data === 'boolean';
352
+ return isScalarValue;
353
+ }
354
+ function getParentRecordKey(astNode, variables) {
355
+ let parentRecordKey = astNode.fieldName;
356
+ const fieldParameters = astNode.arguments;
357
+ if (fieldParameters != null) {
358
+ for (const fieldParameter of fieldParameters) {
359
+ parentRecordKey += getStoreKeyChunkForArgument(fieldParameter, variables);
360
+ }
361
+ }
362
+ return parentRecordKey;
363
+ }
364
+ exports.getParentRecordKey = getParentRecordKey;
365
+ function getStoreKeyChunkForArgumentValue(argumentValue, variables) {
366
+ var _a;
367
+ switch (argumentValue.kind) {
368
+ case 'Literal': {
369
+ return argumentValue.value;
370
+ }
371
+ case 'Variable': {
372
+ return (_a = variables[argumentValue.name]) !== null && _a !== void 0 ? _a : 'null';
373
+ }
374
+ case 'String': {
375
+ return argumentValue.value;
376
+ }
377
+ case 'Enum': {
378
+ return argumentValue.value;
379
+ }
380
+ default: {
381
+ // TODO configure eslint to allow unused vars starting with _
382
+ // Ensure we have covered all variants
383
+ const _ = argumentValue;
384
+ _;
385
+ throw new Error('Unexpected case');
386
+ }
387
+ }
388
+ }
389
+ function getStoreKeyChunkForArgument(argument, variables) {
390
+ const chunk = getStoreKeyChunkForArgumentValue(argument[1], variables);
391
+ return `${exports.FIRST_SPLIT_KEY}${argument[0]}${exports.SECOND_SPLIT_KEY}${chunk}`;
392
+ }
393
+ function getNetworkResponseKey(astNode) {
394
+ let networkResponseKey = astNode.fieldName;
395
+ const fieldParameters = astNode.arguments;
396
+ if (fieldParameters != null) {
397
+ for (const fieldParameter of fieldParameters) {
398
+ const [argumentName, argumentValue] = fieldParameter;
399
+ let argumentValueChunk;
400
+ switch (argumentValue.kind) {
401
+ case 'Literal': {
402
+ argumentValueChunk = 'l_' + argumentValue.value;
403
+ break;
404
+ }
405
+ case 'Variable': {
406
+ argumentValueChunk = 'v_' + argumentValue.name;
407
+ break;
408
+ }
409
+ case 'String': {
410
+ argumentValueChunk = 's_' + argumentValue.value;
411
+ break;
412
+ }
413
+ case 'Enum': {
414
+ argumentValueChunk = 'e_' + argumentValue.value;
415
+ break;
416
+ }
417
+ default: {
418
+ // Ensure we have covered all variants
419
+ let _ = argumentValue;
420
+ _;
421
+ throw new Error('Unexpected case');
422
+ }
423
+ }
424
+ networkResponseKey += `${exports.FIRST_SPLIT_KEY}${argumentName}${exports.SECOND_SPLIT_KEY}${argumentValueChunk}`;
425
+ }
426
+ }
427
+ return networkResponseKey;
428
+ }
429
+ // an alias might be pullRequests____first___first____after___cursor
430
+ exports.FIRST_SPLIT_KEY = '____';
431
+ exports.SECOND_SPLIT_KEY = '___';
432
+ // Returns a key to look up an item in the store
433
+ function getDataIdOfNetworkResponse(parentRecordId, dataToNormalize, astNode, variables, index) {
434
+ // Check whether the dataToNormalize has an id field. If so, that is the key.
435
+ // If not, we construct an id from the parentRecordId and the field parameters.
436
+ const dataId = dataToNormalize.id;
437
+ if (dataId != null) {
438
+ return dataId;
439
+ }
440
+ let storeKey = `${parentRecordId}.${astNode.fieldName}`;
441
+ if (index != null) {
442
+ storeKey += `.${index}`;
443
+ }
444
+ const fieldParameters = astNode.arguments;
445
+ if (fieldParameters == null) {
446
+ return storeKey;
447
+ }
448
+ for (const fieldParameter of fieldParameters) {
449
+ storeKey += getStoreKeyChunkForArgument(fieldParameter, variables);
450
+ }
451
+ return storeKey;
452
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference types="react" />
2
+ import { IsographEnvironment } from './IsographEnvironment';
3
+ import { FragmentReference } from './FragmentReference';
4
+ import { NetworkRequestReaderOptions } from './read';
5
+ export declare function getOrCreateCachedComponent(environment: IsographEnvironment, componentName: string, fragmentReference: FragmentReference<any, any>, networkRequestOptions: NetworkRequestReaderOptions): React.FC<any>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getOrCreateCachedComponent = void 0;
4
+ const cache_1 = require("./cache");
5
+ const useReadAndSubscribe_1 = require("../react/useReadAndSubscribe");
6
+ const PromiseWrapper_1 = require("./PromiseWrapper");
7
+ function getOrCreateCachedComponent(environment, componentName, fragmentReference, networkRequestOptions) {
8
+ var _a, _b, _c;
9
+ // cachedComponentsById is a three layer cache: id, then component name, then
10
+ // stringified args. These three, together, uniquely identify a read at a given
11
+ // time.
12
+ const cachedComponentsById = environment.componentCache;
13
+ cachedComponentsById[fragmentReference.root] =
14
+ (_a = cachedComponentsById[fragmentReference.root]) !== null && _a !== void 0 ? _a : {};
15
+ const componentsByName = cachedComponentsById[fragmentReference.root];
16
+ componentsByName[componentName] = (_b = componentsByName[componentName]) !== null && _b !== void 0 ? _b : {};
17
+ const byArgs = componentsByName[componentName];
18
+ const stringifiedArgs = JSON.stringify((0, cache_1.stableCopy)(fragmentReference.variables));
19
+ byArgs[stringifiedArgs] =
20
+ (_c = byArgs[stringifiedArgs]) !== null && _c !== void 0 ? _c : (() => {
21
+ function Component(additionalRuntimeProps) {
22
+ const data = (0, useReadAndSubscribe_1.useReadAndSubscribe)(fragmentReference, networkRequestOptions);
23
+ // @ts-expect-error
24
+ if (typeof window !== 'undefined' && window.__LOG) {
25
+ console.log('Component re-rendered: ' +
26
+ componentName +
27
+ ' ' +
28
+ fragmentReference.root);
29
+ }
30
+ const readerWithRefetchQueries = (0, PromiseWrapper_1.readPromise)(fragmentReference.readerWithRefetchQueries);
31
+ return readerWithRefetchQueries.readerArtifact.resolver(data, additionalRuntimeProps);
32
+ }
33
+ Component.displayName = `${componentName} (id: ${fragmentReference.root}) @component`;
34
+ return Component;
35
+ })();
36
+ return byArgs[stringifiedArgs];
37
+ }
38
+ exports.getOrCreateCachedComponent = getOrCreateCachedComponent;
@@ -0,0 +1,50 @@
1
+ /// <reference types="react" />
2
+ import { TopLevelReaderArtifact } from './reader';
3
+ import { Arguments } from './util';
4
+ export type ReaderWithRefetchQueries<TReadFromStore extends Object, TClientFieldValue> = {
5
+ readonly kind: 'ReaderWithRefetchQueries';
6
+ readonly readerArtifact: TopLevelReaderArtifact<TReadFromStore, TClientFieldValue, any>;
7
+ readonly nestedRefetchQueries: RefetchQueryNormalizationArtifactWrapper[];
8
+ };
9
+ export type IsographEntrypoint<TReadFromStore extends Object, TClientFieldValue> = {
10
+ readonly kind: 'Entrypoint';
11
+ readonly queryText: string;
12
+ readonly normalizationAst: NormalizationAst;
13
+ readonly readerWithRefetchQueries: ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>;
14
+ };
15
+ export type IsographEntrypointLoader<TReadFromStore extends Object, TClientFieldValue> = {
16
+ readonly kind: 'EntrypointLoader';
17
+ readonly typeAndField: string;
18
+ readonly loader: () => Promise<IsographEntrypoint<TReadFromStore, TClientFieldValue>>;
19
+ };
20
+ export type NormalizationAstNode = NormalizationScalarField | NormalizationLinkedField | NormalizationInlineFragment;
21
+ export type NormalizationAst = ReadonlyArray<NormalizationAstNode>;
22
+ export type NormalizationScalarField = {
23
+ readonly kind: 'Scalar';
24
+ readonly fieldName: string;
25
+ readonly arguments: Arguments | null;
26
+ };
27
+ export type NormalizationLinkedField = {
28
+ readonly kind: 'Linked';
29
+ readonly fieldName: string;
30
+ readonly arguments: Arguments | null;
31
+ readonly selections: NormalizationAst;
32
+ };
33
+ export type NormalizationInlineFragment = {
34
+ readonly kind: 'InlineFragment';
35
+ readonly type: string;
36
+ readonly selections: NormalizationAst;
37
+ };
38
+ export type RefetchQueryNormalizationArtifact = {
39
+ readonly kind: 'RefetchQuery';
40
+ readonly queryText: string;
41
+ readonly normalizationAst: NormalizationAst;
42
+ };
43
+ export type RefetchQueryNormalizationArtifactWrapper = {
44
+ readonly artifact: RefetchQueryNormalizationArtifact;
45
+ readonly allowedVariables: string[];
46
+ };
47
+ export declare function assertIsEntrypoint<TReadFromStore extends Object, TClientFieldValue>(value: IsographEntrypoint<TReadFromStore, TClientFieldValue> | ((_: any) => any) | any): asserts value is IsographEntrypoint<TReadFromStore, TClientFieldValue>;
48
+ export type ExtractReadFromStore<Type> = Type extends IsographEntrypoint<infer X, any> ? X : never;
49
+ export type ExtractResolverResult<Type> = Type extends IsographEntrypoint<any, infer X> ? X : never;
50
+ export type ExtractProps<Type> = Type extends React.FC<infer X> ? X : never;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertIsEntrypoint = void 0;
4
+ function assertIsEntrypoint(value) {
5
+ if (typeof value === 'function')
6
+ throw new Error('Not a string');
7
+ }
8
+ exports.assertIsEntrypoint = assertIsEntrypoint;
@@ -0,0 +1,11 @@
1
+ import { IsographEnvironment } from './IsographEnvironment';
2
+ import { NormalizationAst } from './entrypoint';
3
+ export type RetainedQuery = {
4
+ readonly normalizationAst: NormalizationAst;
5
+ readonly variables: {};
6
+ };
7
+ type DidUnretainSomeQuery = boolean;
8
+ export declare function unretainQuery(environment: IsographEnvironment, retainedQuery: RetainedQuery): DidUnretainSomeQuery;
9
+ export declare function retainQuery(environment: IsographEnvironment, queryToRetain: RetainedQuery): void;
10
+ export declare function garbageCollectEnvironment(environment: IsographEnvironment): void;
11
+ export {};
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.garbageCollectEnvironment = exports.retainQuery = exports.unretainQuery = void 0;
4
+ const IsographEnvironment_1 = require("./IsographEnvironment");
5
+ const cache_1 = require("./cache");
6
+ function unretainQuery(environment, retainedQuery) {
7
+ environment.retainedQueries.delete(retainedQuery);
8
+ environment.gcBuffer.push(retainedQuery);
9
+ if (environment.gcBuffer.length > environment.gcBufferSize) {
10
+ environment.gcBuffer.shift();
11
+ return true;
12
+ }
13
+ return false;
14
+ }
15
+ exports.unretainQuery = unretainQuery;
16
+ function retainQuery(environment, queryToRetain) {
17
+ environment.retainedQueries.add(queryToRetain);
18
+ // TODO can we remove this query from the buffer somehow?
19
+ // We are relying on === equality, but we really should be comparing
20
+ // id + variables
21
+ }
22
+ exports.retainQuery = retainQuery;
23
+ function garbageCollectEnvironment(environment) {
24
+ const retainedIds = new Set([IsographEnvironment_1.ROOT_ID]);
25
+ for (const query of environment.retainedQueries) {
26
+ recordReachableIds(environment.store, query, retainedIds);
27
+ }
28
+ for (const query of environment.gcBuffer) {
29
+ recordReachableIds(environment.store, query, retainedIds);
30
+ }
31
+ for (const dataId in environment.store) {
32
+ if (!retainedIds.has(dataId)) {
33
+ delete environment.store[dataId];
34
+ }
35
+ }
36
+ }
37
+ exports.garbageCollectEnvironment = garbageCollectEnvironment;
38
+ function recordReachableIds(store, retainedQuery, mutableRetainedIds) {
39
+ recordReachableIdsFromRecord(store, store[IsographEnvironment_1.ROOT_ID], mutableRetainedIds, retainedQuery.normalizationAst, retainedQuery.variables);
40
+ }
41
+ function recordReachableIdsFromRecord(store, currentRecord, mutableRetainedIds, selections, variables) {
42
+ for (const selection of selections) {
43
+ switch (selection.kind) {
44
+ case 'Linked':
45
+ const linkKey = (0, cache_1.getParentRecordKey)(selection, variables !== null && variables !== void 0 ? variables : {});
46
+ const linkedFieldOrFields = currentRecord[linkKey];
47
+ const ids = [];
48
+ if (Array.isArray(linkedFieldOrFields)) {
49
+ for (const maybeLink of linkedFieldOrFields) {
50
+ const link = (0, IsographEnvironment_1.assertLink)(maybeLink);
51
+ if (link != null) {
52
+ ids.push(link.__link);
53
+ }
54
+ }
55
+ }
56
+ else {
57
+ const link = (0, IsographEnvironment_1.assertLink)(linkedFieldOrFields);
58
+ if (link != null) {
59
+ ids.push(link.__link);
60
+ }
61
+ }
62
+ for (const nextRecordId of ids) {
63
+ const nextRecord = store[nextRecordId];
64
+ if (nextRecord != null) {
65
+ mutableRetainedIds.add(nextRecordId);
66
+ recordReachableIdsFromRecord(store, nextRecord, mutableRetainedIds, selection.selections, variables);
67
+ }
68
+ }
69
+ continue;
70
+ case 'Scalar':
71
+ continue;
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,6 @@
1
+ import { ItemCleanupPair } from '@isograph/disposable-types';
2
+ import { IsographEntrypoint, RefetchQueryNormalizationArtifact } from './entrypoint';
3
+ import { Variables } from './FragmentReference';
4
+ import { IsographEnvironment } from './IsographEnvironment';
5
+ import { AnyError, PromiseWrapper } from './PromiseWrapper';
6
+ export declare function makeNetworkRequest(environment: IsographEnvironment, artifact: RefetchQueryNormalizationArtifact | IsographEntrypoint<any, any>, variables: Variables): ItemCleanupPair<PromiseWrapper<void, AnyError>>;