@lewebsimple/nuxt-graphql 0.1.10 → 0.1.11
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/dist/module.d.mts +7 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +80 -28
- package/dist/runtime/composables/useGraphQLCache.d.ts +5 -0
- package/dist/runtime/composables/useGraphQLCache.js +15 -0
- package/dist/runtime/composables/useGraphQLMutation.js +3 -3
- package/dist/runtime/composables/useGraphQLQuery.d.ts +6 -1
- package/dist/runtime/composables/useGraphQLQuery.js +32 -5
- package/dist/runtime/composables/useGraphQLSubscription.d.ts +2 -1
- package/dist/runtime/composables/useGraphQLSubscription.js +3 -2
- package/dist/runtime/plugins/graphql.js +15 -9
- package/dist/runtime/types/graphql-client.d.ts +7 -0
- package/dist/runtime/utils/graphql-cache.d.ts +26 -0
- package/dist/runtime/utils/graphql-cache.js +65 -0
- package/dist/runtime/utils/graphql-error.d.ts +13 -0
- package/dist/runtime/utils/graphql-error.js +24 -0
- package/package.json +4 -2
package/dist/module.d.mts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { GraphQLCacheConfig } from '../dist/runtime/utils/graphql-cache.js';
|
|
2
3
|
|
|
3
4
|
interface ModuleOptions {
|
|
4
5
|
endpoint?: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
cache?: Partial<GraphQLCacheConfig>;
|
|
5
8
|
codegen?: {
|
|
6
9
|
pattern?: string;
|
|
7
10
|
schemaOutput?: string;
|
|
11
|
+
scalars?: Record<string, string | {
|
|
12
|
+
input: string;
|
|
13
|
+
output: string;
|
|
14
|
+
}>;
|
|
8
15
|
};
|
|
9
16
|
}
|
|
10
17
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -175,36 +175,65 @@ function formatDefinitions(defs) {
|
|
|
175
175
|
return defs.map((def) => `${colorOf(def)}${def.name}${reset}`).join(`${dim} / ${reset}`);
|
|
176
176
|
}
|
|
177
177
|
async function runCodegen(options) {
|
|
178
|
-
const { sdl, documents, operationsFile } = options;
|
|
178
|
+
const { sdl, documents, operationsFile, schemasFile, scalars } = options;
|
|
179
179
|
if (documents.length === 0) {
|
|
180
180
|
logger.warn("No GraphQL documents found");
|
|
181
181
|
return;
|
|
182
182
|
}
|
|
183
|
+
const zodScalars = {};
|
|
184
|
+
if (scalars) {
|
|
185
|
+
for (const [name, config] of Object.entries(scalars)) {
|
|
186
|
+
const inputType = typeof config === "string" ? config : config.input;
|
|
187
|
+
switch (inputType) {
|
|
188
|
+
case "Date":
|
|
189
|
+
zodScalars[name] = "z.coerce.date()";
|
|
190
|
+
break;
|
|
191
|
+
case "number":
|
|
192
|
+
zodScalars[name] = "z.coerce.number()";
|
|
193
|
+
break;
|
|
194
|
+
case "boolean":
|
|
195
|
+
zodScalars[name] = "z.coerce.boolean()";
|
|
196
|
+
break;
|
|
197
|
+
default:
|
|
198
|
+
zodScalars[name] = "z.string()";
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
183
202
|
try {
|
|
184
|
-
|
|
185
|
-
{
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
const generates = {
|
|
204
|
+
[operationsFile]: {
|
|
205
|
+
plugins: ["typescript", "typescript-operations", "typed-document-node"],
|
|
206
|
+
config: {
|
|
207
|
+
useTypeImports: true,
|
|
208
|
+
enumsAsTypes: true,
|
|
209
|
+
skipTypename: true,
|
|
210
|
+
documentVariableSuffix: "Document",
|
|
211
|
+
documentMode: "documentNode",
|
|
212
|
+
strictScalars: true,
|
|
213
|
+
defaultScalarType: "never",
|
|
214
|
+
scalars
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
if (schemasFile) {
|
|
219
|
+
generates[schemasFile] = {
|
|
220
|
+
plugins: ["typescript-validation-schema"],
|
|
221
|
+
config: {
|
|
222
|
+
schema: "zodv4",
|
|
223
|
+
importFrom: "#graphql/operations",
|
|
224
|
+
useTypeImports: true,
|
|
225
|
+
directives: {
|
|
226
|
+
constraint: {
|
|
227
|
+
minLength: "min",
|
|
228
|
+
maxLength: "max",
|
|
229
|
+
pattern: "regex"
|
|
200
230
|
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
);
|
|
231
|
+
},
|
|
232
|
+
scalarSchemas: zodScalars
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
await generate({ schema: sdl, documents, generates, silent: true, errorsOnly: true }, true);
|
|
208
237
|
logger.success(`Generated types for ${documents.length} document(s)`);
|
|
209
238
|
} catch (error) {
|
|
210
239
|
logger.error("GraphQL codegen failed:", error instanceof Error ? error.message : error);
|
|
@@ -253,13 +282,23 @@ const module$1 = defineNuxtModule({
|
|
|
253
282
|
nuxt.hook("listen", (_, { url }) => {
|
|
254
283
|
logger.success(`GraphQL Yoga ready at ${cyan}${url.replace(/\/$/, "")}${endpoint}${reset}`);
|
|
255
284
|
});
|
|
256
|
-
nuxt.options.runtimeConfig.public.graphql = {
|
|
285
|
+
nuxt.options.runtimeConfig.public.graphql = {
|
|
286
|
+
endpoint,
|
|
287
|
+
headers: options.headers || {},
|
|
288
|
+
cache: {
|
|
289
|
+
enabled: options.cache?.enabled ?? false,
|
|
290
|
+
ttl: options.cache?.ttl ?? 6e4,
|
|
291
|
+
storage: options.cache?.storage ?? "memory"
|
|
292
|
+
}
|
|
293
|
+
};
|
|
257
294
|
const codegenPattern = options.codegen?.pattern ?? "**/*.gql";
|
|
258
295
|
const graphqlrcFile = join(rootDir, ".graphqlrc");
|
|
259
296
|
const operationsFile = join(nuxt.options.buildDir, "graphql/operations.ts");
|
|
260
297
|
const registryFile = join(nuxt.options.buildDir, "graphql/registry.ts");
|
|
298
|
+
const schemasFile = join(nuxt.options.buildDir, "graphql/schemas.ts");
|
|
261
299
|
nuxt.options.alias["#graphql/operations"] = operationsFile;
|
|
262
300
|
nuxt.options.alias["#graphql/registry"] = registryFile;
|
|
301
|
+
nuxt.options.alias["#graphql/schemas"] = schemasFile;
|
|
263
302
|
const schemaOutput = options.codegen?.schemaOutput ?? "server/graphql/schema.graphql";
|
|
264
303
|
if (schemaOutput) {
|
|
265
304
|
if (!schemaOutput.endsWith(".graphql")) {
|
|
@@ -279,12 +318,24 @@ const module$1 = defineNuxtModule({
|
|
|
279
318
|
const defs = analysis.byFile.get(doc.path) ?? [];
|
|
280
319
|
logger.info(`${cyan}${relativePath}${reset} [${formatDefinitions(defs)}]`);
|
|
281
320
|
}
|
|
282
|
-
await runCodegen({
|
|
321
|
+
await runCodegen({
|
|
322
|
+
sdl,
|
|
323
|
+
documents,
|
|
324
|
+
operationsFile,
|
|
325
|
+
schemasFile,
|
|
326
|
+
scalars: options.codegen?.scalars
|
|
327
|
+
});
|
|
283
328
|
if (writeFileIfChanged(schemaFile, sdl)) {
|
|
284
329
|
logger.info(`GraphQL schema saved to ${cyan}${schemaOutput}${reset}`);
|
|
285
330
|
}
|
|
286
|
-
const
|
|
287
|
-
|
|
331
|
+
const graphqlrc = {
|
|
332
|
+
schema: relative(rootDir, schemaFile),
|
|
333
|
+
documents: codegenPattern
|
|
334
|
+
};
|
|
335
|
+
if (options.codegen?.scalars) {
|
|
336
|
+
graphqlrc.scalars = options.codegen.scalars;
|
|
337
|
+
}
|
|
338
|
+
if (writeFileIfChanged(graphqlrcFile, JSON.stringify(graphqlrc, null, 2))) {
|
|
288
339
|
logger.info(`GraphQL config saved to ${cyan}.graphqlrc${reset}`);
|
|
289
340
|
}
|
|
290
341
|
if (writeFileIfChanged(registryFile, generateRegistryByTypeSource(analysis.operationsByType))) {
|
|
@@ -295,6 +346,7 @@ const module$1 = defineNuxtModule({
|
|
|
295
346
|
await generate();
|
|
296
347
|
if (existsSync(operationsFile)) references.push({ path: operationsFile });
|
|
297
348
|
if (existsSync(registryFile)) references.push({ path: registryFile });
|
|
349
|
+
if (existsSync(schemasFile)) references.push({ path: schemasFile });
|
|
298
350
|
});
|
|
299
351
|
if (nuxt.options.dev) {
|
|
300
352
|
nuxt.hook("builder:watch", async (event, path) => {
|
|
@@ -0,0 +1,15 @@
|
|
|
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,6 +1,7 @@
|
|
|
1
1
|
import { ref } from "vue";
|
|
2
2
|
import { useNuxtApp } from "#imports";
|
|
3
3
|
import { mutations } from "#graphql/registry";
|
|
4
|
+
import { wrapError } from "../utils/graphql-error.js";
|
|
4
5
|
export function useGraphQLMutation(operationName) {
|
|
5
6
|
const document = mutations[operationName];
|
|
6
7
|
const { $graphql } = useNuxtApp();
|
|
@@ -11,9 +12,8 @@ export function useGraphQLMutation(operationName) {
|
|
|
11
12
|
const [variables, headers] = args;
|
|
12
13
|
const result = await $graphql().request(document, variables, headers);
|
|
13
14
|
return { data: result, error: null };
|
|
14
|
-
} catch (
|
|
15
|
-
|
|
16
|
-
return { data: null, error };
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return { data: null, error: wrapError(error) };
|
|
17
17
|
} finally {
|
|
18
18
|
pending.value = false;
|
|
19
19
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { AsyncData, AsyncDataOptions } from "#app";
|
|
2
2
|
import { type QueryName, type QueryResult, type QueryVariables } from "#graphql/registry";
|
|
3
|
+
import { type CacheOptions } from "../utils/graphql-cache.js";
|
|
3
4
|
import type { IsEmptyObject } from "../utils/helpers.js";
|
|
4
|
-
export
|
|
5
|
+
export interface UseGraphQLQueryOptions<T> extends AsyncDataOptions<T> {
|
|
6
|
+
cache?: CacheOptions | false;
|
|
7
|
+
headers?: HeadersInit;
|
|
8
|
+
}
|
|
9
|
+
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>;
|
|
@@ -1,10 +1,37 @@
|
|
|
1
|
-
import { useAsyncData, useNuxtApp } from "#imports";
|
|
2
|
-
import { hash } from "ohash";
|
|
1
|
+
import { useAsyncData, useNuxtApp, useRuntimeConfig, onScopeDispose } from "#imports";
|
|
3
2
|
import { queries } from "#graphql/registry";
|
|
3
|
+
import { cacheGet, cacheSet, dedupeGet, dedupeSet, registerRefresh, initCache, getCacheKey } from "../utils/graphql-cache.js";
|
|
4
4
|
export function useGraphQLQuery(operationName, ...args) {
|
|
5
5
|
const { $graphql } = useNuxtApp();
|
|
6
|
+
const { public: { graphql: { cache: globalCache } } } = useRuntimeConfig();
|
|
6
7
|
const document = queries[operationName];
|
|
7
|
-
const [variables, options
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
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
|
+
}
|
|
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;
|
|
19
|
+
}
|
|
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);
|
|
27
|
+
}
|
|
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
|
+
}
|
|
36
|
+
return asyncData;
|
|
10
37
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
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";
|
|
3
4
|
import type { IsEmptyObject } from "../utils/helpers.js";
|
|
4
5
|
export type UseGraphQLSubscriptionReturn<N extends SubscriptionName> = {
|
|
5
6
|
data: Ref<SubscriptionResult<N> | null>;
|
|
6
|
-
error: Ref<
|
|
7
|
+
error: Ref<GraphQLClientError | null>;
|
|
7
8
|
start: () => void;
|
|
8
9
|
stop: () => void;
|
|
9
10
|
};
|
|
@@ -2,6 +2,7 @@ import { ref, 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
6
|
export function useGraphQLSubscription(operationName, ...args) {
|
|
6
7
|
const { $graphqlSSE } = useNuxtApp();
|
|
7
8
|
const [variables] = args;
|
|
@@ -19,13 +20,13 @@ export function useGraphQLSubscription(operationName, ...args) {
|
|
|
19
20
|
{
|
|
20
21
|
next: (result) => {
|
|
21
22
|
if (result.errors?.length) {
|
|
22
|
-
error.value =
|
|
23
|
+
error.value = wrapError({ errors: result.errors });
|
|
23
24
|
} else if (result.data) {
|
|
24
25
|
data.value = result.data;
|
|
25
26
|
}
|
|
26
27
|
},
|
|
27
28
|
error: (e) => {
|
|
28
|
-
error.value =
|
|
29
|
+
error.value = wrapError(e);
|
|
29
30
|
},
|
|
30
31
|
complete: () => {
|
|
31
32
|
unsubscribe = null;
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { GraphQLClient } from "graphql-request";
|
|
2
2
|
import { createClient } from "graphql-sse";
|
|
3
3
|
import { defineNuxtPlugin, useRequestHeaders, useRequestURL, useRuntimeConfig } from "#imports";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { wrapError } from "../utils/graphql-error.js";
|
|
5
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
6
|
+
const { public: { graphql: { endpoint, headers: staticHeaders } } } = useRuntimeConfig();
|
|
6
7
|
const { origin } = useRequestURL();
|
|
7
8
|
const url = `${origin}${endpoint}`;
|
|
8
|
-
let client = null;
|
|
9
9
|
const getClient = () => {
|
|
10
|
-
|
|
11
|
-
client = new GraphQLClient(url);
|
|
12
|
-
}
|
|
10
|
+
const headers = { ...staticHeaders };
|
|
13
11
|
if (import.meta.server) {
|
|
14
|
-
const
|
|
15
|
-
|
|
12
|
+
const ssrHeaders = useRequestHeaders(["cookie", "authorization"]);
|
|
13
|
+
Object.assign(headers, ssrHeaders);
|
|
16
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
|
+
});
|
|
17
23
|
return client;
|
|
18
24
|
};
|
|
19
25
|
let sseClient = null;
|
|
@@ -22,7 +28,7 @@ export default defineNuxtPlugin(() => {
|
|
|
22
28
|
throw new Error("SSE subscriptions are not available on the server");
|
|
23
29
|
}
|
|
24
30
|
if (!sseClient) {
|
|
25
|
-
sseClient = createClient({ url });
|
|
31
|
+
sseClient = createClient({ url, headers: staticHeaders });
|
|
26
32
|
}
|
|
27
33
|
return sseClient;
|
|
28
34
|
};
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import type { GraphQLClient } from "graphql-request";
|
|
2
2
|
import type { Client as SSEClient } from "graphql-sse";
|
|
3
|
+
import type { GraphQLClientError } from "../utils/graphql-error";
|
|
3
4
|
|
|
4
5
|
declare module "#app" {
|
|
5
6
|
interface NuxtApp {
|
|
6
7
|
$graphql: () => GraphQLClient;
|
|
7
8
|
$graphqlSSE: () => SSEClient;
|
|
8
9
|
}
|
|
10
|
+
interface RuntimeNuxtHooks {
|
|
11
|
+
"graphql:headers": (headers: Record<string, string>) => void | Promise<void>;
|
|
12
|
+
"graphql:error": (error: GraphQLClientError) => void;
|
|
13
|
+
}
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
declare module "nuxt/schema" {
|
|
12
17
|
interface PublicRuntimeConfig {
|
|
13
18
|
graphql: {
|
|
14
19
|
endpoint: string;
|
|
20
|
+
headers: Record<string, string>;
|
|
21
|
+
cache: GraphQLCacheConfig;
|
|
15
22
|
};
|
|
16
23
|
}
|
|
17
24
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
export declare function registerRefresh(operationName: string, variables: unknown, refresh: () => void): () => void;
|
|
20
|
+
/**
|
|
21
|
+
* Invalidate cached queries and trigger refreshes
|
|
22
|
+
* - No args: invalidate all
|
|
23
|
+
* - operationName only: invalidate all queries with that name
|
|
24
|
+
* - operationName + variables: invalidate specific query
|
|
25
|
+
*/
|
|
26
|
+
export declare function cacheInvalidate(operationName?: string, variables?: unknown): Promise<void>;
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
|
10
|
+
*
|
|
11
|
+
* @returns Wrapped GraphQLClientError
|
|
12
|
+
*/
|
|
13
|
+
export declare function wrapError(error: unknown): GraphQLClientError;
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewebsimple/nuxt-graphql",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Opinionated Nuxt module for using GraphQL",
|
|
5
5
|
"repository": "lewebsimple/nuxt-graphql",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,12 +41,14 @@
|
|
|
41
41
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
42
42
|
"@nuxt/kit": "^4.2.2",
|
|
43
43
|
"graphql": "^16.12.0",
|
|
44
|
+
"graphql-codegen-typescript-validation-schema": "^0.18.1",
|
|
44
45
|
"graphql-request": "^7.4.0",
|
|
45
46
|
"graphql-sse": "^2.6.0",
|
|
46
47
|
"graphql-yoga": "^5.18.0",
|
|
47
48
|
"jiti": "^2.6.1",
|
|
48
49
|
"ohash": "^2.0.11",
|
|
49
|
-
"tinyglobby": "^0.2.15"
|
|
50
|
+
"tinyglobby": "^0.2.15",
|
|
51
|
+
"unstorage": "^1.17.3"
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
54
|
"@nuxt/devtools": "^3.1.1",
|