@khanacademy/wonder-blocks-data 3.0.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 = 13);
85
+ /******/ return __webpack_require__(__webpack_require__.s = 19);
86
86
  /******/ })
87
87
  /************************************************************************/
88
88
  /******/ ([
@@ -93,19 +93,57 @@ module.exports = require("react");
93
93
 
94
94
  /***/ }),
95
95
  /* 1 */
96
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
97
+
98
+ "use strict";
99
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return GqlErrors; });
100
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlError; });
101
+ /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(10);
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 GqlError.
107
+ */
108
+ const GqlErrors = Object.freeze({ ..._khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["Errors"],
109
+ Network: "Network",
110
+ Parse: "Parse",
111
+ BadResponse: "BadResponse",
112
+ ErrorResult: "ErrorResult"
113
+ });
114
+ /**
115
+ * An error from the GQL API.
116
+ */
117
+
118
+ class GqlError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"] {
119
+ constructor(message, kind, {
120
+ metadata,
121
+ cause
122
+ } = {}) {
123
+ super(message, kind, {
124
+ metadata,
125
+ cause,
126
+ prefix: "Gql"
127
+ });
128
+ }
129
+
130
+ }
131
+
132
+ /***/ }),
133
+ /* 2 */
96
134
  /***/ (function(module, exports) {
97
135
 
98
136
  module.exports = require("@khanacademy/wonder-blocks-core");
99
137
 
100
138
  /***/ }),
101
- /* 2 */
139
+ /* 3 */
102
140
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
103
141
 
104
142
  "use strict";
105
143
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ResponseCache; });
106
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
144
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
107
145
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
108
- /* harmony import */ var _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
146
+ /* harmony import */ var _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
109
147
 
110
148
 
111
149
 
@@ -227,7 +265,7 @@ class ResponseCache {
227
265
  }
228
266
 
229
267
  /***/ }),
230
- /* 3 */
268
+ /* 4 */
231
269
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
232
270
 
233
271
  "use strict";
@@ -235,8 +273,8 @@ class ResponseCache {
235
273
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
236
274
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
237
275
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
238
- /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
239
- /* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
276
+ /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
277
+ /* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
240
278
 
241
279
 
242
280
 
@@ -379,7 +417,7 @@ class RequestTracker {
379
417
  }
380
418
 
381
419
  /***/ }),
382
- /* 4 */
420
+ /* 5 */
383
421
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
384
422
 
385
423
  "use strict";
@@ -396,20 +434,20 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
396
434
  /* harmony default export */ __webpack_exports__["a"] = (InterceptContext);
397
435
 
398
436
  /***/ }),
399
- /* 5 */
437
+ /* 6 */
400
438
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
401
439
 
402
440
  "use strict";
403
441
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useData; });
404
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
442
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
405
443
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
406
444
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
407
445
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
408
- /* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
409
- /* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
410
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3);
411
- /* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(12);
412
- /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2);
446
+ /* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
447
+ /* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
448
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4);
449
+ /* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(17);
450
+ /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3);
413
451
 
414
452
 
415
453
 
@@ -424,25 +462,49 @@ const useData = (handler, options) => {
424
462
  // this will have cached data in those cases as it will be present on the
425
463
  // initial render - and subsequent renders on the client it will be null.
426
464
  const cachedResult = _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__[/* ResponseCache */ "a"].Default.getEntry(handler, options);
427
- const [result, setResult] = Object(react__WEBPACK_IMPORTED_MODULE_1__["useState"])(cachedResult); // We only track data requests when we are server-side and we don't
465
+ const [result, setResult] = Object(react__WEBPACK_IMPORTED_MODULE_1__["useState"])(cachedResult); // Lookup to see if there's an interceptor for the handler.
466
+ // If we have one, we need to replace the handler with one that
467
+ // uses the interceptor.
468
+
469
+ const interceptorMap = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"]);
470
+ const interceptor = interceptorMap[handler.type]; // If we have an interceptor, we need to replace the handler with one that
471
+ // uses the interceptor. This helper function generates a new handler.
472
+ // We need this before we track the request as we want the interceptor
473
+ // to also work for tracked requests to simplify testing the server-side
474
+ // request fulfillment.
475
+
476
+ const getMaybeInterceptedHandler = () => {
477
+ if (interceptor == null) {
478
+ return handler;
479
+ }
480
+
481
+ const fulfillRequestFn = options => {
482
+ var _interceptor$fulfillR;
483
+
484
+ return (_interceptor$fulfillR = interceptor.fulfillRequest(options)) != null ? _interceptor$fulfillR : handler.fulfillRequest(options);
485
+ };
486
+
487
+ return {
488
+ fulfillRequest: fulfillRequestFn,
489
+ getKey: options => handler.getKey(options),
490
+ type: handler.type,
491
+ hydrate: handler.hydrate
492
+ };
493
+ }; // We only track data requests when we are server-side and we don't
428
494
  // already have a result, as given by the cachedData (which is also the
429
495
  // initial value for the result state).
430
496
 
497
+
431
498
  const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__[/* TrackerContext */ "b"]);
432
499
 
433
500
  if (result == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
434
- maybeTrack == null ? void 0 : maybeTrack(handler, options);
435
- } // Lookup to see if there's an interceptor for the handler.
436
- // If we have one, we need to replace the handler with one that
437
- // uses the interceptor.
438
-
439
-
440
- const interceptorMap = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"]);
441
- const interceptor = interceptorMap[handler.type]; // We need to update our request when the handler changes or the key
501
+ maybeTrack == null ? void 0 : maybeTrack(getMaybeInterceptedHandler(), options);
502
+ } // We need to update our request when the handler changes or the key
442
503
  // to the options change, so we keep track of those.
443
504
  // However, even if we are hydrating from cache, we still need to make the
444
505
  // request at least once, so we do not initialize these references.
445
506
 
507
+
446
508
  const handlerRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])();
447
509
  const keyRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])();
448
510
  const interceptorRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])(); // This effect will ensure that we fulfill the request as desired.
@@ -467,26 +529,7 @@ const useData = (handler, options) => {
467
529
  if (cachedResult == null) {
468
530
  // Mark ourselves as loading.
469
531
  setResult(null);
470
- }
471
-
472
- const getMaybeInterceptedHandler = () => {
473
- if (interceptor == null) {
474
- return handler;
475
- }
476
-
477
- const fulfillRequestFn = options => {
478
- var _interceptor$fulfillR;
479
-
480
- return (_interceptor$fulfillR = interceptor.fulfillRequest(options)) != null ? _interceptor$fulfillR : handler.fulfillRequest(options);
481
- };
482
-
483
- return {
484
- fulfillRequest: fulfillRequestFn,
485
- getKey: options => handler.getKey(options),
486
- type: handler.type,
487
- hydrate: handler.hydrate
488
- };
489
- }; // We aren't server-side, so let's make the request.
532
+ } // We aren't server-side, so let's make the request.
490
533
  // The request handler is in control of whether that request actually
491
534
  // happens or not.
492
535
 
@@ -529,12 +572,12 @@ const useData = (handler, options) => {
529
572
  };
530
573
 
531
574
  /***/ }),
532
- /* 6 */
575
+ /* 7 */
533
576
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
534
577
 
535
578
  "use strict";
536
579
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
537
- /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
580
+ /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
538
581
 
539
582
 
540
583
  let _default;
@@ -619,7 +662,18 @@ class RequestFulfillment {
619
662
  }
620
663
 
621
664
  /***/ }),
622
- /* 7 */
665
+ /* 8 */
666
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
667
+
668
+ "use strict";
669
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouterContext; });
670
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
671
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
672
+
673
+ const GqlRouterContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
674
+
675
+ /***/ }),
676
+ /* 9 */
623
677
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
624
678
 
625
679
  "use strict";
@@ -768,7 +822,13 @@ class MemoryCache {
768
822
  }
769
823
 
770
824
  /***/ }),
771
- /* 8 */
825
+ /* 10 */
826
+ /***/ (function(module, exports) {
827
+
828
+ module.exports = require("@khanacademy/wonder-stuff-core");
829
+
830
+ /***/ }),
831
+ /* 11 */
772
832
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
773
833
 
774
834
  "use strict";
@@ -808,16 +868,16 @@ class RequestHandler {
808
868
  }
809
869
 
810
870
  /***/ }),
811
- /* 9 */
871
+ /* 12 */
812
872
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
813
873
 
814
874
  "use strict";
815
875
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
816
876
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
817
877
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
818
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
878
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
819
879
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
820
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
880
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
821
881
 
822
882
 
823
883
 
@@ -839,13 +899,13 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
839
899
  }
840
900
 
841
901
  /***/ }),
842
- /* 10 */
902
+ /* 13 */
843
903
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
844
904
 
845
905
  "use strict";
846
906
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
847
907
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
848
- /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
908
+ /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
849
909
 
850
910
 
851
911
 
@@ -862,14 +922,14 @@ const Data = props => {
862
922
  /* harmony default export */ __webpack_exports__["a"] = (Data);
863
923
 
864
924
  /***/ }),
865
- /* 11 */
925
+ /* 14 */
866
926
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
867
927
 
868
928
  "use strict";
869
929
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return InterceptData; });
870
930
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
871
931
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
872
- /* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
932
+ /* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
873
933
 
874
934
 
875
935
 
@@ -908,7 +968,115 @@ class InterceptData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
908
968
  }
909
969
 
910
970
  /***/ }),
911
- /* 12 */
971
+ /* 15 */
972
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
973
+
974
+ "use strict";
975
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouter; });
976
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
977
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
978
+ /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
979
+
980
+
981
+
982
+ /**
983
+ * Configure GraphQL routing for GraphQL hooks and components.
984
+ *
985
+ * These can be nested. Components and hooks relying on the GraphQL routing
986
+ * will use the configuration from their closest ancestral GqlRouter.
987
+ */
988
+ const GqlRouter = ({
989
+ defaultContext: thisDefaultContext,
990
+ fetch: thisFetch,
991
+ children
992
+ }) => {
993
+ // We don't care if we're nested. We always force our callers to define
994
+ // everything. It makes for a clearer API and requires less error checking
995
+ // code (assuming our flow types are correct). We also don't default fetch
996
+ // to anything - our callers can tell us what function to use quite easily.
997
+ // If code that consumes this wants more nuanced nesting, it can implement
998
+ // it within its own GqlRouter than then defers to this one.
999
+ // We want to always use the same object if things haven't changed to avoid
1000
+ // over-rendering consumers of our context, let's memoize the configuration.
1001
+ // By doing this, if a component under children that uses this context
1002
+ // uses React.memo, we won't force it to re-render every time we render
1003
+ // because we'll only change the context value if something has actually
1004
+ // changed.
1005
+ const configuration = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => ({
1006
+ fetch: thisFetch,
1007
+ defaultContext: thisDefaultContext
1008
+ }), [thisDefaultContext, thisFetch]);
1009
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlRouterContext */ "a"].Provider, {
1010
+ value: configuration
1011
+ }, children);
1012
+ };
1013
+
1014
+ /***/ }),
1015
+ /* 16 */
1016
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1017
+
1018
+ "use strict";
1019
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGql; });
1020
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1021
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1022
+ /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
1023
+ /* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18);
1024
+ /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
1025
+
1026
+
1027
+
1028
+
1029
+
1030
+ /**
1031
+ * Hook to obtain a gqlFetch function for performing GraphQL requests.
1032
+ *
1033
+ * The fetch function will resolve null if the request was aborted, otherwise
1034
+ * it will resolve the data returned by the GraphQL server.
1035
+ */
1036
+ const useGql = () => {
1037
+ // This hook only works if the `GqlRouter` has been used to setup context.
1038
+ const gqlRouterContext = Object(react__WEBPACK_IMPORTED_MODULE_0__["useContext"])(_util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlRouterContext */ "a"]);
1039
+
1040
+ if (gqlRouterContext == null) {
1041
+ 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);
1042
+ }
1043
+
1044
+ const {
1045
+ fetch,
1046
+ defaultContext
1047
+ } = gqlRouterContext; // Let's memoize the gqlFetch function we create based off our context.
1048
+ // That way, even if the context happens to change, if its values don't
1049
+ // we give the same function instance back to our callers instead of
1050
+ // making a new one. That then means they can safely use the return value
1051
+ // in hooks deps without fear of it triggering extra renders.
1052
+
1053
+ const gqlFetch = Object(react__WEBPACK_IMPORTED_MODULE_0__["useMemo"])(() => (operation, options = Object.freeze({})) => {
1054
+ const {
1055
+ variables,
1056
+ context
1057
+ } = options; // Invoke the fetch and extract the data.
1058
+
1059
+ return fetch(operation, variables, { ...defaultContext,
1060
+ ...context
1061
+ }).then(_util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__[/* getGqlDataFromResponse */ "a"], error => {
1062
+ // Return null if the request was aborted.
1063
+ // The only way to detect this reliably, it seems, is to
1064
+ // check the error name and see if it's "AbortError" (this
1065
+ // is also what Apollo does).
1066
+ // Even then, it's reliant on the fetch supporting aborts.
1067
+ if (error.name === "AbortError") {
1068
+ return null;
1069
+ } // Need to make sure we pass other errors along.
1070
+
1071
+
1072
+ throw error;
1073
+ });
1074
+ }, [fetch, defaultContext]);
1075
+ return gqlFetch;
1076
+ };
1077
+
1078
+ /***/ }),
1079
+ /* 17 */
912
1080
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
913
1081
 
914
1082
  "use strict";
@@ -951,7 +1119,75 @@ const resultFromCacheEntry = cacheEntry => {
951
1119
  };
952
1120
 
953
1121
  /***/ }),
954
- /* 13 */
1122
+ /* 18 */
1123
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1124
+
1125
+ "use strict";
1126
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlDataFromResponse; });
1127
+ /* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1128
+
1129
+ /**
1130
+ * Validate a GQL operation response and extract the data.
1131
+ */
1132
+
1133
+ const getGqlDataFromResponse = async response => {
1134
+ // Get the response as text, that way we can use the text in error
1135
+ // messaging, should our parsing fail.
1136
+ const bodyText = await response.text();
1137
+ let result;
1138
+
1139
+ try {
1140
+ result = JSON.parse(bodyText);
1141
+ } catch (e) {
1142
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Failed to parse response", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].Parse, {
1143
+ metadata: {
1144
+ statusCode: response.status,
1145
+ bodyText
1146
+ },
1147
+ cause: e
1148
+ });
1149
+ } // Check for a bad status code.
1150
+
1151
+
1152
+ if (response.status >= 300) {
1153
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Response unsuccessful", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].Network, {
1154
+ metadata: {
1155
+ statusCode: response.status,
1156
+ result
1157
+ }
1158
+ });
1159
+ } // Check that we have a valid result payload.
1160
+
1161
+
1162
+ if ( // Flow shouldn't be warning about this.
1163
+ // $FlowIgnore[method-unbinding]
1164
+ !Object.prototype.hasOwnProperty.call(result, "data") && // Flow shouldn't be warning about this.
1165
+ // $FlowIgnore[method-unbinding]
1166
+ !Object.prototype.hasOwnProperty.call(result, "errors")) {
1167
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Server response missing", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].BadResponse, {
1168
+ metadata: {
1169
+ statusCode: response.status,
1170
+ result
1171
+ }
1172
+ });
1173
+ } // If the response payload has errors, throw an error.
1174
+
1175
+
1176
+ if (result.errors != null && Array.isArray(result.errors) && result.errors.length > 0) {
1177
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("GraphQL errors", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].ErrorResult, {
1178
+ metadata: {
1179
+ statusCode: response.status,
1180
+ result
1181
+ }
1182
+ });
1183
+ } // We got here, so return the data.
1184
+
1185
+
1186
+ return result.data;
1187
+ };
1188
+
1189
+ /***/ }),
1190
+ /* 19 */
955
1191
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
956
1192
 
957
1193
  "use strict";
@@ -961,25 +1197,36 @@ __webpack_require__.r(__webpack_exports__);
961
1197
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
962
1198
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
963
1199
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
964
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1200
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
965
1201
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
966
- /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
967
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
968
- /* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
1202
+ /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
1203
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
1204
+ /* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
969
1205
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RequestHandler", function() { return _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
970
1206
 
971
- /* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
1207
+ /* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12);
972
1208
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
973
1209
 
974
- /* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10);
1210
+ /* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13);
975
1211
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
976
1212
 
977
- /* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(11);
1213
+ /* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(14);
978
1214
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptData", function() { return _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
979
1215
 
980
- /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(5);
1216
+ /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(6);
981
1217
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useData", function() { return _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
982
1218
 
1219
+ /* harmony import */ var _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(15);
1220
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
1221
+
1222
+ /* harmony import */ var _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(16);
1223
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useGql", function() { return _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
1224
+
1225
+ /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(1);
1226
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["b"]; });
1227
+
1228
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlError", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
1229
+
983
1230
 
984
1231
 
985
1232
 
@@ -1000,15 +1247,13 @@ const hasUnfulfilledRequests = () => {
1000
1247
  };
1001
1248
  const removeFromCache = (handler, options) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.remove(handler, options);
1002
1249
  const removeAllFromCache = (handler, predicate) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.removeAll(handler, predicate);
1003
- /**
1004
- * TODO(somewhatabstract): Export each cache type we implement.
1005
- *
1006
- * Is there a base type we export, like we do for RequestHandler?
1007
- */
1008
1250
 
1009
1251
 
1010
1252
 
1011
1253
 
1254
+ // GraphQL
1255
+
1256
+
1012
1257
 
1013
1258
 
1014
1259
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-data",
3
- "version": "3.0.0",
3
+ "version": "3.1.2",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -14,13 +14,14 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@babel/runtime": "^7.16.3",
17
- "@khanacademy/wonder-blocks-core": "^4.0.0"
17
+ "@khanacademy/wonder-blocks-core": "^4.2.1"
18
18
  },
19
19
  "peerDependencies": {
20
+ "@khanacademy/wonder-stuff-core": "^0.1.2",
20
21
  "react": "16.14.0"
21
22
  },
22
23
  "devDependencies": {
23
- "wb-dev-build-settings": "^0.2.0"
24
+ "wb-dev-build-settings": "^0.3.0"
24
25
  },
25
26
  "author": "",
26
27
  "license": "MIT"
@@ -0,0 +1,64 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {render} from "@testing-library/react";
4
+
5
+ import {GqlRouterContext} from "../../util/gql-router-context.js";
6
+ import {GqlRouter} from "../gql-router.js";
7
+
8
+ describe("GqlRouter", () => {
9
+ it("should provide the GqlRouterContext as configured", async () => {
10
+ // Arrange
11
+ const defaultContext = {
12
+ foo: "bar",
13
+ };
14
+ const fetch = jest.fn();
15
+ const CaptureContext = ({captureFn}) => {
16
+ captureFn(React.useContext(GqlRouterContext));
17
+ return null;
18
+ };
19
+
20
+ // Act
21
+ const result = await new Promise((resolve, reject) => {
22
+ render(
23
+ <GqlRouter defaultContext={defaultContext} fetch={fetch}>
24
+ <CaptureContext captureFn={resolve} />
25
+ </GqlRouter>,
26
+ );
27
+ });
28
+
29
+ // Assert
30
+ expect(result).toStrictEqual({
31
+ defaultContext,
32
+ fetch,
33
+ });
34
+ });
35
+
36
+ it("should not render React.memo-ized children if props remain the same", () => {
37
+ // Arrange
38
+ const defaultContext = {
39
+ foo: "bar",
40
+ };
41
+ const fetch = jest.fn();
42
+ let renderCount = 0;
43
+ const Child = React.memo(() => {
44
+ const context = React.useContext(GqlRouterContext);
45
+ renderCount++;
46
+ return <div>{JSON.stringify(context)}</div>;
47
+ });
48
+
49
+ // Act
50
+ const {rerender} = render(
51
+ <GqlRouter defaultContext={defaultContext} fetch={fetch}>
52
+ <Child />
53
+ </GqlRouter>,
54
+ );
55
+ rerender(
56
+ <GqlRouter defaultContext={defaultContext} fetch={fetch}>
57
+ <Child />
58
+ </GqlRouter>,
59
+ );
60
+
61
+ // Assert
62
+ expect(renderCount).toBe(1);
63
+ });
64
+ });