nitro-graphql 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Silgi.js
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,476 @@
1
+ # Nitro GraphQL Yoga Module
2
+
3
+ > [!NOTE]
4
+ > This project is actively under development. We're always open to new ideas, different perspectives, and feature suggestions! If you have a suggestion, please first [open an issue](https://github.com/productdevbook/nitro-graphql/issues) to discuss it, then you can contribute with a PR.
5
+
6
+ A standalone Nitro module that integrates GraphQL Yoga server into any Nitro application with automatic type generation and file watching.
7
+
8
+ ## Features
9
+
10
+ - 🚀 Easy GraphQL server setup with GraphQL Yoga
11
+ - 🔧 Auto-discovery of GraphQL schema and resolver files
12
+ - 📝 Automatic TypeScript type generation from GraphQL schemas
13
+ - 🎮 Apollo Sandbox integration (instead of GraphiQL)
14
+ - 🏥 Built-in health check endpoint
15
+ - 💾 Configurable cache headers for better performance
16
+ - 🔌 Works with any Nitro-based application
17
+ - 🎯 Zero-config with sensible defaults
18
+ - 📂 File-based resolver organization
19
+ - 🔄 Hot reload in development mode
20
+ - 📦 Optimized bundle size with dynamic imports
21
+ - 🏷️ Minimal logging with consistent tagging
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install nitro-graphql
27
+ # or
28
+ pnpm add nitro-graphql
29
+ # or
30
+ yarn add nitro-graphql
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### 1. Add the module to your Nitro config
36
+
37
+ ```ts
38
+ // nitro.config.ts
39
+ import { defineNitroConfig } from 'nitropack/config'
40
+
41
+ export default defineNitroConfig({
42
+ modules: ['nitro-graphql'],
43
+
44
+ // Optional configuration
45
+ graphqlYoga: {
46
+ endpoint: '/api/graphql', // default
47
+ playground: true, // default (Apollo Sandbox)
48
+ cors: false, // default
49
+ cacheHeaders: {
50
+ enabled: true, // default
51
+ maxAge: 604800, // default (1 week)
52
+ },
53
+ client: {
54
+ enabled: false, // default
55
+ outputPath: undefined, // Will default to .nitro/types/graphql-client.generated.ts
56
+ watchPatterns: undefined, // Will default to src/**/*.{graphql,gql} excluding server/graphql
57
+ config: {
58
+ documentMode: 'string',
59
+ emitLegacyCommonJSImports: false,
60
+ useTypeImports: true,
61
+ enumsAsTypes: true,
62
+ }
63
+ }
64
+ }
65
+ })
66
+ ```
67
+
68
+ ### 2. Create your GraphQL schema files
69
+
70
+ The module automatically scans for GraphQL files in your `graphql/` directory using a domain-driven structure:
71
+
72
+ #### Main Schema File
73
+ ```graphql
74
+ # server/graphql/schema.graphql
75
+ scalar DateTime
76
+ scalar JSON
77
+
78
+ type Query {
79
+ hello: String!
80
+ greeting(name: String!): String!
81
+ }
82
+
83
+ type Mutation
84
+ ```
85
+
86
+ #### Domain-specific Schema Files
87
+ ```graphql
88
+ # server/graphql/users/user.graphql
89
+ type User {
90
+ id: ID!
91
+ name: String!
92
+ email: String!
93
+ createdAt: DateTime!
94
+ }
95
+
96
+ input CreateUserInput {
97
+ name: String!
98
+ email: String!
99
+ }
100
+
101
+ extend type Query {
102
+ users: [User!]!
103
+ user(id: ID!): User
104
+ }
105
+
106
+ extend type Mutation {
107
+ createUser(input: CreateUserInput!): User!
108
+ }
109
+ ```
110
+
111
+ #### Domain-based Resolver Files
112
+ ```ts
113
+ // server/graphql/users/user-queries.ts
114
+ import { createResolver } from 'nitro-graphql'
115
+
116
+ export default createResolver({
117
+ Query: {
118
+ users: async (_, __, { storage }) => {
119
+ return await storage.getItem('users') || []
120
+ },
121
+ user: async (_, { id }, { storage }) => {
122
+ const users = await storage.getItem('users') || []
123
+ return users.find(user => user.id === id)
124
+ }
125
+ }
126
+ })
127
+ ```
128
+
129
+ ```ts
130
+ // server/graphql/users/create-user.ts
131
+ import { createResolver } from 'nitro-graphql'
132
+
133
+ export default createResolver({
134
+ Mutation: {
135
+ createUser: async (_, { input }, { storage }) => {
136
+ const users = await storage.getItem('users') || []
137
+ const user = {
138
+ id: Date.now().toString(),
139
+ ...input,
140
+ createdAt: new Date()
141
+ }
142
+ users.push(user)
143
+ await storage.setItem('users', users)
144
+ return user
145
+ }
146
+ }
147
+ })
148
+ ```
149
+
150
+ ### 3. Using the utilities
151
+
152
+ The module provides utilities for better developer experience:
153
+
154
+ ```ts
155
+ // server/graphql/hello.ts
156
+ import { createResolver } from 'nitro-graphql'
157
+
158
+ export default createResolver({
159
+ Query: {
160
+ hello: () => 'Hello World!',
161
+ greeting: (_, { name }) => `Hello ${name}!`
162
+ }
163
+ })
164
+ ```
165
+
166
+ ### 4. Type Generation
167
+
168
+ The module automatically generates TypeScript types from your GraphQL schema:
169
+
170
+ - **Server types**: `.nitro/types/graphql-types.generated.ts`
171
+ - **Type declarations**: `.nitro/types/graphql.d.ts`
172
+
173
+ These types are automatically available in your resolvers:
174
+
175
+ ```ts
176
+ import type { QueryResolvers } from '#build/graphql-types.generated'
177
+ // server/graphql/users/user-queries.ts
178
+ import { createResolver } from 'nitro-graphql'
179
+
180
+ export default createResolver({
181
+ Query: {
182
+ users: async (_, __, { storage }): Promise<User[]> => {
183
+ return await storage.getItem('users') || []
184
+ }
185
+ } satisfies QueryResolvers
186
+ })
187
+ ```
188
+
189
+ ## File Structure
190
+
191
+ The module follows a domain-driven file structure:
192
+
193
+ ```
194
+ server/
195
+ ├── graphql/
196
+ │ ├── schema.graphql # Main schema file with scalars and base types
197
+ │ ├── hello.ts # Global resolvers
198
+ │ ├── users/
199
+ │ │ ├── user.graphql # User schema definitions
200
+ │ │ ├── user-queries.ts # User query resolvers
201
+ │ │ └── create-user.ts # User mutation resolvers
202
+ │ ├── todos/
203
+ │ │ ├── todo.graphql # Todo schema definitions
204
+ │ │ ├── todo-queries.ts # Todo query resolvers
205
+ │ │ └── todo-mutations.ts # Todo mutation resolvers
206
+ │ ├── posts/
207
+ │ │ ├── post.graphql # Post schema definitions
208
+ │ │ ├── post-queries.ts # Post query resolvers
209
+ │ │ └── create-post.ts # Post mutation resolvers
210
+ │ └── comments/
211
+ │ ├── comment.graphql # Comment schema definitions
212
+ │ ├── comment-queries.ts # Comment query resolvers
213
+ │ └── add-comment.ts # Comment mutation resolvers
214
+ ```
215
+
216
+ ## Context
217
+
218
+ The GraphQL context includes:
219
+ - `event`: The H3 event object
220
+ - `request`: The original request object
221
+ - `storage`: Nitro storage instance
222
+
223
+ ```ts
224
+ // Example resolver with full context usage
225
+ import { createResolver } from 'nitro-graphql'
226
+
227
+ export default createResolver({
228
+ Query: {
229
+ currentUser: async (_, __, { event, request, storage }) => {
230
+ const token = getCookie(event, 'auth-token')
231
+ const userAgent = getHeader(event, 'user-agent')
232
+
233
+ if (!token) {
234
+ throw new Error('Unauthorized')
235
+ }
236
+
237
+ const userId = await verifyToken(token)
238
+ return await storage.getItem(`user:${userId}`)
239
+ }
240
+ }
241
+ })
242
+ ```
243
+
244
+ ## Configuration Options
245
+
246
+ ```ts
247
+ interface NitroGraphQLYogaOptions {
248
+ // GraphQL endpoint path
249
+ endpoint?: string // default: '/api/graphql'
250
+
251
+ // Enable/disable Apollo Sandbox
252
+ playground?: boolean // default: true
253
+
254
+ // CORS configuration
255
+ cors?: boolean | {
256
+ origin?: string | string[] | boolean
257
+ credentials?: boolean
258
+ methods?: string[]
259
+ }
260
+
261
+ // Cache headers configuration
262
+ cacheHeaders?: {
263
+ enabled?: boolean // default: true
264
+ maxAge?: number // default: 604800 (1 week)
265
+ }
266
+
267
+ // Client type generation
268
+ client?: {
269
+ enabled?: boolean // default: false
270
+ outputPath?: string // default: buildDir/types/graphql-client.generated.ts
271
+ watchPatterns?: string[] // default: src/**/*.{graphql,gql} excluding server/graphql
272
+ config?: {
273
+ documentMode?: 'string' | 'graphQLTag'
274
+ emitLegacyCommonJSImports?: boolean
275
+ useTypeImports?: boolean
276
+ enumsAsTypes?: boolean
277
+ }
278
+ }
279
+ }
280
+ ```
281
+
282
+ ## Development Features
283
+
284
+ ### Hot Reload
285
+ The module watches for changes in your GraphQL files and automatically:
286
+ - Regenerates TypeScript types
287
+ - Reloads the GraphQL schema
288
+ - Updates resolvers
289
+
290
+ ### Bundle Optimization
291
+ - Uses dynamic imports to prevent bundling large codegen dependencies
292
+ - Optimized chunk organization for better caching
293
+ - Minimal logging output with consistent `[graphql]` tagging
294
+
295
+ ### Health Check
296
+ Access the health check endpoint at `/api/graphql/health` to verify your GraphQL server status.
297
+
298
+ ## Advanced Usage
299
+
300
+ ### Custom GraphQL Yoga Configuration
301
+
302
+ You can create a custom GraphQL Yoga configuration file that will be automatically merged with the default configuration. The module will look for this file **only in the `server/graphql/` directory**:
303
+
304
+ - `server/graphql/yoga.config.ts`
305
+
306
+ ```ts
307
+ import { useCORS } from '@graphql-yoga/plugin-cors'
308
+ import { useResponseCache } from '@graphql-yoga/plugin-response-cache'
309
+ // server/graphql/yoga.config.ts
310
+ import { defineYogaConfig } from 'nitro-graphql'
311
+
312
+ export default defineYogaConfig({
313
+ // Add custom plugins
314
+ plugins: [
315
+ useCORS({
316
+ origin: process.env.NODE_ENV === 'production' ? 'https://yourdomain.com' : '*',
317
+ credentials: true,
318
+ }),
319
+ useResponseCache({
320
+ session: () => null,
321
+ ttl: 60_000, // 1 minute
322
+ }),
323
+ ],
324
+
325
+ // Enhanced context with custom properties
326
+ context: async ({ request }) => {
327
+ const event = request.$$event
328
+
329
+ return {
330
+ event,
331
+ request,
332
+ storage: useStorage(),
333
+ // Add custom context properties
334
+ user: await authenticateUser(event),
335
+ db: await connectDatabase(),
336
+ startTime: Date.now(),
337
+ }
338
+ },
339
+
340
+ // Custom error handling
341
+ maskedErrors: {
342
+ maskError: (error, message, isDev) => {
343
+ if (error.message.startsWith('USER_')) {
344
+ return error // Don't mask user-facing errors
345
+ }
346
+ return isDev ? error : new Error('Internal server error')
347
+ },
348
+ },
349
+
350
+ // Additional yoga options
351
+ // See: https://the-guild.dev/graphql/yoga-server/docs
352
+ })
353
+ ```
354
+
355
+ **Configuration Merging**: Your custom config is merged with the default config, allowing you to override specific options while keeping defaults for others. The `schema` and `graphqlEndpoint` are always preserved from the module's configuration.
356
+
357
+ ### Client Type Generation
358
+
359
+ Enable client type generation for your frontend queries:
360
+
361
+ ```ts
362
+ // nitro.config.ts
363
+ export default defineNitroConfig({
364
+ graphqlYoga: {
365
+ client: {
366
+ enabled: true,
367
+ watchPatterns: [
368
+ 'client/**/*.graphql',
369
+ 'pages/**/*.vue',
370
+ 'components/**/*.vue'
371
+ ]
372
+ }
373
+ }
374
+ })
375
+ ```
376
+
377
+ ### Custom Scalars
378
+
379
+ ```ts
380
+ import { GraphQLScalarType } from 'graphql'
381
+ import { Kind } from 'graphql/language'
382
+ // server/graphql/scalars/DateTime.ts
383
+ import { createResolver } from 'nitro-graphql'
384
+
385
+ export default createResolver({
386
+ DateTime: new GraphQLScalarType({
387
+ name: 'DateTime',
388
+ serialize: (value: Date) => value.toISOString(),
389
+ parseValue: (value: string) => new Date(value),
390
+ parseLiteral: (ast) => {
391
+ if (ast.kind === Kind.STRING) {
392
+ return new Date(ast.value)
393
+ }
394
+ return null
395
+ }
396
+ })
397
+ })
398
+ ```
399
+
400
+ ### Error Handling
401
+
402
+ ```ts
403
+ // server/graphql/users/user-queries.ts
404
+ import { createResolver } from 'nitro-graphql'
405
+
406
+ export default createResolver({
407
+ Query: {
408
+ user: async (_, { id }, { storage }) => {
409
+ try {
410
+ const user = await storage.getItem(`user:${id}`)
411
+ if (!user) {
412
+ throw new Error(`User with id ${id} not found`)
413
+ }
414
+ return user
415
+ }
416
+ catch (error) {
417
+ console.error('[graphql] Error fetching user:', error)
418
+ throw error
419
+ }
420
+ }
421
+ }
422
+ })
423
+ ```
424
+
425
+ ## Performance
426
+
427
+ ### Bundle Size
428
+ The module is optimized for minimal bundle size:
429
+ - Development dependencies are excluded from production builds
430
+ - Uses Function constructor for dynamic imports to prevent bundling
431
+ - Efficient chunk organization
432
+
433
+ ### Caching
434
+ - Built-in cache headers for Apollo Sandbox (1 week)
435
+ - Configurable cache settings
436
+ - Lazy schema initialization
437
+
438
+ ## Community & Contributing
439
+
440
+ > [!TIP]
441
+ > **Want to contribute?** We believe you can play a role in the growth of this project!
442
+
443
+ ### 🎯 How You Can Contribute
444
+
445
+ - **💡 Share your ideas**: Use [GitHub Issues](https://github.com/productdevbook/nitro-graphql/issues) for new feature suggestions
446
+ - **🐛 Report bugs**: Report issues you encounter in detail
447
+ - **📖 Improve documentation**: Enhance README, examples, and guides
448
+ - **🔧 Code contributions**: Develop bug fixes and new features
449
+ - **🌟 Support the project**: Support the project by giving it a star
450
+
451
+ ### 💬 Discussion and Support
452
+
453
+ - **GitHub Issues**: Feature requests and bug reports
454
+ - **GitHub Discussions**: General discussions and questions
455
+ - **Pull Requests**: Code contributions
456
+
457
+ ### 🚀 Contribution Process
458
+
459
+ 1. **Open an issue**: Let's discuss what you want to do first
460
+ 2. **Fork & Branch**: Fork the project and create a feature branch
461
+ 3. **Write code**: Develop according to existing code standards
462
+ 4. **Test**: Test your changes
463
+ 5. **Send PR**: Create a pull request with detailed description
464
+
465
+ > [!IMPORTANT]
466
+ > Please don't forget to read the [Contribution Guidelines](CONTRIBUTING.md) document before contributing.
467
+
468
+ ---
469
+
470
+ ### 🌟 Thank You
471
+
472
+ Thank you for using and developing this project. Every contribution makes the GraphQL ecosystem stronger!
473
+
474
+ ## License
475
+
476
+ MIT
@@ -0,0 +1,86 @@
1
+ import { consola } from "consola";
2
+ import { codegen } from "@graphql-codegen/core";
3
+ import { plugin } from "@graphql-codegen/typescript";
4
+ import { printSchemaWithDirectives } from "@graphql-tools/utils";
5
+ import { defu } from "defu";
6
+ import { parse } from "graphql";
7
+ import { plugin as plugin$1 } from "@graphql-codegen/typescript-generic-sdk";
8
+ import { plugin as plugin$2 } from "@graphql-codegen/typescript-operations";
9
+ import { GraphQLFileLoader } from "@graphql-tools/graphql-file-loader";
10
+ import { loadDocuments } from "@graphql-tools/load";
11
+
12
+ //#region src/client-codegen.ts
13
+ function pluginContent(_schema, _documents, _config, _info) {
14
+ return {
15
+ prepend: [
16
+ "// THIS FILE IS GENERATED, DO NOT EDIT!",
17
+ "/* eslint-disable eslint-comments/no-unlimited-disable */",
18
+ "/* tslint:disable */",
19
+ "/* eslint-disable */",
20
+ "/* prettier-ignore */"
21
+ ],
22
+ content: ""
23
+ };
24
+ }
25
+ async function loadGraphQLDocuments(patterns) {
26
+ try {
27
+ const result = await loadDocuments(patterns, { loaders: [new GraphQLFileLoader()] });
28
+ return result;
29
+ } catch (e) {
30
+ if ((e.message || "").includes("Unable to find any GraphQL type definitions for the following pointers:")) return [];
31
+ else throw e;
32
+ }
33
+ }
34
+ async function generateClientTypes(schema, patterns, config = {}, outputPath) {
35
+ const docs = await loadGraphQLDocuments(patterns);
36
+ if (docs.length === 0) {
37
+ consola.info("[graphql] No client GraphQL files found. Skipping client type generation.");
38
+ return "";
39
+ }
40
+ consola.info(`[graphql] Found ${docs.length} client GraphQL documents`);
41
+ const defaultConfig = {
42
+ documentMode: "string",
43
+ emitLegacyCommonJSImports: false,
44
+ useTypeImports: true,
45
+ enumsAsTypes: true,
46
+ strictScalars: true,
47
+ maybeValue: "T | null | undefined",
48
+ inputMaybeValue: "T | undefined",
49
+ scalars: {
50
+ DateTime: "string",
51
+ JSON: "any",
52
+ UUID: "string",
53
+ NonEmptyString: "string",
54
+ Currency: "string"
55
+ }
56
+ };
57
+ const mergedConfig = defu(config, defaultConfig);
58
+ try {
59
+ const output = await codegen({
60
+ filename: outputPath || "client-types.generated.ts",
61
+ schema: parse(printSchemaWithDirectives(schema)),
62
+ documents: [...docs],
63
+ config: mergedConfig,
64
+ plugins: [
65
+ { pluginContent: {} },
66
+ { typescript: {} },
67
+ { typescriptOperations: {} },
68
+ { typescriptGenericSdk: { rawRequest: false } }
69
+ ],
70
+ pluginMap: {
71
+ pluginContent: { plugin: pluginContent },
72
+ typescript: { plugin },
73
+ typescriptOperations: { plugin: plugin$2 },
74
+ typescriptGenericSdk: { plugin: plugin$1 }
75
+ }
76
+ });
77
+ return output;
78
+ } catch (error) {
79
+ consola.warn("[graphql] Client type generation failed:", error);
80
+ return "";
81
+ }
82
+ }
83
+
84
+ //#endregion
85
+ export { generateClientTypes };
86
+ //# sourceMappingURL=client-codegen-DM2n5Gt2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-codegen-DM2n5Gt2.js","names":["_schema: any","_documents: any","_config: any","_info: any","patterns: string | string[]","e: any","schema: GraphQLSchema","config: CodegenClientConfig","outputPath?: string","defaultConfig: CodegenClientConfig","typescriptPlugin","typescriptOperations","typescriptGenericSdk"],"sources":["../src/client-codegen.ts"],"sourcesContent":["import type { GraphQLSchema } from 'graphql'\nimport type { CodegenClientConfig } from './types'\nimport { codegen } from '@graphql-codegen/core'\nimport { plugin as typescriptPlugin } from '@graphql-codegen/typescript'\nimport { plugin as typescriptGenericSdk } from '@graphql-codegen/typescript-generic-sdk'\nimport { plugin as typescriptOperations } from '@graphql-codegen/typescript-operations'\nimport { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'\nimport { loadDocuments } from '@graphql-tools/load'\nimport { printSchemaWithDirectives } from '@graphql-tools/utils'\nimport { consola } from 'consola'\nimport { defu } from 'defu'\nimport { parse } from 'graphql'\n\nfunction pluginContent(_schema: any, _documents: any, _config: any, _info: any) {\n return {\n prepend: [\n '// THIS FILE IS GENERATED, DO NOT EDIT!',\n '/* eslint-disable eslint-comments/no-unlimited-disable */',\n '/* tslint:disable */',\n '/* eslint-disable */',\n '/* prettier-ignore */',\n ],\n content: '',\n }\n}\n\nasync function loadGraphQLDocuments(patterns: string | string[]) {\n try {\n const result = await loadDocuments(patterns, {\n loaders: [new GraphQLFileLoader()],\n })\n return result\n }\n catch (e: any) {\n if (\n (e.message || '').includes(\n 'Unable to find any GraphQL type definitions for the following pointers:',\n )\n ) {\n // No GraphQL files found - this is normal\n return []\n }\n else {\n // Re-throw other errors\n throw e\n }\n }\n}\n\nexport async function generateClientTypes(\n schema: GraphQLSchema,\n patterns: string | string[],\n config: CodegenClientConfig = {},\n outputPath?: string,\n) {\n const docs = await loadGraphQLDocuments(patterns)\n\n if (docs.length === 0) {\n consola.info('[graphql] No client GraphQL files found. Skipping client type generation.')\n return ''\n }\n\n consola.info(`[graphql] Found ${docs.length} client GraphQL documents`)\n\n const defaultConfig: CodegenClientConfig = {\n documentMode: 'string',\n emitLegacyCommonJSImports: false,\n useTypeImports: true,\n enumsAsTypes: true,\n strictScalars: true,\n maybeValue: 'T | null | undefined',\n inputMaybeValue: 'T | undefined',\n scalars: {\n DateTime: 'string',\n JSON: 'any',\n UUID: 'string',\n NonEmptyString: 'string',\n Currency: 'string',\n },\n }\n\n const mergedConfig = defu(config, defaultConfig)\n\n try {\n const output = await codegen({\n filename: outputPath || 'client-types.generated.ts',\n schema: parse(printSchemaWithDirectives(schema)),\n documents: [...docs],\n config: mergedConfig,\n plugins: [\n { pluginContent: {} },\n { typescript: {} },\n { typescriptOperations: {} },\n { typescriptGenericSdk: { rawRequest: false } },\n ],\n pluginMap: {\n pluginContent: { plugin: pluginContent },\n typescript: { plugin: typescriptPlugin },\n typescriptOperations: { plugin: typescriptOperations },\n typescriptGenericSdk: { plugin: typescriptGenericSdk },\n },\n })\n\n return output\n }\n catch (error) {\n consola.warn('[graphql] Client type generation failed:', error)\n return ''\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAS,cAAcA,SAAcC,YAAiBC,SAAcC,OAAY;AAC9E,QAAO;EACL,SAAS;GACP;GACA;GACA;GACA;GACA;EACD;EACD,SAAS;CACV;AACF;AAED,eAAe,qBAAqBC,UAA6B;AAC/D,KAAI;EACF,MAAM,SAAS,MAAM,cAAc,UAAU,EAC3C,SAAS,CAAC,IAAI,mBAAoB,EACnC,EAAC;AACF,SAAO;CACR,SACMC,GAAQ;AACb,MACE,CAAC,EAAE,WAAW,IAAI,SAChB,0EACD,CAGD,QAAO,CAAE;MAIT,OAAM;CAET;AACF;AAED,eAAsB,oBACpBC,QACAF,UACAG,SAA8B,CAAE,GAChCC,YACA;CACA,MAAM,OAAO,MAAM,qBAAqB,SAAS;AAEjD,KAAI,KAAK,WAAW,GAAG;AACrB,UAAQ,KAAK,4EAA4E;AACzF,SAAO;CACR;AAED,SAAQ,KAAK,CAAC,gBAAgB,EAAE,KAAK,OAAO,yBAAyB,CAAC,CAAC;CAEvE,MAAMC,gBAAqC;EACzC,cAAc;EACd,2BAA2B;EAC3B,gBAAgB;EAChB,cAAc;EACd,eAAe;EACf,YAAY;EACZ,iBAAiB;EACjB,SAAS;GACP,UAAU;GACV,MAAM;GACN,MAAM;GACN,gBAAgB;GAChB,UAAU;EACX;CACF;CAED,MAAM,eAAe,KAAK,QAAQ,cAAc;AAEhD,KAAI;EACF,MAAM,SAAS,MAAM,QAAQ;GAC3B,UAAU,cAAc;GACxB,QAAQ,MAAM,0BAA0B,OAAO,CAAC;GAChD,WAAW,CAAC,GAAG,IAAK;GACpB,QAAQ;GACR,SAAS;IACP,EAAE,eAAe,CAAE,EAAE;IACrB,EAAE,YAAY,CAAE,EAAE;IAClB,EAAE,sBAAsB,CAAE,EAAE;IAC5B,EAAE,sBAAsB,EAAE,YAAY,MAAO,EAAE;GAChD;GACD,WAAW;IACT,eAAe,EAAE,QAAQ,cAAe;IACxC,YAAY,EAAUC,OAAkB;IACxC,sBAAsB,EAAE,QAAQC,SAAsB;IACtD,sBAAsB,EAAE,QAAQC,SAAsB;GACvD;EACF,EAAC;AAEF,SAAO;CACR,SACM,OAAO;AACZ,UAAQ,KAAK,4CAA4C,MAAM;AAC/D,SAAO;CACR;AACF"}
@@ -0,0 +1,10 @@
1
+ import { NitroGraphQLOptions } from "./types-D_NqyCcy.js";
2
+ import { Nitro } from "nitropack/types";
3
+
4
+ //#region src/client-watcher.d.ts
5
+ declare function setupClientWatcher(nitro: Nitro, options: NitroGraphQLOptions): Promise<void>;
6
+ //# sourceMappingURL=client-watcher.d.ts.map
7
+
8
+ //#endregion
9
+ export { setupClientWatcher };
10
+ //# sourceMappingURL=client-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-watcher.d.ts","names":[],"sources":["../src/client-watcher.ts"],"sourcesContent":[],"mappings":";;;;iBAmEsB,kBAAA,QAA0B,gBAAgB,sBAAmB"}
@@ -0,0 +1,92 @@
1
+ import { scanGraphQLFiles } from "./scanner-BdcKEPQk.js";
2
+ import { debounce } from "./utils-87_22aIA.js";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { mergeTypeDefs } from "@graphql-tools/merge";
5
+ import { makeExecutableSchema } from "@graphql-tools/schema";
6
+ import { consola } from "consola";
7
+ import { join } from "pathe";
8
+
9
+ //#region src/client-watcher.ts
10
+ const logger = consola.withTag("graphql");
11
+ async function regenerateClientTypes(nitro, options) {
12
+ try {
13
+ if (!options.client?.enabled) return;
14
+ const scanResult = await scanGraphQLFiles(nitro);
15
+ if (scanResult.typeDefs.length === 0) {
16
+ logger.warn("⚠️ No server schema found for client type generation");
17
+ return;
18
+ }
19
+ const mergedTypeDefs = mergeTypeDefs(scanResult.typeDefs);
20
+ const schema = makeExecutableSchema({
21
+ typeDefs: mergedTypeDefs,
22
+ resolvers: {}
23
+ });
24
+ const clientPatterns = options.client.watchPatterns || [
25
+ join(nitro.options.srcDir, "**/*.graphql"),
26
+ join(nitro.options.srcDir, "**/*.gql"),
27
+ `!${join(nitro.options.srcDir, "graphql/**/*")}`
28
+ ];
29
+ const { generateClientTypes } = await import("./client-codegen-DM2n5Gt2.js");
30
+ const generatedTypes = await generateClientTypes(schema, clientPatterns, options.client.config, options.client.outputPath);
31
+ if (generatedTypes) {
32
+ const outputPath = options.client.outputPath || join(nitro.options.buildDir, "types", "graphql-client.generated.ts");
33
+ const typesDir = join(nitro.options.buildDir, "types");
34
+ await mkdir(typesDir, { recursive: true });
35
+ await writeFile(outputPath, generatedTypes);
36
+ logger.success("✨ Client types updated");
37
+ }
38
+ } catch (error) {
39
+ const errorMessage = error instanceof Error ? error.message : String(error);
40
+ logger.error("❌ Client type generation failed:", errorMessage);
41
+ }
42
+ }
43
+ async function setupClientWatcher(nitro, options) {
44
+ if (!options.client?.enabled) {
45
+ logger.info("🚫 Client type generation disabled");
46
+ return;
47
+ }
48
+ const clientPatterns = options.client.watchPatterns || [join(nitro.options.srcDir, "**/*.graphql"), join(nitro.options.srcDir, "**/*.gql")];
49
+ const generateClientTypesDebounced = debounce(async () => {
50
+ await regenerateClientTypes(nitro, options);
51
+ }, 300);
52
+ const { watch } = await import("chokidar");
53
+ const { globby } = await import("globby");
54
+ const existingClientFiles = await globby(clientPatterns, {
55
+ absolute: true,
56
+ ignore: [join(nitro.options.srcDir, "graphql/**/*")]
57
+ });
58
+ const watchPatterns = existingClientFiles.length > 0 ? existingClientFiles : clientPatterns;
59
+ const watcher = watch(watchPatterns, {
60
+ persistent: true,
61
+ ignoreInitial: true,
62
+ ignored: /(^|[/\\])\../,
63
+ followSymlinks: false,
64
+ depth: 10,
65
+ usePolling: true,
66
+ interval: 1e3,
67
+ binaryInterval: 1e3
68
+ });
69
+ watcher.on("change", (_path) => {
70
+ generateClientTypesDebounced();
71
+ });
72
+ watcher.on("add", (_path) => {
73
+ generateClientTypesDebounced();
74
+ });
75
+ watcher.on("unlink", (_path) => {
76
+ generateClientTypesDebounced();
77
+ });
78
+ watcher.on("error", (error) => {
79
+ const errorMessage = error instanceof Error ? error.message : String(error);
80
+ logger.error("❌ Client watcher error:", errorMessage);
81
+ });
82
+ nitro.hooks.hook("close", () => {
83
+ logger.info("🔒 Closing client watcher");
84
+ watcher.close();
85
+ });
86
+ await generateClientTypesDebounced();
87
+ logger.success("✅ Client watcher ready");
88
+ }
89
+
90
+ //#endregion
91
+ export { setupClientWatcher };
92
+ //# sourceMappingURL=client-watcher.js.map