@khanacademy/wonder-blocks-data 3.1.3 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/dist/es/index.js +408 -349
- package/dist/index.js +599 -494
- package/docs.md +17 -35
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +7 -46
- package/src/__tests__/generated-snapshot.test.js +60 -126
- package/src/components/__tests__/data.test.js +373 -313
- package/src/components/__tests__/intercept-requests.test.js +58 -0
- package/src/components/data.js +139 -21
- package/src/components/data.md +38 -69
- package/src/components/intercept-context.js +6 -3
- package/src/components/intercept-requests.js +69 -0
- package/src/components/intercept-requests.md +54 -0
- package/src/components/track-data.md +9 -23
- package/src/hooks/__tests__/__snapshots__/use-shared-cache.test.js.snap +17 -0
- package/src/hooks/__tests__/use-gql.test.js +1 -0
- package/src/hooks/__tests__/use-request-interception.test.js +255 -0
- package/src/hooks/__tests__/use-server-effect.test.js +217 -0
- package/src/hooks/__tests__/use-shared-cache.test.js +307 -0
- package/src/hooks/use-gql.js +36 -23
- package/src/hooks/use-request-interception.js +54 -0
- package/src/hooks/use-server-effect.js +45 -0
- package/src/hooks/use-shared-cache.js +106 -0
- package/src/index.js +18 -20
- package/src/util/__tests__/__snapshots__/scoped-in-memory-cache.test.js.snap +19 -0
- package/src/util/__tests__/request-fulfillment.test.js +42 -85
- package/src/util/__tests__/request-tracking.test.js +72 -191
- package/src/util/__tests__/{result-from-cache-entry.test.js → result-from-cache-response.test.js} +9 -10
- package/src/util/__tests__/scoped-in-memory-cache.test.js +396 -0
- package/src/util/__tests__/ssr-cache.test.js +639 -0
- package/src/util/request-fulfillment.js +36 -44
- package/src/util/request-tracking.js +62 -75
- package/src/util/{result-from-cache-entry.js → result-from-cache-response.js} +10 -13
- package/src/util/scoped-in-memory-cache.js +149 -0
- package/src/util/ssr-cache.js +206 -0
- package/src/util/types.js +43 -108
- package/src/components/__tests__/intercept-data.test.js +0 -87
- package/src/components/intercept-data.js +0 -77
- package/src/components/intercept-data.md +0 -65
- package/src/hooks/__tests__/use-data.test.js +0 -826
- package/src/hooks/use-data.js +0 -143
- package/src/util/__tests__/memory-cache.test.js +0 -446
- package/src/util/__tests__/request-handler.test.js +0 -121
- package/src/util/__tests__/response-cache.test.js +0 -879
- package/src/util/memory-cache.js +0 -187
- package/src/util/request-handler.js +0 -42
- package/src/util/request-handler.md +0 -51
- package/src/util/response-cache.js +0 -213
package/dist/index.js
CHANGED
|
@@ -82,7 +82,7 @@ module.exports =
|
|
|
82
82
|
/******/
|
|
83
83
|
/******/
|
|
84
84
|
/******/ // Load entry module and return exports
|
|
85
|
-
/******/ return __webpack_require__(__webpack_require__.s =
|
|
85
|
+
/******/ return __webpack_require__(__webpack_require__.s = 20);
|
|
86
86
|
/******/ })
|
|
87
87
|
/************************************************************************/
|
|
88
88
|
/******/ ([
|
|
@@ -93,12 +93,18 @@ module.exports = require("react");
|
|
|
93
93
|
|
|
94
94
|
/***/ }),
|
|
95
95
|
/* 1 */
|
|
96
|
+
/***/ (function(module, exports) {
|
|
97
|
+
|
|
98
|
+
module.exports = require("@khanacademy/wonder-stuff-core");
|
|
99
|
+
|
|
100
|
+
/***/ }),
|
|
101
|
+
/* 2 */
|
|
96
102
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
97
103
|
|
|
98
104
|
"use strict";
|
|
99
105
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return GqlErrors; });
|
|
100
106
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlError; });
|
|
101
|
-
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
107
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
102
108
|
/* 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
109
|
|
|
104
110
|
|
|
@@ -130,27 +136,28 @@ class GqlError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0
|
|
|
130
136
|
}
|
|
131
137
|
|
|
132
138
|
/***/ }),
|
|
133
|
-
/*
|
|
139
|
+
/* 3 */
|
|
134
140
|
/***/ (function(module, exports) {
|
|
135
141
|
|
|
136
142
|
module.exports = require("@khanacademy/wonder-blocks-core");
|
|
137
143
|
|
|
138
144
|
/***/ }),
|
|
139
|
-
/*
|
|
145
|
+
/* 4 */
|
|
140
146
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
141
147
|
|
|
142
148
|
"use strict";
|
|
143
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return
|
|
144
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
149
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SsrCache; });
|
|
150
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
|
|
145
151
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
146
|
-
/* harmony import */ var
|
|
147
|
-
|
|
152
|
+
/* harmony import */ var _scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
148
153
|
|
|
149
154
|
|
|
155
|
+
const DefaultScope = "default";
|
|
150
156
|
/**
|
|
151
157
|
* The default instance is stored here.
|
|
152
158
|
* It's created below in the Default() static property.
|
|
153
159
|
*/
|
|
160
|
+
|
|
154
161
|
let _default;
|
|
155
162
|
/**
|
|
156
163
|
* Implements the response cache.
|
|
@@ -159,10 +166,10 @@ let _default;
|
|
|
159
166
|
*/
|
|
160
167
|
|
|
161
168
|
|
|
162
|
-
class
|
|
169
|
+
class SsrCache {
|
|
163
170
|
static get Default() {
|
|
164
171
|
if (!_default) {
|
|
165
|
-
_default = new
|
|
172
|
+
_default = new SsrCache();
|
|
166
173
|
}
|
|
167
174
|
|
|
168
175
|
return _default;
|
|
@@ -174,31 +181,29 @@ class ResponseCache {
|
|
|
174
181
|
throw new Error("Cannot initialize data response cache more than once");
|
|
175
182
|
}
|
|
176
183
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
184
|
+
this._hydrationCache = new _scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ScopedInMemoryCache */ "a"]({
|
|
185
|
+
// $FlowIgnore[incompatible-call]
|
|
186
|
+
[DefaultScope]: source
|
|
187
|
+
});
|
|
182
188
|
};
|
|
183
189
|
|
|
184
|
-
this.cacheData = (
|
|
190
|
+
this.cacheData = (id, data, hydrate) => this._setCachedResponse(id, {
|
|
185
191
|
data
|
|
186
|
-
});
|
|
192
|
+
}, hydrate);
|
|
187
193
|
|
|
188
|
-
this.cacheError = (
|
|
194
|
+
this.cacheError = (id, error, hydrate) => {
|
|
189
195
|
const errorMessage = typeof error === "string" ? error : error.message;
|
|
190
|
-
return this.
|
|
196
|
+
return this._setCachedResponse(id, {
|
|
191
197
|
error: errorMessage
|
|
192
|
-
});
|
|
198
|
+
}, hydrate);
|
|
193
199
|
};
|
|
194
200
|
|
|
195
|
-
this.getEntry =
|
|
201
|
+
this.getEntry = id => {
|
|
202
|
+
var _this$_ssrOnlyCache$g, _this$_ssrOnlyCache;
|
|
203
|
+
|
|
196
204
|
// Get the cached entry for this value.
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
// we use our SSR-only cache. Otherwise, there's no entry to return.
|
|
200
|
-
const cache = handler.hydrate ? this._hydrationCache : _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? this._ssrOnlyCache : undefined;
|
|
201
|
-
const internalEntry = cache == null ? void 0 : cache.retrieve(handler, options); // If we are not server-side and we hydrated something, let's clear
|
|
205
|
+
// We first look in the ssr cache and then the hydration cache.
|
|
206
|
+
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
|
|
202
207
|
// that from the hydration cache to save memory.
|
|
203
208
|
|
|
204
209
|
if (this._ssrOnlyCache == null && internalEntry != null) {
|
|
@@ -208,48 +213,71 @@ class ResponseCache {
|
|
|
208
213
|
// that's not an expected use-case. If two different places use the
|
|
209
214
|
// same handler and options (i.e. the same request), then the
|
|
210
215
|
// handler should cater to that to ensure they share the result.
|
|
211
|
-
this._hydrationCache.
|
|
212
|
-
}
|
|
216
|
+
this._hydrationCache.purge(DefaultScope, id);
|
|
217
|
+
} // Getting the typing right between the in-memory cache and this
|
|
218
|
+
// is hard. Just telling flow it's OK.
|
|
219
|
+
// $FlowIgnore[incompatible-return]
|
|
220
|
+
|
|
213
221
|
|
|
214
222
|
return internalEntry;
|
|
215
223
|
};
|
|
216
224
|
|
|
217
|
-
this.remove =
|
|
218
|
-
var _this$_ssrOnlyCache$
|
|
225
|
+
this.remove = id => {
|
|
226
|
+
var _this$_ssrOnlyCache$p, _this$_ssrOnlyCache2;
|
|
219
227
|
|
|
220
228
|
// NOTE(somewhatabstract): We could invoke removeAll with a predicate
|
|
221
229
|
// to match the key of the entry we're removing, but that's an
|
|
222
230
|
// inefficient way to remove a single item, so let's not do that.
|
|
223
231
|
// Delete the entry from the appropriate cache.
|
|
224
|
-
return
|
|
232
|
+
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);
|
|
225
233
|
};
|
|
226
234
|
|
|
227
|
-
this.removeAll =
|
|
228
|
-
var _this$
|
|
235
|
+
this.removeAll = predicate => {
|
|
236
|
+
var _this$_ssrOnlyCache3;
|
|
229
237
|
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
const realPredicate = predicate ? // We know what we're putting into the cache so let's assume it
|
|
239
|
+
// conforms.
|
|
240
|
+
// $FlowIgnore[incompatible-call]
|
|
241
|
+
(_, key, cachedEntry) => predicate(key, cachedEntry) : undefined; // Apply the predicate to what we have in our caches.
|
|
242
|
+
|
|
243
|
+
this._hydrationCache.purgeAll(realPredicate);
|
|
244
|
+
|
|
245
|
+
(_this$_ssrOnlyCache3 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache3.purgeAll(realPredicate);
|
|
232
246
|
};
|
|
233
247
|
|
|
234
248
|
this.cloneHydratableData = () => {
|
|
249
|
+
var _cache$DefaultScope;
|
|
250
|
+
|
|
235
251
|
// We return our hydration cache only.
|
|
236
|
-
|
|
252
|
+
const cache = this._hydrationCache.clone(); // If we're empty, we still want to return an object, so we default
|
|
253
|
+
// to an empty object.
|
|
254
|
+
// We only need the default scope out of our scoped in-memory cache.
|
|
255
|
+
// We know that it conforms to our expectations.
|
|
256
|
+
// $FlowIgnore[incompatible-return]
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
return (_cache$DefaultScope = cache[DefaultScope]) != null ? _cache$DefaultScope : {};
|
|
237
260
|
};
|
|
238
261
|
|
|
239
|
-
this._ssrOnlyCache = _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? ssrOnlyCache || new
|
|
240
|
-
this._hydrationCache = hydrationCache || new
|
|
262
|
+
this._ssrOnlyCache = _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? ssrOnlyCache || new _scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ScopedInMemoryCache */ "a"]() : undefined;
|
|
263
|
+
this._hydrationCache = hydrationCache || new _scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ScopedInMemoryCache */ "a"]();
|
|
241
264
|
}
|
|
242
265
|
|
|
243
|
-
|
|
266
|
+
_setCachedResponse(id, entry, hydrate) {
|
|
244
267
|
const frozenEntry = Object.freeze(entry);
|
|
245
268
|
|
|
246
|
-
if (
|
|
269
|
+
if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
247
270
|
// We are server-side.
|
|
248
271
|
// We need to store this value.
|
|
249
|
-
if (
|
|
250
|
-
this._hydrationCache.
|
|
272
|
+
if (hydrate) {
|
|
273
|
+
this._hydrationCache.set(DefaultScope, id, frozenEntry);
|
|
251
274
|
} else {
|
|
252
|
-
|
|
275
|
+
var _this$_ssrOnlyCache4;
|
|
276
|
+
|
|
277
|
+
// Usually, when server-side, this cache will always be present.
|
|
278
|
+
// We do fake server-side in our doc example though, when it
|
|
279
|
+
// won't be.
|
|
280
|
+
(_this$_ssrOnlyCache4 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache4.set(DefaultScope, id, frozenEntry);
|
|
253
281
|
}
|
|
254
282
|
}
|
|
255
283
|
|
|
@@ -265,7 +293,124 @@ class ResponseCache {
|
|
|
265
293
|
}
|
|
266
294
|
|
|
267
295
|
/***/ }),
|
|
268
|
-
/*
|
|
296
|
+
/* 5 */
|
|
297
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
298
|
+
|
|
299
|
+
"use strict";
|
|
300
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ScopedInMemoryCache; });
|
|
301
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
302
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Describe an in-memory cache.
|
|
307
|
+
*/
|
|
308
|
+
class ScopedInMemoryCache {
|
|
309
|
+
constructor(initialCache = Object.freeze({})) {
|
|
310
|
+
this.set = (scope, id, value) => {
|
|
311
|
+
var _this$_cache$scope;
|
|
312
|
+
|
|
313
|
+
if (!id || typeof id !== "string") {
|
|
314
|
+
throw new _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"]("id must be non-empty string", _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["Errors"].InvalidInput);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!scope || typeof scope !== "string") {
|
|
318
|
+
throw new _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"]("scope must be non-empty string", _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["Errors"].InvalidInput);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (typeof value === "function") {
|
|
322
|
+
throw new _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"]("value must be a non-function value", _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["Errors"].InvalidInput);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this._cache[scope] = (_this$_cache$scope = this._cache[scope]) != null ? _this$_cache$scope : {};
|
|
326
|
+
this._cache[scope][id] = Object.freeze(Object(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["clone"])(value));
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
this.get = (scope, id) => {
|
|
330
|
+
var _this$_cache$scope$id, _this$_cache$scope2;
|
|
331
|
+
|
|
332
|
+
return (_this$_cache$scope$id = (_this$_cache$scope2 = this._cache[scope]) == null ? void 0 : _this$_cache$scope2[id]) != null ? _this$_cache$scope$id : null;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
this.purge = (scope, id) => {
|
|
336
|
+
var _this$_cache$scope3;
|
|
337
|
+
|
|
338
|
+
if (!((_this$_cache$scope3 = this._cache[scope]) != null && _this$_cache$scope3[id])) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
delete this._cache[scope][id];
|
|
343
|
+
|
|
344
|
+
if (Object.keys(this._cache[scope]).length === 0) {
|
|
345
|
+
delete this._cache[scope];
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
this.purgeScope = (scope, predicate) => {
|
|
350
|
+
if (!this._cache[scope]) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (predicate == null) {
|
|
355
|
+
delete this._cache[scope];
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for (const key of Object.keys(this._cache[scope])) {
|
|
360
|
+
if (predicate(key, this._cache[scope][key])) {
|
|
361
|
+
delete this._cache[scope][key];
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (Object.keys(this._cache[scope]).length === 0) {
|
|
366
|
+
delete this._cache[scope];
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
this.purgeAll = predicate => {
|
|
371
|
+
if (predicate == null) {
|
|
372
|
+
this._cache = {};
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
for (const scope of Object.keys(this._cache)) {
|
|
377
|
+
this.purgeScope(scope, (id, value) => predicate(scope, id, value));
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
this.clone = () => {
|
|
382
|
+
try {
|
|
383
|
+
return Object(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["clone"])(this._cache);
|
|
384
|
+
} catch (e) {
|
|
385
|
+
throw new Error(`An error occurred while trying to clone the cache: ${e}`);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
this._cache = Object(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["clone"])(initialCache);
|
|
391
|
+
} catch (e) {
|
|
392
|
+
throw new _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"](`An error occurred trying to initialize from a response cache snapshot: ${e}`, _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["Errors"].InvalidInput);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Indicate if this cache is being used or not.
|
|
397
|
+
*
|
|
398
|
+
* When the cache has entries, returns `true`; otherwise, returns `false`.
|
|
399
|
+
*/
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
get inUse() {
|
|
403
|
+
return Object.keys(this._cache).length > 0;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Set a value in the cache.
|
|
407
|
+
*/
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/***/ }),
|
|
413
|
+
/* 6 */
|
|
269
414
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
270
415
|
|
|
271
416
|
"use strict";
|
|
@@ -273,8 +418,8 @@ class ResponseCache {
|
|
|
273
418
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
|
|
274
419
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
275
420
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
276
|
-
/* harmony import */ var
|
|
277
|
-
/* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
421
|
+
/* harmony import */ var _ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
|
|
422
|
+
/* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
|
|
278
423
|
|
|
279
424
|
|
|
280
425
|
|
|
@@ -312,48 +457,31 @@ class RequestTracker {
|
|
|
312
457
|
|
|
313
458
|
|
|
314
459
|
constructor(responseCache = undefined) {
|
|
315
|
-
this._trackedHandlers = {};
|
|
316
460
|
this._trackedRequests = {};
|
|
317
461
|
|
|
318
|
-
this.trackDataRequest = (handler,
|
|
319
|
-
const key = handler.getKey(options);
|
|
320
|
-
const type = handler.type;
|
|
321
|
-
/**
|
|
322
|
-
* Make sure we have stored the handler for use when fulfilling requests.
|
|
323
|
-
*/
|
|
324
|
-
|
|
325
|
-
if (this._trackedHandlers[type] == null) {
|
|
326
|
-
this._trackedHandlers[type] = handler;
|
|
327
|
-
this._trackedRequests[type] = {};
|
|
328
|
-
}
|
|
462
|
+
this.trackDataRequest = (id, handler, hydrate) => {
|
|
329
463
|
/**
|
|
330
464
|
* If we don't already have this tracked, then let's track it.
|
|
331
465
|
*/
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
466
|
+
if (this._trackedRequests[id] == null) {
|
|
467
|
+
this._trackedRequests[id] = {
|
|
468
|
+
handler,
|
|
469
|
+
hydrate
|
|
470
|
+
};
|
|
336
471
|
}
|
|
337
472
|
};
|
|
338
473
|
|
|
339
474
|
this.reset = () => {
|
|
340
|
-
this._trackedHandlers = {};
|
|
341
475
|
this._trackedRequests = {};
|
|
342
476
|
};
|
|
343
477
|
|
|
344
478
|
this.fulfillTrackedRequests = () => {
|
|
345
479
|
const promises = [];
|
|
346
480
|
|
|
347
|
-
for (const
|
|
348
|
-
const
|
|
481
|
+
for (const requestKey of Object.keys(this._trackedRequests)) {
|
|
482
|
+
const promise = this._requestFulfillment.fulfill(requestKey, this._trackedRequests[requestKey]);
|
|
349
483
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
for (const requestKey of Object.keys(requests)) {
|
|
353
|
-
const promise = this._requestFulfillment.fulfill(handler, requests[requestKey]);
|
|
354
|
-
|
|
355
|
-
promises.push(promise);
|
|
356
|
-
}
|
|
484
|
+
promises.push(promise);
|
|
357
485
|
}
|
|
358
486
|
/**
|
|
359
487
|
* Clear out our tracked info.
|
|
@@ -382,7 +510,7 @@ class RequestTracker {
|
|
|
382
510
|
return Promise.all(promises).then(() => this._responseCache.cloneHydratableData());
|
|
383
511
|
};
|
|
384
512
|
|
|
385
|
-
this._responseCache = responseCache ||
|
|
513
|
+
this._responseCache = responseCache || _ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default;
|
|
386
514
|
this._requestFulfillment = new _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"](responseCache);
|
|
387
515
|
}
|
|
388
516
|
/**
|
|
@@ -409,15 +537,15 @@ class RequestTracker {
|
|
|
409
537
|
* Calling this method marks tracked requests as fulfilled; requests are
|
|
410
538
|
* removed from the list of tracked requests by calling this method.
|
|
411
539
|
*
|
|
412
|
-
* @returns {Promise<
|
|
413
|
-
* as a result of fulfilling the tracked requests.
|
|
540
|
+
* @returns {Promise<ResponseCache>} The promise of the data that was
|
|
541
|
+
* cached as a result of fulfilling the tracked requests.
|
|
414
542
|
*/
|
|
415
543
|
|
|
416
544
|
|
|
417
545
|
}
|
|
418
546
|
|
|
419
547
|
/***/ }),
|
|
420
|
-
/*
|
|
548
|
+
/* 7 */
|
|
421
549
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
422
550
|
|
|
423
551
|
"use strict";
|
|
@@ -426,158 +554,163 @@ class RequestTracker {
|
|
|
426
554
|
|
|
427
555
|
|
|
428
556
|
/**
|
|
429
|
-
* InterceptContext defines a map from
|
|
557
|
+
* InterceptContext defines a map from request ID to interception methods.
|
|
430
558
|
*
|
|
431
559
|
* INTERNAL USE ONLY
|
|
432
560
|
*/
|
|
433
|
-
const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](
|
|
561
|
+
const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"]([]);
|
|
434
562
|
/* harmony default export */ __webpack_exports__["a"] = (InterceptContext);
|
|
435
563
|
|
|
436
564
|
/***/ }),
|
|
437
|
-
/*
|
|
565
|
+
/* 8 */
|
|
438
566
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
439
567
|
|
|
440
568
|
"use strict";
|
|
441
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return
|
|
442
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
569
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useServerEffect; });
|
|
570
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
|
|
443
571
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
444
572
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
|
|
445
573
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
|
|
446
|
-
/* harmony import */ var
|
|
447
|
-
/* harmony import */ var
|
|
448
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4);
|
|
449
|
-
/* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(17);
|
|
450
|
-
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3);
|
|
574
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
575
|
+
/* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
|
|
451
576
|
|
|
452
577
|
|
|
453
578
|
|
|
454
579
|
|
|
455
580
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
581
|
+
/**
|
|
582
|
+
* Hook to perform an asynchronous action during server-side rendering.
|
|
583
|
+
*
|
|
584
|
+
* This hook registers an asynchronous action to be performed during
|
|
585
|
+
* server-side rendering. The action is performed only once, and the result
|
|
586
|
+
* is cached against the given identifier so that subsequent calls return that
|
|
587
|
+
* cached result allowing components to render more of the component.
|
|
588
|
+
*
|
|
589
|
+
* This hook requires the Wonder Blocks Data functionality for resolving
|
|
590
|
+
* pending requests, as well as support for the hydration cache to be
|
|
591
|
+
* embedded into a page so that the result can by hydrated (if that is a
|
|
592
|
+
* requirement).
|
|
593
|
+
*
|
|
594
|
+
* The asynchronous action is never invoked on the client-side.
|
|
595
|
+
*/
|
|
596
|
+
const useServerEffect = (requestId, handler, hydrate = true) => {
|
|
459
597
|
// If we're server-side or hydrating, we'll have a cached entry to use.
|
|
460
598
|
// So we get that and use it to initialize our state.
|
|
461
599
|
// This works in both hydration and SSR because the very first call to
|
|
462
600
|
// this will have cached data in those cases as it will be present on the
|
|
463
601
|
// initial render - and subsequent renders on the client it will be null.
|
|
464
|
-
const cachedResult =
|
|
465
|
-
const [result, setResult] = Object(react__WEBPACK_IMPORTED_MODULE_1__["useState"])(cachedResult); // Lookup to see if there's an interceptor for the handler.
|
|
466
|
-
// If we have one, we need to replace the handler with one that
|
|
467
|
-
// uses the interceptor.
|
|
468
|
-
|
|
469
|
-
const interceptorMap = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"]);
|
|
470
|
-
const interceptor = interceptorMap[handler.type]; // If we have an interceptor, we need to replace the handler with one that
|
|
471
|
-
// uses the interceptor. This helper function generates a new handler.
|
|
472
|
-
// We need this before we track the request as we want the interceptor
|
|
473
|
-
// to also work for tracked requests to simplify testing the server-side
|
|
474
|
-
// request fulfillment.
|
|
475
|
-
|
|
476
|
-
const getMaybeInterceptedHandler = () => {
|
|
477
|
-
if (interceptor == null) {
|
|
478
|
-
return handler;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
const fulfillRequestFn = options => {
|
|
482
|
-
var _interceptor$fulfillR;
|
|
483
|
-
|
|
484
|
-
return (_interceptor$fulfillR = interceptor.fulfillRequest(options)) != null ? _interceptor$fulfillR : handler.fulfillRequest(options);
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
return {
|
|
488
|
-
fulfillRequest: fulfillRequestFn,
|
|
489
|
-
getKey: options => handler.getKey(options),
|
|
490
|
-
type: handler.type,
|
|
491
|
-
hydrate: handler.hydrate
|
|
492
|
-
};
|
|
493
|
-
}; // We only track data requests when we are server-side and we don't
|
|
602
|
+
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
|
|
494
603
|
// already have a result, as given by the cachedData (which is also the
|
|
495
604
|
// initial value for the result state).
|
|
496
605
|
|
|
606
|
+
const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* TrackerContext */ "b"]);
|
|
497
607
|
|
|
498
|
-
|
|
608
|
+
if (cachedResult == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
609
|
+
maybeTrack == null ? void 0 : maybeTrack(requestId, handler, hydrate);
|
|
610
|
+
}
|
|
499
611
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
} // We need to update our request when the handler changes or the key
|
|
503
|
-
// to the options change, so we keep track of those.
|
|
504
|
-
// However, even if we are hydrating from cache, we still need to make the
|
|
505
|
-
// request at least once, so we do not initialize these references.
|
|
612
|
+
return cachedResult;
|
|
613
|
+
};
|
|
506
614
|
|
|
615
|
+
/***/ }),
|
|
616
|
+
/* 9 */
|
|
617
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
507
618
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
619
|
+
"use strict";
|
|
620
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useRequestInterception; });
|
|
621
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
622
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
623
|
+
/* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
511
624
|
|
|
512
|
-
Object(react__WEBPACK_IMPORTED_MODULE_1__["useEffect"])(() => {
|
|
513
|
-
// If we are server-side, then just skip the effect. We track requests
|
|
514
|
-
// during SSR and fulfill them outside of the React render cycle.
|
|
515
|
-
// NOTE: This shouldn't happen since effects would not run on the server
|
|
516
|
-
// but let's be defensive - I think it makes the code clearer.
|
|
517
625
|
|
|
518
|
-
/* istanbul ignore next */
|
|
519
|
-
if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
520
|
-
return;
|
|
521
|
-
} // Update our refs to the current handler and key.
|
|
522
626
|
|
|
627
|
+
/**
|
|
628
|
+
* Allow request handling to be intercepted.
|
|
629
|
+
*
|
|
630
|
+
* Hook to take a uniquely identified request handler and return a
|
|
631
|
+
* method that will support request interception from the InterceptRequest
|
|
632
|
+
* component.
|
|
633
|
+
*
|
|
634
|
+
* If you want request interception to be supported with `useServerEffect` or
|
|
635
|
+
* any client-side effect that uses the handler, call this first to generate
|
|
636
|
+
* an intercepted handler, and then invoke `useServerEffect` (or other things)
|
|
637
|
+
* with that intercepted handler.
|
|
638
|
+
*/
|
|
639
|
+
const useRequestInterception = (requestId, handler) => {
|
|
640
|
+
// Get the interceptors that have been registered.
|
|
641
|
+
const interceptors = react__WEBPACK_IMPORTED_MODULE_0__["useContext"](_components_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"]); // Now, we need to create a new handler that will check if the
|
|
642
|
+
// request is intercepted before ultimately calling the original handler
|
|
643
|
+
// if nothing intercepted it.
|
|
644
|
+
// We memoize this so that it only changes if something related to it
|
|
645
|
+
// changes.
|
|
646
|
+
|
|
647
|
+
const interceptedHandler = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => () => {
|
|
648
|
+
// Call the interceptors from closest to furthest.
|
|
649
|
+
// If one returns a non-null result, then we keep that.
|
|
650
|
+
const interceptResponse = interceptors.reduceRight((prev, interceptor) => {
|
|
651
|
+
if (prev != null) {
|
|
652
|
+
return prev;
|
|
653
|
+
}
|
|
523
654
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
//
|
|
655
|
+
return interceptor(requestId);
|
|
656
|
+
}, null); // If nothing intercepted this request, invoke the original handler.
|
|
657
|
+
// NOTE: We can't guarantee all interceptors return the same type
|
|
658
|
+
// as our handler, so how can flow know? Let's just suppress that.
|
|
659
|
+
// $FlowFixMe[incompatible-return]
|
|
528
660
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
// The request handler is in control of whether that request actually
|
|
534
|
-
// happens or not.
|
|
661
|
+
return interceptResponse != null ? interceptResponse : handler();
|
|
662
|
+
}, [handler, interceptors, requestId]);
|
|
663
|
+
return interceptedHandler;
|
|
664
|
+
};
|
|
535
665
|
|
|
666
|
+
/***/ }),
|
|
667
|
+
/* 10 */
|
|
668
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
536
669
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
670
|
+
"use strict";
|
|
671
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return resultFromCachedResponse; });
|
|
672
|
+
/**
|
|
673
|
+
* Turns a cache entry into a stateful result.
|
|
674
|
+
*/
|
|
675
|
+
const resultFromCachedResponse = cacheEntry => {
|
|
676
|
+
// No cache entry means we didn't load one yet.
|
|
677
|
+
if (cacheEntry == null) {
|
|
678
|
+
return {
|
|
679
|
+
status: "loading"
|
|
680
|
+
};
|
|
681
|
+
}
|
|
542
682
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* We should never get here as errors in fulfillment are part
|
|
551
|
-
* of the `then`, but if we do.
|
|
552
|
-
*/
|
|
553
|
-
// eslint-disable-next-line no-console
|
|
683
|
+
const {
|
|
684
|
+
data,
|
|
685
|
+
error
|
|
686
|
+
} = cacheEntry;
|
|
554
687
|
|
|
688
|
+
if (error != null) {
|
|
689
|
+
return {
|
|
690
|
+
status: "error",
|
|
691
|
+
error
|
|
692
|
+
};
|
|
693
|
+
}
|
|
555
694
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
// - We don't want to trigger on cachedResult changing, we're
|
|
567
|
-
// just using that as a flag for render state if the other things
|
|
568
|
-
// trigger this effect.
|
|
569
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
570
|
-
}, [handler, handler.getKey(options), interceptor]);
|
|
571
|
-
return Object(_util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__[/* resultFromCacheEntry */ "a"])(result);
|
|
695
|
+
if (data != null) {
|
|
696
|
+
return {
|
|
697
|
+
status: "success",
|
|
698
|
+
data
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return {
|
|
703
|
+
status: "aborted"
|
|
704
|
+
};
|
|
572
705
|
};
|
|
573
706
|
|
|
574
707
|
/***/ }),
|
|
575
|
-
/*
|
|
708
|
+
/* 11 */
|
|
576
709
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
577
710
|
|
|
578
711
|
"use strict";
|
|
579
712
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
|
|
580
|
-
/* harmony import */ var
|
|
713
|
+
/* harmony import */ var _ssr_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
|
|
581
714
|
|
|
582
715
|
|
|
583
716
|
let _default;
|
|
@@ -594,23 +727,14 @@ class RequestFulfillment {
|
|
|
594
727
|
constructor(responseCache = undefined) {
|
|
595
728
|
this._requests = {};
|
|
596
729
|
|
|
597
|
-
this.
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return this._requests[handler.type];
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
this.fulfill = (handler, options) => {
|
|
606
|
-
const handlerRequests = this._getHandlerSubcache(handler);
|
|
607
|
-
|
|
608
|
-
const key = handler.getKey(options);
|
|
730
|
+
this.fulfill = (id, {
|
|
731
|
+
handler,
|
|
732
|
+
hydrate = true
|
|
733
|
+
}) => {
|
|
609
734
|
/**
|
|
610
735
|
* If we have an inflight request, we'll provide that.
|
|
611
736
|
*/
|
|
612
|
-
|
|
613
|
-
const inflight = handlerRequests[key];
|
|
737
|
+
const inflight = this._requests[id];
|
|
614
738
|
|
|
615
739
|
if (inflight) {
|
|
616
740
|
return inflight;
|
|
@@ -626,43 +750,56 @@ class RequestFulfillment {
|
|
|
626
750
|
} = this._responseCache;
|
|
627
751
|
|
|
628
752
|
try {
|
|
629
|
-
const request = handler
|
|
630
|
-
delete
|
|
753
|
+
const request = handler().then(data => {
|
|
754
|
+
delete this._requests[id];
|
|
755
|
+
|
|
756
|
+
if (data == null) {
|
|
757
|
+
// Request aborted. We won't cache this.
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
631
760
|
/**
|
|
632
761
|
* Let's cache the data!
|
|
633
762
|
*
|
|
634
763
|
* NOTE: This only caches when we're server side.
|
|
635
764
|
*/
|
|
636
765
|
|
|
637
|
-
|
|
766
|
+
|
|
767
|
+
return cacheData(id, data, hydrate);
|
|
638
768
|
}).catch(error => {
|
|
639
|
-
delete
|
|
769
|
+
delete this._requests[id];
|
|
640
770
|
/**
|
|
641
771
|
* Let's cache the error!
|
|
642
772
|
*
|
|
643
773
|
* NOTE: This only caches when we're server side.
|
|
644
774
|
*/
|
|
645
775
|
|
|
646
|
-
return cacheError(
|
|
776
|
+
return cacheError(id, error, hydrate);
|
|
647
777
|
});
|
|
648
|
-
|
|
778
|
+
this._requests[id] = request;
|
|
649
779
|
return request;
|
|
650
780
|
} catch (e) {
|
|
651
781
|
/**
|
|
652
782
|
* In this case, we don't cache an inflight request, because there
|
|
653
783
|
* really isn't one.
|
|
654
784
|
*/
|
|
655
|
-
return Promise.resolve(cacheError(
|
|
785
|
+
return Promise.resolve(cacheError(id, e, hydrate));
|
|
656
786
|
}
|
|
657
787
|
};
|
|
658
788
|
|
|
659
|
-
this._responseCache = responseCache ||
|
|
789
|
+
this._responseCache = responseCache || _ssr_cache_js__WEBPACK_IMPORTED_MODULE_0__[/* SsrCache */ "a"].Default;
|
|
660
790
|
}
|
|
791
|
+
/**
|
|
792
|
+
* Get a promise of a request for a given handler and options.
|
|
793
|
+
*
|
|
794
|
+
* This will return an inflight request if one exists, otherwise it will
|
|
795
|
+
* make a new request. Inflight requests are deleted once they resolve.
|
|
796
|
+
*/
|
|
797
|
+
|
|
661
798
|
|
|
662
799
|
}
|
|
663
800
|
|
|
664
801
|
/***/ }),
|
|
665
|
-
/*
|
|
802
|
+
/* 12 */
|
|
666
803
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
667
804
|
|
|
668
805
|
"use strict";
|
|
@@ -673,211 +810,107 @@ class RequestFulfillment {
|
|
|
673
810
|
const GqlRouterContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
|
|
674
811
|
|
|
675
812
|
/***/ }),
|
|
676
|
-
/*
|
|
813
|
+
/* 13 */
|
|
677
814
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
678
815
|
|
|
679
816
|
"use strict";
|
|
680
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
* NOTE: This doesn't work for get/set property accessors.
|
|
688
|
-
*/
|
|
689
|
-
const serializedInitCache = JSON.stringify(source);
|
|
690
|
-
const cloneInitCache = JSON.parse(serializedInitCache);
|
|
691
|
-
return Object.freeze(cloneInitCache);
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* INTERNAL USE ONLY
|
|
695
|
-
*
|
|
696
|
-
* Special case cache implementation for the memory cache.
|
|
697
|
-
*
|
|
698
|
-
* This is only used within our framework for SSR (see ./response-cache.js).
|
|
699
|
-
*/
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
class MemoryCache {
|
|
703
|
-
constructor(source = null) {
|
|
704
|
-
this.store = (handler, options, entry) => {
|
|
705
|
-
const requestType = handler.type;
|
|
706
|
-
const frozenEntry = Object.freeze(entry); // Ensure we have a cache location for this handler type.
|
|
707
|
-
|
|
708
|
-
this._cache[requestType] = this._cache[requestType] || {}; // Cache the data.
|
|
709
|
-
|
|
710
|
-
const key = handler.getKey(options);
|
|
711
|
-
this._cache[requestType][key] = frozenEntry;
|
|
712
|
-
};
|
|
713
|
-
|
|
714
|
-
this.retrieve = (handler, options) => {
|
|
715
|
-
const requestType = handler.type; // Get the internal subcache for the handler.
|
|
716
|
-
|
|
717
|
-
const handlerCache = this._cache[requestType];
|
|
718
|
-
|
|
719
|
-
if (!handlerCache) {
|
|
720
|
-
return null;
|
|
721
|
-
} // Get the response.
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
const key = handler.getKey(options);
|
|
725
|
-
const internalEntry = handlerCache[key];
|
|
726
|
-
|
|
727
|
-
if (internalEntry == null) {
|
|
728
|
-
return null;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
return internalEntry;
|
|
732
|
-
};
|
|
733
|
-
|
|
734
|
-
this.remove = (handler, options) => {
|
|
735
|
-
const requestType = handler.type; // NOTE(somewhatabstract): We could invoke removeAll with a predicate
|
|
736
|
-
// to match the key of the entry we're removing, but that's an
|
|
737
|
-
// inefficient way to remove a single item, so let's not do that.
|
|
738
|
-
// Get the internal subcache for the handler.
|
|
739
|
-
|
|
740
|
-
const handlerCache = this._cache[requestType];
|
|
741
|
-
|
|
742
|
-
if (!handlerCache) {
|
|
743
|
-
return false;
|
|
744
|
-
} // Get the entry.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const key = handler.getKey(options);
|
|
748
|
-
const internalEntry = handlerCache[key];
|
|
749
|
-
|
|
750
|
-
if (internalEntry == null) {
|
|
751
|
-
return false;
|
|
752
|
-
} // Delete the entry.
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
delete handlerCache[key];
|
|
756
|
-
return true;
|
|
757
|
-
};
|
|
758
|
-
|
|
759
|
-
this.removeAll = (handler, predicate) => {
|
|
760
|
-
const requestType = handler.type; // Get the internal subcache for the handler.
|
|
817
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return clearSharedCache; });
|
|
818
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return useSharedCache; });
|
|
819
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
820
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
821
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
|
|
822
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
823
|
+
/* harmony import */ var _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
|
|
761
824
|
|
|
762
|
-
const handlerCache = this._cache[requestType];
|
|
763
825
|
|
|
764
|
-
if (!handlerCache) {
|
|
765
|
-
return 0;
|
|
766
|
-
}
|
|
767
826
|
|
|
768
|
-
let removedCount = 0;
|
|
769
827
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
// We're removing everything so delete the entire subcache.
|
|
780
|
-
removedCount = Object.keys(handlerCache).length;
|
|
781
|
-
delete this._cache[requestType];
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
return removedCount;
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
this.cloneData = () => {
|
|
788
|
-
try {
|
|
789
|
-
return deepClone(this._cache);
|
|
790
|
-
} catch (e) {
|
|
791
|
-
throw new Error(`An error occurred while trying to clone the cache: ${e}`);
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
|
|
795
|
-
this._cache = {};
|
|
828
|
+
/**
|
|
829
|
+
* This is the cache.
|
|
830
|
+
* It's incredibly complex.
|
|
831
|
+
* Very in-memory. So cache. Such complex. Wow.
|
|
832
|
+
*/
|
|
833
|
+
const cache = new _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* ScopedInMemoryCache */ "a"]();
|
|
834
|
+
/**
|
|
835
|
+
* Clear the in-memory cache or a single scope within it.
|
|
836
|
+
*/
|
|
796
837
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
const cloneInitCache = deepClone(source);
|
|
805
|
-
Object.assign(this._cache, cloneInitCache);
|
|
806
|
-
} catch (e) {
|
|
807
|
-
throw new Error(`An error occurred trying to initialize from a response cache snapshot: ${e}`);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
838
|
+
const clearSharedCache = (scope = "") => {
|
|
839
|
+
// If we have a valid scope (empty string is falsy), then clear that scope.
|
|
840
|
+
if (scope && typeof scope === "string") {
|
|
841
|
+
cache.purgeScope(scope);
|
|
842
|
+
} else {
|
|
843
|
+
// Just reset the object. This should be sufficient.
|
|
844
|
+
cache.purgeAll();
|
|
810
845
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
846
|
+
};
|
|
847
|
+
/**
|
|
848
|
+
* Hook to retrieve data from and store data in an in-memory cache.
|
|
849
|
+
*
|
|
850
|
+
* @returns {[?ReadOnlyCacheValue, CacheValueFn]}
|
|
851
|
+
* Returns an array containing the current cache entry (or undefined), a
|
|
852
|
+
* function to set the cache entry (passing null or undefined to this function
|
|
853
|
+
* will delete the entry).
|
|
854
|
+
*
|
|
855
|
+
* To clear a single scope within the cache or the entire cache,
|
|
856
|
+
* the `clearScopedCache` export is available.
|
|
857
|
+
*
|
|
858
|
+
* NOTE: Unlike useState or useReducer, we don't automatically update folks
|
|
859
|
+
* if the value they reference changes. We might add it later (if we need to),
|
|
860
|
+
* but the likelihood here is that things won't be changing in this cache in a
|
|
861
|
+
* way where we would need that. If we do (and likely only in specific
|
|
862
|
+
* circumstances), we should consider adding a simple boolean useState that can
|
|
863
|
+
* be toggled to cause a rerender whenever the referenced cached data changes
|
|
864
|
+
* so that callers can re-render on cache changes. However, we should make
|
|
865
|
+
* sure this toggling is optional - or we could use a callback argument, to
|
|
866
|
+
* achieve this on an as-needed basis.
|
|
867
|
+
*/
|
|
817
868
|
|
|
818
|
-
|
|
819
|
-
|
|
869
|
+
const useSharedCache = (id, scope, initialValue) => {
|
|
870
|
+
// Verify arguments.
|
|
871
|
+
if (!id || typeof id !== "string") {
|
|
872
|
+
throw new _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__["KindError"]("id must be a non-empty string", _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__["Errors"].InvalidInput);
|
|
820
873
|
}
|
|
821
874
|
|
|
822
|
-
|
|
875
|
+
if (!scope || typeof scope !== "string") {
|
|
876
|
+
throw new _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__["KindError"]("scope must be a non-empty string", _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__["Errors"].InvalidInput);
|
|
877
|
+
} // Memoize our APIs.
|
|
878
|
+
// This one allows callers to set or replace the cached value.
|
|
823
879
|
|
|
824
|
-
/***/ }),
|
|
825
|
-
/* 10 */
|
|
826
|
-
/***/ (function(module, exports) {
|
|
827
880
|
|
|
828
|
-
|
|
881
|
+
const cacheValue = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => 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
|
|
882
|
+
// since our last run through. Also, our cache does not know what type it
|
|
883
|
+
// stores, so we have to cast it to the type we're exporting. This is a
|
|
884
|
+
// dev time courtesy, rather than a runtime thing.
|
|
885
|
+
// $FlowIgnore[incompatible-type]
|
|
829
886
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
887
|
+
let currentValue = cache.get(scope, id); // If we have an initial value, we need to add it to the cache
|
|
888
|
+
// and use it as our current value.
|
|
833
889
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
* Base implementation for creating a request handler.
|
|
838
|
-
*
|
|
839
|
-
* Provides a base implementation of the `IRequestHandler` base class for
|
|
840
|
-
* use with the Wonder Blocks Data framework.
|
|
841
|
-
*/
|
|
842
|
-
class RequestHandler {
|
|
843
|
-
constructor(type, hydrate = true) {
|
|
844
|
-
this._type = type;
|
|
845
|
-
this._hydrate = !!hydrate;
|
|
846
|
-
}
|
|
890
|
+
if (currentValue == null && initialValue !== undefined) {
|
|
891
|
+
// Get the initial value.
|
|
892
|
+
const value = typeof initialValue === "function" ? initialValue() : initialValue; // Update the cache.
|
|
847
893
|
|
|
848
|
-
|
|
849
|
-
return this._type;
|
|
850
|
-
}
|
|
894
|
+
cacheValue(value); // Make sure we return this value as our current value.
|
|
851
895
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
}
|
|
896
|
+
currentValue = value;
|
|
897
|
+
} // Now we have everything, let's return it.
|
|
855
898
|
|
|
856
|
-
getKey(options) {
|
|
857
|
-
try {
|
|
858
|
-
return options === undefined ? "undefined" : JSON.stringify(options);
|
|
859
|
-
} catch (e) {
|
|
860
|
-
throw new Error(`Failed to auto-generate key: ${e}`);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
fulfillRequest(options) {
|
|
865
|
-
throw new Error("Not implemented");
|
|
866
|
-
}
|
|
867
899
|
|
|
868
|
-
|
|
900
|
+
return [currentValue, cacheValue];
|
|
901
|
+
};
|
|
869
902
|
|
|
870
903
|
/***/ }),
|
|
871
|
-
/*
|
|
904
|
+
/* 14 */
|
|
872
905
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
873
906
|
|
|
874
907
|
"use strict";
|
|
875
908
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
|
|
876
909
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
877
910
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
878
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
911
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
879
912
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
880
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
913
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
881
914
|
|
|
882
915
|
|
|
883
916
|
|
|
@@ -899,13 +932,22 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
899
932
|
}
|
|
900
933
|
|
|
901
934
|
/***/ }),
|
|
902
|
-
/*
|
|
935
|
+
/* 15 */
|
|
903
936
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
904
937
|
|
|
905
938
|
"use strict";
|
|
906
939
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
907
940
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
908
|
-
/* harmony import */ var
|
|
941
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
942
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
943
|
+
/* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
|
|
944
|
+
/* harmony import */ var _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
|
|
945
|
+
/* harmony import */ var _hooks_use_request_interception_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
|
|
946
|
+
/* harmony import */ var _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10);
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
|
|
909
951
|
|
|
910
952
|
|
|
911
953
|
|
|
@@ -914,68 +956,145 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
914
956
|
* requirements can be placed in a React application in a manner that will
|
|
915
957
|
* support server-side rendering and efficient caching.
|
|
916
958
|
*/
|
|
917
|
-
const Data =
|
|
918
|
-
|
|
919
|
-
|
|
959
|
+
const Data = ({
|
|
960
|
+
requestId,
|
|
961
|
+
handler,
|
|
962
|
+
children,
|
|
963
|
+
hydrate,
|
|
964
|
+
showOldDataWhileLoading,
|
|
965
|
+
alwaysRequestOnHydration
|
|
966
|
+
}) => {
|
|
967
|
+
const interceptedHandler = Object(_hooks_use_request_interception_js__WEBPACK_IMPORTED_MODULE_4__[/* useRequestInterception */ "a"])(requestId, handler);
|
|
968
|
+
const hydrateResult = Object(_hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_3__[/* useServerEffect */ "a"])(requestId, interceptedHandler, hydrate);
|
|
969
|
+
const [currentResult, setResult] = react__WEBPACK_IMPORTED_MODULE_0__["useState"](hydrateResult); // Here we make sure the request still occurs client-side as needed.
|
|
970
|
+
// This is for legacy usage that expects this. Eventually we will want
|
|
971
|
+
// to deprecate.
|
|
972
|
+
|
|
973
|
+
react__WEBPACK_IMPORTED_MODULE_0__["useEffect"](() => {
|
|
974
|
+
// This is here until I can do a better documentation example for
|
|
975
|
+
// the TrackData docs.
|
|
976
|
+
// istanbul ignore next
|
|
977
|
+
if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__["Server"].isServerSide()) {
|
|
978
|
+
return;
|
|
979
|
+
} // We don't bother with this if we have hydration data and we're not
|
|
980
|
+
// forcing a request on hydration.
|
|
981
|
+
// We don't care if these things change after the first render,
|
|
982
|
+
// so we don't want them in the inputs array.
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
if (!alwaysRequestOnHydration && (hydrateResult == null ? void 0 : hydrateResult.data) != null) {
|
|
986
|
+
return;
|
|
987
|
+
} // If we're not hydrating a result and we're not going to render
|
|
988
|
+
// with old data until we're loaded, we want to make sure we set our
|
|
989
|
+
// result to null so that we're in the loading state.
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
if (!showOldDataWhileLoading) {
|
|
993
|
+
// Mark ourselves as loading.
|
|
994
|
+
setResult(null);
|
|
995
|
+
} // We aren't server-side, so let's make the request.
|
|
996
|
+
// We don't need to use our built-in request fulfillment here if we
|
|
997
|
+
// don't want, but it does mean we'll share inflight requests for the
|
|
998
|
+
// same ID and the result will be in the same format as the
|
|
999
|
+
// hydrated value.
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
let cancel = false;
|
|
1003
|
+
_util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"].Default.fulfill(requestId, {
|
|
1004
|
+
handler: interceptedHandler
|
|
1005
|
+
}).then(result => {
|
|
1006
|
+
if (cancel) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
setResult(result);
|
|
1011
|
+
return;
|
|
1012
|
+
}).catch(e => {
|
|
1013
|
+
if (cancel) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* We should never get here as errors in fulfillment are part
|
|
1018
|
+
* of the `then`, but if we do.
|
|
1019
|
+
*/
|
|
1020
|
+
// eslint-disable-next-line no-console
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
console.error(`Unexpected error occurred during data fulfillment: ${e}`);
|
|
1024
|
+
setResult({
|
|
1025
|
+
error: typeof e === "string" ? e : e.message
|
|
1026
|
+
});
|
|
1027
|
+
return;
|
|
1028
|
+
});
|
|
1029
|
+
return () => {
|
|
1030
|
+
cancel = true;
|
|
1031
|
+
}; // If the handler changes, we don't care. The ID is what indicates
|
|
1032
|
+
// the request that should be made and folks shouldn't be changing the
|
|
1033
|
+
// handler without changing the ID as well.
|
|
1034
|
+
// In addition, we don't want to include hydrateResult nor
|
|
1035
|
+
// alwaysRequestOnHydration as them changinng after the first pass
|
|
1036
|
+
// is irrelevant.
|
|
1037
|
+
// Finally, we don't want to include showOldDataWhileLoading as that
|
|
1038
|
+
// changing on its own is also not relevant. It only matters if the
|
|
1039
|
+
// request itself changes. All of which is to say that we only
|
|
1040
|
+
// run this effect for the ID changing.
|
|
1041
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1042
|
+
}, [requestId]);
|
|
1043
|
+
return children(Object(_util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_5__[/* resultFromCachedResponse */ "a"])(currentResult));
|
|
920
1044
|
};
|
|
921
1045
|
|
|
922
1046
|
/* harmony default export */ __webpack_exports__["a"] = (Data);
|
|
923
1047
|
|
|
924
1048
|
/***/ }),
|
|
925
|
-
/*
|
|
1049
|
+
/* 16 */
|
|
926
1050
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
927
1051
|
|
|
928
1052
|
"use strict";
|
|
929
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return InterceptData; });
|
|
930
1053
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
931
1054
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
932
|
-
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
1055
|
+
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
933
1056
|
|
|
934
1057
|
|
|
935
1058
|
|
|
936
1059
|
/**
|
|
937
|
-
* This component provides a mechanism to intercept
|
|
938
|
-
*
|
|
939
|
-
* useful for testing.
|
|
1060
|
+
* This component provides a mechanism to intercept data requests.
|
|
1061
|
+
* This is for use in testing.
|
|
940
1062
|
*
|
|
941
1063
|
* This component is not recommended for use in production code as it
|
|
942
1064
|
* can prevent predictable functioning of the Wonder Blocks Data framework.
|
|
943
1065
|
* One possible side-effect is that inflight requests from the interceptor could
|
|
944
|
-
* be picked up by `Data` component requests
|
|
945
|
-
*
|
|
1066
|
+
* be picked up by `Data` component requests from outside the children of this
|
|
1067
|
+
* component.
|
|
946
1068
|
*
|
|
947
|
-
*
|
|
948
|
-
*
|
|
949
|
-
*
|
|
950
|
-
* will be replaced.
|
|
1069
|
+
* Interceptions within the same component tree are chained such that the
|
|
1070
|
+
* interceptor closest to the intercepted request is called first, and the
|
|
1071
|
+
* furthest interceptor is called last.
|
|
951
1072
|
*/
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
});
|
|
966
|
-
}
|
|
1073
|
+
const InterceptRequests = ({
|
|
1074
|
+
interceptor,
|
|
1075
|
+
children
|
|
1076
|
+
}) => {
|
|
1077
|
+
const interceptors = react__WEBPACK_IMPORTED_MODULE_0__["useContext"](_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"]);
|
|
1078
|
+
const updatedInterceptors = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"]( // We could build this in reverse order so that our hook that does
|
|
1079
|
+
// the interception didn't have to use reduceRight, but I think it
|
|
1080
|
+
// is easier to think about if we do this in component tree order.
|
|
1081
|
+
() => [].concat(interceptors, [interceptor]), [interceptors, interceptor]);
|
|
1082
|
+
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"].Provider, {
|
|
1083
|
+
value: updatedInterceptors
|
|
1084
|
+
}, children);
|
|
1085
|
+
};
|
|
967
1086
|
|
|
968
|
-
|
|
1087
|
+
/* harmony default export */ __webpack_exports__["a"] = (InterceptRequests);
|
|
969
1088
|
|
|
970
1089
|
/***/ }),
|
|
971
|
-
/*
|
|
1090
|
+
/* 17 */
|
|
972
1091
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
973
1092
|
|
|
974
1093
|
"use strict";
|
|
975
1094
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouter; });
|
|
976
1095
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
977
1096
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
978
|
-
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
1097
|
+
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
|
|
979
1098
|
|
|
980
1099
|
|
|
981
1100
|
|
|
@@ -1012,16 +1131,16 @@ const GqlRouter = ({
|
|
|
1012
1131
|
};
|
|
1013
1132
|
|
|
1014
1133
|
/***/ }),
|
|
1015
|
-
/*
|
|
1134
|
+
/* 18 */
|
|
1016
1135
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1017
1136
|
|
|
1018
1137
|
"use strict";
|
|
1019
1138
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGql; });
|
|
1020
1139
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1021
1140
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
1022
|
-
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
1023
|
-
/* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
1024
|
-
/* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
|
|
1141
|
+
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
|
|
1142
|
+
/* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(19);
|
|
1143
|
+
/* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2);
|
|
1025
1144
|
|
|
1026
1145
|
|
|
1027
1146
|
|
|
@@ -1032,6 +1151,10 @@ const GqlRouter = ({
|
|
|
1032
1151
|
*
|
|
1033
1152
|
* The fetch function will resolve null if the request was aborted, otherwise
|
|
1034
1153
|
* it will resolve the data returned by the GraphQL server.
|
|
1154
|
+
*
|
|
1155
|
+
* Context is merged with the default context provided to the GqlRouter.
|
|
1156
|
+
* Values in the partial context given to the returned fetch function will
|
|
1157
|
+
* only be included if they have a value other than undefined.
|
|
1035
1158
|
*/
|
|
1036
1159
|
const useGql = () => {
|
|
1037
1160
|
// This hook only works if the `GqlRouter` has been used to setup context.
|
|
@@ -1053,12 +1176,23 @@ const useGql = () => {
|
|
|
1053
1176
|
const gqlFetch = Object(react__WEBPACK_IMPORTED_MODULE_0__["useMemo"])(() => (operation, options = Object.freeze({})) => {
|
|
1054
1177
|
const {
|
|
1055
1178
|
variables,
|
|
1056
|
-
context
|
|
1057
|
-
} = options; //
|
|
1179
|
+
context = {}
|
|
1180
|
+
} = options; // Let's merge the partial context of the fetch with the
|
|
1181
|
+
// default context. We deliberately don't spread because
|
|
1182
|
+
// spreading would overwrite default context values with
|
|
1183
|
+
// undefined if the partial context includes a value explicitly
|
|
1184
|
+
// set to undefined. Instead, we use a map/reduce of keys.
|
|
1185
|
+
|
|
1186
|
+
const mergedContext = Object.keys(context).reduce((acc, key) => {
|
|
1187
|
+
if (context[key] !== undefined) {
|
|
1188
|
+
acc[key] = context[key];
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
return acc;
|
|
1192
|
+
}, { ...defaultContext
|
|
1193
|
+
}); // Invoke the fetch and extract the data.
|
|
1058
1194
|
|
|
1059
|
-
return fetch(operation, variables,
|
|
1060
|
-
...context
|
|
1061
|
-
}).then(_util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__[/* getGqlDataFromResponse */ "a"], error => {
|
|
1195
|
+
return fetch(operation, variables, mergedContext).then(_util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__[/* getGqlDataFromResponse */ "a"], error => {
|
|
1062
1196
|
// Return null if the request was aborted.
|
|
1063
1197
|
// The only way to detect this reliably, it seems, is to
|
|
1064
1198
|
// check the error name and see if it's "AbortError" (this
|
|
@@ -1076,55 +1210,12 @@ const useGql = () => {
|
|
|
1076
1210
|
};
|
|
1077
1211
|
|
|
1078
1212
|
/***/ }),
|
|
1079
|
-
/*
|
|
1080
|
-
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1081
|
-
|
|
1082
|
-
"use strict";
|
|
1083
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return resultFromCacheEntry; });
|
|
1084
|
-
/**
|
|
1085
|
-
* Turns a cache entry into a stateful result.
|
|
1086
|
-
*/
|
|
1087
|
-
const resultFromCacheEntry = cacheEntry => {
|
|
1088
|
-
// No cache entry means we didn't load one yet.
|
|
1089
|
-
if (cacheEntry == null) {
|
|
1090
|
-
return {
|
|
1091
|
-
status: "loading"
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
const {
|
|
1096
|
-
data,
|
|
1097
|
-
error
|
|
1098
|
-
} = cacheEntry;
|
|
1099
|
-
|
|
1100
|
-
if (data != null) {
|
|
1101
|
-
return {
|
|
1102
|
-
status: "success",
|
|
1103
|
-
data
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
if (error == null) {
|
|
1108
|
-
// We should never get here ever.
|
|
1109
|
-
return {
|
|
1110
|
-
status: "error",
|
|
1111
|
-
error: "Loaded result has invalid state where data and error are missing"
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
return {
|
|
1116
|
-
status: "error",
|
|
1117
|
-
error
|
|
1118
|
-
};
|
|
1119
|
-
};
|
|
1120
|
-
|
|
1121
|
-
/***/ }),
|
|
1122
|
-
/* 18 */
|
|
1213
|
+
/* 19 */
|
|
1123
1214
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1124
1215
|
|
|
1125
1216
|
"use strict";
|
|
1126
1217
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlDataFromResponse; });
|
|
1127
|
-
/* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
1218
|
+
/* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
1128
1219
|
|
|
1129
1220
|
/**
|
|
1130
1221
|
* Validate a GQL operation response and extract the data.
|
|
@@ -1187,7 +1278,7 @@ const getGqlDataFromResponse = async response => {
|
|
|
1187
1278
|
};
|
|
1188
1279
|
|
|
1189
1280
|
/***/ }),
|
|
1190
|
-
/*
|
|
1281
|
+
/* 20 */
|
|
1191
1282
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1192
1283
|
|
|
1193
1284
|
"use strict";
|
|
@@ -1197,40 +1288,51 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1197
1288
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
|
|
1198
1289
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
|
|
1199
1290
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
|
|
1200
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
1291
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
|
|
1201
1292
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
1202
|
-
/* harmony import */ var
|
|
1203
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
1204
|
-
/* harmony import */ var
|
|
1205
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1293
|
+
/* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
|
|
1294
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
1295
|
+
/* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(14);
|
|
1296
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
|
|
1297
|
+
|
|
1298
|
+
/* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(15);
|
|
1299
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
|
|
1206
1300
|
|
|
1207
|
-
/* harmony import */ var
|
|
1208
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1301
|
+
/* harmony import */ var _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(16);
|
|
1302
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptRequests", function() { return _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
|
|
1209
1303
|
|
|
1210
|
-
/* harmony import */ var
|
|
1211
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1304
|
+
/* harmony import */ var _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(8);
|
|
1305
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useServerEffect", function() { return _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
|
|
1212
1306
|
|
|
1213
|
-
/* harmony import */ var
|
|
1214
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1307
|
+
/* harmony import */ var _hooks_use_request_interception_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(9);
|
|
1308
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useRequestInterception", function() { return _hooks_use_request_interception_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
|
|
1215
1309
|
|
|
1216
|
-
/* harmony import */ var
|
|
1217
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1310
|
+
/* harmony import */ var _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(13);
|
|
1311
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useSharedCache", function() { return _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_8__["b"]; });
|
|
1218
1312
|
|
|
1219
|
-
/* harmony
|
|
1220
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
|
|
1313
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "clearSharedCache", function() { return _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
|
|
1221
1314
|
|
|
1222
|
-
/* harmony import */ var
|
|
1223
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1315
|
+
/* harmony import */ var _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(5);
|
|
1316
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ScopedInMemoryCache", function() { return _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
|
|
1224
1317
|
|
|
1225
|
-
/* harmony import */ var
|
|
1226
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1318
|
+
/* harmony import */ var _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(10);
|
|
1319
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "resultFromCachedResponse", function() { return _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
|
|
1227
1320
|
|
|
1228
|
-
/* harmony
|
|
1321
|
+
/* harmony import */ var _components_gql_router_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(17);
|
|
1322
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_11__["a"]; });
|
|
1229
1323
|
|
|
1324
|
+
/* harmony import */ var _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(18);
|
|
1325
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useGql", function() { return _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_12__["a"]; });
|
|
1230
1326
|
|
|
1327
|
+
/* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(2);
|
|
1328
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_13__["b"]; });
|
|
1231
1329
|
|
|
1330
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlError", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_13__["a"]; });
|
|
1232
1331
|
|
|
1233
|
-
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
const initializeCache = source => _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default.initialize(source);
|
|
1234
1336
|
const fulfillAllDataRequests = () => {
|
|
1235
1337
|
if (!_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
1236
1338
|
return Promise.reject(new Error("Data requests are not tracked when client-side"));
|
|
@@ -1245,8 +1347,11 @@ const hasUnfulfilledRequests = () => {
|
|
|
1245
1347
|
|
|
1246
1348
|
return _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestTracker */ "a"].Default.hasUnfulfilledRequests;
|
|
1247
1349
|
};
|
|
1248
|
-
const removeFromCache =
|
|
1249
|
-
const removeAllFromCache =
|
|
1350
|
+
const removeFromCache = id => _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default.remove(id);
|
|
1351
|
+
const removeAllFromCache = predicate => _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default.removeAll(predicate);
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
|
|
1250
1355
|
|
|
1251
1356
|
|
|
1252
1357
|
|