@lewebsimple/nuxt-graphql 0.2.2 → 0.3.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.
- package/README.md +361 -55
- package/dist/module.d.mts +33 -21
- package/dist/module.json +1 -1
- package/dist/module.mjs +337 -373
- package/dist/runtime/app/composables/useGraphQLCache.client.d.ts +10 -0
- package/dist/runtime/app/composables/useGraphQLCache.client.js +41 -0
- package/dist/runtime/app/composables/useGraphQLMutation.d.ts +17 -8
- package/dist/runtime/app/composables/useGraphQLMutation.js +10 -9
- package/dist/runtime/app/composables/useGraphQLQuery.d.ts +5 -12
- package/dist/runtime/app/composables/useGraphQLQuery.js +74 -28
- package/dist/runtime/app/composables/useGraphQLSubscription.d.ts +5 -6
- package/dist/runtime/app/composables/useGraphQLSubscription.js +13 -12
- package/dist/runtime/app/lib/graphql-cache.d.ts +7 -0
- package/dist/runtime/app/lib/graphql-cache.js +24 -0
- package/dist/runtime/app/lib/persisted.d.ts +4 -0
- package/dist/runtime/app/lib/persisted.js +70 -0
- package/dist/runtime/app/plugins/graphql-request.d.ts +7 -0
- package/dist/runtime/app/plugins/graphql-request.js +21 -0
- package/dist/runtime/app/plugins/graphql-sse.client.d.ts +7 -0
- package/dist/runtime/app/plugins/graphql-sse.client.js +15 -0
- package/dist/runtime/app/types/nuxt-graphql.d.ts +37 -0
- package/dist/runtime/server/api/yoga-handler.js +42 -0
- package/dist/runtime/server/lib/define-graphql-context.d.ts +5 -0
- package/dist/runtime/server/lib/define-graphql-context.js +4 -0
- package/dist/runtime/server/lib/define-remote-exec-middleware.d.ts +30 -0
- package/dist/runtime/server/lib/define-remote-exec-middleware.js +3 -0
- package/dist/runtime/server/lib/define-yoga-middleware.d.ts +21 -0
- package/dist/runtime/server/lib/define-yoga-middleware.js +3 -0
- package/dist/runtime/server/lib/execute-server-graphql.d.ts +7 -0
- package/dist/runtime/server/lib/execute-server-graphql.js +34 -0
- package/dist/runtime/server/lib/remote-executor.d.ts +35 -0
- package/dist/runtime/server/lib/remote-executor.js +64 -0
- package/dist/runtime/server/tsconfig.json +1 -1
- package/dist/runtime/server/utils/useServerGraphQLMutation.d.ts +8 -14
- package/dist/runtime/server/utils/useServerGraphQLMutation.js +8 -11
- package/dist/runtime/server/utils/useServerGraphQLQuery.d.ts +2 -10
- package/dist/runtime/server/utils/useServerGraphQLQuery.js +3 -4
- package/dist/runtime/shared/lib/graphql-error.d.ts +17 -0
- package/dist/runtime/shared/lib/graphql-error.js +28 -0
- package/dist/runtime/shared/lib/headers.d.ts +3 -0
- package/dist/runtime/shared/lib/headers.js +39 -0
- package/dist/types.d.mts +13 -1
- package/package.json +10 -14
- package/dist/runtime/app/composables/useGraphQLCache.d.ts +0 -10
- package/dist/runtime/app/composables/useGraphQLCache.js +0 -15
- package/dist/runtime/app/plugins/graphql.d.ts +0 -31
- package/dist/runtime/app/plugins/graphql.js +0 -42
- package/dist/runtime/app/utils/graphql-cache.d.ts +0 -36
- package/dist/runtime/app/utils/graphql-cache.js +0 -65
- package/dist/runtime/app/utils/graphql-error.d.ts +0 -12
- package/dist/runtime/app/utils/graphql-error.js +0 -24
- package/dist/runtime/server/api/graphql-handler.js +0 -15
- package/dist/runtime/server/lib/constants.d.ts +0 -1
- package/dist/runtime/server/lib/constants.js +0 -1
- package/dist/runtime/server/lib/create-yoga.d.ts +0 -1
- package/dist/runtime/server/lib/create-yoga.js +0 -17
- package/dist/runtime/server/lib/default-context.d.ts +0 -7
- package/dist/runtime/server/lib/default-context.js +0 -1
- package/dist/runtime/server/utils/graphql-client.d.ts +0 -14
- package/dist/runtime/server/utils/graphql-client.js +0 -14
- package/dist/runtime/server/utils/remote-middleware.d.ts +0 -18
- package/dist/runtime/server/utils/remote-middleware.js +0 -0
- /package/dist/runtime/server/api/{graphql-handler.d.ts → yoga-handler.d.ts} +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { H3Event } from "h3";
|
|
2
|
+
import type { DocumentNode } from "graphql";
|
|
3
|
+
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
|
|
4
|
+
export interface ExecuteServerGraphQLOptions {
|
|
5
|
+
headers?: HeadersInit;
|
|
6
|
+
}
|
|
7
|
+
export declare function executeServerGraphQL<TResult = unknown, TVariables extends Record<string, unknown> = Record<string, unknown>>(event: H3Event, document: TypedDocumentNode<TResult, TVariables> | DocumentNode, variables?: TVariables, options?: ExecuteServerGraphQLOptions): Promise<TResult>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { execute } from "graphql";
|
|
2
|
+
import { getGraphQLContext } from "#graphql/context";
|
|
3
|
+
import { schema } from "#graphql/schema";
|
|
4
|
+
import { normalizeGraphQLError } from "../../shared/lib/graphql-error.js";
|
|
5
|
+
async function buildContextWithHeaders(event, headers) {
|
|
6
|
+
if (!headers) {
|
|
7
|
+
return getGraphQLContext(event);
|
|
8
|
+
}
|
|
9
|
+
const headerOverrides = Object.fromEntries(new Headers(headers).entries());
|
|
10
|
+
if (Object.keys(headerOverrides).length === 0) {
|
|
11
|
+
return getGraphQLContext(event);
|
|
12
|
+
}
|
|
13
|
+
const req = event.node.req;
|
|
14
|
+
const originalHeaders = req.headers;
|
|
15
|
+
req.headers = { ...originalHeaders, ...headerOverrides };
|
|
16
|
+
try {
|
|
17
|
+
return await getGraphQLContext(event);
|
|
18
|
+
} finally {
|
|
19
|
+
req.headers = originalHeaders;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function executeServerGraphQL(event, document, variables, options) {
|
|
23
|
+
const contextValue = await buildContextWithHeaders(event, options?.headers);
|
|
24
|
+
const result = await execute({
|
|
25
|
+
schema,
|
|
26
|
+
document,
|
|
27
|
+
variableValues: variables,
|
|
28
|
+
contextValue
|
|
29
|
+
});
|
|
30
|
+
if (result.errors?.length) {
|
|
31
|
+
throw normalizeGraphQLError({ errors: result.errors });
|
|
32
|
+
}
|
|
33
|
+
return result.data;
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Executor } from "@graphql-tools/utils";
|
|
2
|
+
import type { GraphQLContext } from "#graphql/context";
|
|
3
|
+
export type RemoteExecMiddlewareOnRequestArgs = {
|
|
4
|
+
remoteName: string;
|
|
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 {
|
|
30
|
+
url: string;
|
|
31
|
+
remoteName: string;
|
|
32
|
+
headers?: HeadersInit;
|
|
33
|
+
middleware?: RemoteExecMiddlewareHandler;
|
|
34
|
+
}
|
|
35
|
+
export declare function createRemoteExecutor(options: CreateRemoteExecutorOptions): Executor;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { parse, print, getOperationAST } from "graphql";
|
|
2
|
+
import { normalizeGraphQLError } from "../../shared/lib/graphql-error.js";
|
|
3
|
+
export function createRemoteExecutor(options) {
|
|
4
|
+
const { url, remoteName, headers = {}, middleware } = options;
|
|
5
|
+
const { onRequest, onResponse, onError } = middleware ?? {};
|
|
6
|
+
return async ({ document, variables, context, operationName }) => {
|
|
7
|
+
const parsedDocument = typeof document === "string" ? parse(document) : document;
|
|
8
|
+
const op = getOperationAST(parsedDocument, operationName);
|
|
9
|
+
const resolvedOperationName = op?.name?.value ?? operationName ?? "anonymous";
|
|
10
|
+
const query = typeof document === "string" ? document : print(document);
|
|
11
|
+
const graphQLContext = context;
|
|
12
|
+
const requestHeaders = new Headers({ "Content-Type": "application/json", ...headers });
|
|
13
|
+
const fetchOptions = {
|
|
14
|
+
method: "POST",
|
|
15
|
+
headers: requestHeaders,
|
|
16
|
+
body: JSON.stringify({ query, variables, operationName: resolvedOperationName })
|
|
17
|
+
};
|
|
18
|
+
if (onRequest) {
|
|
19
|
+
await onRequest({
|
|
20
|
+
remoteName,
|
|
21
|
+
operationName: resolvedOperationName,
|
|
22
|
+
context: graphQLContext,
|
|
23
|
+
fetchOptions: { headers: requestHeaders }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(url, fetchOptions);
|
|
28
|
+
const safeResponse = response.clone();
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const statusError = normalizeGraphQLError(new Error(`Remote ${remoteName} responded with status ${response.status}`));
|
|
31
|
+
if (onError) {
|
|
32
|
+
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: statusError, response: safeResponse });
|
|
33
|
+
}
|
|
34
|
+
throw statusError;
|
|
35
|
+
}
|
|
36
|
+
if (onResponse) {
|
|
37
|
+
await onResponse({ remoteName, operationName: resolvedOperationName, context: graphQLContext, response: safeResponse });
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const json = await response.json();
|
|
41
|
+
if (json && typeof json === "object" && Array.isArray(json.errors)) {
|
|
42
|
+
const normalized = normalizeGraphQLError({ errors: json.errors });
|
|
43
|
+
if (onError) {
|
|
44
|
+
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: normalized, response: safeResponse });
|
|
45
|
+
}
|
|
46
|
+
throw normalized;
|
|
47
|
+
}
|
|
48
|
+
return json;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
const normalized = normalizeGraphQLError(error);
|
|
51
|
+
if (onError) {
|
|
52
|
+
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: normalized, response: safeResponse });
|
|
53
|
+
}
|
|
54
|
+
throw normalized;
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const normalized = normalizeGraphQLError(error);
|
|
58
|
+
if (onError) {
|
|
59
|
+
await onError({ remoteName, operationName: resolvedOperationName, context: graphQLContext, error: normalized });
|
|
60
|
+
}
|
|
61
|
+
throw normalized;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
-
import { type MutationName, type MutationResult } from "#graphql/registry";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare function useServerGraphQLMutation<N extends MutationName>(event: H3Event, operationName: N): Promise<{
|
|
11
|
-
mutate: (variables: MutationVariables<N>, headers?: HeadersInit | undefined) => Promise<{
|
|
12
|
-
data: MutationResult<N> | null;
|
|
13
|
-
error: Error | null;
|
|
14
|
-
}>;
|
|
15
|
-
}>;
|
|
2
|
+
import { type MutationName, type MutationResult, type MutationVariables } from "#graphql/registry";
|
|
3
|
+
import { type ExecuteServerGraphQLOptions } from "../lib/execute-server-graphql.js";
|
|
4
|
+
export interface ServerMutateOptions {
|
|
5
|
+
headers?: HeadersInit;
|
|
6
|
+
}
|
|
7
|
+
export declare function useServerGraphQLMutation<N extends MutationName>(event: H3Event, operationName: N, options?: ExecuteServerGraphQLOptions): {
|
|
8
|
+
mutate: (...args: IsEmptyObject<MutationVariables<N>> extends true ? [variables?: MutationVariables<N>, mutateOptions?: ServerMutateOptions] : [variables: MutationVariables<N>, mutateOptions?: ServerMutateOptions]) => Promise<MutationResult<N>>;
|
|
9
|
+
};
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { getGraphQLClient } from "./graphql-client.js";
|
|
2
1
|
import { mutations } from "#graphql/registry";
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { executeServerGraphQL } from "../lib/execute-server-graphql.js";
|
|
3
|
+
import { mergeHeaders } from "../../shared/lib/headers.js";
|
|
4
|
+
export function useServerGraphQLMutation(event, operationName, options) {
|
|
5
5
|
async function mutate(...args) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
12
|
-
return { data: null, error };
|
|
13
|
-
}
|
|
6
|
+
const [variables, mutateOptions] = args;
|
|
7
|
+
return executeServerGraphQL(event, mutations[operationName], variables, {
|
|
8
|
+
...options,
|
|
9
|
+
headers: mergeHeaders(options?.headers, mutateOptions?.headers)
|
|
10
|
+
});
|
|
14
11
|
}
|
|
15
12
|
return { mutate };
|
|
16
13
|
}
|
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
2
|
import { type QueryName, type QueryResult, type QueryVariables } from "#graphql/registry";
|
|
3
|
-
import type
|
|
4
|
-
|
|
5
|
-
* Server-side GraphQL query composable
|
|
6
|
-
*
|
|
7
|
-
* @param event H3 event
|
|
8
|
-
* @param operationName Query operation name
|
|
9
|
-
* @param args Variables and optional headers
|
|
10
|
-
* @returns Query result
|
|
11
|
-
*/
|
|
12
|
-
export declare function useServerGraphQLQuery<N extends QueryName>(event: H3Event, operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: QueryVariables<N>, headers?: HeadersInit] : [variables: QueryVariables<N>, headers?: HeadersInit]): Promise<QueryResult<N>>;
|
|
3
|
+
import { type ExecuteServerGraphQLOptions } from "../lib/execute-server-graphql.js";
|
|
4
|
+
export declare function useServerGraphQLQuery<N extends QueryName>(event: H3Event, operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: QueryVariables<N>, options?: ExecuteServerGraphQLOptions] : [variables: QueryVariables<N>, options?: ExecuteServerGraphQLOptions]): Promise<QueryResult<N>>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { getGraphQLClient } from "./graphql-client.js";
|
|
2
1
|
import { queries } from "#graphql/registry";
|
|
2
|
+
import { executeServerGraphQL } from "../lib/execute-server-graphql.js";
|
|
3
3
|
export async function useServerGraphQLQuery(event, operationName, ...args) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
return client.request(queries[operationName], variables, headers);
|
|
4
|
+
const [variables, options] = args;
|
|
5
|
+
return executeServerGraphQL(event, queries[operationName], variables, options);
|
|
7
6
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GraphQLError } from "graphql";
|
|
2
|
+
export declare class NormalizedGraphQLError extends Error {
|
|
3
|
+
readonly errors: GraphQLError[];
|
|
4
|
+
constructor(message: string, errors?: GraphQLError[]);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a generic error into a NormalizedGraphQLError
|
|
8
|
+
*
|
|
9
|
+
* @param error Generic error from various sources
|
|
10
|
+
* @returns Normalized GraphQL error
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeGraphQLError(error: unknown): NormalizedGraphQLError;
|
|
13
|
+
declare module "#app" {
|
|
14
|
+
interface RuntimeNuxtHooks {
|
|
15
|
+
"graphql:error": (error: NormalizedGraphQLError) => void;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class NormalizedGraphQLError extends Error {
|
|
2
|
+
errors;
|
|
3
|
+
constructor(message, errors = []) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "NormalizedGraphQLError";
|
|
6
|
+
this.errors = errors;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function normalizeGraphQLError(error) {
|
|
10
|
+
if (error instanceof NormalizedGraphQLError) {
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
if (error && typeof error === "object" && "message" in error && "locations" in error) {
|
|
14
|
+
const graphQLError = error;
|
|
15
|
+
return new NormalizedGraphQLError(graphQLError.message, [graphQLError]);
|
|
16
|
+
}
|
|
17
|
+
if (error && typeof error === "object" && "response" in error) {
|
|
18
|
+
const clientError = error;
|
|
19
|
+
return new NormalizedGraphQLError(clientError.message, clientError.response?.errors);
|
|
20
|
+
}
|
|
21
|
+
if (error && typeof error === "object" && "errors" in error) {
|
|
22
|
+
const { errors } = error;
|
|
23
|
+
const message2 = errors.map((e) => e.message).join(", ");
|
|
24
|
+
return new NormalizedGraphQLError(message2, errors);
|
|
25
|
+
}
|
|
26
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
+
return new NormalizedGraphQLError(message);
|
|
28
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getRequestHeader } from "h3";
|
|
2
|
+
export function getClientForwardHeaders(event) {
|
|
3
|
+
if (import.meta.client) return void 0;
|
|
4
|
+
const allowedHeaders = [
|
|
5
|
+
"accept-language",
|
|
6
|
+
"authorization",
|
|
7
|
+
"cookie",
|
|
8
|
+
"user-agent",
|
|
9
|
+
"x-forwarded-for",
|
|
10
|
+
"x-forwarded-host",
|
|
11
|
+
"x-forwarded-proto",
|
|
12
|
+
"x-request-id"
|
|
13
|
+
];
|
|
14
|
+
const headers = new Headers();
|
|
15
|
+
let hasHeaders = false;
|
|
16
|
+
for (const header of allowedHeaders) {
|
|
17
|
+
const value = getRequestHeader(event, header);
|
|
18
|
+
if (value) {
|
|
19
|
+
headers.set(header, Array.isArray(value) ? value.join(", ") : value);
|
|
20
|
+
hasHeaders = true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return hasHeaders ? headers : void 0;
|
|
24
|
+
}
|
|
25
|
+
export function mergeHeaders(...headers) {
|
|
26
|
+
const present = headers.filter(Boolean);
|
|
27
|
+
if (present.length === 0) return void 0;
|
|
28
|
+
if (present.length === 1) return present[0];
|
|
29
|
+
if (typeof Headers === "undefined") {
|
|
30
|
+
throw new TypeError("mergeHeaders: Node 18+ is required (global Headers is not available).");
|
|
31
|
+
}
|
|
32
|
+
const merged = new Headers();
|
|
33
|
+
for (const headerInit of present) {
|
|
34
|
+
for (const [key, value] of new Headers(headerInit).entries()) {
|
|
35
|
+
merged.set(key, value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return merged;
|
|
39
|
+
}
|
package/dist/types.d.mts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import type { NuxtModule } from '@nuxt/schema'
|
|
2
|
+
|
|
3
|
+
import type { default as Module } from './module.mjs'
|
|
4
|
+
|
|
5
|
+
export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
|
6
|
+
|
|
7
|
+
export { type defineGraphQLContext } from '../dist/runtime/server/lib/define-graphql-context.js'
|
|
8
|
+
|
|
9
|
+
export { type defineRemoteExecMiddleware } from '../dist/runtime/server/lib/define-remote-exec-middleware.js'
|
|
10
|
+
|
|
11
|
+
export { type defineYogaMiddleware } from '../dist/runtime/server/lib/define-yoga-middleware.js'
|
|
12
|
+
|
|
1
13
|
export { default } from './module.mjs'
|
|
2
14
|
|
|
3
|
-
export { type
|
|
15
|
+
export { type NuxtGraphQLModuleOptions } from './module.mjs'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewebsimple/nuxt-graphql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Opinionated Nuxt module for using GraphQL",
|
|
5
5
|
"repository": "lewebsimple/nuxt-graphql",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,24 +25,21 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"prepack": "nuxt-module-build build",
|
|
27
27
|
"dev": "npm run dev:prepare && nuxi dev playground",
|
|
28
|
-
"dev:build": "nuxi build playground",
|
|
29
|
-
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
28
|
+
"dev:build": "export PLAYGROUND_MODULE_BUILD=true && nuxi build playground",
|
|
29
|
+
"dev:prepare": "export PLAYGROUND_MODULE_BUILD=true && nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
30
30
|
"release": "npm run lint && npm run test && npm run prepack && changelogen --release --push && npm publish",
|
|
31
31
|
"lint": "eslint .",
|
|
32
32
|
"test": "vitest run",
|
|
33
33
|
"test:watch": "vitest watch",
|
|
34
|
-
"test:coverage": "vitest run --coverage",
|
|
35
34
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
36
35
|
},
|
|
37
36
|
"dependencies": {
|
|
38
|
-
"@graphql-codegen/cli": "^6.1.
|
|
37
|
+
"@graphql-codegen/cli": "^6.1.1",
|
|
39
38
|
"@graphql-codegen/typed-document-node": "^6.1.5",
|
|
40
39
|
"@graphql-codegen/typescript": "^5.0.7",
|
|
41
40
|
"@graphql-codegen/typescript-operations": "^5.0.7",
|
|
42
|
-
"@graphql-tools/delegate": "^12.0.
|
|
43
|
-
"@graphql-tools/stitch": "^10.1.
|
|
44
|
-
"@graphql-tools/utils": "^10.11.0",
|
|
45
|
-
"@graphql-tools/wrap": "^11.1.2",
|
|
41
|
+
"@graphql-tools/delegate": "^12.0.4",
|
|
42
|
+
"@graphql-tools/stitch": "^10.1.8",
|
|
46
43
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
47
44
|
"@nuxt/kit": "^4.2.2",
|
|
48
45
|
"graphql": "^16.12.0",
|
|
@@ -61,22 +58,21 @@
|
|
|
61
58
|
"@nuxt/eslint-config": "^1.12.1",
|
|
62
59
|
"@nuxt/module-builder": "^1.0.2",
|
|
63
60
|
"@nuxt/schema": "^4.2.2",
|
|
64
|
-
"@nuxt/test-utils": "^3.
|
|
61
|
+
"@nuxt/test-utils": "^3.23.0",
|
|
65
62
|
"@types/node": "latest",
|
|
66
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
67
63
|
"changelogen": "^0.6.2",
|
|
68
64
|
"eslint": "^9.39.2",
|
|
69
65
|
"nuxt": "^4.2.2",
|
|
70
66
|
"typescript": "~5.9.3",
|
|
71
|
-
"vitest": "^
|
|
67
|
+
"vitest": "^4.0.17",
|
|
72
68
|
"vue-tsc": "^3.2.2"
|
|
73
69
|
},
|
|
74
70
|
"publishConfig": {
|
|
75
71
|
"access": "public"
|
|
76
72
|
},
|
|
77
|
-
"
|
|
73
|
+
"changelog": {
|
|
78
74
|
"types": {
|
|
79
75
|
"chore": false
|
|
80
76
|
}
|
|
81
77
|
}
|
|
82
|
-
}
|
|
78
|
+
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { QueryName, QueryVariables } from "#graphql/registry";
|
|
2
|
-
/**
|
|
3
|
-
* GraphQL cache management composable
|
|
4
|
-
*
|
|
5
|
-
* @returns Object with enabled flag and invalidate function
|
|
6
|
-
*/
|
|
7
|
-
export declare function useGraphQLCache(): {
|
|
8
|
-
enabled: boolean;
|
|
9
|
-
invalidate: <N extends QueryName>(operationName?: N, variables?: QueryVariables<N>) => Promise<void>;
|
|
10
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { useRuntimeConfig } from "#imports";
|
|
2
|
-
import { cacheInvalidate, initCache } from "../utils/graphql-cache.js";
|
|
3
|
-
export function useGraphQLCache() {
|
|
4
|
-
const { public: { graphql: { cache: cacheConfig } } } = useRuntimeConfig();
|
|
5
|
-
if (import.meta.client && cacheConfig.enabled) {
|
|
6
|
-
initCache(cacheConfig.storage);
|
|
7
|
-
}
|
|
8
|
-
async function invalidate(operationName, variables) {
|
|
9
|
-
await cacheInvalidate(operationName, variables);
|
|
10
|
-
}
|
|
11
|
-
return {
|
|
12
|
-
enabled: cacheConfig.enabled,
|
|
13
|
-
invalidate
|
|
14
|
-
};
|
|
15
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { GraphQLClient } from "graphql-request";
|
|
2
|
-
import { type Client as SSEClient } from "graphql-sse";
|
|
3
|
-
import { type GraphQLClientError } from "../utils/graphql-error.js";
|
|
4
|
-
import type { GraphQLCacheConfig } from "../utils/graphql-cache.js";
|
|
5
|
-
declare const _default: import("#app").Plugin<{
|
|
6
|
-
graphql: () => GraphQLClient;
|
|
7
|
-
graphqlSSE: () => SSEClient;
|
|
8
|
-
}> & import("#app").ObjectPlugin<{
|
|
9
|
-
graphql: () => GraphQLClient;
|
|
10
|
-
graphqlSSE: () => SSEClient;
|
|
11
|
-
}>;
|
|
12
|
-
export default _default;
|
|
13
|
-
declare module "#app" {
|
|
14
|
-
interface NuxtApp {
|
|
15
|
-
$graphql: () => GraphQLClient;
|
|
16
|
-
$graphqlSSE: () => SSEClient;
|
|
17
|
-
}
|
|
18
|
-
interface RuntimeNuxtHooks {
|
|
19
|
-
"graphql:error": (error: GraphQLClientError) => void;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
declare module "nuxt/schema" {
|
|
23
|
-
interface PublicRuntimeConfig {
|
|
24
|
-
graphql: {
|
|
25
|
-
endpoint: string;
|
|
26
|
-
headers: Record<string, string>;
|
|
27
|
-
cache: GraphQLCacheConfig;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { GraphQLClient } from "graphql-request";
|
|
2
|
-
import { createClient } from "graphql-sse";
|
|
3
|
-
import { defineNuxtPlugin, useRequestHeaders, useRequestURL, useRuntimeConfig } from "#imports";
|
|
4
|
-
import { wrapError } from "../utils/graphql-error.js";
|
|
5
|
-
export default defineNuxtPlugin((nuxtApp) => {
|
|
6
|
-
const { public: { graphql: { endpoint, headers: staticHeaders } } } = useRuntimeConfig();
|
|
7
|
-
const { origin } = useRequestURL();
|
|
8
|
-
const url = `${origin}${endpoint}`;
|
|
9
|
-
const getClient = () => {
|
|
10
|
-
const headers = { ...staticHeaders };
|
|
11
|
-
if (import.meta.server) {
|
|
12
|
-
const ssrHeaders = useRequestHeaders(["cookie", "authorization"]);
|
|
13
|
-
Object.assign(headers, ssrHeaders);
|
|
14
|
-
}
|
|
15
|
-
const client = new GraphQLClient(url, {
|
|
16
|
-
headers,
|
|
17
|
-
responseMiddleware: (response) => {
|
|
18
|
-
if (response instanceof Error) {
|
|
19
|
-
nuxtApp.callHook("graphql:error", wrapError(response));
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
return client;
|
|
24
|
-
};
|
|
25
|
-
let sseClient = null;
|
|
26
|
-
const getSSEClient = () => {
|
|
27
|
-
if (import.meta.server) {
|
|
28
|
-
throw new Error("SSE subscriptions are not available on the server");
|
|
29
|
-
}
|
|
30
|
-
if (!sseClient) {
|
|
31
|
-
sseClient = createClient({ url, headers: staticHeaders });
|
|
32
|
-
}
|
|
33
|
-
return sseClient;
|
|
34
|
-
};
|
|
35
|
-
return {
|
|
36
|
-
provide: {
|
|
37
|
-
graphql: getClient,
|
|
38
|
-
graphqlSSE: getSSEClient
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
});
|
|
42
|
-
export {};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export interface GraphQLCacheConfig {
|
|
2
|
-
enabled: boolean;
|
|
3
|
-
ttl: number;
|
|
4
|
-
storage: "memory" | "localStorage";
|
|
5
|
-
}
|
|
6
|
-
export interface CacheOptions {
|
|
7
|
-
enabled?: boolean;
|
|
8
|
-
ttl?: number;
|
|
9
|
-
}
|
|
10
|
-
export declare function initCache(type: "memory" | "localStorage"): void;
|
|
11
|
-
export declare function getCacheKey(operationName: string, variables: unknown): string;
|
|
12
|
-
export declare function cacheGet<T>(operationName: string, variables: unknown): Promise<T | null>;
|
|
13
|
-
export declare function cacheSet<T>(operationName: string, variables: unknown, data: T, ttl: number): Promise<void>;
|
|
14
|
-
export declare function dedupeGet(operationName: string, variables: unknown): Promise<unknown> | null;
|
|
15
|
-
export declare function dedupeSet(operationName: string, variables: unknown, promise: Promise<unknown>): void;
|
|
16
|
-
/**
|
|
17
|
-
* Register a refresh callback for a query
|
|
18
|
-
*
|
|
19
|
-
* @param operationName GraphQL operation name
|
|
20
|
-
* @param variables Query variables
|
|
21
|
-
* @param refresh Callback to execute on cache invalidation
|
|
22
|
-
* @returns Unregister function
|
|
23
|
-
*/
|
|
24
|
-
export declare function registerRefresh(operationName: string, variables: unknown, refresh: () => void): () => void;
|
|
25
|
-
/**
|
|
26
|
-
* Invalidate cached queries and trigger refresh callbacks
|
|
27
|
-
*
|
|
28
|
-
* @param operationName Optional operation name to filter invalidation
|
|
29
|
-
* @param variables Optional variables to target specific query
|
|
30
|
-
*
|
|
31
|
-
* Usage:
|
|
32
|
-
* - No args: invalidate all queries
|
|
33
|
-
* - operationName only: invalidate all queries with that name
|
|
34
|
-
* - operationName + variables: invalidate specific query
|
|
35
|
-
*/
|
|
36
|
-
export declare function cacheInvalidate(operationName?: string, variables?: unknown): Promise<void>;
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { createStorage } from "unstorage";
|
|
2
|
-
import memoryDriver from "unstorage/drivers/memory";
|
|
3
|
-
import localStorageDriver from "unstorage/drivers/localstorage";
|
|
4
|
-
import { hash } from "ohash";
|
|
5
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
6
|
-
const refreshCallbacks = /* @__PURE__ */ new Map();
|
|
7
|
-
let storage = null;
|
|
8
|
-
export function initCache(type) {
|
|
9
|
-
if (storage) return;
|
|
10
|
-
storage = createStorage({
|
|
11
|
-
driver: type === "localStorage" ? localStorageDriver({ base: "graphql:" }) : memoryDriver()
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
export function getCacheKey(operationName, variables) {
|
|
15
|
-
return `${operationName}:${hash(variables ?? {})}`;
|
|
16
|
-
}
|
|
17
|
-
export async function cacheGet(operationName, variables) {
|
|
18
|
-
if (!storage) return null;
|
|
19
|
-
const key = getCacheKey(operationName, variables);
|
|
20
|
-
const entry = await storage.getItem(key);
|
|
21
|
-
if (!entry) return null;
|
|
22
|
-
if (Date.now() > entry.expires) {
|
|
23
|
-
await storage.removeItem(key);
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
return entry.data;
|
|
27
|
-
}
|
|
28
|
-
export async function cacheSet(operationName, variables, data, ttl) {
|
|
29
|
-
if (!storage) return;
|
|
30
|
-
const key = getCacheKey(operationName, variables);
|
|
31
|
-
const entry = { data, expires: Date.now() + ttl };
|
|
32
|
-
await storage.setItem(key, entry);
|
|
33
|
-
}
|
|
34
|
-
export function dedupeGet(operationName, variables) {
|
|
35
|
-
const key = getCacheKey(operationName, variables);
|
|
36
|
-
return inFlight.get(key) ?? null;
|
|
37
|
-
}
|
|
38
|
-
export function dedupeSet(operationName, variables, promise) {
|
|
39
|
-
const key = getCacheKey(operationName, variables);
|
|
40
|
-
inFlight.set(key, promise);
|
|
41
|
-
promise.finally(() => inFlight.delete(key));
|
|
42
|
-
}
|
|
43
|
-
export function registerRefresh(operationName, variables, refresh) {
|
|
44
|
-
const key = getCacheKey(operationName, variables);
|
|
45
|
-
if (!refreshCallbacks.has(key)) {
|
|
46
|
-
refreshCallbacks.set(key, /* @__PURE__ */ new Set());
|
|
47
|
-
}
|
|
48
|
-
refreshCallbacks.get(key).add(refresh);
|
|
49
|
-
return () => refreshCallbacks.get(key)?.delete(refresh);
|
|
50
|
-
}
|
|
51
|
-
export async function cacheInvalidate(operationName, variables) {
|
|
52
|
-
if (!storage) return;
|
|
53
|
-
if (operationName && variables !== void 0) {
|
|
54
|
-
const key = getCacheKey(operationName, variables);
|
|
55
|
-
await storage.removeItem(key);
|
|
56
|
-
refreshCallbacks.get(key)?.forEach((cb) => cb());
|
|
57
|
-
} else {
|
|
58
|
-
const keys = await storage.getKeys();
|
|
59
|
-
const toRemove = operationName ? keys.filter((k) => k.startsWith(`${operationName}:`)) : keys;
|
|
60
|
-
await Promise.all(toRemove.map((k) => storage.removeItem(k)));
|
|
61
|
-
for (const key of toRemove) {
|
|
62
|
-
refreshCallbacks.get(key)?.forEach((cb) => cb());
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { GraphQLError } from "graphql";
|
|
2
|
-
export declare class GraphQLClientError extends Error {
|
|
3
|
-
readonly errors: GraphQLError[];
|
|
4
|
-
constructor(message: string, errors?: GraphQLError[]);
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Wrap a generic error into a GraphQLClientError
|
|
8
|
-
*
|
|
9
|
-
* @param error Generic error from various sources
|
|
10
|
-
* @returns Wrapped GraphQLClientError
|
|
11
|
-
*/
|
|
12
|
-
export declare function wrapError(error: unknown): GraphQLClientError;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export class GraphQLClientError extends Error {
|
|
2
|
-
errors;
|
|
3
|
-
constructor(message, errors = []) {
|
|
4
|
-
super(message);
|
|
5
|
-
this.name = "GraphQLClientError";
|
|
6
|
-
this.errors = errors;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
export function wrapError(error) {
|
|
10
|
-
if (error instanceof GraphQLClientError) {
|
|
11
|
-
return error;
|
|
12
|
-
}
|
|
13
|
-
if (error && typeof error === "object" && "response" in error) {
|
|
14
|
-
const clientError = error;
|
|
15
|
-
return new GraphQLClientError(clientError.message, clientError.response?.errors);
|
|
16
|
-
}
|
|
17
|
-
if (error && typeof error === "object" && "errors" in error) {
|
|
18
|
-
const { errors } = error;
|
|
19
|
-
const message2 = errors.map((e) => e.message).join(", ");
|
|
20
|
-
return new GraphQLClientError(message2, errors);
|
|
21
|
-
}
|
|
22
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
23
|
-
return new GraphQLClientError(message);
|
|
24
|
-
}
|