@lewebsimple/nuxt-graphql 0.5.4 → 0.5.6
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 +1 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +236 -238
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.d.ts +1 -2
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.js +1 -2
- package/dist/runtime/app/composables/useGraphQLCache.client.js +1 -1
- package/dist/runtime/server/lib/remote-executor.d.ts +2 -4
- package/dist/runtime/server/tsconfig.json +2 -2
- package/dist/runtime/{app → shared}/lib/cache.d.ts +9 -3
- package/dist/runtime/{app → shared}/lib/cache.js +9 -0
- package/package.json +1 -1
- package/dist/runtime/shared/lib/cache-config.d.ts +0 -42
- package/dist/runtime/shared/lib/cache-config.js +0 -9
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<
|
|
41
|
+
cache?: Partial<GraphQLCacheConfig>;
|
|
43
42
|
/**
|
|
44
43
|
* GraphQL documents glob pattern.
|
|
45
44
|
* Default: "**\/*.gql"
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,89 +1,49 @@
|
|
|
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
|
-
import { hash } from 'ohash';
|
|
6
|
-
import { loadDocuments as loadDocuments$1 } from '@graphql-tools/load';
|
|
7
4
|
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
|
|
8
|
-
import {
|
|
5
|
+
import { loadDocuments } from '@graphql-tools/load';
|
|
6
|
+
import { stitchSchemas } from '@graphql-tools/stitch';
|
|
7
|
+
import { defineNuxtModule, useLogger, createResolver, addTemplate, addServerTemplate, addTypeTemplate, addServerHandler, addPlugin, addImportsDir, addServerImportsDir } from '@nuxt/kit';
|
|
9
8
|
import { codegen } from '@graphql-codegen/core';
|
|
10
9
|
import * as typescriptPlugin from '@graphql-codegen/typescript';
|
|
11
10
|
import * as typescriptOperationsPlugin from '@graphql-codegen/typescript-operations';
|
|
12
11
|
import * as typedDocumentNodePlugin from '@graphql-codegen/typed-document-node';
|
|
12
|
+
import { Kind, getIntrospectionQuery, buildClientSchema, GraphQLSchema } from 'graphql';
|
|
13
13
|
import { mergeHeaders } from '../dist/runtime/shared/lib/headers.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { resolveCacheConfig } from '../dist/runtime/shared/lib/cache.js';
|
|
15
|
+
import { hash } from 'ohash';
|
|
16
16
|
|
|
17
17
|
const cyan = "\x1B[36m";
|
|
18
18
|
const reset = "\x1B[0m";
|
|
19
19
|
|
|
20
20
|
function renderContextTemplate({ contextModules }) {
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
return
|
|
24
|
-
|
|
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");
|
|
37
|
-
}
|
|
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")}
|
|
38
25
|
|
|
39
|
-
async function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"!**/.cache/**",
|
|
44
|
-
"!**/.nuxt/**",
|
|
45
|
-
"!**/.output/**",
|
|
46
|
-
"!**/dist/**",
|
|
47
|
-
"!**/node_modules/**"
|
|
48
|
-
], { loaders: [new GraphQLFileLoader()] });
|
|
49
|
-
} catch {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
26
|
+
export async function createContext(event) {
|
|
27
|
+
const parts = await Promise.all([${contexts.join(", ")}]);
|
|
28
|
+
return Object.assign({}, ...parts);
|
|
29
|
+
}`.trim();
|
|
52
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")}
|
|
53
37
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return [
|
|
60
|
-
`export type {`,
|
|
61
|
-
...fragments.map((name) => ` ${name}Fragment,`),
|
|
62
|
-
`} from "./operations";`
|
|
63
|
-
].join("\n");
|
|
64
|
-
}
|
|
65
|
-
function collectFragments(documents) {
|
|
66
|
-
const fragments = /* @__PURE__ */ new Set();
|
|
67
|
-
for (const source of documents) {
|
|
68
|
-
const doc = source.document;
|
|
69
|
-
if (!doc) continue;
|
|
70
|
-
for (const def of doc.definitions) {
|
|
71
|
-
if (def.kind !== Kind.FRAGMENT_DEFINITION) continue;
|
|
72
|
-
const name = def.name?.value;
|
|
73
|
-
if (!name) continue;
|
|
74
|
-
if (fragments.has(name)) {
|
|
75
|
-
throw new Error(`Duplicate GraphQL fragment name "${name}"`);
|
|
76
|
-
}
|
|
77
|
-
fragments.add(name);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return [...fragments.values()];
|
|
38
|
+
declare module "#graphql/context" {
|
|
39
|
+
export type GraphQLContext = ${contextTypes.join(" & ")};
|
|
40
|
+
export function createContext(event: H3Event): Promise<GraphQLContext>;
|
|
41
|
+
}`.trim();
|
|
81
42
|
}
|
|
82
43
|
|
|
83
44
|
async function renderOperationsTemplate({ schema, documents }) {
|
|
84
|
-
const
|
|
45
|
+
const content = await codegen({
|
|
85
46
|
filename: "operations.ts",
|
|
86
|
-
// @graphql-codegen/core codegen supports GraphQLSchema at runtime, but types expect DocumentNode
|
|
87
47
|
schema,
|
|
88
48
|
documents,
|
|
89
49
|
plugins: [
|
|
@@ -133,51 +93,72 @@ async function renderOperationsTemplate({ schema, documents }) {
|
|
|
133
93
|
},
|
|
134
94
|
config: {}
|
|
135
95
|
});
|
|
136
|
-
|
|
96
|
+
const docs = splitDocuments(content);
|
|
97
|
+
const module = docs.map(({ name, object }) => `export const ${name} = ${object};`).join("\n");
|
|
98
|
+
const types = `${content.replace(/export const \w+ = [\s\S]*?;\n?/g, "")}
|
|
99
|
+
declare module "#graphql/operations" {
|
|
100
|
+
${docs.map(({ name, type }) => `export type ${name} = ${type};`).join("\n ")}
|
|
101
|
+
}`.trim();
|
|
102
|
+
return { module, types };
|
|
103
|
+
}
|
|
104
|
+
function splitDocuments(content) {
|
|
105
|
+
const documents = [];
|
|
106
|
+
const documentRegex = /export const (\w+) = ([\s\S]*?) as unknown as (DocumentNode<[^>]+>);/g;
|
|
107
|
+
let match;
|
|
108
|
+
while (match = documentRegex.exec(content)) {
|
|
109
|
+
const [, name, object, type] = match;
|
|
110
|
+
if (!name || !object || !type) {
|
|
111
|
+
throw new Error("Invalid typed document matched while splitting documents.");
|
|
112
|
+
}
|
|
113
|
+
documents.push({ name, object: object.trim(), type });
|
|
114
|
+
}
|
|
115
|
+
return documents;
|
|
137
116
|
}
|
|
138
117
|
|
|
139
118
|
async function renderRegistryTemplate({ documents }) {
|
|
140
119
|
const operations = collectOperations(documents);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
120
|
+
const module = `
|
|
121
|
+
import {
|
|
122
|
+
${operations.map(({ name }) => `${name}Document`).join(",\n ")}
|
|
123
|
+
} from "./operations";
|
|
124
|
+
|
|
125
|
+
export const registry = {
|
|
126
|
+
${operations.map(({ name, kind }) => `${name}: { kind: "${kind}", document: ${name}Document },`).join("\n ")}
|
|
127
|
+
};`.trim();
|
|
128
|
+
const types = `
|
|
129
|
+
import type { DocumentNode } from "graphql";
|
|
130
|
+
import type {
|
|
131
|
+
${operations.map(({ name, kind }) => `${name}${capitalize(kind)}Variables, ${name}${capitalize(kind)}Result,`).join("\n ")}
|
|
132
|
+
} from "./operations";
|
|
133
|
+
|
|
134
|
+
// Operation entry
|
|
135
|
+
export interface OperationEntry<TVariables, TResult, TKind extends "query" | "mutation" | "subscription"> {
|
|
136
|
+
kind: TKind;
|
|
137
|
+
variables: TVariables;
|
|
138
|
+
result: TResult;
|
|
139
|
+
document: DocumentNode;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Operation registry type
|
|
143
|
+
export type OperationRegistry = {
|
|
144
|
+
${operations.map(({ name, kind }) => `${name}: OperationEntry<${name}${capitalize(kind)}Variables, ${name}${capitalize(kind)}Result, "${kind}">;`).join("\n ")}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Operation name types
|
|
148
|
+
export type OperationName = keyof OperationRegistry;
|
|
149
|
+
export type QueryName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "query" ? K : never }[keyof OperationRegistry];
|
|
150
|
+
export type MutationName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "mutation" ? K : never }[keyof OperationRegistry];
|
|
151
|
+
export type SubscriptionName = { [K in keyof OperationRegistry]: OperationRegistry[K]["kind"] extends "subscription" ? K : never }[keyof OperationRegistry];
|
|
152
|
+
|
|
153
|
+
// Projection helpers (variables / result)
|
|
154
|
+
export type VariablesOf<TName extends keyof OperationRegistry> = OperationRegistry[TName]["variables"];
|
|
155
|
+
export type ResultOf<TName extends keyof OperationRegistry> = OperationRegistry[TName]["result"];
|
|
156
|
+
|
|
157
|
+
declare module "#graphql/registry" {
|
|
158
|
+
export const registry: { [K in keyof OperationRegistry]: { kind: OperationRegistry[K]["kind"]; document: DocumentNode; }; };
|
|
159
|
+
export type { OperationRegistry, OperationName, QueryName, MutationName, SubscriptionName, VariablesOf, ResultOf };
|
|
160
|
+
}`.trim();
|
|
161
|
+
return { module, types };
|
|
181
162
|
}
|
|
182
163
|
function collectOperations(documents) {
|
|
183
164
|
const operations = /* @__PURE__ */ new Map();
|
|
@@ -193,50 +174,71 @@ function collectOperations(documents) {
|
|
|
193
174
|
if (operations.has(name)) {
|
|
194
175
|
throw new Error(`Duplicate GraphQL operation name "${name}"`);
|
|
195
176
|
}
|
|
196
|
-
operations.set(name, {
|
|
197
|
-
name,
|
|
198
|
-
kind: def.operation
|
|
199
|
-
});
|
|
177
|
+
operations.set(name, { name, kind: def.operation });
|
|
200
178
|
}
|
|
201
179
|
}
|
|
202
|
-
return
|
|
180
|
+
return Array.from(operations.values());
|
|
181
|
+
}
|
|
182
|
+
function capitalize(value) {
|
|
183
|
+
if (!value) return value;
|
|
184
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function renderLocalSchemaTemplate({ schemaModule }) {
|
|
188
|
+
return `export { schema } from ${JSON.stringify(schemaModule)};`;
|
|
189
|
+
}
|
|
190
|
+
async function renderRemoteSchemaTemplate({ remoteExecutorModule, hooksModules, schemaDef, schemaLoader }) {
|
|
191
|
+
const importHooks = hooksModules.map((hookPath, index) => `import hooks${index} from ${JSON.stringify(hookPath)};`);
|
|
192
|
+
const hooks = hooksModules.map((_, index) => `hooks${index}`);
|
|
193
|
+
const { url, headers } = schemaDef;
|
|
194
|
+
const schema = await schemaLoader();
|
|
195
|
+
const sdl = await printSchemaSDL(schema);
|
|
196
|
+
return `
|
|
197
|
+
import { buildSchema } from "graphql";
|
|
198
|
+
import { createRemoteExecutor } from ${JSON.stringify(remoteExecutorModule)};
|
|
199
|
+
${importHooks.join("\n")}
|
|
200
|
+
|
|
201
|
+
const sdl = /* GraphQL */ \`${sdl.replace(/`/g, "\\`")}\`;
|
|
202
|
+
|
|
203
|
+
export const schema = {
|
|
204
|
+
schema: buildSchema(sdl),
|
|
205
|
+
executor: createRemoteExecutor({
|
|
206
|
+
url: ${JSON.stringify(url)},
|
|
207
|
+
headers: ${JSON.stringify(headers || {})},
|
|
208
|
+
hooks: [${hooks.join(", ")}]
|
|
209
|
+
}),
|
|
210
|
+
};
|
|
211
|
+
`.trim();
|
|
212
|
+
}
|
|
213
|
+
function renderSchemaTemplate({ schemaNames }) {
|
|
214
|
+
const importSchemas = schemaNames.map((name) => `import { schema as ${name}Schema } from ${JSON.stringify(`./schemas/${name}`)};`);
|
|
215
|
+
const schemas = schemaNames.map((name) => `${name}Schema`);
|
|
216
|
+
return `
|
|
217
|
+
import { stitchSchemas } from "@graphql-tools/stitch";
|
|
218
|
+
${importSchemas.join("\n")}
|
|
219
|
+
|
|
220
|
+
export const schema = stitchSchemas({
|
|
221
|
+
subschemas: [${schemas.join(", ")}],
|
|
222
|
+
});`.trim();
|
|
203
223
|
}
|
|
224
|
+
function renderSchemaTypesTemplate() {
|
|
225
|
+
return `
|
|
226
|
+
import type { GraphQLSchema } from "graphql";
|
|
204
227
|
|
|
205
|
-
|
|
206
|
-
|
|
228
|
+
declare module "#graphql/schema" {
|
|
229
|
+
export const schema: GraphQLSchema;
|
|
230
|
+
}
|
|
231
|
+
`.trim();
|
|
207
232
|
}
|
|
208
|
-
async function loadLocalSchema({
|
|
233
|
+
async function loadLocalSchema({ schemaModule }) {
|
|
209
234
|
const { createJiti } = await import('jiti');
|
|
210
235
|
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
211
|
-
const module = await jiti.import(
|
|
236
|
+
const module = await jiti.import(schemaModule);
|
|
212
237
|
if (!module.schema || !(module.schema instanceof Object) || typeof module.schema.getQueryType !== "function") {
|
|
213
|
-
throw new Error(`${
|
|
238
|
+
throw new Error(`${schemaModule} must export a valid 'schema' of type GraphQLSchema.`);
|
|
214
239
|
}
|
|
215
240
|
return module.schema;
|
|
216
241
|
}
|
|
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
242
|
async function introspectRemoteSchema({ url, headers }) {
|
|
241
243
|
const response = await fetch(url, {
|
|
242
244
|
method: "POST",
|
|
@@ -266,34 +268,9 @@ async function printSchemaSDL(schema) {
|
|
|
266
268
|
const { printSchema, lexicographicSortSchema } = await import('graphql');
|
|
267
269
|
return printSchema(lexicographicSortSchema(schema));
|
|
268
270
|
}
|
|
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
271
|
|
|
295
272
|
function renderAppTypesTemplate() {
|
|
296
|
-
return
|
|
273
|
+
return `
|
|
297
274
|
import type { GraphQLClient } from "graphql-request";
|
|
298
275
|
import type { Client as SSEClient } from "graphql-sse";
|
|
299
276
|
|
|
@@ -312,20 +289,10 @@ declare module "#app" {
|
|
|
312
289
|
}
|
|
313
290
|
|
|
314
291
|
export {};
|
|
315
|
-
|
|
292
|
+
`.trim();
|
|
316
293
|
}
|
|
317
294
|
function renderServerTypesTemplate() {
|
|
318
|
-
return
|
|
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
|
-
|
|
295
|
+
return `
|
|
329
296
|
declare module "h3" {
|
|
330
297
|
interface H3EventContext {
|
|
331
298
|
_graphqlInFlightRequestsMap?: Map<string, Promise<unknown>>;
|
|
@@ -333,24 +300,34 @@ declare module "h3" {
|
|
|
333
300
|
}
|
|
334
301
|
|
|
335
302
|
export {};
|
|
336
|
-
|
|
303
|
+
`.trim();
|
|
337
304
|
}
|
|
338
305
|
function renderSharedTypesTemplate() {
|
|
339
|
-
return
|
|
306
|
+
return `
|
|
340
307
|
import type { DocumentNode } from "graphql";
|
|
341
308
|
import type { CacheConfig } from "nuxt-graphql/runtime/shared/lib/cache-config";
|
|
342
309
|
|
|
310
|
+
declare global {
|
|
311
|
+
type GraphQLCacheConfig = {
|
|
312
|
+
policy: "no-cache" | "cache-first" | "network-first" | "swr";
|
|
313
|
+
ttl?: number;
|
|
314
|
+
keyPrefix: string;
|
|
315
|
+
keyVersion: string | number;
|
|
316
|
+
};;
|
|
317
|
+
}
|
|
318
|
+
|
|
343
319
|
declare module "nuxt/schema" {
|
|
344
320
|
interface PublicRuntimeConfig {
|
|
345
321
|
graphql: {
|
|
346
|
-
cacheConfig
|
|
322
|
+
cacheConfig: GraphQLCacheConfig;
|
|
347
323
|
ssrForwardHeaders: string[];
|
|
348
324
|
};
|
|
349
325
|
}
|
|
350
326
|
}
|
|
351
327
|
|
|
352
|
-
export {};
|
|
353
|
-
|
|
328
|
+
export { };
|
|
329
|
+
|
|
330
|
+
`.trim();
|
|
354
331
|
}
|
|
355
332
|
|
|
356
333
|
const module$1 = defineNuxtModule({
|
|
@@ -384,87 +361,107 @@ const module$1 = defineNuxtModule({
|
|
|
384
361
|
}
|
|
385
362
|
nuxt.options.alias ||= {};
|
|
386
363
|
nuxt.options.alias["#graphql"] ||= resolveBuild("graphql");
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
364
|
+
const buildCache = /* @__PURE__ */ new Map();
|
|
365
|
+
function cachedLoader(baseKey, loader) {
|
|
366
|
+
return async (...args) => {
|
|
367
|
+
const key = `${baseKey}:${hash(args)}`;
|
|
368
|
+
const cached = buildCache.get(key);
|
|
369
|
+
if (cached?.key === key) {
|
|
370
|
+
return cached.data;
|
|
371
|
+
}
|
|
372
|
+
const data = await loader(...args);
|
|
373
|
+
buildCache.set(key, { key, data });
|
|
374
|
+
return data;
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const contextModules = await Promise.all((options.yoga?.context || []).map((path) => resolveRootPath(path, true)));
|
|
378
|
+
addTemplate({ filename: "graphql/context.mjs", getContents: () => renderContextTemplate({ contextModules }), write: true });
|
|
379
|
+
addTemplate({ filename: "graphql/context.d.ts", getContents: () => renderContextTypesTemplate({ contextModules }), write: true });
|
|
380
|
+
addServerTemplate({ filename: "#graphql/context.mjs", getContents: () => renderContextTemplate({ contextModules }) });
|
|
381
|
+
const remoteExecutorModule = resolveModule("./runtime/server/lib/remote-executor");
|
|
382
|
+
const schemaCachedLoaders = {};
|
|
393
383
|
for (const [schemaName, schemaDef] of Object.entries(options.yoga?.schemas || {})) {
|
|
394
384
|
if (schemaDef.type === "local") {
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
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 }) });
|
|
385
|
+
const schemaModule = await resolveRootPath(schemaDef.path, true);
|
|
386
|
+
schemaCachedLoaders[schemaName] = cachedLoader(`schema:local:${schemaName}`, async () => await loadLocalSchema({ schemaModule }));
|
|
387
|
+
addTemplate({ filename: `graphql/schemas/${schemaName}.mjs`, getContents: async () => renderLocalSchemaTemplate({ schemaModule }), write: true });
|
|
388
|
+
addServerTemplate({ filename: `#graphql/schemas/${schemaName}.mjs`, getContents: async () => renderLocalSchemaTemplate({ schemaModule }) });
|
|
402
389
|
} else if (schemaDef.type === "remote") {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
390
|
+
schemaCachedLoaders[schemaName] = cachedLoader(`schema:remote:${schemaName}`, async () => await introspectRemoteSchema(schemaDef));
|
|
391
|
+
const input = {
|
|
392
|
+
remoteExecutorModule,
|
|
393
|
+
hooksModules: await Promise.all((schemaDef.hooks || []).map((hookPath) => resolveRootPath(hookPath, true))),
|
|
394
|
+
schemaDef,
|
|
395
|
+
schemaLoader: schemaCachedLoaders[schemaName]
|
|
407
396
|
};
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
addServerTemplate({ filename: `#graphql/schemas/${schemaName}.ts`, getContents: async () => await renderRemoteSchemaTemplate({ ...remoteSchemaDef }) });
|
|
397
|
+
addTemplate({ filename: `graphql/schemas/${schemaName}.mjs`, getContents: async () => await renderRemoteSchemaTemplate(input), write: true });
|
|
398
|
+
addServerTemplate({ filename: `#graphql/schemas/${schemaName}.mjs`, getContents: async () => await renderRemoteSchemaTemplate(input) });
|
|
411
399
|
} else {
|
|
412
400
|
throw new Error(`Unknown schema type for schema "${schemaName}"`);
|
|
413
401
|
}
|
|
414
402
|
}
|
|
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
403
|
const sdlPath = resolveRoot(options.saveSDL || "server/graphql/schema.graphql");
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const schema = await loadStitchedSchema(schemaDefs2);
|
|
431
|
-
schemaCache = { key, data: schema };
|
|
404
|
+
const loadCachedSchema = cachedLoader("schema:stitched", async () => {
|
|
405
|
+
const schema = stitchSchemas({
|
|
406
|
+
subschemas: await Promise.all(Object.values(schemaCachedLoaders).map((loader) => loader()))
|
|
407
|
+
});
|
|
432
408
|
const sdl = await printSchemaSDL(schema);
|
|
433
409
|
mkdirSync(dirname(sdlPath), { recursive: true });
|
|
434
410
|
writeFileSync(sdlPath, sdl, { encoding: "utf-8" });
|
|
435
411
|
logger.info(`GraphQL SDL saved to: ${cyan}${getRelativePath(sdlPath)}${reset}`);
|
|
436
412
|
return schema;
|
|
437
|
-
}
|
|
438
|
-
addTemplate({
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
413
|
+
});
|
|
414
|
+
addTemplate({ filename: "graphql/schema.mjs", getContents: () => renderSchemaTemplate({ schemaNames: Object.keys(options.yoga?.schemas || {}) }), write: true });
|
|
415
|
+
addTemplate({ filename: "graphql/schema.d.ts", getContents: () => renderSchemaTypesTemplate(), write: true });
|
|
416
|
+
addServerTemplate({ filename: "#graphql/schema.mjs", getContents: () => renderSchemaTemplate({ schemaNames: Object.keys(options.yoga?.schemas || {}) }) });
|
|
417
|
+
const loadCachedDocuments = cachedLoader("documents", async (documentsGlob) => {
|
|
418
|
+
try {
|
|
419
|
+
return await loadDocuments([
|
|
420
|
+
documentsGlob,
|
|
421
|
+
"!**/.cache/**",
|
|
422
|
+
"!**/.nuxt/**",
|
|
423
|
+
"!**/.output/**",
|
|
424
|
+
"!**/dist/**",
|
|
425
|
+
"!**/node_modules/**"
|
|
426
|
+
], { loaders: [new GraphQLFileLoader()] });
|
|
427
|
+
} catch {
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
const loadCachedOperations = cachedLoader("operations", async (documentsGlob) => {
|
|
432
|
+
const schema = await loadCachedSchema();
|
|
433
|
+
const documents = await loadCachedDocuments(documentsGlob);
|
|
434
|
+
return await renderOperationsTemplate({ schema, documents });
|
|
435
|
+
});
|
|
436
|
+
addTemplate({ filename: "graphql/operations.mjs", getContents: async () => (await loadCachedOperations(options.client?.documents || "**/*.gql")).module, write: true });
|
|
437
|
+
addServerTemplate({ filename: "#graphql/operations.mjs", getContents: async () => (await loadCachedOperations(options.client?.documents || "**/*.gql")).module });
|
|
438
|
+
addTemplate({ filename: "graphql/operations.d.ts", getContents: async () => (await loadCachedOperations(options.client?.documents || "**/*.gql")).types, write: true });
|
|
439
|
+
const loadCachedRegistry = cachedLoader("registry", async (documentsGlob) => {
|
|
440
|
+
const documents = await loadCachedDocuments(documentsGlob);
|
|
441
|
+
return await renderRegistryTemplate({ documents });
|
|
445
442
|
});
|
|
446
443
|
addTemplate({
|
|
447
|
-
filename: "graphql/
|
|
448
|
-
getContents: async () => await
|
|
449
|
-
documents: await getDocuments(options.client?.documents || "**/*.gql")
|
|
450
|
-
}),
|
|
444
|
+
filename: "graphql/registry.mjs",
|
|
445
|
+
getContents: async () => (await loadCachedRegistry(options.client?.documents || "**/*.gql")).module,
|
|
451
446
|
write: true
|
|
452
447
|
});
|
|
448
|
+
addServerTemplate({
|
|
449
|
+
filename: "#graphql/registry.mjs",
|
|
450
|
+
getContents: async () => (await loadCachedRegistry(options.client?.documents || "**/*.gql")).module
|
|
451
|
+
});
|
|
453
452
|
addTemplate({
|
|
454
|
-
filename: "graphql/registry.ts",
|
|
455
|
-
getContents: async () => await
|
|
456
|
-
documents: await getDocuments(options.client?.documents || "**/*.gql")
|
|
457
|
-
}),
|
|
453
|
+
filename: "graphql/registry.d.ts",
|
|
454
|
+
getContents: async () => (await loadCachedRegistry(options.client?.documents || "**/*.gql")).types,
|
|
458
455
|
write: true
|
|
459
456
|
});
|
|
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
457
|
const configPath = resolveRoot(options.saveConfig || "graphql.config.json");
|
|
464
458
|
const config = { schema: getRelativePath(sdlPath), documents: options.client?.documents || "**/*.gql" };
|
|
465
459
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
466
460
|
writeFileSync(configPath, JSON.stringify(config, null, 2), { encoding: "utf-8" });
|
|
467
461
|
logger.info(`GraphQL config saved to: ${cyan}${getRelativePath(configPath)}${reset}`);
|
|
462
|
+
addTypeTemplate({ filename: "types/nuxt-graphql.app.d.ts", getContents: () => renderAppTypesTemplate() }, { nuxt: true });
|
|
463
|
+
addTypeTemplate({ filename: "types/nuxt-graphql.server.d.ts", getContents: () => renderServerTypesTemplate() }, { nitro: true, node: true });
|
|
464
|
+
addTypeTemplate({ filename: "types/nuxt-graphql.shared.d.ts", getContents: () => renderSharedTypesTemplate() }, { nuxt: true, nitro: true, node: true });
|
|
468
465
|
nuxt.options.runtimeConfig.public.graphql = defu(nuxt.options.runtimeConfig.public.graphql, {
|
|
469
466
|
cacheConfig: resolveCacheConfig(options.client?.cache),
|
|
470
467
|
ssrForwardHeaders: options.client?.ssrForwardHeaders || ["authorization", "cookie"]
|
|
@@ -473,7 +470,8 @@ const module$1 = defineNuxtModule({
|
|
|
473
470
|
nuxt.hook("builder:watch", async (_event, changedPath) => {
|
|
474
471
|
if (changedPath.endsWith(".gql")) {
|
|
475
472
|
logger.info(`Documents change detected: ${cyan}${getRelativePath(changedPath)}${reset}`);
|
|
476
|
-
|
|
473
|
+
buildCache.delete("documents");
|
|
474
|
+
buildCache.delete("operations");
|
|
477
475
|
}
|
|
478
476
|
});
|
|
479
477
|
}
|
|
@@ -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<
|
|
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
|
|
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 "
|
|
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
|
|
5
|
-
|
|
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
|
-
|
|
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 {
|
|
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 }:
|
|
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,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 {};
|