@lewebsimple/nuxt-graphql 0.4.0 → 0.5.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/README.md +130 -188
- package/dist/module.d.mts +71 -22
- package/dist/module.json +2 -2
- package/dist/module.mjs +406 -369
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.d.ts +19 -0
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.js +82 -0
- package/dist/runtime/app/composables/useGraphQLCache.client.d.ts +10 -7
- package/dist/runtime/app/composables/useGraphQLCache.client.js +16 -31
- package/dist/runtime/app/composables/useGraphQLMutation.d.ts +10 -18
- package/dist/runtime/app/composables/useGraphQLMutation.js +7 -11
- package/dist/runtime/app/composables/useGraphQLQuery.d.ts +11 -9
- package/dist/runtime/app/composables/useGraphQLQuery.js +20 -78
- package/dist/runtime/app/composables/useGraphQLSubscription.client.d.ts +19 -0
- package/dist/runtime/app/composables/{useGraphQLSubscription.js → useGraphQLSubscription.client.js} +9 -14
- package/dist/runtime/app/lib/cache.d.ts +18 -0
- package/dist/runtime/app/lib/cache.js +9 -0
- package/dist/runtime/app/lib/execute-http.d.ts +14 -0
- package/dist/runtime/app/lib/execute-http.js +8 -0
- package/dist/runtime/app/lib/in-flight.d.ts +14 -0
- package/dist/runtime/app/lib/{graphql-cache.js → in-flight.js} +2 -7
- package/dist/runtime/app/lib/persisted.d.ts +26 -0
- package/dist/runtime/app/plugins/graphql-request.d.ts +7 -2
- package/dist/runtime/app/plugins/graphql-request.js +13 -14
- package/dist/runtime/app/plugins/graphql-sse.client.d.ts +7 -2
- package/dist/runtime/server/api/graphql.d.ts +8 -0
- package/dist/runtime/server/api/graphql.js +16 -0
- package/dist/runtime/server/lib/default-context.d.ts +5 -0
- package/dist/runtime/server/lib/default-context.js +2 -0
- package/dist/runtime/server/lib/default-schema.d.ts +7 -0
- package/dist/runtime/server/lib/default-schema.js +18 -0
- package/dist/runtime/server/lib/execute-schema.d.ts +11 -0
- package/dist/runtime/server/lib/execute-schema.js +23 -0
- package/dist/runtime/server/lib/remote-executor.d.ts +17 -33
- package/dist/runtime/server/lib/remote-executor.js +24 -59
- package/dist/runtime/server/lib/yoga.d.ts +6 -0
- package/dist/runtime/server/lib/yoga.js +20 -0
- package/dist/runtime/server/utils/defineGraphQLContext.d.ts +10 -0
- package/dist/runtime/server/utils/defineGraphQLContext.js +3 -0
- package/dist/runtime/server/utils/defineGraphQLSchema.d.ts +14 -0
- package/dist/runtime/server/utils/defineGraphQLSchema.js +3 -0
- package/dist/runtime/server/utils/defineRemoteExecutorHooks.d.ts +13 -0
- package/dist/runtime/server/utils/defineRemoteExecutorHooks.js +3 -0
- package/dist/runtime/server/utils/useServerGraphQLMutation.d.ts +16 -8
- package/dist/runtime/server/utils/useServerGraphQLMutation.js +12 -11
- package/dist/runtime/server/utils/useServerGraphQLQuery.d.ts +16 -3
- package/dist/runtime/server/utils/useServerGraphQLQuery.js +16 -4
- package/dist/runtime/shared/lib/cache-config.d.ts +42 -0
- package/dist/runtime/{app → shared}/lib/cache-config.js +3 -3
- package/dist/runtime/shared/lib/error.d.ts +56 -0
- package/dist/runtime/shared/lib/error.js +61 -0
- package/dist/runtime/shared/lib/headers.d.ts +13 -3
- package/dist/runtime/shared/lib/headers.js +10 -35
- package/dist/runtime/shared/lib/registry.d.ts +12 -0
- package/dist/runtime/shared/lib/registry.js +8 -0
- package/dist/runtime/shared/lib/utils.d.ts +1 -0
- package/dist/runtime/shared/lib/utils.js +0 -0
- package/dist/runtime/{app → shared}/types/nuxt-graphql.d.ts +1 -5
- package/package.json +19 -33
- package/dist/helpers.d.mts +0 -3
- package/dist/helpers.mjs +0 -3
- package/dist/runtime/app/composables/useGraphQLSubscription.d.ts +0 -17
- package/dist/runtime/app/lib/cache-config.d.ts +0 -8
- package/dist/runtime/app/lib/graphql-cache.d.ts +0 -7
- package/dist/runtime/server/api/yoga-handler.d.ts +0 -2
- package/dist/runtime/server/api/yoga-handler.js +0 -42
- package/dist/runtime/server/lib/define-graphql-context.d.ts +0 -5
- package/dist/runtime/server/lib/define-graphql-context.js +0 -4
- package/dist/runtime/server/lib/define-remote-exec-middleware.d.ts +0 -30
- package/dist/runtime/server/lib/define-remote-exec-middleware.js +0 -3
- package/dist/runtime/server/lib/define-yoga-middleware.d.ts +0 -21
- package/dist/runtime/server/lib/define-yoga-middleware.js +0 -3
- package/dist/runtime/server/lib/execute-server-graphql.d.ts +0 -7
- package/dist/runtime/server/lib/execute-server-graphql.js +0 -34
- package/dist/runtime/shared/lib/graphql-error.d.ts +0 -17
- package/dist/runtime/shared/lib/graphql-error.js +0 -28
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createError, defineEventHandler, sendWebResponse, toWebRequest } from "h3";
|
|
2
|
+
import { createContext } from "#graphql/context";
|
|
3
|
+
import { getYogaInstance } from "../lib/yoga.js";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
try {
|
|
6
|
+
const request = toWebRequest(event);
|
|
7
|
+
const context = await createContext(event);
|
|
8
|
+
const yoga = getYogaInstance();
|
|
9
|
+
const response = await yoga.handleRequest(request, context);
|
|
10
|
+
return sendWebResponse(event, response);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13
|
+
console.error("GraphQL server error:", message);
|
|
14
|
+
throw createError({ statusCode: 500, message: "GraphQL server error (see server logs for details)." });
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createSchema } from "graphql-yoga";
|
|
2
|
+
import { defineGraphQLSchema } from "../utils/defineGraphQLSchema.js";
|
|
3
|
+
const schema = createSchema({
|
|
4
|
+
typeDefs: (
|
|
5
|
+
/* GraphQL */
|
|
6
|
+
`
|
|
7
|
+
type Query {
|
|
8
|
+
hello(name: String): String!
|
|
9
|
+
}
|
|
10
|
+
`
|
|
11
|
+
),
|
|
12
|
+
resolvers: {
|
|
13
|
+
Query: {
|
|
14
|
+
hello: (_parent, { name }) => `Hello ${name || "world"}!`
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
export default defineGraphQLSchema({ schema });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
import type { VariablesOf, ResultOf, QueryName, MutationName } from "#graphql/registry";
|
|
3
|
+
/**
|
|
4
|
+
* Execute a GraphQL operation directly against the stitched schema.
|
|
5
|
+
*
|
|
6
|
+
* @param event H3 event for context creation.
|
|
7
|
+
* @param operationName Operation name from the registry.
|
|
8
|
+
* @param variables Operation variables.
|
|
9
|
+
* @returns Operation result data.
|
|
10
|
+
*/
|
|
11
|
+
export declare function executeGraphQLSchema<TName extends QueryName | MutationName>(event: H3Event, operationName: TName, variables: VariablesOf<TName>): Promise<ResultOf<TName>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { execute } from "graphql";
|
|
2
|
+
import { createContext } from "#graphql/context";
|
|
3
|
+
import { schema } from "#graphql/schema";
|
|
4
|
+
import { normalizeError } from "../../shared/lib/error.js";
|
|
5
|
+
import { getOperationDocument } from "../../shared/lib/registry.js";
|
|
6
|
+
export async function executeGraphQLSchema(event, operationName, variables) {
|
|
7
|
+
const document = getOperationDocument(operationName);
|
|
8
|
+
const result = await execute({
|
|
9
|
+
schema,
|
|
10
|
+
document,
|
|
11
|
+
variableValues: variables,
|
|
12
|
+
contextValue: await createContext(event)
|
|
13
|
+
});
|
|
14
|
+
if (result.errors?.length) {
|
|
15
|
+
throw normalizeError(result.errors);
|
|
16
|
+
}
|
|
17
|
+
if (!result.data) {
|
|
18
|
+
throw normalizeError(
|
|
19
|
+
new Error("GraphQL execution returned no data")
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return result.data;
|
|
23
|
+
}
|
|
@@ -1,35 +1,19 @@
|
|
|
1
1
|
import type { Executor } from "@graphql-tools/utils";
|
|
2
|
-
import type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
operationName: string;
|
|
6
|
-
context: GraphQLContext;
|
|
7
|
-
fetchOptions: {
|
|
8
|
-
headers: Headers;
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
export type RemoteExecMiddlewareOnResponseArgs = {
|
|
12
|
-
remoteName: string;
|
|
13
|
-
operationName: string;
|
|
14
|
-
context: GraphQLContext;
|
|
15
|
-
response: Response;
|
|
16
|
-
};
|
|
17
|
-
export type RemoteExecMiddlewareOnErrorArgs = {
|
|
18
|
-
remoteName: string;
|
|
19
|
-
operationName: string;
|
|
20
|
-
context: GraphQLContext;
|
|
21
|
-
error: unknown;
|
|
22
|
-
response?: Response;
|
|
23
|
-
};
|
|
24
|
-
export type RemoteExecMiddlewareHandler = {
|
|
25
|
-
onRequest?: (args: RemoteExecMiddlewareOnRequestArgs) => Promise<void> | void;
|
|
26
|
-
onResponse?: (args: RemoteExecMiddlewareOnResponseArgs) => Promise<void> | void;
|
|
27
|
-
onError?: (args: RemoteExecMiddlewareOnErrorArgs) => Promise<void> | void;
|
|
28
|
-
};
|
|
29
|
-
export interface CreateRemoteExecutorOptions {
|
|
2
|
+
import { type HeadersInput } from "../../shared/lib/headers.js";
|
|
3
|
+
import type { GraphQLRemoteExecHooks } from "../utils/defineRemoteExecutorHooks.js";
|
|
4
|
+
type CreateRemoteExecutorInput = {
|
|
30
5
|
url: string;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
6
|
+
headers?: HeadersInput;
|
|
7
|
+
hooks: GraphQLRemoteExecHooks[];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Create an HTTP executor for a remote GraphQL schema.
|
|
11
|
+
*
|
|
12
|
+
* @param {CreateRemoteExecutorInput} options Remote executor configuration.
|
|
13
|
+
* @param options.url Remote GraphQL endpoint.
|
|
14
|
+
* @param options.headers Static headers for all requests.
|
|
15
|
+
* @param options.hooks Per-operation hooks.
|
|
16
|
+
* @returns Executor function for GraphQL Tools.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createRemoteExecutor({ url, headers, hooks }: CreateRemoteExecutorInput): Executor;
|
|
19
|
+
export {};
|
|
@@ -1,68 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export function createRemoteExecutor(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const fetchOptions = {
|
|
16
|
-
method: "POST",
|
|
17
|
-
headers: requestHeaders,
|
|
18
|
-
body: JSON.stringify({ query, variables, operationName: resolvedOperationName })
|
|
19
|
-
};
|
|
20
|
-
if (onRequest && executionDepth === 1) {
|
|
21
|
-
await onRequest({
|
|
22
|
-
remoteName,
|
|
23
|
-
operationName: resolvedOperationName,
|
|
24
|
-
context: graphQLContext,
|
|
25
|
-
fetchOptions: { headers: requestHeaders }
|
|
26
|
-
});
|
|
27
|
-
}
|
|
1
|
+
import { buildHTTPExecutor } from "@graphql-tools/executor-http";
|
|
2
|
+
import { mergeHeaders } from "../../shared/lib/headers.js";
|
|
3
|
+
export function createRemoteExecutor({ url, headers, hooks }) {
|
|
4
|
+
function getHeaders(request) {
|
|
5
|
+
const extHeaders = request?.extensions?.headers || {};
|
|
6
|
+
const mergedHeaders = mergeHeaders(headers, extHeaders);
|
|
7
|
+
return Object.fromEntries(mergedHeaders.entries());
|
|
8
|
+
}
|
|
9
|
+
const executor = buildHTTPExecutor({
|
|
10
|
+
endpoint: url,
|
|
11
|
+
headers: (request) => getHeaders(request),
|
|
12
|
+
fetch: globalThis.fetch
|
|
13
|
+
});
|
|
14
|
+
return async (request) => {
|
|
28
15
|
try {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
if (!response.ok) {
|
|
32
|
-
const statusError = normalizeGraphQLError(new Error(`Remote ${remoteName} responded with status ${response.status}`));
|
|
33
|
-
if (onError) {
|
|
34
|
-
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: statusError, response: safeResponse });
|
|
35
|
-
}
|
|
36
|
-
throw statusError;
|
|
16
|
+
for (const hook of hooks) {
|
|
17
|
+
await hook.onRequest?.(request);
|
|
37
18
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const json = await response.json();
|
|
43
|
-
if (json && typeof json === "object" && Array.isArray(json.errors)) {
|
|
44
|
-
const normalized = normalizeGraphQLError({ errors: json.errors });
|
|
45
|
-
if (onError) {
|
|
46
|
-
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: normalized, response: safeResponse });
|
|
47
|
-
}
|
|
48
|
-
throw normalized;
|
|
49
|
-
}
|
|
50
|
-
return json;
|
|
51
|
-
} catch (error) {
|
|
52
|
-
const normalized = normalizeGraphQLError(error);
|
|
53
|
-
if (onError) {
|
|
54
|
-
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: normalized, response: safeResponse });
|
|
19
|
+
const result = await executor(request);
|
|
20
|
+
if (!(Symbol.asyncIterator in result)) {
|
|
21
|
+
for (const hook of hooks) {
|
|
22
|
+
await hook.onResult?.(result);
|
|
55
23
|
}
|
|
56
|
-
throw normalized;
|
|
57
24
|
}
|
|
25
|
+
return result;
|
|
58
26
|
} catch (error) {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: normalized });
|
|
27
|
+
for (const hook of hooks) {
|
|
28
|
+
await hook.onError?.(error);
|
|
62
29
|
}
|
|
63
|
-
throw
|
|
64
|
-
} finally {
|
|
65
|
-
executionDepth--;
|
|
30
|
+
throw error;
|
|
66
31
|
}
|
|
67
32
|
};
|
|
68
33
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createYoga } from "graphql-yoga";
|
|
2
|
+
import { schema } from "#graphql/schema";
|
|
3
|
+
let yoga = null;
|
|
4
|
+
export function getYogaInstance() {
|
|
5
|
+
if (!yoga) {
|
|
6
|
+
yoga = createYoga({
|
|
7
|
+
schema,
|
|
8
|
+
graphqlEndpoint: "/api/graphql",
|
|
9
|
+
fetchAPI: globalThis,
|
|
10
|
+
graphiql: process.env.NODE_ENV !== "production",
|
|
11
|
+
// @ts-expect-error subscriptions type available at runtime
|
|
12
|
+
subscriptions: { protocol: "SSE" }
|
|
13
|
+
});
|
|
14
|
+
if (!yoga) {
|
|
15
|
+
throw new Error("Failed to create Yoga instance");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return yoga;
|
|
19
|
+
}
|
|
20
|
+
;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
type GraphQLContextFactory<TContext extends Record<string, unknown>> = (event: H3Event) => TContext | Promise<TContext>;
|
|
3
|
+
/**
|
|
4
|
+
* Define a GraphQL context factory with proper typing.
|
|
5
|
+
*
|
|
6
|
+
* @param factory Context factory function.
|
|
7
|
+
* @returns The same factory, typed for inference.
|
|
8
|
+
*/
|
|
9
|
+
export declare function defineGraphQLContext<TContext extends Record<string, unknown>>(factory: GraphQLContextFactory<TContext>): GraphQLContextFactory<TContext>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GraphQLSchema } from "graphql";
|
|
2
|
+
import type { SubschemaConfig } from "@graphql-tools/delegate";
|
|
3
|
+
/**
|
|
4
|
+
* Wrap a GraphQL schema or subschema config for module consumption.
|
|
5
|
+
*
|
|
6
|
+
* @param {{ schema: GraphQLSchema | SubschemaConfig }} options Schema wrapper input.
|
|
7
|
+
* @param options.schema Local schema or subschema config.
|
|
8
|
+
* @returns Wrapper object containing the schema.
|
|
9
|
+
*/
|
|
10
|
+
export declare function defineGraphQLSchema({ schema }: {
|
|
11
|
+
schema: GraphQLSchema | SubschemaConfig;
|
|
12
|
+
}): {
|
|
13
|
+
schema: GraphQLSchema | SubschemaConfig<any, any, any, Record<string, any>>;
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExecutionRequest, ExecutionResult } from "@graphql-tools/utils";
|
|
2
|
+
export interface GraphQLRemoteExecHooks {
|
|
3
|
+
onRequest?: (request: ExecutionRequest) => void | Promise<void>;
|
|
4
|
+
onResult?: (result: ExecutionResult) => void | Promise<void>;
|
|
5
|
+
onError?: (error: unknown) => void | Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Define remote executor hooks with proper typing.
|
|
9
|
+
*
|
|
10
|
+
* @param hooks Hooks implementation.
|
|
11
|
+
* @returns The same hooks object.
|
|
12
|
+
*/
|
|
13
|
+
export declare function defineRemoteExecutorHooks(hooks: GraphQLRemoteExecHooks): GraphQLRemoteExecHooks;
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
-
import {
|
|
3
|
-
import { type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
import type { MutationName, VariablesOf, ResultOf } from "#graphql/registry";
|
|
3
|
+
import { type SafeResult } from "../../shared/lib/error.js";
|
|
4
|
+
import type { IsEmptyObject } from "../../shared/lib/utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Execute a GraphQL mutation against the local stitched schema (server-side).
|
|
7
|
+
*
|
|
8
|
+
* - Schema execution only (no HTTP)
|
|
9
|
+
* - Context comes from the H3 event
|
|
10
|
+
* - Errors are normalized
|
|
11
|
+
*
|
|
12
|
+
* @param event H3 event used to create context.
|
|
13
|
+
* @param operationName Operation name from the registry.
|
|
14
|
+
* @param variables Operation variables.
|
|
15
|
+
* @returns SafeResult containing data or a normalized error.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useServerGraphQLMutation<TName extends MutationName>(event: H3Event, operationName: TName, ...args: IsEmptyObject<VariablesOf<TName>> extends true ? [variables?: VariablesOf<TName>] : [variables: VariablesOf<TName>]): Promise<SafeResult<ResultOf<TName>>>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { executeGraphQLSchema } from "../lib/execute-schema.js";
|
|
2
|
+
import { normalizeError } from "../../shared/lib/error.js";
|
|
3
|
+
export async function useServerGraphQLMutation(event, operationName, ...args) {
|
|
4
|
+
try {
|
|
5
|
+
const [variables] = args;
|
|
6
|
+
const data = await executeGraphQLSchema(event, operationName, variables);
|
|
7
|
+
return { data, error: null };
|
|
8
|
+
} catch (err) {
|
|
9
|
+
return {
|
|
10
|
+
data: null,
|
|
11
|
+
error: normalizeError(err)
|
|
12
|
+
};
|
|
11
13
|
}
|
|
12
|
-
return { mutate };
|
|
13
14
|
}
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
-
import {
|
|
3
|
-
import { type
|
|
4
|
-
|
|
2
|
+
import type { QueryName, VariablesOf, ResultOf } from "#graphql/registry";
|
|
3
|
+
import { type SafeResult } from "../../shared/lib/error.js";
|
|
4
|
+
import type { IsEmptyObject } from "../../shared/lib/utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Execute a GraphQL query against the local stitched schema (server-side).
|
|
7
|
+
*
|
|
8
|
+
* - Schema execution only (no HTTP)
|
|
9
|
+
* - Context comes from the H3 event
|
|
10
|
+
* - Errors are normalized
|
|
11
|
+
*
|
|
12
|
+
* @param event H3 event used to create context.
|
|
13
|
+
* @param operationName Operation name from the registry.
|
|
14
|
+
* @param variables Operation variables.
|
|
15
|
+
* @returns SafeResult containing data or a normalized error.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useServerGraphQLQuery<TName extends QueryName>(event: H3Event, operationName: TName, ...args: IsEmptyObject<VariablesOf<TName>> extends true ? [variables?: VariablesOf<TName>] : [variables: VariablesOf<TName>]): Promise<SafeResult<ResultOf<TName>>>;
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { executeGraphQLSchema } from "../lib/execute-schema.js";
|
|
2
|
+
import { normalizeError } from "../../shared/lib/error.js";
|
|
3
3
|
export async function useServerGraphQLQuery(event, operationName, ...args) {
|
|
4
|
-
const [variables
|
|
5
|
-
|
|
4
|
+
const [variables] = args;
|
|
5
|
+
try {
|
|
6
|
+
const data = await executeGraphQLSchema(
|
|
7
|
+
event,
|
|
8
|
+
operationName,
|
|
9
|
+
variables
|
|
10
|
+
);
|
|
11
|
+
return { data, error: null };
|
|
12
|
+
} catch (err) {
|
|
13
|
+
return {
|
|
14
|
+
data: null,
|
|
15
|
+
error: normalizeError(err)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
6
18
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type GraphQLCachePolicy = "no-cache" | "cache-first" | "network-first" | "swr";
|
|
2
|
+
export interface CacheConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Prefix used for all persisted cache keys.
|
|
5
|
+
*
|
|
6
|
+
* Used for namespacing and bulk invalidation.
|
|
7
|
+
* Default: 'graphql'
|
|
8
|
+
*/
|
|
9
|
+
keyPrefix: string;
|
|
10
|
+
/**
|
|
11
|
+
* Version included in cache keys.
|
|
12
|
+
*
|
|
13
|
+
* Changing this value invalidates all existing cache entries.
|
|
14
|
+
* Default: '1'
|
|
15
|
+
*/
|
|
16
|
+
keyVersion: string | number;
|
|
17
|
+
/**
|
|
18
|
+
* Cache strategy used by useAsyncGraphQLQuery.
|
|
19
|
+
*
|
|
20
|
+
* - 'no-cache' → always fetch, never read/write cache
|
|
21
|
+
* - 'cache-first' → return cache if valid, otherwise fetch
|
|
22
|
+
* - 'network-first' → fetch first, fallback to cache on failure
|
|
23
|
+
* - 'swr' → return cache immediately, revalidate in background
|
|
24
|
+
*/
|
|
25
|
+
policy: GraphQLCachePolicy;
|
|
26
|
+
/**
|
|
27
|
+
* Time-to-live in seconds.
|
|
28
|
+
*
|
|
29
|
+
* - undefined → inherit from higher-level config
|
|
30
|
+
* - 0 → never expires
|
|
31
|
+
* - > 0 → expires after TTL
|
|
32
|
+
*/
|
|
33
|
+
ttl?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Merge the default cache config with user overrides.
|
|
37
|
+
*
|
|
38
|
+
* @param overrides Partial cache config overrides.
|
|
39
|
+
* @returns Resolved cache configuration.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveCacheConfig(...overrides: Array<Partial<CacheConfig> | undefined>): CacheConfig;
|
|
42
|
+
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const defaultCacheConfig = {
|
|
2
|
-
cachePolicy: "cache-first",
|
|
3
|
-
cacheVersion: "1",
|
|
4
2
|
keyPrefix: "gql",
|
|
5
|
-
|
|
3
|
+
keyVersion: "1",
|
|
4
|
+
policy: "no-cache",
|
|
5
|
+
ttl: void 0
|
|
6
6
|
};
|
|
7
7
|
export function resolveCacheConfig(...overrides) {
|
|
8
8
|
return Object.assign({}, defaultCacheConfig, ...overrides);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { GraphQLError } from "graphql";
|
|
2
|
+
/**
|
|
3
|
+
* Safe result type.
|
|
4
|
+
*
|
|
5
|
+
* - On success, `data` contains the result and `error` is `null`.
|
|
6
|
+
* - On failure, `data` is `null` and `error` contains a NormalizedError.
|
|
7
|
+
*/
|
|
8
|
+
export type SafeResult<T> = {
|
|
9
|
+
data: T;
|
|
10
|
+
error: null;
|
|
11
|
+
} | {
|
|
12
|
+
data: null;
|
|
13
|
+
error: NormalizedError;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Base GraphQL error codes.
|
|
17
|
+
* Users may extend this interface via declaration merging.
|
|
18
|
+
*/
|
|
19
|
+
export interface GraphQLErrorCodeMap {
|
|
20
|
+
INTERNAL_ERROR: true;
|
|
21
|
+
NETWORK_ERROR: true;
|
|
22
|
+
BAD_REQUEST: true;
|
|
23
|
+
UNAUTHORIZED: true;
|
|
24
|
+
FORBIDDEN: true;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Typed GraphQL error code.
|
|
28
|
+
* Extensible via interface augmentation.
|
|
29
|
+
*/
|
|
30
|
+
type GraphQLErrorCode = keyof GraphQLErrorCodeMap;
|
|
31
|
+
/**
|
|
32
|
+
* Unified, transport-agnostic GraphQL error.
|
|
33
|
+
*
|
|
34
|
+
* - `message` : human-readable summary
|
|
35
|
+
* - `code` : typed, extensible error code
|
|
36
|
+
* - `errors[]` : underlying GraphQL execution errors (lossless)
|
|
37
|
+
*/
|
|
38
|
+
export declare class NormalizedError extends Error {
|
|
39
|
+
readonly errors: readonly GraphQLError[];
|
|
40
|
+
readonly code?: GraphQLErrorCode;
|
|
41
|
+
constructor(input: {
|
|
42
|
+
message: string;
|
|
43
|
+
errors?: readonly GraphQLError[];
|
|
44
|
+
code?: GraphQLErrorCode;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Normalize an unknown error into a NormalizedError.
|
|
49
|
+
*
|
|
50
|
+
* This function is the **only** place where untrusted errors are interpreted.
|
|
51
|
+
*
|
|
52
|
+
* @param error Unknown error value.
|
|
53
|
+
* @returns NormalizedError instance.
|
|
54
|
+
*/
|
|
55
|
+
export declare function normalizeError(error: unknown): NormalizedError;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export class NormalizedError extends Error {
|
|
2
|
+
errors;
|
|
3
|
+
code;
|
|
4
|
+
constructor(input) {
|
|
5
|
+
super(input.message);
|
|
6
|
+
this.name = "NormalizedError";
|
|
7
|
+
this.errors = input.errors ?? [];
|
|
8
|
+
this.code = input.code;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function isGraphQLError(error) {
|
|
12
|
+
return typeof error === "object" && error !== null && "message" in error && "extensions" in error;
|
|
13
|
+
}
|
|
14
|
+
function isGraphQLErrorArray(error) {
|
|
15
|
+
return Array.isArray(error) && error.every(isGraphQLError);
|
|
16
|
+
}
|
|
17
|
+
function isGraphQLClientError(error) {
|
|
18
|
+
return typeof error === "object" && error !== null && "response" in error;
|
|
19
|
+
}
|
|
20
|
+
function extractCode(error) {
|
|
21
|
+
const code = error?.extensions?.code;
|
|
22
|
+
return typeof code === "string" ? code : void 0;
|
|
23
|
+
}
|
|
24
|
+
export function normalizeError(error) {
|
|
25
|
+
if (error instanceof NormalizedError) {
|
|
26
|
+
return error;
|
|
27
|
+
}
|
|
28
|
+
if (isGraphQLError(error)) {
|
|
29
|
+
return new NormalizedError({
|
|
30
|
+
message: error.message,
|
|
31
|
+
errors: [error],
|
|
32
|
+
code: extractCode(error)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (isGraphQLClientError(error)) {
|
|
36
|
+
const errors = error.response?.errors;
|
|
37
|
+
if (errors && isGraphQLErrorArray(errors)) {
|
|
38
|
+
return new NormalizedError({
|
|
39
|
+
message: errors.map(({ message }) => message).join("\n"),
|
|
40
|
+
errors,
|
|
41
|
+
code: extractCode(errors[0])
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return new NormalizedError({
|
|
45
|
+
message: error.message ?? "GraphQL network error",
|
|
46
|
+
code: "NETWORK_ERROR"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (typeof error === "object" && error !== null && "errors" in error && isGraphQLErrorArray(error.errors)) {
|
|
50
|
+
const errors = error.errors;
|
|
51
|
+
return new NormalizedError({
|
|
52
|
+
message: errors.map(({ message }) => message).join("\n"),
|
|
53
|
+
errors,
|
|
54
|
+
code: extractCode(errors[0])
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return new NormalizedError({
|
|
58
|
+
message: error instanceof Error ? error.message : String(error),
|
|
59
|
+
code: "INTERNAL_ERROR"
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Header map where:
|
|
3
|
+
* - string → set / override header
|
|
4
|
+
* - null → delete header
|
|
5
|
+
*/
|
|
6
|
+
export type HeadersInput = Record<string, string | null>;
|
|
7
|
+
/**
|
|
8
|
+
* Merge multiple header inputs into a single Headers instance.
|
|
9
|
+
*
|
|
10
|
+
* @param inputs Header input objects (later inputs override earlier).
|
|
11
|
+
* @returns Merged Headers instance.
|
|
12
|
+
*/
|
|
13
|
+
export declare function mergeHeaders(...inputs: Array<HeadersInput | undefined>): Headers;
|