@khanacademy/wonder-blocks-data 6.0.1 → 8.0.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 (56) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/es/index.js +329 -777
  3. package/dist/index.js +1196 -834
  4. package/package.json +3 -3
  5. package/src/__docs__/_overview_ssr_.stories.mdx +13 -13
  6. package/src/__docs__/exports.abort-inflight-requests.stories.mdx +20 -0
  7. package/src/__docs__/exports.data.stories.mdx +3 -3
  8. package/src/__docs__/{exports.fulfill-all-data-requests.stories.mdx → exports.fetch-tracked-requests.stories.mdx} +5 -5
  9. package/src/__docs__/exports.get-gql-request-id.stories.mdx +24 -0
  10. package/src/__docs__/{exports.has-unfulfilled-requests.stories.mdx → exports.has-tracked-requests-to-be-fetched.stories.mdx} +4 -4
  11. package/src/__docs__/exports.intialize-hydration-cache.stories.mdx +29 -0
  12. package/src/__docs__/exports.purge-caches.stories.mdx +23 -0
  13. package/src/__docs__/{exports.remove-all-from-cache.stories.mdx → exports.purge-hydration-cache.stories.mdx} +4 -4
  14. package/src/__docs__/{exports.clear-shared-cache.stories.mdx → exports.purge-shared-cache.stories.mdx} +4 -4
  15. package/src/__docs__/exports.track-data.stories.mdx +4 -4
  16. package/src/__docs__/exports.use-cached-effect.stories.mdx +7 -4
  17. package/src/__docs__/exports.use-gql.stories.mdx +1 -33
  18. package/src/__docs__/exports.use-server-effect.stories.mdx +14 -2
  19. package/src/__docs__/exports.use-shared-cache.stories.mdx +2 -2
  20. package/src/__docs__/types.fetch-policy.stories.mdx +44 -0
  21. package/src/__docs__/types.response-cache.stories.mdx +1 -1
  22. package/src/__tests__/generated-snapshot.test.js +5 -5
  23. package/src/components/__tests__/data.test.js +2 -6
  24. package/src/hooks/__tests__/use-cached-effect.test.js +341 -100
  25. package/src/hooks/__tests__/use-hydratable-effect.test.js +17 -34
  26. package/src/hooks/__tests__/use-server-effect.test.js +51 -0
  27. package/src/hooks/__tests__/use-shared-cache.test.js +6 -6
  28. package/src/hooks/use-cached-effect.js +169 -93
  29. package/src/hooks/use-hydratable-effect.js +12 -12
  30. package/src/hooks/use-server-effect.js +30 -5
  31. package/src/hooks/use-shared-cache.js +2 -2
  32. package/src/index.js +14 -78
  33. package/src/util/__tests__/get-gql-request-id.test.js +74 -0
  34. package/src/util/__tests__/graphql-document-node-parser.test.js +542 -0
  35. package/src/util/__tests__/hydration-cache-api.test.js +35 -0
  36. package/src/util/__tests__/purge-caches.test.js +29 -0
  37. package/src/util/__tests__/request-api.test.js +188 -0
  38. package/src/util/__tests__/request-fulfillment.test.js +42 -0
  39. package/src/util/__tests__/ssr-cache.test.js +10 -60
  40. package/src/util/__tests__/to-gql-operation.test.js +42 -0
  41. package/src/util/data-error.js +6 -0
  42. package/src/util/get-gql-request-id.js +50 -0
  43. package/src/util/graphql-document-node-parser.js +133 -0
  44. package/src/util/graphql-types.js +30 -0
  45. package/src/util/hydration-cache-api.js +28 -0
  46. package/src/util/purge-caches.js +15 -0
  47. package/src/util/request-api.js +66 -0
  48. package/src/util/request-fulfillment.js +32 -12
  49. package/src/util/request-tracking.js +1 -1
  50. package/src/util/ssr-cache.js +1 -21
  51. package/src/util/to-gql-operation.js +44 -0
  52. package/src/util/types.js +31 -0
  53. package/src/__docs__/exports.intialize-cache.stories.mdx +0 -29
  54. package/src/__docs__/exports.remove-from-cache.stories.mdx +0 -25
  55. package/src/__docs__/exports.request-fulfillment.stories.mdx +0 -36
  56. package/src/util/abort-error.js +0 -15
package/dist/index.js CHANGED
@@ -82,23 +82,17 @@ module.exports =
82
82
  /******/
83
83
  /******/
84
84
  /******/ // Load entry module and return exports
85
- /******/ return __webpack_require__(__webpack_require__.s = 28);
85
+ /******/ return __webpack_require__(__webpack_require__.s = 35);
86
86
  /******/ })
87
87
  /************************************************************************/
88
88
  /******/ ([
89
89
  /* 0 */
90
- /***/ (function(module, exports) {
91
-
92
- module.exports = require("react");
93
-
94
- /***/ }),
95
- /* 1 */
96
90
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
97
91
 
98
92
  "use strict";
99
93
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return DataErrors; });
100
94
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return DataError; });
101
- /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
95
+ /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7);
102
96
  /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__);
103
97
 
104
98
 
@@ -126,6 +120,12 @@ const DataErrors = Object.freeze({
126
120
  */
127
121
  Network: "Network",
128
122
 
123
+ /**
124
+ * There was a problem due to the state of the system not matching the
125
+ * requested operation or input.
126
+ */
127
+ NotAllowed: "NotAllowed",
128
+
129
129
  /**
130
130
  * Response could not be parsed.
131
131
  */
@@ -157,20 +157,50 @@ class DataError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_
157
157
 
158
158
  }
159
159
 
160
+ /***/ }),
161
+ /* 1 */
162
+ /***/ (function(module, exports) {
163
+
164
+ module.exports = require("react");
165
+
160
166
  /***/ }),
161
167
  /* 2 */
168
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
169
+
170
+ "use strict";
171
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return FetchPolicy; });
172
+ // TODO(somewhatabstract, FEI-4172): Update eslint-plugin-flowtype when
173
+ // they've fixed https://github.com/gajus/eslint-plugin-flowtype/issues/502
174
+
175
+ /* eslint-disable no-undef */
176
+
177
+ /**
178
+ * Defines the various fetch policies that can be applied to requests.
179
+ */
180
+ const FetchPolicy = __webpack_require__(22).Mirrored(["CacheBeforeNetwork", "CacheAndNetwork", "CacheOnly", "NetworkOnly"]);
181
+ /* eslint-enable no-undef */
182
+
183
+ /**
184
+ * Define what can be cached.
185
+ *
186
+ * We disallow functions and undefined as undefined represents a cache miss
187
+ * and functions are not allowed.
188
+ */
189
+
190
+ /***/ }),
191
+ /* 3 */
162
192
  /***/ (function(module, exports) {
163
193
 
164
194
  module.exports = require("@khanacademy/wonder-blocks-core");
165
195
 
166
196
  /***/ }),
167
- /* 3 */
197
+ /* 4 */
168
198
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
169
199
 
170
200
  "use strict";
171
201
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return GqlErrors; });
172
202
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlError; });
173
- /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
203
+ /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7);
174
204
  /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__);
175
205
 
176
206
 
@@ -215,7 +245,100 @@ class GqlError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0
215
245
  }
216
246
 
217
247
  /***/ }),
218
- /* 4 */
248
+ /* 5 */
249
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
250
+
251
+ "use strict";
252
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return purgeSharedCache; });
253
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return useSharedCache; });
254
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
255
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
256
+ /* harmony import */ var _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
257
+ /* harmony import */ var _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
258
+
259
+
260
+
261
+
262
+ /**
263
+ * This is the cache.
264
+ * It's incredibly complex.
265
+ * Very in-memory. So cache. Such complex. Wow.
266
+ */
267
+ const cache = new _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* ScopedInMemoryCache */ "a"]();
268
+ /**
269
+ * Purge the in-memory cache or a single scope within it.
270
+ */
271
+
272
+ const purgeSharedCache = (scope = "") => {
273
+ // If we have a valid scope (empty string is falsy), then clear that scope.
274
+ if (scope && typeof scope === "string") {
275
+ cache.purgeScope(scope);
276
+ } else {
277
+ // Just reset the object. This should be sufficient.
278
+ cache.purgeAll();
279
+ }
280
+ };
281
+ /**
282
+ * Hook to retrieve data from and store data in an in-memory cache.
283
+ *
284
+ * @returns {[?ReadOnlyCacheValue, CacheValueFn]}
285
+ * Returns an array containing the current cache entry (or undefined), a
286
+ * function to set the cache entry (passing null or undefined to this function
287
+ * will delete the entry).
288
+ *
289
+ * To clear a single scope within the cache or the entire cache,
290
+ * the `clearScopedCache` export is available.
291
+ *
292
+ * NOTE: Unlike useState or useReducer, we don't automatically update folks
293
+ * if the value they reference changes. We might add it later (if we need to),
294
+ * but the likelihood here is that things won't be changing in this cache in a
295
+ * way where we would need that. If we do (and likely only in specific
296
+ * circumstances), we should consider adding a simple boolean useState that can
297
+ * be toggled to cause a rerender whenever the referenced cached data changes
298
+ * so that callers can re-render on cache changes. However, we should make
299
+ * sure this toggling is optional - or we could use a callback argument, to
300
+ * achieve this on an as-needed basis.
301
+ */
302
+
303
+ const useSharedCache = (id, scope, initialValue) => {
304
+ // Verify arguments.
305
+ if (!id || typeof id !== "string") {
306
+ throw new _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataError */ "a"]("id must be a non-empty string", _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataErrors */ "b"].InvalidInput);
307
+ }
308
+
309
+ if (!scope || typeof scope !== "string") {
310
+ throw new _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataError */ "a"]("scope must be a non-empty string", _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataErrors */ "b"].InvalidInput);
311
+ } // Memoize our APIs.
312
+ // This one allows callers to set or replace the cached value.
313
+
314
+
315
+ const cacheValue = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](value => value == null ? cache.purge(scope, id) : cache.set(scope, id, value), [id, scope]); // We don't memo-ize the current value, just in case the cache was updated
316
+ // since our last run through. Also, our cache does not know what type it
317
+ // stores, so we have to cast it to the type we're exporting. This is a
318
+ // dev time courtesy, rather than a runtime thing.
319
+ // $FlowIgnore[incompatible-type]
320
+
321
+ let currentValue = cache.get(scope, id); // If we have an initial value, we need to add it to the cache
322
+ // and use it as our current value.
323
+
324
+ if (currentValue == null && initialValue !== undefined) {
325
+ // Get the initial value.
326
+ const value = typeof initialValue === "function" ? initialValue() : initialValue;
327
+
328
+ if (value != null) {
329
+ // Update the cache.
330
+ cacheValue(value); // Make sure we return this value as our current value.
331
+
332
+ currentValue = value;
333
+ }
334
+ } // Now we have everything, let's return it.
335
+
336
+
337
+ return [currentValue, cacheValue];
338
+ };
339
+
340
+ /***/ }),
341
+ /* 6 */
219
342
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
220
343
 
221
344
  "use strict";
@@ -244,17 +367,32 @@ const Status = Object.freeze({
244
367
  });
245
368
 
246
369
  /***/ }),
247
- /* 5 */
370
+ /* 7 */
371
+ /***/ (function(module, exports) {
372
+
373
+ module.exports = require("@khanacademy/wonder-stuff-core");
374
+
375
+ /***/ }),
376
+ /* 8 */
248
377
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
249
378
 
250
379
  "use strict";
251
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SsrCache; });
252
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
253
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
254
- /* harmony import */ var _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
380
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return TrackerContext; });
381
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
382
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
383
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
384
+ /* harmony import */ var _ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
385
+ /* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
255
386
 
256
387
 
257
- const DefaultScope = "default";
388
+
389
+
390
+ /**
391
+ * Used to inject our tracking function into the render framework.
392
+ *
393
+ * INTERNAL USE ONLY
394
+ */
395
+ const TrackerContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
258
396
  /**
259
397
  * The default instance is stored here.
260
398
  * It's created below in the Default() static property.
@@ -262,252 +400,86 @@ const DefaultScope = "default";
262
400
 
263
401
  let _default;
264
402
  /**
265
- * Implements the response cache.
403
+ * Implements request tracking and fulfillment.
266
404
  *
267
405
  * INTERNAL USE ONLY
268
406
  */
269
407
 
270
408
 
271
- class SsrCache {
409
+ class RequestTracker {
272
410
  static get Default() {
273
411
  if (!_default) {
274
- _default = new SsrCache();
412
+ _default = new RequestTracker();
275
413
  }
276
414
 
277
415
  return _default;
278
416
  }
417
+ /**
418
+ * These are the caches for tracked requests, their handlers, and responses.
419
+ */
279
420
 
280
- constructor(hydrationCache = null, ssrOnlyCache = null) {
281
- this.initialize = source => {
282
- if (this._hydrationCache.inUse) {
283
- throw new Error("Cannot initialize data response cache more than once");
284
- }
285
-
286
- this._hydrationCache = new _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SerializableInMemoryCache */ "a"]({
287
- // $FlowIgnore[incompatible-call]
288
- [DefaultScope]: source
289
- });
290
- };
291
-
292
- this.cacheData = (id, data, hydrate) => this._setCachedResponse(id, {
293
- data
294
- }, hydrate);
295
-
296
- this.cacheError = (id, error, hydrate) => {
297
- const errorMessage = typeof error === "string" ? error : error.message;
298
- return this._setCachedResponse(id, {
299
- error: errorMessage
300
- }, hydrate);
301
- };
302
-
303
- this.getEntry = id => {
304
- var _this$_ssrOnlyCache$g, _this$_ssrOnlyCache;
305
-
306
- // Get the cached entry for this value.
307
- // We first look in the ssr cache and then the hydration cache.
308
- const internalEntry = (_this$_ssrOnlyCache$g = (_this$_ssrOnlyCache = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache.get(DefaultScope, id)) != null ? _this$_ssrOnlyCache$g : this._hydrationCache.get(DefaultScope, id); // If we are not server-side and we hydrated something, let's clear
309
- // that from the hydration cache to save memory.
310
-
311
- if (this._ssrOnlyCache == null && internalEntry != null) {
312
- // We now delete this from our hydration cache as we don't need it.
313
- // This does mean that if another handler of the same type but
314
- // without some sort of linked cache won't get the value, but
315
- // that's not an expected use-case. If two different places use the
316
- // same handler and options (i.e. the same request), then the
317
- // handler should cater to that to ensure they share the result.
318
- this._hydrationCache.purge(DefaultScope, id);
319
- } // Getting the typing right between the in-memory cache and this
320
- // is hard. Just telling flow it's OK.
321
- // $FlowIgnore[incompatible-return]
322
421
 
422
+ constructor(responseCache = undefined) {
423
+ this._trackedRequests = {};
323
424
 
324
- return internalEntry;
425
+ this.trackDataRequest = (id, handler, hydrate) => {
426
+ /**
427
+ * If we don't already have this tracked, then let's track it.
428
+ */
429
+ if (this._trackedRequests[id] == null) {
430
+ this._trackedRequests[id] = {
431
+ handler,
432
+ hydrate
433
+ };
434
+ }
325
435
  };
326
436
 
327
- this.remove = id => {
328
- var _this$_ssrOnlyCache$p, _this$_ssrOnlyCache2;
329
-
330
- // NOTE(somewhatabstract): We could invoke removeAll with a predicate
331
- // to match the key of the entry we're removing, but that's an
332
- // inefficient way to remove a single item, so let's not do that.
333
- // Delete the entry from the appropriate cache.
334
- return this._hydrationCache.purge(DefaultScope, id) || ((_this$_ssrOnlyCache$p = (_this$_ssrOnlyCache2 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache2.purge(DefaultScope, id)) != null ? _this$_ssrOnlyCache$p : false);
437
+ this.reset = () => {
438
+ this._trackedRequests = {};
335
439
  };
336
440
 
337
- this.removeAll = predicate => {
338
- var _this$_ssrOnlyCache3;
441
+ this.fulfillTrackedRequests = () => {
442
+ const promises = [];
443
+ const {
444
+ cacheData,
445
+ cacheError
446
+ } = this._responseCache;
339
447
 
340
- const realPredicate = predicate ? // We know what we're putting into the cache so let's assume it
341
- // conforms.
342
- // $FlowIgnore[incompatible-call]
343
- (_, key, cachedEntry) => predicate(key, cachedEntry) : undefined; // Apply the predicate to what we have in our caches.
448
+ for (const requestKey of Object.keys(this._trackedRequests)) {
449
+ const options = this._trackedRequests[requestKey];
344
450
 
345
- this._hydrationCache.purgeAll(realPredicate);
451
+ try {
452
+ promises.push(this._requestFulfillment.fulfill(requestKey, { ...options
453
+ }).then(result => {
454
+ switch (result.status) {
455
+ case "success":
456
+ /**
457
+ * Let's cache the data!
458
+ *
459
+ * NOTE: This only caches when we're
460
+ * server side.
461
+ */
462
+ cacheData(requestKey, result.data, options.hydrate);
463
+ break;
346
464
 
347
- (_this$_ssrOnlyCache3 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache3.purgeAll(realPredicate);
348
- };
349
-
350
- this.cloneHydratableData = () => {
351
- var _cache$DefaultScope;
352
-
353
- // We return our hydration cache only.
354
- const cache = this._hydrationCache.clone(); // If we're empty, we still want to return an object, so we default
355
- // to an empty object.
356
- // We only need the default scope out of our scoped in-memory cache.
357
- // We know that it conforms to our expectations.
358
- // $FlowIgnore[incompatible-return]
359
-
360
-
361
- return (_cache$DefaultScope = cache[DefaultScope]) != null ? _cache$DefaultScope : {};
362
- };
363
-
364
- this._ssrOnlyCache = _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? ssrOnlyCache || new _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SerializableInMemoryCache */ "a"]() : undefined;
365
- this._hydrationCache = hydrationCache || new _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SerializableInMemoryCache */ "a"]();
366
- }
367
-
368
- _setCachedResponse(id, entry, hydrate) {
369
- const frozenEntry = Object.freeze(entry);
370
-
371
- if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
372
- // We are server-side.
373
- // We need to store this value.
374
- if (hydrate) {
375
- this._hydrationCache.set(DefaultScope, id, frozenEntry);
376
- } else {
377
- var _this$_ssrOnlyCache4;
378
-
379
- // Usually, when server-side, this cache will always be present.
380
- // We do fake server-side in our doc example though, when it
381
- // won't be.
382
- (_this$_ssrOnlyCache4 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache4.set(DefaultScope, id, frozenEntry);
383
- }
384
- }
385
-
386
- return frozenEntry;
387
- }
388
- /**
389
- * Initialize the cache from a given cache state.
390
- *
391
- * This can only be called if the cache is not already in use.
392
- */
393
-
394
-
395
- }
396
-
397
- /***/ }),
398
- /* 6 */
399
- /***/ (function(module, exports) {
400
-
401
- module.exports = require("@khanacademy/wonder-stuff-core");
402
-
403
- /***/ }),
404
- /* 7 */
405
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
406
-
407
- "use strict";
408
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return TrackerContext; });
409
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
410
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
411
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
412
- /* harmony import */ var _ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
413
- /* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
414
-
415
-
416
-
417
-
418
- /**
419
- * Used to inject our tracking function into the render framework.
420
- *
421
- * INTERNAL USE ONLY
422
- */
423
- const TrackerContext = new react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
424
- /**
425
- * The default instance is stored here.
426
- * It's created below in the Default() static property.
427
- */
428
-
429
- let _default;
430
- /**
431
- * Implements request tracking and fulfillment.
432
- *
433
- * INTERNAL USE ONLY
434
- */
435
-
436
-
437
- class RequestTracker {
438
- static get Default() {
439
- if (!_default) {
440
- _default = new RequestTracker();
441
- }
442
-
443
- return _default;
444
- }
445
- /**
446
- * These are the caches for tracked requests, their handlers, and responses.
447
- */
448
-
449
-
450
- constructor(responseCache = undefined) {
451
- this._trackedRequests = {};
452
-
453
- this.trackDataRequest = (id, handler, hydrate) => {
454
- /**
455
- * If we don't already have this tracked, then let's track it.
456
- */
457
- if (this._trackedRequests[id] == null) {
458
- this._trackedRequests[id] = {
459
- handler,
460
- hydrate
461
- };
462
- }
463
- };
464
-
465
- this.reset = () => {
466
- this._trackedRequests = {};
467
- };
468
-
469
- this.fulfillTrackedRequests = () => {
470
- const promises = [];
471
- const {
472
- cacheData,
473
- cacheError
474
- } = this._responseCache;
475
-
476
- for (const requestKey of Object.keys(this._trackedRequests)) {
477
- const options = this._trackedRequests[requestKey];
478
-
479
- try {
480
- promises.push(this._requestFulfillment.fulfill(requestKey, { ...options
481
- }).then(result => {
482
- switch (result.status) {
483
- case "success":
484
- /**
485
- * Let's cache the data!
486
- *
487
- * NOTE: This only caches when we're
488
- * server side.
489
- */
490
- cacheData(requestKey, result.data, options.hydrate);
491
- break;
492
-
493
- case "error":
494
- /**
495
- * Let's cache the error!
496
- *
497
- * NOTE: This only caches when we're
498
- * server side.
499
- */
500
- cacheError(requestKey, result.error, options.hydrate);
501
- break;
502
- } // For status === "loading":
503
- // Could never get here unless we wrote
504
- // the code wrong. Rather than bloat
505
- // code with useless error, just ignore.
506
- // For status === "aborted":
507
- // We won't cache this.
508
- // We don't hydrate aborted requests,
509
- // so the client would just see them
510
- // as unfulfilled data.
465
+ case "error":
466
+ /**
467
+ * Let's cache the error!
468
+ *
469
+ * NOTE: This only caches when we're
470
+ * server side.
471
+ */
472
+ cacheError(requestKey, result.error, options.hydrate);
473
+ break;
474
+ } // For status === "loading":
475
+ // Could never get here unless we wrote
476
+ // the code wrong. Rather than bloat
477
+ // code with useless error, just ignore.
478
+ // For status === "aborted":
479
+ // We won't cache this.
480
+ // We don't hydrate aborted requests,
481
+ // so the client would just see them
482
+ // as unfulfilled data.
511
483
 
512
484
 
513
485
  return;
@@ -579,105 +551,247 @@ class RequestTracker {
579
551
  }
580
552
 
581
553
  /***/ }),
582
- /* 8 */
554
+ /* 9 */
583
555
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
584
556
 
585
557
  "use strict";
586
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return clearSharedCache; });
587
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return useSharedCache; });
588
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
589
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
590
- /* harmony import */ var _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
591
- /* harmony import */ var _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
592
-
593
-
558
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SsrCache; });
559
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
560
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
561
+ /* harmony import */ var _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13);
594
562
 
595
563
 
564
+ const DefaultScope = "default";
596
565
  /**
597
- * This is the cache.
598
- * It's incredibly complex.
599
- * Very in-memory. So cache. Such complex. Wow.
600
- */
601
- const cache = new _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* ScopedInMemoryCache */ "a"]();
602
- /**
603
- * Clear the in-memory cache or a single scope within it.
566
+ * The default instance is stored here.
567
+ * It's created below in the Default() static property.
604
568
  */
605
569
 
606
- const clearSharedCache = (scope = "") => {
607
- // If we have a valid scope (empty string is falsy), then clear that scope.
608
- if (scope && typeof scope === "string") {
609
- cache.purgeScope(scope);
610
- } else {
611
- // Just reset the object. This should be sufficient.
612
- cache.purgeAll();
613
- }
614
- };
570
+ let _default;
615
571
  /**
616
- * Hook to retrieve data from and store data in an in-memory cache.
617
- *
618
- * @returns {[?ReadOnlyCacheValue, CacheValueFn]}
619
- * Returns an array containing the current cache entry (or undefined), a
620
- * function to set the cache entry (passing null or undefined to this function
621
- * will delete the entry).
622
- *
623
- * To clear a single scope within the cache or the entire cache,
624
- * the `clearScopedCache` export is available.
572
+ * Implements the response cache.
625
573
  *
626
- * NOTE: Unlike useState or useReducer, we don't automatically update folks
627
- * if the value they reference changes. We might add it later (if we need to),
628
- * but the likelihood here is that things won't be changing in this cache in a
629
- * way where we would need that. If we do (and likely only in specific
630
- * circumstances), we should consider adding a simple boolean useState that can
631
- * be toggled to cause a rerender whenever the referenced cached data changes
632
- * so that callers can re-render on cache changes. However, we should make
633
- * sure this toggling is optional - or we could use a callback argument, to
634
- * achieve this on an as-needed basis.
574
+ * INTERNAL USE ONLY
635
575
  */
636
576
 
637
- const useSharedCache = (id, scope, initialValue) => {
638
- // Verify arguments.
639
- if (!id || typeof id !== "string") {
640
- throw new _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataError */ "a"]("id must be a non-empty string", _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataErrors */ "b"].InvalidInput);
641
- }
642
-
643
- if (!scope || typeof scope !== "string") {
644
- throw new _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataError */ "a"]("scope must be a non-empty string", _util_data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataErrors */ "b"].InvalidInput);
645
- } // Memoize our APIs.
646
- // This one allows callers to set or replace the cached value.
647
577
 
578
+ class SsrCache {
579
+ static get Default() {
580
+ if (!_default) {
581
+ _default = new SsrCache();
582
+ }
648
583
 
649
- const cacheValue = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](value => value == null ? cache.purge(scope, id) : cache.set(scope, id, value), [id, scope]); // We don't memo-ize the current value, just in case the cache was updated
650
- // since our last run through. Also, our cache does not know what type it
651
- // stores, so we have to cast it to the type we're exporting. This is a
652
- // dev time courtesy, rather than a runtime thing.
653
- // $FlowIgnore[incompatible-type]
584
+ return _default;
585
+ }
654
586
 
655
- let currentValue = cache.get(scope, id); // If we have an initial value, we need to add it to the cache
656
- // and use it as our current value.
587
+ constructor(hydrationCache = null, ssrOnlyCache = null) {
588
+ this.initialize = source => {
589
+ if (this._hydrationCache.inUse) {
590
+ throw new Error("Cannot initialize data response cache more than once");
591
+ }
657
592
 
658
- if (currentValue == null && initialValue !== undefined) {
659
- // Get the initial value.
660
- const value = typeof initialValue === "function" ? initialValue() : initialValue;
593
+ this._hydrationCache = new _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SerializableInMemoryCache */ "a"]({
594
+ // $FlowIgnore[incompatible-call]
595
+ [DefaultScope]: source
596
+ });
597
+ };
661
598
 
662
- if (value != null) {
663
- // Update the cache.
664
- cacheValue(value); // Make sure we return this value as our current value.
599
+ this.cacheData = (id, data, hydrate) => this._setCachedResponse(id, {
600
+ data
601
+ }, hydrate);
665
602
 
666
- currentValue = value;
667
- }
668
- } // Now we have everything, let's return it.
603
+ this.cacheError = (id, error, hydrate) => {
604
+ const errorMessage = typeof error === "string" ? error : error.message;
605
+ return this._setCachedResponse(id, {
606
+ error: errorMessage
607
+ }, hydrate);
608
+ };
669
609
 
610
+ this.getEntry = id => {
611
+ var _this$_ssrOnlyCache$g, _this$_ssrOnlyCache;
670
612
 
671
- return [currentValue, cacheValue];
672
- };
613
+ // Get the cached entry for this value.
614
+ // We first look in the ssr cache and then the hydration cache.
615
+ const internalEntry = (_this$_ssrOnlyCache$g = (_this$_ssrOnlyCache = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache.get(DefaultScope, id)) != null ? _this$_ssrOnlyCache$g : this._hydrationCache.get(DefaultScope, id); // If we are not server-side and we hydrated something, let's clear
616
+ // that from the hydration cache to save memory.
617
+
618
+ if (this._ssrOnlyCache == null && internalEntry != null) {
619
+ // We now delete this from our hydration cache as we don't need it.
620
+ // This does mean that if another handler of the same type but
621
+ // without some sort of linked cache won't get the value, but
622
+ // that's not an expected use-case. If two different places use the
623
+ // same handler and options (i.e. the same request), then the
624
+ // handler should cater to that to ensure they share the result.
625
+ this._hydrationCache.purge(DefaultScope, id);
626
+ } // Getting the typing right between the in-memory cache and this
627
+ // is hard. Just telling flow it's OK.
628
+ // $FlowIgnore[incompatible-return]
629
+
630
+
631
+ return internalEntry;
632
+ };
633
+
634
+ this.purgeData = predicate => {
635
+ var _this$_ssrOnlyCache2;
636
+
637
+ const realPredicate = predicate ? // We know what we're putting into the cache so let's assume it
638
+ // conforms.
639
+ // $FlowIgnore[incompatible-call]
640
+ (_, key, cachedEntry) => predicate(key, cachedEntry) : undefined; // Apply the predicate to what we have in our caches.
641
+
642
+ this._hydrationCache.purgeAll(realPredicate);
643
+
644
+ (_this$_ssrOnlyCache2 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache2.purgeAll(realPredicate);
645
+ };
646
+
647
+ this.cloneHydratableData = () => {
648
+ var _cache$DefaultScope;
649
+
650
+ // We return our hydration cache only.
651
+ const cache = this._hydrationCache.clone(); // If we're empty, we still want to return an object, so we default
652
+ // to an empty object.
653
+ // We only need the default scope out of our scoped in-memory cache.
654
+ // We know that it conforms to our expectations.
655
+ // $FlowIgnore[incompatible-return]
656
+
657
+
658
+ return (_cache$DefaultScope = cache[DefaultScope]) != null ? _cache$DefaultScope : {};
659
+ };
660
+
661
+ this._ssrOnlyCache = _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? ssrOnlyCache || new _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SerializableInMemoryCache */ "a"]() : undefined;
662
+ this._hydrationCache = hydrationCache || new _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SerializableInMemoryCache */ "a"]();
663
+ }
664
+
665
+ _setCachedResponse(id, entry, hydrate) {
666
+ const frozenEntry = Object.freeze(entry);
667
+
668
+ if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
669
+ // We are server-side.
670
+ // We need to store this value.
671
+ if (hydrate) {
672
+ this._hydrationCache.set(DefaultScope, id, frozenEntry);
673
+ } else {
674
+ var _this$_ssrOnlyCache3;
675
+
676
+ // Usually, when server-side, this cache will always be present.
677
+ // We do fake server-side in our doc example though, when it
678
+ // won't be.
679
+ (_this$_ssrOnlyCache3 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache3.set(DefaultScope, id, frozenEntry);
680
+ }
681
+ }
682
+
683
+ return frozenEntry;
684
+ }
685
+ /**
686
+ * Initialize the cache from a given cache state.
687
+ *
688
+ * This can only be called if the cache is not already in use.
689
+ */
690
+
691
+
692
+ }
673
693
 
674
694
  /***/ }),
675
- /* 9 */
695
+ /* 10 */
696
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
697
+
698
+ "use strict";
699
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
700
+ /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
701
+
702
+
703
+ let _default;
704
+ /**
705
+ * This fulfills a request, making sure that in-flight requests are shared.
706
+ */
707
+
708
+
709
+ class RequestFulfillment {
710
+ constructor() {
711
+ this._requests = {};
712
+
713
+ this.fulfill = (id, {
714
+ handler,
715
+ hydrate = true
716
+ }) => {
717
+ /**
718
+ * If we have an inflight request, we'll provide that.
719
+ */
720
+ const inflight = this._requests[id];
721
+
722
+ if (inflight) {
723
+ return inflight;
724
+ }
725
+ /**
726
+ * We don't have an inflight request, so let's set one up.
727
+ */
728
+
729
+
730
+ const request = handler().then(data => ({
731
+ status: "success",
732
+ data
733
+ })).catch(error => {
734
+ const actualError = typeof error === "string" ? new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("Request failed", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].Unknown, {
735
+ metadata: {
736
+ unexpectedError: error
737
+ }
738
+ }) : error; // Return aborted result if the request was aborted.
739
+ // The only way to detect this reliably, it seems, is to
740
+ // check the error name and see if it's "AbortError" (this
741
+ // is also what Apollo does).
742
+ // Even then, it's reliant on the handler supporting aborts.
743
+ // TODO(somewhatabstract, FEI-4276): Add first class abort
744
+ // support to the handler API.
745
+
746
+ if (actualError.name === "AbortError") {
747
+ return {
748
+ status: "aborted"
749
+ };
750
+ }
751
+
752
+ return {
753
+ status: "error",
754
+ error: actualError
755
+ };
756
+ }).finally(() => {
757
+ delete this._requests[id];
758
+ }); // Store the request in our cache.
759
+
760
+ this._requests[id] = request;
761
+ return request;
762
+ };
763
+
764
+ this.abort = id => {
765
+ // TODO(somewhatabstract, FEI-4276): Add first class abort
766
+ // support to the handler API.
767
+ // For now, we will just clear the request out of the list.
768
+ // When abort is implemented, the `finally` in the `fulfill` method
769
+ // would handle the deletion.
770
+ delete this._requests[id];
771
+ };
772
+
773
+ this.abortAll = () => {
774
+ Object.keys(this._requests).forEach(id => this.abort(id));
775
+ };
776
+ }
777
+
778
+ static get Default() {
779
+ if (!_default) {
780
+ _default = new RequestFulfillment();
781
+ }
782
+
783
+ return _default;
784
+ }
785
+
786
+ }
787
+
788
+ /***/ }),
789
+ /* 11 */
676
790
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
677
791
 
678
792
  "use strict";
679
793
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ScopedInMemoryCache; });
680
- /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
794
+ /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
681
795
 
682
796
 
683
797
  /**
@@ -796,109 +910,32 @@ class ScopedInMemoryCache {
796
910
  }
797
911
 
798
912
  /***/ }),
799
- /* 10 */
800
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
801
-
802
- "use strict";
803
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
804
- /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
805
-
806
-
807
- let _default;
808
- /**
809
- * This fulfills a request, making sure that in-flight requests are shared.
810
- */
811
-
812
-
813
- class RequestFulfillment {
814
- constructor() {
815
- this._requests = {};
816
-
817
- this.fulfill = (id, {
818
- handler,
819
- hydrate = true
820
- }) => {
821
- /**
822
- * If we have an inflight request, we'll provide that.
823
- */
824
- const inflight = this._requests[id];
825
-
826
- if (inflight) {
827
- return inflight;
828
- }
829
- /**
830
- * We don't have an inflight request, so let's set one up.
831
- */
832
-
833
-
834
- const request = handler().then(data => ({
835
- status: "success",
836
- data
837
- })).catch(error => {
838
- const actualError = typeof error === "string" ? new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("Request failed", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].Unknown, {
839
- metadata: {
840
- unexpectedError: error
841
- }
842
- }) : error; // Return aborted result if the request was aborted.
843
- // The only way to detect this reliably, it seems, is to
844
- // check the error name and see if it's "AbortError" (this
845
- // is also what Apollo does).
846
- // Even then, it's reliant on the handler supporting aborts.
847
- // TODO(somewhatabstract, FEI-4276): Add first class abort
848
- // support to the handler API.
849
-
850
- if (actualError.name === "AbortError") {
851
- return {
852
- status: "aborted"
853
- };
854
- }
855
-
856
- return {
857
- status: "error",
858
- error: actualError
859
- };
860
- }).finally(() => {
861
- delete this._requests[id];
862
- }); // Store the request in our cache.
863
-
864
- this._requests[id] = request;
865
- return request;
866
- };
867
- }
868
-
869
- static get Default() {
870
- if (!_default) {
871
- _default = new RequestFulfillment();
872
- }
873
-
874
- return _default;
875
- }
876
-
877
- }
878
-
879
- /***/ }),
880
- /* 11 */
913
+ /* 12 */
881
914
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
882
915
 
883
916
  "use strict";
884
917
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return WhenClientSide; });
885
918
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return useHydratableEffect; });
886
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
919
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
887
920
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
888
- /* harmony import */ var _util_abort_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(24);
889
- /* harmony import */ var _use_server_effect_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(14);
890
- /* harmony import */ var _use_shared_cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
891
- /* harmony import */ var _use_cached_effect_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(15);
921
+ /* harmony import */ var _use_server_effect_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(16);
922
+ /* harmony import */ var _use_shared_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
923
+ /* harmony import */ var _use_cached_effect_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(17);
924
+ /* harmony import */ var _util_types_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(2);
892
925
 
893
926
 
894
927
 
928
+ // TODO(somewhatabstract, FEI-4174): Update eslint-plugin-import when they
929
+ // have fixed:
930
+ // https://github.com/import-js/eslint-plugin-import/issues/2073
931
+ // eslint-disable-next-line import/named
895
932
 
896
933
 
897
934
 
898
935
  /**
899
936
  * Policies to define how a hydratable effect should behave client-side.
900
937
  */
901
- const WhenClientSide = __webpack_require__(29).Mirrored(["DoNotHydrate", "ExecuteWhenNoResult", "ExecuteWhenNoSuccessResult", "AlwaysExecute"]);
938
+ const WhenClientSide = __webpack_require__(22).Mirrored(["DoNotHydrate", "ExecuteWhenNoResult", "ExecuteWhenNoSuccessResult", "AlwaysExecute"]);
902
939
  const DefaultScope = "useHydratableEffect";
903
940
  /**
904
941
  * Hook to execute an async operation on server and client.
@@ -922,10 +959,11 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
922
959
  // When client-side, this will look up any response for hydration; it does
923
960
  // not invoke the handler.
924
961
 
925
- const serverResult = Object(_use_server_effect_js__WEBPACK_IMPORTED_MODULE_2__[/* useServerEffect */ "a"])(requestId, // If we're skipped (unlikely in server worlds, but maybe),
926
- // just give an aborted response.
927
- skip ? () => Promise.reject(new _util_abort_error_js__WEBPACK_IMPORTED_MODULE_1__[/* AbortError */ "a"]("skipped")) : handler, // Only hydrate if our behavior isn't telling us not to.
928
- clientBehavior !== WhenClientSide.DoNotHydrate);
962
+ const serverResult = Object(_use_server_effect_js__WEBPACK_IMPORTED_MODULE_1__[/* useServerEffect */ "a"])(requestId, handler, {
963
+ // Only hydrate if our behavior isn't telling us not to.
964
+ hydrate: clientBehavior !== WhenClientSide.DoNotHydrate,
965
+ skip
966
+ });
929
967
  const getDefaultCacheValue = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](() => {
930
968
  // If we don't have a requestId, it's our first render, the one
931
969
  // where we hydrated. So defer to our clientBehavior value.
@@ -965,15 +1003,17 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
965
1003
  }, [serverResult]); // Instead of using state, which would be local to just this hook instance,
966
1004
  // we use a shared in-memory cache.
967
1005
 
968
- Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_3__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
1006
+ Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
969
1007
  scope, // The scope of the cached items
970
1008
  getDefaultCacheValue); // When we're client-side, we ultimately want the result from this call.
971
1009
 
972
- const clientResult = Object(_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_4__[/* useCachedEffect */ "a"])(requestId, handler, {
1010
+ const [clientResult] = Object(_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_3__[/* useCachedEffect */ "a"])(requestId, handler, {
973
1011
  skip,
974
1012
  onResultChanged,
975
1013
  retainResultOnChange,
976
- scope
1014
+ scope,
1015
+ // Be explicit about our fetch policy for clarity.
1016
+ fetchPolicy: _util_types_js__WEBPACK_IMPORTED_MODULE_4__[/* FetchPolicy */ "a"].CacheBeforeNetwork
977
1017
  }); // OK, now which result do we return.
978
1018
  // Well, we return the serverResult on our very first call and then
979
1019
  // the clientResult thereafter. The great thing is that after the very
@@ -983,15 +1023,15 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
983
1023
  };
984
1024
 
985
1025
  /***/ }),
986
- /* 12 */
1026
+ /* 13 */
987
1027
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
988
1028
 
989
1029
  "use strict";
990
1030
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SerializableInMemoryCache; });
991
- /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
1031
+ /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7);
992
1032
  /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__);
993
- /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
994
- /* harmony import */ var _scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
1033
+ /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
1034
+ /* harmony import */ var _scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
995
1035
 
996
1036
 
997
1037
 
@@ -1033,11 +1073,11 @@ class SerializableInMemoryCache extends _scoped_in_memory_cache_js__WEBPACK_IMPO
1033
1073
  }
1034
1074
 
1035
1075
  /***/ }),
1036
- /* 13 */
1076
+ /* 14 */
1037
1077
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1038
1078
 
1039
1079
  "use strict";
1040
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1080
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1041
1081
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1042
1082
 
1043
1083
 
@@ -1050,21 +1090,49 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
1050
1090
  /* harmony default export */ __webpack_exports__["a"] = (InterceptContext);
1051
1091
 
1052
1092
  /***/ }),
1053
- /* 14 */
1093
+ /* 15 */
1054
1094
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1055
1095
 
1056
1096
  "use strict";
1057
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useServerEffect; });
1058
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
1059
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
1060
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
1061
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
1062
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
1063
- /* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
1064
- /* harmony import */ var _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(25);
1065
- /* harmony import */ var _use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(16);
1066
-
1067
-
1097
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return initializeHydrationCache; });
1098
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return purgeHydrationCache; });
1099
+ /* harmony import */ var _ssr_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
1100
+
1101
+
1102
+ /**
1103
+ * Initialize the hydration cache.
1104
+ *
1105
+ * @param {ResponseCache} source The cache content to use for initializing the
1106
+ * cache.
1107
+ * @throws {Error} If the cache is already initialized.
1108
+ */
1109
+ const initializeHydrationCache = source => _ssr_cache_js__WEBPACK_IMPORTED_MODULE_0__[/* SsrCache */ "a"].Default.initialize(source);
1110
+ /**
1111
+ * Purge cached hydration responses that match the given predicate.
1112
+ *
1113
+ * @param {(id: string) => boolean} [predicate] The predicate to match against
1114
+ * the cached hydration responses. If no predicate is provided, all cached
1115
+ * hydration responses will be purged.
1116
+ */
1117
+
1118
+ const purgeHydrationCache = predicate => _ssr_cache_js__WEBPACK_IMPORTED_MODULE_0__[/* SsrCache */ "a"].Default.purgeData(predicate);
1119
+
1120
+ /***/ }),
1121
+ /* 16 */
1122
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1123
+
1124
+ "use strict";
1125
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useServerEffect; });
1126
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
1127
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
1128
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
1129
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
1130
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
1131
+ /* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
1132
+ /* harmony import */ var _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(32);
1133
+ /* harmony import */ var _use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(19);
1134
+
1135
+
1068
1136
 
1069
1137
 
1070
1138
 
@@ -1085,22 +1153,26 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
1085
1153
  *
1086
1154
  * The asynchronous action is never invoked on the client-side.
1087
1155
  */
1088
- const useServerEffect = (requestId, handler, hydrate = true) => {
1089
- // Plug in to the request interception framework for code that wants
1156
+ const useServerEffect = (requestId, handler, options = {}) => {
1157
+ const {
1158
+ hydrate = true,
1159
+ skip = false
1160
+ } = options; // Plug in to the request interception framework for code that wants
1090
1161
  // to use that.
1162
+
1091
1163
  const interceptedHandler = Object(_use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__[/* useRequestInterception */ "a"])(requestId, handler); // If we're server-side or hydrating, we'll have a cached entry to use.
1092
1164
  // So we get that and use it to initialize our state.
1093
1165
  // This works in both hydration and SSR because the very first call to
1094
1166
  // this will have cached data in those cases as it will be present on the
1095
1167
  // initial render - and subsequent renders on the client it will be null.
1096
1168
 
1097
- const cachedResult = _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__[/* SsrCache */ "a"].Default.getEntry(requestId); // We only track data requests when we are server-side and we don't
1098
- // already have a result, as given by the cachedData (which is also the
1099
- // initial value for the result state).
1169
+ const cachedResult = _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__[/* SsrCache */ "a"].Default.getEntry(requestId); // We only track data requests when we are server-side, we are not skipping
1170
+ // the request, and we don't already have a result, as given by the
1171
+ // cachedData (which is also the initial value for the result state).
1100
1172
 
1101
1173
  const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* TrackerContext */ "b"]);
1102
1174
 
1103
- if (cachedResult == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
1175
+ if (!skip && cachedResult == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
1104
1176
  maybeTrack == null ? void 0 : maybeTrack(requestId, interceptedHandler, hydrate);
1105
1177
  } // A null result means there was no result to hydrate.
1106
1178
 
@@ -1109,24 +1181,32 @@ const useServerEffect = (requestId, handler, hydrate = true) => {
1109
1181
  };
1110
1182
 
1111
1183
  /***/ }),
1112
- /* 15 */
1184
+ /* 17 */
1113
1185
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1114
1186
 
1115
1187
  "use strict";
1116
1188
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useCachedEffect; });
1117
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1189
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1118
1190
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1119
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
1191
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
1120
1192
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
1121
- /* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
1122
- /* harmony import */ var _util_status_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
1123
- /* harmony import */ var _use_shared_cache_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8);
1124
- /* harmony import */ var _use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(16);
1193
+ /* harmony import */ var _util_data_error_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(0);
1194
+ /* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(10);
1195
+ /* harmony import */ var _util_status_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6);
1196
+ /* harmony import */ var _use_shared_cache_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5);
1197
+ /* harmony import */ var _use_request_interception_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19);
1198
+ /* harmony import */ var _util_types_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(2);
1199
+
1200
+
1125
1201
 
1126
1202
 
1127
1203
 
1128
1204
 
1129
1205
 
1206
+ // TODO(somewhatabstract, FEI-4174): Update eslint-plugin-import when they
1207
+ // have fixed:
1208
+ // https://github.com/import-js/eslint-plugin-import/issues/2073
1209
+ // eslint-disable-next-line import/named
1130
1210
 
1131
1211
  const DefaultScope = "useCachedEffect";
1132
1212
  /**
@@ -1147,7 +1227,10 @@ const DefaultScope = "useCachedEffect";
1147
1227
  */
1148
1228
 
1149
1229
  const useCachedEffect = (requestId, handler, options = {}) => {
1230
+ var _ref;
1231
+
1150
1232
  const {
1233
+ fetchPolicy = _util_types_js__WEBPACK_IMPORTED_MODULE_7__[/* FetchPolicy */ "a"].CacheBeforeNetwork,
1151
1234
  skip: hardSkip = false,
1152
1235
  retainResultOnChange = false,
1153
1236
  onResultChanged,
@@ -1155,124 +1238,263 @@ const useCachedEffect = (requestId, handler, options = {}) => {
1155
1238
  } = options; // Plug in to the request interception framework for code that wants
1156
1239
  // to use that.
1157
1240
 
1158
- const interceptedHandler = Object(_use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__[/* useRequestInterception */ "a"])(requestId, handler); // Instead of using state, which would be local to just this hook instance,
1241
+ const interceptedHandler = Object(_use_request_interception_js__WEBPACK_IMPORTED_MODULE_6__[/* useRequestInterception */ "a"])(requestId, handler); // Instead of using state, which would be local to just this hook instance,
1159
1242
  // we use a shared in-memory cache.
1160
1243
 
1161
- const [mostRecentResult, setMostRecentResult] = Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_4__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
1244
+ const [mostRecentResult, setMostRecentResult] = Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_5__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
1162
1245
  scope // The scope of the cached items
1163
1246
  // No default value. We don't want the loading status there; to ensure
1164
1247
  // that all calls when the request is in-flight will update once that
1165
1248
  // request is done, we want the cache to be empty until that point.
1166
- ); // Build a function that will update the cache and either invoke the
1167
- // callback provided in options, or force an update.
1168
-
1169
- const forceUpdate = Object(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__["useForceUpdate"])();
1170
- const setCacheAndNotify = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](value => {
1171
- setMostRecentResult(value); // If our caller provided a cacheUpdated callback, we use that.
1172
- // Otherwise, we toggle our little state update.
1173
-
1174
- if (onResultChanged != null) {
1175
- onResultChanged(value);
1176
- } else {
1177
- forceUpdate();
1178
- }
1179
- }, [setMostRecentResult, onResultChanged, forceUpdate]); // We need to trigger a re-render when the request ID changes as that
1180
- // indicates its a different request. We don't default the current id as
1181
- // this is a proxy for the first render, where we will make the request
1182
- // if we don't already have a cached value.
1183
-
1184
- const requestIdRef = react__WEBPACK_IMPORTED_MODULE_0__["useRef"]();
1185
- const previousRequestId = requestIdRef.current; // Calculate our soft skip state.
1186
- // Soft skip changes are things that should skip the effect if something
1187
- // else triggers the effect to run, but should not itself trigger the effect
1188
- // (which would cancel a previous invocation).
1189
-
1190
- const softSkip = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => {
1191
- if (requestId === previousRequestId) {
1192
- // If the requestId is unchanged, it means we already rendered at
1193
- // least once and so we already made the request at least once. So
1194
- // we can bail out right here.
1195
- return true;
1196
- } // If we already have a cached value, we're going to skip.
1197
-
1198
-
1199
- if (mostRecentResult != null) {
1200
- return true;
1249
+ );
1250
+ const forceUpdate = Object(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__["useForceUpdate"])(); // For the NetworkOnly fetch policy, we ignore the cached value.
1251
+ // So we need somewhere else to store the network value.
1252
+
1253
+ const networkResultRef = react__WEBPACK_IMPORTED_MODULE_0__["useRef"](); // Set up the function that will do the fetching.
1254
+
1255
+ const currentRequestRef = react__WEBPACK_IMPORTED_MODULE_0__["useRef"]();
1256
+ const fetchRequest = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => {
1257
+ var _currentRequestRef$cu;
1258
+
1259
+ // We aren't using useCallback here because we need to make sure that
1260
+ // if we are rememo-izing, we cancel any inflight request for the old
1261
+ // callback.
1262
+ (_currentRequestRef$cu = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu.cancel();
1263
+ currentRequestRef.current = null;
1264
+ networkResultRef.current = null;
1265
+
1266
+ const fetchFn = () => {
1267
+ var _currentRequestRef$cu2, _currentRequestRef$cu3;
1268
+
1269
+ if (fetchPolicy === _util_types_js__WEBPACK_IMPORTED_MODULE_7__[/* FetchPolicy */ "a"].CacheOnly) {
1270
+ throw new _util_data_error_js__WEBPACK_IMPORTED_MODULE_2__[/* DataError */ "a"]("Cannot fetch with CacheOnly policy", _util_data_error_js__WEBPACK_IMPORTED_MODULE_2__[/* DataErrors */ "b"].NotAllowed);
1271
+ } // We use our request fulfillment here so that in-flight
1272
+ // requests are shared. In order to ensure that we don't share
1273
+ // in-flight requests for different scopes, we add the scope to the
1274
+ // requestId.
1275
+ // We do this as a courtesy to simplify usage in sandboxed
1276
+ // uses like storybook where we want each story to perform their
1277
+ // own requests from scratch and not share inflight requests across
1278
+ // stories.
1279
+ // Since this only occurs here, nothing else will care about this
1280
+ // change except the request tracking.
1281
+
1282
+
1283
+ const request = _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_3__[/* RequestFulfillment */ "a"].Default.fulfill(`${requestId}|${scope}`, {
1284
+ handler: interceptedHandler
1285
+ });
1286
+
1287
+ if (request === ((_currentRequestRef$cu2 = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu2.request)) {
1288
+ // The request inflight is the same, so do nothing.
1289
+ // NOTE: Perhaps if invoked via a refetch, we will want to
1290
+ // override this behavior and force a new request?
1291
+ return;
1292
+ } // Clear the last network result.
1293
+
1294
+
1295
+ networkResultRef.current = null; // Cancel the previous request.
1296
+
1297
+ (_currentRequestRef$cu3 = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu3.cancel(); // TODO(somewhatabstract, FEI-4276):
1298
+ // Until our RequestFulfillment API supports cancelling/aborting, we
1299
+ // will have to do it.
1300
+
1301
+ let cancel = false; // NOTE: Our request fulfillment handles the error cases here.
1302
+ // Catching shouldn't serve a purpose.
1303
+ // eslint-disable-next-line promise/catch-or-return
1304
+
1305
+ request.then(result => {
1306
+ currentRequestRef.current = null;
1307
+
1308
+ if (cancel) {
1309
+ // We don't modify our result if the request was cancelled
1310
+ // as it means that this hook no longer cares about that old
1311
+ // request.
1312
+ return;
1313
+ } // Now we need to update the cache and notify or force a rerender.
1314
+
1315
+
1316
+ setMostRecentResult(result);
1317
+ networkResultRef.current = result;
1318
+
1319
+ if (onResultChanged != null) {
1320
+ // If we have a callback, call it to let our caller know we
1321
+ // got a result.
1322
+ onResultChanged(result);
1323
+ } else {
1324
+ // If there's no callback, and this is using cache in some
1325
+ // capacity, just force a rerender.
1326
+ forceUpdate();
1327
+ }
1328
+
1329
+ return; // Shut up eslint always-return rule.
1330
+ });
1331
+ currentRequestRef.current = {
1332
+ requestId,
1333
+ request,
1334
+
1335
+ cancel() {
1336
+ cancel = true;
1337
+ _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_3__[/* RequestFulfillment */ "a"].Default.abort(requestId);
1338
+ }
1339
+
1340
+ };
1341
+ }; // Now we can return the new fetch function.
1342
+
1343
+
1344
+ return fetchFn; // We deliberately ignore the handler here because we want folks to use
1345
+ // interceptor functions inline in props for simplicity. This is OK
1346
+ // since changing the handler without changing the requestId doesn't
1347
+ // really make sense - the same requestId should be handled the same as
1348
+ // each other.
1349
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1350
+ }, [requestId, onResultChanged, forceUpdate, setMostRecentResult, fetchPolicy]); // We need to trigger a re-render when the request ID changes as that
1351
+ // indicates its a different request.
1352
+
1353
+ const requestIdRef = react__WEBPACK_IMPORTED_MODULE_0__["useRef"](requestId); // Calculate if we want to fetch the result or not.
1354
+ // If this is true, we will do a new fetch, cancelling the previous fetch
1355
+ // if there is one inflight.
1356
+
1357
+ const shouldFetch = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => {
1358
+ if (hardSkip) {
1359
+ // We don't fetch if we've been told to hard skip.
1360
+ return false;
1201
1361
  }
1202
1362
 
1203
- return false;
1204
- }, [requestId, previousRequestId, mostRecentResult]); // So now we make sure the client-side request happens per our various
1205
- // options.
1363
+ switch (fetchPolicy) {
1364
+ case _util_types_js__WEBPACK_IMPORTED_MODULE_7__[/* FetchPolicy */ "a"].CacheOnly:
1365
+ // Don't want to do a network request if we're only
1366
+ // interested in the cache.
1367
+ return false;
1368
+
1369
+ case _util_types_js__WEBPACK_IMPORTED_MODULE_7__[/* FetchPolicy */ "a"].CacheBeforeNetwork:
1370
+ // If we don't have a cached value or this is a new requestId,
1371
+ // then we need to fetch.
1372
+ return mostRecentResult == null || requestId !== requestIdRef.current;
1206
1373
 
1374
+ case _util_types_js__WEBPACK_IMPORTED_MODULE_7__[/* FetchPolicy */ "a"].CacheAndNetwork:
1375
+ case _util_types_js__WEBPACK_IMPORTED_MODULE_7__[/* FetchPolicy */ "a"].NetworkOnly:
1376
+ // We don't care about the cache. If we don't have a network
1377
+ // result, then we need to fetch one.
1378
+ return networkResultRef.current == null;
1379
+ }
1380
+ }, [requestId, mostRecentResult, fetchPolicy, hardSkip]); // Let's make sure our ref is set to the most recent requestId.
1381
+
1382
+ requestIdRef.current = requestId;
1207
1383
  react__WEBPACK_IMPORTED_MODULE_0__["useEffect"](() => {
1208
- let cancel = false; // We don't do anything if we've been told to hard skip (a hard skip
1209
- // means we should cancel the previous request and is therefore a
1210
- // dependency on that), or we have determined we have already done
1211
- // enough and can soft skip (a soft skip doesn't trigger the request
1212
- // to re-run; we don't want to cancel the in progress effect if we're
1213
- // soft skipping.
1214
-
1215
- if (hardSkip || softSkip) {
1384
+ if (!shouldFetch) {
1216
1385
  return;
1217
- } // If we got here, we're going to perform the request.
1218
- // Let's make sure our ref is set to the most recent requestId.
1219
-
1220
-
1221
- requestIdRef.current = requestId; // OK, we've done all our checks and things. It's time to make the
1222
- // request. We use our request fulfillment here so that in-flight
1223
- // requests are shared.
1224
- // NOTE: Our request fulfillment handles the error cases here.
1225
- // Catching shouldn't serve a purpose.
1226
- // eslint-disable-next-line promise/catch-or-return
1227
-
1228
- _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"].Default.fulfill(requestId, {
1229
- handler: interceptedHandler
1230
- }).then(result => {
1231
- if (cancel) {
1232
- // We don't modify our result if an earlier effect was
1233
- // cancelled as it means that this hook no longer cares about
1234
- // that old request.
1235
- return;
1236
- }
1386
+ }
1237
1387
 
1238
- setCacheAndNotify(result);
1239
- return; // Shut up eslint always-return rule.
1240
- });
1388
+ fetchRequest();
1241
1389
  return () => {
1242
- // TODO(somewhatabstract, FEI-4276): Eventually, we will want to be
1243
- // able abort in-flight requests, but for now, we don't have that.
1244
- // (Of course, we will only want to abort them if no one is waiting
1245
- // on them)
1246
- // For now, we just block cancelled requests from changing our
1247
- // cache.
1248
- cancel = true;
1249
- }; // We only want to run this effect if the requestId, or skip values
1250
- // change. These are the only two things that should affect the
1251
- // cancellation of a pending request. We do not update if the handler
1252
- // changes, in order to simplify the API - otherwise, callers would
1253
- // not be able to use inline functions with this hook.
1254
- // eslint-disable-next-line react-hooks/exhaustive-deps
1255
- }, [hardSkip, requestId]); // We track the last result we returned in order to support the
1390
+ var _currentRequestRef$cu4;
1391
+
1392
+ (_currentRequestRef$cu4 = currentRequestRef.current) == null ? void 0 : _currentRequestRef$cu4.cancel();
1393
+ currentRequestRef.current = null;
1394
+ };
1395
+ }, [shouldFetch, fetchRequest]); // We track the last result we returned in order to support the
1256
1396
  // "retainResultOnChange" option.
1257
1397
 
1258
- const lastResultAgnosticOfIdRef = react__WEBPACK_IMPORTED_MODULE_0__["useRef"](_util_status_js__WEBPACK_IMPORTED_MODULE_3__[/* Status */ "a"].loading());
1259
- const loadingResult = retainResultOnChange ? lastResultAgnosticOfIdRef.current : _util_status_js__WEBPACK_IMPORTED_MODULE_3__[/* Status */ "a"].loading(); // Loading is a transient state, so we only use it here; it's not something
1398
+ const lastResultAgnosticOfIdRef = react__WEBPACK_IMPORTED_MODULE_0__["useRef"](_util_status_js__WEBPACK_IMPORTED_MODULE_4__[/* Status */ "a"].loading());
1399
+ const loadingResult = retainResultOnChange ? lastResultAgnosticOfIdRef.current : _util_status_js__WEBPACK_IMPORTED_MODULE_4__[/* Status */ "a"].loading(); // Loading is a transient state, so we only use it here; it's not something
1260
1400
  // we cache.
1261
1401
 
1262
- const result = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => mostRecentResult != null ? mostRecentResult : loadingResult, [mostRecentResult, loadingResult]);
1263
- lastResultAgnosticOfIdRef.current = result;
1264
- return result;
1402
+ const result = (_ref = fetchPolicy === _util_types_js__WEBPACK_IMPORTED_MODULE_7__[/* FetchPolicy */ "a"].NetworkOnly ? networkResultRef.current : mostRecentResult) != null ? _ref : loadingResult;
1403
+ lastResultAgnosticOfIdRef.current = result; // We return the result and a function for triggering a refetch.
1404
+
1405
+ return [result, fetchRequest];
1265
1406
  };
1266
1407
 
1267
1408
  /***/ }),
1268
- /* 16 */
1409
+ /* 18 */
1410
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1411
+
1412
+ "use strict";
1413
+ /* unused harmony export DocumentTypes */
1414
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return graphQLDocumentNodeParser; });
1415
+ /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1416
+
1417
+ const DocumentTypes = Object.freeze({
1418
+ query: "query",
1419
+ mutation: "mutation"
1420
+ });
1421
+ const cache = new Map();
1422
+ /**
1423
+ * Parse a GraphQL document node to determine some info about it.
1424
+ *
1425
+ * This is based on:
1426
+ * https://github.com/apollographql/react-apollo/blob/3bc993b2ea91704bd6a2667f42d1940656c071ff/src/parser.ts
1427
+ */
1428
+
1429
+ function graphQLDocumentNodeParser(document) {
1430
+ var _definition$name;
1431
+
1432
+ const cached = cache.get(document);
1433
+
1434
+ if (cached) {
1435
+ return cached;
1436
+ }
1437
+ /**
1438
+ * Saftey check for proper usage.
1439
+ */
1440
+
1441
+
1442
+ if (!(document != null && document.kind)) {
1443
+ if (true) {
1444
+ throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("Bad DocumentNode", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].InvalidInput);
1445
+ } else {}
1446
+ }
1447
+
1448
+ const fragments = document.definitions.filter(x => x.kind === "FragmentDefinition");
1449
+ const queries = document.definitions.filter(x => // $FlowIgnore[prop-missing]
1450
+ x.kind === "OperationDefinition" && x.operation === "query");
1451
+ const mutations = document.definitions.filter(x => // $FlowIgnore[prop-missing]
1452
+ x.kind === "OperationDefinition" && x.operation === "mutation");
1453
+ const subscriptions = document.definitions.filter(x => // $FlowIgnore[prop-missing]
1454
+ x.kind === "OperationDefinition" && x.operation === "subscription");
1455
+
1456
+ if (fragments.length && !queries.length && !mutations.length) {
1457
+ if (true) {
1458
+ throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("Fragment only", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].InvalidInput);
1459
+ } else {}
1460
+ }
1461
+
1462
+ if (subscriptions.length) {
1463
+ if (true) {
1464
+ throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("No subscriptions", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].InvalidInput);
1465
+ } else {}
1466
+ }
1467
+
1468
+ if (queries.length + mutations.length > 1) {
1469
+ if (true) {
1470
+ throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("Too many ops", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].InvalidInput);
1471
+ } else {}
1472
+ }
1473
+
1474
+ const type = queries.length ? DocumentTypes.query : DocumentTypes.mutation;
1475
+ const definitions = queries.length ? queries : mutations;
1476
+ const definition = definitions[0];
1477
+ const variables = definition.variableDefinitions || []; // fallback to using data if no name
1478
+
1479
+ const name = ((_definition$name = definition.name) == null ? void 0 : _definition$name.kind) === "Name" ? definition.name.value : "data";
1480
+ const payload = {
1481
+ name,
1482
+ type,
1483
+ variables
1484
+ };
1485
+ cache.set(document, payload);
1486
+ return payload;
1487
+ }
1488
+
1489
+ /***/ }),
1490
+ /* 19 */
1269
1491
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1270
1492
 
1271
1493
  "use strict";
1272
1494
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useRequestInterception; });
1273
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1495
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1274
1496
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1275
- /* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13);
1497
+ /* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14);
1276
1498
 
1277
1499
 
1278
1500
 
@@ -1310,67 +1532,289 @@ const useRequestInterception = (requestId, handler) => {
1310
1532
  // as our handler, so how can flow know? Let's just suppress that.
1311
1533
  // $FlowFixMe[incompatible-return]
1312
1534
 
1313
- return interceptResponse != null ? interceptResponse : handler();
1314
- }, [handler, interceptors, requestId]);
1315
- return interceptedHandler;
1535
+ return interceptResponse != null ? interceptResponse : handler();
1536
+ }, [handler, interceptors, requestId]);
1537
+ return interceptedHandler;
1538
+ };
1539
+
1540
+ /***/ }),
1541
+ /* 20 */
1542
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1543
+
1544
+ "use strict";
1545
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouterContext; });
1546
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1547
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1548
+
1549
+ const GqlRouterContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
1550
+
1551
+ /***/ }),
1552
+ /* 21 */
1553
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1554
+
1555
+ "use strict";
1556
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return mergeGqlContext; });
1557
+ /**
1558
+ * Construct a complete GqlContext from current defaults and a partial context.
1559
+ *
1560
+ * Values in the partial context that are `undefined` will be ignored.
1561
+ * Values in the partial context that are `null` will be deleted.
1562
+ */
1563
+ const mergeGqlContext = (defaultContext, overrides) => {
1564
+ // Let's merge the partial context default context. We deliberately
1565
+ // don't spread because spreading would overwrite default context
1566
+ // values with undefined or null if the partial context includes a value
1567
+ // explicitly set to undefined or null.
1568
+ return Object.keys(overrides).reduce((acc, key) => {
1569
+ // Undefined values are ignored.
1570
+ if (overrides[key] !== undefined) {
1571
+ if (overrides[key] === null) {
1572
+ // Null indicates we delete this context value.
1573
+ delete acc[key];
1574
+ } else {
1575
+ // Otherwise, we set it.
1576
+ acc[key] = overrides[key];
1577
+ }
1578
+ }
1579
+
1580
+ return acc;
1581
+ }, { ...defaultContext
1582
+ });
1583
+ };
1584
+
1585
+ /***/ }),
1586
+ /* 22 */
1587
+ /***/ (function(module, exports, __webpack_require__) {
1588
+
1589
+ "use strict";
1590
+ /**
1591
+ * Copyright (c) Facebook, Inc. and its affiliates.
1592
+ *
1593
+ * This source code is licensed under the MIT license found in the
1594
+ * LICENSE file in the root directory of this source tree.
1595
+ */
1596
+
1597
+
1598
+
1599
+ // Below we want to use `hasOwnProperty` on an object that doesn't have
1600
+ // `Object.prototype` in its proto chain, so we must extract it here.
1601
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
1602
+
1603
+ // Map from an enum object to a reverse map of its values to names
1604
+ var reverseMapCache = typeof WeakMap === 'function' ? new WeakMap() : new Map();
1605
+
1606
+ // Computes the reverse mapping of the enum object: from value to name.
1607
+ // Flow Enum values are unique (enforced by the parser), so this is a
1608
+ // one to one mapping.
1609
+ function getReverseMap(enumObject) {
1610
+ var reverseMap = reverseMapCache.get(enumObject);
1611
+ if (reverseMap !== undefined) {
1612
+ return reverseMap;
1613
+ }
1614
+ // We aren't using `Object.values` because that gets enumerable
1615
+ // properties, and our properties aren't enumerable.
1616
+ var newReverseMap = new Map();
1617
+ Object.getOwnPropertyNames(enumObject).forEach(function (name) {
1618
+ newReverseMap.set(enumObject[name], name);
1619
+ });
1620
+ reverseMapCache.set(enumObject, newReverseMap);
1621
+ return newReverseMap;
1622
+ }
1623
+
1624
+ var EnumPrototype = Object.freeze(
1625
+ Object.defineProperties(Object.create(null), {
1626
+ isValid: {
1627
+ value: function (x) {
1628
+ return getReverseMap(this).has(x);
1629
+ },
1630
+ },
1631
+ cast: {
1632
+ value: function (x) {
1633
+ return this.isValid(x) ? x : undefined;
1634
+ },
1635
+ },
1636
+ members: {
1637
+ value: function () {
1638
+ return getReverseMap(this).keys();
1639
+ },
1640
+ },
1641
+ getName: {
1642
+ value: function (value) {
1643
+ return getReverseMap(this).get(value);
1644
+ }
1645
+ }
1646
+ })
1647
+ );
1648
+
1649
+ // `members` is an object mapping name to value.
1650
+ function Enum(members) {
1651
+ var o = Object.create(EnumPrototype);
1652
+ for (var k in members) {
1653
+ if (hasOwnProperty.call(members, k)) {
1654
+ // Create non-enumerable properties.
1655
+ Object.defineProperty(o, k, {value: members[k]});
1656
+ }
1657
+ }
1658
+ return Object.freeze(o);
1659
+ }
1660
+
1661
+ // Mirrored enum (string enum with no member initializers).
1662
+ // Optimized implementation, taking advantage of the fact that
1663
+ // keys and values are identical.
1664
+ var EnumMirroredPrototype = Object.freeze(
1665
+ Object.defineProperties(Object.create(null), {
1666
+ isValid: {
1667
+ value: function (x) {
1668
+ if (typeof x === 'string') {
1669
+ return hasOwnProperty.call(this, x);
1670
+ }
1671
+ return false;
1672
+ },
1673
+ },
1674
+ cast: {
1675
+ value: EnumPrototype.cast,
1676
+ },
1677
+ members: {
1678
+ value: function () {
1679
+ // We aren't using `Object.values` because that gets enumerable
1680
+ // properties, and our properties aren't enumerable.
1681
+ return Object.getOwnPropertyNames(this).values();
1682
+ },
1683
+ },
1684
+ getName: {
1685
+ value: function (value) {
1686
+ return value;
1687
+ }
1688
+ }
1689
+ })
1690
+ );
1691
+
1692
+ // `members` is an array of names (which, are also the values).
1693
+ Enum.Mirrored = function EnumMirrored(members) {
1694
+ var o = Object.create(EnumMirroredPrototype);
1695
+ for (var i = 0, len = members.length; i < len; ++i) {
1696
+ // Value is same as key. Also, non-enumerable.
1697
+ Object.defineProperty(o, members[i], {value: members[i]});
1698
+ }
1699
+ return Object.freeze(o);
1700
+ };
1701
+
1702
+ Object.freeze(Enum.Mirrored);
1703
+
1704
+ module.exports = Object.freeze(Enum);
1705
+
1706
+
1707
+ /***/ }),
1708
+ /* 23 */
1709
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1710
+
1711
+ "use strict";
1712
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return fetchTrackedRequests; });
1713
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return hasTrackedRequestsToBeFetched; });
1714
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return abortInflightRequests; });
1715
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
1716
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
1717
+ /* harmony import */ var _request_tracking_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
1718
+ /* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
1719
+ /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(0);
1720
+
1721
+
1722
+
1723
+
1724
+
1725
+ const SSRCheck = () => {
1726
+ if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
1727
+ return null;
1728
+ }
1729
+
1730
+ if (true) {
1731
+ return new _data_error_js__WEBPACK_IMPORTED_MODULE_3__[/* DataError */ "a"]("No CSR tracking", _data_error_js__WEBPACK_IMPORTED_MODULE_3__[/* DataErrors */ "b"].NotAllowed);
1732
+ } else {}
1733
+ };
1734
+ /**
1735
+ * Fetches all tracked data requests.
1736
+ *
1737
+ * This is for use with the `TrackData` component during server-side rendering.
1738
+ *
1739
+ * @throws {Error} If executed outside of server-side rendering.
1740
+ * @returns {Promise<void>} A promise that resolves when all tracked requests
1741
+ * have been fetched.
1742
+ */
1743
+
1744
+
1745
+ const fetchTrackedRequests = () => {
1746
+ const ssrCheck = SSRCheck();
1747
+
1748
+ if (ssrCheck != null) {
1749
+ return Promise.reject(ssrCheck);
1750
+ }
1751
+
1752
+ return _request_tracking_js__WEBPACK_IMPORTED_MODULE_1__[/* RequestTracker */ "a"].Default.fulfillTrackedRequests();
1753
+ };
1754
+ /**
1755
+ * Indicate if there are tracked requests waiting to be fetched.
1756
+ *
1757
+ * This is used in conjunction with `TrackData`.
1758
+ *
1759
+ * @throws {Error} If executed outside of server-side rendering.
1760
+ * @returns {boolean} `true` if there are unfetched tracked requests;
1761
+ * otherwise, `false`.
1762
+ */
1763
+
1764
+ const hasTrackedRequestsToBeFetched = () => {
1765
+ const ssrCheck = SSRCheck();
1766
+
1767
+ if (ssrCheck != null) {
1768
+ throw ssrCheck;
1769
+ }
1770
+
1771
+ return _request_tracking_js__WEBPACK_IMPORTED_MODULE_1__[/* RequestTracker */ "a"].Default.hasUnfulfilledRequests;
1772
+ };
1773
+ /**
1774
+ * Abort all in-flight requests.
1775
+ *
1776
+ * This aborts all requests currently inflight via our default request
1777
+ * fulfillment.
1778
+ */
1779
+
1780
+ const abortInflightRequests = () => {
1781
+ _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"].Default.abortAll();
1316
1782
  };
1317
1783
 
1318
1784
  /***/ }),
1319
- /* 17 */
1785
+ /* 24 */
1320
1786
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1321
1787
 
1322
1788
  "use strict";
1323
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouterContext; });
1324
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1325
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1326
-
1327
- const GqlRouterContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
1789
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return purgeCaches; });
1790
+ /* harmony import */ var _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
1791
+ /* harmony import */ var _hydration_cache_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15);
1328
1792
 
1329
- /***/ }),
1330
- /* 18 */
1331
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1332
1793
 
1333
- "use strict";
1334
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return mergeGqlContext; });
1335
1794
  /**
1336
- * Construct a complete GqlContext from current defaults and a partial context.
1795
+ * Purge all caches managed by Wonder Blocks Data.
1337
1796
  *
1338
- * Values in the partial context that are `undefined` will be ignored.
1339
- * Values in the partial context that are `null` will be deleted.
1797
+ * This is a convenience method that purges the shared cache and the hydration
1798
+ * cache. It is useful for testing purposes to avoid having to reason about
1799
+ * which caches may have been used during a given test run.
1340
1800
  */
1341
- const mergeGqlContext = (defaultContext, overrides) => {
1342
- // Let's merge the partial context default context. We deliberately
1343
- // don't spread because spreading would overwrite default context
1344
- // values with undefined or null if the partial context includes a value
1345
- // explicitly set to undefined or null.
1346
- return Object.keys(overrides).reduce((acc, key) => {
1347
- // Undefined values are ignored.
1348
- if (overrides[key] !== undefined) {
1349
- if (overrides[key] === null) {
1350
- // Null indicates we delete this context value.
1351
- delete acc[key];
1352
- } else {
1353
- // Otherwise, we set it.
1354
- acc[key] = overrides[key];
1355
- }
1356
- }
1357
1801
 
1358
- return acc;
1359
- }, { ...defaultContext
1360
- });
1802
+ const purgeCaches = () => {
1803
+ Object(_hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_0__[/* purgeSharedCache */ "a"])();
1804
+ Object(_hydration_cache_api_js__WEBPACK_IMPORTED_MODULE_1__[/* purgeHydrationCache */ "b"])();
1361
1805
  };
1362
1806
 
1363
1807
  /***/ }),
1364
- /* 19 */
1808
+ /* 25 */
1365
1809
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1366
1810
 
1367
1811
  "use strict";
1368
1812
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
1369
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1813
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1370
1814
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1371
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
1815
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
1372
1816
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
1373
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
1817
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
1374
1818
 
1375
1819
 
1376
1820
 
@@ -1392,13 +1836,13 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
1392
1836
  }
1393
1837
 
1394
1838
  /***/ }),
1395
- /* 20 */
1839
+ /* 26 */
1396
1840
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1397
1841
 
1398
1842
  "use strict";
1399
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1843
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1400
1844
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1401
- /* harmony import */ var _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11);
1845
+ /* harmony import */ var _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
1402
1846
 
1403
1847
 
1404
1848
 
@@ -1424,13 +1868,13 @@ const Data = ({
1424
1868
  /* harmony default export */ __webpack_exports__["a"] = (Data);
1425
1869
 
1426
1870
  /***/ }),
1427
- /* 21 */
1871
+ /* 27 */
1428
1872
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1429
1873
 
1430
1874
  "use strict";
1431
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1875
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1432
1876
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1433
- /* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13);
1877
+ /* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14);
1434
1878
 
1435
1879
 
1436
1880
 
@@ -1465,14 +1909,110 @@ const InterceptRequests = ({
1465
1909
  /* harmony default export */ __webpack_exports__["a"] = (InterceptRequests);
1466
1910
 
1467
1911
  /***/ }),
1468
- /* 22 */
1912
+ /* 28 */
1913
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1914
+
1915
+ "use strict";
1916
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlRequestId; });
1917
+ const toString = valid => {
1918
+ var _JSON$stringify;
1919
+
1920
+ if (typeof valid === "string") {
1921
+ return valid;
1922
+ }
1923
+
1924
+ return (_JSON$stringify = JSON.stringify(valid)) != null ? _JSON$stringify : "";
1925
+ };
1926
+ /**
1927
+ * Get an identifier for a given request.
1928
+ */
1929
+
1930
+
1931
+ const getGqlRequestId = (operation, variables, context) => {
1932
+ // We add all the bits for this into an array and then join them with
1933
+ // a chosen separator.
1934
+ const parts = []; // First, we push the context values.
1935
+
1936
+ const sortableContext = new URLSearchParams(context); // $FlowIgnore[prop-missing] Flow has incomplete support for URLSearchParams
1937
+
1938
+ sortableContext.sort();
1939
+ parts.push(sortableContext.toString()); // Now we add the operation identifier.
1940
+
1941
+ parts.push(operation.id); // Finally, if we have variables, we add those too.
1942
+
1943
+ if (variables != null) {
1944
+ // We need to turn each variable into a string.
1945
+ const stringifiedVariables = Object.keys(variables).reduce((acc, key) => {
1946
+ acc[key] = toString(variables[key]);
1947
+ return acc;
1948
+ }, {}); // We use the same mechanism as context to sort and arrange the
1949
+ // variables.
1950
+
1951
+ const sortableVariables = new URLSearchParams(stringifiedVariables); // $FlowIgnore[prop-missing] Flow has incomplete support for URLSearchParams
1952
+
1953
+ sortableVariables.sort();
1954
+ parts.push(sortableVariables.toString());
1955
+ }
1956
+
1957
+ return parts.join("|");
1958
+ };
1959
+
1960
+ /***/ }),
1961
+ /* 29 */
1962
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1963
+
1964
+ "use strict";
1965
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return toGqlOperation; });
1966
+ /* harmony import */ var _graphql_document_node_parser_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(18);
1967
+
1968
+
1969
+ /**
1970
+ * Convert a GraphQL DocumentNode to a base Wonder Blocks Data GqlOperation.
1971
+ *
1972
+ * If you want to include the query/mutation body, extend the result of this
1973
+ * method and use the `graphql/language/printer` like:
1974
+ *
1975
+ * ```js
1976
+ * import {print} from "graphql/language/printer";
1977
+ *
1978
+ * const gqlOpWithBody = {
1979
+ * ...toGqlOperation(documentNode),
1980
+ * query: print(documentNode),
1981
+ * };
1982
+ * ```
1983
+ *
1984
+ * If you want to enforce inclusion of __typename properties, then you can use
1985
+ * `apollo-utilities` first to modify the document:
1986
+ *
1987
+ * ```js
1988
+ * import {print} from "graphql/language/printer";
1989
+ * import {addTypenameToDocument} from "apollo-utilities";
1990
+ *
1991
+ * const documentWithTypenames = addTypenameToDocument(documentNode);
1992
+ * const gqlOpWithBody = {
1993
+ * ...toGqlOperation(documentWithTypenames),
1994
+ * query: print(documentWithTypenames),
1995
+ * };
1996
+ * ```
1997
+ */
1998
+ const toGqlOperation = documentNode => {
1999
+ const definition = Object(_graphql_document_node_parser_js__WEBPACK_IMPORTED_MODULE_0__[/* graphQLDocumentNodeParser */ "a"])(documentNode);
2000
+ const wbDataOperation = {
2001
+ id: definition.name,
2002
+ type: definition.type
2003
+ };
2004
+ return wbDataOperation;
2005
+ };
2006
+
2007
+ /***/ }),
2008
+ /* 30 */
1469
2009
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1470
2010
 
1471
2011
  "use strict";
1472
2012
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouter; });
1473
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
2013
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1474
2014
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1475
- /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(17);
2015
+ /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(20);
1476
2016
 
1477
2017
 
1478
2018
 
@@ -1509,16 +2049,16 @@ const GqlRouter = ({
1509
2049
  };
1510
2050
 
1511
2051
  /***/ }),
1512
- /* 23 */
2052
+ /* 31 */
1513
2053
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1514
2054
 
1515
2055
  "use strict";
1516
2056
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGql; });
1517
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
2057
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1518
2058
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1519
- /* harmony import */ var _util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18);
1520
- /* harmony import */ var _use_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(26);
1521
- /* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(27);
2059
+ /* harmony import */ var _util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(21);
2060
+ /* harmony import */ var _use_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(33);
2061
+ /* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(34);
1522
2062
 
1523
2063
 
1524
2064
 
@@ -1559,34 +2099,13 @@ const useGql = (context = {}) => {
1559
2099
  };
1560
2100
 
1561
2101
  /***/ }),
1562
- /* 24 */
1563
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1564
-
1565
- "use strict";
1566
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return AbortError; });
1567
- /**
1568
- * Simple implementation to represent aborting.
1569
- *
1570
- * Other frameworks may provide this too, so we won't be sharing this with
1571
- * the outside world. It's just a utility for test and internal use whenever
1572
- * we need to represent the concept of aborted things.
1573
- */
1574
- class AbortError extends Error {
1575
- constructor(message) {
1576
- super(message);
1577
- this.name = "AbortError";
1578
- }
1579
-
1580
- }
1581
-
1582
- /***/ }),
1583
- /* 25 */
2102
+ /* 32 */
1584
2103
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1585
2104
 
1586
2105
  "use strict";
1587
2106
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return resultFromCachedResponse; });
1588
- /* harmony import */ var _status_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
1589
- /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
2107
+ /* harmony import */ var _status_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
2108
+ /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
1590
2109
 
1591
2110
 
1592
2111
 
@@ -1620,16 +2139,16 @@ const resultFromCachedResponse = cacheEntry => {
1620
2139
  };
1621
2140
 
1622
2141
  /***/ }),
1623
- /* 26 */
2142
+ /* 33 */
1624
2143
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1625
2144
 
1626
2145
  "use strict";
1627
2146
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGqlRouterContext; });
1628
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
2147
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1629
2148
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1630
- /* harmony import */ var _util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18);
1631
- /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(17);
1632
- /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3);
2149
+ /* harmony import */ var _util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(21);
2150
+ /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(20);
2151
+ /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
1633
2152
 
1634
2153
 
1635
2154
 
@@ -1672,13 +2191,13 @@ const useGqlRouterContext = (contextOverrides = {}) => {
1672
2191
  };
1673
2192
 
1674
2193
  /***/ }),
1675
- /* 27 */
2194
+ /* 34 */
1676
2195
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1677
2196
 
1678
2197
  "use strict";
1679
2198
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlDataFromResponse; });
1680
- /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1681
- /* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
2199
+ /* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
2200
+ /* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
1682
2201
 
1683
2202
 
1684
2203
  /**
@@ -1742,272 +2261,115 @@ const getGqlDataFromResponse = async response => {
1742
2261
  };
1743
2262
 
1744
2263
  /***/ }),
1745
- /* 28 */
2264
+ /* 35 */
1746
2265
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1747
2266
 
1748
2267
  "use strict";
1749
2268
  __webpack_require__.r(__webpack_exports__);
1750
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initializeCache", function() { return initializeCache; });
1751
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fulfillAllDataRequests", function() { return fulfillAllDataRequests; });
1752
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
1753
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
1754
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
1755
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
1756
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
1757
- /* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
1758
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
1759
- /* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(19);
1760
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
1761
-
1762
- /* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(20);
1763
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
1764
-
1765
- /* harmony import */ var _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(21);
1766
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptRequests", function() { return _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
1767
-
1768
- /* harmony import */ var _util_data_error_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(1);
1769
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DataError", function() { return _util_data_error_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
1770
-
1771
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DataErrors", function() { return _util_data_error_js__WEBPACK_IMPORTED_MODULE_6__["b"]; });
1772
-
1773
- /* harmony import */ var _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(14);
1774
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useServerEffect", function() { return _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
1775
-
1776
- /* harmony import */ var _hooks_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(15);
1777
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useCachedEffect", function() { return _hooks_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
1778
-
1779
- /* harmony import */ var _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(8);
1780
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useSharedCache", function() { return _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_9__["b"]; });
1781
-
1782
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "clearSharedCache", function() { return _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
2269
+ /* harmony import */ var _util_types_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
2270
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "FetchPolicy", function() { return _util_types_js__WEBPACK_IMPORTED_MODULE_0__["a"]; });
1783
2271
 
1784
- /* harmony import */ var _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(11);
1785
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useHydratableEffect", function() { return _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_10__["b"]; });
2272
+ /* harmony import */ var _util_hydration_cache_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15);
2273
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "initializeHydrationCache", function() { return _util_hydration_cache_api_js__WEBPACK_IMPORTED_MODULE_1__["a"]; });
1786
2274
 
1787
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "WhenClientSide", function() { return _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
2275
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "purgeHydrationCache", function() { return _util_hydration_cache_api_js__WEBPACK_IMPORTED_MODULE_1__["b"]; });
1788
2276
 
1789
- /* harmony import */ var _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(9);
1790
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ScopedInMemoryCache", function() { return _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_11__["a"]; });
2277
+ /* harmony import */ var _util_request_api_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(23);
2278
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "fetchTrackedRequests", function() { return _util_request_api_js__WEBPACK_IMPORTED_MODULE_2__["b"]; });
1791
2279
 
1792
- /* harmony import */ var _util_serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(12);
1793
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SerializableInMemoryCache", function() { return _util_serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_12__["a"]; });
2280
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "hasTrackedRequestsToBeFetched", function() { return _util_request_api_js__WEBPACK_IMPORTED_MODULE_2__["c"]; });
1794
2281
 
1795
- /* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(10);
1796
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RequestFulfillment", function() { return _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_13__["a"]; });
2282
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "abortInflightRequests", function() { return _util_request_api_js__WEBPACK_IMPORTED_MODULE_2__["a"]; });
1797
2283
 
1798
- /* harmony import */ var _util_status_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(4);
1799
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Status", function() { return _util_status_js__WEBPACK_IMPORTED_MODULE_14__["a"]; });
1800
-
1801
- /* harmony import */ var _components_gql_router_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(22);
1802
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_15__["a"]; });
2284
+ /* harmony import */ var _util_purge_caches_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24);
2285
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "purgeCaches", function() { return _util_purge_caches_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
1803
2286
 
1804
- /* harmony import */ var _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(23);
1805
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useGql", function() { return _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_16__["a"]; });
2287
+ /* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(25);
2288
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
1806
2289
 
1807
- /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(3);
1808
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlError", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_17__["a"]; });
2290
+ /* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26);
2291
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
1809
2292
 
1810
- /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_17__["b"]; });
2293
+ /* harmony import */ var _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(27);
2294
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptRequests", function() { return _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
1811
2295
 
2296
+ /* harmony import */ var _util_data_error_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(0);
2297
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DataError", function() { return _util_data_error_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
1812
2298
 
2299
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DataErrors", function() { return _util_data_error_js__WEBPACK_IMPORTED_MODULE_7__["b"]; });
1813
2300
 
2301
+ /* harmony import */ var _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(16);
2302
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useServerEffect", function() { return _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
1814
2303
 
2304
+ /* harmony import */ var _hooks_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(17);
2305
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useCachedEffect", function() { return _hooks_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
1815
2306
 
1816
- /**
1817
- * Initialize the hydration cache.
1818
- *
1819
- * @param {ResponseCache} source The cache content to use for initializing the
1820
- * cache.
1821
- * @throws {Error} If the cache is already initialized.
1822
- */
1823
- const initializeCache = source => _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default.initialize(source);
1824
- /**
1825
- * Fulfill all tracked data requests.
1826
- *
1827
- * This is for use with the `TrackData` component during server-side rendering.
1828
- *
1829
- * @throws {Error} If executed outside of server-side rendering.
1830
- * @returns {Promise<void>} A promise that resolves when all tracked requests
1831
- * have been fulfilled.
1832
- */
2307
+ /* harmony import */ var _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(5);
2308
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useSharedCache", function() { return _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_10__["b"]; });
1833
2309
 
1834
- const fulfillAllDataRequests = () => {
1835
- if (!_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
1836
- return Promise.reject(new Error("Data requests are not tracked when client-side"));
1837
- }
2310
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "purgeSharedCache", function() { return _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
1838
2311
 
1839
- return _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestTracker */ "a"].Default.fulfillTrackedRequests();
1840
- };
1841
- /**
1842
- * Indicate if there are unfulfilled tracked requests.
1843
- *
1844
- * This is used in conjunction with `TrackData`.
1845
- *
1846
- * @throws {Error} If executed outside of server-side rendering.
1847
- * @returns {boolean} `true` if there are unfulfilled tracked requests;
1848
- * otherwise, `false`.
1849
- */
2312
+ /* harmony import */ var _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(12);
2313
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useHydratableEffect", function() { return _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_11__["b"]; });
1850
2314
 
1851
- const hasUnfulfilledRequests = () => {
1852
- if (!_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
1853
- throw new Error("Data requests are not tracked when client-side");
1854
- }
2315
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "WhenClientSide", function() { return _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_11__["a"]; });
1855
2316
 
1856
- return _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestTracker */ "a"].Default.hasUnfulfilledRequests;
1857
- };
1858
- /**
1859
- * Remove the request identified from the cached hydration responses.
1860
- *
1861
- * @param {string} id The request ID of the response to remove from the cache.
1862
- */
2317
+ /* harmony import */ var _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(11);
2318
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ScopedInMemoryCache", function() { return _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_12__["a"]; });
1863
2319
 
1864
- const removeFromCache = id => _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default.remove(id);
1865
- /**
1866
- * Remove all cached hydration responses that match the given predicate.
1867
- *
1868
- * @param {(id: string) => boolean} [predicate] The predicate to match against
1869
- * the cached hydration responses. If no predicate is provided, all cached
1870
- * hydration responses will be removed.
1871
- */
2320
+ /* harmony import */ var _util_serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(13);
2321
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SerializableInMemoryCache", function() { return _util_serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_13__["a"]; });
1872
2322
 
1873
- const removeAllFromCache = predicate => _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default.removeAll(predicate);
2323
+ /* harmony import */ var _util_status_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(6);
2324
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Status", function() { return _util_status_js__WEBPACK_IMPORTED_MODULE_14__["a"]; });
1874
2325
 
2326
+ /* harmony import */ var _util_get_gql_request_id_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(28);
2327
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getGqlRequestId", function() { return _util_get_gql_request_id_js__WEBPACK_IMPORTED_MODULE_15__["a"]; });
1875
2328
 
2329
+ /* harmony import */ var _util_graphql_document_node_parser_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(18);
2330
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "graphQLDocumentNodeParser", function() { return _util_graphql_document_node_parser_js__WEBPACK_IMPORTED_MODULE_16__["a"]; });
1876
2331
 
2332
+ /* harmony import */ var _util_to_gql_operation_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(29);
2333
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toGqlOperation", function() { return _util_to_gql_operation_js__WEBPACK_IMPORTED_MODULE_17__["a"]; });
1877
2334
 
2335
+ /* harmony import */ var _components_gql_router_js__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(30);
2336
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_18__["a"]; });
1878
2337
 
2338
+ /* harmony import */ var _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(31);
2339
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useGql", function() { return _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_19__["a"]; });
1879
2340
 
2341
+ /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(4);
2342
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlError", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_20__["a"]; });
1880
2343
 
2344
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_20__["b"]; });
1881
2345
 
2346
+ // TODO(somewhatabstract, FEI-4174): Update eslint-plugin-import when they
2347
+ // have fixed:
2348
+ // https://github.com/import-js/eslint-plugin-import/issues/2073
2349
+ // eslint-disable-next-line import/named
1882
2350
 
1883
2351
 
1884
2352
 
1885
- // GraphQL
1886
2353
 
1887
2354
 
1888
2355
 
1889
2356
 
1890
2357
 
1891
- /***/ }),
1892
- /* 29 */
1893
- /***/ (function(module, exports, __webpack_require__) {
1894
2358
 
1895
- "use strict";
1896
- /**
1897
- * Copyright (c) Facebook, Inc. and its affiliates.
1898
- *
1899
- * This source code is licensed under the MIT license found in the
1900
- * LICENSE file in the root directory of this source tree.
1901
- */
1902
2359
 
1903
2360
 
1904
2361
 
1905
- // Below we want to use `hasOwnProperty` on an object that doesn't have
1906
- // `Object.prototype` in its proto chain, so we must extract it here.
1907
- var hasOwnProperty = Object.prototype.hasOwnProperty;
1908
2362
 
1909
- // Map from an enum object to a reverse map of its values to names
1910
- var reverseMapCache = typeof WeakMap === 'function' ? new WeakMap() : new Map();
1911
2363
 
1912
- // Computes the reverse mapping of the enum object: from value to name.
1913
- // Flow Enum values are unique (enforced by the parser), so this is a
1914
- // one to one mapping.
1915
- function getReverseMap(enumObject) {
1916
- var reverseMap = reverseMapCache.get(enumObject);
1917
- if (reverseMap !== undefined) {
1918
- return reverseMap;
1919
- }
1920
- // We aren't using `Object.values` because that gets enumerable
1921
- // properties, and our properties aren't enumerable.
1922
- var newReverseMap = new Map();
1923
- Object.getOwnPropertyNames(enumObject).forEach(function (name) {
1924
- newReverseMap.set(enumObject[name], name);
1925
- });
1926
- reverseMapCache.set(enumObject, newReverseMap);
1927
- return newReverseMap;
1928
- }
2364
+ ////////////////////////////////////////////////////////////////////////////////
2365
+ // GraphQL
2366
+ ////////////////////////////////////////////////////////////////////////////////
1929
2367
 
1930
- var EnumPrototype = Object.freeze(
1931
- Object.defineProperties(Object.create(null), {
1932
- isValid: {
1933
- value: function (x) {
1934
- return getReverseMap(this).has(x);
1935
- },
1936
- },
1937
- cast: {
1938
- value: function (x) {
1939
- return this.isValid(x) ? x : undefined;
1940
- },
1941
- },
1942
- members: {
1943
- value: function () {
1944
- return getReverseMap(this).keys();
1945
- },
1946
- },
1947
- getName: {
1948
- value: function (value) {
1949
- return getReverseMap(this).get(value);
1950
- }
1951
- }
1952
- })
1953
- );
1954
2368
 
1955
- // `members` is an object mapping name to value.
1956
- function Enum(members) {
1957
- var o = Object.create(EnumPrototype);
1958
- for (var k in members) {
1959
- if (hasOwnProperty.call(members, k)) {
1960
- // Create non-enumerable properties.
1961
- Object.defineProperty(o, k, {value: members[k]});
1962
- }
1963
- }
1964
- return Object.freeze(o);
1965
- }
1966
2369
 
1967
- // Mirrored enum (string enum with no member initializers).
1968
- // Optimized implementation, taking advantage of the fact that
1969
- // keys and values are identical.
1970
- var EnumMirroredPrototype = Object.freeze(
1971
- Object.defineProperties(Object.create(null), {
1972
- isValid: {
1973
- value: function (x) {
1974
- if (typeof x === 'string') {
1975
- return hasOwnProperty.call(this, x);
1976
- }
1977
- return false;
1978
- },
1979
- },
1980
- cast: {
1981
- value: EnumPrototype.cast,
1982
- },
1983
- members: {
1984
- value: function () {
1985
- // We aren't using `Object.values` because that gets enumerable
1986
- // properties, and our properties aren't enumerable.
1987
- return Object.getOwnPropertyNames(this).values();
1988
- },
1989
- },
1990
- getName: {
1991
- value: function (value) {
1992
- return value;
1993
- }
1994
- }
1995
- })
1996
- );
1997
2370
 
1998
- // `members` is an array of names (which, are also the values).
1999
- Enum.Mirrored = function EnumMirrored(members) {
2000
- var o = Object.create(EnumMirroredPrototype);
2001
- for (var i = 0, len = members.length; i < len; ++i) {
2002
- // Value is same as key. Also, non-enumerable.
2003
- Object.defineProperty(o, members[i], {value: members[i]});
2004
- }
2005
- return Object.freeze(o);
2006
- };
2007
2371
 
2008
- Object.freeze(Enum.Mirrored);
2009
2372
 
2010
- module.exports = Object.freeze(Enum);
2011
2373
 
2012
2374
 
2013
2375
  /***/ })