@khanacademy/wonder-blocks-data 13.0.11 → 14.0.0

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 (66) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +5 -5
  3. package/src/components/__tests__/data.test.tsx +0 -832
  4. package/src/components/__tests__/gql-router.test.tsx +0 -63
  5. package/src/components/__tests__/intercept-requests.test.tsx +0 -57
  6. package/src/components/__tests__/track-data.test.tsx +0 -56
  7. package/src/components/data.ts +0 -73
  8. package/src/components/gql-router.tsx +0 -63
  9. package/src/components/intercept-context.ts +0 -19
  10. package/src/components/intercept-requests.tsx +0 -67
  11. package/src/components/track-data.tsx +0 -28
  12. package/src/hooks/__tests__/__snapshots__/use-shared-cache.test.ts.snap +0 -17
  13. package/src/hooks/__tests__/use-cached-effect.test.tsx +0 -789
  14. package/src/hooks/__tests__/use-gql-router-context.test.tsx +0 -132
  15. package/src/hooks/__tests__/use-gql.test.tsx +0 -204
  16. package/src/hooks/__tests__/use-hydratable-effect.test.ts +0 -708
  17. package/src/hooks/__tests__/use-request-interception.test.tsx +0 -254
  18. package/src/hooks/__tests__/use-server-effect.test.ts +0 -293
  19. package/src/hooks/__tests__/use-shared-cache.test.ts +0 -263
  20. package/src/hooks/use-cached-effect.ts +0 -297
  21. package/src/hooks/use-gql-router-context.ts +0 -49
  22. package/src/hooks/use-gql.ts +0 -58
  23. package/src/hooks/use-hydratable-effect.ts +0 -201
  24. package/src/hooks/use-request-interception.ts +0 -53
  25. package/src/hooks/use-server-effect.ts +0 -75
  26. package/src/hooks/use-shared-cache.ts +0 -107
  27. package/src/index.ts +0 -46
  28. package/src/util/__tests__/__snapshots__/scoped-in-memory-cache.test.ts.snap +0 -19
  29. package/src/util/__tests__/__snapshots__/serializable-in-memory-cache.test.ts.snap +0 -19
  30. package/src/util/__tests__/get-gql-data-from-response.test.ts +0 -186
  31. package/src/util/__tests__/get-gql-request-id.test.ts +0 -132
  32. package/src/util/__tests__/graphql-document-node-parser.test.ts +0 -535
  33. package/src/util/__tests__/hydration-cache-api.test.ts +0 -34
  34. package/src/util/__tests__/merge-gql-context.test.ts +0 -73
  35. package/src/util/__tests__/purge-caches.test.ts +0 -28
  36. package/src/util/__tests__/request-api.test.ts +0 -176
  37. package/src/util/__tests__/request-fulfillment.test.ts +0 -146
  38. package/src/util/__tests__/request-tracking.test.tsx +0 -321
  39. package/src/util/__tests__/result-from-cache-response.test.ts +0 -79
  40. package/src/util/__tests__/scoped-in-memory-cache.test.ts +0 -316
  41. package/src/util/__tests__/serializable-in-memory-cache.test.ts +0 -397
  42. package/src/util/__tests__/ssr-cache.test.ts +0 -636
  43. package/src/util/__tests__/to-gql-operation.test.ts +0 -41
  44. package/src/util/data-error.ts +0 -63
  45. package/src/util/get-gql-data-from-response.ts +0 -65
  46. package/src/util/get-gql-request-id.ts +0 -106
  47. package/src/util/gql-error.ts +0 -43
  48. package/src/util/gql-router-context.ts +0 -9
  49. package/src/util/gql-types.ts +0 -64
  50. package/src/util/graphql-document-node-parser.ts +0 -132
  51. package/src/util/graphql-types.ts +0 -28
  52. package/src/util/hydration-cache-api.ts +0 -30
  53. package/src/util/merge-gql-context.ts +0 -35
  54. package/src/util/purge-caches.ts +0 -14
  55. package/src/util/request-api.ts +0 -65
  56. package/src/util/request-fulfillment.ts +0 -121
  57. package/src/util/request-tracking.ts +0 -211
  58. package/src/util/result-from-cache-response.ts +0 -30
  59. package/src/util/scoped-in-memory-cache.ts +0 -121
  60. package/src/util/serializable-in-memory-cache.ts +0 -44
  61. package/src/util/ssr-cache.ts +0 -193
  62. package/src/util/status.ts +0 -35
  63. package/src/util/to-gql-operation.ts +0 -43
  64. package/src/util/types.ts +0 -145
  65. package/tsconfig-build.json +0 -12
  66. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,63 +0,0 @@
1
- import {KindError} from "@khanacademy/wonder-stuff-core";
2
- import type {ErrorOptions} from "./types";
3
-
4
- /**
5
- * Error kinds for DataError.
6
- */
7
- export const DataErrors = Object.freeze({
8
- /**
9
- * The kind of error is not known.
10
- */
11
- Unknown: "Unknown",
12
-
13
- /**
14
- * The error is internal to the executing code.
15
- */
16
- Internal: "Internal",
17
-
18
- /**
19
- * There was a problem with the provided input.
20
- */
21
- InvalidInput: "InvalidInput",
22
-
23
- /**
24
- * A network error occurred.
25
- */
26
- Network: "Network",
27
-
28
- /**
29
- * There was a problem due to the state of the system not matching the
30
- * requested operation or input.
31
- */
32
- NotAllowed: "NotAllowed",
33
-
34
- /**
35
- * Response could not be parsed.
36
- */
37
- Parse: "Parse",
38
-
39
- /**
40
- * An error that occurred during SSR and was hydrated from cache
41
- */
42
- Hydrated: "Hydrated",
43
- });
44
-
45
- /**
46
- * An error from the Wonder Blocks Data API.
47
- *
48
- * Errors of this type will have names of the format:
49
- * `${kind}DataError`
50
- */
51
- export class DataError extends KindError {
52
- constructor(
53
- message: string,
54
- kind: typeof DataErrors[keyof typeof DataErrors],
55
- {metadata, cause}: ErrorOptions = {} as Partial<ErrorOptions>,
56
- ) {
57
- super(message, kind, {
58
- metadata,
59
- cause,
60
- name: "Data",
61
- });
62
- }
63
- }
@@ -1,65 +0,0 @@
1
- import {DataError, DataErrors} from "./data-error";
2
- import {GqlError, GqlErrors} from "./gql-error";
3
-
4
- /**
5
- * Validate a GQL operation response and extract the data.
6
- */
7
- export const getGqlDataFromResponse = async <TData>(
8
- response: Response,
9
- ): Promise<TData> => {
10
- // Get the response as text, that way we can use the text in error
11
- // messaging, should our parsing fail.
12
- const bodyText = await response.text();
13
- let result;
14
- try {
15
- result = JSON.parse(bodyText);
16
- } catch (e: any) {
17
- throw new DataError("Failed to parse response", DataErrors.Parse, {
18
- metadata: {
19
- statusCode: response.status,
20
- bodyText,
21
- },
22
- cause: e,
23
- });
24
- }
25
-
26
- // Check for a bad status code.
27
- if (response.status >= 300) {
28
- throw new DataError("Response unsuccessful", DataErrors.Network, {
29
- metadata: {
30
- statusCode: response.status,
31
- result,
32
- },
33
- });
34
- }
35
-
36
- // Check that we have a valid result payload.
37
- if (
38
- !Object.prototype.hasOwnProperty.call(result, "data") &&
39
- !Object.prototype.hasOwnProperty.call(result, "errors")
40
- ) {
41
- throw new GqlError("Server response missing", GqlErrors.BadResponse, {
42
- metadata: {
43
- statusCode: response.status,
44
- result,
45
- },
46
- });
47
- }
48
-
49
- // If the response payload has errors, throw an error.
50
- if (
51
- result.errors != null &&
52
- Array.isArray(result.errors) &&
53
- result.errors.length > 0
54
- ) {
55
- throw new GqlError("GraphQL errors", GqlErrors.ErrorResult, {
56
- metadata: {
57
- statusCode: response.status,
58
- result,
59
- },
60
- });
61
- }
62
-
63
- // We got here, so return the data.
64
- return result.data;
65
- };
@@ -1,106 +0,0 @@
1
- import {entries} from "@khanacademy/wonder-stuff-core";
2
- import type {GqlOperation, GqlContext} from "./gql-types";
3
-
4
- const toString = (value: unknown): string => {
5
- if (typeof value === "string") {
6
- return value;
7
- }
8
-
9
- if (typeof value === "object" && value != null) {
10
- if (value instanceof Date) {
11
- return value.toISOString();
12
- } else if (typeof value.toString === "function") {
13
- return value.toString();
14
- }
15
- }
16
- return JSON.stringify(value) ?? "";
17
- };
18
-
19
- const toStringifiedVariables = (acc: any, key: string, value: unknown): any => {
20
- if (typeof value === "object" && value !== null) {
21
- // If we have an object or array, we build sub-variables so that
22
- // the ID is easily human-readable rather than having lots of
23
- // extra %-encodings. This means that an object or array variable
24
- // turns into x variables, where x is the field or element count of
25
- // variable. See below for example.
26
- const subValues = entries(value);
27
-
28
- // If we don't get any entries, it's possible this is a Date, Error,
29
- // or some other non-standard value. While these generally should be
30
- // avoided as variables, we should handle them gracefully.
31
- if (subValues.length !== 0) {
32
- return subValues.reduce((innerAcc, [i, v]: [any, any]) => {
33
- const subKey = `${key}.${i}`;
34
- return toStringifiedVariables(innerAcc, subKey, v);
35
- }, acc);
36
- }
37
- }
38
-
39
- acc[key] = toString(value);
40
- return acc;
41
- };
42
-
43
- /**
44
- * Get an identifier for a given request.
45
- */
46
- export const getGqlRequestId = <TData, TVariables extends Record<any, any>>(
47
- operation: GqlOperation<TData, TVariables>,
48
- variables: TVariables | null | undefined,
49
- context: GqlContext,
50
- ): string => {
51
- // We add all the bits for this into an array and then join them with
52
- // a chosen separator.
53
- const parts: Array<string> = [];
54
-
55
- // First, we push the context values.
56
- const sortableContext = new URLSearchParams(context);
57
- sortableContext.sort();
58
- parts.push(sortableContext.toString());
59
-
60
- // Now we add the operation identifier.
61
- parts.push(operation.id);
62
-
63
- // Finally, if we have variables, we add those too.
64
- if (variables != null) {
65
- // We need to turn each variable into a string.
66
- // We also need to ensure we sort any sub-object keys.
67
- // `toStringifiedVariables` helps us with this by hoisting nested
68
- // data to individual variables for the purposes of ID generation.
69
- //
70
- // For example, consider variables:
71
- // {x: [1,2,3], y: {a: 1, b: 2, c: 3}, z: 123}
72
- //
73
- // Each variable, x, y and z, would be stringified into
74
- // stringifiedVariables as follows:
75
- // x becomes {"x.0": "1", "x.1": "2", "x.2": "3"}
76
- // y becomes {"y.a": "1", "y.b": "2", "y.c": "3"}
77
- // z becomes {"z": "123"}
78
- //
79
- // This then leads to stringifiedVariables being:
80
- // {
81
- // "x.0": "1",
82
- // "x.1": "2",
83
- // "x.2": "3",
84
- // "y.a": "1",
85
- // "y.b": "2",
86
- // "y.c": "3",
87
- // "z": "123",
88
- // }
89
- //
90
- // Thus allowing our use of URLSearchParams to both sort and easily
91
- // encode the variables into an idempotent identifier for those
92
- // variable values that is also human-readable.
93
- const stringifiedVariables = Object.keys(variables).reduce<
94
- Record<string, any>
95
- >((acc, key) => {
96
- const value = variables[key];
97
- return toStringifiedVariables(acc, key, value);
98
- }, {});
99
- // We use the same mechanism as context to sort and arrange the
100
- // variables.
101
- const sortableVariables = new URLSearchParams(stringifiedVariables);
102
- sortableVariables.sort();
103
- parts.push(sortableVariables.toString());
104
- }
105
- return parts.join("|");
106
- };
@@ -1,43 +0,0 @@
1
- import {KindError} from "@khanacademy/wonder-stuff-core";
2
-
3
- import type {ErrorOptions} from "./types";
4
-
5
- /**
6
- * Error kinds for GqlError.
7
- */
8
- export const GqlErrors = Object.freeze({
9
- /**
10
- * An internal framework error.
11
- */
12
- Internal: "Internal",
13
-
14
- /**
15
- * Response does not have the correct structure for a GraphQL response.
16
- */
17
- BadResponse: "BadResponse",
18
-
19
- /**
20
- * A valid GraphQL result with errors field in the payload.
21
- */
22
- ErrorResult: "ErrorResult",
23
- });
24
-
25
- /**
26
- * An error from the GQL API.
27
- *
28
- * Errors of this type will have names of the format:
29
- * `${kind}GqlError`
30
- */
31
- export class GqlError extends KindError {
32
- constructor(
33
- message: string,
34
- kind: typeof GqlErrors[keyof typeof GqlErrors],
35
- {metadata, cause}: ErrorOptions = {} as Partial<ErrorOptions>,
36
- ) {
37
- super(message, kind, {
38
- metadata,
39
- cause,
40
- name: "Gql",
41
- });
42
- }
43
- }
@@ -1,9 +0,0 @@
1
- import * as React from "react";
2
- import type {GqlRouterConfiguration} from "./gql-types";
3
-
4
- const GqlRouterContext: React.Context<
5
- GqlRouterConfiguration<any> | null | undefined
6
- > = React.createContext<GqlRouterConfiguration<any> | null | undefined>(null);
7
- GqlRouterContext.displayName = "GqlRouterContext";
8
-
9
- export {GqlRouterContext};
@@ -1,64 +0,0 @@
1
- /**
2
- * Operation types.
3
- */
4
- export type GqlOperationType = "mutation" | "query";
5
-
6
- /**
7
- * A GraphQL operation.
8
- */
9
- export type GqlOperation<
10
- // TData is not used to define a field on this type, but it is used
11
- // to ensure that calls using this operation will properly return the
12
- // correct data type.
13
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
14
- TData, // TVariables is not used to define a field on this type, but it is used
15
- // to ensure that calls using this operation will properly consume the
16
- // correct variables type.
17
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
18
- TVariables extends object = Empty,
19
- > = {
20
- type: GqlOperationType;
21
- id: string;
22
- // We allow other things here to be passed along to the fetch function.
23
- // For example, we might want to pass the full query/mutation definition
24
- // as a string here to allow that to be sent to an Apollo server that
25
- // expects it. This is a courtesy to calling code; these additional
26
- // values are ignored by WB Data, and passed through as-is.
27
- [key: string]: unknown;
28
- };
29
-
30
- export type GqlContext = {
31
- [key: string]: string;
32
- };
33
-
34
- /**
35
- * Functions that make fetches of GQL operations.
36
- */
37
- export type GqlFetchFn<
38
- TData,
39
- TVariables extends Record<any, any>,
40
- TContext extends GqlContext,
41
- > = (
42
- operation: GqlOperation<TData, TVariables>,
43
- variables: TVariables | null | undefined,
44
- context: TContext,
45
- ) => Promise<Response>;
46
-
47
- /**
48
- * The configuration stored in the GqlRouterContext context.
49
- */
50
- export type GqlRouterConfiguration<TContext extends GqlContext> = {
51
- fetch: GqlFetchFn<any, any, any>;
52
- defaultContext: TContext;
53
- };
54
-
55
- /**
56
- * Options for configuring a GQL fetch.
57
- */
58
- export type GqlFetchOptions<
59
- TVariables extends Record<any, any>,
60
- TContext extends GqlContext,
61
- > = {
62
- variables?: TVariables;
63
- context?: Partial<TContext>;
64
- };
@@ -1,132 +0,0 @@
1
- import type {
2
- DocumentNode,
3
- DefinitionNode,
4
- VariableDefinitionNode,
5
- OperationDefinitionNode,
6
- } from "./graphql-types";
7
- import {DataError, DataErrors} from "./data-error";
8
-
9
- export const DocumentTypes = Object.freeze({
10
- query: "query",
11
- mutation: "mutation",
12
- });
13
-
14
- export type DocumentType = typeof DocumentTypes[keyof typeof DocumentTypes];
15
-
16
- export interface IDocumentDefinition {
17
- type: DocumentType;
18
- name: string;
19
- variables: ReadonlyArray<VariableDefinitionNode>;
20
- }
21
-
22
- const cache = new Map<DocumentNode, IDocumentDefinition>();
23
-
24
- /**
25
- * Parse a GraphQL document node to determine some info about it.
26
- *
27
- * This is based on:
28
- * https://github.com/apollographql/react-apollo/blob/3bc993b2ea91704bd6a2667f42d1940656c071ff/src/parser.ts
29
- */
30
- export function graphQLDocumentNodeParser(
31
- document: DocumentNode,
32
- ): IDocumentDefinition {
33
- const cached = cache.get(document);
34
- if (cached) {
35
- return cached;
36
- }
37
-
38
- /**
39
- * Saftey check for proper usage.
40
- */
41
- if (!document?.kind) {
42
- if (process.env.NODE_ENV === "production") {
43
- throw new DataError("Bad DocumentNode", DataErrors.InvalidInput);
44
- } else {
45
- throw new DataError(
46
- `Argument of ${JSON.stringify(
47
- document,
48
- )} passed to parser was not a valid GraphQL ` +
49
- `DocumentNode. You may need to use 'graphql-tag' or another method ` +
50
- `to convert your operation into a document`,
51
- DataErrors.InvalidInput,
52
- );
53
- }
54
- }
55
-
56
- const fragments = document.definitions.filter(
57
- (x: DefinitionNode) => x.kind === "FragmentDefinition",
58
- );
59
-
60
- const queries = document.definitions.filter(
61
- (x: DefinitionNode) =>
62
- x.kind === "OperationDefinition" &&
63
- (x as OperationDefinitionNode).operation === "query",
64
- );
65
-
66
- const mutations = document.definitions.filter(
67
- (x: DefinitionNode) =>
68
- x.kind === "OperationDefinition" &&
69
- (x as OperationDefinitionNode).operation === "mutation",
70
- );
71
-
72
- const subscriptions = document.definitions.filter(
73
- (x: DefinitionNode) =>
74
- x.kind === "OperationDefinition" &&
75
- (x as OperationDefinitionNode).operation === "subscription",
76
- );
77
-
78
- if (fragments.length && !queries.length && !mutations.length) {
79
- if (process.env.NODE_ENV === "production") {
80
- throw new DataError("Fragment only", DataErrors.InvalidInput);
81
- } else {
82
- throw new DataError(
83
- `Passing only a fragment to 'graphql' is not supported. ` +
84
- `You must include a query or mutation as well`,
85
- DataErrors.InvalidInput,
86
- );
87
- }
88
- }
89
-
90
- if (subscriptions.length) {
91
- if (process.env.NODE_ENV === "production") {
92
- throw new DataError("No subscriptions", DataErrors.InvalidInput);
93
- } else {
94
- throw new DataError(
95
- `We do not support subscriptions. ` +
96
- `${JSON.stringify(document)} had ${
97
- subscriptions.length
98
- } subscriptions`,
99
- DataErrors.InvalidInput,
100
- );
101
- }
102
- }
103
-
104
- if (queries.length + mutations.length > 1) {
105
- if (process.env.NODE_ENV === "production") {
106
- throw new DataError("Too many ops", DataErrors.InvalidInput);
107
- } else {
108
- throw new DataError(
109
- `We only support one query or mutation per component. ` +
110
- `${JSON.stringify(document)} had ${
111
- queries.length
112
- } queries and ` +
113
- `${mutations.length} mutations. `,
114
- DataErrors.InvalidInput,
115
- );
116
- }
117
- }
118
-
119
- const type = queries.length ? DocumentTypes.query : DocumentTypes.mutation;
120
- const definitions = queries.length ? queries : mutations;
121
-
122
- const definition: OperationDefinitionNode = definitions[0] as any;
123
- const variables = definition.variableDefinitions || [];
124
-
125
- // fallback to using data if no name
126
- const name =
127
- definition.name?.kind === "Name" ? definition.name.value : "data";
128
-
129
- const payload: IDocumentDefinition = {name, type, variables};
130
- cache.set(document, payload);
131
- return payload;
132
- }
@@ -1,28 +0,0 @@
1
- // WARNING: If you modify this file you must update graphql-types.js.flow.
2
- // NOTE(somewhatabstract):
3
- // These types are bare minimum to support document parsing. They're derived
4
- // from graphql@14.5.8, the last version that provided TypeScript types.
5
- // Doing this avoids us having to take a dependency on that library just for
6
- // these types.
7
- export interface DefinitionNode {
8
- readonly kind: string;
9
- }
10
-
11
- export type VariableDefinitionNode = {
12
- readonly kind: "VariableDefinition";
13
- };
14
-
15
- export interface OperationDefinitionNode extends DefinitionNode {
16
- readonly kind: "OperationDefinition";
17
- readonly operation: string;
18
- readonly variableDefinitions: ReadonlyArray<VariableDefinitionNode>;
19
- readonly name?: {
20
- readonly kind: unknown;
21
- readonly value: string;
22
- };
23
- }
24
-
25
- export type DocumentNode = {
26
- readonly kind: "Document";
27
- readonly definitions: ReadonlyArray<DefinitionNode>;
28
- };
@@ -1,30 +0,0 @@
1
- import {SsrCache} from "./ssr-cache";
2
-
3
- import type {ValidCacheData, CachedResponse, ResponseCache} from "./types";
4
-
5
- /**
6
- * Initialize the hydration cache.
7
- *
8
- * @param {ResponseCache} source The cache content to use for initializing the
9
- * cache.
10
- * @throws {Error} If the cache is already initialized.
11
- */
12
- export const initializeHydrationCache = (source: ResponseCache): void =>
13
- SsrCache.Default.initialize(source);
14
-
15
- /**
16
- * Purge cached hydration responses that match the given predicate.
17
- *
18
- * @param {(id: string) => boolean} [predicate] The predicate to match against
19
- * the cached hydration responses. If no predicate is provided, all cached
20
- * hydration responses will be purged.
21
- */
22
- export const purgeHydrationCache = (
23
- predicate?: (
24
- key: string,
25
- cacheEntry?:
26
- | Readonly<CachedResponse<ValidCacheData>>
27
- | null
28
- | undefined,
29
- ) => boolean,
30
- ): void => SsrCache.Default.purgeData(predicate);
@@ -1,35 +0,0 @@
1
- import type {GqlContext} from "./gql-types";
2
-
3
- /**
4
- * Construct a complete GqlContext from current defaults and a partial context.
5
- *
6
- * Values in the partial context that are `undefined` will be ignored.
7
- * Values in the partial context that are `null` will be deleted.
8
- */
9
- export const mergeGqlContext = <TContext extends GqlContext>(
10
- defaultContext: TContext,
11
- overrides: Partial<TContext>,
12
- ): TContext => {
13
- // Let's merge the partial context default context. We deliberately
14
- // don't spread because spreading would overwrite default context
15
- // values with undefined or null if the partial context includes a value
16
- // explicitly set to undefined or null.
17
- return Object.keys(overrides).reduce(
18
- (acc, key) => {
19
- // Undefined values are ignored.
20
- if (overrides[key] !== undefined) {
21
- if (overrides[key] === null) {
22
- // Null indicates we delete this context value.
23
- delete acc[key];
24
- } else {
25
- // Otherwise, we set it.
26
- // @ts-expect-error TypeScript doesn't seem to see that
27
- // TContext can have string keys.
28
- acc[key] = overrides[key];
29
- }
30
- }
31
- return acc;
32
- },
33
- {...defaultContext},
34
- );
35
- };
@@ -1,14 +0,0 @@
1
- import {SharedCache} from "../hooks/use-shared-cache";
2
- import {purgeHydrationCache} from "./hydration-cache-api";
3
-
4
- /**
5
- * Purge all caches managed by Wonder Blocks Data.
6
- *
7
- * This is a convenience method that purges the shared cache and the hydration
8
- * cache. It is useful for testing purposes to avoid having to reason about
9
- * which caches may have been used during a given test run.
10
- */
11
- export const purgeCaches = () => {
12
- SharedCache.purgeAll();
13
- purgeHydrationCache();
14
- };
@@ -1,65 +0,0 @@
1
- import {Server} from "@khanacademy/wonder-blocks-core";
2
- import {RequestTracker} from "./request-tracking";
3
- import {RequestFulfillment} from "./request-fulfillment";
4
- import {DataError, DataErrors} from "./data-error";
5
-
6
- import type {ResponseCache} from "./types";
7
-
8
- const SSRCheck = () => {
9
- if (Server.isServerSide()) {
10
- return null;
11
- }
12
-
13
- if (process.env.NODE_ENV === "production") {
14
- return new DataError("No CSR tracking", DataErrors.NotAllowed);
15
- } else {
16
- return new DataError(
17
- "Data requests are not tracked for fulfillment when when client-side",
18
- DataErrors.NotAllowed,
19
- );
20
- }
21
- };
22
-
23
- /**
24
- * Fetches all tracked data requests.
25
- *
26
- * This is for use with the `TrackData` component during server-side rendering.
27
- *
28
- * @throws {Error} If executed outside of server-side rendering.
29
- * @returns {Promise<void>} A promise that resolves when all tracked requests
30
- * have been fetched.
31
- */
32
- export const fetchTrackedRequests = (): Promise<ResponseCache> => {
33
- const ssrCheck = SSRCheck();
34
- if (ssrCheck != null) {
35
- return Promise.reject(ssrCheck);
36
- }
37
- return RequestTracker.Default.fulfillTrackedRequests();
38
- };
39
-
40
- /**
41
- * Indicate if there are tracked requests waiting to be fetched.
42
- *
43
- * This is used in conjunction with `TrackData`.
44
- *
45
- * @throws {Error} If executed outside of server-side rendering.
46
- * @returns {boolean} `true` if there are unfetched tracked requests;
47
- * otherwise, `false`.
48
- */
49
- export const hasTrackedRequestsToBeFetched = (): boolean => {
50
- const ssrCheck = SSRCheck();
51
- if (ssrCheck != null) {
52
- throw ssrCheck;
53
- }
54
- return RequestTracker.Default.hasUnfulfilledRequests;
55
- };
56
-
57
- /**
58
- * Abort all in-flight requests.
59
- *
60
- * This aborts all requests currently inflight via our default request
61
- * fulfillment.
62
- */
63
- export const abortInflightRequests = (): void => {
64
- RequestFulfillment.Default.abortAll();
65
- };