@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.
- package/CHANGELOG.md +24 -0
- package/dist/components/data.d.ts +52 -0
- package/dist/components/data.js.flow +63 -0
- package/dist/components/gql-router.d.ts +24 -0
- package/dist/components/gql-router.js.flow +33 -0
- package/dist/components/intercept-context.d.ts +10 -0
- package/dist/components/intercept-context.js.flow +19 -0
- package/dist/components/intercept-requests.d.ts +42 -0
- package/dist/components/intercept-requests.js.flow +51 -0
- package/dist/components/track-data.d.ts +11 -0
- package/dist/components/track-data.js.flow +18 -0
- package/dist/es/index.js +184 -212
- package/dist/hooks/use-cached-effect.d.ts +70 -0
- package/dist/hooks/use-cached-effect.js.flow +85 -0
- package/dist/hooks/use-gql-router-context.d.ts +5 -0
- package/dist/hooks/use-gql-router-context.js.flow +15 -0
- package/dist/hooks/use-gql.d.ts +12 -0
- package/dist/hooks/use-gql.js.flow +29 -0
- package/dist/hooks/use-hydratable-effect.d.ts +102 -0
- package/dist/hooks/use-hydratable-effect.js.flow +125 -0
- package/dist/hooks/use-request-interception.d.ts +14 -0
- package/dist/hooks/use-request-interception.js.flow +25 -0
- package/dist/hooks/use-server-effect.d.ts +39 -0
- package/dist/hooks/use-server-effect.js.flow +51 -0
- package/dist/hooks/use-shared-cache.d.ts +32 -0
- package/dist/hooks/use-shared-cache.js.flow +43 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +186 -217
- package/dist/index.js.flow +48 -2
- package/dist/util/data-error.d.ts +45 -0
- package/dist/util/data-error.js.flow +64 -0
- package/dist/util/get-gql-data-from-response.d.ts +4 -0
- package/dist/util/get-gql-data-from-response.js.flow +13 -0
- package/dist/util/get-gql-request-id.d.ts +5 -0
- package/dist/util/get-gql-request-id.js.flow +20 -0
- package/dist/util/gql-error.d.ts +28 -0
- package/dist/util/gql-error.js.flow +43 -0
- package/dist/util/gql-router-context.d.ts +3 -0
- package/dist/util/gql-router-context.js.flow +10 -0
- package/dist/util/gql-types.d.ts +34 -0
- package/dist/util/gql-types.js.flow +53 -0
- package/dist/util/graphql-document-node-parser.d.ts +18 -0
- package/dist/util/graphql-document-node-parser.js.flow +31 -0
- package/dist/util/graphql-types.d.ts +19 -0
- package/dist/util/graphql-types.js.flow +30 -0
- package/dist/util/hydration-cache-api.d.ts +17 -0
- package/dist/util/hydration-cache-api.js.flow +30 -0
- package/dist/util/merge-gql-context.d.ts +8 -0
- package/dist/util/merge-gql-context.js.flow +19 -0
- package/dist/util/purge-caches.d.ts +8 -0
- package/dist/util/purge-caches.js.flow +15 -0
- package/dist/util/request-api.d.ts +28 -0
- package/dist/util/request-api.js.flow +34 -0
- package/dist/util/request-fulfillment.d.ts +37 -0
- package/dist/util/request-fulfillment.js.flow +50 -0
- package/dist/util/request-tracking.d.ts +62 -0
- package/dist/util/request-tracking.js.flow +81 -0
- package/dist/util/result-from-cache-response.d.ts +5 -0
- package/dist/util/result-from-cache-response.js.flow +15 -0
- package/dist/util/scoped-in-memory-cache.d.ts +38 -0
- package/dist/util/scoped-in-memory-cache.js.flow +57 -0
- package/dist/util/serializable-in-memory-cache.d.ts +16 -0
- package/dist/util/serializable-in-memory-cache.js.flow +26 -0
- package/dist/util/ssr-cache.d.ts +51 -0
- package/dist/util/ssr-cache.js.flow +87 -0
- package/dist/util/status.d.ts +10 -0
- package/dist/util/status.js.flow +19 -0
- package/dist/util/to-gql-operation.d.ts +32 -0
- package/dist/util/to-gql-operation.js.flow +45 -0
- package/dist/util/types.d.ts +111 -0
- package/dist/util/types.js.flow +151 -0
- package/package.json +5 -6
- package/src/components/__tests__/{data.test.js → data.test.tsx} +42 -2
- package/src/components/__tests__/{gql-router.test.js → gql-router.test.tsx} +4 -5
- package/src/components/__tests__/{intercept-requests.test.js → intercept-requests.test.tsx} +2 -3
- package/src/components/__tests__/{track-data.test.js → track-data.test.tsx} +2 -3
- package/src/components/{data.js → data.ts} +11 -15
- package/src/components/{gql-router.js → gql-router.tsx} +12 -14
- package/src/components/{intercept-context.js → intercept-context.ts} +4 -3
- package/src/components/{intercept-requests.js → intercept-requests.tsx} +7 -8
- package/src/components/{track-data.js → track-data.tsx} +4 -5
- package/src/hooks/__tests__/{use-cached-effect.test.js → use-cached-effect.test.tsx} +55 -50
- package/src/hooks/__tests__/{use-gql-router-context.test.js → use-gql-router-context.test.tsx} +7 -7
- package/src/hooks/__tests__/{use-gql.test.js → use-gql.test.tsx} +20 -21
- package/src/hooks/__tests__/{use-hydratable-effect.test.js → use-hydratable-effect.test.ts} +42 -37
- package/src/hooks/__tests__/{use-request-interception.test.js → use-request-interception.test.tsx} +5 -3
- package/src/hooks/__tests__/{use-server-effect.test.js → use-server-effect.test.ts} +8 -2
- package/src/hooks/__tests__/{use-shared-cache.test.js → use-shared-cache.test.ts} +12 -12
- package/src/hooks/{use-cached-effect.js → use-cached-effect.ts} +27 -20
- package/src/hooks/{use-gql-router-context.js → use-gql-router-context.ts} +2 -3
- package/src/hooks/{use-gql.js → use-gql.ts} +5 -5
- package/src/hooks/{use-hydratable-effect.js → use-hydratable-effect.ts} +53 -58
- package/src/hooks/{use-request-interception.js → use-request-interception.ts} +4 -4
- package/src/hooks/{use-server-effect.js → use-server-effect.ts} +7 -9
- package/src/hooks/{use-shared-cache.js → use-shared-cache.ts} +13 -8
- package/src/{index.js → index.ts} +0 -1
- package/src/util/__tests__/{get-gql-data-from-response.test.js → get-gql-data-from-response.test.ts} +0 -1
- package/src/util/__tests__/{get-gql-request-id.test.js → get-gql-request-id.test.ts} +9 -11
- package/src/util/__tests__/{graphql-document-node-parser.test.js → graphql-document-node-parser.test.ts} +11 -12
- package/src/util/__tests__/{hydration-cache-api.test.js → hydration-cache-api.test.ts} +1 -2
- package/src/util/__tests__/{merge-gql-context.test.js → merge-gql-context.test.ts} +4 -5
- package/src/util/__tests__/{purge-caches.test.js → purge-caches.test.ts} +0 -1
- package/src/util/__tests__/{request-api.test.js → request-api.test.ts} +2 -2
- package/src/util/__tests__/{request-fulfillment.test.js → request-fulfillment.test.ts} +0 -1
- package/src/util/__tests__/{request-tracking.test.js → request-tracking.test.tsx} +13 -6
- package/src/util/__tests__/{result-from-cache-response.test.js → result-from-cache-response.test.ts} +2 -4
- package/src/util/__tests__/{scoped-in-memory-cache.test.js → scoped-in-memory-cache.test.ts} +4 -5
- package/src/util/__tests__/{serializable-in-memory-cache.test.js → serializable-in-memory-cache.test.ts} +7 -7
- package/src/util/__tests__/{ssr-cache.test.js → ssr-cache.test.ts} +3 -2
- package/src/util/__tests__/{to-gql-operation.test.js → to-gql-operation.test.ts} +2 -1
- package/src/util/{data-error.js → data-error.ts} +2 -3
- package/src/util/{get-gql-data-from-response.js → get-gql-data-from-response.ts} +1 -6
- package/src/util/{get-gql-request-id.js → get-gql-request-id.ts} +12 -16
- package/src/util/{gql-error.js → gql-error.ts} +2 -3
- package/src/util/gql-router-context.ts +6 -0
- package/src/util/{gql-types.js → gql-types.ts} +27 -23
- package/src/util/{graphql-document-node-parser.js → graphql-document-node-parser.ts} +6 -7
- package/src/util/graphql-types.ts +27 -0
- package/src/util/{hydration-cache-api.js → hydration-cache-api.ts} +4 -2
- package/src/util/{merge-gql-context.js → merge-gql-context.ts} +2 -2
- package/src/util/{purge-caches.js → purge-caches.ts} +0 -1
- package/src/util/{request-api.js → request-api.ts} +0 -1
- package/src/util/{request-fulfillment.js → request-fulfillment.ts} +13 -12
- package/src/util/{request-tracking.js → request-tracking.ts} +12 -13
- package/src/util/{result-from-cache-response.js → result-from-cache-response.ts} +3 -4
- package/src/util/{scoped-in-memory-cache.js → scoped-in-memory-cache.ts} +1 -2
- package/src/util/{serializable-in-memory-cache.js → serializable-in-memory-cache.ts} +2 -3
- package/src/util/{ssr-cache.js → ssr-cache.ts} +19 -18
- package/src/util/{status.js → status.ts} +4 -5
- package/src/util/{to-gql-operation.js → to-gql-operation.ts} +1 -2
- package/src/util/{types.js → types.ts} +39 -48
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/__docs__/_overview_.stories.mdx +0 -18
- package/src/__docs__/_overview_graphql.stories.mdx +0 -35
- package/src/__docs__/_overview_ssr_.stories.mdx +0 -185
- package/src/__docs__/_overview_testing_.stories.mdx +0 -123
- package/src/__docs__/exports.abort-inflight-requests.stories.mdx +0 -20
- package/src/__docs__/exports.data-error.stories.mdx +0 -23
- package/src/__docs__/exports.data-errors.stories.mdx +0 -23
- package/src/__docs__/exports.data.stories.mdx +0 -146
- package/src/__docs__/exports.fetch-tracked-requests.stories.mdx +0 -24
- package/src/__docs__/exports.get-gql-request-id.stories.mdx +0 -24
- package/src/__docs__/exports.gql-error.stories.mdx +0 -23
- package/src/__docs__/exports.gql-errors.stories.mdx +0 -20
- package/src/__docs__/exports.gql-router.stories.mdx +0 -29
- package/src/__docs__/exports.has-tracked-requests-to-be-fetched.stories.mdx +0 -20
- package/src/__docs__/exports.intercept-requests.stories.mdx +0 -69
- package/src/__docs__/exports.intialize-hydration-cache.stories.mdx +0 -29
- package/src/__docs__/exports.purge-caches.stories.mdx +0 -23
- package/src/__docs__/exports.purge-hydration-cache.stories.mdx +0 -24
- package/src/__docs__/exports.scoped-in-memory-cache.stories.mdx +0 -92
- package/src/__docs__/exports.serializable-in-memory-cache.stories.mdx +0 -112
- package/src/__docs__/exports.shared-cache.stories.mdx +0 -16
- package/src/__docs__/exports.status.stories.mdx +0 -31
- package/src/__docs__/exports.track-data.stories.mdx +0 -209
- package/src/__docs__/exports.use-cached-effect.stories.mdx +0 -44
- package/src/__docs__/exports.use-gql.stories.mdx +0 -41
- package/src/__docs__/exports.use-hydratable-effect.stories.mdx +0 -43
- package/src/__docs__/exports.use-server-effect.stories.mdx +0 -50
- package/src/__docs__/exports.use-shared-cache.stories.mdx +0 -30
- package/src/__docs__/exports.when-client-side.stories.mdx +0 -33
- package/src/__docs__/types.cached-response.stories.mdx +0 -29
- package/src/__docs__/types.error-options.stories.mdx +0 -21
- package/src/__docs__/types.fetch-policy.stories.mdx +0 -44
- package/src/__docs__/types.gql-context.stories.mdx +0 -20
- package/src/__docs__/types.gql-fetch-fn.stories.mdx +0 -24
- package/src/__docs__/types.gql-fetch-options.stories.mdx +0 -24
- package/src/__docs__/types.gql-operation-type.stories.mdx +0 -24
- package/src/__docs__/types.gql-operation.stories.mdx +0 -67
- package/src/__docs__/types.raw-scoped-cache.stories.mdx +0 -27
- package/src/__docs__/types.response-cache.stories.mdx +0 -33
- package/src/__docs__/types.result.stories.mdx +0 -39
- package/src/__docs__/types.scoped-cache.stories.mdx +0 -114
- package/src/__docs__/types.valid-cache-data.stories.mdx +0 -23
- package/src/util/gql-router-context.js +0 -6
- package/src/util/graphql-types.js +0 -30
- /package/src/hooks/__tests__/__snapshots__/{use-shared-cache.test.js.snap → use-shared-cache.test.ts.snap} +0 -0
- /package/src/util/__tests__/__snapshots__/{scoped-in-memory-cache.test.js.snap → scoped-in-memory-cache.test.ts.snap} +0 -0
- /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
|
|
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?:
|
|
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
|
|
84
|
+
export const useCachedEffect = <TData extends ValidCacheData>(
|
|
90
85
|
requestId: string,
|
|
91
86
|
handler: () => Promise<TData>,
|
|
92
|
-
options: CachedEffectOptions<TData> =
|
|
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
|
|
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
|
|
14
|
-
contextOverrides: Partial<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
|
|
25
|
-
context: Partial<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:
|
|
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:
|
|
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:
|
|
50
|
-
};
|
|
47
|
+
AlwaysExecute: "AlwaysExecute" as const,
|
|
48
|
+
} as const;
|
|
51
49
|
|
|
52
|
-
type HydratableEffectOptions<TData
|
|
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?:
|
|
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
|
|
117
|
+
export const useHydratableEffect = <TData extends ValidCacheData>(
|
|
124
118
|
requestId: string,
|
|
125
119
|
handler: () => Promise<TData>,
|
|
126
|
-
options: HydratableEffectOptions<TData> =
|
|
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: () =>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
188
|
-
scope,
|
|
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
|
|
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
|
|
46
|
-
//
|
|
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
|
|
47
|
+
export const useServerEffect = <TData extends ValidCacheData>(
|
|
50
48
|
requestId: string,
|
|
51
49
|
handler: () => Promise<TData>,
|
|
52
|
-
options: ServerEffectOptions =
|
|
53
|
-
):
|
|
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
|
|
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
|
|
46
|
+
export const useSharedCache = <TValue extends ValidCacheData>(
|
|
46
47
|
id: string,
|
|
47
48
|
scope: string,
|
|
48
|
-
initialValue?:
|
|
49
|
-
|
|
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
|
|
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
|
-
//
|
|
80
|
-
let currentValue:
|
|
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,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, {
|