@khanacademy/wonder-blocks-data 7.0.1 → 8.0.2
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/CHANGELOG.md +32 -0
- package/dist/es/index.js +286 -107
- package/dist/index.js +1089 -713
- package/package.json +1 -1
- package/src/__docs__/_overview_ssr_.stories.mdx +13 -13
- package/src/__docs__/exports.abort-inflight-requests.stories.mdx +20 -0
- package/src/__docs__/exports.data.stories.mdx +3 -3
- package/src/__docs__/{exports.fulfill-all-data-requests.stories.mdx → exports.fetch-tracked-requests.stories.mdx} +5 -5
- package/src/__docs__/exports.get-gql-request-id.stories.mdx +24 -0
- package/src/__docs__/{exports.has-unfulfilled-requests.stories.mdx → exports.has-tracked-requests-to-be-fetched.stories.mdx} +4 -4
- package/src/__docs__/exports.intialize-hydration-cache.stories.mdx +29 -0
- package/src/__docs__/exports.purge-caches.stories.mdx +23 -0
- package/src/__docs__/{exports.remove-all-from-cache.stories.mdx → exports.purge-hydration-cache.stories.mdx} +4 -4
- package/src/__docs__/{exports.clear-shared-cache.stories.mdx → exports.purge-shared-cache.stories.mdx} +4 -4
- package/src/__docs__/exports.track-data.stories.mdx +4 -4
- package/src/__docs__/exports.use-cached-effect.stories.mdx +7 -4
- package/src/__docs__/exports.use-gql.stories.mdx +1 -33
- package/src/__docs__/exports.use-server-effect.stories.mdx +1 -1
- package/src/__docs__/exports.use-shared-cache.stories.mdx +2 -2
- package/src/__docs__/types.fetch-policy.stories.mdx +44 -0
- package/src/__docs__/types.response-cache.stories.mdx +1 -1
- package/src/__tests__/generated-snapshot.test.js +5 -5
- package/src/components/__tests__/data.test.js +2 -6
- package/src/hooks/__tests__/use-cached-effect.test.js +341 -100
- package/src/hooks/__tests__/use-hydratable-effect.test.js +15 -9
- package/src/hooks/__tests__/use-shared-cache.test.js +6 -6
- package/src/hooks/use-cached-effect.js +169 -93
- package/src/hooks/use-hydratable-effect.js +8 -1
- package/src/hooks/use-shared-cache.js +2 -2
- package/src/index.js +14 -78
- package/src/util/__tests__/get-gql-request-id.test.js +74 -0
- package/src/util/__tests__/graphql-document-node-parser.test.js +542 -0
- package/src/util/__tests__/hydration-cache-api.test.js +35 -0
- package/src/util/__tests__/purge-caches.test.js +29 -0
- package/src/util/__tests__/request-api.test.js +188 -0
- package/src/util/__tests__/request-fulfillment.test.js +42 -0
- package/src/util/__tests__/ssr-cache.test.js +58 -60
- package/src/util/__tests__/to-gql-operation.test.js +42 -0
- package/src/util/data-error.js +6 -0
- package/src/util/get-gql-request-id.js +50 -0
- package/src/util/graphql-document-node-parser.js +133 -0
- package/src/util/graphql-types.js +30 -0
- package/src/util/hydration-cache-api.js +28 -0
- package/src/util/purge-caches.js +15 -0
- package/src/util/request-api.js +66 -0
- package/src/util/request-fulfillment.js +32 -12
- package/src/util/request-tracking.js +1 -1
- package/src/util/ssr-cache.js +13 -31
- package/src/util/to-gql-operation.js +44 -0
- package/src/util/types.js +31 -0
- package/src/__docs__/exports.intialize-cache.stories.mdx +0 -29
- package/src/__docs__/exports.remove-from-cache.stories.mdx +0 -25
- package/src/__docs__/exports.request-fulfillment.stories.mdx +0 -36
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-data
|
|
2
2
|
|
|
3
|
+
## 8.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 580141ed: Always initialize SSR-only cache
|
|
8
|
+
|
|
9
|
+
## 8.0.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- e5fa4d9e: Make sure ssr-only cache is initialized when purging caches in test environment
|
|
14
|
+
|
|
15
|
+
## 8.0.0
|
|
16
|
+
|
|
17
|
+
### Major Changes
|
|
18
|
+
|
|
19
|
+
- 0720470e: Renamed `initializeCache` to `initializeHydrationCache`, deleted `removeFromCache`, renamed `removeAllFromCache` to `purgeHydrationCache`
|
|
20
|
+
- cf9ed87f: Add `fetchPolicy` to `useCachedEffect` options, add `refetch` to `useCachedEffect` return value (return value is now a tuple of [result, refetch]), add abort API to request fulfillment (not truly aborting though)
|
|
21
|
+
- b882b082: Stop exporting `RequestFulfillment`, update `useCachedEffect` to consider scope with sharing inflight requests
|
|
22
|
+
- 0720470e: Rename `fulfillAllDataRequests` to `fetchTrackedRequests`, rename `hasUnfulfilledRequests` to `hasTrackedRequestsToBeFetched`
|
|
23
|
+
- 0720470e: Renamed `clearSharedCache` to `purgeSharedCache`
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- 0720470e: Add `purgeCaches` export for purging all caches with one call
|
|
28
|
+
- 75c10036: Add `abortInflightRequests` to exports
|
|
29
|
+
- a85f2f3a: Provide `getGqlRequestId` function
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 1385f468: Removed unnecessary `new` on tracking context creation
|
|
34
|
+
|
|
3
35
|
## 7.0.1
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/dist/es/index.js
CHANGED
|
@@ -4,11 +4,14 @@ import _extends from '@babel/runtime/helpers/extends';
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { useContext, useRef, useMemo, useCallback } from 'react';
|
|
6
6
|
|
|
7
|
+
const FetchPolicy = require("flow-enums-runtime").Mirrored(["CacheBeforeNetwork", "CacheAndNetwork", "CacheOnly", "NetworkOnly"]);
|
|
8
|
+
|
|
7
9
|
const DataErrors = Object.freeze({
|
|
8
10
|
Unknown: "Unknown",
|
|
9
11
|
Internal: "Internal",
|
|
10
12
|
InvalidInput: "InvalidInput",
|
|
11
13
|
Network: "Network",
|
|
14
|
+
NotAllowed: "NotAllowed",
|
|
12
15
|
Parse: "Parse",
|
|
13
16
|
Hydrated: "Hydrated"
|
|
14
17
|
});
|
|
@@ -169,31 +172,22 @@ class SsrCache {
|
|
|
169
172
|
};
|
|
170
173
|
|
|
171
174
|
this.getEntry = id => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const internalEntry = (_this$_ssrOnlyCache$g = (_this$_ssrOnlyCache = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache.get(DefaultScope$2, id)) != null ? _this$_ssrOnlyCache$g : this._hydrationCache.get(DefaultScope$2, id);
|
|
175
|
+
const ssrEntry = Server.isServerSide() ? this._ssrOnlyCache.get(DefaultScope$2, id) : null;
|
|
176
|
+
const internalEntry = ssrEntry != null ? ssrEntry : this._hydrationCache.get(DefaultScope$2, id);
|
|
175
177
|
|
|
176
|
-
if (
|
|
178
|
+
if (!Server.isServerSide() && internalEntry != null) {
|
|
177
179
|
this._hydrationCache.purge(DefaultScope$2, id);
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
return internalEntry;
|
|
181
183
|
};
|
|
182
184
|
|
|
183
|
-
this.
|
|
184
|
-
var _this$_ssrOnlyCache$p, _this$_ssrOnlyCache2;
|
|
185
|
-
|
|
186
|
-
return this._hydrationCache.purge(DefaultScope$2, id) || ((_this$_ssrOnlyCache$p = (_this$_ssrOnlyCache2 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache2.purge(DefaultScope$2, id)) != null ? _this$_ssrOnlyCache$p : false);
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
this.removeAll = predicate => {
|
|
190
|
-
var _this$_ssrOnlyCache3;
|
|
191
|
-
|
|
185
|
+
this.purgeData = predicate => {
|
|
192
186
|
const realPredicate = predicate ? (_, key, cachedEntry) => predicate(key, cachedEntry) : undefined;
|
|
193
187
|
|
|
194
188
|
this._hydrationCache.purgeAll(realPredicate);
|
|
195
189
|
|
|
196
|
-
|
|
190
|
+
this._ssrOnlyCache.purgeAll(realPredicate);
|
|
197
191
|
};
|
|
198
192
|
|
|
199
193
|
this.cloneHydratableData = () => {
|
|
@@ -204,7 +198,7 @@ class SsrCache {
|
|
|
204
198
|
return (_cache$DefaultScope = cache[DefaultScope$2]) != null ? _cache$DefaultScope : {};
|
|
205
199
|
};
|
|
206
200
|
|
|
207
|
-
this._ssrOnlyCache =
|
|
201
|
+
this._ssrOnlyCache = ssrOnlyCache || new SerializableInMemoryCache();
|
|
208
202
|
this._hydrationCache = hydrationCache || new SerializableInMemoryCache();
|
|
209
203
|
}
|
|
210
204
|
|
|
@@ -215,9 +209,7 @@ class SsrCache {
|
|
|
215
209
|
if (hydrate) {
|
|
216
210
|
this._hydrationCache.set(DefaultScope$2, id, frozenEntry);
|
|
217
211
|
} else {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
(_this$_ssrOnlyCache4 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache4.set(DefaultScope$2, id, frozenEntry);
|
|
212
|
+
this._ssrOnlyCache.set(DefaultScope$2, id, frozenEntry);
|
|
221
213
|
}
|
|
222
214
|
}
|
|
223
215
|
|
|
@@ -226,6 +218,9 @@ class SsrCache {
|
|
|
226
218
|
|
|
227
219
|
}
|
|
228
220
|
|
|
221
|
+
const initializeHydrationCache = source => SsrCache.Default.initialize(source);
|
|
222
|
+
const purgeHydrationCache = predicate => SsrCache.Default.purgeData(predicate);
|
|
223
|
+
|
|
229
224
|
let _default$1;
|
|
230
225
|
|
|
231
226
|
class RequestFulfillment {
|
|
@@ -268,6 +263,14 @@ class RequestFulfillment {
|
|
|
268
263
|
this._requests[id] = request;
|
|
269
264
|
return request;
|
|
270
265
|
};
|
|
266
|
+
|
|
267
|
+
this.abort = id => {
|
|
268
|
+
delete this._requests[id];
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
this.abortAll = () => {
|
|
272
|
+
Object.keys(this._requests).forEach(id => this.abort(id));
|
|
273
|
+
};
|
|
271
274
|
}
|
|
272
275
|
|
|
273
276
|
static get Default() {
|
|
@@ -280,7 +283,7 @@ class RequestFulfillment {
|
|
|
280
283
|
|
|
281
284
|
}
|
|
282
285
|
|
|
283
|
-
const TrackerContext =
|
|
286
|
+
const TrackerContext = React.createContext(null);
|
|
284
287
|
|
|
285
288
|
let _default;
|
|
286
289
|
|
|
@@ -352,6 +355,77 @@ class RequestTracker {
|
|
|
352
355
|
|
|
353
356
|
}
|
|
354
357
|
|
|
358
|
+
const SSRCheck = () => {
|
|
359
|
+
if (Server.isServerSide()) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (process.env.NODE_ENV === "production") {
|
|
364
|
+
return new DataError("No CSR tracking", DataErrors.NotAllowed);
|
|
365
|
+
} else {
|
|
366
|
+
return new DataError("Data requests are not tracked for fulfillment when when client-side", DataErrors.NotAllowed);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const fetchTrackedRequests = () => {
|
|
371
|
+
const ssrCheck = SSRCheck();
|
|
372
|
+
|
|
373
|
+
if (ssrCheck != null) {
|
|
374
|
+
return Promise.reject(ssrCheck);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return RequestTracker.Default.fulfillTrackedRequests();
|
|
378
|
+
};
|
|
379
|
+
const hasTrackedRequestsToBeFetched = () => {
|
|
380
|
+
const ssrCheck = SSRCheck();
|
|
381
|
+
|
|
382
|
+
if (ssrCheck != null) {
|
|
383
|
+
throw ssrCheck;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return RequestTracker.Default.hasUnfulfilledRequests;
|
|
387
|
+
};
|
|
388
|
+
const abortInflightRequests = () => {
|
|
389
|
+
RequestFulfillment.Default.abortAll();
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const cache$1 = new ScopedInMemoryCache();
|
|
393
|
+
const purgeSharedCache = (scope = "") => {
|
|
394
|
+
if (scope && typeof scope === "string") {
|
|
395
|
+
cache$1.purgeScope(scope);
|
|
396
|
+
} else {
|
|
397
|
+
cache$1.purgeAll();
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const useSharedCache = (id, scope, initialValue) => {
|
|
401
|
+
if (!id || typeof id !== "string") {
|
|
402
|
+
throw new DataError("id must be a non-empty string", DataErrors.InvalidInput);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (!scope || typeof scope !== "string") {
|
|
406
|
+
throw new DataError("scope must be a non-empty string", DataErrors.InvalidInput);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const cacheValue = React.useCallback(value => value == null ? cache$1.purge(scope, id) : cache$1.set(scope, id, value), [id, scope]);
|
|
410
|
+
let currentValue = cache$1.get(scope, id);
|
|
411
|
+
|
|
412
|
+
if (currentValue == null && initialValue !== undefined) {
|
|
413
|
+
const value = typeof initialValue === "function" ? initialValue() : initialValue;
|
|
414
|
+
|
|
415
|
+
if (value != null) {
|
|
416
|
+
cacheValue(value);
|
|
417
|
+
currentValue = value;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return [currentValue, cacheValue];
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const purgeCaches = () => {
|
|
425
|
+
purgeSharedCache();
|
|
426
|
+
purgeHydrationCache();
|
|
427
|
+
};
|
|
428
|
+
|
|
355
429
|
class TrackData extends React.Component {
|
|
356
430
|
render() {
|
|
357
431
|
if (!Server.isServerSide()) {
|
|
@@ -438,41 +512,12 @@ const useServerEffect = (requestId, handler, options = {}) => {
|
|
|
438
512
|
return cachedResult == null ? null : resultFromCachedResponse(cachedResult);
|
|
439
513
|
};
|
|
440
514
|
|
|
441
|
-
const cache = new ScopedInMemoryCache();
|
|
442
|
-
const clearSharedCache = (scope = "") => {
|
|
443
|
-
if (scope && typeof scope === "string") {
|
|
444
|
-
cache.purgeScope(scope);
|
|
445
|
-
} else {
|
|
446
|
-
cache.purgeAll();
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
const useSharedCache = (id, scope, initialValue) => {
|
|
450
|
-
if (!id || typeof id !== "string") {
|
|
451
|
-
throw new DataError("id must be a non-empty string", DataErrors.InvalidInput);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
if (!scope || typeof scope !== "string") {
|
|
455
|
-
throw new DataError("scope must be a non-empty string", DataErrors.InvalidInput);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const cacheValue = React.useCallback(value => value == null ? cache.purge(scope, id) : cache.set(scope, id, value), [id, scope]);
|
|
459
|
-
let currentValue = cache.get(scope, id);
|
|
460
|
-
|
|
461
|
-
if (currentValue == null && initialValue !== undefined) {
|
|
462
|
-
const value = typeof initialValue === "function" ? initialValue() : initialValue;
|
|
463
|
-
|
|
464
|
-
if (value != null) {
|
|
465
|
-
cacheValue(value);
|
|
466
|
-
currentValue = value;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return [currentValue, cacheValue];
|
|
471
|
-
};
|
|
472
|
-
|
|
473
515
|
const DefaultScope$1 = "useCachedEffect";
|
|
474
516
|
const useCachedEffect = (requestId, handler, options = {}) => {
|
|
517
|
+
var _ref;
|
|
518
|
+
|
|
475
519
|
const {
|
|
520
|
+
fetchPolicy = FetchPolicy.CacheBeforeNetwork,
|
|
476
521
|
skip: hardSkip = false,
|
|
477
522
|
retainResultOnChange = false,
|
|
478
523
|
onResultChanged,
|
|
@@ -481,55 +526,102 @@ const useCachedEffect = (requestId, handler, options = {}) => {
|
|
|
481
526
|
const interceptedHandler = useRequestInterception(requestId, handler);
|
|
482
527
|
const [mostRecentResult, setMostRecentResult] = useSharedCache(requestId, scope);
|
|
483
528
|
const forceUpdate = useForceUpdate();
|
|
484
|
-
const
|
|
485
|
-
|
|
529
|
+
const networkResultRef = React.useRef();
|
|
530
|
+
const currentRequestRef = React.useRef();
|
|
531
|
+
const fetchRequest = React.useMemo(() => {
|
|
532
|
+
var _currentRequestRef$cu;
|
|
486
533
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
forceUpdate();
|
|
491
|
-
}
|
|
492
|
-
}, [setMostRecentResult, onResultChanged, forceUpdate]);
|
|
493
|
-
const requestIdRef = React.useRef();
|
|
494
|
-
const previousRequestId = requestIdRef.current;
|
|
495
|
-
const softSkip = React.useMemo(() => {
|
|
496
|
-
if (requestId === previousRequestId) {
|
|
497
|
-
return true;
|
|
498
|
-
}
|
|
534
|
+
(_currentRequestRef$cu = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu.cancel();
|
|
535
|
+
currentRequestRef.current = null;
|
|
536
|
+
networkResultRef.current = null;
|
|
499
537
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
538
|
+
const fetchFn = () => {
|
|
539
|
+
var _currentRequestRef$cu2, _currentRequestRef$cu3;
|
|
503
540
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
let cancel = false;
|
|
541
|
+
if (fetchPolicy === FetchPolicy.CacheOnly) {
|
|
542
|
+
throw new DataError("Cannot fetch with CacheOnly policy", DataErrors.NotAllowed);
|
|
543
|
+
}
|
|
508
544
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
545
|
+
const request = RequestFulfillment.Default.fulfill(`${requestId}|${scope}`, {
|
|
546
|
+
handler: interceptedHandler
|
|
547
|
+
});
|
|
512
548
|
|
|
513
|
-
|
|
514
|
-
RequestFulfillment.Default.fulfill(requestId, {
|
|
515
|
-
handler: interceptedHandler
|
|
516
|
-
}).then(result => {
|
|
517
|
-
if (cancel) {
|
|
549
|
+
if (request === ((_currentRequestRef$cu2 = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu2.request)) {
|
|
518
550
|
return;
|
|
519
551
|
}
|
|
520
552
|
|
|
521
|
-
|
|
553
|
+
networkResultRef.current = null;
|
|
554
|
+
(_currentRequestRef$cu3 = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu3.cancel();
|
|
555
|
+
let cancel = false;
|
|
556
|
+
request.then(result => {
|
|
557
|
+
currentRequestRef.current = null;
|
|
558
|
+
|
|
559
|
+
if (cancel) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
setMostRecentResult(result);
|
|
564
|
+
networkResultRef.current = result;
|
|
565
|
+
|
|
566
|
+
if (onResultChanged != null) {
|
|
567
|
+
onResultChanged(result);
|
|
568
|
+
} else {
|
|
569
|
+
forceUpdate();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return;
|
|
573
|
+
});
|
|
574
|
+
currentRequestRef.current = {
|
|
575
|
+
requestId,
|
|
576
|
+
request,
|
|
577
|
+
|
|
578
|
+
cancel() {
|
|
579
|
+
cancel = true;
|
|
580
|
+
RequestFulfillment.Default.abort(requestId);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
};
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
return fetchFn;
|
|
587
|
+
}, [requestId, onResultChanged, forceUpdate, setMostRecentResult, fetchPolicy]);
|
|
588
|
+
const requestIdRef = React.useRef(requestId);
|
|
589
|
+
const shouldFetch = React.useMemo(() => {
|
|
590
|
+
if (hardSkip) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
switch (fetchPolicy) {
|
|
595
|
+
case FetchPolicy.CacheOnly:
|
|
596
|
+
return false;
|
|
597
|
+
|
|
598
|
+
case FetchPolicy.CacheBeforeNetwork:
|
|
599
|
+
return mostRecentResult == null || requestId !== requestIdRef.current;
|
|
600
|
+
|
|
601
|
+
case FetchPolicy.CacheAndNetwork:
|
|
602
|
+
case FetchPolicy.NetworkOnly:
|
|
603
|
+
return networkResultRef.current == null;
|
|
604
|
+
}
|
|
605
|
+
}, [requestId, mostRecentResult, fetchPolicy, hardSkip]);
|
|
606
|
+
requestIdRef.current = requestId;
|
|
607
|
+
React.useEffect(() => {
|
|
608
|
+
if (!shouldFetch) {
|
|
522
609
|
return;
|
|
523
|
-
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
fetchRequest();
|
|
524
613
|
return () => {
|
|
525
|
-
|
|
614
|
+
var _currentRequestRef$cu4;
|
|
615
|
+
|
|
616
|
+
(_currentRequestRef$cu4 = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu4.cancel();
|
|
617
|
+
currentRequestRef.current = null;
|
|
526
618
|
};
|
|
527
|
-
}, [
|
|
619
|
+
}, [shouldFetch, fetchRequest]);
|
|
528
620
|
const lastResultAgnosticOfIdRef = React.useRef(Status.loading());
|
|
529
621
|
const loadingResult = retainResultOnChange ? lastResultAgnosticOfIdRef.current : Status.loading();
|
|
530
|
-
const result =
|
|
622
|
+
const result = (_ref = fetchPolicy === FetchPolicy.NetworkOnly ? networkResultRef.current : mostRecentResult) != null ? _ref : loadingResult;
|
|
531
623
|
lastResultAgnosticOfIdRef.current = result;
|
|
532
|
-
return result;
|
|
624
|
+
return [result, fetchRequest];
|
|
533
625
|
};
|
|
534
626
|
|
|
535
627
|
const WhenClientSide = require("flow-enums-runtime").Mirrored(["DoNotHydrate", "ExecuteWhenNoResult", "ExecuteWhenNoSuccessResult", "AlwaysExecute"]);
|
|
@@ -564,11 +656,12 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
|
|
|
564
656
|
}
|
|
565
657
|
}, [serverResult]);
|
|
566
658
|
useSharedCache(requestId, scope, getDefaultCacheValue);
|
|
567
|
-
const clientResult = useCachedEffect(requestId, handler, {
|
|
659
|
+
const [clientResult] = useCachedEffect(requestId, handler, {
|
|
568
660
|
skip,
|
|
569
661
|
onResultChanged,
|
|
570
662
|
retainResultOnChange,
|
|
571
|
-
scope
|
|
663
|
+
scope,
|
|
664
|
+
fetchPolicy: FetchPolicy.CacheBeforeNetwork
|
|
572
665
|
});
|
|
573
666
|
return serverResult != null ? serverResult : clientResult;
|
|
574
667
|
};
|
|
@@ -598,6 +691,110 @@ const InterceptRequests = ({
|
|
|
598
691
|
}, children);
|
|
599
692
|
};
|
|
600
693
|
|
|
694
|
+
const toString = valid => {
|
|
695
|
+
var _JSON$stringify;
|
|
696
|
+
|
|
697
|
+
if (typeof valid === "string") {
|
|
698
|
+
return valid;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return (_JSON$stringify = JSON.stringify(valid)) != null ? _JSON$stringify : "";
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
const getGqlRequestId = (operation, variables, context) => {
|
|
705
|
+
const parts = [];
|
|
706
|
+
const sortableContext = new URLSearchParams(context);
|
|
707
|
+
sortableContext.sort();
|
|
708
|
+
parts.push(sortableContext.toString());
|
|
709
|
+
parts.push(operation.id);
|
|
710
|
+
|
|
711
|
+
if (variables != null) {
|
|
712
|
+
const stringifiedVariables = Object.keys(variables).reduce((acc, key) => {
|
|
713
|
+
acc[key] = toString(variables[key]);
|
|
714
|
+
return acc;
|
|
715
|
+
}, {});
|
|
716
|
+
const sortableVariables = new URLSearchParams(stringifiedVariables);
|
|
717
|
+
sortableVariables.sort();
|
|
718
|
+
parts.push(sortableVariables.toString());
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
return parts.join("|");
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
const DocumentTypes = Object.freeze({
|
|
725
|
+
query: "query",
|
|
726
|
+
mutation: "mutation"
|
|
727
|
+
});
|
|
728
|
+
const cache = new Map();
|
|
729
|
+
function graphQLDocumentNodeParser(document) {
|
|
730
|
+
var _definition$name;
|
|
731
|
+
|
|
732
|
+
const cached = cache.get(document);
|
|
733
|
+
|
|
734
|
+
if (cached) {
|
|
735
|
+
return cached;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (!(document != null && document.kind)) {
|
|
739
|
+
if (process.env.NODE_ENV === "production") {
|
|
740
|
+
throw new DataError("Bad DocumentNode", DataErrors.InvalidInput);
|
|
741
|
+
} else {
|
|
742
|
+
throw new DataError(`Argument of ${JSON.stringify(document)} passed to parser was not a valid GraphQL ` + `DocumentNode. You may need to use 'graphql-tag' or another method ` + `to convert your operation into a document`, DataErrors.InvalidInput);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const fragments = document.definitions.filter(x => x.kind === "FragmentDefinition");
|
|
747
|
+
const queries = document.definitions.filter(x => x.kind === "OperationDefinition" && x.operation === "query");
|
|
748
|
+
const mutations = document.definitions.filter(x => x.kind === "OperationDefinition" && x.operation === "mutation");
|
|
749
|
+
const subscriptions = document.definitions.filter(x => x.kind === "OperationDefinition" && x.operation === "subscription");
|
|
750
|
+
|
|
751
|
+
if (fragments.length && !queries.length && !mutations.length) {
|
|
752
|
+
if (process.env.NODE_ENV === "production") {
|
|
753
|
+
throw new DataError("Fragment only", DataErrors.InvalidInput);
|
|
754
|
+
} else {
|
|
755
|
+
throw new DataError(`Passing only a fragment to 'graphql' is not supported. ` + `You must include a query or mutation as well`, DataErrors.InvalidInput);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (subscriptions.length) {
|
|
760
|
+
if (process.env.NODE_ENV === "production") {
|
|
761
|
+
throw new DataError("No subscriptions", DataErrors.InvalidInput);
|
|
762
|
+
} else {
|
|
763
|
+
throw new DataError(`We do not support subscriptions. ` + `${JSON.stringify(document)} had ${subscriptions.length} subscriptions`, DataErrors.InvalidInput);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (queries.length + mutations.length > 1) {
|
|
768
|
+
if (process.env.NODE_ENV === "production") {
|
|
769
|
+
throw new DataError("Too many ops", DataErrors.InvalidInput);
|
|
770
|
+
} else {
|
|
771
|
+
throw new DataError(`We only support one query or mutation per component. ` + `${JSON.stringify(document)} had ${queries.length} queries and ` + `${mutations.length} mutations. `, DataErrors.InvalidInput);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const type = queries.length ? DocumentTypes.query : DocumentTypes.mutation;
|
|
776
|
+
const definitions = queries.length ? queries : mutations;
|
|
777
|
+
const definition = definitions[0];
|
|
778
|
+
const variables = definition.variableDefinitions || [];
|
|
779
|
+
const name = ((_definition$name = definition.name) == null ? void 0 : _definition$name.kind) === "Name" ? definition.name.value : "data";
|
|
780
|
+
const payload = {
|
|
781
|
+
name,
|
|
782
|
+
type,
|
|
783
|
+
variables
|
|
784
|
+
};
|
|
785
|
+
cache.set(document, payload);
|
|
786
|
+
return payload;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const toGqlOperation = documentNode => {
|
|
790
|
+
const definition = graphQLDocumentNodeParser(documentNode);
|
|
791
|
+
const wbDataOperation = {
|
|
792
|
+
id: definition.name,
|
|
793
|
+
type: definition.type
|
|
794
|
+
};
|
|
795
|
+
return wbDataOperation;
|
|
796
|
+
};
|
|
797
|
+
|
|
601
798
|
const GqlRouterContext = React.createContext(null);
|
|
602
799
|
|
|
603
800
|
const GqlRouter = ({
|
|
@@ -739,22 +936,4 @@ const useGql = (context = {}) => {
|
|
|
739
936
|
return gqlFetch;
|
|
740
937
|
};
|
|
741
938
|
|
|
742
|
-
|
|
743
|
-
const fulfillAllDataRequests = () => {
|
|
744
|
-
if (!Server.isServerSide()) {
|
|
745
|
-
return Promise.reject(new Error("Data requests are not tracked when client-side"));
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
return RequestTracker.Default.fulfillTrackedRequests();
|
|
749
|
-
};
|
|
750
|
-
const hasUnfulfilledRequests = () => {
|
|
751
|
-
if (!Server.isServerSide()) {
|
|
752
|
-
throw new Error("Data requests are not tracked when client-side");
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
return RequestTracker.Default.hasUnfulfilledRequests;
|
|
756
|
-
};
|
|
757
|
-
const removeFromCache = id => SsrCache.Default.remove(id);
|
|
758
|
-
const removeAllFromCache = predicate => SsrCache.Default.removeAll(predicate);
|
|
759
|
-
|
|
760
|
-
export { Data, DataError, DataErrors, GqlError, GqlErrors, GqlRouter, InterceptRequests, RequestFulfillment, ScopedInMemoryCache, SerializableInMemoryCache, Status, TrackData, WhenClientSide, clearSharedCache, fulfillAllDataRequests, hasUnfulfilledRequests, initializeCache, removeAllFromCache, removeFromCache, useCachedEffect, useGql, useHydratableEffect, useServerEffect, useSharedCache };
|
|
939
|
+
export { Data, DataError, DataErrors, FetchPolicy, GqlError, GqlErrors, GqlRouter, InterceptRequests, ScopedInMemoryCache, SerializableInMemoryCache, Status, TrackData, WhenClientSide, abortInflightRequests, fetchTrackedRequests, getGqlRequestId, graphQLDocumentNodeParser, hasTrackedRequestsToBeFetched, initializeHydrationCache, purgeCaches, purgeHydrationCache, purgeSharedCache, toGqlOperation, useCachedEffect, useGql, useHydratableEffect, useServerEffect, useSharedCache };
|