@lewebsimple/nuxt-graphql 0.5.4 → 0.5.5

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 CHANGED
@@ -1,6 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
  import { HeadersInput } from '../dist/runtime/shared/lib/headers.js';
3
- import { CacheConfig } from '../dist/runtime/shared/lib/cache-config.js';
4
3
 
5
4
  interface LocalSchemaDef {
6
5
  type: "local";
@@ -39,7 +38,7 @@ interface NuxtGraphQLModuleOptions {
39
38
  /**
40
39
  * Global cache configuration for queries.
41
40
  */
42
- cache?: Partial<CacheConfig>;
41
+ cache?: Partial<GraphQLCacheConfig>;
43
42
  /**
44
43
  * GraphQL documents glob pattern.
45
44
  * Default: "**\/*.gql"
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-graphql",
3
3
  "configKey": "graphql",
4
- "version": "0.5.4",
4
+ "version": "0.5.5",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  import { mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, relative, resolve } from 'node:path';
3
- import { defineNuxtModule, useLogger, createResolver, addTemplate, addServerTemplate, addTypeTemplate, addServerHandler, addPlugin, addImportsDir, addServerImportsDir } from '@nuxt/kit';
4
3
  import { defu } from 'defu';
5
4
  import { hash } from 'ohash';
5
+ import { stitchSchemas } from '@graphql-tools/stitch';
6
+ import { defineNuxtModule, useLogger, createResolver, addTemplate, addServerTemplate, addTypeTemplate, addServerHandler, addPlugin, addImportsDir, addServerImportsDir } from '@nuxt/kit';
6
7
  import { loadDocuments as loadDocuments$1 } from '@graphql-tools/load';
7
8
  import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
8
9
  import { Kind, getIntrospectionQuery, buildClientSchema, GraphQLSchema } from 'graphql';
@@ -11,29 +12,33 @@ import * as typescriptPlugin from '@graphql-codegen/typescript';
11
12
  import * as typescriptOperationsPlugin from '@graphql-codegen/typescript-operations';
12
13
  import * as typedDocumentNodePlugin from '@graphql-codegen/typed-document-node';
13
14
  import { mergeHeaders } from '../dist/runtime/shared/lib/headers.js';
14
- import { stitchSchemas } from '@graphql-tools/stitch';
15
- import { resolveCacheConfig } from '../dist/runtime/shared/lib/cache-config.js';
15
+ import { resolveCacheConfig } from '../dist/runtime/shared/lib/cache.js';
16
16
 
17
17
  const cyan = "\x1B[36m";
18
18
  const reset = "\x1B[0m";
19
19
 
20
20
  function renderContextTemplate({ contextModules }) {
21
- const imports = contextModules.map((module, index) => `import context${index} from ${JSON.stringify(module)};`);
22
- const types = ["{}", ...contextModules.map((_, index) => `Awaited<ReturnType<typeof context${index}>>`)];
23
- return [
24
- `import type { H3Event } from "h3";`,
25
- ...imports,
26
- "",
27
- `export type GraphQLContext = ${types.join(" & ")};`,
28
- "",
29
- "export async function createContext(event: H3Event): Promise<GraphQLContext> {",
30
- " const parts = await Promise.all([",
31
- " () => ({}),",
32
- ...contextModules.map((_, index) => ` context${index}(event),`),
33
- " ]);",
34
- " return Object.assign({}, ...parts) as GraphQLContext;",
35
- "}"
36
- ].join("\n");
21
+ const contextImports = contextModules.map((contextModule, index) => `import { createContext as createContext${index} } from '${contextModule}';`);
22
+ const contexts = contextModules.map((_, index) => `createContext${index}(event)`);
23
+ return `
24
+ ${contextImports.join("\n")}
25
+
26
+ export async function createContext(event) {
27
+ const parts = await Promise.all([${contexts.join(", ")}]);
28
+ return Object.assign({}, ...parts);
29
+ }`.trim();
30
+ }
31
+ function renderContextTypesTemplate({ contextModules }) {
32
+ const contextImports = contextModules.map((module, index) => `import createContext${index} from ${JSON.stringify(module)};`);
33
+ const contextTypes = ["{}", ...contextModules.map((_, index) => `Awaited<ReturnType<typeof createContext${index}>>`)];
34
+ return `
35
+ import type { H3Event } from "h3";
36
+ ${contextImports.join("\n")}
37
+
38
+ declare module "#graphql/context" {
39
+ export type GraphQLContext = ${contextTypes.join(" & ")};
40
+ export function createContext(event: H3Event): Promise<GraphQLContext>;
41
+ }`.trim();
37
42
  }
38
43
 
39
44
  async function loadDocuments(documents) {
@@ -53,14 +58,10 @@ async function loadDocuments(documents) {
53
58
 
54
59
  async function renderFragmentsTemplate({ documents }) {
55
60
  const fragments = collectFragments(documents);
56
- if (fragments.length === 0) {
57
- return `export { }`;
58
- }
59
- return [
60
- `export type {`,
61
- ...fragments.map((name) => ` ${name}Fragment,`),
62
- `} from "./operations";`
63
- ].join("\n");
61
+ return fragments.length === 0 ? "export { }" : `
62
+ export type {
63
+ ${fragments.map((name) => ` ${name}Fragment,`).join("\n")}
64
+ } from "./operations";`.trim();
64
65
  }
65
66
  function collectFragments(documents) {
66
67
  const fragments = /* @__PURE__ */ new Set();
@@ -81,7 +82,7 @@ function collectFragments(documents) {
81
82
  }
82
83
 
83
84
  async function renderOperationsTemplate({ schema, documents }) {
84
- const output = await codegen({
85
+ return await codegen({
85
86
  filename: "operations.ts",
86
87
  // @graphql-codegen/core codegen supports GraphQLSchema at runtime, but types expect DocumentNode
87
88
  schema,
@@ -133,51 +134,44 @@ async function renderOperationsTemplate({ schema, documents }) {
133
134
  },
134
135
  config: {}
135
136
  });
136
- return output;
137
137
  }
138
138
 
139
139
  async function renderRegistryTemplate({ documents }) {
140
140
  const operations = collectOperations(documents);
141
- return [
142
- `import type { DocumentNode } from "graphql";`,
143
- `import {`,
144
- ...operations.map(
145
- ({ name }) => ` ${name}Document, type ${name}QueryVariables, type ${name}QueryResult,`
146
- ),
147
- `} from "./operations";`,
148
- ``,
149
- // Operation entry
150
- `export interface OperationEntry<TVariables, TResult, TKind extends "query" | "mutation" | "subscription"> {`,
151
- ` kind: TKind;`,
152
- ` variables: TVariables;`,
153
- ` result: TResult;`,
154
- ` document: DocumentNode;`,
155
- `}`,
156
- ``,
157
- // Operation registry type
158
- `export type OperationRegistry = {`,
159
- ...operations.map(
160
- ({ name, kind }) => ` ${name}: OperationEntry<${name}QueryVariables, ${name}QueryResult, "${kind}">;`
161
- ),
162
- `};`,
163
- ``,
164
- // Operation name types
165
- `export type QueryName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "query" ? K : never }[keyof OperationRegistry];`,
166
- `export type MutationName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "mutation" ? K : never }[keyof OperationRegistry];`,
167
- `export type SubscriptionName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "subscription" ? K : never }[keyof OperationRegistry];`,
168
- ``,
169
- // Projection helpers (variables / result)
170
- `export type VariablesOf<TName extends keyof OperationRegistry> = OperationRegistry[TName]["variables"];`,
171
- `export type ResultOf<TName extends keyof OperationRegistry> = OperationRegistry[TName]["result"];`,
172
- ``,
173
- // Runtime registry (document + kind only)
174
- `export const registry: { [K in keyof OperationRegistry]: { kind: OperationRegistry[K]["kind"]; document: DocumentNode; }; } = {`,
175
- ...operations.map(
176
- ({ name, kind }) => ` ${name}: { kind: "${kind}", document: ${name}Document },`
177
- ),
178
- `};`,
179
- ``
180
- ].join("\n");
141
+ return `
142
+ import type { DocumentNode } from "graphql";
143
+ import {
144
+ ${operations.map(({ name }) => ` ${name}Document, type ${name}QueryVariables, type ${name}QueryResult,`).join("\n")}
145
+ } from "./operations";
146
+
147
+ // Operation entry
148
+ export interface OperationEntry<TVariables, TResult, TKind extends "query" | "mutation" | "subscription"> {
149
+ kind: TKind;
150
+ variables: TVariables;
151
+ result: TResult;
152
+ document: DocumentNode;
153
+ }
154
+
155
+ // Operation registry type
156
+ export type OperationRegistry = {
157
+ ${operations.map(({ name, kind }) => ` ${name}: OperationEntry<${name}QueryVariables, ${name}QueryResult, "${kind}">;`).join("\n")}
158
+ };
159
+
160
+ // Operation name types
161
+ export type OperationName = keyof OperationRegistry;
162
+ export type QueryName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "query" ? K : never }[keyof OperationRegistry];
163
+ export type MutationName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "mutation" ? K : never }[keyof OperationRegistry];
164
+ export type SubscriptionName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "subscription" ? K : never }[keyof OperationRegistry];
165
+
166
+ // Projection helpers (variables / result)
167
+ export type VariablesOf<TName extends keyof OperationRegistry> = OperationRegistry[TName]["variables"];
168
+ export type ResultOf<TName extends keyof OperationRegistry> = OperationRegistry[TName]["result"];
169
+
170
+
171
+ // Runtime registry (document + kind only)
172
+ export const registry: { [K in keyof OperationRegistry]: { kind: OperationRegistry[K]["kind"]; document: DocumentNode; }; } = {
173
+ ${operations.map(({ name, kind }) => ` ${name}: { kind: "${kind}", document: ${name}Document}, `).join("\n")}
174
+ };`.trim();
181
175
  }
182
176
  function collectOperations(documents) {
183
177
  const operations = /* @__PURE__ */ new Map();
@@ -202,41 +196,61 @@ function collectOperations(documents) {
202
196
  return [...operations.values()];
203
197
  }
204
198
 
205
- function renderLocalSchemaTemplate({ path }) {
206
- return `export { schema } from ${JSON.stringify(path)};`;
199
+ function renderLocalSchemaTemplate({ schemaModule }) {
200
+ return `export { schema } from ${JSON.stringify(schemaModule)};`;
207
201
  }
208
- async function loadLocalSchema({ path }) {
202
+ async function renderRemoteSchemaTemplate({ remoteExecutorModule, hooksModules, schemaDef, schemaLoader }) {
203
+ const importHooks = hooksModules.map((hookPath, index) => `import hooks${index} from ${JSON.stringify(hookPath)};`);
204
+ const hooks = hooksModules.map((_, index) => `hooks${index}`);
205
+ const { url, headers } = schemaDef;
206
+ const schema = await schemaLoader();
207
+ const sdl = await printSchemaSDL(schema);
208
+ return `
209
+ import { buildSchema } from "graphql";
210
+ import { createRemoteExecutor } from ${JSON.stringify(remoteExecutorModule)};
211
+ ${importHooks.join("\n")}
212
+
213
+ const sdl = /* GraphQL */ \`${sdl.replace(/`/g, "\\`")}\`;
214
+
215
+ export const schema = {
216
+ schema: buildSchema(sdl),
217
+ executor: createRemoteExecutor({
218
+ url: ${JSON.stringify(url)},
219
+ headers: ${JSON.stringify(headers || {})},
220
+ hooks: [${hooks.join(", ")}]
221
+ }),
222
+ };
223
+ `.trim();
224
+ }
225
+ function renderSchemaTemplate({ schemaNames }) {
226
+ const importSchemas = schemaNames.map((name) => `import { schema as ${name}Schema } from ${JSON.stringify(`./schemas/${name}`)};`);
227
+ const schemas = schemaNames.map((name) => `${name}Schema`);
228
+ return `
229
+ import { stitchSchemas } from "@graphql-tools/stitch";
230
+ ${importSchemas.join("\n")}
231
+
232
+ export const schema = stitchSchemas({
233
+ subschemas: [${schemas.join(", ")}],
234
+ });`.trim();
235
+ }
236
+ function renderSchemaTypesTemplate() {
237
+ return `
238
+ import type { GraphQLSchema } from "graphql";
239
+
240
+ declare module "#graphql/schema" {
241
+ export const schema: GraphQLSchema;
242
+ }
243
+ `.trim();
244
+ }
245
+ async function loadLocalSchema({ schemaModule }) {
209
246
  const { createJiti } = await import('jiti');
210
247
  const jiti = createJiti(import.meta.url, { interopDefault: true });
211
- const module = await jiti.import(path);
248
+ const module = await jiti.import(schemaModule);
212
249
  if (!module.schema || !(module.schema instanceof Object) || typeof module.schema.getQueryType !== "function") {
213
- throw new Error(`${path} must export a valid 'schema' of type GraphQLSchema.`);
250
+ throw new Error(`${schemaModule} must export a valid 'schema' of type GraphQLSchema.`);
214
251
  }
215
252
  return module.schema;
216
253
  }
217
- async function renderRemoteSchemaTemplate({ remoteExecutorModule, type, hooks, ...schemaDef }) {
218
- const schema = await introspectRemoteSchema(schemaDef);
219
- const sdl = await printSchemaSDL(schema);
220
- const imports = (hooks || []).map((hookPath, index) => `import hooks${index} from ${JSON.stringify(hookPath)};`);
221
- return [
222
- `import { buildSchema } from "graphql";`,
223
- `import type { SubschemaConfig } from "@graphql-tools/delegate";`,
224
- `import { createRemoteExecutor } from ${JSON.stringify(remoteExecutorModule)};`,
225
- ...imports,
226
- ``,
227
- `const sdl = /* GraphQL */ \`${sdl.replace(/`/g, "\\`")}\`;`,
228
- ``,
229
- `export const schema: SubschemaConfig = {`,
230
- ` schema: buildSchema(sdl),`,
231
- ` executor: createRemoteExecutor({`,
232
- ` ...${JSON.stringify(schemaDef)},`,
233
- ` hooks: [`,
234
- ...(hooks || []).map((_, index) => ` hooks${index},`),
235
- ` ]`,
236
- ` }),`,
237
- `};`
238
- ].join("\n");
239
- }
240
254
  async function introspectRemoteSchema({ url, headers }) {
241
255
  const response = await fetch(url, {
242
256
  method: "POST",
@@ -266,34 +280,9 @@ async function printSchemaSDL(schema) {
266
280
  const { printSchema, lexicographicSortSchema } = await import('graphql');
267
281
  return printSchema(lexicographicSortSchema(schema));
268
282
  }
269
- function renderStitchedSchemaTemplate({ schemaNames }) {
270
- return [
271
- `import type { GraphQLSchema } from "graphql"; `,
272
- `import type { SubschemaConfig } from "@graphql-tools/delegate"; `,
273
- `import { stitchSchemas } from "@graphql-tools/stitch"; `,
274
- ...schemaNames.map((name) => `import { schema as ${name}Schema } from ${JSON.stringify(`./schemas/${name}`)}; `),
275
- ``,
276
- `const subschemas: Array<GraphQLSchema | SubschemaConfig> = [`,
277
- ...schemaNames.map((name) => ` ${name}Schema, `),
278
- `]; `,
279
- ``,
280
- `export const schema = stitchSchemas({ subschemas }); `
281
- ].join("\n");
282
- }
283
- async function loadStitchedSchema(schemaDefs) {
284
- const subschemas = [];
285
- for (const schemaDef of Object.values(schemaDefs)) {
286
- if (schemaDef.type === "local") {
287
- subschemas.push(await loadLocalSchema(schemaDef));
288
- } else if (schemaDef.type === "remote") {
289
- subschemas.push(await introspectRemoteSchema(schemaDef));
290
- }
291
- }
292
- return stitchSchemas({ subschemas });
293
- }
294
283
 
295
284
  function renderAppTypesTemplate() {
296
- return `// Nuxt GraphQL types (app)
285
+ return `
297
286
  import type { GraphQLClient } from "graphql-request";
298
287
  import type { Client as SSEClient } from "graphql-sse";
299
288
 
@@ -312,20 +301,10 @@ declare module "#app" {
312
301
  }
313
302
 
314
303
  export {};
315
- `;
304
+ `.trim();
316
305
  }
317
306
  function renderServerTypesTemplate() {
318
- return `// Nuxt GraphQL types (server)
319
- import type { GraphQLSchema } from "graphql";
320
-
321
- declare module "#graphql/context" {
322
- export type { GraphQLContext };
323
- }
324
-
325
- declare module "#graphql/schema" {
326
- export const schema: GraphQLSchema;
327
- }
328
-
307
+ return `
329
308
  declare module "h3" {
330
309
  interface H3EventContext {
331
310
  _graphqlInFlightRequestsMap?: Map<string, Promise<unknown>>;
@@ -333,24 +312,34 @@ declare module "h3" {
333
312
  }
334
313
 
335
314
  export {};
336
- `;
315
+ `.trim();
337
316
  }
338
317
  function renderSharedTypesTemplate() {
339
- return `// Nuxt GraphQL types (shared)
318
+ return `
340
319
  import type { DocumentNode } from "graphql";
341
320
  import type { CacheConfig } from "nuxt-graphql/runtime/shared/lib/cache-config";
342
321
 
322
+ declare global {
323
+ type GraphQLCacheConfig = {
324
+ policy: "no-cache" | "cache-first" | "network-first" | "swr";
325
+ ttl?: number;
326
+ keyPrefix: string;
327
+ keyVersion: string | number;
328
+ };;
329
+ }
330
+
343
331
  declare module "nuxt/schema" {
344
332
  interface PublicRuntimeConfig {
345
333
  graphql: {
346
- cacheConfig?: CacheConfig;
334
+ cacheConfig: GraphQLCacheConfig;
347
335
  ssrForwardHeaders: string[];
348
336
  };
349
337
  }
350
338
  }
351
339
 
352
- export {};
353
- `;
340
+ export { };
341
+
342
+ `.trim();
354
343
  }
355
344
 
356
345
  const module$1 = defineNuxtModule({
@@ -384,87 +373,96 @@ const module$1 = defineNuxtModule({
384
373
  }
385
374
  nuxt.options.alias ||= {};
386
375
  nuxt.options.alias["#graphql"] ||= resolveBuild("graphql");
387
- const contextModules = [
388
- ...await Promise.all((options.yoga?.context || []).map((path) => resolveRootPath(path, true)))
389
- ];
390
- addTemplate({ filename: "graphql/context.ts", getContents: () => renderContextTemplate({ contextModules }), write: true });
391
- addServerTemplate({ filename: "#graphql/#context.ts", getContents: () => renderContextTemplate({ contextModules }) });
392
- const schemaDefs = {};
376
+ const contextModules = await Promise.all((options.yoga?.context || []).map((path) => resolveRootPath(path, true)));
377
+ addTemplate({ filename: "graphql/context.mjs", getContents: () => renderContextTemplate({ contextModules }), write: true });
378
+ addTemplate({ filename: "graphql/context.d.ts", getContents: () => renderContextTypesTemplate({ contextModules }), write: true });
379
+ addServerTemplate({ filename: "#graphql/context.mjs", getContents: () => renderContextTemplate({ contextModules }) });
380
+ const schemasCache = /* @__PURE__ */ new Map([]);
381
+ function cachedSchemaLoader(schemaDef, loader) {
382
+ return async () => {
383
+ const key = `schemaDef:${hash(schemaDef)}`;
384
+ const cached = schemasCache.get(key);
385
+ if (cached?.key === key) return cached.data;
386
+ const schema = await loader();
387
+ schemasCache.set(key, { key, data: schema });
388
+ return schema;
389
+ };
390
+ }
391
+ const remoteExecutorModule = resolveModule("./runtime/server/lib/remote-executor");
392
+ const schemaLoaders = {};
393
393
  for (const [schemaName, schemaDef] of Object.entries(options.yoga?.schemas || {})) {
394
394
  if (schemaDef.type === "local") {
395
- const localSchemaDef = {
396
- ...schemaDef,
397
- path: await resolveRootPath(schemaDef.path, true)
398
- };
399
- schemaDefs[schemaName] = localSchemaDef;
400
- addTemplate({ filename: `graphql/schemas/${schemaName}.ts`, getContents: async () => renderLocalSchemaTemplate({ ...localSchemaDef }), write: true });
401
- addServerTemplate({ filename: `#graphql/schemas/${schemaName}.ts`, getContents: async () => renderLocalSchemaTemplate({ ...localSchemaDef }) });
395
+ const schemaModule = await resolveRootPath(schemaDef.path, true);
396
+ schemaLoaders[schemaName] = cachedSchemaLoader(schemaDef, async () => await loadLocalSchema({ schemaModule }));
397
+ addTemplate({ filename: `graphql/schemas/${schemaName}.mjs`, getContents: async () => renderLocalSchemaTemplate({ schemaModule }), write: true });
398
+ addServerTemplate({ filename: `#graphql/schemas/${schemaName}.mjs`, getContents: async () => renderLocalSchemaTemplate({ schemaModule }) });
402
399
  } else if (schemaDef.type === "remote") {
403
- const remoteSchemaDef = {
404
- ...schemaDef,
405
- hooks: await Promise.all((schemaDef.hooks || []).map((hookPath) => resolveRootPath(hookPath, true))),
406
- remoteExecutorModule: resolveModule("./runtime/server/lib/remote-executor")
400
+ schemaLoaders[schemaName] = cachedSchemaLoader(schemaDef, async () => await introspectRemoteSchema(schemaDef));
401
+ const input = {
402
+ remoteExecutorModule,
403
+ hooksModules: await Promise.all((schemaDef.hooks || []).map((hookPath) => resolveRootPath(hookPath, true))),
404
+ schemaDef,
405
+ schemaLoader: schemaLoaders[schemaName]
407
406
  };
408
- schemaDefs[schemaName] = remoteSchemaDef;
409
- addTemplate({ filename: `graphql/schemas/${schemaName}.ts`, getContents: async () => await renderRemoteSchemaTemplate({ ...remoteSchemaDef }), write: true });
410
- addServerTemplate({ filename: `#graphql/schemas/${schemaName}.ts`, getContents: async () => await renderRemoteSchemaTemplate({ ...remoteSchemaDef }) });
407
+ addTemplate({ filename: `graphql/schemas/${schemaName}.mjs`, getContents: async () => await renderRemoteSchemaTemplate(input), write: true });
408
+ addServerTemplate({ filename: `#graphql/schemas/${schemaName}.mjs`, getContents: async () => await renderRemoteSchemaTemplate(input) });
411
409
  } else {
412
410
  throw new Error(`Unknown schema type for schema "${schemaName}"`);
413
411
  }
414
412
  }
415
- addTemplate({ filename: "graphql/schema.ts", getContents: () => renderStitchedSchemaTemplate({ schemaNames: Object.keys(options.yoga?.schemas || {}) }), write: true });
416
- addServerTemplate({ filename: "#graphql/schema.ts", getContents: () => renderStitchedSchemaTemplate({ schemaNames: Object.keys(options.yoga?.schemas || {}) }) });
417
- let documentsCache = null;
418
- async function getDocuments(glob) {
419
- const key = `documents:${glob}`;
420
- if (documentsCache?.key === key) return documentsCache.data;
421
- const documents = await loadDocuments(glob);
422
- documentsCache = { key, data: documents };
423
- return documents;
424
- }
425
413
  const sdlPath = resolveRoot(options.saveSDL || "server/graphql/schema.graphql");
426
- let schemaCache = null;
427
- async function getStitchedSchema(schemaDefs2) {
428
- const key = `schema:${hash(schemaDefs2)}`;
429
- if (schemaCache?.key === key) return schemaCache.data;
430
- const schema = await loadStitchedSchema(schemaDefs2);
431
- schemaCache = { key, data: schema };
414
+ const loadStitchedSchema = cachedSchemaLoader({ type: "stitched" }, async () => {
415
+ const schema = stitchSchemas({
416
+ subschemas: await Promise.all(Object.values(schemaLoaders).map((loader) => loader()))
417
+ });
432
418
  const sdl = await printSchemaSDL(schema);
433
419
  mkdirSync(dirname(sdlPath), { recursive: true });
434
420
  writeFileSync(sdlPath, sdl, { encoding: "utf-8" });
435
421
  logger.info(`GraphQL SDL saved to: ${cyan}${getRelativePath(sdlPath)}${reset}`);
436
422
  return schema;
423
+ });
424
+ addTemplate({ filename: "graphql/schema.mjs", getContents: () => renderSchemaTemplate({ schemaNames: Object.keys(options.yoga?.schemas || {}) }), write: true });
425
+ addTemplate({ filename: "graphql/schema.d.ts", getContents: () => renderSchemaTypesTemplate(), write: true });
426
+ addServerTemplate({ filename: "#graphql/schema.mjs", getContents: () => renderSchemaTemplate({ schemaNames: Object.keys(options.yoga?.schemas || {}) }) });
427
+ const documentsGlob = options.client?.documents || "**/*.gql";
428
+ let documentsCache = null;
429
+ async function loadDocumentsCached(glob) {
430
+ const key = `documents:${glob}`;
431
+ if (documentsCache?.key === key) return documentsCache.data;
432
+ const documents = await loadDocuments(glob);
433
+ documentsCache = { key, data: documents };
434
+ return documents;
437
435
  }
438
436
  addTemplate({
439
437
  filename: "graphql/operations.ts",
440
438
  getContents: async () => await renderOperationsTemplate({
441
- schema: await getStitchedSchema(schemaDefs),
442
- documents: await getDocuments(options.client?.documents || "**/*.gql")
439
+ schema: await loadStitchedSchema(),
440
+ documents: await loadDocumentsCached(documentsGlob)
443
441
  }),
444
442
  write: true
445
443
  });
446
444
  addTemplate({
447
445
  filename: "graphql/fragments.ts",
448
446
  getContents: async () => await renderFragmentsTemplate({
449
- documents: await getDocuments(options.client?.documents || "**/*.gql")
447
+ documents: await loadDocumentsCached(documentsGlob)
450
448
  }),
451
449
  write: true
452
450
  });
453
451
  addTemplate({
454
452
  filename: "graphql/registry.ts",
455
453
  getContents: async () => await renderRegistryTemplate({
456
- documents: await getDocuments(options.client?.documents || "**/*.gql")
454
+ documents: await loadDocumentsCached(documentsGlob)
457
455
  }),
458
456
  write: true
459
457
  });
460
- addTypeTemplate({ filename: "types/nuxt-graphql.app.d.ts", getContents: () => renderAppTypesTemplate() }, { nuxt: true });
461
- addTypeTemplate({ filename: "types/nuxt-graphql.server.d.ts", getContents: () => renderServerTypesTemplate() }, { nitro: true });
462
- addTypeTemplate({ filename: "types/nuxt-graphql.shared.d.ts", getContents: () => renderSharedTypesTemplate() }, { nuxt: true, nitro: true });
463
458
  const configPath = resolveRoot(options.saveConfig || "graphql.config.json");
464
- const config = { schema: getRelativePath(sdlPath), documents: options.client?.documents || "**/*.gql" };
459
+ const config = { schema: getRelativePath(sdlPath), documents: documentsGlob };
465
460
  mkdirSync(dirname(configPath), { recursive: true });
466
461
  writeFileSync(configPath, JSON.stringify(config, null, 2), { encoding: "utf-8" });
467
462
  logger.info(`GraphQL config saved to: ${cyan}${getRelativePath(configPath)}${reset}`);
463
+ addTypeTemplate({ filename: "types/nuxt-graphql.app.d.ts", getContents: () => renderAppTypesTemplate() }, { nuxt: true });
464
+ addTypeTemplate({ filename: "types/nuxt-graphql.server.d.ts", getContents: () => renderServerTypesTemplate() }, { nitro: true, node: true });
465
+ addTypeTemplate({ filename: "types/nuxt-graphql.shared.d.ts", getContents: () => renderSharedTypesTemplate() }, { nuxt: true, nitro: true, node: true });
468
466
  nuxt.options.runtimeConfig.public.graphql = defu(nuxt.options.runtimeConfig.public.graphql, {
469
467
  cacheConfig: resolveCacheConfig(options.client?.cache),
470
468
  ssrForwardHeaders: options.client?.ssrForwardHeaders || ["authorization", "cookie"]
@@ -2,10 +2,9 @@ import { useAsyncData, type AsyncDataOptions } from "#app";
2
2
  import type { QueryName, ResultOf, VariablesOf } from "#graphql/registry";
3
3
  import { type MaybeRefOrGetter } from "#imports";
4
4
  import { type ExecuteGraphQLHTTPOptions } from "../lib/execute-http.js";
5
- import { type CacheConfig } from "../../shared/lib/cache-config.js";
6
5
  import type { IsEmptyObject } from "../../shared/lib/utils.js";
7
6
  type UseAsyncGraphQLQueryOptions<TName extends QueryName> = ExecuteGraphQLHTTPOptions & {
8
- cache?: Partial<CacheConfig>;
7
+ cache?: Partial<GraphQLCacheConfig>;
9
8
  } & AsyncDataOptions<ResultOf<TName>>;
10
9
  /**
11
10
  * Async GraphQL query composable with caching support.
@@ -1,10 +1,9 @@
1
1
  import { useAsyncData, useNuxtData, useRuntimeConfig } from "#app";
2
2
  import { computed, toValue } from "#imports";
3
- import { getCacheKeyParts } from "../lib/cache.js";
4
3
  import { executeGraphQLHTTP } from "../lib/execute-http.js";
5
4
  import { getInFlightRequests } from "../lib/in-flight.js";
6
5
  import { getPersistedEntry, setPersistedEntry } from "../lib/persisted.js";
7
- import { resolveCacheConfig } from "../../shared/lib/cache-config.js";
6
+ import { getCacheKeyParts, resolveCacheConfig } from "../../shared/lib/cache.js";
8
7
  export function useAsyncGraphQLQuery(operationName, ...args) {
9
8
  const [variables, options] = args;
10
9
  const isClient = import.meta.client;
@@ -1,5 +1,5 @@
1
1
  import { clearNuxtData, useRuntimeConfig } from "#imports";
2
- import { getCacheKeyParts } from "../lib/cache.js";
2
+ import { getCacheKeyParts } from "../../shared/lib/cache.js";
3
3
  import { deletePersistedByPrefix, deletePersistedEntry } from "../lib/persisted.js";
4
4
  export function useGraphQLCache() {
5
5
  const { public: { graphql: { cacheConfig } } } = useRuntimeConfig();
@@ -1,9 +1,7 @@
1
1
  import type { Executor } from "@graphql-tools/utils";
2
- import { type HeadersInput } from "../../shared/lib/headers.js";
3
2
  import type { GraphQLRemoteExecHooks } from "../utils/defineRemoteExecutorHooks.js";
4
- type CreateRemoteExecutorInput = {
5
- url: string;
6
- headers?: HeadersInput;
3
+ import type { RemoteSchemaDef } from "../../../lib/schemas.js";
4
+ type CreateRemoteExecutorInput = Pick<RemoteSchemaDef, "url" | "headers"> & {
7
5
  hooks: GraphQLRemoteExecHooks[];
8
6
  };
9
7
  /**
@@ -1,3 +1,3 @@
1
1
  {
2
- "extends": "../../../.nuxt/tsconfig.server.json",
3
- }
2
+ "extends": "../../../.nuxt/tsconfig.server.json"
3
+ }
@@ -1,4 +1,10 @@
1
- import type { CacheConfig } from "../../shared/lib/cache-config.js";
1
+ /**
2
+ * Merge the default cache config with user overrides.
3
+ *
4
+ * @param overrides Partial cache config overrides.
5
+ * @returns Resolved cache configuration.
6
+ */
7
+ export declare function resolveCacheConfig(...overrides: Array<Partial<GraphQLCacheConfig> | undefined>): GraphQLCacheConfig;
2
8
  type CacheKeyParts = {
3
9
  key: string;
4
10
  opPrefix: string;
@@ -6,7 +12,7 @@ type CacheKeyParts = {
6
12
  /**
7
13
  * Build cache key parts from config, operation name, and variables.
8
14
  *
9
- * @param {CacheConfig} options Cache configuration.
15
+ * @param {GraphQLCacheConfig} options Cache configuration.
10
16
  * @param options.keyPrefix Cache key prefix.
11
17
  * @param options.keyVersion Cache key version.
12
18
  * @param operationName Operation name.
@@ -14,5 +20,5 @@ type CacheKeyParts = {
14
20
  * @param scope Optional cache scope segment.
15
21
  * @returns Key parts including full key and operation prefix.
16
22
  */
17
- export declare function getCacheKeyParts({ keyPrefix, keyVersion }: CacheConfig, operationName: string, variables: unknown, scope?: string): CacheKeyParts;
23
+ export declare function getCacheKeyParts({ keyPrefix, keyVersion }: GraphQLCacheConfig, operationName: string, variables: unknown, scope?: string): CacheKeyParts;
18
24
  export {};
@@ -1,4 +1,13 @@
1
1
  import { hash } from "ohash";
2
+ const defaultCacheConfig = {
3
+ keyPrefix: "gql",
4
+ keyVersion: "1",
5
+ policy: "no-cache",
6
+ ttl: void 0
7
+ };
8
+ export function resolveCacheConfig(...overrides) {
9
+ return Object.assign({}, defaultCacheConfig, ...overrides);
10
+ }
2
11
  export function getCacheKeyParts({ keyPrefix, keyVersion }, operationName, variables, scope) {
3
12
  const parts = [keyPrefix, keyVersion];
4
13
  if (scope) parts.push(scope);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lewebsimple/nuxt-graphql",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Opinionated Nuxt module for using GraphQL",
5
5
  "repository": "lewebsimple/nuxt-graphql",
6
6
  "license": "AGPL-3.0-only",
@@ -1,42 +0,0 @@
1
- type GraphQLCachePolicy = "no-cache" | "cache-first" | "network-first" | "swr";
2
- export type CacheConfig = {
3
- /**
4
- * Prefix used for all persisted cache keys.
5
- *
6
- * Used for namespacing and bulk invalidation.
7
- * Default: 'graphql'
8
- */
9
- keyPrefix: string;
10
- /**
11
- * Version included in cache keys.
12
- *
13
- * Changing this value invalidates all existing cache entries.
14
- * Default: '1'
15
- */
16
- keyVersion: string | number;
17
- /**
18
- * Cache strategy used by useAsyncGraphQLQuery.
19
- *
20
- * - 'no-cache' → always fetch, never read/write cache
21
- * - 'cache-first' → return cache if valid, otherwise fetch
22
- * - 'network-first' → fetch first, fallback to cache on failure
23
- * - 'swr' → return cache immediately, revalidate in background
24
- */
25
- policy: GraphQLCachePolicy;
26
- /**
27
- * Time-to-live in seconds.
28
- *
29
- * - undefined → inherit from higher-level config
30
- * - 0 → never expires
31
- * - > 0 → expires after TTL
32
- */
33
- ttl?: number;
34
- };
35
- /**
36
- * Merge the default cache config with user overrides.
37
- *
38
- * @param overrides Partial cache config overrides.
39
- * @returns Resolved cache configuration.
40
- */
41
- export declare function resolveCacheConfig(...overrides: Array<Partial<CacheConfig> | undefined>): CacheConfig;
42
- export {};
@@ -1,9 +0,0 @@
1
- const defaultCacheConfig = {
2
- keyPrefix: "gql",
3
- keyVersion: "1",
4
- policy: "no-cache",
5
- ttl: void 0
6
- };
7
- export function resolveCacheConfig(...overrides) {
8
- return Object.assign({}, defaultCacheConfig, ...overrides);
9
- }