@khanacademy/wonder-blocks-data 10.1.0 → 10.1.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.
Files changed (180) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/components/data.d.ts +52 -0
  3. package/dist/components/data.js.flow +63 -0
  4. package/dist/components/gql-router.d.ts +24 -0
  5. package/dist/components/gql-router.js.flow +33 -0
  6. package/dist/components/intercept-context.d.ts +10 -0
  7. package/dist/components/intercept-context.js.flow +19 -0
  8. package/dist/components/intercept-requests.d.ts +42 -0
  9. package/dist/components/intercept-requests.js.flow +51 -0
  10. package/dist/components/track-data.d.ts +11 -0
  11. package/dist/components/track-data.js.flow +18 -0
  12. package/dist/es/index.js +184 -212
  13. package/dist/hooks/use-cached-effect.d.ts +70 -0
  14. package/dist/hooks/use-cached-effect.js.flow +85 -0
  15. package/dist/hooks/use-gql-router-context.d.ts +5 -0
  16. package/dist/hooks/use-gql-router-context.js.flow +15 -0
  17. package/dist/hooks/use-gql.d.ts +12 -0
  18. package/dist/hooks/use-gql.js.flow +29 -0
  19. package/dist/hooks/use-hydratable-effect.d.ts +102 -0
  20. package/dist/hooks/use-hydratable-effect.js.flow +125 -0
  21. package/dist/hooks/use-request-interception.d.ts +14 -0
  22. package/dist/hooks/use-request-interception.js.flow +25 -0
  23. package/dist/hooks/use-server-effect.d.ts +39 -0
  24. package/dist/hooks/use-server-effect.js.flow +51 -0
  25. package/dist/hooks/use-shared-cache.d.ts +32 -0
  26. package/dist/hooks/use-shared-cache.js.flow +43 -0
  27. package/dist/index.d.ts +24 -0
  28. package/dist/index.js +186 -217
  29. package/dist/index.js.flow +48 -2
  30. package/dist/util/data-error.d.ts +45 -0
  31. package/dist/util/data-error.js.flow +64 -0
  32. package/dist/util/get-gql-data-from-response.d.ts +4 -0
  33. package/dist/util/get-gql-data-from-response.js.flow +13 -0
  34. package/dist/util/get-gql-request-id.d.ts +5 -0
  35. package/dist/util/get-gql-request-id.js.flow +20 -0
  36. package/dist/util/gql-error.d.ts +28 -0
  37. package/dist/util/gql-error.js.flow +43 -0
  38. package/dist/util/gql-router-context.d.ts +3 -0
  39. package/dist/util/gql-router-context.js.flow +10 -0
  40. package/dist/util/gql-types.d.ts +34 -0
  41. package/dist/util/gql-types.js.flow +53 -0
  42. package/dist/util/graphql-document-node-parser.d.ts +18 -0
  43. package/dist/util/graphql-document-node-parser.js.flow +31 -0
  44. package/dist/util/graphql-types.d.ts +19 -0
  45. package/dist/util/graphql-types.js.flow +30 -0
  46. package/dist/util/hydration-cache-api.d.ts +17 -0
  47. package/dist/util/hydration-cache-api.js.flow +30 -0
  48. package/dist/util/merge-gql-context.d.ts +8 -0
  49. package/dist/util/merge-gql-context.js.flow +19 -0
  50. package/dist/util/purge-caches.d.ts +8 -0
  51. package/dist/util/purge-caches.js.flow +15 -0
  52. package/dist/util/request-api.d.ts +28 -0
  53. package/dist/util/request-api.js.flow +34 -0
  54. package/dist/util/request-fulfillment.d.ts +37 -0
  55. package/dist/util/request-fulfillment.js.flow +50 -0
  56. package/dist/util/request-tracking.d.ts +62 -0
  57. package/dist/util/request-tracking.js.flow +81 -0
  58. package/dist/util/result-from-cache-response.d.ts +5 -0
  59. package/dist/util/result-from-cache-response.js.flow +15 -0
  60. package/dist/util/scoped-in-memory-cache.d.ts +38 -0
  61. package/dist/util/scoped-in-memory-cache.js.flow +57 -0
  62. package/dist/util/serializable-in-memory-cache.d.ts +16 -0
  63. package/dist/util/serializable-in-memory-cache.js.flow +26 -0
  64. package/dist/util/ssr-cache.d.ts +51 -0
  65. package/dist/util/ssr-cache.js.flow +87 -0
  66. package/dist/util/status.d.ts +10 -0
  67. package/dist/util/status.js.flow +19 -0
  68. package/dist/util/to-gql-operation.d.ts +32 -0
  69. package/dist/util/to-gql-operation.js.flow +45 -0
  70. package/dist/util/types.d.ts +111 -0
  71. package/dist/util/types.js.flow +151 -0
  72. package/package.json +5 -6
  73. package/src/components/__tests__/{data.test.js → data.test.tsx} +42 -2
  74. package/src/components/__tests__/{gql-router.test.js → gql-router.test.tsx} +4 -5
  75. package/src/components/__tests__/{intercept-requests.test.js → intercept-requests.test.tsx} +2 -3
  76. package/src/components/__tests__/{track-data.test.js → track-data.test.tsx} +2 -3
  77. package/src/components/{data.js → data.ts} +11 -15
  78. package/src/components/{gql-router.js → gql-router.tsx} +12 -14
  79. package/src/components/{intercept-context.js → intercept-context.ts} +4 -3
  80. package/src/components/{intercept-requests.js → intercept-requests.tsx} +7 -8
  81. package/src/components/{track-data.js → track-data.tsx} +4 -5
  82. package/src/hooks/__tests__/{use-cached-effect.test.js → use-cached-effect.test.tsx} +55 -50
  83. package/src/hooks/__tests__/{use-gql-router-context.test.js → use-gql-router-context.test.tsx} +7 -7
  84. package/src/hooks/__tests__/{use-gql.test.js → use-gql.test.tsx} +20 -21
  85. package/src/hooks/__tests__/{use-hydratable-effect.test.js → use-hydratable-effect.test.ts} +42 -37
  86. package/src/hooks/__tests__/{use-request-interception.test.js → use-request-interception.test.tsx} +5 -3
  87. package/src/hooks/__tests__/{use-server-effect.test.js → use-server-effect.test.ts} +8 -2
  88. package/src/hooks/__tests__/{use-shared-cache.test.js → use-shared-cache.test.ts} +12 -12
  89. package/src/hooks/{use-cached-effect.js → use-cached-effect.ts} +27 -20
  90. package/src/hooks/{use-gql-router-context.js → use-gql-router-context.ts} +2 -3
  91. package/src/hooks/{use-gql.js → use-gql.ts} +5 -5
  92. package/src/hooks/{use-hydratable-effect.js → use-hydratable-effect.ts} +53 -58
  93. package/src/hooks/{use-request-interception.js → use-request-interception.ts} +4 -4
  94. package/src/hooks/{use-server-effect.js → use-server-effect.ts} +7 -9
  95. package/src/hooks/{use-shared-cache.js → use-shared-cache.ts} +13 -8
  96. package/src/{index.js → index.ts} +0 -1
  97. package/src/util/__tests__/{get-gql-data-from-response.test.js → get-gql-data-from-response.test.ts} +0 -1
  98. package/src/util/__tests__/{get-gql-request-id.test.js → get-gql-request-id.test.ts} +9 -11
  99. package/src/util/__tests__/{graphql-document-node-parser.test.js → graphql-document-node-parser.test.ts} +11 -12
  100. package/src/util/__tests__/{hydration-cache-api.test.js → hydration-cache-api.test.ts} +1 -2
  101. package/src/util/__tests__/{merge-gql-context.test.js → merge-gql-context.test.ts} +4 -5
  102. package/src/util/__tests__/{purge-caches.test.js → purge-caches.test.ts} +0 -1
  103. package/src/util/__tests__/{request-api.test.js → request-api.test.ts} +2 -2
  104. package/src/util/__tests__/{request-fulfillment.test.js → request-fulfillment.test.ts} +0 -1
  105. package/src/util/__tests__/{request-tracking.test.js → request-tracking.test.tsx} +13 -6
  106. package/src/util/__tests__/{result-from-cache-response.test.js → result-from-cache-response.test.ts} +2 -4
  107. package/src/util/__tests__/{scoped-in-memory-cache.test.js → scoped-in-memory-cache.test.ts} +4 -5
  108. package/src/util/__tests__/{serializable-in-memory-cache.test.js → serializable-in-memory-cache.test.ts} +7 -7
  109. package/src/util/__tests__/{ssr-cache.test.js → ssr-cache.test.ts} +3 -2
  110. package/src/util/__tests__/{to-gql-operation.test.js → to-gql-operation.test.ts} +2 -1
  111. package/src/util/{data-error.js → data-error.ts} +2 -3
  112. package/src/util/{get-gql-data-from-response.js → get-gql-data-from-response.ts} +1 -6
  113. package/src/util/{get-gql-request-id.js → get-gql-request-id.ts} +12 -16
  114. package/src/util/{gql-error.js → gql-error.ts} +2 -3
  115. package/src/util/gql-router-context.ts +6 -0
  116. package/src/util/{gql-types.js → gql-types.ts} +27 -23
  117. package/src/util/{graphql-document-node-parser.js → graphql-document-node-parser.ts} +6 -7
  118. package/src/util/graphql-types.ts +27 -0
  119. package/src/util/{hydration-cache-api.js → hydration-cache-api.ts} +4 -2
  120. package/src/util/{merge-gql-context.js → merge-gql-context.ts} +2 -2
  121. package/src/util/{purge-caches.js → purge-caches.ts} +0 -1
  122. package/src/util/{request-api.js → request-api.ts} +0 -1
  123. package/src/util/{request-fulfillment.js → request-fulfillment.ts} +13 -12
  124. package/src/util/{request-tracking.js → request-tracking.ts} +12 -13
  125. package/src/util/{result-from-cache-response.js → result-from-cache-response.ts} +3 -4
  126. package/src/util/{scoped-in-memory-cache.js → scoped-in-memory-cache.ts} +1 -2
  127. package/src/util/{serializable-in-memory-cache.js → serializable-in-memory-cache.ts} +2 -3
  128. package/src/util/{ssr-cache.js → ssr-cache.ts} +19 -18
  129. package/src/util/{status.js → status.ts} +4 -5
  130. package/src/util/{to-gql-operation.js → to-gql-operation.ts} +1 -2
  131. package/src/util/{types.js → types.ts} +39 -48
  132. package/tsconfig.json +11 -0
  133. package/tsconfig.tsbuildinfo +1 -0
  134. package/src/__docs__/_overview_.stories.mdx +0 -18
  135. package/src/__docs__/_overview_graphql.stories.mdx +0 -35
  136. package/src/__docs__/_overview_ssr_.stories.mdx +0 -185
  137. package/src/__docs__/_overview_testing_.stories.mdx +0 -123
  138. package/src/__docs__/exports.abort-inflight-requests.stories.mdx +0 -20
  139. package/src/__docs__/exports.data-error.stories.mdx +0 -23
  140. package/src/__docs__/exports.data-errors.stories.mdx +0 -23
  141. package/src/__docs__/exports.data.stories.mdx +0 -146
  142. package/src/__docs__/exports.fetch-tracked-requests.stories.mdx +0 -24
  143. package/src/__docs__/exports.get-gql-request-id.stories.mdx +0 -24
  144. package/src/__docs__/exports.gql-error.stories.mdx +0 -23
  145. package/src/__docs__/exports.gql-errors.stories.mdx +0 -20
  146. package/src/__docs__/exports.gql-router.stories.mdx +0 -29
  147. package/src/__docs__/exports.has-tracked-requests-to-be-fetched.stories.mdx +0 -20
  148. package/src/__docs__/exports.intercept-requests.stories.mdx +0 -69
  149. package/src/__docs__/exports.intialize-hydration-cache.stories.mdx +0 -29
  150. package/src/__docs__/exports.purge-caches.stories.mdx +0 -23
  151. package/src/__docs__/exports.purge-hydration-cache.stories.mdx +0 -24
  152. package/src/__docs__/exports.scoped-in-memory-cache.stories.mdx +0 -92
  153. package/src/__docs__/exports.serializable-in-memory-cache.stories.mdx +0 -112
  154. package/src/__docs__/exports.shared-cache.stories.mdx +0 -16
  155. package/src/__docs__/exports.status.stories.mdx +0 -31
  156. package/src/__docs__/exports.track-data.stories.mdx +0 -209
  157. package/src/__docs__/exports.use-cached-effect.stories.mdx +0 -44
  158. package/src/__docs__/exports.use-gql.stories.mdx +0 -41
  159. package/src/__docs__/exports.use-hydratable-effect.stories.mdx +0 -43
  160. package/src/__docs__/exports.use-server-effect.stories.mdx +0 -50
  161. package/src/__docs__/exports.use-shared-cache.stories.mdx +0 -30
  162. package/src/__docs__/exports.when-client-side.stories.mdx +0 -33
  163. package/src/__docs__/types.cached-response.stories.mdx +0 -29
  164. package/src/__docs__/types.error-options.stories.mdx +0 -21
  165. package/src/__docs__/types.fetch-policy.stories.mdx +0 -44
  166. package/src/__docs__/types.gql-context.stories.mdx +0 -20
  167. package/src/__docs__/types.gql-fetch-fn.stories.mdx +0 -24
  168. package/src/__docs__/types.gql-fetch-options.stories.mdx +0 -24
  169. package/src/__docs__/types.gql-operation-type.stories.mdx +0 -24
  170. package/src/__docs__/types.gql-operation.stories.mdx +0 -67
  171. package/src/__docs__/types.raw-scoped-cache.stories.mdx +0 -27
  172. package/src/__docs__/types.response-cache.stories.mdx +0 -33
  173. package/src/__docs__/types.result.stories.mdx +0 -39
  174. package/src/__docs__/types.scoped-cache.stories.mdx +0 -114
  175. package/src/__docs__/types.valid-cache-data.stories.mdx +0 -23
  176. package/src/util/gql-router-context.js +0 -6
  177. package/src/util/graphql-types.js +0 -30
  178. /package/src/hooks/__tests__/__snapshots__/{use-shared-cache.test.js.snap → use-shared-cache.test.ts.snap} +0 -0
  179. /package/src/util/__tests__/__snapshots__/{scoped-in-memory-cache.test.js.snap → scoped-in-memory-cache.test.ts.snap} +0 -0
  180. /package/src/util/__tests__/__snapshots__/{serializable-in-memory-cache.test.js.snap → serializable-in-memory-cache.test.ts.snap} +0 -0
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
  import {useForceUpdate} from "@khanacademy/wonder-blocks-core";
4
3
  import {DataError, DataErrors} from "../util/data-error";
@@ -13,15 +12,14 @@ import type {Result, ValidCacheData} from "../util/types";
13
12
 
14
13
  import {FetchPolicy} from "../util/types";
15
14
 
16
- type CachedEffectOptions<TData: ValidCacheData> = {|
15
+ type CachedEffectOptions<TData extends ValidCacheData> = {
17
16
  /**
18
17
  * The policy to use when determining how to retrieve the request data from
19
18
  * cache and network.
20
19
  *
21
20
  * Defaults to `FetchPolicy.CacheBeforeNetwork`.
22
21
  */
23
- fetchPolicy?: $Values<typeof FetchPolicy>,
24
-
22
+ fetchPolicy?: typeof FetchPolicy[keyof typeof FetchPolicy];
25
23
  /**
26
24
  * When `true`, the effect will not be executed; otherwise, the effect will
27
25
  * be executed.
@@ -31,8 +29,7 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
31
29
  *
32
30
  * Default is `false`.
33
31
  */
34
- skip?: boolean,
35
-
32
+ skip?: boolean;
36
33
  /**
37
34
  * When `true`, the effect will not reset the result to the loading status
38
35
  * while executing if the requestId changes, instead, returning
@@ -43,8 +40,7 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
43
40
  * loading; old pending effects are discarded on changes and as such this
44
41
  * value has no effect in that case.
45
42
  */
46
- retainResultOnChange?: boolean,
47
-
43
+ retainResultOnChange?: boolean;
48
44
  /**
49
45
  * Callback that is invoked if the result for the given hook has changed.
50
46
  *
@@ -55,8 +51,7 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
55
51
  * When not defined, the hook will ensure the component re-renders to pick
56
52
  * up the latest result.
57
53
  */
58
- onResultChanged?: (result: Result<TData>) => void,
59
-
54
+ onResultChanged?: (result: Result<TData>) => void;
60
55
  /**
61
56
  * Scope to use with the shared cache.
62
57
  *
@@ -65,8 +60,8 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
65
60
  *
66
61
  * Changing this value after the first call is not supported.
67
62
  */
68
- scope?: string,
69
- |};
63
+ scope?: string;
64
+ };
70
65
 
71
66
  const DefaultScope = "useCachedEffect";
72
67
 
@@ -86,12 +81,12 @@ const DefaultScope = "useCachedEffect";
86
81
  * Once the request has been tried once and a non-loading response has been
87
82
  * cached, the request will not executed made again.
88
83
  */
89
- export const useCachedEffect = <TData: ValidCacheData>(
84
+ export const useCachedEffect = <TData extends ValidCacheData>(
90
85
  requestId: string,
91
86
  handler: () => Promise<TData>,
92
- options: CachedEffectOptions<TData> = ({}: $Shape<
93
- CachedEffectOptions<TData>,
94
- >),
87
+ options: CachedEffectOptions<TData> = {} as Partial<
88
+ CachedEffectOptions<TData>
89
+ >,
95
90
  ): [Result<TData>, () => void] => {
96
91
  const {
97
92
  fetchPolicy = FetchPolicy.CacheBeforeNetwork,
@@ -108,13 +103,13 @@ export const useCachedEffect = <TData: ValidCacheData>(
108
103
  // Instead of using state, which would be local to just this hook instance,
109
104
  // we use a shared in-memory cache.
110
105
  const [mostRecentResult, setMostRecentResult] = useSharedCache<
111
- Result<TData>,
112
- >(
113
- requestId, // The key of the cached item
114
- scope, // The scope of the cached items
106
+ Result<TData>
107
+ >( // The key of the cached item
108
+ requestId, // The scope of the cached items
115
109
  // No default value. We don't want the loading status there; to ensure
116
110
  // that all calls when the request is in-flight will update once that
117
111
  // request is done, we want the cache to be empty until that point.
112
+ scope,
118
113
  );
119
114
  const forceUpdate = useForceUpdate();
120
115
  // For the NetworkOnly fetch policy, we ignore the cached value.
@@ -127,8 +122,11 @@ export const useCachedEffect = <TData: ValidCacheData>(
127
122
  // We aren't using useCallback here because we need to make sure that
128
123
  // if we are rememo-izing, we cancel any inflight request for the old
129
124
  // callback.
125
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'cancel' does not exist on type 'never'.
130
126
  currentRequestRef.current?.cancel();
127
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
131
128
  currentRequestRef.current = null;
129
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
132
130
  networkResultRef.current = null;
133
131
 
134
132
  const fetchFn = () => {
@@ -155,6 +153,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
155
153
  },
156
154
  );
157
155
 
156
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'request' does not exist on type 'never'.
158
157
  if (request === currentRequestRef.current?.request) {
159
158
  // The request inflight is the same, so do nothing.
160
159
  // NOTE: Perhaps if invoked via a refetch, we will want to
@@ -163,9 +162,11 @@ export const useCachedEffect = <TData: ValidCacheData>(
163
162
  }
164
163
 
165
164
  // Clear the last network result.
165
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
166
166
  networkResultRef.current = null;
167
167
 
168
168
  // Cancel the previous request.
169
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'cancel' does not exist on type 'never'.
169
170
  currentRequestRef.current?.cancel();
170
171
 
171
172
  // TODO(somewhatabstract, FEI-4276):
@@ -177,6 +178,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
177
178
  // Catching shouldn't serve a purpose.
178
179
  // eslint-disable-next-line promise/catch-or-return
179
180
  request.then((result) => {
181
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
180
182
  currentRequestRef.current = null;
181
183
  if (cancel) {
182
184
  // We don't modify our result if the request was cancelled
@@ -187,6 +189,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
187
189
 
188
190
  // Now we need to update the cache and notify or force a rerender.
189
191
  setMostRecentResult(result);
192
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'Result<TData>' is not assignable to type 'undefined'.
190
193
  networkResultRef.current = result;
191
194
 
192
195
  if (onResultChanged != null) {
@@ -201,6 +204,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
201
204
  return; // Shut up eslint always-return rule.
202
205
  });
203
206
 
207
+ // @ts-expect-error [FEI-5019] - TS2322 - Type '{ requestId: string; request: Promise<Result<TData>>; cancel(): void; }' is not assignable to type 'undefined'.
204
208
  currentRequestRef.current = {
205
209
  requestId,
206
210
  request,
@@ -261,7 +265,9 @@ export const useCachedEffect = <TData: ValidCacheData>(
261
265
  }
262
266
  fetchRequest();
263
267
  return () => {
268
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'cancel' does not exist on type 'never'.
264
269
  currentRequestRef.current?.cancel();
270
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
265
271
  currentRequestRef.current = null;
266
272
  };
267
273
  }, [shouldFetch, fetchRequest]);
@@ -282,5 +288,6 @@ export const useCachedEffect = <TData: ValidCacheData>(
282
288
  lastResultAgnosticOfIdRef.current = result;
283
289
 
284
290
  // We return the result and a function for triggering a refetch.
291
+ // @ts-expect-error [FEI-5019] - TS2322 - Type '{ status: "loading"; } | { status: "error"; error: Error; } | { status: "aborted"; } | { status: "success"; data: ValidCacheData; }' is not assignable to type 'Result<TData>'.
285
292
  return [result, fetchRequest];
286
293
  };
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import {useContext, useRef, useMemo} from "react";
3
2
 
4
3
  import {mergeGqlContext} from "../util/merge-gql-context";
@@ -10,8 +9,8 @@ import type {GqlRouterConfiguration, GqlContext} from "../util/gql-types";
10
9
  /**
11
10
  * Construct a GqlRouterContext from the current one and partial context.
12
11
  */
13
- export const useGqlRouterContext = <TContext: GqlContext>(
14
- contextOverrides: Partial<TContext> = ({}: $Shape<TContext>),
12
+ export const useGqlRouterContext = <TContext extends GqlContext>(
13
+ contextOverrides: Partial<TContext> = {} as Partial<TContext>,
15
14
  ): GqlRouterConfiguration<TContext> => {
16
15
  // This hook only works if the `GqlRouter` has been used to setup context.
17
16
  const gqlRouterContext = useContext(GqlRouterContext);
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import {useCallback} from "react";
3
2
 
4
3
  import {mergeGqlContext} from "../util/merge-gql-context";
@@ -21,9 +20,9 @@ import type {
21
20
  * Values in the partial context given to the returned fetch function will
22
21
  * only be included if they have a value other than undefined.
23
22
  */
24
- export const useGql = <TContext: GqlContext>(
25
- context: Partial<TContext> = ({}: $Shape<TContext>),
26
- ): (<TData, TVariables: {...}>(
23
+ export const useGql = <TContext extends GqlContext>(
24
+ context: Partial<TContext> = {} as Partial<TContext>,
25
+ ): (<TData, TVariables extends Record<any, any>>(
27
26
  operation: GqlOperation<TData, TVariables>,
28
27
  options?: GqlFetchOptions<TVariables, TContext>,
29
28
  ) => Promise<TData>) => {
@@ -36,7 +35,7 @@ export const useGql = <TContext: GqlContext>(
36
35
  // making a new one. That then means they can safely use the return value
37
36
  // in hooks deps without fear of it triggering extra renders.
38
37
  const gqlFetch = useCallback(
39
- <TData, TVariables: {...}>(
38
+ <TData, TVariables extends Record<any, any>>(
40
39
  operation: GqlOperation<TData, TVariables>,
41
40
  options: GqlFetchOptions<TVariables, TContext> = Object.freeze({}),
42
41
  ) => {
@@ -51,5 +50,6 @@ export const useGql = <TContext: GqlContext>(
51
50
  },
52
51
  [gqlRouterContext],
53
52
  );
53
+ // @ts-expect-error [FEI-5019] - TS2322 - Type '<TData, TVariables extends Record<any, any>>(operation: GqlOperation<TData, TVariables>, options?: GqlFetchOptions<TVariables, TContext>) => Promise<unknown>' is not assignable to type '<TData, TVariables extends Record<any, any>>(operation: GqlOperation<TData, TVariables>, options?: GqlFetchOptions<TVariables, TContext> | undefined) => Promise<...>'.
54
54
  return gqlFetch;
55
55
  };
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
3
  import {useServerEffect} from "./use-server-effect";
@@ -21,7 +20,7 @@ export const WhenClientSide = {
21
20
  * for properly hydrating this component (for example, the action invokes
22
21
  * Apollo which manages its own cache to ensure things render properly).
23
22
  */
24
- DoNotHydrate: ("DoNotHydrate": "DoNotHydrate"),
23
+ DoNotHydrate: "DoNotHydrate" as const,
25
24
 
26
25
  /**
27
26
  * The result from executing the effect server-side will be hydrated.
@@ -29,7 +28,7 @@ export const WhenClientSide = {
29
28
  * be hydrated (i.e. both error and success hydration results prevent the
30
29
  * effect running client-side).
31
30
  */
32
- ExecuteWhenNoResult: ("ExecuteWhenNoResult": "ExecuteWhenNoResult"),
31
+ ExecuteWhenNoResult: "ExecuteWhenNoResult" as const,
33
32
 
34
33
  /**
35
34
  * The result from executing the effect server-side will be hydrated.
@@ -38,18 +37,17 @@ export const WhenClientSide = {
38
37
  * If the hydrated result was not a success result, or there was no
39
38
  * hydrated result, the effect will not be executed.
40
39
  */
41
- ExecuteWhenNoSuccessResult:
42
- ("ExecuteWhenNoSuccessResult": "ExecuteWhenNoSuccessResult"),
40
+ ExecuteWhenNoSuccessResult: "ExecuteWhenNoSuccessResult" as const,
43
41
 
44
42
  /**
45
43
  * The result from executing the effect server-side will be hydrated.
46
44
  * The effect will always be executed client-side, regardless of the
47
45
  * hydrated result status.
48
46
  */
49
- AlwaysExecute: ("AlwaysExecute": "AlwaysExecute"),
50
- };
47
+ AlwaysExecute: "AlwaysExecute" as const,
48
+ } as const;
51
49
 
52
- type HydratableEffectOptions<TData: ValidCacheData> = {|
50
+ type HydratableEffectOptions<TData extends ValidCacheData> = {
53
51
  /**
54
52
  * How the hook should behave when rendering client-side for the first time.
55
53
  *
@@ -60,8 +58,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
60
58
  * Changing this value after the first call is irrelevant as it only
61
59
  * affects the initial render behavior.
62
60
  */
63
- clientBehavior?: $Values<typeof WhenClientSide>,
64
-
61
+ clientBehavior?: typeof WhenClientSide[keyof typeof WhenClientSide];
65
62
  /**
66
63
  * When `true`, the effect will not be executed; otherwise, the effect will
67
64
  * be executed.
@@ -71,8 +68,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
71
68
  *
72
69
  * Default is `false`.
73
70
  */
74
- skip?: boolean,
75
-
71
+ skip?: boolean;
76
72
  /**
77
73
  * When `true`, the effect will not reset the result to the loading status
78
74
  * while executing if the requestId changes, instead, returning
@@ -83,8 +79,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
83
79
  * loading; old pending effects are discarded on changes and as such this
84
80
  * value has no effect in that case.
85
81
  */
86
- retainResultOnChange?: boolean,
87
-
82
+ retainResultOnChange?: boolean;
88
83
  /**
89
84
  * Callback that is invoked if the result for the given hook has changed.
90
85
  *
@@ -95,8 +90,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
95
90
  * When not defined, the hook will ensure the component re-renders to pick
96
91
  * up the latest result.
97
92
  */
98
- onResultChanged?: (result: Result<TData>) => void,
99
-
93
+ onResultChanged?: (result: Result<TData>) => void;
100
94
  /**
101
95
  * Scope to use with the shared cache.
102
96
  *
@@ -105,8 +99,8 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
105
99
  *
106
100
  * Changing this value after the first call is not supported.
107
101
  */
108
- scope?: string,
109
- |};
102
+ scope?: string;
103
+ };
110
104
 
111
105
  const DefaultScope = "useHydratableEffect";
112
106
 
@@ -120,12 +114,12 @@ const DefaultScope = "useHydratableEffect";
120
114
  * invocations. Cache changes from one hook instance do not trigger renders
121
115
  * in components that use the same requestID.
122
116
  */
123
- export const useHydratableEffect = <TData: ValidCacheData>(
117
+ export const useHydratableEffect = <TData extends ValidCacheData>(
124
118
  requestId: string,
125
119
  handler: () => Promise<TData>,
126
- options: HydratableEffectOptions<TData> = ({}: $Shape<
127
- HydratableEffectOptions<TData>,
128
- >),
120
+ options: HydratableEffectOptions<TData> = {} as Partial<
121
+ HydratableEffectOptions<TData>
122
+ >,
129
123
  ): Result<TData> => {
130
124
  const {
131
125
  clientBehavior = WhenClientSide.ExecuteWhenNoSuccessResult,
@@ -144,48 +138,49 @@ export const useHydratableEffect = <TData: ValidCacheData>(
144
138
  skip,
145
139
  });
146
140
 
147
- const getDefaultCacheValue: () => ?Result<TData> = React.useCallback(() => {
148
- // If we don't have a requestId, it's our first render, the one
149
- // where we hydrated. So defer to our clientBehavior value.
150
- switch (clientBehavior) {
151
- case WhenClientSide.DoNotHydrate:
152
- case WhenClientSide.AlwaysExecute:
153
- // Either we weren't hydrating at all, or we don't care
154
- // if we hydrated something or not, either way, we're
155
- // doing a request.
156
- return null;
157
-
158
- case WhenClientSide.ExecuteWhenNoResult:
159
- // We only execute if we didn't hydrate something.
160
- // So, returning the hydration result as default for our
161
- // cache, will then prevent the cached effect running.
162
- return serverResult;
163
-
164
- case WhenClientSide.ExecuteWhenNoSuccessResult:
165
- // We only execute if we didn't hydrate a success result.
166
- if (serverResult?.status === "success") {
141
+ const getDefaultCacheValue: () => Result<TData> | null | undefined =
142
+ React.useCallback(() => {
143
+ // If we don't have a requestId, it's our first render, the one
144
+ // where we hydrated. So defer to our clientBehavior value.
145
+ switch (clientBehavior) {
146
+ case WhenClientSide.DoNotHydrate:
147
+ case WhenClientSide.AlwaysExecute:
148
+ // Either we weren't hydrating at all, or we don't care
149
+ // if we hydrated something or not, either way, we're
150
+ // doing a request.
151
+ return null;
152
+
153
+ case WhenClientSide.ExecuteWhenNoResult:
154
+ // We only execute if we didn't hydrate something.
167
155
  // So, returning the hydration result as default for our
168
156
  // cache, will then prevent the cached effect running.
169
157
  return serverResult;
170
- }
171
- return null;
172
- }
173
- // There is no reason for this to change after the first render,
174
- // you might think, but the function closes around serverResult and if
175
- // the requestId changes, it still returns the hydrate result of the
176
- // first render of the previous requestId. This then means that the
177
- // hydrate result is still the same, and the effect is not re-executed
178
- // because the cache gets incorrectly defaulted.
179
- // However, we don't want to bother doing anything with this on
180
- // client behavior changing since that truly is irrelevant.
181
- // eslint-disable-next-line react-hooks/exhaustive-deps
182
- }, [serverResult]);
158
+
159
+ case WhenClientSide.ExecuteWhenNoSuccessResult:
160
+ // We only execute if we didn't hydrate a success result.
161
+ if (serverResult?.status === "success") {
162
+ // So, returning the hydration result as default for our
163
+ // cache, will then prevent the cached effect running.
164
+ return serverResult;
165
+ }
166
+ return null;
167
+ }
168
+ // There is no reason for this to change after the first render,
169
+ // you might think, but the function closes around serverResult and if
170
+ // the requestId changes, it still returns the hydrate result of the
171
+ // first render of the previous requestId. This then means that the
172
+ // hydrate result is still the same, and the effect is not re-executed
173
+ // because the cache gets incorrectly defaulted.
174
+ // However, we don't want to bother doing anything with this on
175
+ // client behavior changing since that truly is irrelevant.
176
+ // eslint-disable-next-line react-hooks/exhaustive-deps
177
+ }, [serverResult]);
183
178
 
184
179
  // Instead of using state, which would be local to just this hook instance,
185
180
  // we use a shared in-memory cache.
186
- useSharedCache<Result<TData>>(
187
- requestId, // The key of the cached item
188
- scope, // The scope of the cached items
181
+ useSharedCache<Result<TData>>( // The key of the cached item
182
+ requestId, // The scope of the cached items
183
+ scope,
189
184
  getDefaultCacheValue,
190
185
  );
191
186
 
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
3
  import InterceptContext from "../components/intercept-context";
@@ -16,7 +15,7 @@ import type {ValidCacheData} from "../util/types";
16
15
  * an intercepted handler, and then invoke `useServerEffect` (or other things)
17
16
  * with that intercepted handler.
18
17
  */
19
- export const useRequestInterception = <TData: ValidCacheData>(
18
+ export const useRequestInterception = <TData extends ValidCacheData>(
20
19
  requestId: string,
21
20
  handler: () => Promise<TData>,
22
21
  ): (() => Promise<TData>) => {
@@ -32,6 +31,7 @@ export const useRequestInterception = <TData: ValidCacheData>(
32
31
  // Call the interceptors from closest to furthest.
33
32
  // If one returns a non-null result, then we keep that.
34
33
  const interceptResponse = interceptors.reduceRight(
34
+ // @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
35
35
  (prev, interceptor) => {
36
36
  if (prev != null) {
37
37
  return prev;
@@ -42,8 +42,8 @@ export const useRequestInterception = <TData: ValidCacheData>(
42
42
  );
43
43
  // If nothing intercepted this request, invoke the original handler.
44
44
  // NOTE: We can't guarantee all interceptors return the same type
45
- // as our handler, so how can flow know? Let's just suppress that.
46
- // $FlowFixMe[incompatible-return]
45
+ // as our handler, so how can TypeScript know? Let's just suppress that.
46
+ // @ts-expect-error [FEI-5019] - TS2739 - Type '(requestId: string) => Promise<ValidCacheData | null | undefined> | null | undefined' is missing the following properties from type 'Promise<TData>': then, catch, finally, [Symbol.toStringTag]
47
47
  return interceptResponse ?? handler();
48
48
  }, [handler, interceptors, requestId]);
49
49
 
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import {Server} from "@khanacademy/wonder-blocks-core";
3
2
  import {useContext} from "react";
4
3
  import {TrackerContext} from "../util/request-tracking";
@@ -8,7 +7,7 @@ import {useRequestInterception} from "./use-request-interception";
8
7
 
9
8
  import type {Result, ValidCacheData} from "../util/types";
10
9
 
11
- type ServerEffectOptions = {|
10
+ type ServerEffectOptions = {
12
11
  /**
13
12
  * When `true`, the result of the effect when fulfilled using Wonder Blocks
14
13
  * Data will be stored in the hydration cache for hydrating client-side;
@@ -20,16 +19,15 @@ type ServerEffectOptions = {|
20
19
  *
21
20
  * Default is `true`.
22
21
  */
23
- hydrate?: boolean,
24
-
22
+ hydrate?: boolean;
25
23
  /**
26
24
  * When `true`, the effect will not be tracked for fulfillment; otherwise,
27
25
  * the effect will be tracked for fulfillment.
28
26
  *
29
27
  * Default is `false`.
30
28
  */
31
- skip?: boolean,
32
- |};
29
+ skip?: boolean;
30
+ };
33
31
 
34
32
  /**
35
33
  * Hook to perform an asynchronous action during server-side rendering.
@@ -46,11 +44,11 @@ type ServerEffectOptions = {|
46
44
  *
47
45
  * The asynchronous action is never invoked on the client-side.
48
46
  */
49
- export const useServerEffect = <TData: ValidCacheData>(
47
+ export const useServerEffect = <TData extends ValidCacheData>(
50
48
  requestId: string,
51
49
  handler: () => Promise<TData>,
52
- options: ServerEffectOptions = ({}: $Shape<ServerEffectOptions>),
53
- ): ?Result<TData> => {
50
+ options: ServerEffectOptions = {} as Partial<ServerEffectOptions>,
51
+ ): Result<TData> | null | undefined => {
54
52
  const {hydrate = true, skip = false} = options;
55
53
 
56
54
  // Plug in to the request interception framework for code that wants
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
  import {DataError, DataErrors} from "../util/data-error";
4
3
  import {ScopedInMemoryCache} from "../util/scoped-in-memory-cache";
@@ -7,7 +6,9 @@ import type {ValidCacheData, ScopedCache} from "../util/types";
7
6
  /**
8
7
  * A function for inserting a value into the cache or clearing it.
9
8
  */
10
- type CacheValueFn<TValue: ValidCacheData> = (value: ?TValue) => void;
9
+ type CacheValueFn<TValue extends ValidCacheData> = (
10
+ value?: TValue | null | undefined,
11
+ ) => void;
11
12
 
12
13
  /**
13
14
  * This is the cache.
@@ -42,11 +43,15 @@ export const SharedCache: ScopedCache = cache;
42
43
  * sure this toggling is optional - or we could use a callback argument, to
43
44
  * achieve this on an as-needed basis.
44
45
  */
45
- export const useSharedCache = <TValue: ValidCacheData>(
46
+ export const useSharedCache = <TValue extends ValidCacheData>(
46
47
  id: string,
47
48
  scope: string,
48
- initialValue?: ?TValue | (() => ?TValue),
49
- ): [?TValue, CacheValueFn<TValue>] => {
49
+ initialValue?:
50
+ | TValue
51
+ | null
52
+ | undefined
53
+ | (() => TValue | null | undefined),
54
+ ): [TValue | null | undefined, CacheValueFn<TValue>] => {
50
55
  // Verify arguments.
51
56
  if (!id || typeof id !== "string") {
52
57
  throw new DataError(
@@ -65,7 +70,7 @@ export const useSharedCache = <TValue: ValidCacheData>(
65
70
  // Memoize our APIs.
66
71
  // This one allows callers to set or replace the cached value.
67
72
  const cacheValue = React.useCallback(
68
- (value: ?TValue) =>
73
+ (value?: TValue | null) =>
69
74
  value == null
70
75
  ? cache.purge(scope, id)
71
76
  : cache.set(scope, id, value),
@@ -76,8 +81,8 @@ export const useSharedCache = <TValue: ValidCacheData>(
76
81
  // since our last run through. Also, our cache does not know what type it
77
82
  // stores, so we have to cast it to the type we're exporting. This is a
78
83
  // dev time courtesy, rather than a runtime thing.
79
- // $FlowIgnore[incompatible-type]
80
- let currentValue: ?TValue = cache.get(scope, id);
84
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'ValidCacheData | null | undefined' is not assignable to type 'TValue | null | undefined'.
85
+ let currentValue: TValue | null | undefined = cache.get(scope, id);
81
86
 
82
87
  // If we have an initial value, we need to add it to the cache
83
88
  // and use it as our current value.
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  export {FetchPolicy} from "./util/types";
3
2
  export type {
4
3
  ErrorOptions,
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import {getGqlDataFromResponse} from "../get-gql-data-from-response";
3
2
 
4
3
  describe("#getGqlDataFromReponse", () => {
@@ -1,5 +1,3 @@
1
- // @flow
2
-
3
1
  import {getGqlRequestId} from "../get-gql-request-id";
4
2
 
5
3
  describe("#getGqlRequestId", () => {
@@ -8,7 +6,7 @@ describe("#getGqlRequestId", () => {
8
6
  const operation = {
9
7
  type: "query",
10
8
  id: "myQuery",
11
- };
9
+ } as const;
12
10
 
13
11
  // Act
14
12
  const requestId = getGqlRequestId(operation, null, {
@@ -27,12 +25,12 @@ describe("#getGqlRequestId", () => {
27
25
  const operation = {
28
26
  type: "query",
29
27
  id: "myQuery",
30
- };
28
+ } as const;
31
29
  const context = {
32
30
  context3: "value3",
33
31
  context2: "value2",
34
32
  context1: "value1",
35
- };
33
+ } as const;
36
34
 
37
35
  // Act
38
36
  const requestId = getGqlRequestId(operation, null, context);
@@ -49,14 +47,14 @@ describe("#getGqlRequestId", () => {
49
47
  const operation = {
50
48
  type: "query",
51
49
  id: "myQuery",
52
- };
50
+ } as const;
53
51
  const variables = {
54
52
  variable4: null,
55
53
  variable2: 42,
56
54
  variable1: "value1",
57
55
  variable5: true,
58
56
  variable3: undefined,
59
- };
57
+ } as const;
60
58
 
61
59
  // Act
62
60
  const requestId = getGqlRequestId(operation, variables, {
@@ -77,7 +75,7 @@ describe("#getGqlRequestId", () => {
77
75
  const operation = {
78
76
  type: "query",
79
77
  id: "myQuery",
80
- };
78
+ } as const;
81
79
  const variables = {
82
80
  variable4: null,
83
81
  variable2: 42,
@@ -89,7 +87,7 @@ describe("#getGqlRequestId", () => {
89
87
  nested1: "nested1",
90
88
  },
91
89
  variable7: [1, 2, 3],
92
- };
90
+ } as const;
93
91
 
94
92
  // Act
95
93
  const requestId = getGqlRequestId(operation, variables, {
@@ -110,13 +108,13 @@ describe("#getGqlRequestId", () => {
110
108
  const operation = {
111
109
  type: "query",
112
110
  id: "myQuery",
113
- };
111
+ } as const;
114
112
  const variables = {
115
113
  variable1: {
116
114
  date: new Date("2020-01-01"),
117
115
  error: new Error("BOOM!"),
118
116
  },
119
- };
117
+ } as const;
120
118
 
121
119
  // Act
122
120
  const requestId = getGqlRequestId(operation, variables, {