@lewebsimple/nuxt-graphql 0.2.0 → 0.2.2
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/README.md +67 -36
- package/dist/module.d.mts +20 -6
- package/dist/module.json +1 -1
- package/dist/module.mjs +302 -197
- package/dist/runtime/app/composables/useGraphQLMutation.d.ts +2 -3
- package/dist/runtime/server/api/graphql-handler.js +1 -2
- package/dist/runtime/server/utils/remote-middleware.d.ts +18 -0
- package/dist/runtime/server/utils/remote-middleware.js +0 -0
- package/dist/runtime/server/utils/useServerGraphQLMutation.d.ts +2 -3
- package/package.json +8 -4
- package/dist/runtime/server/lib/logger.d.ts +0 -2
- package/dist/runtime/server/lib/logger.js +0 -2
- package/dist/runtime/server/yoga-handler.d.ts +0 -1
- package/dist/runtime/server/yoga-handler.js +0 -1
package/README.md
CHANGED
|
@@ -5,79 +5,110 @@
|
|
|
5
5
|
[![License][license-src]][license-href]
|
|
6
6
|
[![Nuxt][nuxt-src]][nuxt-href]
|
|
7
7
|
|
|
8
|
-
Opinionated Nuxt module
|
|
8
|
+
Opinionated Nuxt module that ships a stitched GraphQL Yoga server, typed GraphQL Codegen, and client/server composables powered by graphql-request and graphql-sse.
|
|
9
9
|
|
|
10
10
|
- ✨ [Release Notes](/CHANGELOG.md)
|
|
11
11
|
- 🏀 [Online playground](https://stackblitz.com/github/lewebsimple/nuxt-graphql?file=playground%2Fapp.vue)
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
|
-
- 🧘♂️ GraphQL Yoga server handler with user-provided schema / context
|
|
15
|
-
- 📄 Auto-import GraphQL documents from `**/*.gql` (configurable)
|
|
16
|
-
- 🧩 Type-safe composables to call operations by name, i.e. `useGraphQLQuery("Hello")`
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
- 🧘♂️ GraphQL Yoga handler at `/api/graphql` with GraphiQL in development.
|
|
16
|
+
- 🪡 Schema stitching: mix local schemas and remote endpoints (with per-source headers). Stitched SDL is emitted to `server/graphql/schema.graphql` (configurable).
|
|
17
|
+
- 🚦 Remote middleware hooks: per-remote `onRequest` / `onResponse` callbacks to tweak headers, log responses, or short-circuit requests before forwarding.
|
|
18
|
+
- 🪄 Code generation: scans named operations in `**/*.gql` (across Nuxt layers), generates typed documents, operations, registry (`#graphql/registry`), and optional Zod validation.
|
|
19
|
+
- 🧩 Typed composables: `useGraphQLQuery`, `useGraphQLMutation`, `useGraphQLSubscription` consume registry names (e.g. `useGraphQLQuery("Hello")`). Server equivalents mirror the API for Nitro handlers.
|
|
20
|
+
- 🚀 Caching and dedupe: in-memory or localStorage TTL cache, in-flight request deduplication, and refresh callbacks driven by runtime config.
|
|
21
|
+
- 📡 SSE subscriptions: client-only via graphql-sse, using the same registry documents.
|
|
22
|
+
- 🛡️SSR-friendly clients: forward `cookie` and `authorization` headers automatically on the server.
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
19
25
|
|
|
20
26
|
Install the module to your Nuxt application with one command:
|
|
21
27
|
|
|
22
28
|
```bash
|
|
23
|
-
|
|
29
|
+
pnpx nuxi module add @lewebsimple/nuxt-graphql
|
|
24
30
|
```
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
Configure at least one schema (local or remote) and optionnally your context (path to your context factory). Example with a local schema and remote stitched source:
|
|
27
33
|
|
|
28
34
|
```ts
|
|
29
35
|
// nuxt.config.ts
|
|
30
36
|
export default defineNuxtConfig({
|
|
31
37
|
modules: ["@lewebsimple/nuxt-graphql"],
|
|
32
38
|
graphql: {
|
|
33
|
-
//
|
|
39
|
+
// Optional: path to your GraphQL context factory
|
|
40
|
+
// Defaults to src/runtime/server/lib/default-context.ts if omitted
|
|
41
|
+
context: "server/graphql/context.ts",
|
|
42
|
+
schemas: {
|
|
43
|
+
local: { type: "local", path: "server/graphql/schema.ts" },
|
|
44
|
+
swapi: {
|
|
45
|
+
type: "remote",
|
|
46
|
+
url: "https://swapi-graphql.netlify.app/.netlify/functions/index",
|
|
47
|
+
middleware: "server/graphql/swapi-middleware.ts",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
34
50
|
codegen: {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
schemaOutput: "server/graphql/schema.graphql", // saved SDL
|
|
51
|
+
documents: "**/*.gql", // only named operations allowed
|
|
52
|
+
saveSchema: "server/graphql/schema.graphql",
|
|
38
53
|
},
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Define your GraphQL schema in `server/graphql/schema.ts`:
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
import { createSchema } from "graphql-yoga";
|
|
47
|
-
import type { GraphQLContext } from "./context";
|
|
48
|
-
|
|
49
|
-
export const schema = createSchema<GraphQLContext>({
|
|
50
|
-
typeDefs: /* GraphQL */ `
|
|
51
|
-
type Query {
|
|
52
|
-
hello: String!
|
|
53
|
-
}
|
|
54
|
-
`,
|
|
55
|
-
resolvers: {
|
|
56
|
-
Query: {
|
|
57
|
-
hello: () => "Hello world!",
|
|
54
|
+
client: {
|
|
55
|
+
cache: { enabled: true, ttl: 60_000, storage: "memory" },
|
|
56
|
+
headers: {},
|
|
58
57
|
},
|
|
59
58
|
},
|
|
60
59
|
});
|
|
61
60
|
```
|
|
62
61
|
|
|
63
|
-
|
|
62
|
+
Define context (optional) in `server/graphql/context.ts`:
|
|
64
63
|
|
|
65
64
|
```ts
|
|
66
65
|
import type { H3Event } from "h3";
|
|
67
66
|
|
|
68
|
-
export async function createContext(
|
|
69
|
-
return {
|
|
70
|
-
foo: "bar",
|
|
71
|
-
};
|
|
67
|
+
export async function createContext(event: H3Event) {
|
|
68
|
+
return { event, user: event.context.user };
|
|
72
69
|
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Write named operations in `.gql` files and use the auto-generated composables by operation name:
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
```ts
|
|
75
|
+
const { data, pending, error } = useGraphQLQuery("Hello", { name: "world" });
|
|
76
|
+
const { mutate } = useGraphQLMutation("Ping");
|
|
77
|
+
const { data: time } = useGraphQLSubscription("Time");
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
That's it! You can now use Nuxt GraphQL in your Nuxt app ✨
|
|
78
81
|
|
|
79
82
|
Yoga GraphiQL is available at `http://localhost:3000/api/graphql` by default.
|
|
80
83
|
|
|
84
|
+
Optional: add a remote middleware at `server/graphql/swapi-middleware.ts` to adjust headers or log activity for stitched sources:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
export default {
|
|
88
|
+
async onRequest({ fetchOptions }) {
|
|
89
|
+
return {
|
|
90
|
+
...fetchOptions,
|
|
91
|
+
headers: {
|
|
92
|
+
...fetchOptions.headers,
|
|
93
|
+
"x-swapi-api-key": process.env.SWAPI_TOKEN ?? "",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
async onResponse({ operationName }) {
|
|
98
|
+
console.log(`[SWAPI] completed ${operationName ?? "unknown"}`);
|
|
99
|
+
},
|
|
100
|
+
} satisfies RemoteMiddleware
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Both hooks are optional; return a new `RequestInit` from `onRequest` to override the outgoing fetch, or use `onResponse` for side-effects such as metrics and logging.
|
|
104
|
+
|
|
105
|
+
## Development notes
|
|
106
|
+
|
|
107
|
+
- Generated artifacts live under `.nuxt/graphql` and `.graphqlrc`; they are rewritten only when contents change.
|
|
108
|
+
- Operations must be **named and unique**; duplicates or unnamed operations fail codegen.
|
|
109
|
+
- SSE subscriptions are client-only; do not call `$graphqlSSE` on the server.
|
|
110
|
+
- Cache defaults come from `runtimeConfig.public.graphql.cache`; pass `cache: false` to per-call options to bypass.
|
|
111
|
+
|
|
81
112
|
## Contribution
|
|
82
113
|
|
|
83
114
|
<details>
|
package/dist/module.d.mts
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
import { GraphQLCacheConfig } from '../dist/runtime/app/utils/graphql-cache.js';
|
|
3
|
-
import { CodegenConfig } from '@graphql-codegen/cli';
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
type LocalSchema = {
|
|
5
|
+
type: "local";
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
8
|
+
type RemoteSchema = {
|
|
9
|
+
type: "remote";
|
|
10
|
+
url: string;
|
|
6
11
|
headers?: Record<string, string>;
|
|
7
|
-
|
|
12
|
+
middleware?: string;
|
|
13
|
+
};
|
|
14
|
+
type SchemaDefinition = LocalSchema | RemoteSchema;
|
|
15
|
+
|
|
16
|
+
interface ModuleOptions {
|
|
17
|
+
context?: string;
|
|
18
|
+
schemas: Record<string, SchemaDefinition>;
|
|
8
19
|
codegen?: {
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
documents?: string;
|
|
21
|
+
saveSchema?: string;
|
|
11
22
|
scalars?: Record<string, string | {
|
|
12
23
|
input: string;
|
|
13
24
|
output: string;
|
|
14
25
|
}>;
|
|
15
|
-
|
|
26
|
+
};
|
|
27
|
+
client?: {
|
|
28
|
+
cache?: Partial<GraphQLCacheConfig>;
|
|
29
|
+
headers?: Record<string, string>;
|
|
16
30
|
};
|
|
17
31
|
}
|
|
18
32
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useLogger, defineNuxtModule, createResolver, getLayerDirectories, addServerHandler, addImportsDir, addServerImportsDir
|
|
1
|
+
import { useLogger, defineNuxtModule, createResolver, getLayerDirectories, addServerHandler, addPlugin, addImportsDir, addServerImportsDir } from '@nuxt/kit';
|
|
2
|
+
import { join, parse, relative, dirname } from 'node:path';
|
|
2
3
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join, dirname, relative } from 'node:path';
|
|
4
4
|
import { glob } from 'tinyglobby';
|
|
5
5
|
import { generate } from '@graphql-codegen/cli';
|
|
6
|
-
import { parse, Kind } from 'graphql';
|
|
6
|
+
import { parse as parse$1, Kind, getIntrospectionQuery, buildClientSchema, printSchema } from 'graphql';
|
|
7
7
|
import { GRAPHQL_ENDPOINT } from '../dist/runtime/server/lib/constants.js';
|
|
8
8
|
|
|
9
9
|
const logger = useLogger("@lewebsimple/nuxt-graphql");
|
|
@@ -44,8 +44,16 @@ function writeFileIfChanged(path, content) {
|
|
|
44
44
|
writeFileSync(path, content, "utf-8");
|
|
45
45
|
return true;
|
|
46
46
|
}
|
|
47
|
+
function toImportPath(from, to) {
|
|
48
|
+
const { dir, name } = parse(to);
|
|
49
|
+
let importPath = relative(dirname(from), join(dir, name));
|
|
50
|
+
if (!importPath.startsWith(".")) {
|
|
51
|
+
importPath = "./" + importPath;
|
|
52
|
+
}
|
|
53
|
+
return importPath;
|
|
54
|
+
}
|
|
47
55
|
|
|
48
|
-
async function
|
|
56
|
+
async function loadSchemaSdl(schemaPath) {
|
|
49
57
|
const { createJiti } = await import('jiti');
|
|
50
58
|
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
51
59
|
const module = await jiti.import(schemaPath);
|
|
@@ -55,7 +63,67 @@ async function loadGraphQLSchema(schemaPath) {
|
|
|
55
63
|
const { printSchema, lexicographicSortSchema } = await import('graphql');
|
|
56
64
|
return printSchema(lexicographicSortSchema(module.schema));
|
|
57
65
|
}
|
|
58
|
-
function
|
|
66
|
+
async function runCodegen({ schema, documents, operationsPath, zodPath, scalars }) {
|
|
67
|
+
const generates = {
|
|
68
|
+
[operationsPath]: {
|
|
69
|
+
schema,
|
|
70
|
+
documents,
|
|
71
|
+
plugins: ["typescript", "typescript-operations", "typed-document-node"],
|
|
72
|
+
config: {
|
|
73
|
+
useTypeImports: true,
|
|
74
|
+
enumsAsTypes: true,
|
|
75
|
+
skipTypename: true,
|
|
76
|
+
documentVariableSuffix: "Document",
|
|
77
|
+
documentMode: "documentNode",
|
|
78
|
+
strictScalars: true,
|
|
79
|
+
defaultScalarType: "never",
|
|
80
|
+
scalars
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
if (zodPath) {
|
|
85
|
+
const zodScalars = {};
|
|
86
|
+
if (scalars) {
|
|
87
|
+
for (const [name, config] of Object.entries(scalars)) {
|
|
88
|
+
const inputType = typeof config === "string" ? config : config.input;
|
|
89
|
+
switch (inputType) {
|
|
90
|
+
case "Date":
|
|
91
|
+
zodScalars[name] = "z.coerce.date()";
|
|
92
|
+
break;
|
|
93
|
+
case "number":
|
|
94
|
+
zodScalars[name] = "z.coerce.number()";
|
|
95
|
+
break;
|
|
96
|
+
case "boolean":
|
|
97
|
+
zodScalars[name] = "z.coerce.boolean()";
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
zodScalars[name] = "z.string()";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
generates[zodPath] = {
|
|
105
|
+
schema,
|
|
106
|
+
documents,
|
|
107
|
+
plugins: ["typescript-validation-schema"],
|
|
108
|
+
config: {
|
|
109
|
+
schema: "zodv4",
|
|
110
|
+
importFrom: "#graphql/operations",
|
|
111
|
+
useTypeImports: true,
|
|
112
|
+
directives: {
|
|
113
|
+
constraint: {
|
|
114
|
+
minLength: "min",
|
|
115
|
+
maxLength: "max",
|
|
116
|
+
pattern: "regex"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
scalarSchemas: zodScalars
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
await generate({ generates, silent: true, errorsOnly: true }, true);
|
|
124
|
+
}
|
|
125
|
+
function analyzeDocuments(documents) {
|
|
126
|
+
const docs = documents.map((path) => ({ path, content: readFileSync(path, "utf-8") }));
|
|
59
127
|
const byFile = /* @__PURE__ */ new Map();
|
|
60
128
|
const operationsByType = {
|
|
61
129
|
query: [],
|
|
@@ -65,7 +133,7 @@ function analyzeGraphQLDocuments(docs) {
|
|
|
65
133
|
const operationNameToFile = /* @__PURE__ */ new Map();
|
|
66
134
|
const fragmentNameToFile = /* @__PURE__ */ new Map();
|
|
67
135
|
for (const doc of docs) {
|
|
68
|
-
const ast = parse(doc.content);
|
|
136
|
+
const ast = parse$1(doc.content);
|
|
69
137
|
const defs = [];
|
|
70
138
|
for (const def of ast.definitions) {
|
|
71
139
|
if (def.kind === Kind.FRAGMENT_DEFINITION) {
|
|
@@ -102,11 +170,26 @@ function analyzeGraphQLDocuments(docs) {
|
|
|
102
170
|
}
|
|
103
171
|
return { byFile, operationsByType };
|
|
104
172
|
}
|
|
105
|
-
function
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
173
|
+
function formatDefinitions(defs) {
|
|
174
|
+
if (defs.length === 0) return "";
|
|
175
|
+
const colorOf = (def) => {
|
|
176
|
+
if (def.kind === "fragment") return green;
|
|
177
|
+
switch (def.type) {
|
|
178
|
+
case "query":
|
|
179
|
+
return blue;
|
|
180
|
+
case "mutation":
|
|
181
|
+
return magenta;
|
|
182
|
+
case "subscription":
|
|
183
|
+
return yellow;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
return defs.map((def) => `${colorOf(def)}${def.name}${reset}`).join(`${dim} / ${reset}`);
|
|
187
|
+
}
|
|
188
|
+
function writeRegistryModule({ registryPath, operationsByType }) {
|
|
189
|
+
const queries = operationsByType.query.map((o) => o.name);
|
|
190
|
+
const mutations = operationsByType.mutation.map((o) => o.name);
|
|
191
|
+
const subscriptions = operationsByType.subscription.map((o) => o.name);
|
|
192
|
+
const content = [
|
|
110
193
|
`import type { TypedDocumentNode } from "@graphql-typed-document-node/core";`,
|
|
111
194
|
`import * as ops from "#graphql/operations";`,
|
|
112
195
|
``,
|
|
@@ -114,138 +197,146 @@ function generateRegistryByTypeSource(analysis) {
|
|
|
114
197
|
`type VariablesOf<T> = T extends { __apiType?: (variables: infer V) => infer _ } ? V : never;`
|
|
115
198
|
];
|
|
116
199
|
if (queries.length > 0) {
|
|
117
|
-
|
|
200
|
+
content.push(
|
|
118
201
|
``,
|
|
202
|
+
`// Queries`,
|
|
119
203
|
`export const queries = {`,
|
|
120
204
|
...queries.map((name) => ` ${name}: ops.${name}Document,`),
|
|
121
205
|
`} as const;`
|
|
122
206
|
);
|
|
123
207
|
} else {
|
|
124
|
-
|
|
208
|
+
content.push(``, `export const queries = {} as const;`);
|
|
125
209
|
}
|
|
126
|
-
|
|
210
|
+
content.push(
|
|
211
|
+
``,
|
|
127
212
|
`export type QueryName = keyof typeof queries;`,
|
|
128
213
|
`export type QueryResult<N extends QueryName> = ResultOf<(typeof queries)[N]>;`,
|
|
129
214
|
`export type QueryVariables<N extends QueryName> = VariablesOf<(typeof queries)[N]>;`
|
|
130
215
|
);
|
|
131
216
|
if (mutations.length > 0) {
|
|
132
|
-
|
|
217
|
+
content.push(
|
|
133
218
|
``,
|
|
219
|
+
`// Mutations`,
|
|
134
220
|
`export const mutations = {`,
|
|
135
221
|
...mutations.map((name) => ` ${name}: ops.${name}Document,`),
|
|
136
222
|
`} as const;`
|
|
137
223
|
);
|
|
138
224
|
} else {
|
|
139
|
-
|
|
225
|
+
content.push(``, `export const mutations = {} as const;`);
|
|
140
226
|
}
|
|
141
|
-
|
|
227
|
+
content.push(
|
|
228
|
+
``,
|
|
142
229
|
`export type MutationName = keyof typeof mutations;`,
|
|
143
230
|
`export type MutationResult<N extends MutationName> = ResultOf<(typeof mutations)[N]>;`,
|
|
144
231
|
`export type MutationVariables<N extends MutationName> = VariablesOf<(typeof mutations)[N]>;`
|
|
145
232
|
);
|
|
146
233
|
if (subscriptions.length > 0) {
|
|
147
|
-
|
|
234
|
+
content.push(
|
|
148
235
|
``,
|
|
236
|
+
`// Subscriptions`,
|
|
149
237
|
`export const subscriptions = {`,
|
|
150
238
|
...subscriptions.map((name) => ` ${name}: ops.${name}Document,`),
|
|
151
239
|
`} as const;`
|
|
152
240
|
);
|
|
153
241
|
} else {
|
|
154
|
-
|
|
242
|
+
content.push(``, `export const subscriptions = {} as const;`);
|
|
155
243
|
}
|
|
156
|
-
|
|
244
|
+
content.push(
|
|
245
|
+
``,
|
|
157
246
|
`export type SubscriptionName = keyof typeof subscriptions;`,
|
|
158
247
|
`export type SubscriptionResult<N extends SubscriptionName> = ResultOf<(typeof subscriptions)[N]>;`,
|
|
159
248
|
`export type SubscriptionVariables<N extends SubscriptionName> = VariablesOf<(typeof subscriptions)[N]>;`
|
|
160
249
|
);
|
|
161
|
-
return
|
|
250
|
+
return writeFileIfChanged(registryPath, content.join("\n") + "\n");
|
|
162
251
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
switch (def.type) {
|
|
168
|
-
case "query":
|
|
169
|
-
return blue;
|
|
170
|
-
case "mutation":
|
|
171
|
-
return magenta;
|
|
172
|
-
case "subscription":
|
|
173
|
-
return yellow;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
return defs.map((def) => `${colorOf(def)}${def.name}${reset}`).join(`${dim} / ${reset}`);
|
|
177
|
-
}
|
|
178
|
-
async function runCodegen(options) {
|
|
179
|
-
const { schema, documents, operationsFile, schemasFile, scalars, generates: customGenerates } = options;
|
|
180
|
-
if (documents.length === 0) {
|
|
181
|
-
logger.warn("No GraphQL documents found");
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
const zodScalars = {};
|
|
185
|
-
if (scalars) {
|
|
186
|
-
for (const [name, config] of Object.entries(scalars)) {
|
|
187
|
-
const inputType = typeof config === "string" ? config : config.input;
|
|
188
|
-
switch (inputType) {
|
|
189
|
-
case "Date":
|
|
190
|
-
zodScalars[name] = "z.coerce.date()";
|
|
191
|
-
break;
|
|
192
|
-
case "number":
|
|
193
|
-
zodScalars[name] = "z.coerce.number()";
|
|
194
|
-
break;
|
|
195
|
-
case "boolean":
|
|
196
|
-
zodScalars[name] = "z.coerce.boolean()";
|
|
197
|
-
break;
|
|
198
|
-
default:
|
|
199
|
-
zodScalars[name] = "z.string()";
|
|
200
|
-
}
|
|
201
|
-
}
|
|
252
|
+
|
|
253
|
+
function writeLocalSchemaModule({ localPath, modulePath }) {
|
|
254
|
+
if (!existsSync(localPath)) {
|
|
255
|
+
throw new Error(`Local schema file not found at path: ${localPath}`);
|
|
202
256
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
scalars
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
if (schemasFile) {
|
|
222
|
-
generates[schemasFile] = {
|
|
223
|
-
schema,
|
|
224
|
-
documents,
|
|
225
|
-
plugins: ["typescript-validation-schema"],
|
|
226
|
-
config: {
|
|
227
|
-
schema: "zodv4",
|
|
228
|
-
importFrom: "#graphql/operations",
|
|
229
|
-
useTypeImports: true,
|
|
230
|
-
directives: {
|
|
231
|
-
constraint: {
|
|
232
|
-
minLength: "min",
|
|
233
|
-
maxLength: "max",
|
|
234
|
-
pattern: "regex"
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
scalarSchemas: zodScalars
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
if (customGenerates) {
|
|
241
|
-
Object.assign(generates, customGenerates);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
await generate({ generates, silent: true, errorsOnly: true }, true);
|
|
245
|
-
logger.success(`Generated types for ${documents.length} document(s)`);
|
|
246
|
-
} catch (error) {
|
|
247
|
-
logger.error("GraphQL codegen failed:", error instanceof Error ? error.message : error);
|
|
257
|
+
const content = [
|
|
258
|
+
`export { schema } from ${JSON.stringify(toImportPath(modulePath, localPath))};`
|
|
259
|
+
].join("\n");
|
|
260
|
+
return writeFileIfChanged(modulePath, content);
|
|
261
|
+
}
|
|
262
|
+
async function writeRemoteSchemaSdl({ schemaDef: { url, headers }, sdlPath }) {
|
|
263
|
+
const response = await fetch(url, {
|
|
264
|
+
method: "POST",
|
|
265
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
266
|
+
body: JSON.stringify({ query: getIntrospectionQuery() })
|
|
267
|
+
});
|
|
268
|
+
const json = await response.json();
|
|
269
|
+
if (json.errors) {
|
|
270
|
+
throw new Error(`Failed to fetch remote schema from ${url}: ${JSON.stringify(json.errors)}`);
|
|
248
271
|
}
|
|
272
|
+
const schema = buildClientSchema(json.data);
|
|
273
|
+
const sdl = printSchema(schema);
|
|
274
|
+
const content = `export const sdl = /* GraphQL */ \`${sdl.replace(/`/g, "\\`")}\`;`;
|
|
275
|
+
return writeFileIfChanged(sdlPath, content);
|
|
276
|
+
}
|
|
277
|
+
function writeRemoteSchemaModule({ name, schemaDef: { url, headers }, sdlPath, modulePath, middlewarePath }) {
|
|
278
|
+
const headerSource = headers && Object.keys(headers).length > 0 ? JSON.stringify(headers, null, 2) : "{}";
|
|
279
|
+
const middlewareImport = middlewarePath ? `import middleware from ${JSON.stringify(toImportPath(modulePath, middlewarePath))};` : "";
|
|
280
|
+
const content = [
|
|
281
|
+
`import { buildSchema, print } from "graphql";`,
|
|
282
|
+
`import type { Executor } from "@graphql-tools/utils";`,
|
|
283
|
+
`import type { SubschemaConfig } from "@graphql-tools/delegate";`,
|
|
284
|
+
`import { sdl } from ${JSON.stringify(toImportPath(modulePath, sdlPath))};`,
|
|
285
|
+
middlewareImport,
|
|
286
|
+
``,
|
|
287
|
+
`const endpoint = ${JSON.stringify(url)};`,
|
|
288
|
+
`const headers = ${headerSource} as Record<string, string>;`,
|
|
289
|
+
`const remoteName = ${JSON.stringify(name)};`,
|
|
290
|
+
`const mw = (typeof middleware === 'object' && middleware) || {};`,
|
|
291
|
+
``,
|
|
292
|
+
`const executor: Executor = async ({ document, variables, context, operationName }) => {`,
|
|
293
|
+
` const query = typeof document === "string" ? document : print(document);`,
|
|
294
|
+
` let fetchOptions = {`,
|
|
295
|
+
` method: "POST",`,
|
|
296
|
+
` headers: { "Content-Type": "application/json", ...headers },`,
|
|
297
|
+
` body: JSON.stringify({ query, variables }),`,
|
|
298
|
+
` };`,
|
|
299
|
+
` const mwContext = { remoteName, operationName, context, fetchOptions };`,
|
|
300
|
+
` if (typeof mw.onRequest === "function") {`,
|
|
301
|
+
` const maybeOverride = await mw.onRequest(mwContext);`,
|
|
302
|
+
` if (maybeOverride && typeof maybeOverride === "object") {`,
|
|
303
|
+
` fetchOptions = maybeOverride;`,
|
|
304
|
+
` }`,
|
|
305
|
+
` }`,
|
|
306
|
+
` const response = await fetch(endpoint, fetchOptions);`,
|
|
307
|
+
` if (typeof mw.onResponse === "function") {`,
|
|
308
|
+
` await mw.onResponse({ ...mwContext, response });`,
|
|
309
|
+
` }`,
|
|
310
|
+
` return response.json();`,
|
|
311
|
+
`};`,
|
|
312
|
+
``,
|
|
313
|
+
`export const schema: SubschemaConfig = {`,
|
|
314
|
+
` schema: buildSchema(sdl),`,
|
|
315
|
+
` executor,`,
|
|
316
|
+
`};`,
|
|
317
|
+
``
|
|
318
|
+
].join("\n");
|
|
319
|
+
return writeFileIfChanged(modulePath, content);
|
|
320
|
+
}
|
|
321
|
+
function writeStitchedSchemaModule({ schemaNames, modulePath }) {
|
|
322
|
+
const schemas = schemaNames.map((name) => ({
|
|
323
|
+
path: `./schemas/${name}`,
|
|
324
|
+
ref: `${name}Schema`
|
|
325
|
+
}));
|
|
326
|
+
const content = [
|
|
327
|
+
`import { stitchSchemas } from "@graphql-tools/stitch";`,
|
|
328
|
+
`import type { GraphQLSchema } from "graphql";`,
|
|
329
|
+
`import type { SubschemaConfig } from "@graphql-tools/delegate";`,
|
|
330
|
+
...schemas.map(({ path, ref }) => `import { schema as ${ref} } from ${JSON.stringify(path)};`),
|
|
331
|
+
``,
|
|
332
|
+
`const subschemas: Array<GraphQLSchema | SubschemaConfig> = [`,
|
|
333
|
+
...schemas.map(({ ref }) => ` ${ref},`),
|
|
334
|
+
`];`,
|
|
335
|
+
``,
|
|
336
|
+
`export const schema = stitchSchemas({ subschemas });`,
|
|
337
|
+
``
|
|
338
|
+
].join("\n");
|
|
339
|
+
return writeFileIfChanged(modulePath, content);
|
|
249
340
|
}
|
|
250
341
|
|
|
251
342
|
const module$1 = defineNuxtModule({
|
|
@@ -254,102 +345,102 @@ const module$1 = defineNuxtModule({
|
|
|
254
345
|
configKey: "graphql"
|
|
255
346
|
},
|
|
256
347
|
defaults: {
|
|
348
|
+
schemas: {},
|
|
257
349
|
codegen: {
|
|
258
|
-
|
|
259
|
-
|
|
350
|
+
documents: "**/*.gql",
|
|
351
|
+
saveSchema: "server/graphql/schema.graphql"
|
|
352
|
+
},
|
|
353
|
+
client: {
|
|
354
|
+
headers: {},
|
|
355
|
+
cache: {
|
|
356
|
+
enabled: false,
|
|
357
|
+
ttl: 6e4,
|
|
358
|
+
storage: "memory"
|
|
359
|
+
}
|
|
260
360
|
}
|
|
261
361
|
},
|
|
262
362
|
async setup(options, nuxt) {
|
|
263
363
|
const { resolve } = createResolver(import.meta.url);
|
|
264
|
-
const {
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
364
|
+
const layerRootDirs = getLayerDirectories(nuxt).map(({ root }) => root);
|
|
365
|
+
const middlewarePath = resolve("./runtime/server/utils/remote-middleware");
|
|
366
|
+
const stitchedPath = join(nuxt.options.buildDir, "graphql/schema.ts");
|
|
367
|
+
const sdlPath = join(nuxt.options.rootDir, options.codegen?.saveSchema || ".nuxt/graphql/schema.graphql");
|
|
368
|
+
nuxt.options.alias ||= {};
|
|
369
|
+
async function setupContextSchemas() {
|
|
370
|
+
let contextPath;
|
|
371
|
+
if (options.context) {
|
|
372
|
+
contextPath = await findSingleFile(layerRootDirs, options.context, true);
|
|
373
|
+
logger.info(`Using GraphQL context from ${cyan}${relative(nuxt.options.rootDir, contextPath)}${reset}`);
|
|
374
|
+
} else {
|
|
375
|
+
contextPath = resolve("./runtime/server/lib/default-context.ts");
|
|
376
|
+
logger.info(`Using default GraphQL context`);
|
|
377
|
+
}
|
|
378
|
+
const schemasPath = {};
|
|
379
|
+
const middlewaresPath = {};
|
|
380
|
+
for (const [name, schemaDef] of Object.entries(options.schemas)) {
|
|
381
|
+
schemasPath[name] = join(nuxt.options.buildDir, `graphql/schemas/${name}.ts`);
|
|
382
|
+
if (schemaDef.type === "local") {
|
|
383
|
+
const localPath = await findSingleFile(layerRootDirs, schemaDef.path, true);
|
|
384
|
+
writeLocalSchemaModule({ localPath, modulePath: schemasPath[name] });
|
|
385
|
+
logger.info(`Local GraphQL schema "${blue}${name}${reset}" loaded from ${cyan}${relative(nuxt.options.rootDir, localPath)}${reset}`);
|
|
386
|
+
} else if (schemaDef.type === "remote") {
|
|
387
|
+
const sdlPath2 = join(nuxt.options.buildDir, `graphql/schemas/${name}-sdl.ts`);
|
|
388
|
+
if (schemaDef.middleware) {
|
|
389
|
+
middlewaresPath[name] = await findSingleFile(layerRootDirs, schemaDef.middleware, true);
|
|
390
|
+
}
|
|
391
|
+
await writeRemoteSchemaSdl({ schemaDef, sdlPath: sdlPath2 });
|
|
392
|
+
writeRemoteSchemaModule({ name, schemaDef, sdlPath: sdlPath2, modulePath: schemasPath[name], middlewarePath: middlewaresPath[name] });
|
|
393
|
+
logger.info(`Remote GraphQL schema "${magenta}${name}${reset}" loaded from ${cyan}${schemaDef.url}${reset}`);
|
|
394
|
+
} else {
|
|
395
|
+
throw new Error(`Unknown schema type for schema '${name}'`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
writeStitchedSchemaModule({ schemaNames: Object.keys(options.schemas), modulePath: stitchedPath });
|
|
284
399
|
nuxt.hook("nitro:config", (config) => {
|
|
285
400
|
config.alias ||= {};
|
|
286
|
-
config.alias["#graphql/schema"] = schemaPath;
|
|
287
401
|
config.alias["#graphql/context"] = contextPath;
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
nuxt.options.alias["#graphql/zod"] = zodSchemasFile;
|
|
292
|
-
};
|
|
293
|
-
const setupHandler = () => {
|
|
294
|
-
addServerHandler({ route: GRAPHQL_ENDPOINT, handler: resolve("./runtime/server/api/graphql-handler") });
|
|
295
|
-
nuxt.hook("listen", (_, { url }) => {
|
|
296
|
-
logger.success(`GraphQL Yoga ready at ${cyan}${url.replace(/\/$/, "")}${GRAPHQL_ENDPOINT}${reset}`);
|
|
297
|
-
});
|
|
298
|
-
};
|
|
299
|
-
const setupRuntimeConfig = () => {
|
|
300
|
-
nuxt.options.runtimeConfig.public.graphql = {
|
|
301
|
-
endpoint: GRAPHQL_ENDPOINT,
|
|
302
|
-
headers: options.headers || {},
|
|
303
|
-
cache: {
|
|
304
|
-
enabled: options.cache?.enabled ?? false,
|
|
305
|
-
ttl: options.cache?.ttl ?? 6e4,
|
|
306
|
-
storage: options.cache?.storage ?? "memory"
|
|
402
|
+
config.alias["#graphql/middleware"] = middlewarePath;
|
|
403
|
+
for (const name of Object.keys(options.schemas)) {
|
|
404
|
+
config.alias[`#graphql/schemas/${name}`] = schemasPath[name];
|
|
307
405
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const setupCodegen = () => {
|
|
311
|
-
const generate = async () => {
|
|
312
|
-
const [schema, documents] = await Promise.all([
|
|
313
|
-
loadGraphQLSchema(schemaPath),
|
|
314
|
-
findMultipleFiles(layerRootDirs, codegenPattern)
|
|
315
|
-
]);
|
|
316
|
-
const docs = documents.map((document) => ({ path: document, content: readFileSync(document, "utf-8") }));
|
|
317
|
-
const analysis = analyzeGraphQLDocuments(docs);
|
|
318
|
-
for (const doc of docs) {
|
|
319
|
-
const relativePath = doc.path.startsWith(rootDir) ? doc.path.slice(rootDir.length + 1) : doc.path;
|
|
320
|
-
const defs = analysis.byFile.get(doc.path) ?? [];
|
|
321
|
-
logger.info(`${cyan}${relativePath}${reset} [${formatDefinitions(defs)}]`);
|
|
322
|
-
}
|
|
323
|
-
await runCodegen({
|
|
324
|
-
schema,
|
|
325
|
-
documents,
|
|
326
|
-
operationsFile,
|
|
327
|
-
schemasFile: zodSchemasFile,
|
|
328
|
-
scalars: options.codegen?.scalars,
|
|
329
|
-
generates: options.codegen?.generates
|
|
330
|
-
});
|
|
331
|
-
if (writeFileIfChanged(schemaFile, schema)) {
|
|
332
|
-
logger.info(`GraphQL schema saved to ${cyan}${schemaOutput}${reset}`);
|
|
406
|
+
for (const name of Object.keys(middlewaresPath)) {
|
|
407
|
+
config.alias[`#graphql/middlewares/${name}`] = middlewaresPath[name];
|
|
333
408
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
409
|
+
config.alias["#graphql/schema"] = stitchedPath;
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
async function setupCodegen() {
|
|
413
|
+
const configPath = join(nuxt.options.rootDir, ".graphqlrc");
|
|
414
|
+
const operationsPath = nuxt.options.alias["#graphql/operations"] = join(nuxt.options.buildDir, "graphql/operations.ts");
|
|
415
|
+
const registryPath = nuxt.options.alias["#graphql/registry"] = join(nuxt.options.buildDir, "graphql/registry.ts");
|
|
416
|
+
const zodPath = nuxt.options.alias["#graphql/zod"] = join(nuxt.options.buildDir, "graphql/zod.ts");
|
|
417
|
+
async function generate() {
|
|
418
|
+
try {
|
|
419
|
+
const sdlContent = await loadSchemaSdl(stitchedPath);
|
|
420
|
+
writeFileIfChanged(sdlPath, sdlContent);
|
|
421
|
+
const documentsPattern = options.codegen?.documents ?? "**/*.gql";
|
|
422
|
+
const documents = await findMultipleFiles(layerRootDirs, documentsPattern);
|
|
423
|
+
await runCodegen({ schema: sdlPath, documents, operationsPath, zodPath, scalars: options.codegen?.scalars });
|
|
424
|
+
const { byFile, operationsByType } = analyzeDocuments(documents);
|
|
425
|
+
byFile.forEach((defs, path) => {
|
|
426
|
+
const relativePath = relative(nuxt.options.rootDir, path);
|
|
427
|
+
logger.info(`${cyan}${relativePath}${reset} [${formatDefinitions(defs)}]`);
|
|
428
|
+
});
|
|
429
|
+
writeRegistryModule({ registryPath, operationsByType });
|
|
430
|
+
const config = {
|
|
431
|
+
schema: relative(nuxt.options.rootDir, sdlPath),
|
|
432
|
+
documents: documentsPattern
|
|
433
|
+
};
|
|
434
|
+
writeFileIfChanged(configPath, JSON.stringify(config, null, 2));
|
|
435
|
+
} catch (error) {
|
|
436
|
+
logger.warn(`GraphQL codegen failed: ${error.message}`);
|
|
346
437
|
}
|
|
347
|
-
}
|
|
438
|
+
}
|
|
348
439
|
nuxt.hook("prepare:types", async ({ references }) => {
|
|
349
440
|
await generate();
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
441
|
+
references.push({ path: operationsPath });
|
|
442
|
+
references.push({ path: registryPath });
|
|
443
|
+
references.push({ path: zodPath });
|
|
353
444
|
});
|
|
354
445
|
if (nuxt.options.dev) {
|
|
355
446
|
nuxt.hook("builder:watch", async (event, path) => {
|
|
@@ -358,17 +449,31 @@ const module$1 = defineNuxtModule({
|
|
|
358
449
|
}
|
|
359
450
|
});
|
|
360
451
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
452
|
+
}
|
|
453
|
+
function setupYogaHandler() {
|
|
454
|
+
addServerHandler({ route: GRAPHQL_ENDPOINT, handler: resolve("./runtime/server/api/graphql-handler") });
|
|
455
|
+
nuxt.hook("listen", (_, { url }) => {
|
|
456
|
+
logger.success(`GraphQL Yoga ready at ${cyan}${url.replace(/\/$/, "")}${GRAPHQL_ENDPOINT}${reset}`);
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
function setupClient() {
|
|
460
|
+
nuxt.options.runtimeConfig.public.graphql = {
|
|
461
|
+
endpoint: GRAPHQL_ENDPOINT,
|
|
462
|
+
headers: options.client?.headers || {},
|
|
463
|
+
cache: {
|
|
464
|
+
enabled: options.client?.cache?.enabled ?? false,
|
|
465
|
+
ttl: options.client?.cache?.ttl ?? 6e4,
|
|
466
|
+
storage: options.client?.cache?.storage ?? "memory"
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
addPlugin(resolve("./runtime/app/plugins/graphql"));
|
|
363
470
|
addImportsDir(resolve("./runtime/app/composables"));
|
|
364
471
|
addServerImportsDir(resolve("./runtime/server/utils"));
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
setupCodegen();
|
|
371
|
-
setupAppRuntime();
|
|
472
|
+
}
|
|
473
|
+
await setupContextSchemas();
|
|
474
|
+
await setupCodegen();
|
|
475
|
+
setupYogaHandler();
|
|
476
|
+
setupClient();
|
|
372
477
|
}
|
|
373
478
|
});
|
|
374
479
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { type MutationName, type MutationResult
|
|
2
|
-
import type { IsEmptyObject } from "../../../helpers/is-empty-object.js";
|
|
1
|
+
import { type MutationName, type MutationResult } from "#graphql/registry";
|
|
3
2
|
/**
|
|
4
3
|
* Client-side GraphQL mutation composable
|
|
5
4
|
*
|
|
@@ -7,7 +6,7 @@ import type { IsEmptyObject } from "../../../helpers/is-empty-object.js";
|
|
|
7
6
|
* @returns Object with mutate function and pending state
|
|
8
7
|
*/
|
|
9
8
|
export declare function useGraphQLMutation<N extends MutationName>(operationName: N): {
|
|
10
|
-
mutate: (
|
|
9
|
+
mutate: (variables: MutationVariables<N>, headers?: HeadersInit | undefined) => Promise<{
|
|
11
10
|
data: MutationResult<N> | null;
|
|
12
11
|
error: Error | null;
|
|
13
12
|
}>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { defineEventHandler, toWebRequest, sendWebResponse, createError } from "h3";
|
|
2
|
-
import { logger } from "../lib/logger.js";
|
|
3
2
|
import { getYoga } from "../lib/create-yoga.js";
|
|
4
3
|
import { createContext } from "#graphql/context";
|
|
5
4
|
export default defineEventHandler(async (event) => {
|
|
@@ -10,7 +9,7 @@ export default defineEventHandler(async (event) => {
|
|
|
10
9
|
return sendWebResponse(event, response);
|
|
11
10
|
} catch (error) {
|
|
12
11
|
const message = error instanceof Error ? error.message : String(error);
|
|
13
|
-
|
|
12
|
+
console.error("GraphQL Server Error:", message);
|
|
14
13
|
throw createError({ statusCode: 500, message: "GraphQL server error" });
|
|
15
14
|
}
|
|
16
15
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { GraphQLContext } from "#graphql/context";
|
|
2
|
+
type Awaitable<T> = T | Promise<T>;
|
|
3
|
+
export type RemoteMiddlewareContext = {
|
|
4
|
+
remoteName: string;
|
|
5
|
+
operationName?: string | null;
|
|
6
|
+
context: GraphQLContext;
|
|
7
|
+
};
|
|
8
|
+
export type RemoteMiddlewareRequestContext = RemoteMiddlewareContext & {
|
|
9
|
+
fetchOptions: RequestInit;
|
|
10
|
+
};
|
|
11
|
+
export type RemoteMiddlewareResponseContext = RemoteMiddlewareRequestContext & {
|
|
12
|
+
response: Response;
|
|
13
|
+
};
|
|
14
|
+
export type RemoteMiddleware = {
|
|
15
|
+
onRequest?: (context: RemoteMiddlewareRequestContext) => Awaitable<RequestInit | undefined>;
|
|
16
|
+
onResponse?: (context: RemoteMiddlewareResponseContext) => Awaitable<void>;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
File without changes
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
-
import { type MutationName, type MutationResult
|
|
3
|
-
import type { IsEmptyObject } from "../../../helpers/is-empty-object.js";
|
|
2
|
+
import { type MutationName, type MutationResult } from "#graphql/registry";
|
|
4
3
|
/**
|
|
5
4
|
* Server-side GraphQL mutation composable
|
|
6
5
|
*
|
|
@@ -9,7 +8,7 @@ import type { IsEmptyObject } from "../../../helpers/is-empty-object.js";
|
|
|
9
8
|
* @returns Object with mutate function
|
|
10
9
|
*/
|
|
11
10
|
export declare function useServerGraphQLMutation<N extends MutationName>(event: H3Event, operationName: N): Promise<{
|
|
12
|
-
mutate: (
|
|
11
|
+
mutate: (variables: MutationVariables<N>, headers?: HeadersInit | undefined) => Promise<{
|
|
13
12
|
data: MutationResult<N> | null;
|
|
14
13
|
error: Error | null;
|
|
15
14
|
}>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewebsimple/nuxt-graphql",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Opinionated Nuxt module for using GraphQL",
|
|
5
5
|
"repository": "lewebsimple/nuxt-graphql",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"@graphql-codegen/typed-document-node": "^6.1.5",
|
|
40
40
|
"@graphql-codegen/typescript": "^5.0.7",
|
|
41
41
|
"@graphql-codegen/typescript-operations": "^5.0.7",
|
|
42
|
+
"@graphql-tools/delegate": "^12.0.2",
|
|
43
|
+
"@graphql-tools/stitch": "^10.1.6",
|
|
44
|
+
"@graphql-tools/utils": "^10.11.0",
|
|
45
|
+
"@graphql-tools/wrap": "^11.1.2",
|
|
42
46
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
43
47
|
"@nuxt/kit": "^4.2.2",
|
|
44
48
|
"graphql": "^16.12.0",
|
|
@@ -59,13 +63,13 @@
|
|
|
59
63
|
"@nuxt/schema": "^4.2.2",
|
|
60
64
|
"@nuxt/test-utils": "^3.22.0",
|
|
61
65
|
"@types/node": "latest",
|
|
62
|
-
"@vitest/coverage-v8": "^
|
|
66
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
63
67
|
"changelogen": "^0.6.2",
|
|
64
68
|
"eslint": "^9.39.2",
|
|
65
69
|
"nuxt": "^4.2.2",
|
|
66
70
|
"typescript": "~5.9.3",
|
|
67
|
-
"vitest": "^
|
|
68
|
-
"vue-tsc": "^3.2.
|
|
71
|
+
"vitest": "^3.2.4",
|
|
72
|
+
"vue-tsc": "^3.2.2"
|
|
69
73
|
},
|
|
70
74
|
"publishConfig": {
|
|
71
75
|
"access": "public"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./api/graphql-handler.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./api/graphql-handler.js";
|