@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.
Files changed (63) hide show
  1. package/README.md +361 -55
  2. package/dist/module.d.mts +33 -21
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +337 -373
  5. package/dist/runtime/app/composables/useGraphQLCache.client.d.ts +10 -0
  6. package/dist/runtime/app/composables/useGraphQLCache.client.js +41 -0
  7. package/dist/runtime/app/composables/useGraphQLMutation.d.ts +17 -8
  8. package/dist/runtime/app/composables/useGraphQLMutation.js +10 -9
  9. package/dist/runtime/app/composables/useGraphQLQuery.d.ts +5 -12
  10. package/dist/runtime/app/composables/useGraphQLQuery.js +74 -28
  11. package/dist/runtime/app/composables/useGraphQLSubscription.d.ts +5 -6
  12. package/dist/runtime/app/composables/useGraphQLSubscription.js +13 -12
  13. package/dist/runtime/app/lib/graphql-cache.d.ts +7 -0
  14. package/dist/runtime/app/lib/graphql-cache.js +24 -0
  15. package/dist/runtime/app/lib/persisted.d.ts +4 -0
  16. package/dist/runtime/app/lib/persisted.js +70 -0
  17. package/dist/runtime/app/plugins/graphql-request.d.ts +7 -0
  18. package/dist/runtime/app/plugins/graphql-request.js +21 -0
  19. package/dist/runtime/app/plugins/graphql-sse.client.d.ts +7 -0
  20. package/dist/runtime/app/plugins/graphql-sse.client.js +15 -0
  21. package/dist/runtime/app/types/nuxt-graphql.d.ts +37 -0
  22. package/dist/runtime/server/api/yoga-handler.js +42 -0
  23. package/dist/runtime/server/lib/define-graphql-context.d.ts +5 -0
  24. package/dist/runtime/server/lib/define-graphql-context.js +4 -0
  25. package/dist/runtime/server/lib/define-remote-exec-middleware.d.ts +30 -0
  26. package/dist/runtime/server/lib/define-remote-exec-middleware.js +3 -0
  27. package/dist/runtime/server/lib/define-yoga-middleware.d.ts +21 -0
  28. package/dist/runtime/server/lib/define-yoga-middleware.js +3 -0
  29. package/dist/runtime/server/lib/execute-server-graphql.d.ts +7 -0
  30. package/dist/runtime/server/lib/execute-server-graphql.js +34 -0
  31. package/dist/runtime/server/lib/remote-executor.d.ts +35 -0
  32. package/dist/runtime/server/lib/remote-executor.js +64 -0
  33. package/dist/runtime/server/tsconfig.json +1 -1
  34. package/dist/runtime/server/utils/useServerGraphQLMutation.d.ts +8 -14
  35. package/dist/runtime/server/utils/useServerGraphQLMutation.js +8 -11
  36. package/dist/runtime/server/utils/useServerGraphQLQuery.d.ts +2 -10
  37. package/dist/runtime/server/utils/useServerGraphQLQuery.js +3 -4
  38. package/dist/runtime/shared/lib/graphql-error.d.ts +17 -0
  39. package/dist/runtime/shared/lib/graphql-error.js +28 -0
  40. package/dist/runtime/shared/lib/headers.d.ts +3 -0
  41. package/dist/runtime/shared/lib/headers.js +39 -0
  42. package/dist/types.d.mts +13 -1
  43. package/package.json +10 -14
  44. package/dist/runtime/app/composables/useGraphQLCache.d.ts +0 -10
  45. package/dist/runtime/app/composables/useGraphQLCache.js +0 -15
  46. package/dist/runtime/app/plugins/graphql.d.ts +0 -31
  47. package/dist/runtime/app/plugins/graphql.js +0 -42
  48. package/dist/runtime/app/utils/graphql-cache.d.ts +0 -36
  49. package/dist/runtime/app/utils/graphql-cache.js +0 -65
  50. package/dist/runtime/app/utils/graphql-error.d.ts +0 -12
  51. package/dist/runtime/app/utils/graphql-error.js +0 -24
  52. package/dist/runtime/server/api/graphql-handler.js +0 -15
  53. package/dist/runtime/server/lib/constants.d.ts +0 -1
  54. package/dist/runtime/server/lib/constants.js +0 -1
  55. package/dist/runtime/server/lib/create-yoga.d.ts +0 -1
  56. package/dist/runtime/server/lib/create-yoga.js +0 -17
  57. package/dist/runtime/server/lib/default-context.d.ts +0 -7
  58. package/dist/runtime/server/lib/default-context.js +0 -1
  59. package/dist/runtime/server/utils/graphql-client.d.ts +0 -14
  60. package/dist/runtime/server/utils/graphql-client.js +0 -14
  61. package/dist/runtime/server/utils/remote-middleware.d.ts +0 -18
  62. package/dist/runtime/server/utils/remote-middleware.js +0 -0
  63. /package/dist/runtime/server/api/{graphql-handler.d.ts → yoga-handler.d.ts} +0 -0
@@ -0,0 +1,3 @@
1
+ export function defineYogaMiddleware(yogaMiddlewareHandler) {
2
+ return { yogaMiddlewareHandler };
3
+ }
@@ -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,3 +1,3 @@
1
1
  {
2
2
  "extends": "../../../.nuxt/tsconfig.server.json",
3
- }
3
+ }
@@ -1,15 +1,9 @@
1
1
  import type { H3Event } from "h3";
2
- import { type MutationName, type MutationResult } from "#graphql/registry";
3
- /**
4
- * Server-side GraphQL mutation composable
5
- *
6
- * @param event H3 event
7
- * @param operationName Mutation operation name
8
- * @returns Object with mutate function
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
- export async function useServerGraphQLMutation(event, operationName) {
4
- const client = getGraphQLClient(event);
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
- try {
7
- const [variables, headers] = args;
8
- const result = await client.request(mutations[operationName], variables, headers);
9
- return { data: result, error: null };
10
- } catch (e) {
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 { IsEmptyObject } from "../../../helpers/is-empty-object.js";
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 client = getGraphQLClient(event);
5
- const [variables, headers] = args;
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,3 @@
1
+ import type { H3Event } from "h3";
2
+ export declare function getClientForwardHeaders(event: H3Event): HeadersInit | undefined;
3
+ export declare function mergeHeaders(...headers: Array<HeadersInit | undefined>): HeadersInit | undefined;
@@ -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 ModuleOptions } from './module.mjs'
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.2.2",
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.0",
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.2",
43
- "@graphql-tools/stitch": "^10.1.6",
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.22.0",
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": "^3.2.4",
67
+ "vitest": "^4.0.17",
72
68
  "vue-tsc": "^3.2.2"
73
69
  },
74
70
  "publishConfig": {
75
71
  "access": "public"
76
72
  },
77
- "changlog": {
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
- }