@khanacademy/wonder-blocks-data 10.0.5 → 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 (181) hide show
  1. package/CHANGELOG.md +38 -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 +196 -214
  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 +198 -219
  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} +50 -16
  74. package/src/components/__tests__/{gql-router.test.js → gql-router.test.tsx} +6 -7
  75. package/src/components/__tests__/{intercept-requests.test.js → intercept-requests.test.tsx} +4 -5
  76. package/src/components/__tests__/{track-data.test.js → track-data.test.tsx} +4 -5
  77. package/src/components/{data.js → data.ts} +13 -21
  78. package/src/components/{gql-router.js → gql-router.tsx} +14 -16
  79. package/src/components/{intercept-context.js → intercept-context.ts} +5 -4
  80. package/src/components/{intercept-requests.js → intercept-requests.tsx} +9 -10
  81. package/src/components/{track-data.js → track-data.tsx} +5 -6
  82. package/src/hooks/__tests__/{use-cached-effect.test.js → use-cached-effect.test.tsx} +65 -63
  83. package/src/hooks/__tests__/{use-gql-router-context.test.js → use-gql-router-context.test.tsx} +9 -9
  84. package/src/hooks/__tests__/{use-gql.test.js → use-gql.test.tsx} +23 -24
  85. package/src/hooks/__tests__/{use-hydratable-effect.test.js → use-hydratable-effect.test.ts} +52 -54
  86. package/src/hooks/__tests__/{use-request-interception.test.js → use-request-interception.test.tsx} +7 -5
  87. package/src/hooks/__tests__/{use-server-effect.test.js → use-server-effect.test.ts} +16 -10
  88. package/src/hooks/__tests__/{use-shared-cache.test.js → use-shared-cache.test.ts} +13 -13
  89. package/src/hooks/{use-cached-effect.js → use-cached-effect.ts} +34 -31
  90. package/src/hooks/{use-gql-router-context.js → use-gql-router-context.ts} +6 -7
  91. package/src/hooks/{use-gql.js → use-gql.ts} +9 -9
  92. package/src/hooks/{use-hydratable-effect.js → use-hydratable-effect.ts} +60 -67
  93. package/src/hooks/{use-request-interception.js → use-request-interception.ts} +6 -6
  94. package/src/hooks/{use-server-effect.js → use-server-effect.ts} +12 -14
  95. package/src/hooks/{use-shared-cache.js → use-shared-cache.ts} +16 -11
  96. package/src/index.ts +46 -0
  97. package/src/util/__tests__/{get-gql-data-from-response.test.js → get-gql-data-from-response.test.ts} +1 -2
  98. package/src/util/__tests__/{get-gql-request-id.test.js → get-gql-request-id.test.ts} +10 -12
  99. package/src/util/__tests__/{graphql-document-node-parser.test.js → graphql-document-node-parser.test.ts} +12 -13
  100. package/src/util/__tests__/{hydration-cache-api.test.js → hydration-cache-api.test.ts} +3 -4
  101. package/src/util/__tests__/{merge-gql-context.test.js → merge-gql-context.test.ts} +5 -6
  102. package/src/util/__tests__/{purge-caches.test.js → purge-caches.test.ts} +3 -4
  103. package/src/util/__tests__/{request-api.test.js → request-api.test.ts} +5 -5
  104. package/src/util/__tests__/{request-fulfillment.test.js → request-fulfillment.test.ts} +2 -3
  105. package/src/util/__tests__/{request-tracking.test.js → request-tracking.test.tsx} +15 -8
  106. package/src/util/__tests__/{result-from-cache-response.test.js → result-from-cache-response.test.ts} +3 -5
  107. package/src/util/__tests__/{scoped-in-memory-cache.test.js → scoped-in-memory-cache.test.ts} +5 -6
  108. package/src/util/__tests__/{serializable-in-memory-cache.test.js → serializable-in-memory-cache.test.ts} +8 -8
  109. package/src/util/__tests__/{ssr-cache.test.js → ssr-cache.test.ts} +5 -4
  110. package/src/util/__tests__/{to-gql-operation.test.js → to-gql-operation.test.ts} +5 -4
  111. package/src/util/{data-error.js → data-error.ts} +3 -4
  112. package/src/util/{get-gql-data-from-response.js → get-gql-data-from-response.ts} +3 -8
  113. package/src/util/{get-gql-request-id.js → get-gql-request-id.ts} +13 -17
  114. package/src/util/{gql-error.js → gql-error.ts} +3 -4
  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} +8 -9
  118. package/src/util/graphql-types.ts +27 -0
  119. package/src/util/{hydration-cache-api.js → hydration-cache-api.ts} +6 -4
  120. package/src/util/{merge-gql-context.js → merge-gql-context.ts} +3 -3
  121. package/src/util/{purge-caches.js → purge-caches.ts} +2 -3
  122. package/src/util/{request-api.js → request-api.ts} +4 -5
  123. package/src/util/{request-fulfillment.js → request-fulfillment.ts} +15 -14
  124. package/src/util/{request-tracking.js → request-tracking.ts} +15 -16
  125. package/src/util/{result-from-cache-response.js → result-from-cache-response.ts} +6 -7
  126. package/src/util/{scoped-in-memory-cache.js → scoped-in-memory-cache.ts} +3 -4
  127. package/src/util/{serializable-in-memory-cache.js → serializable-in-memory-cache.ts} +5 -6
  128. package/src/util/{ssr-cache.js → ssr-cache.ts} +21 -20
  129. package/src/util/{status.js → status.ts} +5 -6
  130. package/src/util/{to-gql-operation.js → to-gql-operation.ts} +4 -5
  131. package/src/util/{types.js → types.ts} +41 -49
  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/index.js +0 -55
  177. package/src/util/gql-router-context.js +0 -6
  178. package/src/util/graphql-types.js +0 -30
  179. /package/src/hooks/__tests__/__snapshots__/{use-shared-cache.test.js.snap → use-shared-cache.test.ts.snap} +0 -0
  180. /package/src/util/__tests__/__snapshots__/{scoped-in-memory-cache.test.js.snap → scoped-in-memory-cache.test.ts.snap} +0 -0
  181. /package/src/util/__tests__/__snapshots__/{serializable-in-memory-cache.test.js.snap → serializable-in-memory-cache.test.ts.snap} +0 -0
@@ -1,31 +1,25 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
  import {useForceUpdate} from "@khanacademy/wonder-blocks-core";
4
- import {DataError, DataErrors} from "../util/data-error.js";
3
+ import {DataError, DataErrors} from "../util/data-error";
5
4
 
6
- import {RequestFulfillment} from "../util/request-fulfillment.js";
7
- import {Status} from "../util/status.js";
5
+ import {RequestFulfillment} from "../util/request-fulfillment";
6
+ import {Status} from "../util/status";
8
7
 
9
- import {useSharedCache} from "./use-shared-cache.js";
10
- import {useRequestInterception} from "./use-request-interception.js";
8
+ import {useSharedCache} from "./use-shared-cache";
9
+ import {useRequestInterception} from "./use-request-interception";
11
10
 
12
- import type {Result, ValidCacheData} from "../util/types.js";
11
+ import type {Result, ValidCacheData} from "../util/types";
13
12
 
14
- // TODO(somewhatabstract, FEI-4174): Update eslint-plugin-import when they
15
- // have fixed:
16
- // https://github.com/import-js/eslint-plugin-import/issues/2073
17
- // eslint-disable-next-line import/named
18
- import {FetchPolicy} from "../util/types.js";
13
+ import {FetchPolicy} from "../util/types";
19
14
 
20
- type CachedEffectOptions<TData: ValidCacheData> = {|
15
+ type CachedEffectOptions<TData extends ValidCacheData> = {
21
16
  /**
22
17
  * The policy to use when determining how to retrieve the request data from
23
18
  * cache and network.
24
19
  *
25
20
  * Defaults to `FetchPolicy.CacheBeforeNetwork`.
26
21
  */
27
- fetchPolicy?: FetchPolicy,
28
-
22
+ fetchPolicy?: typeof FetchPolicy[keyof typeof FetchPolicy];
29
23
  /**
30
24
  * When `true`, the effect will not be executed; otherwise, the effect will
31
25
  * be executed.
@@ -35,8 +29,7 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
35
29
  *
36
30
  * Default is `false`.
37
31
  */
38
- skip?: boolean,
39
-
32
+ skip?: boolean;
40
33
  /**
41
34
  * When `true`, the effect will not reset the result to the loading status
42
35
  * while executing if the requestId changes, instead, returning
@@ -47,8 +40,7 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
47
40
  * loading; old pending effects are discarded on changes and as such this
48
41
  * value has no effect in that case.
49
42
  */
50
- retainResultOnChange?: boolean,
51
-
43
+ retainResultOnChange?: boolean;
52
44
  /**
53
45
  * Callback that is invoked if the result for the given hook has changed.
54
46
  *
@@ -59,8 +51,7 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
59
51
  * When not defined, the hook will ensure the component re-renders to pick
60
52
  * up the latest result.
61
53
  */
62
- onResultChanged?: (result: Result<TData>) => void,
63
-
54
+ onResultChanged?: (result: Result<TData>) => void;
64
55
  /**
65
56
  * Scope to use with the shared cache.
66
57
  *
@@ -69,8 +60,8 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
69
60
  *
70
61
  * Changing this value after the first call is not supported.
71
62
  */
72
- scope?: string,
73
- |};
63
+ scope?: string;
64
+ };
74
65
 
75
66
  const DefaultScope = "useCachedEffect";
76
67
 
@@ -90,12 +81,12 @@ const DefaultScope = "useCachedEffect";
90
81
  * Once the request has been tried once and a non-loading response has been
91
82
  * cached, the request will not executed made again.
92
83
  */
93
- export const useCachedEffect = <TData: ValidCacheData>(
84
+ export const useCachedEffect = <TData extends ValidCacheData>(
94
85
  requestId: string,
95
86
  handler: () => Promise<TData>,
96
- options: CachedEffectOptions<TData> = ({}: $Shape<
97
- CachedEffectOptions<TData>,
98
- >),
87
+ options: CachedEffectOptions<TData> = {} as Partial<
88
+ CachedEffectOptions<TData>
89
+ >,
99
90
  ): [Result<TData>, () => void] => {
100
91
  const {
101
92
  fetchPolicy = FetchPolicy.CacheBeforeNetwork,
@@ -112,13 +103,13 @@ export const useCachedEffect = <TData: ValidCacheData>(
112
103
  // Instead of using state, which would be local to just this hook instance,
113
104
  // we use a shared in-memory cache.
114
105
  const [mostRecentResult, setMostRecentResult] = useSharedCache<
115
- Result<TData>,
116
- >(
117
- requestId, // The key of the cached item
118
- 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
119
109
  // No default value. We don't want the loading status there; to ensure
120
110
  // that all calls when the request is in-flight will update once that
121
111
  // request is done, we want the cache to be empty until that point.
112
+ scope,
122
113
  );
123
114
  const forceUpdate = useForceUpdate();
124
115
  // For the NetworkOnly fetch policy, we ignore the cached value.
@@ -131,8 +122,11 @@ export const useCachedEffect = <TData: ValidCacheData>(
131
122
  // We aren't using useCallback here because we need to make sure that
132
123
  // if we are rememo-izing, we cancel any inflight request for the old
133
124
  // callback.
125
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'cancel' does not exist on type 'never'.
134
126
  currentRequestRef.current?.cancel();
127
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
135
128
  currentRequestRef.current = null;
129
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
136
130
  networkResultRef.current = null;
137
131
 
138
132
  const fetchFn = () => {
@@ -159,6 +153,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
159
153
  },
160
154
  );
161
155
 
156
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'request' does not exist on type 'never'.
162
157
  if (request === currentRequestRef.current?.request) {
163
158
  // The request inflight is the same, so do nothing.
164
159
  // NOTE: Perhaps if invoked via a refetch, we will want to
@@ -167,9 +162,11 @@ export const useCachedEffect = <TData: ValidCacheData>(
167
162
  }
168
163
 
169
164
  // Clear the last network result.
165
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
170
166
  networkResultRef.current = null;
171
167
 
172
168
  // Cancel the previous request.
169
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'cancel' does not exist on type 'never'.
173
170
  currentRequestRef.current?.cancel();
174
171
 
175
172
  // TODO(somewhatabstract, FEI-4276):
@@ -181,6 +178,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
181
178
  // Catching shouldn't serve a purpose.
182
179
  // eslint-disable-next-line promise/catch-or-return
183
180
  request.then((result) => {
181
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
184
182
  currentRequestRef.current = null;
185
183
  if (cancel) {
186
184
  // We don't modify our result if the request was cancelled
@@ -191,6 +189,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
191
189
 
192
190
  // Now we need to update the cache and notify or force a rerender.
193
191
  setMostRecentResult(result);
192
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'Result<TData>' is not assignable to type 'undefined'.
194
193
  networkResultRef.current = result;
195
194
 
196
195
  if (onResultChanged != null) {
@@ -205,6 +204,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
205
204
  return; // Shut up eslint always-return rule.
206
205
  });
207
206
 
207
+ // @ts-expect-error [FEI-5019] - TS2322 - Type '{ requestId: string; request: Promise<Result<TData>>; cancel(): void; }' is not assignable to type 'undefined'.
208
208
  currentRequestRef.current = {
209
209
  requestId,
210
210
  request,
@@ -265,7 +265,9 @@ export const useCachedEffect = <TData: ValidCacheData>(
265
265
  }
266
266
  fetchRequest();
267
267
  return () => {
268
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'cancel' does not exist on type 'never'.
268
269
  currentRequestRef.current?.cancel();
270
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'null' is not assignable to type 'undefined'.
269
271
  currentRequestRef.current = null;
270
272
  };
271
273
  }, [shouldFetch, fetchRequest]);
@@ -286,5 +288,6 @@ export const useCachedEffect = <TData: ValidCacheData>(
286
288
  lastResultAgnosticOfIdRef.current = result;
287
289
 
288
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>'.
289
292
  return [result, fetchRequest];
290
293
  };
@@ -1,17 +1,16 @@
1
- // @flow
2
1
  import {useContext, useRef, useMemo} from "react";
3
2
 
4
- import {mergeGqlContext} from "../util/merge-gql-context.js";
5
- import {GqlRouterContext} from "../util/gql-router-context.js";
6
- import {GqlError, GqlErrors} from "../util/gql-error.js";
3
+ import {mergeGqlContext} from "../util/merge-gql-context";
4
+ import {GqlRouterContext} from "../util/gql-router-context";
5
+ import {GqlError, GqlErrors} from "../util/gql-error";
7
6
 
8
- import type {GqlRouterConfiguration, GqlContext} from "../util/gql-types.js";
7
+ import type {GqlRouterConfiguration, GqlContext} from "../util/gql-types";
9
8
 
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,15 +1,14 @@
1
- // @flow
2
1
  import {useCallback} from "react";
3
2
 
4
- import {mergeGqlContext} from "../util/merge-gql-context.js";
5
- import {useGqlRouterContext} from "./use-gql-router-context.js";
6
- import {getGqlDataFromResponse} from "../util/get-gql-data-from-response.js";
3
+ import {mergeGqlContext} from "../util/merge-gql-context";
4
+ import {useGqlRouterContext} from "./use-gql-router-context";
5
+ import {getGqlDataFromResponse} from "../util/get-gql-data-from-response";
7
6
 
8
7
  import type {
9
8
  GqlContext,
10
9
  GqlOperation,
11
10
  GqlFetchOptions,
12
- } from "../util/gql-types.js";
11
+ } from "../util/gql-types";
13
12
 
14
13
  /**
15
14
  * Hook to obtain a gqlFetch function for performing GraphQL requests.
@@ -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,21 +1,17 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
- import {useServerEffect} from "./use-server-effect.js";
5
- import {useSharedCache} from "./use-shared-cache.js";
6
- import {useCachedEffect} from "./use-cached-effect.js";
3
+ import {useServerEffect} from "./use-server-effect";
4
+ import {useSharedCache} from "./use-shared-cache";
5
+ import {useCachedEffect} from "./use-cached-effect";
7
6
 
8
- // TODO(somewhatabstract, FEI-4174): Update eslint-plugin-import when they
9
- // have fixed:
10
- // https://github.com/import-js/eslint-plugin-import/issues/2073
11
- // eslint-disable-next-line import/named
12
- import {FetchPolicy} from "../util/types.js";
13
- import type {Result, ValidCacheData} from "../util/types.js";
7
+ import {FetchPolicy} from "../util/types";
8
+ import type {Result, ValidCacheData} from "../util/types";
14
9
 
15
10
  /**
16
11
  * Policies to define how a hydratable effect should behave client-side.
17
12
  */
18
- export enum WhenClientSide {
13
+ // TODO(FEI-5000): Convert to TS enum after all codebases have been migrated
14
+ export const WhenClientSide = {
19
15
  /**
20
16
  * The result from executing the effect server-side will not be hydrated.
21
17
  * The effect will always be executed client-side.
@@ -24,7 +20,7 @@ export enum WhenClientSide {
24
20
  * for properly hydrating this component (for example, the action invokes
25
21
  * Apollo which manages its own cache to ensure things render properly).
26
22
  */
27
- DoNotHydrate,
23
+ DoNotHydrate: "DoNotHydrate" as const,
28
24
 
29
25
  /**
30
26
  * The result from executing the effect server-side will be hydrated.
@@ -32,7 +28,7 @@ export enum WhenClientSide {
32
28
  * be hydrated (i.e. both error and success hydration results prevent the
33
29
  * effect running client-side).
34
30
  */
35
- ExecuteWhenNoResult,
31
+ ExecuteWhenNoResult: "ExecuteWhenNoResult" as const,
36
32
 
37
33
  /**
38
34
  * The result from executing the effect server-side will be hydrated.
@@ -41,17 +37,17 @@ export enum WhenClientSide {
41
37
  * If the hydrated result was not a success result, or there was no
42
38
  * hydrated result, the effect will not be executed.
43
39
  */
44
- ExecuteWhenNoSuccessResult,
40
+ ExecuteWhenNoSuccessResult: "ExecuteWhenNoSuccessResult" as const,
45
41
 
46
42
  /**
47
43
  * The result from executing the effect server-side will be hydrated.
48
44
  * The effect will always be executed client-side, regardless of the
49
45
  * hydrated result status.
50
46
  */
51
- AlwaysExecute,
52
- }
47
+ AlwaysExecute: "AlwaysExecute" as const,
48
+ } as const;
53
49
 
54
- type HydratableEffectOptions<TData: ValidCacheData> = {|
50
+ type HydratableEffectOptions<TData extends ValidCacheData> = {
55
51
  /**
56
52
  * How the hook should behave when rendering client-side for the first time.
57
53
  *
@@ -62,8 +58,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
62
58
  * Changing this value after the first call is irrelevant as it only
63
59
  * affects the initial render behavior.
64
60
  */
65
- clientBehavior?: WhenClientSide,
66
-
61
+ clientBehavior?: typeof WhenClientSide[keyof typeof WhenClientSide];
67
62
  /**
68
63
  * When `true`, the effect will not be executed; otherwise, the effect will
69
64
  * be executed.
@@ -73,8 +68,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
73
68
  *
74
69
  * Default is `false`.
75
70
  */
76
- skip?: boolean,
77
-
71
+ skip?: boolean;
78
72
  /**
79
73
  * When `true`, the effect will not reset the result to the loading status
80
74
  * while executing if the requestId changes, instead, returning
@@ -85,8 +79,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
85
79
  * loading; old pending effects are discarded on changes and as such this
86
80
  * value has no effect in that case.
87
81
  */
88
- retainResultOnChange?: boolean,
89
-
82
+ retainResultOnChange?: boolean;
90
83
  /**
91
84
  * Callback that is invoked if the result for the given hook has changed.
92
85
  *
@@ -97,8 +90,7 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
97
90
  * When not defined, the hook will ensure the component re-renders to pick
98
91
  * up the latest result.
99
92
  */
100
- onResultChanged?: (result: Result<TData>) => void,
101
-
93
+ onResultChanged?: (result: Result<TData>) => void;
102
94
  /**
103
95
  * Scope to use with the shared cache.
104
96
  *
@@ -107,8 +99,8 @@ type HydratableEffectOptions<TData: ValidCacheData> = {|
107
99
  *
108
100
  * Changing this value after the first call is not supported.
109
101
  */
110
- scope?: string,
111
- |};
102
+ scope?: string;
103
+ };
112
104
 
113
105
  const DefaultScope = "useHydratableEffect";
114
106
 
@@ -122,12 +114,12 @@ const DefaultScope = "useHydratableEffect";
122
114
  * invocations. Cache changes from one hook instance do not trigger renders
123
115
  * in components that use the same requestID.
124
116
  */
125
- export const useHydratableEffect = <TData: ValidCacheData>(
117
+ export const useHydratableEffect = <TData extends ValidCacheData>(
126
118
  requestId: string,
127
119
  handler: () => Promise<TData>,
128
- options: HydratableEffectOptions<TData> = ({}: $Shape<
129
- HydratableEffectOptions<TData>,
130
- >),
120
+ options: HydratableEffectOptions<TData> = {} as Partial<
121
+ HydratableEffectOptions<TData>
122
+ >,
131
123
  ): Result<TData> => {
132
124
  const {
133
125
  clientBehavior = WhenClientSide.ExecuteWhenNoSuccessResult,
@@ -146,48 +138,49 @@ export const useHydratableEffect = <TData: ValidCacheData>(
146
138
  skip,
147
139
  });
148
140
 
149
- const getDefaultCacheValue: () => ?Result<TData> = React.useCallback(() => {
150
- // If we don't have a requestId, it's our first render, the one
151
- // where we hydrated. So defer to our clientBehavior value.
152
- switch (clientBehavior) {
153
- case WhenClientSide.DoNotHydrate:
154
- case WhenClientSide.AlwaysExecute:
155
- // Either we weren't hydrating at all, or we don't care
156
- // if we hydrated something or not, either way, we're
157
- // doing a request.
158
- return null;
159
-
160
- case WhenClientSide.ExecuteWhenNoResult:
161
- // We only execute if we didn't hydrate something.
162
- // So, returning the hydration result as default for our
163
- // cache, will then prevent the cached effect running.
164
- return serverResult;
165
-
166
- case WhenClientSide.ExecuteWhenNoSuccessResult:
167
- // We only execute if we didn't hydrate a success result.
168
- 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.
169
155
  // So, returning the hydration result as default for our
170
156
  // cache, will then prevent the cached effect running.
171
157
  return serverResult;
172
- }
173
- return null;
174
- }
175
- // There is no reason for this to change after the first render,
176
- // you might think, but the function closes around serverResult and if
177
- // the requestId changes, it still returns the hydrate result of the
178
- // first render of the previous requestId. This then means that the
179
- // hydrate result is still the same, and the effect is not re-executed
180
- // because the cache gets incorrectly defaulted.
181
- // However, we don't want to bother doing anything with this on
182
- // client behavior changing since that truly is irrelevant.
183
- // eslint-disable-next-line react-hooks/exhaustive-deps
184
- }, [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]);
185
178
 
186
179
  // Instead of using state, which would be local to just this hook instance,
187
180
  // we use a shared in-memory cache.
188
- useSharedCache<Result<TData>>(
189
- requestId, // The key of the cached item
190
- 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,
191
184
  getDefaultCacheValue,
192
185
  );
193
186
 
@@ -1,8 +1,7 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
- import InterceptContext from "../components/intercept-context.js";
5
- import type {ValidCacheData} from "../util/types.js";
3
+ import InterceptContext from "../components/intercept-context";
4
+ import type {ValidCacheData} from "../util/types";
6
5
 
7
6
  /**
8
7
  * Allow request handling to be intercepted.
@@ -16,7 +15,7 @@ import type {ValidCacheData} from "../util/types.js";
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,14 +1,13 @@
1
- // @flow
2
1
  import {Server} from "@khanacademy/wonder-blocks-core";
3
2
  import {useContext} from "react";
4
- import {TrackerContext} from "../util/request-tracking.js";
5
- import {SsrCache} from "../util/ssr-cache.js";
6
- import {resultFromCachedResponse} from "../util/result-from-cache-response.js";
7
- import {useRequestInterception} from "./use-request-interception.js";
3
+ import {TrackerContext} from "../util/request-tracking";
4
+ import {SsrCache} from "../util/ssr-cache";
5
+ import {resultFromCachedResponse} from "../util/result-from-cache-response";
6
+ import {useRequestInterception} from "./use-request-interception";
8
7
 
9
- import type {Result, ValidCacheData} from "../util/types.js";
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,13 +1,14 @@
1
- // @flow
2
1
  import * as React from "react";
3
- import {DataError, DataErrors} from "../util/data-error.js";
4
- import {ScopedInMemoryCache} from "../util/scoped-in-memory-cache.js";
5
- import type {ValidCacheData, ScopedCache} from "../util/types.js";
2
+ import {DataError, DataErrors} from "../util/data-error";
3
+ import {ScopedInMemoryCache} from "../util/scoped-in-memory-cache";
4
+ import type {ValidCacheData, ScopedCache} from "../util/types";
6
5
 
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.