@khanacademy/wonder-blocks-data 5.0.0 → 6.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 +31 -0
- package/dist/es/index.js +778 -372
- package/dist/index.js +1203 -551
- package/legacy-docs.md +3 -0
- package/package.json +2 -2
- package/src/__docs__/_overview_.stories.mdx +18 -0
- package/src/__docs__/_overview_graphql.stories.mdx +35 -0
- package/src/__docs__/_overview_ssr_.stories.mdx +185 -0
- package/src/__docs__/_overview_testing_.stories.mdx +123 -0
- package/src/__docs__/exports.clear-shared-cache.stories.mdx +20 -0
- package/src/__docs__/exports.data-error.stories.mdx +23 -0
- package/src/__docs__/exports.data-errors.stories.mdx +23 -0
- package/src/{components/data.md → __docs__/exports.data.stories.mdx} +15 -18
- package/src/__docs__/exports.fulfill-all-data-requests.stories.mdx +24 -0
- package/src/__docs__/exports.gql-error.stories.mdx +23 -0
- package/src/__docs__/exports.gql-errors.stories.mdx +20 -0
- package/src/__docs__/exports.gql-router.stories.mdx +29 -0
- package/src/__docs__/exports.has-unfulfilled-requests.stories.mdx +20 -0
- package/src/{components/intercept-requests.md → __docs__/exports.intercept-requests.stories.mdx} +16 -1
- package/src/__docs__/exports.intialize-cache.stories.mdx +29 -0
- package/src/__docs__/exports.remove-all-from-cache.stories.mdx +24 -0
- package/src/__docs__/exports.remove-from-cache.stories.mdx +25 -0
- package/src/__docs__/exports.request-fulfillment.stories.mdx +36 -0
- package/src/__docs__/exports.scoped-in-memory-cache.stories.mdx +92 -0
- package/src/__docs__/exports.serializable-in-memory-cache.stories.mdx +112 -0
- package/src/__docs__/exports.status.stories.mdx +31 -0
- package/src/{components/track-data.md → __docs__/exports.track-data.stories.mdx} +15 -0
- package/src/__docs__/exports.use-cached-effect.stories.mdx +41 -0
- package/src/__docs__/exports.use-gql.stories.mdx +73 -0
- package/src/__docs__/exports.use-hydratable-effect.stories.mdx +43 -0
- package/src/__docs__/exports.use-server-effect.stories.mdx +38 -0
- package/src/__docs__/exports.use-shared-cache.stories.mdx +30 -0
- package/src/__docs__/exports.when-client-side.stories.mdx +33 -0
- package/src/__docs__/types.cached-response.stories.mdx +29 -0
- package/src/__docs__/types.error-options.stories.mdx +21 -0
- package/src/__docs__/types.gql-context.stories.mdx +20 -0
- package/src/__docs__/types.gql-fetch-fn.stories.mdx +24 -0
- package/src/__docs__/types.gql-fetch-options.stories.mdx +24 -0
- package/src/__docs__/types.gql-operation-type.stories.mdx +24 -0
- package/src/__docs__/types.gql-operation.stories.mdx +67 -0
- package/src/__docs__/types.response-cache.stories.mdx +33 -0
- package/src/__docs__/types.result.stories.mdx +39 -0
- package/src/__docs__/types.scoped-cache.stories.mdx +27 -0
- package/src/__docs__/types.valid-cache-data.stories.mdx +23 -0
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +0 -80
- package/src/__tests__/generated-snapshot.test.js +0 -24
- package/src/components/__tests__/data.test.js +149 -128
- package/src/components/data.js +22 -112
- package/src/components/intercept-requests.js +1 -1
- package/src/hooks/__tests__/__snapshots__/use-shared-cache.test.js.snap +8 -8
- package/src/hooks/__tests__/use-cached-effect.test.js +507 -0
- package/src/hooks/__tests__/use-gql-router-context.test.js +133 -0
- package/src/hooks/__tests__/use-gql.test.js +1 -30
- package/src/hooks/__tests__/use-hydratable-effect.test.js +728 -0
- package/src/hooks/__tests__/use-server-effect.test.js +39 -11
- package/src/hooks/use-cached-effect.js +225 -0
- package/src/hooks/use-gql-router-context.js +50 -0
- package/src/hooks/use-gql.js +22 -52
- package/src/hooks/use-hydratable-effect.js +213 -0
- package/src/hooks/use-request-interception.js +20 -23
- package/src/hooks/use-server-effect.js +12 -5
- package/src/hooks/use-shared-cache.js +13 -11
- package/src/index.js +53 -2
- package/src/util/__tests__/__snapshots__/serializable-in-memory-cache.test.js.snap +19 -0
- package/src/util/__tests__/merge-gql-context.test.js +74 -0
- package/src/util/__tests__/request-fulfillment.test.js +23 -42
- package/src/util/__tests__/request-tracking.test.js +26 -7
- package/src/util/__tests__/result-from-cache-response.test.js +19 -5
- package/src/util/__tests__/scoped-in-memory-cache.test.js +6 -85
- package/src/util/__tests__/serializable-in-memory-cache.test.js +398 -0
- package/src/util/__tests__/ssr-cache.test.js +52 -52
- package/src/util/abort-error.js +15 -0
- package/src/util/data-error.js +58 -0
- package/src/util/get-gql-data-from-response.js +3 -2
- package/src/util/gql-error.js +19 -11
- package/src/util/merge-gql-context.js +34 -0
- package/src/util/request-fulfillment.js +49 -46
- package/src/util/request-tracking.js +69 -15
- package/src/util/result-from-cache-response.js +12 -16
- package/src/util/scoped-in-memory-cache.js +24 -47
- package/src/util/serializable-in-memory-cache.js +49 -0
- package/src/util/ssr-cache.js +9 -8
- package/src/util/status.js +30 -0
- package/src/util/types.js +18 -1
- package/docs.md +0 -122
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 = 28);
|
|
86
86
|
/******/ })
|
|
87
87
|
/************************************************************************/
|
|
88
88
|
/******/ ([
|
|
@@ -93,32 +93,111 @@ module.exports = require("react");
|
|
|
93
93
|
|
|
94
94
|
/***/ }),
|
|
95
95
|
/* 1 */
|
|
96
|
-
/***/ (function(module,
|
|
96
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
"use strict";
|
|
99
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return DataErrors; });
|
|
100
|
+
/* 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);
|
|
102
|
+
/* 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
|
+
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Error kinds for DataError.
|
|
107
|
+
*/
|
|
108
|
+
const DataErrors = Object.freeze({
|
|
109
|
+
/**
|
|
110
|
+
* The kind of error is not known.
|
|
111
|
+
*/
|
|
112
|
+
Unknown: "Unknown",
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The error is internal to the executing code.
|
|
116
|
+
*/
|
|
117
|
+
Internal: "Internal",
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* There was a problem with the provided input.
|
|
121
|
+
*/
|
|
122
|
+
InvalidInput: "InvalidInput",
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A network error occurred.
|
|
126
|
+
*/
|
|
127
|
+
Network: "Network",
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Response could not be parsed.
|
|
131
|
+
*/
|
|
132
|
+
Parse: "Parse",
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* An error that occurred during SSR and was hydrated from cache
|
|
136
|
+
*/
|
|
137
|
+
Hydrated: "Hydrated"
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* An error from the Wonder Blocks Data API.
|
|
141
|
+
*
|
|
142
|
+
* Errors of this type will have names of the format:
|
|
143
|
+
* `${kind}DataError`
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
class DataError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"] {
|
|
147
|
+
constructor(message, kind, {
|
|
148
|
+
metadata,
|
|
149
|
+
cause
|
|
150
|
+
} = {}) {
|
|
151
|
+
super(message, kind, {
|
|
152
|
+
metadata,
|
|
153
|
+
cause,
|
|
154
|
+
name: "Data"
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|
|
99
159
|
|
|
100
160
|
/***/ }),
|
|
101
161
|
/* 2 */
|
|
162
|
+
/***/ (function(module, exports) {
|
|
163
|
+
|
|
164
|
+
module.exports = require("@khanacademy/wonder-blocks-core");
|
|
165
|
+
|
|
166
|
+
/***/ }),
|
|
167
|
+
/* 3 */
|
|
102
168
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
103
169
|
|
|
104
170
|
"use strict";
|
|
105
171
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return GqlErrors; });
|
|
106
172
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlError; });
|
|
107
|
-
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
173
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
|
|
108
174
|
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
109
175
|
|
|
110
176
|
|
|
111
177
|
/**
|
|
112
178
|
* Error kinds for GqlError.
|
|
113
179
|
*/
|
|
114
|
-
const GqlErrors = Object.freeze({
|
|
115
|
-
|
|
116
|
-
|
|
180
|
+
const GqlErrors = Object.freeze({
|
|
181
|
+
/**
|
|
182
|
+
* An internal framework error.
|
|
183
|
+
*/
|
|
184
|
+
Internal: "Internal",
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Response does not have the correct structure for a GraphQL response.
|
|
188
|
+
*/
|
|
117
189
|
BadResponse: "BadResponse",
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* A valid GraphQL result with errors field in the payload.
|
|
193
|
+
*/
|
|
118
194
|
ErrorResult: "ErrorResult"
|
|
119
195
|
});
|
|
120
196
|
/**
|
|
121
197
|
* An error from the GQL API.
|
|
198
|
+
*
|
|
199
|
+
* Errors of this type will have names of the format:
|
|
200
|
+
* `${kind}GqlError`
|
|
122
201
|
*/
|
|
123
202
|
|
|
124
203
|
class GqlError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"] {
|
|
@@ -129,27 +208,50 @@ class GqlError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0
|
|
|
129
208
|
super(message, kind, {
|
|
130
209
|
metadata,
|
|
131
210
|
cause,
|
|
132
|
-
|
|
211
|
+
name: "Gql"
|
|
133
212
|
});
|
|
134
213
|
}
|
|
135
214
|
|
|
136
215
|
}
|
|
137
216
|
|
|
138
217
|
/***/ }),
|
|
139
|
-
/*
|
|
140
|
-
/***/ (function(module,
|
|
218
|
+
/* 4 */
|
|
219
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
141
220
|
|
|
142
|
-
|
|
221
|
+
"use strict";
|
|
222
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Status; });
|
|
223
|
+
const loadingStatus = Object.freeze({
|
|
224
|
+
status: "loading"
|
|
225
|
+
});
|
|
226
|
+
const abortedStatus = Object.freeze({
|
|
227
|
+
status: "aborted"
|
|
228
|
+
});
|
|
229
|
+
/**
|
|
230
|
+
* Create Result<TData> instances with specific statuses.
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
const Status = Object.freeze({
|
|
234
|
+
loading: () => loadingStatus,
|
|
235
|
+
aborted: () => abortedStatus,
|
|
236
|
+
success: data => ({
|
|
237
|
+
status: "success",
|
|
238
|
+
data
|
|
239
|
+
}),
|
|
240
|
+
error: error => ({
|
|
241
|
+
status: "error",
|
|
242
|
+
error
|
|
243
|
+
})
|
|
244
|
+
});
|
|
143
245
|
|
|
144
246
|
/***/ }),
|
|
145
|
-
/*
|
|
247
|
+
/* 5 */
|
|
146
248
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
147
249
|
|
|
148
250
|
"use strict";
|
|
149
251
|
/* 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__(
|
|
252
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
151
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__);
|
|
152
|
-
/* harmony import */ var
|
|
254
|
+
/* harmony import */ var _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
|
|
153
255
|
|
|
154
256
|
|
|
155
257
|
const DefaultScope = "default";
|
|
@@ -181,7 +283,7 @@ class SsrCache {
|
|
|
181
283
|
throw new Error("Cannot initialize data response cache more than once");
|
|
182
284
|
}
|
|
183
285
|
|
|
184
|
-
this._hydrationCache = new
|
|
286
|
+
this._hydrationCache = new _serializable_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SerializableInMemoryCache */ "a"]({
|
|
185
287
|
// $FlowIgnore[incompatible-call]
|
|
186
288
|
[DefaultScope]: source
|
|
187
289
|
});
|
|
@@ -259,8 +361,8 @@ class SsrCache {
|
|
|
259
361
|
return (_cache$DefaultScope = cache[DefaultScope]) != null ? _cache$DefaultScope : {};
|
|
260
362
|
};
|
|
261
363
|
|
|
262
|
-
this._ssrOnlyCache = _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? ssrOnlyCache || new
|
|
263
|
-
this._hydrationCache = hydrationCache || new
|
|
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"]();
|
|
264
366
|
}
|
|
265
367
|
|
|
266
368
|
_setCachedResponse(id, entry, hydrate) {
|
|
@@ -293,124 +395,13 @@ class SsrCache {
|
|
|
293
395
|
}
|
|
294
396
|
|
|
295
397
|
/***/ }),
|
|
296
|
-
/*
|
|
297
|
-
/***/ (function(module,
|
|
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
|
-
|
|
398
|
+
/* 6 */
|
|
399
|
+
/***/ (function(module, exports) {
|
|
409
400
|
|
|
410
|
-
|
|
401
|
+
module.exports = require("@khanacademy/wonder-stuff-core");
|
|
411
402
|
|
|
412
403
|
/***/ }),
|
|
413
|
-
/*
|
|
404
|
+
/* 7 */
|
|
414
405
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
415
406
|
|
|
416
407
|
"use strict";
|
|
@@ -418,7 +409,7 @@ class ScopedInMemoryCache {
|
|
|
418
409
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
|
|
419
410
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
420
411
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
421
|
-
/* harmony import */ var _ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
412
|
+
/* harmony import */ var _ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
422
413
|
/* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
|
|
423
414
|
|
|
424
415
|
|
|
@@ -477,11 +468,55 @@ class RequestTracker {
|
|
|
477
468
|
|
|
478
469
|
this.fulfillTrackedRequests = () => {
|
|
479
470
|
const promises = [];
|
|
471
|
+
const {
|
|
472
|
+
cacheData,
|
|
473
|
+
cacheError
|
|
474
|
+
} = this._responseCache;
|
|
480
475
|
|
|
481
476
|
for (const requestKey of Object.keys(this._trackedRequests)) {
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
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.
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
return;
|
|
514
|
+
}));
|
|
515
|
+
} catch (e) {
|
|
516
|
+
// This captures if there are problems in the code that
|
|
517
|
+
// begins the requests.
|
|
518
|
+
promises.push(Promise.resolve(cacheError(requestKey, e, options.hydrate)));
|
|
519
|
+
}
|
|
485
520
|
}
|
|
486
521
|
/**
|
|
487
522
|
* Clear out our tracked info.
|
|
@@ -489,16 +524,15 @@ class RequestTracker {
|
|
|
489
524
|
* We call this now for a simpler API.
|
|
490
525
|
*
|
|
491
526
|
* If we reset the tracked calls after all promises resolve, any
|
|
492
|
-
*
|
|
527
|
+
* request tracking done while promises are in flight would be lost.
|
|
493
528
|
*
|
|
494
529
|
* If we don't reset at all, then we have to expose the `reset` call
|
|
495
530
|
* for consumers to use, or they'll only ever be able to accumulate
|
|
496
531
|
* more and more tracked requests, having to fulfill them all every
|
|
497
532
|
* time.
|
|
498
533
|
*
|
|
499
|
-
* Calling it here means we can have multiple "track -> request"
|
|
500
|
-
* in a row and in an easy to reason about manner.
|
|
501
|
-
*
|
|
534
|
+
* Calling it here means we can have multiple "track -> request"
|
|
535
|
+
* cycles in a row and in an easy to reason about manner.
|
|
502
536
|
*/
|
|
503
537
|
|
|
504
538
|
|
|
@@ -511,7 +545,7 @@ class RequestTracker {
|
|
|
511
545
|
};
|
|
512
546
|
|
|
513
547
|
this._responseCache = responseCache || _ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default;
|
|
514
|
-
this._requestFulfillment = new _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"](
|
|
548
|
+
this._requestFulfillment = new _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"]();
|
|
515
549
|
}
|
|
516
550
|
/**
|
|
517
551
|
* Track a request.
|
|
@@ -545,123 +579,221 @@ class RequestTracker {
|
|
|
545
579
|
}
|
|
546
580
|
|
|
547
581
|
/***/ }),
|
|
548
|
-
/*
|
|
582
|
+
/* 8 */
|
|
549
583
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
550
584
|
|
|
551
585
|
"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; });
|
|
552
588
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
553
589
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
/**
|
|
557
|
-
* InterceptContext defines a map from request ID to interception methods.
|
|
558
|
-
*
|
|
559
|
-
* INTERNAL USE ONLY
|
|
560
|
-
*/
|
|
561
|
-
const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"]([]);
|
|
562
|
-
/* harmony default export */ __webpack_exports__["a"] = (InterceptContext);
|
|
563
|
-
|
|
564
|
-
/***/ }),
|
|
565
|
-
/* 8 */
|
|
566
|
-
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
567
|
-
|
|
568
|
-
"use strict";
|
|
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);
|
|
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__);
|
|
572
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
|
|
573
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
|
|
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);
|
|
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);
|
|
576
592
|
|
|
577
593
|
|
|
578
594
|
|
|
579
595
|
|
|
596
|
+
/**
|
|
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.
|
|
604
|
+
*/
|
|
580
605
|
|
|
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
|
+
};
|
|
581
615
|
/**
|
|
582
|
-
* Hook to
|
|
616
|
+
* Hook to retrieve data from and store data in an in-memory cache.
|
|
583
617
|
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
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).
|
|
588
622
|
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
* embedded into a page so that the result can by hydrated (if that is a
|
|
592
|
-
* requirement).
|
|
623
|
+
* To clear a single scope within the cache or the entire cache,
|
|
624
|
+
* the `clearScopedCache` export is available.
|
|
593
625
|
*
|
|
594
|
-
*
|
|
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.
|
|
595
635
|
*/
|
|
596
|
-
const useServerEffect = (requestId, handler, hydrate = true) => {
|
|
597
|
-
// If we're server-side or hydrating, we'll have a cached entry to use.
|
|
598
|
-
// So we get that and use it to initialize our state.
|
|
599
|
-
// This works in both hydration and SSR because the very first call to
|
|
600
|
-
// this will have cached data in those cases as it will be present on the
|
|
601
|
-
// initial render - and subsequent renders on the client it will be null.
|
|
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
|
|
603
|
-
// already have a result, as given by the cachedData (which is also the
|
|
604
|
-
// initial value for the result state).
|
|
605
|
-
|
|
606
|
-
const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* TrackerContext */ "b"]);
|
|
607
636
|
|
|
608
|
-
|
|
609
|
-
|
|
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);
|
|
610
641
|
}
|
|
611
642
|
|
|
612
|
-
|
|
613
|
-
|
|
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.
|
|
614
647
|
|
|
615
|
-
/***/ }),
|
|
616
|
-
/* 9 */
|
|
617
|
-
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
618
648
|
|
|
619
|
-
"
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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]
|
|
624
654
|
|
|
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.
|
|
657
|
+
|
|
658
|
+
if (currentValue == null && initialValue !== undefined) {
|
|
659
|
+
// Get the initial value.
|
|
660
|
+
const value = typeof initialValue === "function" ? initialValue() : initialValue;
|
|
661
|
+
|
|
662
|
+
if (value != null) {
|
|
663
|
+
// Update the cache.
|
|
664
|
+
cacheValue(value); // Make sure we return this value as our current value.
|
|
665
|
+
|
|
666
|
+
currentValue = value;
|
|
667
|
+
}
|
|
668
|
+
} // Now we have everything, let's return it.
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
return [currentValue, cacheValue];
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
/***/ }),
|
|
675
|
+
/* 9 */
|
|
676
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
677
|
+
|
|
678
|
+
"use strict";
|
|
679
|
+
/* 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);
|
|
625
681
|
|
|
626
682
|
|
|
627
683
|
/**
|
|
628
|
-
*
|
|
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.
|
|
684
|
+
* Describe an in-memory cache.
|
|
638
685
|
*/
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
686
|
+
class ScopedInMemoryCache {
|
|
687
|
+
constructor(initialCache = {}) {
|
|
688
|
+
this._cache = initialCache;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Indicate if this cache is being used or not.
|
|
692
|
+
*
|
|
693
|
+
* When the cache has entries, returns `true`; otherwise, returns `false`.
|
|
694
|
+
*/
|
|
646
695
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
696
|
+
|
|
697
|
+
get inUse() {
|
|
698
|
+
return Object.keys(this._cache).length > 0;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Set a value in the cache.
|
|
702
|
+
*/
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
set(scope, id, value) {
|
|
706
|
+
var _this$_cache$scope;
|
|
707
|
+
|
|
708
|
+
if (!id || typeof id !== "string") {
|
|
709
|
+
throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("id must be non-empty string", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].InvalidInput);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (!scope || typeof scope !== "string") {
|
|
713
|
+
throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("scope must be non-empty string", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].InvalidInput);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (typeof value === "function") {
|
|
717
|
+
throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("value must be a non-function value", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].InvalidInput);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
this._cache[scope] = (_this$_cache$scope = this._cache[scope]) != null ? _this$_cache$scope : {};
|
|
721
|
+
this._cache[scope][id] = value;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Retrieve a value from the cache.
|
|
725
|
+
*/
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
get(scope, id) {
|
|
729
|
+
var _this$_cache$scope$id, _this$_cache$scope2;
|
|
730
|
+
|
|
731
|
+
return (_this$_cache$scope$id = (_this$_cache$scope2 = this._cache[scope]) == null ? void 0 : _this$_cache$scope2[id]) != null ? _this$_cache$scope$id : null;
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Purge an item from the cache.
|
|
735
|
+
*/
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
purge(scope, id) {
|
|
739
|
+
var _this$_cache$scope3;
|
|
740
|
+
|
|
741
|
+
if (!((_this$_cache$scope3 = this._cache[scope]) != null && _this$_cache$scope3[id])) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
delete this._cache[scope][id];
|
|
746
|
+
|
|
747
|
+
if (Object.keys(this._cache[scope]).length === 0) {
|
|
748
|
+
delete this._cache[scope];
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Purge a scope of items that match the given predicate.
|
|
753
|
+
*
|
|
754
|
+
* If the predicate is omitted, then all items in the scope are purged.
|
|
755
|
+
*/
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
purgeScope(scope, predicate) {
|
|
759
|
+
if (!this._cache[scope]) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (predicate == null) {
|
|
764
|
+
delete this._cache[scope];
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
for (const key of Object.keys(this._cache[scope])) {
|
|
769
|
+
if (predicate(key, this._cache[scope][key])) {
|
|
770
|
+
delete this._cache[scope][key];
|
|
653
771
|
}
|
|
772
|
+
}
|
|
654
773
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
774
|
+
if (Object.keys(this._cache[scope]).length === 0) {
|
|
775
|
+
delete this._cache[scope];
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Purge all items from the cache that match the given predicate.
|
|
780
|
+
*
|
|
781
|
+
* If the predicate is omitted, then all items in the cache are purged.
|
|
782
|
+
*/
|
|
660
783
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
};
|
|
784
|
+
|
|
785
|
+
purgeAll(predicate) {
|
|
786
|
+
if (predicate == null) {
|
|
787
|
+
this._cache = {};
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
for (const scope of Object.keys(this._cache)) {
|
|
792
|
+
this.purgeScope(scope, (id, value) => predicate(scope, id, value));
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
}
|
|
665
797
|
|
|
666
798
|
/***/ }),
|
|
667
799
|
/* 10 */
|
|
@@ -669,21 +801,17 @@ const useRequestInterception = (requestId, handler) => {
|
|
|
669
801
|
|
|
670
802
|
"use strict";
|
|
671
803
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
|
|
672
|
-
/* harmony import */ var
|
|
804
|
+
/* harmony import */ var _data_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
673
805
|
|
|
674
806
|
|
|
675
807
|
let _default;
|
|
808
|
+
/**
|
|
809
|
+
* This fulfills a request, making sure that in-flight requests are shared.
|
|
810
|
+
*/
|
|
676
811
|
|
|
677
|
-
class RequestFulfillment {
|
|
678
|
-
static get Default() {
|
|
679
|
-
if (!_default) {
|
|
680
|
-
_default = new RequestFulfillment();
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
return _default;
|
|
684
|
-
}
|
|
685
812
|
|
|
686
|
-
|
|
813
|
+
class RequestFulfillment {
|
|
814
|
+
constructor() {
|
|
687
815
|
this._requests = {};
|
|
688
816
|
|
|
689
817
|
this.fulfill = (id, {
|
|
@@ -703,62 +831,492 @@ class RequestFulfillment {
|
|
|
703
831
|
*/
|
|
704
832
|
|
|
705
833
|
|
|
706
|
-
const {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}
|
|
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
|
+
}
|
|
710
855
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
856
|
+
return {
|
|
857
|
+
status: "error",
|
|
858
|
+
error: actualError
|
|
859
|
+
};
|
|
860
|
+
}).finally(() => {
|
|
861
|
+
delete this._requests[id];
|
|
862
|
+
}); // Store the request in our cache.
|
|
714
863
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
return null;
|
|
718
|
-
}
|
|
719
|
-
/**
|
|
720
|
-
* Let's cache the data!
|
|
721
|
-
*
|
|
722
|
-
* NOTE: This only caches when we're server side.
|
|
723
|
-
*/
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
return cacheData(id, data, hydrate);
|
|
727
|
-
}).catch(error => {
|
|
728
|
-
delete this._requests[id];
|
|
729
|
-
/**
|
|
730
|
-
* Let's cache the error!
|
|
731
|
-
*
|
|
732
|
-
* NOTE: This only caches when we're server side.
|
|
733
|
-
*/
|
|
734
|
-
|
|
735
|
-
return cacheError(id, error, hydrate);
|
|
736
|
-
});
|
|
737
|
-
this._requests[id] = request;
|
|
738
|
-
return request;
|
|
739
|
-
} catch (e) {
|
|
740
|
-
/**
|
|
741
|
-
* In this case, we don't cache an inflight request, because there
|
|
742
|
-
* really isn't one.
|
|
743
|
-
*/
|
|
744
|
-
return Promise.resolve(cacheError(id, e, hydrate));
|
|
745
|
-
}
|
|
864
|
+
this._requests[id] = request;
|
|
865
|
+
return request;
|
|
746
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 */
|
|
881
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
882
|
+
|
|
883
|
+
"use strict";
|
|
884
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return WhenClientSide; });
|
|
885
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return useHydratableEffect; });
|
|
886
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
887
|
+
/* 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);
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Policies to define how a hydratable effect should behave client-side.
|
|
900
|
+
*/
|
|
901
|
+
const WhenClientSide = __webpack_require__(29).Mirrored(["DoNotHydrate", "ExecuteWhenNoResult", "ExecuteWhenNoSuccessResult", "AlwaysExecute"]);
|
|
902
|
+
const DefaultScope = "useHydratableEffect";
|
|
903
|
+
/**
|
|
904
|
+
* Hook to execute an async operation on server and client.
|
|
905
|
+
*
|
|
906
|
+
* This hook executes the given handler on the server and on the client,
|
|
907
|
+
* and, depending on the given options, can hydrate the server-side result.
|
|
908
|
+
*
|
|
909
|
+
* Results are cached on the client so they can be shared between equivalent
|
|
910
|
+
* invocations. Cache changes from one hook instance do not trigger renders
|
|
911
|
+
* in components that use the same requestID.
|
|
912
|
+
*/
|
|
913
|
+
|
|
914
|
+
const useHydratableEffect = (requestId, handler, options = {}) => {
|
|
915
|
+
const {
|
|
916
|
+
clientBehavior = WhenClientSide.ExecuteWhenNoSuccessResult,
|
|
917
|
+
skip = false,
|
|
918
|
+
retainResultOnChange = false,
|
|
919
|
+
onResultChanged,
|
|
920
|
+
scope = DefaultScope
|
|
921
|
+
} = options; // Now we instruct the server to perform the operation.
|
|
922
|
+
// When client-side, this will look up any response for hydration; it does
|
|
923
|
+
// not invoke the handler.
|
|
924
|
+
|
|
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);
|
|
929
|
+
const getDefaultCacheValue = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](() => {
|
|
930
|
+
// If we don't have a requestId, it's our first render, the one
|
|
931
|
+
// where we hydrated. So defer to our clientBehavior value.
|
|
932
|
+
switch (clientBehavior) {
|
|
933
|
+
case WhenClientSide.DoNotHydrate:
|
|
934
|
+
case WhenClientSide.AlwaysExecute:
|
|
935
|
+
// Either we weren't hydrating at all, or we don't care
|
|
936
|
+
// if we hydrated something or not, either way, we're
|
|
937
|
+
// doing a request.
|
|
938
|
+
return null;
|
|
939
|
+
|
|
940
|
+
case WhenClientSide.ExecuteWhenNoResult:
|
|
941
|
+
// We only execute if we didn't hydrate something.
|
|
942
|
+
// So, returning the hydration result as default for our
|
|
943
|
+
// cache, will then prevent the cached effect running.
|
|
944
|
+
return serverResult;
|
|
945
|
+
|
|
946
|
+
case WhenClientSide.ExecuteWhenNoSuccessResult:
|
|
947
|
+
// We only execute if we didn't hydrate a success result.
|
|
948
|
+
if ((serverResult == null ? void 0 : serverResult.status) === "success") {
|
|
949
|
+
// So, returning the hydration result as default for our
|
|
950
|
+
// cache, will then prevent the cached effect running.
|
|
951
|
+
return serverResult;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
return null;
|
|
955
|
+
} // There is no reason for this to change after the first render,
|
|
956
|
+
// you might think, but the function closes around serverResult and if
|
|
957
|
+
// the requestId changes, it still returns the hydrate result of the
|
|
958
|
+
// first render of the previous requestId. This then means that the
|
|
959
|
+
// hydrate result is still the same, and the effect is not re-executed
|
|
960
|
+
// because the cache gets incorrectly defaulted.
|
|
961
|
+
// However, we don't want to bother doing anything with this on
|
|
962
|
+
// client behavior changing since that truly is irrelevant.
|
|
963
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
964
|
+
|
|
965
|
+
}, [serverResult]); // Instead of using state, which would be local to just this hook instance,
|
|
966
|
+
// we use a shared in-memory cache.
|
|
967
|
+
|
|
968
|
+
Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_3__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
|
|
969
|
+
scope, // The scope of the cached items
|
|
970
|
+
getDefaultCacheValue); // When we're client-side, we ultimately want the result from this call.
|
|
971
|
+
|
|
972
|
+
const clientResult = Object(_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_4__[/* useCachedEffect */ "a"])(requestId, handler, {
|
|
973
|
+
skip,
|
|
974
|
+
onResultChanged,
|
|
975
|
+
retainResultOnChange,
|
|
976
|
+
scope
|
|
977
|
+
}); // OK, now which result do we return.
|
|
978
|
+
// Well, we return the serverResult on our very first call and then
|
|
979
|
+
// the clientResult thereafter. The great thing is that after the very
|
|
980
|
+
// first call, the serverResult is going to be `null` anyway.
|
|
981
|
+
|
|
982
|
+
return serverResult != null ? serverResult : clientResult;
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
/***/ }),
|
|
986
|
+
/* 12 */
|
|
987
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
988
|
+
|
|
989
|
+
"use strict";
|
|
990
|
+
/* 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);
|
|
992
|
+
/* 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);
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Describe a serializable in-memory cache.
|
|
1001
|
+
*/
|
|
1002
|
+
class SerializableInMemoryCache extends _scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* ScopedInMemoryCache */ "a"] {
|
|
1003
|
+
constructor(initialCache = {}) {
|
|
1004
|
+
try {
|
|
1005
|
+
super(Object(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["clone"])(initialCache));
|
|
1006
|
+
} catch (e) {
|
|
1007
|
+
throw new _data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataError */ "a"](`An error occurred trying to initialize from a response cache snapshot: ${e}`, _data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataErrors */ "b"].InvalidInput);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Set a value in the cache.
|
|
1012
|
+
*/
|
|
747
1013
|
|
|
748
|
-
|
|
1014
|
+
|
|
1015
|
+
set(scope, id, value) {
|
|
1016
|
+
super.set(scope, id, Object.freeze(Object(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["clone"])(value)));
|
|
749
1017
|
}
|
|
750
1018
|
/**
|
|
751
|
-
*
|
|
752
|
-
*
|
|
753
|
-
* This will return an inflight request if one exists, otherwise it will
|
|
754
|
-
* make a new request. Inflight requests are deleted once they resolve.
|
|
1019
|
+
* Clone the cache.
|
|
755
1020
|
*/
|
|
756
1021
|
|
|
757
1022
|
|
|
1023
|
+
clone() {
|
|
1024
|
+
try {
|
|
1025
|
+
return Object(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["clone"])(this._cache);
|
|
1026
|
+
} catch (e) {
|
|
1027
|
+
throw new _data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataError */ "a"]("An error occurred while trying to clone the cache", _data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataErrors */ "b"].Internal, {
|
|
1028
|
+
cause: e
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
758
1033
|
}
|
|
759
1034
|
|
|
760
1035
|
/***/ }),
|
|
761
|
-
/*
|
|
1036
|
+
/* 13 */
|
|
1037
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1038
|
+
|
|
1039
|
+
"use strict";
|
|
1040
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1041
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* InterceptContext defines a map from request ID to interception methods.
|
|
1046
|
+
*
|
|
1047
|
+
* INTERNAL USE ONLY
|
|
1048
|
+
*/
|
|
1049
|
+
const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"]([]);
|
|
1050
|
+
/* harmony default export */ __webpack_exports__["a"] = (InterceptContext);
|
|
1051
|
+
|
|
1052
|
+
/***/ }),
|
|
1053
|
+
/* 14 */
|
|
1054
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1055
|
+
|
|
1056
|
+
"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
|
+
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Hook to perform an asynchronous action during server-side rendering.
|
|
1075
|
+
*
|
|
1076
|
+
* This hook registers an asynchronous action to be performed during
|
|
1077
|
+
* server-side rendering. The action is performed only once, and the result
|
|
1078
|
+
* is cached against the given identifier so that subsequent calls return that
|
|
1079
|
+
* cached result allowing components to render more of the component.
|
|
1080
|
+
*
|
|
1081
|
+
* This hook requires the Wonder Blocks Data functionality for resolving
|
|
1082
|
+
* pending requests, as well as support for the hydration cache to be
|
|
1083
|
+
* embedded into a page so that the result can by hydrated (if that is a
|
|
1084
|
+
* requirement).
|
|
1085
|
+
*
|
|
1086
|
+
* The asynchronous action is never invoked on the client-side.
|
|
1087
|
+
*/
|
|
1088
|
+
const useServerEffect = (requestId, handler, hydrate = true) => {
|
|
1089
|
+
// Plug in to the request interception framework for code that wants
|
|
1090
|
+
// to use that.
|
|
1091
|
+
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
|
+
// So we get that and use it to initialize our state.
|
|
1093
|
+
// This works in both hydration and SSR because the very first call to
|
|
1094
|
+
// this will have cached data in those cases as it will be present on the
|
|
1095
|
+
// initial render - and subsequent renders on the client it will be null.
|
|
1096
|
+
|
|
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).
|
|
1100
|
+
|
|
1101
|
+
const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* TrackerContext */ "b"]);
|
|
1102
|
+
|
|
1103
|
+
if (cachedResult == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
1104
|
+
maybeTrack == null ? void 0 : maybeTrack(requestId, interceptedHandler, hydrate);
|
|
1105
|
+
} // A null result means there was no result to hydrate.
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
return cachedResult == null ? null : Object(_util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_4__[/* resultFromCachedResponse */ "a"])(cachedResult);
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
/***/ }),
|
|
1112
|
+
/* 15 */
|
|
1113
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1114
|
+
|
|
1115
|
+
"use strict";
|
|
1116
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useCachedEffect; });
|
|
1117
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1118
|
+
/* 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);
|
|
1120
|
+
/* 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);
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
const DefaultScope = "useCachedEffect";
|
|
1132
|
+
/**
|
|
1133
|
+
* Hook to execute and cache an async operation on the client.
|
|
1134
|
+
*
|
|
1135
|
+
* This hook executes the given handler on the client if there is no
|
|
1136
|
+
* cached result to use.
|
|
1137
|
+
*
|
|
1138
|
+
* Results are cached so they can be shared between equivalent invocations.
|
|
1139
|
+
* In-flight requests are also shared, so that concurrent calls will
|
|
1140
|
+
* behave as one might exect. Cache updates invoked by one hook instance
|
|
1141
|
+
* do not trigger renders in components that use the same requestID; however,
|
|
1142
|
+
* that should not matter since concurrent requests will share the same
|
|
1143
|
+
* in-flight request, and subsequent renders will grab from the cache.
|
|
1144
|
+
*
|
|
1145
|
+
* Once the request has been tried once and a non-loading response has been
|
|
1146
|
+
* cached, the request will not executed made again.
|
|
1147
|
+
*/
|
|
1148
|
+
|
|
1149
|
+
const useCachedEffect = (requestId, handler, options = {}) => {
|
|
1150
|
+
const {
|
|
1151
|
+
skip: hardSkip = false,
|
|
1152
|
+
retainResultOnChange = false,
|
|
1153
|
+
onResultChanged,
|
|
1154
|
+
scope = DefaultScope
|
|
1155
|
+
} = options; // Plug in to the request interception framework for code that wants
|
|
1156
|
+
// to use that.
|
|
1157
|
+
|
|
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,
|
|
1159
|
+
// we use a shared in-memory cache.
|
|
1160
|
+
|
|
1161
|
+
const [mostRecentResult, setMostRecentResult] = Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_4__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
|
|
1162
|
+
scope // The scope of the cached items
|
|
1163
|
+
// No default value. We don't want the loading status there; to ensure
|
|
1164
|
+
// that all calls when the request is in-flight will update once that
|
|
1165
|
+
// 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;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
return false;
|
|
1204
|
+
}, [requestId, previousRequestId, mostRecentResult]); // So now we make sure the client-side request happens per our various
|
|
1205
|
+
// options.
|
|
1206
|
+
|
|
1207
|
+
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) {
|
|
1216
|
+
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
|
+
}
|
|
1237
|
+
|
|
1238
|
+
setCacheAndNotify(result);
|
|
1239
|
+
return; // Shut up eslint always-return rule.
|
|
1240
|
+
});
|
|
1241
|
+
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
|
|
1256
|
+
// "retainResultOnChange" option.
|
|
1257
|
+
|
|
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
|
|
1260
|
+
// we cache.
|
|
1261
|
+
|
|
1262
|
+
const result = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => mostRecentResult != null ? mostRecentResult : loadingResult, [mostRecentResult, loadingResult]);
|
|
1263
|
+
lastResultAgnosticOfIdRef.current = result;
|
|
1264
|
+
return result;
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
/***/ }),
|
|
1268
|
+
/* 16 */
|
|
1269
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1270
|
+
|
|
1271
|
+
"use strict";
|
|
1272
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useRequestInterception; });
|
|
1273
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1274
|
+
/* 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);
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* Allow request handling to be intercepted.
|
|
1281
|
+
*
|
|
1282
|
+
* Hook to take a uniquely identified request handler and return a
|
|
1283
|
+
* method that will support request interception from the InterceptRequest
|
|
1284
|
+
* component.
|
|
1285
|
+
*
|
|
1286
|
+
* If you want request interception to be supported with `useServerEffect` or
|
|
1287
|
+
* any client-side effect that uses the handler, call this first to generate
|
|
1288
|
+
* an intercepted handler, and then invoke `useServerEffect` (or other things)
|
|
1289
|
+
* with that intercepted handler.
|
|
1290
|
+
*/
|
|
1291
|
+
const useRequestInterception = (requestId, handler) => {
|
|
1292
|
+
// Get the interceptors that have been registered.
|
|
1293
|
+
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
|
|
1294
|
+
// request is intercepted before ultimately calling the original handler
|
|
1295
|
+
// if nothing intercepted it.
|
|
1296
|
+
// We memoize this so that it only changes if something related to it
|
|
1297
|
+
// changes.
|
|
1298
|
+
|
|
1299
|
+
const interceptedHandler = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](() => {
|
|
1300
|
+
// Call the interceptors from closest to furthest.
|
|
1301
|
+
// If one returns a non-null result, then we keep that.
|
|
1302
|
+
const interceptResponse = interceptors.reduceRight((prev, interceptor) => {
|
|
1303
|
+
if (prev != null) {
|
|
1304
|
+
return prev;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
return interceptor(requestId);
|
|
1308
|
+
}, null); // If nothing intercepted this request, invoke the original handler.
|
|
1309
|
+
// NOTE: We can't guarantee all interceptors return the same type
|
|
1310
|
+
// as our handler, so how can flow know? Let's just suppress that.
|
|
1311
|
+
// $FlowFixMe[incompatible-return]
|
|
1312
|
+
|
|
1313
|
+
return interceptResponse != null ? interceptResponse : handler();
|
|
1314
|
+
}, [handler, interceptors, requestId]);
|
|
1315
|
+
return interceptedHandler;
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
/***/ }),
|
|
1319
|
+
/* 17 */
|
|
762
1320
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
763
1321
|
|
|
764
1322
|
"use strict";
|
|
@@ -769,107 +1327,50 @@ class RequestFulfillment {
|
|
|
769
1327
|
const GqlRouterContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
|
|
770
1328
|
|
|
771
1329
|
/***/ }),
|
|
772
|
-
/*
|
|
1330
|
+
/* 18 */
|
|
773
1331
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
774
1332
|
|
|
775
1333
|
"use strict";
|
|
776
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return
|
|
777
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return useSharedCache; });
|
|
778
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
779
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
780
|
-
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
|
|
781
|
-
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
782
|
-
/* harmony import */ var _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
/**
|
|
788
|
-
* This is the cache.
|
|
789
|
-
* It's incredibly complex.
|
|
790
|
-
* Very in-memory. So cache. Such complex. Wow.
|
|
791
|
-
*/
|
|
792
|
-
const cache = new _util_scoped_in_memory_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* ScopedInMemoryCache */ "a"]();
|
|
793
|
-
/**
|
|
794
|
-
* Clear the in-memory cache or a single scope within it.
|
|
795
|
-
*/
|
|
796
|
-
|
|
797
|
-
const clearSharedCache = (scope = "") => {
|
|
798
|
-
// If we have a valid scope (empty string is falsy), then clear that scope.
|
|
799
|
-
if (scope && typeof scope === "string") {
|
|
800
|
-
cache.purgeScope(scope);
|
|
801
|
-
} else {
|
|
802
|
-
// Just reset the object. This should be sufficient.
|
|
803
|
-
cache.purgeAll();
|
|
804
|
-
}
|
|
805
|
-
};
|
|
1334
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return mergeGqlContext; });
|
|
806
1335
|
/**
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
* @returns {[?ReadOnlyCacheValue, CacheValueFn]}
|
|
810
|
-
* Returns an array containing the current cache entry (or undefined), a
|
|
811
|
-
* function to set the cache entry (passing null or undefined to this function
|
|
812
|
-
* will delete the entry).
|
|
813
|
-
*
|
|
814
|
-
* To clear a single scope within the cache or the entire cache,
|
|
815
|
-
* the `clearScopedCache` export is available.
|
|
1336
|
+
* Construct a complete GqlContext from current defaults and a partial context.
|
|
816
1337
|
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
* but the likelihood here is that things won't be changing in this cache in a
|
|
820
|
-
* way where we would need that. If we do (and likely only in specific
|
|
821
|
-
* circumstances), we should consider adding a simple boolean useState that can
|
|
822
|
-
* be toggled to cause a rerender whenever the referenced cached data changes
|
|
823
|
-
* so that callers can re-render on cache changes. However, we should make
|
|
824
|
-
* sure this toggling is optional - or we could use a callback argument, to
|
|
825
|
-
* achieve this on an as-needed basis.
|
|
1338
|
+
* Values in the partial context that are `undefined` will be ignored.
|
|
1339
|
+
* Values in the partial context that are `null` will be deleted.
|
|
826
1340
|
*/
|
|
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
|
+
}
|
|
827
1357
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
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);
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
if (!scope || typeof scope !== "string") {
|
|
835
|
-
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);
|
|
836
|
-
} // Memoize our APIs.
|
|
837
|
-
// This one allows callers to set or replace the cached value.
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
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
|
|
841
|
-
// since our last run through. Also, our cache does not know what type it
|
|
842
|
-
// stores, so we have to cast it to the type we're exporting. This is a
|
|
843
|
-
// dev time courtesy, rather than a runtime thing.
|
|
844
|
-
// $FlowIgnore[incompatible-type]
|
|
845
|
-
|
|
846
|
-
let currentValue = cache.get(scope, id); // If we have an initial value, we need to add it to the cache
|
|
847
|
-
// and use it as our current value.
|
|
848
|
-
|
|
849
|
-
if (currentValue == null && initialValue !== undefined) {
|
|
850
|
-
// Get the initial value.
|
|
851
|
-
const value = typeof initialValue === "function" ? initialValue() : initialValue; // Update the cache.
|
|
852
|
-
|
|
853
|
-
cacheValue(value); // Make sure we return this value as our current value.
|
|
854
|
-
|
|
855
|
-
currentValue = value;
|
|
856
|
-
} // Now we have everything, let's return it.
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
return [currentValue, cacheValue];
|
|
1358
|
+
return acc;
|
|
1359
|
+
}, { ...defaultContext
|
|
1360
|
+
});
|
|
860
1361
|
};
|
|
861
1362
|
|
|
862
1363
|
/***/ }),
|
|
863
|
-
/*
|
|
1364
|
+
/* 19 */
|
|
864
1365
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
865
1366
|
|
|
866
1367
|
"use strict";
|
|
867
1368
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
|
|
868
1369
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
869
1370
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
870
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
1371
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
|
871
1372
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
872
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
1373
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
|
|
873
1374
|
|
|
874
1375
|
|
|
875
1376
|
|
|
@@ -891,22 +1392,13 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
891
1392
|
}
|
|
892
1393
|
|
|
893
1394
|
/***/ }),
|
|
894
|
-
/*
|
|
1395
|
+
/* 20 */
|
|
895
1396
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
896
1397
|
|
|
897
1398
|
"use strict";
|
|
898
1399
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
899
1400
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
900
|
-
/* harmony import */ var
|
|
901
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
902
|
-
/* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
|
|
903
|
-
/* harmony import */ var _hooks_use_server_effect_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
|
|
904
|
-
/* harmony import */ var _hooks_use_request_interception_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
|
|
905
|
-
/* harmony import */ var _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(18);
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1401
|
+
/* harmony import */ var _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11);
|
|
910
1402
|
|
|
911
1403
|
|
|
912
1404
|
|
|
@@ -919,99 +1411,26 @@ const Data = ({
|
|
|
919
1411
|
requestId,
|
|
920
1412
|
handler,
|
|
921
1413
|
children,
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
alwaysRequestOnHydration
|
|
1414
|
+
retainResultOnChange = false,
|
|
1415
|
+
clientBehavior = _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_1__[/* WhenClientSide */ "a"].ExecuteWhenNoSuccessResult
|
|
925
1416
|
}) => {
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
react__WEBPACK_IMPORTED_MODULE_0__["useEffect"](() => {
|
|
933
|
-
// This is here until I can do a better documentation example for
|
|
934
|
-
// the TrackData docs.
|
|
935
|
-
// istanbul ignore next
|
|
936
|
-
if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__["Server"].isServerSide()) {
|
|
937
|
-
return;
|
|
938
|
-
} // We don't bother with this if we have hydration data and we're not
|
|
939
|
-
// forcing a request on hydration.
|
|
940
|
-
// We don't care if these things change after the first render,
|
|
941
|
-
// so we don't want them in the inputs array.
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if (!alwaysRequestOnHydration && (hydrateResult == null ? void 0 : hydrateResult.data) != null) {
|
|
945
|
-
return;
|
|
946
|
-
} // If we're not hydrating a result and we're not going to render
|
|
947
|
-
// with old data until we're loaded, we want to make sure we set our
|
|
948
|
-
// result to null so that we're in the loading state.
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
if (!showOldDataWhileLoading) {
|
|
952
|
-
// Mark ourselves as loading.
|
|
953
|
-
setResult(null);
|
|
954
|
-
} // We aren't server-side, so let's make the request.
|
|
955
|
-
// We don't need to use our built-in request fulfillment here if we
|
|
956
|
-
// don't want, but it does mean we'll share inflight requests for the
|
|
957
|
-
// same ID and the result will be in the same format as the
|
|
958
|
-
// hydrated value.
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
let cancel = false;
|
|
962
|
-
_util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"].Default.fulfill(requestId, {
|
|
963
|
-
handler: interceptedHandler
|
|
964
|
-
}).then(result => {
|
|
965
|
-
if (cancel) {
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
setResult(result);
|
|
970
|
-
return;
|
|
971
|
-
}).catch(e => {
|
|
972
|
-
if (cancel) {
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* We should never get here as errors in fulfillment are part
|
|
977
|
-
* of the `then`, but if we do.
|
|
978
|
-
*/
|
|
979
|
-
// eslint-disable-next-line no-console
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
console.error(`Unexpected error occurred during data fulfillment: ${e}`);
|
|
983
|
-
setResult({
|
|
984
|
-
error: typeof e === "string" ? e : e.message
|
|
985
|
-
});
|
|
986
|
-
return;
|
|
987
|
-
});
|
|
988
|
-
return () => {
|
|
989
|
-
cancel = true;
|
|
990
|
-
}; // If the handler changes, we don't care. The ID is what indicates
|
|
991
|
-
// the request that should be made and folks shouldn't be changing the
|
|
992
|
-
// handler without changing the ID as well.
|
|
993
|
-
// In addition, we don't want to include hydrateResult nor
|
|
994
|
-
// alwaysRequestOnHydration as them changinng after the first pass
|
|
995
|
-
// is irrelevant.
|
|
996
|
-
// Finally, we don't want to include showOldDataWhileLoading as that
|
|
997
|
-
// changing on its own is also not relevant. It only matters if the
|
|
998
|
-
// request itself changes. All of which is to say that we only
|
|
999
|
-
// run this effect for the ID changing.
|
|
1000
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1001
|
-
}, [requestId]);
|
|
1002
|
-
return children(Object(_util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_5__[/* resultFromCachedResponse */ "a"])(currentResult));
|
|
1417
|
+
const result = Object(_hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_1__[/* useHydratableEffect */ "b"])(requestId, handler, {
|
|
1418
|
+
retainResultOnChange,
|
|
1419
|
+
clientBehavior
|
|
1420
|
+
});
|
|
1421
|
+
return children(result);
|
|
1003
1422
|
};
|
|
1004
1423
|
|
|
1005
1424
|
/* harmony default export */ __webpack_exports__["a"] = (Data);
|
|
1006
1425
|
|
|
1007
1426
|
/***/ }),
|
|
1008
|
-
/*
|
|
1427
|
+
/* 21 */
|
|
1009
1428
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1010
1429
|
|
|
1011
1430
|
"use strict";
|
|
1012
1431
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1013
1432
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
1014
|
-
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
1433
|
+
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13);
|
|
1015
1434
|
|
|
1016
1435
|
|
|
1017
1436
|
|
|
@@ -1046,14 +1465,14 @@ const InterceptRequests = ({
|
|
|
1046
1465
|
/* harmony default export */ __webpack_exports__["a"] = (InterceptRequests);
|
|
1047
1466
|
|
|
1048
1467
|
/***/ }),
|
|
1049
|
-
/*
|
|
1468
|
+
/* 22 */
|
|
1050
1469
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1051
1470
|
|
|
1052
1471
|
"use strict";
|
|
1053
1472
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouter; });
|
|
1054
1473
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1055
1474
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
1056
|
-
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
1475
|
+
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(17);
|
|
1057
1476
|
|
|
1058
1477
|
|
|
1059
1478
|
|
|
@@ -1090,16 +1509,16 @@ const GqlRouter = ({
|
|
|
1090
1509
|
};
|
|
1091
1510
|
|
|
1092
1511
|
/***/ }),
|
|
1093
|
-
/*
|
|
1512
|
+
/* 23 */
|
|
1094
1513
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1095
1514
|
|
|
1096
1515
|
"use strict";
|
|
1097
1516
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGql; });
|
|
1098
1517
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1099
1518
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
1100
|
-
/* harmony import */ var
|
|
1101
|
-
/* harmony import */ var
|
|
1102
|
-
/* harmony import */ var
|
|
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);
|
|
1103
1522
|
|
|
1104
1523
|
|
|
1105
1524
|
|
|
@@ -1115,74 +1534,69 @@ const GqlRouter = ({
|
|
|
1115
1534
|
* Values in the partial context given to the returned fetch function will
|
|
1116
1535
|
* only be included if they have a value other than undefined.
|
|
1117
1536
|
*/
|
|
1118
|
-
const useGql = () => {
|
|
1537
|
+
const useGql = (context = {}) => {
|
|
1119
1538
|
// This hook only works if the `GqlRouter` has been used to setup context.
|
|
1120
|
-
const gqlRouterContext = Object(
|
|
1121
|
-
|
|
1122
|
-
if (gqlRouterContext == null) {
|
|
1123
|
-
throw new _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlError */ "a"]("No GqlRouter", _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlErrors */ "b"].Internal);
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
const {
|
|
1127
|
-
fetch,
|
|
1128
|
-
defaultContext
|
|
1129
|
-
} = gqlRouterContext; // Let's memoize the gqlFetch function we create based off our context.
|
|
1539
|
+
const gqlRouterContext = Object(_use_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__[/* useGqlRouterContext */ "a"])(context); // Let's memoize the gqlFetch function we create based off our context.
|
|
1130
1540
|
// That way, even if the context happens to change, if its values don't
|
|
1131
1541
|
// we give the same function instance back to our callers instead of
|
|
1132
1542
|
// making a new one. That then means they can safely use the return value
|
|
1133
1543
|
// in hooks deps without fear of it triggering extra renders.
|
|
1134
1544
|
|
|
1135
|
-
const gqlFetch = Object(react__WEBPACK_IMPORTED_MODULE_0__["
|
|
1545
|
+
const gqlFetch = Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])((operation, options = Object.freeze({})) => {
|
|
1546
|
+
const {
|
|
1547
|
+
fetch,
|
|
1548
|
+
defaultContext
|
|
1549
|
+
} = gqlRouterContext;
|
|
1136
1550
|
const {
|
|
1137
1551
|
variables,
|
|
1138
1552
|
context = {}
|
|
1139
|
-
} = options;
|
|
1140
|
-
|
|
1141
|
-
// spreading would overwrite default context values with
|
|
1142
|
-
// undefined if the partial context includes a value explicitly
|
|
1143
|
-
// set to undefined. Instead, we use a map/reduce of keys.
|
|
1144
|
-
|
|
1145
|
-
const mergedContext = Object.keys(context).reduce((acc, key) => {
|
|
1146
|
-
if (context[key] !== undefined) {
|
|
1147
|
-
acc[key] = context[key];
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
return acc;
|
|
1151
|
-
}, { ...defaultContext
|
|
1152
|
-
}); // Invoke the fetch and extract the data.
|
|
1153
|
-
|
|
1154
|
-
return fetch(operation, variables, mergedContext).then(_util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__[/* getGqlDataFromResponse */ "a"], error => {
|
|
1155
|
-
// Return null if the request was aborted.
|
|
1156
|
-
// The only way to detect this reliably, it seems, is to
|
|
1157
|
-
// check the error name and see if it's "AbortError" (this
|
|
1158
|
-
// is also what Apollo does).
|
|
1159
|
-
// Even then, it's reliant on the fetch supporting aborts.
|
|
1160
|
-
if (error.name === "AbortError") {
|
|
1161
|
-
return null;
|
|
1162
|
-
} // Need to make sure we pass other errors along.
|
|
1163
|
-
|
|
1553
|
+
} = options;
|
|
1554
|
+
const finalContext = Object(_util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__[/* mergeGqlContext */ "a"])(defaultContext, context); // Invoke the fetch and extract the data.
|
|
1164
1555
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
}, [fetch, defaultContext]);
|
|
1556
|
+
return fetch(operation, variables, finalContext).then(_util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_3__[/* getGqlDataFromResponse */ "a"]);
|
|
1557
|
+
}, [gqlRouterContext]);
|
|
1168
1558
|
return gqlFetch;
|
|
1169
1559
|
};
|
|
1170
1560
|
|
|
1171
1561
|
/***/ }),
|
|
1172
|
-
/*
|
|
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 */
|
|
1173
1584
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1174
1585
|
|
|
1175
1586
|
"use strict";
|
|
1176
1587
|
/* 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);
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
|
|
1177
1593
|
/**
|
|
1178
1594
|
* Turns a cache entry into a stateful result.
|
|
1179
1595
|
*/
|
|
1180
1596
|
const resultFromCachedResponse = cacheEntry => {
|
|
1181
|
-
// No cache entry means
|
|
1597
|
+
// No cache entry means no result to be hydrated.
|
|
1182
1598
|
if (cacheEntry == null) {
|
|
1183
|
-
return
|
|
1184
|
-
status: "loading"
|
|
1185
|
-
};
|
|
1599
|
+
return null;
|
|
1186
1600
|
}
|
|
1187
1601
|
|
|
1188
1602
|
const {
|
|
@@ -1191,31 +1605,81 @@ const resultFromCachedResponse = cacheEntry => {
|
|
|
1191
1605
|
} = cacheEntry;
|
|
1192
1606
|
|
|
1193
1607
|
if (error != null) {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1608
|
+
// Let's hydrate the error. We don't persist everything about the
|
|
1609
|
+
// original error on the server, hence why we only superficially
|
|
1610
|
+
// hydrate it to a GqlHydratedError.
|
|
1611
|
+
return _status_js__WEBPACK_IMPORTED_MODULE_0__[/* Status */ "a"].error(new _data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataError */ "a"](error, _data_error_js__WEBPACK_IMPORTED_MODULE_1__[/* DataErrors */ "b"].Hydrated));
|
|
1198
1612
|
}
|
|
1199
1613
|
|
|
1200
1614
|
if (data != null) {
|
|
1201
|
-
return
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1615
|
+
return _status_js__WEBPACK_IMPORTED_MODULE_0__[/* Status */ "a"].success(data);
|
|
1616
|
+
} // We shouldn't get here since we don't actually cache null data.
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
return _status_js__WEBPACK_IMPORTED_MODULE_0__[/* Status */ "a"].aborted();
|
|
1620
|
+
};
|
|
1621
|
+
|
|
1622
|
+
/***/ }),
|
|
1623
|
+
/* 26 */
|
|
1624
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1625
|
+
|
|
1626
|
+
"use strict";
|
|
1627
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGqlRouterContext; });
|
|
1628
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1629
|
+
/* 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);
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* Construct a GqlRouterContext from the current one and partial context.
|
|
1640
|
+
*/
|
|
1641
|
+
const useGqlRouterContext = (contextOverrides = {}) => {
|
|
1642
|
+
// This hook only works if the `GqlRouter` has been used to setup context.
|
|
1643
|
+
const gqlRouterContext = Object(react__WEBPACK_IMPORTED_MODULE_0__["useContext"])(_util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__[/* GqlRouterContext */ "a"]);
|
|
1644
|
+
|
|
1645
|
+
if (gqlRouterContext == null) {
|
|
1646
|
+
throw new _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlError */ "a"]("No GqlRouter", _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlErrors */ "b"].Internal);
|
|
1205
1647
|
}
|
|
1206
1648
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1649
|
+
const {
|
|
1650
|
+
fetch,
|
|
1651
|
+
defaultContext
|
|
1652
|
+
} = gqlRouterContext;
|
|
1653
|
+
const contextRef = Object(react__WEBPACK_IMPORTED_MODULE_0__["useRef"])(defaultContext);
|
|
1654
|
+
const mergedContext = Object(_util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__[/* mergeGqlContext */ "a"])(defaultContext, contextOverrides); // Now, we can see if this represents a new context and if so,
|
|
1655
|
+
// update our ref and return the merged value.
|
|
1656
|
+
|
|
1657
|
+
const refKeys = Object.keys(contextRef.current);
|
|
1658
|
+
const mergedKeys = Object.keys(mergedContext);
|
|
1659
|
+
const shouldWeUpdateRef = refKeys.length !== mergedKeys.length || mergedKeys.every(key => contextRef.current[key] !== mergedContext[key]);
|
|
1660
|
+
|
|
1661
|
+
if (shouldWeUpdateRef) {
|
|
1662
|
+
contextRef.current = mergedContext;
|
|
1663
|
+
} // OK, now we're up-to-date, let's memoize our final result.
|
|
1664
|
+
|
|
1665
|
+
|
|
1666
|
+
const finalContext = contextRef.current;
|
|
1667
|
+
const finalRouterContext = Object(react__WEBPACK_IMPORTED_MODULE_0__["useMemo"])(() => ({
|
|
1668
|
+
fetch,
|
|
1669
|
+
defaultContext: finalContext
|
|
1670
|
+
}), [fetch, finalContext]);
|
|
1671
|
+
return finalRouterContext;
|
|
1210
1672
|
};
|
|
1211
1673
|
|
|
1212
1674
|
/***/ }),
|
|
1213
|
-
/*
|
|
1675
|
+
/* 27 */
|
|
1214
1676
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1215
1677
|
|
|
1216
1678
|
"use strict";
|
|
1217
1679
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlDataFromResponse; });
|
|
1218
|
-
/* harmony import */ var
|
|
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);
|
|
1682
|
+
|
|
1219
1683
|
|
|
1220
1684
|
/**
|
|
1221
1685
|
* Validate a GQL operation response and extract the data.
|
|
@@ -1230,7 +1694,7 @@ const getGqlDataFromResponse = async response => {
|
|
|
1230
1694
|
try {
|
|
1231
1695
|
result = JSON.parse(bodyText);
|
|
1232
1696
|
} catch (e) {
|
|
1233
|
-
throw new
|
|
1697
|
+
throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("Failed to parse response", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].Parse, {
|
|
1234
1698
|
metadata: {
|
|
1235
1699
|
statusCode: response.status,
|
|
1236
1700
|
bodyText
|
|
@@ -1241,7 +1705,7 @@ const getGqlDataFromResponse = async response => {
|
|
|
1241
1705
|
|
|
1242
1706
|
|
|
1243
1707
|
if (response.status >= 300) {
|
|
1244
|
-
throw new
|
|
1708
|
+
throw new _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataError */ "a"]("Response unsuccessful", _data_error_js__WEBPACK_IMPORTED_MODULE_0__[/* DataErrors */ "b"].Network, {
|
|
1245
1709
|
metadata: {
|
|
1246
1710
|
statusCode: response.status,
|
|
1247
1711
|
result
|
|
@@ -1255,7 +1719,7 @@ const getGqlDataFromResponse = async response => {
|
|
|
1255
1719
|
!Object.prototype.hasOwnProperty.call(result, "data") && // Flow shouldn't be warning about this.
|
|
1256
1720
|
// $FlowIgnore[method-unbinding]
|
|
1257
1721
|
!Object.prototype.hasOwnProperty.call(result, "errors")) {
|
|
1258
|
-
throw new
|
|
1722
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlError */ "a"]("Server response missing", _gql_error_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlErrors */ "b"].BadResponse, {
|
|
1259
1723
|
metadata: {
|
|
1260
1724
|
statusCode: response.status,
|
|
1261
1725
|
result
|
|
@@ -1265,7 +1729,7 @@ const getGqlDataFromResponse = async response => {
|
|
|
1265
1729
|
|
|
1266
1730
|
|
|
1267
1731
|
if (result.errors != null && Array.isArray(result.errors) && result.errors.length > 0) {
|
|
1268
|
-
throw new
|
|
1732
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlError */ "a"]("GraphQL errors", _gql_error_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlErrors */ "b"].ErrorResult, {
|
|
1269
1733
|
metadata: {
|
|
1270
1734
|
statusCode: response.status,
|
|
1271
1735
|
result
|
|
@@ -1278,7 +1742,7 @@ const getGqlDataFromResponse = async response => {
|
|
|
1278
1742
|
};
|
|
1279
1743
|
|
|
1280
1744
|
/***/ }),
|
|
1281
|
-
/*
|
|
1745
|
+
/* 28 */
|
|
1282
1746
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1283
1747
|
|
|
1284
1748
|
"use strict";
|
|
@@ -1288,48 +1752,85 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1288
1752
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
|
|
1289
1753
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
|
|
1290
1754
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
|
|
1291
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
1755
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
1292
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__);
|
|
1293
|
-
/* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
1294
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
1295
|
-
/* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
|
|
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);
|
|
1296
1760
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
|
|
1297
1761
|
|
|
1298
|
-
/* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(
|
|
1762
|
+
/* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(20);
|
|
1299
1763
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
|
|
1300
1764
|
|
|
1301
|
-
/* harmony import */ var _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(
|
|
1765
|
+
/* harmony import */ var _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(21);
|
|
1302
1766
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptRequests", function() { return _components_intercept_requests_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
|
|
1303
1767
|
|
|
1304
|
-
/* harmony import */ var
|
|
1305
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
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"]; });
|
|
1306
1781
|
|
|
1307
|
-
/* harmony
|
|
1308
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useRequestInterception", function() { return _hooks_use_request_interception_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
|
|
1782
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "clearSharedCache", function() { return _hooks_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
|
|
1309
1783
|
|
|
1310
|
-
/* harmony import */ var
|
|
1311
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
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"]; });
|
|
1312
1786
|
|
|
1313
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1787
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "WhenClientSide", function() { return _hooks_use_hydratable_effect_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
|
|
1314
1788
|
|
|
1315
|
-
/* harmony import */ var
|
|
1316
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ScopedInMemoryCache", function() { return
|
|
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"]; });
|
|
1317
1791
|
|
|
1318
|
-
/* harmony import */ var
|
|
1319
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
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"]; });
|
|
1320
1794
|
|
|
1321
|
-
/* harmony import */ var
|
|
1322
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
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"]; });
|
|
1323
1797
|
|
|
1324
|
-
/* harmony import */ var
|
|
1325
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
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"]; });
|
|
1326
1800
|
|
|
1327
|
-
/* harmony
|
|
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"]; });
|
|
1328
1803
|
|
|
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"]; });
|
|
1329
1806
|
|
|
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"]; });
|
|
1330
1809
|
|
|
1810
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_17__["b"]; });
|
|
1331
1811
|
|
|
1812
|
+
|
|
1813
|
+
|
|
1814
|
+
|
|
1815
|
+
|
|
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
|
+
*/
|
|
1332
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
|
+
*/
|
|
1833
|
+
|
|
1333
1834
|
const fulfillAllDataRequests = () => {
|
|
1334
1835
|
if (!_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
1335
1836
|
return Promise.reject(new Error("Data requests are not tracked when client-side"));
|
|
@@ -1337,6 +1838,16 @@ const fulfillAllDataRequests = () => {
|
|
|
1337
1838
|
|
|
1338
1839
|
return _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestTracker */ "a"].Default.fulfillTrackedRequests();
|
|
1339
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
|
+
*/
|
|
1850
|
+
|
|
1340
1851
|
const hasUnfulfilledRequests = () => {
|
|
1341
1852
|
if (!_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
1342
1853
|
throw new Error("Data requests are not tracked when client-side");
|
|
@@ -1344,7 +1855,21 @@ const hasUnfulfilledRequests = () => {
|
|
|
1344
1855
|
|
|
1345
1856
|
return _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestTracker */ "a"].Default.hasUnfulfilledRequests;
|
|
1346
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
|
+
*/
|
|
1863
|
+
|
|
1347
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
|
+
*/
|
|
1872
|
+
|
|
1348
1873
|
const removeAllFromCache = predicate => _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* SsrCache */ "a"].Default.removeAll(predicate);
|
|
1349
1874
|
|
|
1350
1875
|
|
|
@@ -1352,11 +1877,138 @@ const removeAllFromCache = predicate => _util_ssr_cache_js__WEBPACK_IMPORTED_MOD
|
|
|
1352
1877
|
|
|
1353
1878
|
|
|
1354
1879
|
|
|
1880
|
+
|
|
1881
|
+
|
|
1882
|
+
|
|
1883
|
+
|
|
1884
|
+
|
|
1355
1885
|
// GraphQL
|
|
1356
1886
|
|
|
1357
1887
|
|
|
1358
1888
|
|
|
1359
1889
|
|
|
1360
1890
|
|
|
1891
|
+
/***/ }),
|
|
1892
|
+
/* 29 */
|
|
1893
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
1894
|
+
|
|
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
|
+
|
|
1903
|
+
|
|
1904
|
+
|
|
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
|
+
|
|
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
|
+
|
|
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
|
+
}
|
|
1929
|
+
|
|
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
|
+
|
|
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
|
+
|
|
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
|
+
|
|
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
|
+
|
|
2008
|
+
Object.freeze(Enum.Mirrored);
|
|
2009
|
+
|
|
2010
|
+
module.exports = Object.freeze(Enum);
|
|
2011
|
+
|
|
2012
|
+
|
|
1361
2013
|
/***/ })
|
|
1362
2014
|
/******/ ]);
|