@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,10 @@
1
+ export type InvalidateCacheLayer = "both" | "memory" | "persisted";
2
+ export interface InvalidateCacheOptions {
3
+ layer?: InvalidateCacheLayer;
4
+ }
5
+ export declare function useGraphQLCache(): {
6
+ readonly cacheConfig: any;
7
+ readonly invalidateByKey: (operationName: string, variables: unknown, options?: InvalidateCacheOptions) => Promise<void>;
8
+ readonly invalidateByOperation: (operationName: string, options?: InvalidateCacheOptions) => Promise<void>;
9
+ readonly invalidateAll: (options?: InvalidateCacheOptions) => Promise<void>;
10
+ };
@@ -0,0 +1,41 @@
1
+ import { clearNuxtData, useRuntimeConfig } from "#imports";
2
+ import { getCacheKeyParts } from "../lib/graphql-cache.js";
3
+ import { deletePersistedByPrefix, deletePersistedEntry } from "../lib/persisted.js";
4
+ export function useGraphQLCache() {
5
+ const { public: { graphql: { cacheConfig } } } = useRuntimeConfig();
6
+ async function invalidateByKey(operationName, variables, options) {
7
+ const cacheLayer = options?.layer ?? "both";
8
+ const { key } = getCacheKeyParts(cacheConfig, operationName, variables);
9
+ if (cacheLayer === "both" || cacheLayer === "memory") {
10
+ clearNuxtData(key);
11
+ }
12
+ if (cacheLayer === "both" || cacheLayer === "persisted") {
13
+ await deletePersistedEntry(key);
14
+ }
15
+ }
16
+ async function invalidateByOperation(operationName, options) {
17
+ const cacheLayer = options?.layer ?? "both";
18
+ const { opPrefix } = getCacheKeyParts(cacheConfig, operationName, {});
19
+ if (cacheLayer === "both" || cacheLayer === "memory") {
20
+ clearNuxtData((k) => k.startsWith(opPrefix));
21
+ }
22
+ if (cacheLayer === "both" || cacheLayer === "persisted") {
23
+ await deletePersistedByPrefix(opPrefix);
24
+ }
25
+ }
26
+ async function invalidateAll(options) {
27
+ const cacheLayer = options?.layer ?? "both";
28
+ if (cacheLayer === "both" || cacheLayer === "memory") {
29
+ clearNuxtData((k) => k.startsWith(`${cacheConfig.keyPrefix}:${cacheConfig.cacheVersion}:`));
30
+ }
31
+ if (cacheLayer === "both" || cacheLayer === "persisted") {
32
+ await deletePersistedByPrefix(`${cacheConfig.keyPrefix}:${cacheConfig.cacheVersion}:`);
33
+ }
34
+ }
35
+ return {
36
+ cacheConfig,
37
+ invalidateByKey,
38
+ invalidateByOperation,
39
+ invalidateAll
40
+ };
41
+ }
@@ -1,14 +1,23 @@
1
- import { type MutationName, type MutationResult } from "#graphql/registry";
1
+ import { type MutationName, type MutationVariables } from "#graphql/registry";
2
+ export interface UseGraphQLMutationOptions {
3
+ headers?: HeadersInit;
4
+ }
5
+ export interface MutateOptions {
6
+ headers?: HeadersInit;
7
+ }
2
8
  /**
3
- * Client-side GraphQL mutation composable
9
+ * GraphQL mutation composable
4
10
  *
5
- * @param operationName Mutation operation name
6
- * @returns Object with mutate function and pending state
11
+ * @param operationName Name of the GraphQL mutation operation in the registry
12
+ * @returns Mutation handler
7
13
  */
8
- export declare function useGraphQLMutation<N extends MutationName>(operationName: N): {
9
- mutate: (variables: MutationVariables<N>, headers?: HeadersInit | undefined) => Promise<{
10
- data: MutationResult<N> | null;
11
- error: Error | null;
14
+ export declare function useGraphQLMutation<N extends MutationName>(operationName: N, options?: UseGraphQLMutationOptions): {
15
+ mutate: (...args: IsEmptyObject<MutationVariables<N>> extends true ? [variables?: MutationVariables<N>, mutateOptions?: MutateOptions] : [variables: MutationVariables<N>, mutateOptions?: MutateOptions]) => Promise<{
16
+ data: any;
17
+ error: null;
18
+ } | {
19
+ data: null;
20
+ error: import("../../shared/lib/graphql-error.js").NormalizedGraphQLError;
12
21
  }>;
13
22
  pending: import("vue").Ref<boolean, boolean>;
14
23
  };
@@ -1,19 +1,20 @@
1
- import { ref } from "vue";
2
- import { useNuxtApp } from "#imports";
1
+ import { ref, useNuxtApp } from "#imports";
3
2
  import { mutations } from "#graphql/registry";
4
- import { wrapError } from "../utils/graphql-error.js";
5
- export function useGraphQLMutation(operationName) {
3
+ import { normalizeGraphQLError } from "../../shared/lib/graphql-error.js";
4
+ import { mergeHeaders } from "../../shared/lib/headers.js";
5
+ export function useGraphQLMutation(operationName, options) {
6
+ const { $getGraphQLClient } = useNuxtApp();
6
7
  const document = mutations[operationName];
7
- const { $graphql } = useNuxtApp();
8
8
  const pending = ref(false);
9
9
  async function mutate(...args) {
10
10
  pending.value = true;
11
11
  try {
12
- const [variables, headers] = args;
13
- const result = await $graphql().request(document, variables, headers);
14
- return { data: result, error: null };
12
+ const [variables, mutateOptions] = args;
13
+ const headers = mergeHeaders(options?.headers, mutateOptions?.headers);
14
+ const data = await $getGraphQLClient().request(document, variables, headers);
15
+ return { data, error: null };
15
16
  } catch (error) {
16
- return { data: null, error: wrapError(error) };
17
+ return { data: null, error: normalizeGraphQLError(error) };
17
18
  } finally {
18
19
  pending.value = false;
19
20
  }
@@ -1,16 +1,9 @@
1
- import type { AsyncData, AsyncDataOptions } from "#app";
1
+ import type { AsyncDataOptions } from "#app";
2
+ import { useAsyncData, type MaybeRefOrGetter } from "#imports";
2
3
  import { type QueryName, type QueryResult, type QueryVariables } from "#graphql/registry";
3
- import { type CacheOptions } from "../utils/graphql-cache.js";
4
- import type { IsEmptyObject } from "../../../helpers/is-empty-object.js";
4
+ import { type CacheConfig } from "../../../helpers/cache-config.js";
5
5
  export interface UseGraphQLQueryOptions<T> extends AsyncDataOptions<T> {
6
- cache?: CacheOptions | false;
7
6
  headers?: HeadersInit;
7
+ cache?: CacheConfig;
8
8
  }
9
- /**
10
- * Client-side GraphQL query composable with caching and deduplication
11
- *
12
- * @param operationName Query operation name
13
- * @param args Variables and optional configuration
14
- * @returns AsyncData object with query result
15
- */
16
- export declare function useGraphQLQuery<N extends QueryName>(operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: QueryVariables<N>, options?: UseGraphQLQueryOptions<QueryResult<N>>] : [variables: QueryVariables<N>, options?: UseGraphQLQueryOptions<QueryResult<N>>]): AsyncData<QueryResult<N>, Error | null>;
9
+ export declare function useGraphQLQuery<N extends QueryName>(operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: MaybeRefOrGetter<QueryVariables<N>>, options?: UseGraphQLQueryOptions<QueryResult<N>>] : [variables: MaybeRefOrGetter<QueryVariables<N>>, options?: UseGraphQLQueryOptions<QueryResult<N>>]): ReturnType<typeof useAsyncData<QueryResult<N>>>;
@@ -1,37 +1,83 @@
1
- import { useAsyncData, useNuxtApp, useRuntimeConfig, onScopeDispose } from "#imports";
1
+ import { computed, toValue, useAsyncData, useNuxtApp, useNuxtData, useRuntimeConfig } from "#imports";
2
2
  import { queries } from "#graphql/registry";
3
- import { cacheGet, cacheSet, dedupeGet, dedupeSet, registerRefresh, initCache, getCacheKey } from "../utils/graphql-cache.js";
3
+ import { resolveCacheConfig } from "../../../helpers/cache-config";
4
+ import { getCacheKeyParts, getInFlightRequests } from "../lib/graphql-cache.js";
5
+ import { getPersistedEntry, setPersistedEntry } from "../lib/persisted.js";
4
6
  export function useGraphQLQuery(operationName, ...args) {
5
- const { $graphql } = useNuxtApp();
6
- const { public: { graphql: { cache: globalCache } } } = useRuntimeConfig();
7
+ const { $getGraphQLClient } = useNuxtApp();
8
+ const inFlightRequests = getInFlightRequests();
9
+ const isClient = import.meta.client;
10
+ const { public: { graphql: { cacheConfig: runtimeCacheConfig } } } = useRuntimeConfig();
7
11
  const document = queries[operationName];
8
12
  const [variables, options] = args;
9
- const cacheEnabled = options?.cache !== false && globalCache.enabled;
10
- const cacheTtl = options?.cache === false ? 0 : options?.cache?.ttl ?? globalCache.ttl;
11
- if (import.meta.client && cacheEnabled) {
12
- initCache(globalCache.storage);
13
+ const { headers, cache, ...asyncDataOptions } = options ?? {};
14
+ const cacheConfig = resolveCacheConfig(runtimeCacheConfig, cache);
15
+ const cacheKey = computed(() => getCacheKeyParts(cacheConfig, operationName, toValue(variables)).key);
16
+ async function executeNetwork() {
17
+ const existing = inFlightRequests.get(cacheKey.value);
18
+ if (existing) {
19
+ return existing;
20
+ }
21
+ const promise = $getGraphQLClient().request(document, toValue(variables), headers).then((result) => {
22
+ if (isClient && cacheConfig.ttl !== void 0) {
23
+ setPersistedEntry(cacheKey.value, result, cacheConfig.ttl);
24
+ }
25
+ return result;
26
+ });
27
+ inFlightRequests.set(cacheKey.value, promise);
28
+ promise.finally(() => {
29
+ inFlightRequests.delete(cacheKey.value);
30
+ });
31
+ return promise;
13
32
  }
14
- const key = getCacheKey(operationName, variables);
15
- const fetcher = async () => {
16
- if (import.meta.client && cacheEnabled) {
17
- const cached = await cacheGet(operationName, variables);
18
- if (cached) return cached;
33
+ ;
34
+ async function asyncDataHandler() {
35
+ const nuxtData = useNuxtData(cacheKey.value);
36
+ let cachedValue = nuxtData.data.value;
37
+ if (cacheConfig.cachePolicy === "no-cache") {
38
+ return await executeNetwork();
39
+ }
40
+ if (isClient && cachedValue === void 0 && cacheConfig.ttl !== void 0) {
41
+ const persisted = await getPersistedEntry(cacheKey.value);
42
+ if (persisted !== void 0) {
43
+ cachedValue = nuxtData.data.value = persisted;
44
+ }
19
45
  }
20
- const inFlight = dedupeGet(operationName, variables);
21
- if (inFlight) return inFlight;
22
- const promise = $graphql().request(document, variables, options?.headers);
23
- dedupeSet(operationName, variables, promise);
24
- const result = await promise;
25
- if (import.meta.client && cacheEnabled && cacheTtl) {
26
- await cacheSet(operationName, variables, result, cacheTtl);
46
+ switch (cacheConfig.cachePolicy) {
47
+ // Cache-first: return cached value if exists, else fetch from network
48
+ case "cache-first": {
49
+ if (cachedValue !== void 0) {
50
+ return cachedValue;
51
+ }
52
+ return await executeNetwork();
53
+ }
54
+ // Network-first: try network request, fallback to cache on error
55
+ case "network-first": {
56
+ try {
57
+ return await executeNetwork();
58
+ } catch (error) {
59
+ if (cachedValue !== void 0) {
60
+ return cachedValue;
61
+ }
62
+ throw error;
63
+ }
64
+ }
65
+ // Stale-while-revalidate: return cached value if exists, then revalidate in background
66
+ case "swr": {
67
+ if (cachedValue !== void 0) {
68
+ if (!inFlightRequests.has(cacheKey.value)) {
69
+ executeNetwork().then((result) => {
70
+ nuxtData.data.value = result;
71
+ }).catch(() => {
72
+ });
73
+ }
74
+ return cachedValue;
75
+ }
76
+ return await executeNetwork();
77
+ }
78
+ default:
79
+ return await executeNetwork();
27
80
  }
28
- return result;
29
- };
30
- const { cache: _cache, headers: _headers, ...asyncDataOptions } = options ?? {};
31
- const asyncData = useAsyncData(key, fetcher, asyncDataOptions);
32
- if (import.meta.client) {
33
- const unregister = registerRefresh(operationName, variables, () => asyncData.refresh());
34
- onScopeDispose(unregister);
35
81
  }
36
- return asyncData;
82
+ return useAsyncData(cacheKey, asyncDataHandler, asyncDataOptions);
37
83
  }
@@ -1,18 +1,17 @@
1
1
  import { type MaybeRefOrGetter, type Ref } from "vue";
2
2
  import { type SubscriptionName, type SubscriptionResult, type SubscriptionVariables } from "#graphql/registry";
3
- import { type GraphQLClientError } from "../utils/graphql-error.js";
4
- import type { IsEmptyObject } from "../../../helpers/is-empty-object.js";
3
+ import { type NormalizedGraphQLError } from "../../shared/lib/graphql-error.js";
5
4
  export type UseGraphQLSubscriptionReturn<N extends SubscriptionName> = {
6
5
  data: Ref<SubscriptionResult<N> | null>;
7
- error: Ref<GraphQLClientError | null>;
6
+ error: Ref<NormalizedGraphQLError | null>;
8
7
  start: () => void;
9
8
  stop: () => void;
10
9
  };
11
10
  /**
12
- * Client-side GraphQL subscription composable using SSE
11
+ * GraphQL subscription composable (client-side only)
13
12
  *
14
13
  * @param operationName Subscription operation name
15
- * @param args Variables (can be reactive)
16
- * @returns Object with data, error, start, and stop
14
+ * @param args Variables (reactive or not)
15
+ * @returns Object with reactive data, error and start / stop helpers
17
16
  */
18
17
  export declare function useGraphQLSubscription<N extends SubscriptionName>(operationName: N, ...args: IsEmptyObject<SubscriptionVariables<N>> extends true ? [variables?: MaybeRefOrGetter<SubscriptionVariables<N>>] : [variables: MaybeRefOrGetter<SubscriptionVariables<N>>]): UseGraphQLSubscriptionReturn<N>;
@@ -1,18 +1,21 @@
1
- import { ref, onScopeDispose, toValue } from "vue";
1
+ import { shallowRef, onScopeDispose, toValue } from "vue";
2
2
  import { print } from "graphql";
3
3
  import { useNuxtApp } from "#imports";
4
4
  import { subscriptions } from "#graphql/registry";
5
- import { wrapError } from "../utils/graphql-error.js";
5
+ import { normalizeGraphQLError } from "../../shared/lib/graphql-error.js";
6
6
  export function useGraphQLSubscription(operationName, ...args) {
7
- const { $graphqlSSE } = useNuxtApp();
7
+ if (import.meta.server) {
8
+ throw new Error("useGraphQLSubscription is not available on the server");
9
+ }
10
+ const { $getGraphQLSSEClient } = useNuxtApp();
8
11
  const [variables] = args;
9
- const data = ref(null);
10
- const error = ref(null);
12
+ const data = shallowRef(null);
13
+ const error = shallowRef(null);
11
14
  let unsubscribe = null;
12
15
  function start() {
13
16
  stop();
14
17
  error.value = null;
15
- unsubscribe = $graphqlSSE().subscribe(
18
+ unsubscribe = $getGraphQLSSEClient().subscribe(
16
19
  {
17
20
  query: print(subscriptions[operationName]),
18
21
  variables: toValue(variables)
@@ -20,13 +23,13 @@ export function useGraphQLSubscription(operationName, ...args) {
20
23
  {
21
24
  next: (result) => {
22
25
  if (result.errors?.length) {
23
- error.value = wrapError({ errors: result.errors });
26
+ error.value = normalizeGraphQLError({ errors: result.errors });
24
27
  } else if (result.data) {
25
28
  data.value = result.data;
26
29
  }
27
30
  },
28
- error: (e) => {
29
- error.value = wrapError(e);
31
+ error: (err) => {
32
+ error.value = normalizeGraphQLError(err);
30
33
  },
31
34
  complete: () => {
32
35
  unsubscribe = null;
@@ -38,9 +41,7 @@ export function useGraphQLSubscription(operationName, ...args) {
38
41
  unsubscribe?.();
39
42
  unsubscribe = null;
40
43
  }
41
- if (import.meta.client) {
42
- start();
43
- }
44
+ start();
44
45
  onScopeDispose(stop);
45
46
  return { data, error, start, stop };
46
47
  }
@@ -0,0 +1,7 @@
1
+ import type { CacheConfig } from "../../../helpers/cache-config.js";
2
+ export type CacheKeyParts = {
3
+ key: string;
4
+ opPrefix: string;
5
+ };
6
+ export declare function getCacheKeyParts(config: CacheConfig, operationName: string, variables: unknown, scope?: string): CacheKeyParts;
7
+ export declare function getInFlightRequests(): Map<string, Promise<unknown>>;
@@ -0,0 +1,24 @@
1
+ import { hash } from "ohash";
2
+ import { useRequestEvent } from "#imports";
3
+ export function getCacheKeyParts(config, operationName, variables, scope) {
4
+ const parts = [config.keyPrefix, config.cacheVersion];
5
+ if (scope) parts.push(scope);
6
+ parts.push(operationName);
7
+ const opPrefix = parts.join(":") + ":";
8
+ const key = opPrefix + hash(variables || {});
9
+ return { key, opPrefix };
10
+ }
11
+ const clientInFlightRequests = /* @__PURE__ */ new Map();
12
+ export function getInFlightRequests() {
13
+ if (import.meta.server) {
14
+ const event = useRequestEvent();
15
+ if (!event) {
16
+ throw new Error("Undefined event context while accessing in-flight requests map on server");
17
+ }
18
+ if (!event.context._graphqlInFlightRequestsMap) {
19
+ event.context._graphqlInFlightRequestsMap = /* @__PURE__ */ new Map();
20
+ }
21
+ return event.context._graphqlInFlightRequestsMap;
22
+ }
23
+ return clientInFlightRequests;
24
+ }
@@ -0,0 +1,4 @@
1
+ export declare function getPersistedEntry<T>(key: string): Promise<T | undefined>;
2
+ export declare function setPersistedEntry<T>(key: string, value: T, ttl: number): Promise<void>;
3
+ export declare function deletePersistedEntry(key: string): Promise<void>;
4
+ export declare function deletePersistedByPrefix(prefix: string): Promise<void>;
@@ -0,0 +1,70 @@
1
+ import { createStorage } from "unstorage";
2
+ import localStorageDriver from "unstorage/drivers/localstorage";
3
+ function isPersistedStorageAvailable() {
4
+ return import.meta.client && typeof window.localStorage !== "undefined";
5
+ }
6
+ let storage = null;
7
+ function getPersistedStorage() {
8
+ if (!storage && isPersistedStorageAvailable()) {
9
+ try {
10
+ storage = createStorage({
11
+ driver: localStorageDriver({ base: "nuxt-graphql:" })
12
+ });
13
+ } catch {
14
+ storage = null;
15
+ }
16
+ }
17
+ return storage;
18
+ }
19
+ export async function getPersistedEntry(key) {
20
+ const ps = getPersistedStorage();
21
+ if (!ps) {
22
+ return void 0;
23
+ }
24
+ try {
25
+ const payload = await ps.getItem(key);
26
+ if (!payload) {
27
+ return void 0;
28
+ }
29
+ if (payload.expiresAt && payload.expiresAt < Date.now()) {
30
+ await ps.removeItem(key);
31
+ return void 0;
32
+ }
33
+ return payload.value;
34
+ } catch {
35
+ return void 0;
36
+ }
37
+ }
38
+ export async function setPersistedEntry(key, value, ttl) {
39
+ const ps = getPersistedStorage();
40
+ if (!ps) {
41
+ return;
42
+ }
43
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1e3 : null;
44
+ try {
45
+ const payload = { value, expiresAt };
46
+ await ps.setItem(key, payload);
47
+ } catch {
48
+ }
49
+ }
50
+ export async function deletePersistedEntry(key) {
51
+ const ps = getPersistedStorage();
52
+ if (!ps) {
53
+ return;
54
+ }
55
+ try {
56
+ await ps.removeItem(key);
57
+ } catch {
58
+ }
59
+ }
60
+ export async function deletePersistedByPrefix(prefix) {
61
+ const ps = getPersistedStorage();
62
+ if (!ps) {
63
+ return;
64
+ }
65
+ try {
66
+ const keys = await ps.getKeys(prefix);
67
+ await Promise.all(keys.map((key) => ps.removeItem(key)));
68
+ } catch {
69
+ }
70
+ }
@@ -0,0 +1,7 @@
1
+ import { GraphQLClient } from "graphql-request";
2
+ declare const _default: import("#app").Plugin<{
3
+ getGraphQLClient: () => GraphQLClient;
4
+ }> & import("#app").ObjectPlugin<{
5
+ getGraphQLClient: () => GraphQLClient;
6
+ }>;
7
+ export default _default;
@@ -0,0 +1,21 @@
1
+ import { GraphQLClient } from "graphql-request";
2
+ import { defineNuxtPlugin, useRequestEvent, useRequestURL } from "#app";
3
+ import { normalizeGraphQLError } from "../../shared/lib/graphql-error.js";
4
+ import { getClientForwardHeaders } from "../../shared/lib/headers.js";
5
+ export default defineNuxtPlugin((nuxtApp) => {
6
+ const { origin } = useRequestURL();
7
+ const event = useRequestEvent();
8
+ const headers = event ? getClientForwardHeaders(event) : void 0;
9
+ function getGraphQLClient() {
10
+ return new GraphQLClient(`${origin}/api/graphql`, {
11
+ headers,
12
+ responseMiddleware: (response, _request) => {
13
+ if (response instanceof Error) {
14
+ nuxtApp.callHook("graphql:error", normalizeGraphQLError(response));
15
+ return;
16
+ }
17
+ }
18
+ });
19
+ }
20
+ return { provide: { getGraphQLClient } };
21
+ });
@@ -0,0 +1,7 @@
1
+ import { type Client as SSEClient } from "graphql-sse";
2
+ declare const _default: import("#app").Plugin<{
3
+ getGraphQLSSEClient: () => SSEClient;
4
+ }> & import("#app").ObjectPlugin<{
5
+ getGraphQLSSEClient: () => SSEClient;
6
+ }>;
7
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import { defineNuxtPlugin, useRequestURL } from "#app";
2
+ import { createClient } from "graphql-sse";
3
+ export default defineNuxtPlugin((_nuxtApp) => {
4
+ const { origin } = useRequestURL();
5
+ let sseClient;
6
+ function getGraphQLSSEClient() {
7
+ if (import.meta.server) {
8
+ throw new Error("GraphQL SSE client is not available on the server");
9
+ }
10
+ if (sseClient) return sseClient;
11
+ return sseClient = createClient({ url: `${origin}/api/graphql` });
12
+ }
13
+ ;
14
+ return { provide: { getGraphQLSSEClient } };
15
+ });
@@ -0,0 +1,37 @@
1
+ import type { GraphQLClient } from "graphql-request";
2
+ import type { Client as SSEClient } from "graphql-sse";
3
+ import type { GraphQLCacheConfig } from "../lib/graphql-cache";
4
+
5
+ declare global {
6
+ type IsEmptyObject<T> = T extends Record<string, never> ? true : keyof T extends never ? true : false;
7
+ }
8
+
9
+ declare module "h3" {
10
+ interface H3EventContext {
11
+ _graphqlInFlightRequestsMap?: Map<string, Promise<unknown>>;
12
+ }
13
+ }
14
+
15
+ declare module "nuxt/schema" {
16
+ interface PublicRuntimeConfig {
17
+ graphql: {
18
+ cacheConfig: GraphQLCacheConfig;
19
+ };
20
+ }
21
+ }
22
+
23
+ declare module "#app/nuxt" {
24
+ interface NuxtApp {
25
+ $getGraphQLClient: () => GraphQLClient;
26
+ $getGraphQLSSEClient: () => SSEClient;
27
+ }
28
+ }
29
+
30
+ declare module "#app" {
31
+ interface NuxtApp {
32
+ $getGraphQLClient: () => GraphQLClient;
33
+ $getGraphQLSSEClient: () => SSEClient;
34
+ }
35
+ }
36
+
37
+ export { };
@@ -0,0 +1,42 @@
1
+ import { defineEventHandler, toWebRequest, sendWebResponse, createError } from "h3";
2
+ import { createYoga } from "graphql-yoga";
3
+ import { getGraphQLContext } from "#graphql/context";
4
+ import { yogaMiddlewareHandler } from "#graphql/yoga-middleware";
5
+ import { schema } from "#graphql/schema";
6
+ let yoga = null;
7
+ function getYogaInstance() {
8
+ if (!yoga) {
9
+ yoga = createYoga({
10
+ schema,
11
+ graphqlEndpoint: "/api/graphql",
12
+ fetchAPI: globalThis,
13
+ graphiql: process.env.NODE_ENV !== "production",
14
+ // @ts-expect-error subscriptions type available at runtime
15
+ subscriptions: { protocol: "SSE" }
16
+ });
17
+ }
18
+ return yoga;
19
+ }
20
+ export default defineEventHandler(async (event) => {
21
+ try {
22
+ const request = toWebRequest(event);
23
+ const context = await getGraphQLContext(event);
24
+ const { onRequest, onResponse } = yogaMiddlewareHandler ?? {};
25
+ if (onRequest) {
26
+ await onRequest({ event, context, request });
27
+ }
28
+ const response = await getYogaInstance().handleRequest(request, context);
29
+ let finalResponse = response;
30
+ const setResponse = (next) => {
31
+ finalResponse = next;
32
+ };
33
+ if (onResponse) {
34
+ await onResponse({ event, context, request, response, setResponse });
35
+ }
36
+ return sendWebResponse(event, finalResponse);
37
+ } catch (error) {
38
+ const message = error instanceof Error ? error.message : String(error);
39
+ console.error("GraphQL Server Error:", message);
40
+ throw createError({ statusCode: 500, message: "GraphQL server error (see server logs for details)" });
41
+ }
42
+ });
@@ -0,0 +1,5 @@
1
+ import type { H3Event } from "h3";
2
+ export type GetGraphQLContextFn<TContext extends Record<string, unknown>> = (event: H3Event) => Promise<TContext> | TContext;
3
+ export declare function defineGraphQLContext<TContext extends Record<string, unknown>>(getGraphQLContext: GetGraphQLContextFn<TContext>): {
4
+ getGraphQLContext: GetGraphQLContextFn<TContext>;
5
+ };
@@ -0,0 +1,4 @@
1
+ export function defineGraphQLContext(getGraphQLContext) {
2
+ return { getGraphQLContext };
3
+ }
4
+ ;
@@ -0,0 +1,30 @@
1
+ import type { GraphQLContext } from "#graphql/context";
2
+ export type RemoteExecMiddlewareOnRequestArgs = {
3
+ remoteName: string;
4
+ operationName: string;
5
+ context: GraphQLContext;
6
+ fetchOptions: {
7
+ headers: Headers;
8
+ };
9
+ };
10
+ export type RemoteExecMiddlewareOnResponseArgs = {
11
+ remoteName: string;
12
+ operationName: string;
13
+ context: GraphQLContext;
14
+ response: Response;
15
+ };
16
+ export type RemoteExecMiddlewareOnErrorArgs = {
17
+ remoteName: string;
18
+ operationName: string;
19
+ context: GraphQLContext;
20
+ error: unknown;
21
+ response?: Response;
22
+ };
23
+ export type RemoteExecMiddlewareHandler = {
24
+ onRequest?: (args: RemoteExecMiddlewareOnRequestArgs) => Promise<void> | void;
25
+ onResponse?: (args: RemoteExecMiddlewareOnResponseArgs) => Promise<void> | void;
26
+ onError?: (args: RemoteExecMiddlewareOnErrorArgs) => Promise<void> | void;
27
+ };
28
+ export declare function defineRemoteExecMiddleware(remoteExecMiddlewareHandler: RemoteExecMiddlewareHandler): {
29
+ remoteExecMiddlewareHandler: RemoteExecMiddlewareHandler;
30
+ };
@@ -0,0 +1,3 @@
1
+ export function defineRemoteExecMiddleware(remoteExecMiddlewareHandler) {
2
+ return { remoteExecMiddlewareHandler };
3
+ }
@@ -0,0 +1,21 @@
1
+ import type { H3Event } from "h3";
2
+ import type { GraphQLContext } from "#graphql/context";
3
+ export type YogaMiddlewareRequestArgs = {
4
+ event: H3Event;
5
+ context: GraphQLContext;
6
+ request: Request;
7
+ };
8
+ export type YogaMiddlewareResponseArgs = {
9
+ event: H3Event;
10
+ context: GraphQLContext;
11
+ request: Request;
12
+ response: Response;
13
+ setResponse: (next: Response) => void;
14
+ };
15
+ export type YogaMiddlewareHandler = {
16
+ onRequest?: (args: YogaMiddlewareRequestArgs) => Promise<void> | void;
17
+ onResponse?: (args: YogaMiddlewareResponseArgs) => Promise<void> | void;
18
+ };
19
+ export declare function defineYogaMiddleware(yogaMiddlewareHandler: YogaMiddlewareHandler): {
20
+ yogaMiddlewareHandler: YogaMiddlewareHandler;
21
+ };