nitro-graphql 2.0.0-beta.30 → 2.0.0-beta.32

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.
Files changed (42) hide show
  1. package/README.md +3 -3
  2. package/dist/codegen/client-types.d.mts +13 -0
  3. package/dist/codegen/client-types.mjs +176 -0
  4. package/dist/codegen/external-types.d.mts +12 -0
  5. package/dist/codegen/external-types.mjs +129 -0
  6. package/dist/codegen/index.d.mts +25 -0
  7. package/dist/codegen/index.mjs +38 -0
  8. package/dist/codegen/server-types.d.mts +13 -0
  9. package/dist/codegen/server-types.mjs +76 -0
  10. package/dist/codegen/validation.d.mts +13 -0
  11. package/dist/codegen/validation.mjs +96 -0
  12. package/dist/config/defaults.mjs +36 -0
  13. package/dist/constants.mjs +91 -0
  14. package/dist/define.d.mts +3 -3
  15. package/dist/define.mjs +3 -3
  16. package/dist/ecosystem/nuxt.mjs +3 -3
  17. package/dist/rollup.d.mts +7 -7
  18. package/dist/rollup.mjs +73 -73
  19. package/dist/routes/apollo-server.d.mts +2 -2
  20. package/dist/routes/debug.d.mts +2 -2
  21. package/dist/routes/graphql-yoga.d.mts +2 -2
  22. package/dist/routes/health.d.mts +2 -2
  23. package/dist/setup/file-watcher.mjs +80 -0
  24. package/dist/setup/rollup-integration.mjs +90 -0
  25. package/dist/setup/scaffold-generator.mjs +109 -0
  26. package/dist/setup/ts-config.mjs +69 -0
  27. package/dist/setup.d.mts +2 -2
  28. package/dist/setup.mjs +127 -290
  29. package/dist/types/index.d.mts +1 -1
  30. package/dist/utils/client-codegen.d.mts +1 -1
  31. package/dist/utils/client-codegen.mjs +5 -2
  32. package/dist/utils/directive-parser.d.mts +2 -1
  33. package/dist/utils/directive-parser.mjs +4 -2
  34. package/dist/utils/file-generator.mjs +1 -1
  35. package/dist/utils/index.d.mts +2 -2
  36. package/dist/utils/index.mjs +12 -11
  37. package/dist/utils/path-resolver.d.mts +2 -2
  38. package/dist/utils/path-resolver.mjs +2 -2
  39. package/dist/utils/server-codegen.mjs +4 -1
  40. package/dist/utils/type-generation.d.mts +6 -12
  41. package/dist/utils/type-generation.mjs +6 -419
  42. package/package.json +10 -4
package/README.md CHANGED
@@ -620,7 +620,7 @@ export default defineNitroConfig({
620
620
  > **⚠️ Breaking Change**: Utilities are **NOT auto-imported**. You must explicitly import them from `nitro-graphql/define`:
621
621
 
622
622
  ```typescript
623
- import { defineResolver, defineQuery, defineMutation, defineType, defineDirective } from 'nitro-graphql/define'
623
+ import { defineResolver, defineQuery, defineMutation, defineField, defineDirective } from 'nitro-graphql/define'
624
624
  ```
625
625
 
626
626
  | Function | Purpose | Example |
@@ -628,7 +628,7 @@ import { defineResolver, defineQuery, defineMutation, defineType, defineDirectiv
628
628
  | `defineResolver` | Complete resolvers | `defineResolver({ Query: {...}, Mutation: {...} })` |
629
629
  | `defineQuery` | Query-only resolvers | `defineQuery({ users: () => [...] })` |
630
630
  | `defineMutation` | Mutation-only resolvers | `defineMutation({ createUser: (...) => {...} })` |
631
- | `defineType` | Custom type resolvers | `defineType({ User: { posts: (parent) => [...] } })` |
631
+ | `defineField` | Custom type resolvers | `defineField({ User: { posts: (parent) => [...] } })` |
632
632
  | `defineDirective` | Custom directives | `defineDirective({ name: 'auth', ... })` |
633
633
  | `defineGraphQLConfig` | GraphQL server config | `defineGraphQLConfig({ maskedErrors: {...} })` |
634
634
  | `defineSchema` | Schema with Zod integration | `defineSchema({ Book: selectBookSchema })` |
@@ -777,7 +777,7 @@ This error occurs when the resolver scanner can't find the expected export in yo
777
777
  - `defineResolver` - Complete resolver with Query, Mutation, etc.
778
778
  - `defineQuery` - Query-only resolvers
779
779
  - `defineMutation` - Mutation-only resolvers
780
- - `defineType` - Custom type resolvers
780
+ - `defineField` - Custom type resolvers
781
781
  - `defineSubscription` - Subscription resolvers
782
782
  - `defineDirective` - Directive resolvers
783
783
  - Only exports using these functions are included in the virtual module
@@ -0,0 +1,13 @@
1
+ import { Nitro } from "nitro/types";
2
+
3
+ //#region src/codegen/client-types.d.ts
4
+
5
+ /**
6
+ * Generate main client types (for the default service)
7
+ */
8
+ declare function generateMainClientTypes(nitro: Nitro, options?: {
9
+ silent?: boolean;
10
+ isInitial?: boolean;
11
+ }): Promise<void>;
12
+ //#endregion
13
+ export { generateMainClientTypes };
@@ -0,0 +1,176 @@
1
+ import { FILE_INDEX_TS, LOG_TAG, SERVICE_DEFAULT } from "../constants.mjs";
2
+ import { generateClientTypes, loadGraphQLDocuments } from "../utils/client-codegen.mjs";
3
+ import { writeFileIfNotExists } from "../utils/file-generator.mjs";
4
+ import { getClientUtilsConfig, getDefaultPaths, getSdkConfig, getTypesConfig, resolveFilePath, shouldGenerateClientUtils } from "../utils/path-resolver.mjs";
5
+ import consola from "consola";
6
+ import { dirname, join, resolve } from "pathe";
7
+ import { buildSchema, parse } from "graphql";
8
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
+
10
+ //#region src/codegen/client-types.ts
11
+ const logger = consola.withTag(LOG_TAG);
12
+ /**
13
+ * Load federation support for client-side schema building
14
+ */
15
+ async function loadFederationSupport() {
16
+ try {
17
+ return (await import("@apollo/subgraph")).buildSubgraphSchema;
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+ /**
23
+ * Check for old structure files and warn user about manual migration
24
+ */
25
+ function warnLegacyGraphQLStructure(clientDir) {
26
+ const oldOfetchPath = resolve(clientDir, "ofetch.ts");
27
+ const oldSdkPath = resolve(clientDir, "sdk.ts");
28
+ if (existsSync(oldOfetchPath) || existsSync(oldSdkPath)) {
29
+ const foundFiles = [];
30
+ if (existsSync(oldOfetchPath)) foundFiles.push("app/graphql/ofetch.ts");
31
+ if (existsSync(oldSdkPath)) foundFiles.push("app/graphql/sdk.ts");
32
+ consola.error(`⚠️ OLD GRAPHQL STRUCTURE DETECTED!
33
+
34
+ 📁 Found old files in app/graphql/ directory that need to be moved:
35
+ • ${foundFiles.join("\n • ")}
36
+
37
+ 🔄 Please manually move these files to the new structure:
38
+ • app/graphql/ofetch.ts → app/graphql/default/ofetch.ts
39
+ • app/graphql/sdk.ts → app/graphql/default/sdk.ts
40
+
41
+ 📝 Also update your app/graphql/index.ts to include:
42
+ export * from './default/ofetch'
43
+
44
+ 💡 After moving, update your imports to use:
45
+ import { $sdk } from "#graphql/client"
46
+
47
+ 🚫 The old files will cause import conflicts until moved!`);
48
+ }
49
+ }
50
+ /**
51
+ * Generate main client types (for the default service)
52
+ */
53
+ async function generateMainClientTypes(nitro, options = {}) {
54
+ warnLegacyGraphQLStructure(nitro.graphql.clientDir);
55
+ const docs = nitro.scanDocuments;
56
+ const loadedDocs = await loadGraphQLDocuments(docs);
57
+ const schemaFilePath = join(nitro.graphql.buildDir, "schema.graphql");
58
+ if (!existsSync(schemaFilePath)) {
59
+ if (!options.silent) consola.info("Schema file not ready yet for client type generation. Server types need to be generated first.");
60
+ return;
61
+ }
62
+ const graphqlString = readFileSync(schemaFilePath, "utf-8");
63
+ const federationEnabled = nitro.options.graphql?.federation?.enabled === true;
64
+ let schema;
65
+ if (federationEnabled) {
66
+ const buildSubgraph = await loadFederationSupport();
67
+ if (!buildSubgraph) throw new Error("Federation is enabled but @apollo/subgraph is not installed. Run: pnpm add @apollo/subgraph");
68
+ schema = buildSubgraph([{ typeDefs: parse(graphqlString) }]);
69
+ } else schema = buildSchema(graphqlString);
70
+ const types = await generateClientTypes(schema, loadedDocs, nitro.options.graphql?.codegen?.client ?? {}, nitro.options.graphql?.codegen?.clientSDK ?? {}, void 0, void 0, void 0, options);
71
+ if (types === false) return;
72
+ const placeholders = getDefaultPaths(nitro);
73
+ const typesConfig = getTypesConfig(nitro);
74
+ const sdkConfig = getSdkConfig(nitro);
75
+ const clientTypesPath = resolveFilePath(typesConfig.client, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client.d.ts", placeholders);
76
+ if (clientTypesPath) {
77
+ mkdirSync(dirname(clientTypesPath), { recursive: true });
78
+ writeFileSync(clientTypesPath, types.types, "utf-8");
79
+ if (!options.silent) logger.success(`Generated client types at: ${clientTypesPath}`);
80
+ }
81
+ const sdkPath = resolveFilePath(sdkConfig.main, sdkConfig.enabled, true, "{clientGraphql}/default/sdk.ts", placeholders);
82
+ if (sdkPath) {
83
+ mkdirSync(dirname(sdkPath), { recursive: true });
84
+ writeFileSync(sdkPath, types.sdk, "utf-8");
85
+ if (!options.silent) logger.success(`Generated SDK at: ${sdkPath}`);
86
+ }
87
+ generateNuxtOfetchClient(nitro, nitro.graphql.clientDir, SERVICE_DEFAULT);
88
+ const externalServices = nitro.options.graphql?.externalServices || [];
89
+ if (externalServices.length > 0) generateGraphQLIndexFile(nitro, nitro.graphql.clientDir, externalServices);
90
+ }
91
+ /**
92
+ * Generate GraphQL index file that exports all services
93
+ */
94
+ function generateGraphQLIndexFile(nitro, clientDir, externalServices = []) {
95
+ if (!shouldGenerateClientUtils(nitro)) return;
96
+ const placeholders = getDefaultPaths(nitro);
97
+ const clientUtilsConfig = getClientUtilsConfig(nitro);
98
+ const indexPath = resolveFilePath(clientUtilsConfig.index, clientUtilsConfig.enabled, true, `{clientGraphql}/${FILE_INDEX_TS}`, placeholders);
99
+ if (!indexPath) return;
100
+ if (!existsSync(indexPath)) {
101
+ let indexContent = `// This file is auto-generated once by nitro-graphql for quick start
102
+ // You can modify this file according to your needs
103
+ //
104
+ // Export your main GraphQL service (auto-generated)
105
+ export * from './default/ofetch'
106
+
107
+ // Export external GraphQL services (auto-generated for existing services)
108
+ // When you add new external services, don't forget to add their exports here:
109
+ // export * from './yourServiceName/ofetch'
110
+ `;
111
+ for (const service of externalServices) indexContent += `export * from './${service.name}/ofetch'\n`;
112
+ writeFileIfNotExists(indexPath, indexContent, `client ${FILE_INDEX_TS}`);
113
+ }
114
+ }
115
+ /**
116
+ * Generate ofetch client wrapper for Nuxt/Nitro
117
+ */
118
+ function generateNuxtOfetchClient(nitro, clientDir, serviceName = SERVICE_DEFAULT) {
119
+ if (!shouldGenerateClientUtils(nitro)) return;
120
+ const placeholders = {
121
+ ...getDefaultPaths(nitro),
122
+ serviceName
123
+ };
124
+ const clientUtilsConfig = getClientUtilsConfig(nitro);
125
+ const ofetchPath = resolveFilePath(clientUtilsConfig.ofetch, clientUtilsConfig.enabled, true, "{clientGraphql}/{serviceName}/ofetch.ts", placeholders);
126
+ if (!ofetchPath) return;
127
+ const serviceDir = dirname(ofetchPath);
128
+ if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
129
+ if (existsSync(ofetchPath)) return;
130
+ writeFileIfNotExists(ofetchPath, nitro.options.framework?.name === "nuxt" ? `// This file is auto-generated once by nitro-graphql for quick start
131
+ // You can modify this file according to your needs
132
+ import type { Requester } from './sdk'
133
+ import { getSdk } from './sdk'
134
+
135
+ export function createGraphQLClient(endpoint: string): Requester {
136
+ return async <R>(doc: string, vars?: any): Promise<R> => {
137
+ const headers = import.meta.server ? useRequestHeaders() : undefined
138
+
139
+ const result = await $fetch(endpoint, {
140
+ method: 'POST',
141
+ body: { query: doc, variables: vars },
142
+ headers: {
143
+ 'Content-Type': 'application/json',
144
+ ...headers,
145
+ },
146
+ })
147
+
148
+ return result as R
149
+ }
150
+ }
151
+
152
+ export const $sdk = getSdk(createGraphQLClient('/api/graphql'))` : `// This file is auto-generated once by nitro-graphql for quick start
153
+ // You can modify this file according to your needs
154
+ import type { Requester } from './sdk'
155
+ import { ofetch } from 'ofetch'
156
+ import { getSdk } from './sdk'
157
+
158
+ export function createGraphQLClient(endpoint: string): Requester {
159
+ return async <R>(doc: string, vars?: any): Promise<R> => {
160
+ const result = await ofetch(endpoint, {
161
+ method: 'POST',
162
+ body: { query: doc, variables: vars },
163
+ headers: {
164
+ 'Content-Type': 'application/json',
165
+ },
166
+ })
167
+
168
+ return result as R
169
+ }
170
+ }
171
+
172
+ export const $sdk = getSdk(createGraphQLClient('/api/graphql'))`, `${serviceName} ofetch.ts`);
173
+ }
174
+
175
+ //#endregion
176
+ export { generateMainClientTypes };
@@ -0,0 +1,12 @@
1
+ import { Nitro } from "nitro/types";
2
+
3
+ //#region src/codegen/external-types.d.ts
4
+
5
+ /**
6
+ * Generate types for all external GraphQL services
7
+ */
8
+ declare function generateExternalServicesTypes(nitro: Nitro, options?: {
9
+ silent?: boolean;
10
+ }): Promise<void>;
11
+ //#endregion
12
+ export { generateExternalServicesTypes };
@@ -0,0 +1,129 @@
1
+ import { LOG_TAG } from "../constants.mjs";
2
+ import { downloadAndSaveSchema, generateExternalClientTypes, loadExternalSchema, loadGraphQLDocuments } from "../utils/client-codegen.mjs";
3
+ import { writeFileIfNotExists } from "../utils/file-generator.mjs";
4
+ import { getClientUtilsConfig, getDefaultPaths, getSdkConfig, getTypesConfig, resolveFilePath, shouldGenerateClientUtils } from "../utils/path-resolver.mjs";
5
+ import consola from "consola";
6
+ import { dirname } from "pathe";
7
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
8
+
9
+ //#region src/codegen/external-types.ts
10
+ consola.withTag(LOG_TAG);
11
+ /**
12
+ * Generate types for all external GraphQL services
13
+ */
14
+ async function generateExternalServicesTypes(nitro, options = {}) {
15
+ const externalServices = nitro.options.graphql?.externalServices || [];
16
+ for (const service of externalServices) try {
17
+ if (!options.silent) consola.info(`[graphql:${service.name}] Processing external service`);
18
+ await downloadAndSaveSchema(service, nitro.options.buildDir);
19
+ const schema = await loadExternalSchema(service, nitro.options.buildDir);
20
+ if (!schema) {
21
+ consola.warn(`[graphql:${service.name}] Failed to load schema, skipping`);
22
+ continue;
23
+ }
24
+ const documentPatterns = service.documents || [];
25
+ let loadedDocs = [];
26
+ if (documentPatterns.length > 0) try {
27
+ loadedDocs = await loadGraphQLDocuments(documentPatterns);
28
+ if (!loadedDocs || loadedDocs.length === 0) {
29
+ consola.warn(`[graphql:${service.name}] No GraphQL documents found, skipping service generation`);
30
+ continue;
31
+ }
32
+ } catch (error) {
33
+ consola.warn(`[graphql:${service.name}] No documents found, skipping service generation:`, error);
34
+ continue;
35
+ }
36
+ const types = await generateExternalClientTypes(service, schema, loadedDocs);
37
+ if (types === false) {
38
+ consola.warn(`[graphql:${service.name}] Type generation failed`);
39
+ continue;
40
+ }
41
+ const placeholders = {
42
+ ...getDefaultPaths(nitro),
43
+ serviceName: service.name
44
+ };
45
+ const typesConfig = getTypesConfig(nitro);
46
+ const sdkConfig = getSdkConfig(nitro);
47
+ const serviceTypesPath = resolveFilePath(service.paths?.types ?? typesConfig.external, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client-{serviceName}.d.ts", placeholders);
48
+ if (serviceTypesPath) {
49
+ mkdirSync(dirname(serviceTypesPath), { recursive: true });
50
+ writeFileSync(serviceTypesPath, types.types, "utf-8");
51
+ if (!options.silent) consola.success(`[graphql:${service.name}] Generated types at: ${serviceTypesPath}`);
52
+ }
53
+ const serviceSdkPath = resolveFilePath(service.paths?.sdk ?? sdkConfig.external, sdkConfig.enabled, true, "{clientGraphql}/{serviceName}/sdk.ts", placeholders);
54
+ if (serviceSdkPath) {
55
+ mkdirSync(dirname(serviceSdkPath), { recursive: true });
56
+ writeFileSync(serviceSdkPath, types.sdk, "utf-8");
57
+ if (!options.silent) consola.success(`[graphql:${service.name}] Generated SDK at: ${serviceSdkPath}`);
58
+ }
59
+ generateExternalOfetchClient(nitro, service, service.endpoint);
60
+ if (!options.silent) consola.success(`[graphql:${service.name}] External service types generated successfully`);
61
+ } catch (error) {
62
+ consola.error(`[graphql:${service.name}] External service generation failed:`, error);
63
+ }
64
+ }
65
+ /**
66
+ * Generate ofetch client for external service
67
+ */
68
+ function generateExternalOfetchClient(nitro, service, endpoint) {
69
+ if (!shouldGenerateClientUtils(nitro)) return;
70
+ const serviceName = service.name;
71
+ const placeholders = {
72
+ ...getDefaultPaths(nitro),
73
+ serviceName
74
+ };
75
+ const clientUtilsConfig = getClientUtilsConfig(nitro);
76
+ const ofetchPath = resolveFilePath(service.paths?.ofetch ?? clientUtilsConfig.ofetch, clientUtilsConfig.enabled, true, "{clientGraphql}/{serviceName}/ofetch.ts", placeholders);
77
+ if (!ofetchPath) return;
78
+ const serviceDir = dirname(ofetchPath);
79
+ if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
80
+ if (!existsSync(ofetchPath)) {
81
+ const capitalizedServiceName = serviceName.charAt(0).toUpperCase() + serviceName.slice(1);
82
+ writeFileIfNotExists(ofetchPath, nitro.options.framework?.name === "nuxt" ? `// This file is auto-generated once by nitro-graphql for quick start
83
+ // You can modify this file according to your needs
84
+ import type { Sdk, Requester } from './sdk'
85
+ import { getSdk } from './sdk'
86
+
87
+ export function create${capitalizedServiceName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
88
+ return async <R>(doc: string, vars?: any): Promise<R> => {
89
+ const headers = import.meta.server ? useRequestHeaders() : undefined
90
+
91
+ const result = await $fetch(endpoint, {
92
+ method: 'POST',
93
+ body: { query: doc, variables: vars },
94
+ headers: {
95
+ 'Content-Type': 'application/json',
96
+ ...headers,
97
+ },
98
+ })
99
+
100
+ return result as R
101
+ }
102
+ }
103
+
104
+ export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedServiceName}GraphQLClient())` : `// This file is auto-generated once by nitro-graphql for quick start
105
+ // You can modify this file according to your needs
106
+ import type { Sdk, Requester } from './sdk'
107
+ import { ofetch } from 'ofetch'
108
+ import { getSdk } from './sdk'
109
+
110
+ export function create${capitalizedServiceName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
111
+ return async <R>(doc: string, vars?: any): Promise<R> => {
112
+ const result = await ofetch(endpoint, {
113
+ method: 'POST',
114
+ body: { query: doc, variables: vars },
115
+ headers: {
116
+ 'Content-Type': 'application/json',
117
+ },
118
+ })
119
+
120
+ return result as R
121
+ }
122
+ }
123
+
124
+ export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedServiceName}GraphQLClient())`, `${serviceName} external ofetch.ts`);
125
+ }
126
+ }
127
+
128
+ //#endregion
129
+ export { generateExternalServicesTypes };
@@ -0,0 +1,25 @@
1
+ import { generateMainClientTypes } from "./client-types.mjs";
2
+ import { generateExternalServicesTypes } from "./external-types.mjs";
3
+ import { generateServerTypes } from "./server-types.mjs";
4
+ import { validateNoDuplicateTypes } from "./validation.mjs";
5
+ import { Nitro } from "nitro/types";
6
+
7
+ //#region src/codegen/index.d.ts
8
+
9
+ /**
10
+ * Generate server-side GraphQL resolver types
11
+ * @deprecated Use generateServerTypes instead
12
+ */
13
+ declare function serverTypeGeneration(nitro: Nitro, options?: {
14
+ silent?: boolean;
15
+ }): Promise<void>;
16
+ /**
17
+ * Generate client-side GraphQL types (main service + external services)
18
+ * @deprecated Use generateClientTypes instead
19
+ */
20
+ declare function clientTypeGeneration(nitro: Nitro, options?: {
21
+ silent?: boolean;
22
+ isInitial?: boolean;
23
+ }): Promise<void>;
24
+ //#endregion
25
+ export { clientTypeGeneration, serverTypeGeneration };
@@ -0,0 +1,38 @@
1
+ import { LOG_TAG } from "../constants.mjs";
2
+ import { generateMainClientTypes } from "./client-types.mjs";
3
+ import { generateExternalServicesTypes } from "./external-types.mjs";
4
+ import { validateNoDuplicateTypes } from "./validation.mjs";
5
+ import { generateServerTypes } from "./server-types.mjs";
6
+ import consola from "consola";
7
+
8
+ //#region src/codegen/index.ts
9
+ const logger = consola.withTag(LOG_TAG);
10
+ /**
11
+ * Generate server-side GraphQL resolver types
12
+ * @deprecated Use generateServerTypes instead
13
+ */
14
+ async function serverTypeGeneration(nitro, options = {}) {
15
+ return generateServerTypes(nitro, options);
16
+ }
17
+ /**
18
+ * Generate client-side GraphQL types (main service + external services)
19
+ * @deprecated Use generateClientTypes instead
20
+ */
21
+ async function clientTypeGeneration(nitro, options = {}) {
22
+ return generateClientTypes(nitro, options);
23
+ }
24
+ /**
25
+ * Generate client-side GraphQL types
26
+ * Generates types for both main service and external services
27
+ */
28
+ async function generateClientTypes(nitro, options = {}) {
29
+ try {
30
+ if (nitro.scanSchemas && nitro.scanSchemas.length > 0) await generateMainClientTypes(nitro, options);
31
+ if (nitro.options.graphql?.externalServices?.length) await generateExternalServicesTypes(nitro, options);
32
+ } catch (error) {
33
+ logger.error("Client schema generation error:", error);
34
+ }
35
+ }
36
+
37
+ //#endregion
38
+ export { clientTypeGeneration, serverTypeGeneration };
@@ -0,0 +1,13 @@
1
+ import { Nitro } from "nitro/types";
2
+
3
+ //#region src/codegen/server-types.d.ts
4
+
5
+ /**
6
+ * Generate server-side GraphQL types
7
+ * Creates resolver types from GraphQL schemas
8
+ */
9
+ declare function generateServerTypes(nitro: Nitro, options?: {
10
+ silent?: boolean;
11
+ }): Promise<void>;
12
+ //#endregion
13
+ export { generateServerTypes };
@@ -0,0 +1,76 @@
1
+ import { LOG_TAG } from "../constants.mjs";
2
+ import { getDefaultPaths, getTypesConfig, resolveFilePath, shouldGenerateTypes } from "../utils/path-resolver.mjs";
3
+ import { generateTypes } from "../utils/server-codegen.mjs";
4
+ import { validateNoDuplicateTypes } from "./validation.mjs";
5
+ import consola from "consola";
6
+ import { dirname, resolve } from "pathe";
7
+ import { buildSchema, parse } from "graphql";
8
+ import { mkdirSync, writeFileSync } from "node:fs";
9
+ import { printSchemaWithDirectives } from "@graphql-tools/utils";
10
+ import { loadFilesSync } from "@graphql-tools/load-files";
11
+ import { mergeTypeDefs } from "@graphql-tools/merge";
12
+
13
+ //#region src/codegen/server-types.ts
14
+ const logger = consola.withTag(LOG_TAG);
15
+ let buildSubgraphSchema = null;
16
+ /**
17
+ * Load Apollo Federation support if enabled
18
+ */
19
+ async function loadFederationSupport() {
20
+ if (buildSubgraphSchema !== null) return buildSubgraphSchema;
21
+ try {
22
+ buildSubgraphSchema = (await import("@apollo/subgraph")).buildSubgraphSchema;
23
+ } catch {
24
+ buildSubgraphSchema = false;
25
+ }
26
+ return buildSubgraphSchema;
27
+ }
28
+ /**
29
+ * Generate server-side GraphQL types
30
+ * Creates resolver types from GraphQL schemas
31
+ */
32
+ async function generateServerTypes(nitro, options = {}) {
33
+ try {
34
+ if (!shouldGenerateTypes(nitro)) {
35
+ logger.debug("Server type generation is disabled");
36
+ return;
37
+ }
38
+ const schemas = nitro.scanSchemas || [];
39
+ if (!schemas.length) {
40
+ if (!options.silent) consola.info("No GraphQL definitions found for server type generation.");
41
+ return;
42
+ }
43
+ const schemaStrings = loadFilesSync(schemas).map((schema$1) => typeof schema$1 === "string" ? schema$1 : schema$1.loc?.source?.body || "").filter(Boolean);
44
+ if (!validateNoDuplicateTypes(schemas, schemaStrings)) return;
45
+ const federationEnabled = nitro.options.graphql?.federation?.enabled === true;
46
+ const mergedSchemas = mergeTypeDefs([schemaStrings.join("\n\n")], {
47
+ throwOnConflict: true,
48
+ commentDescriptions: true,
49
+ sort: true
50
+ });
51
+ let schema;
52
+ if (federationEnabled) {
53
+ const buildSubgraph = await loadFederationSupport();
54
+ if (!buildSubgraph) throw new Error("Federation is enabled but @apollo/subgraph is not installed. Run: pnpm add @apollo/subgraph");
55
+ schema = buildSubgraph([{ typeDefs: parse(mergedSchemas) }]);
56
+ } else schema = buildSchema(mergedSchemas);
57
+ const data = await generateTypes(nitro.options.graphql?.framework || "graphql-yoga", schema, nitro.options.graphql ?? {});
58
+ const printSchema = printSchemaWithDirectives(schema);
59
+ const schemaPath = resolve(nitro.graphql.buildDir, "schema.graphql");
60
+ mkdirSync(dirname(schemaPath), { recursive: true });
61
+ writeFileSync(schemaPath, printSchema, "utf-8");
62
+ const placeholders = getDefaultPaths(nitro);
63
+ const typesConfig = getTypesConfig(nitro);
64
+ const serverTypesPath = resolveFilePath(typesConfig.server, typesConfig.enabled, true, "{typesDir}/nitro-graphql-server.d.ts", placeholders);
65
+ if (serverTypesPath) {
66
+ mkdirSync(dirname(serverTypesPath), { recursive: true });
67
+ writeFileSync(serverTypesPath, data, "utf-8");
68
+ if (!options.silent) logger.success(`Generated server types at: ${serverTypesPath}`);
69
+ }
70
+ } catch (error) {
71
+ logger.error("Server schema generation error:", error);
72
+ }
73
+ }
74
+
75
+ //#endregion
76
+ export { generateServerTypes };
@@ -0,0 +1,13 @@
1
+ //#region src/codegen/validation.d.ts
2
+ /**
3
+ * GraphQL schema validation utilities
4
+ * Validates schemas for duplicate type definitions and conflicts
5
+ */
6
+ /**
7
+ * Check for duplicate type definitions using a simpler approach
8
+ * Try to build each schema individually - if that succeeds but merging fails, we have duplicates
9
+ * @returns true if validation passes, false if duplicates found
10
+ */
11
+ declare function validateNoDuplicateTypes(schemas: string[], schemaStrings: string[]): boolean;
12
+ //#endregion
13
+ export { validateNoDuplicateTypes };
@@ -0,0 +1,96 @@
1
+ import { BUILTIN_SCALARS } from "../constants.mjs";
2
+ import consola from "consola";
3
+ import { basename } from "pathe";
4
+ import { parse } from "graphql";
5
+ import { mergeTypeDefs } from "@graphql-tools/merge";
6
+
7
+ //#region src/codegen/validation.ts
8
+ /**
9
+ * GraphQL schema validation utilities
10
+ * Validates schemas for duplicate type definitions and conflicts
11
+ */
12
+ /**
13
+ * Check for duplicate type definitions using a simpler approach
14
+ * Try to build each schema individually - if that succeeds but merging fails, we have duplicates
15
+ * @returns true if validation passes, false if duplicates found
16
+ */
17
+ function validateNoDuplicateTypes(schemas, schemaStrings) {
18
+ const individualSchemasByFile = /* @__PURE__ */ new Map();
19
+ schemaStrings.forEach((schemaContent, index) => {
20
+ const schemaPath = schemas[index];
21
+ const fileName = basename(schemaPath);
22
+ try {
23
+ parse(schemaContent);
24
+ individualSchemasByFile.set(fileName, schemaContent);
25
+ } catch (error) {
26
+ consola.warn(`Invalid GraphQL syntax in ${fileName}:`, error);
27
+ throw error;
28
+ }
29
+ });
30
+ try {
31
+ mergeTypeDefs([schemaStrings.join("\n\n")], {
32
+ throwOnConflict: false,
33
+ commentDescriptions: true,
34
+ sort: true
35
+ });
36
+ mergeTypeDefs([schemaStrings.join("\n\n")], {
37
+ throwOnConflict: true,
38
+ commentDescriptions: true,
39
+ sort: true
40
+ });
41
+ } catch (conflictError) {
42
+ if (conflictError?.message?.includes("already defined with a different type")) throw conflictError;
43
+ }
44
+ const typeNames = /* @__PURE__ */ new Set();
45
+ const duplicateTypes = [];
46
+ schemaStrings.forEach((schemaContent, index) => {
47
+ const fileName = basename(schemas[index]);
48
+ try {
49
+ parse(schemaContent).definitions.forEach((def) => {
50
+ if (def.kind === "ObjectTypeDefinition" || def.kind === "InterfaceTypeDefinition" || def.kind === "UnionTypeDefinition" || def.kind === "EnumTypeDefinition" || def.kind === "InputObjectTypeDefinition" || def.kind === "ScalarTypeDefinition") {
51
+ const typeName = def.name.value;
52
+ if (BUILTIN_SCALARS.includes(typeName)) return;
53
+ if (typeNames.has(typeName)) {
54
+ const existing = duplicateTypes.find((d) => d.type === typeName);
55
+ if (existing) existing.files.push(fileName);
56
+ else {
57
+ const firstFile = schemas.find((_, i) => {
58
+ const content = schemaStrings[i];
59
+ if (!content) return false;
60
+ try {
61
+ return parse(content).definitions.some((d) => (d.kind === "ObjectTypeDefinition" || d.kind === "InterfaceTypeDefinition" || d.kind === "UnionTypeDefinition" || d.kind === "EnumTypeDefinition" || d.kind === "InputObjectTypeDefinition" || d.kind === "ScalarTypeDefinition") && d.name.value === typeName);
62
+ } catch {
63
+ return false;
64
+ }
65
+ });
66
+ duplicateTypes.push({
67
+ type: typeName,
68
+ files: [basename(firstFile || ""), fileName]
69
+ });
70
+ }
71
+ } else typeNames.add(typeName);
72
+ }
73
+ });
74
+ } catch {}
75
+ });
76
+ if (duplicateTypes.length > 0) {
77
+ let errorMessage = "⚠️ DUPLICATE TYPE DEFINITIONS DETECTED!\n\n";
78
+ duplicateTypes.forEach(({ type, files }) => {
79
+ errorMessage += `❌ Type "${type}" is defined in multiple files:\n`;
80
+ files.forEach((fileName) => {
81
+ const fullPath = schemas.find((path) => basename(path) === fileName) || fileName;
82
+ errorMessage += ` • ${fullPath}\n`;
83
+ });
84
+ errorMessage += "\n";
85
+ });
86
+ errorMessage += "💡 Each GraphQL type should only be defined once.\n";
87
+ errorMessage += " Consider using \"extend type\" syntax instead of duplicate definitions.\n";
88
+ errorMessage += `\n🔍 Found ${duplicateTypes.length} duplicate type(s): ${duplicateTypes.map((d) => d.type).join(", ")}`;
89
+ consola.error(errorMessage);
90
+ return false;
91
+ }
92
+ return true;
93
+ }
94
+
95
+ //#endregion
96
+ export { validateNoDuplicateTypes };
@@ -0,0 +1,36 @@
1
+ import { DEFAULT_CLIENT_TYPES_PATH, DEFAULT_SERVER_TYPES_PATH, ENDPOINT_GRAPHQL, ENDPOINT_HEALTH } from "../constants.mjs";
2
+
3
+ //#region src/config/defaults.ts
4
+ /**
5
+ * Default type generation configuration
6
+ */
7
+ const DEFAULT_TYPES_CONFIG = {
8
+ server: DEFAULT_SERVER_TYPES_PATH,
9
+ client: DEFAULT_CLIENT_TYPES_PATH,
10
+ enabled: true
11
+ };
12
+ /**
13
+ * Default runtime GraphQL configuration
14
+ */
15
+ const DEFAULT_RUNTIME_CONFIG = {
16
+ endpoint: {
17
+ graphql: ENDPOINT_GRAPHQL,
18
+ healthCheck: ENDPOINT_HEALTH
19
+ },
20
+ playground: true
21
+ };
22
+ /**
23
+ * Default TypeScript strict mode setting
24
+ */
25
+ const DEFAULT_TYPESCRIPT_STRICT = true;
26
+ /**
27
+ * Default watcher persistence setting
28
+ */
29
+ const DEFAULT_WATCHER_PERSISTENT = true;
30
+ /**
31
+ * Default watcher ignore initial setting
32
+ */
33
+ const DEFAULT_WATCHER_IGNORE_INITIAL = true;
34
+
35
+ //#endregion
36
+ export { DEFAULT_RUNTIME_CONFIG, DEFAULT_TYPESCRIPT_STRICT, DEFAULT_TYPES_CONFIG, DEFAULT_WATCHER_IGNORE_INITIAL, DEFAULT_WATCHER_PERSISTENT };