@constructive-io/graphql-codegen 2.20.1 → 2.22.0

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 (96) hide show
  1. package/README.md +15 -3
  2. package/cli/codegen/barrel.d.ts +4 -1
  3. package/cli/codegen/barrel.js +18 -12
  4. package/cli/codegen/client.js +33 -0
  5. package/cli/codegen/custom-mutations.d.ts +11 -1
  6. package/cli/codegen/custom-mutations.js +49 -15
  7. package/cli/codegen/custom-queries.d.ts +8 -0
  8. package/cli/codegen/custom-queries.js +82 -47
  9. package/cli/codegen/gql-ast.js +9 -5
  10. package/cli/codegen/index.js +39 -8
  11. package/cli/codegen/mutations.d.ts +14 -4
  12. package/cli/codegen/mutations.js +114 -28
  13. package/cli/codegen/orm/barrel.js +4 -2
  14. package/cli/codegen/orm/index.js +17 -0
  15. package/cli/codegen/orm/input-types-generator.js +83 -29
  16. package/cli/codegen/orm/model-generator.js +6 -4
  17. package/cli/codegen/queries.d.ts +7 -3
  18. package/cli/codegen/queries.js +185 -158
  19. package/cli/codegen/scalars.d.ts +6 -4
  20. package/cli/codegen/scalars.js +17 -9
  21. package/cli/codegen/schema-types-generator.d.ts +26 -0
  22. package/cli/codegen/schema-types-generator.js +365 -0
  23. package/cli/codegen/ts-ast.d.ts +3 -1
  24. package/cli/codegen/ts-ast.js +2 -2
  25. package/cli/codegen/type-resolver.d.ts +52 -6
  26. package/cli/codegen/type-resolver.js +97 -19
  27. package/cli/codegen/types.d.ts +7 -4
  28. package/cli/codegen/types.js +94 -41
  29. package/cli/codegen/utils.d.ts +20 -2
  30. package/cli/codegen/utils.js +32 -7
  31. package/cli/commands/generate-orm.js +5 -5
  32. package/cli/commands/generate.d.ts +4 -1
  33. package/cli/commands/generate.js +27 -8
  34. package/cli/introspect/transform-schema.d.ts +33 -21
  35. package/cli/introspect/transform-schema.js +31 -21
  36. package/esm/cli/codegen/barrel.d.ts +4 -1
  37. package/esm/cli/codegen/barrel.js +18 -12
  38. package/esm/cli/codegen/client.js +33 -0
  39. package/esm/cli/codegen/custom-mutations.d.ts +11 -1
  40. package/esm/cli/codegen/custom-mutations.js +50 -16
  41. package/esm/cli/codegen/custom-queries.d.ts +8 -0
  42. package/esm/cli/codegen/custom-queries.js +83 -48
  43. package/esm/cli/codegen/gql-ast.js +10 -6
  44. package/esm/cli/codegen/index.js +39 -8
  45. package/esm/cli/codegen/mutations.d.ts +14 -4
  46. package/esm/cli/codegen/mutations.js +115 -29
  47. package/esm/cli/codegen/orm/barrel.js +4 -2
  48. package/esm/cli/codegen/orm/index.js +17 -0
  49. package/esm/cli/codegen/orm/input-types-generator.js +83 -29
  50. package/esm/cli/codegen/orm/model-generator.js +7 -5
  51. package/esm/cli/codegen/queries.d.ts +7 -3
  52. package/esm/cli/codegen/queries.js +186 -159
  53. package/esm/cli/codegen/scalars.d.ts +6 -4
  54. package/esm/cli/codegen/scalars.js +16 -8
  55. package/esm/cli/codegen/schema-types-generator.d.ts +26 -0
  56. package/esm/cli/codegen/schema-types-generator.js +362 -0
  57. package/esm/cli/codegen/ts-ast.d.ts +3 -1
  58. package/esm/cli/codegen/ts-ast.js +2 -2
  59. package/esm/cli/codegen/type-resolver.d.ts +52 -6
  60. package/esm/cli/codegen/type-resolver.js +97 -20
  61. package/esm/cli/codegen/types.d.ts +7 -4
  62. package/esm/cli/codegen/types.js +95 -41
  63. package/esm/cli/codegen/utils.d.ts +20 -2
  64. package/esm/cli/codegen/utils.js +31 -7
  65. package/esm/cli/commands/generate-orm.js +5 -5
  66. package/esm/cli/commands/generate.d.ts +4 -1
  67. package/esm/cli/commands/generate.js +27 -8
  68. package/esm/cli/introspect/transform-schema.d.ts +33 -21
  69. package/esm/cli/introspect/transform-schema.js +31 -21
  70. package/esm/types/config.d.ts +16 -1
  71. package/esm/types/config.js +6 -0
  72. package/esm/types/schema.d.ts +2 -0
  73. package/package.json +8 -6
  74. package/types/config.d.ts +16 -1
  75. package/types/config.js +6 -0
  76. package/types/schema.d.ts +2 -0
  77. package/__tests__/codegen/input-types-generator.test.d.ts +0 -1
  78. package/__tests__/codegen/input-types-generator.test.js +0 -635
  79. package/cli/codegen/filters.d.ts +0 -27
  80. package/cli/codegen/filters.js +0 -357
  81. package/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
  82. package/cli/codegen/orm/input-types-generator.test.js +0 -75
  83. package/cli/codegen/orm/select-types.test.d.ts +0 -11
  84. package/cli/codegen/orm/select-types.test.js +0 -22
  85. package/cli/introspect/transform-schema.test.d.ts +0 -1
  86. package/cli/introspect/transform-schema.test.js +0 -67
  87. package/esm/__tests__/codegen/input-types-generator.test.d.ts +0 -1
  88. package/esm/__tests__/codegen/input-types-generator.test.js +0 -633
  89. package/esm/cli/codegen/filters.d.ts +0 -27
  90. package/esm/cli/codegen/filters.js +0 -351
  91. package/esm/cli/codegen/orm/input-types-generator.test.d.ts +0 -1
  92. package/esm/cli/codegen/orm/input-types-generator.test.js +0 -73
  93. package/esm/cli/codegen/orm/select-types.test.d.ts +0 -11
  94. package/esm/cli/codegen/orm/select-types.test.js +0 -21
  95. package/esm/cli/introspect/transform-schema.test.d.ts +0 -1
  96. package/esm/cli/introspect/transform-schema.test.js +0 -65
@@ -53,6 +53,39 @@ export function getConfig(): GraphQLClientConfig {
53
53
  return globalConfig;
54
54
  }
55
55
 
56
+ /**
57
+ * Set a single header value
58
+ * Useful for updating Authorization after login
59
+ *
60
+ * @example
61
+ * \`\`\`ts
62
+ * setHeader('Authorization', 'Bearer <new-token>');
63
+ * \`\`\`
64
+ */
65
+ export function setHeader(key: string, value: string): void {
66
+ const config = getConfig();
67
+ globalConfig = {
68
+ ...config,
69
+ headers: { ...config.headers, [key]: value },
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Merge multiple headers into the current configuration
75
+ *
76
+ * @example
77
+ * \`\`\`ts
78
+ * setHeaders({ Authorization: 'Bearer <token>', 'X-Custom': 'value' });
79
+ * \`\`\`
80
+ */
81
+ export function setHeaders(headers: Record<string, string>): void {
82
+ const config = getConfig();
83
+ globalConfig = {
84
+ ...config,
85
+ headers: { ...config.headers, ...headers },
86
+ };
87
+ }
88
+
56
89
  // ============================================================================
57
90
  // Error handling
58
91
  // ============================================================================
@@ -21,18 +21,28 @@ export interface GenerateCustomMutationHookOptions {
21
21
  typeRegistry: TypeRegistry;
22
22
  maxDepth?: number;
23
23
  skipQueryField?: boolean;
24
+ /** Whether to generate React Query hooks (default: true for backwards compatibility) */
25
+ reactQueryEnabled?: boolean;
26
+ /** Table entity type names (for import path resolution) */
27
+ tableTypeNames?: Set<string>;
24
28
  }
25
29
  /**
26
30
  * Generate a custom mutation hook file
31
+ * When reactQueryEnabled is false, returns null since mutations require React Query
27
32
  */
28
- export declare function generateCustomMutationHook(options: GenerateCustomMutationHookOptions): GeneratedCustomMutationFile;
33
+ export declare function generateCustomMutationHook(options: GenerateCustomMutationHookOptions): GeneratedCustomMutationFile | null;
29
34
  export interface GenerateAllCustomMutationHooksOptions {
30
35
  operations: CleanOperation[];
31
36
  typeRegistry: TypeRegistry;
32
37
  maxDepth?: number;
33
38
  skipQueryField?: boolean;
39
+ /** Whether to generate React Query hooks (default: true for backwards compatibility) */
40
+ reactQueryEnabled?: boolean;
41
+ /** Table entity type names (for import path resolution) */
42
+ tableTypeNames?: Set<string>;
34
43
  }
35
44
  /**
36
45
  * Generate all custom mutation hook files
46
+ * When reactQueryEnabled is false, returns empty array since mutations require React Query
37
47
  */
38
48
  export declare function generateAllCustomMutationHooks(options: GenerateAllCustomMutationHooksOptions): GeneratedCustomMutationFile[];
@@ -1,11 +1,16 @@
1
1
  import { createProject, createSourceFile, getFormattedOutput, createFileHeader, createImport, createInterface, createConst, } from './ts-ast';
2
2
  import { buildCustomMutationString } from './schema-gql-ast';
3
- import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, } from './type-resolver';
3
+ import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, createTypeTracker, } from './type-resolver';
4
4
  /**
5
5
  * Generate a custom mutation hook file
6
+ * When reactQueryEnabled is false, returns null since mutations require React Query
6
7
  */
7
8
  export function generateCustomMutationHook(options) {
8
- const { operation } = options;
9
+ const { operation, reactQueryEnabled = true } = options;
10
+ // Mutations require React Query - skip generation when disabled
11
+ if (!reactQueryEnabled) {
12
+ return null;
13
+ }
9
14
  try {
10
15
  return generateCustomMutationHookInternal(options);
11
16
  }
@@ -16,13 +21,15 @@ export function generateCustomMutationHook(options) {
16
21
  }
17
22
  }
18
23
  function generateCustomMutationHookInternal(options) {
19
- const { operation, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
24
+ const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, tableTypeNames } = options;
20
25
  const project = createProject();
21
26
  const hookName = getOperationHookName(operation.name, 'mutation');
22
27
  const fileName = getOperationFileName(operation.name, 'mutation');
23
28
  const variablesTypeName = getOperationVariablesTypeName(operation.name, 'mutation');
24
29
  const resultTypeName = getOperationResultTypeName(operation.name, 'mutation');
25
30
  const documentConstName = getDocumentConstName(operation.name, 'mutation');
31
+ // Create type tracker to collect referenced types (with table type awareness)
32
+ const tracker = createTypeTracker({ tableTypeNames });
26
33
  // Generate GraphQL document
27
34
  const mutationDocument = buildCustomMutationString({
28
35
  operation,
@@ -33,8 +40,21 @@ function generateCustomMutationHookInternal(options) {
33
40
  const sourceFile = createSourceFile(project, fileName);
34
41
  // Add file header
35
42
  sourceFile.insertText(0, createFileHeader(`Custom mutation hook for ${operation.name}`) + '\n\n');
43
+ // Generate variables interface if there are arguments (with tracking)
44
+ let variablesProps = [];
45
+ if (operation.args.length > 0) {
46
+ variablesProps = generateVariablesProperties(operation.args, tracker);
47
+ }
48
+ // Generate result interface (with tracking)
49
+ const resultType = typeRefToTsType(operation.returnType, tracker);
50
+ const resultProps = [
51
+ { name: operation.name, type: resultType },
52
+ ];
53
+ // Get importable types from tracker (separated by source)
54
+ const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
55
+ const tableTypes = tracker.getTableTypes(); // From types.ts
36
56
  // Add imports
37
- sourceFile.addImportDeclarations([
57
+ const imports = [
38
58
  createImport({
39
59
  moduleSpecifier: '@tanstack/react-query',
40
60
  namedImports: ['useMutation'],
@@ -44,21 +64,31 @@ function generateCustomMutationHookInternal(options) {
44
64
  moduleSpecifier: '../client',
45
65
  namedImports: ['execute'],
46
66
  }),
47
- ]);
67
+ ];
68
+ // Add types.ts import for table entity types
69
+ if (tableTypes.length > 0) {
70
+ imports.push(createImport({
71
+ moduleSpecifier: '../types',
72
+ typeOnlyNamedImports: tableTypes,
73
+ }));
74
+ }
75
+ // Add schema-types import for Input/Payload/Enum types
76
+ if (schemaTypes.length > 0) {
77
+ imports.push(createImport({
78
+ moduleSpecifier: '../schema-types',
79
+ typeOnlyNamedImports: schemaTypes,
80
+ }));
81
+ }
82
+ sourceFile.addImportDeclarations(imports);
48
83
  // Add mutation document constant
49
84
  sourceFile.addVariableStatement(createConst(documentConstName, '`\n' + mutationDocument + '`', {
50
85
  docs: ['GraphQL mutation document'],
51
86
  }));
52
- // Generate variables interface if there are arguments
87
+ // Add variables interface
53
88
  if (operation.args.length > 0) {
54
- const variablesProps = generateVariablesProperties(operation.args);
55
89
  sourceFile.addInterface(createInterface(variablesTypeName, variablesProps));
56
90
  }
57
- // Generate result interface
58
- const resultType = typeRefToTsType(operation.returnType);
59
- const resultProps = [
60
- { name: operation.name, type: resultType },
61
- ];
91
+ // Add result interface
62
92
  sourceFile.addInterface(createInterface(resultTypeName, resultProps));
63
93
  // Generate hook function
64
94
  const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
@@ -82,10 +112,10 @@ function generateCustomMutationHookInternal(options) {
82
112
  /**
83
113
  * Generate interface properties from CleanArguments
84
114
  */
85
- function generateVariablesProperties(args) {
115
+ function generateVariablesProperties(args, tracker) {
86
116
  return args.map((arg) => ({
87
117
  name: arg.name,
88
- type: typeRefToTsType(arg.type),
118
+ type: typeRefToTsType(arg.type, tracker),
89
119
  optional: !isTypeRequired(arg.type),
90
120
  docs: arg.description ? [arg.description] : undefined,
91
121
  }));
@@ -131,9 +161,10 @@ function generateHookBody(operation, documentConstName, variablesTypeName, resul
131
161
  }
132
162
  /**
133
163
  * Generate all custom mutation hook files
164
+ * When reactQueryEnabled is false, returns empty array since mutations require React Query
134
165
  */
135
166
  export function generateAllCustomMutationHooks(options) {
136
- const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
167
+ const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
137
168
  return operations
138
169
  .filter((op) => op.kind === 'mutation')
139
170
  .map((operation) => generateCustomMutationHook({
@@ -141,5 +172,8 @@ export function generateAllCustomMutationHooks(options) {
141
172
  typeRegistry,
142
173
  maxDepth,
143
174
  skipQueryField,
144
- }));
175
+ reactQueryEnabled,
176
+ tableTypeNames,
177
+ }))
178
+ .filter((result) => result !== null);
145
179
  }
@@ -21,6 +21,10 @@ export interface GenerateCustomQueryHookOptions {
21
21
  typeRegistry: TypeRegistry;
22
22
  maxDepth?: number;
23
23
  skipQueryField?: boolean;
24
+ /** Whether to generate React Query hooks (default: true for backwards compatibility) */
25
+ reactQueryEnabled?: boolean;
26
+ /** Table entity type names (for import path resolution) */
27
+ tableTypeNames?: Set<string>;
24
28
  }
25
29
  /**
26
30
  * Generate a custom query hook file
@@ -31,6 +35,10 @@ export interface GenerateAllCustomQueryHooksOptions {
31
35
  typeRegistry: TypeRegistry;
32
36
  maxDepth?: number;
33
37
  skipQueryField?: boolean;
38
+ /** Whether to generate React Query hooks (default: true for backwards compatibility) */
39
+ reactQueryEnabled?: boolean;
40
+ /** Table entity type names (for import path resolution) */
41
+ tableTypeNames?: Set<string>;
34
42
  }
35
43
  /**
36
44
  * Generate all custom query hook files
@@ -1,12 +1,12 @@
1
1
  import { createProject, createSourceFile, getFormattedOutput, createFileHeader, createImport, createInterface, createConst, } from './ts-ast';
2
2
  import { buildCustomQueryString } from './schema-gql-ast';
3
- import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, getQueryKeyName, } from './type-resolver';
3
+ import { typeRefToTsType, isTypeRequired, getOperationHookName, getOperationFileName, getOperationVariablesTypeName, getOperationResultTypeName, getDocumentConstName, getQueryKeyName, createTypeTracker, } from './type-resolver';
4
4
  import { ucFirst } from './utils';
5
5
  /**
6
6
  * Generate a custom query hook file
7
7
  */
8
8
  export function generateCustomQueryHook(options) {
9
- const { operation, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
9
+ const { operation, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
10
10
  const project = createProject();
11
11
  const hookName = getOperationHookName(operation.name, 'query');
12
12
  const fileName = getOperationFileName(operation.name, 'query');
@@ -14,6 +14,8 @@ export function generateCustomQueryHook(options) {
14
14
  const resultTypeName = getOperationResultTypeName(operation.name, 'query');
15
15
  const documentConstName = getDocumentConstName(operation.name, 'query');
16
16
  const queryKeyName = getQueryKeyName(operation.name);
17
+ // Create type tracker to collect referenced types (with table type awareness)
18
+ const tracker = createTypeTracker({ tableTypeNames });
17
19
  // Generate GraphQL document
18
20
  const queryDocument = buildCustomQueryString({
19
21
  operation,
@@ -23,34 +25,61 @@ export function generateCustomQueryHook(options) {
23
25
  });
24
26
  const sourceFile = createSourceFile(project, fileName);
25
27
  // Add file header
26
- sourceFile.insertText(0, createFileHeader(`Custom query hook for ${operation.name}`) + '\n\n');
27
- // Add imports
28
- sourceFile.addImportDeclarations([
29
- createImport({
28
+ const headerText = reactQueryEnabled
29
+ ? `Custom query hook for ${operation.name}`
30
+ : `Custom query functions for ${operation.name}`;
31
+ sourceFile.insertText(0, createFileHeader(headerText) + '\n\n');
32
+ // Generate variables interface if there are arguments (with tracking)
33
+ let variablesProps = [];
34
+ if (operation.args.length > 0) {
35
+ variablesProps = generateVariablesProperties(operation.args, tracker);
36
+ }
37
+ // Generate result interface (with tracking)
38
+ const resultType = typeRefToTsType(operation.returnType, tracker);
39
+ const resultProps = [
40
+ { name: operation.name, type: resultType },
41
+ ];
42
+ // Get importable types from tracker (separated by source)
43
+ const schemaTypes = tracker.getImportableTypes(); // From schema-types.ts
44
+ const tableTypes = tracker.getTableTypes(); // From types.ts
45
+ // Add imports - conditionally include React Query imports
46
+ const imports = [];
47
+ if (reactQueryEnabled) {
48
+ imports.push(createImport({
30
49
  moduleSpecifier: '@tanstack/react-query',
31
50
  namedImports: ['useQuery'],
32
51
  typeOnlyNamedImports: ['UseQueryOptions', 'QueryClient'],
33
- }),
34
- createImport({
35
- moduleSpecifier: '../client',
36
- namedImports: ['execute'],
37
- typeOnlyNamedImports: ['ExecuteOptions'],
38
- }),
39
- ]);
52
+ }));
53
+ }
54
+ imports.push(createImport({
55
+ moduleSpecifier: '../client',
56
+ namedImports: ['execute'],
57
+ typeOnlyNamedImports: ['ExecuteOptions'],
58
+ }));
59
+ // Add types.ts import for table entity types
60
+ if (tableTypes.length > 0) {
61
+ imports.push(createImport({
62
+ moduleSpecifier: '../types',
63
+ typeOnlyNamedImports: tableTypes,
64
+ }));
65
+ }
66
+ // Add schema-types import for Input/Payload/Enum types
67
+ if (schemaTypes.length > 0) {
68
+ imports.push(createImport({
69
+ moduleSpecifier: '../schema-types',
70
+ typeOnlyNamedImports: schemaTypes,
71
+ }));
72
+ }
73
+ sourceFile.addImportDeclarations(imports);
40
74
  // Add query document constant
41
75
  sourceFile.addVariableStatement(createConst(documentConstName, '`\n' + queryDocument + '`', {
42
76
  docs: ['GraphQL query document'],
43
77
  }));
44
- // Generate variables interface if there are arguments
78
+ // Add variables interface
45
79
  if (operation.args.length > 0) {
46
- const variablesProps = generateVariablesProperties(operation.args);
47
80
  sourceFile.addInterface(createInterface(variablesTypeName, variablesProps));
48
81
  }
49
- // Generate result interface
50
- const resultType = typeRefToTsType(operation.returnType);
51
- const resultProps = [
52
- { name: operation.name, type: resultType },
53
- ];
82
+ // Add result interface
54
83
  sourceFile.addInterface(createInterface(resultTypeName, resultProps));
55
84
  // Query key factory
56
85
  if (operation.args.length > 0) {
@@ -62,17 +91,19 @@ export function generateCustomQueryHook(options) {
62
91
  docs: ['Query key factory for caching'],
63
92
  }));
64
93
  }
65
- // Generate hook function
66
- const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
67
- const hookBody = generateHookBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
68
- const hookDoc = generateHookDoc(operation, hookName);
69
- sourceFile.addFunction({
70
- name: hookName,
71
- isExported: true,
72
- parameters: hookParams,
73
- statements: hookBody,
74
- docs: [{ description: hookDoc }],
75
- });
94
+ // Generate hook function (only if React Query is enabled)
95
+ if (reactQueryEnabled) {
96
+ const hookParams = generateHookParameters(operation, variablesTypeName, resultTypeName);
97
+ const hookBody = generateHookBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
98
+ const hookDoc = generateHookDoc(operation, hookName);
99
+ sourceFile.addFunction({
100
+ name: hookName,
101
+ isExported: true,
102
+ parameters: hookParams,
103
+ statements: hookBody,
104
+ docs: [{ description: hookDoc }],
105
+ });
106
+ }
76
107
  // Add standalone functions section
77
108
  sourceFile.addStatements('\n// ============================================================================');
78
109
  sourceFile.addStatements('// Standalone Functions (non-React)');
@@ -91,20 +122,22 @@ export function generateCustomQueryHook(options) {
91
122
  statements: fetchBody,
92
123
  docs: [{ description: fetchDoc }],
93
124
  });
94
- // Generate prefetch function
95
- const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`;
96
- const prefetchParams = generatePrefetchParameters(operation, variablesTypeName);
97
- const prefetchBody = generatePrefetchBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
98
- const prefetchDoc = generatePrefetchDoc(operation, prefetchFnName);
99
- sourceFile.addFunction({
100
- name: prefetchFnName,
101
- isExported: true,
102
- isAsync: true,
103
- parameters: prefetchParams,
104
- returnType: 'Promise<void>',
105
- statements: prefetchBody,
106
- docs: [{ description: prefetchDoc }],
107
- });
125
+ // Generate prefetch function (only if React Query is enabled)
126
+ if (reactQueryEnabled) {
127
+ const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`;
128
+ const prefetchParams = generatePrefetchParameters(operation, variablesTypeName);
129
+ const prefetchBody = generatePrefetchBody(operation, documentConstName, queryKeyName, variablesTypeName, resultTypeName);
130
+ const prefetchDoc = generatePrefetchDoc(operation, prefetchFnName);
131
+ sourceFile.addFunction({
132
+ name: prefetchFnName,
133
+ isExported: true,
134
+ isAsync: true,
135
+ parameters: prefetchParams,
136
+ returnType: 'Promise<void>',
137
+ statements: prefetchBody,
138
+ docs: [{ description: prefetchDoc }],
139
+ });
140
+ }
108
141
  return {
109
142
  fileName,
110
143
  content: getFormattedOutput(sourceFile),
@@ -117,10 +150,10 @@ export function generateCustomQueryHook(options) {
117
150
  /**
118
151
  * Generate interface properties from CleanArguments
119
152
  */
120
- function generateVariablesProperties(args) {
153
+ function generateVariablesProperties(args, tracker) {
121
154
  return args.map((arg) => ({
122
155
  name: arg.name,
123
- type: typeRefToTsType(arg.type),
156
+ type: typeRefToTsType(arg.type, tracker),
124
157
  optional: !isTypeRequired(arg.type),
125
158
  docs: arg.description ? [arg.description] : undefined,
126
159
  }));
@@ -342,7 +375,7 @@ await ${fnName}(queryClient);
342
375
  * Generate all custom query hook files
343
376
  */
344
377
  export function generateAllCustomQueryHooks(options) {
345
- const { operations, typeRegistry, maxDepth = 2, skipQueryField = true } = options;
378
+ const { operations, typeRegistry, maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames } = options;
346
379
  return operations
347
380
  .filter((op) => op.kind === 'query')
348
381
  .map((operation) => generateCustomQueryHook({
@@ -350,5 +383,7 @@ export function generateAllCustomQueryHooks(options) {
350
383
  typeRegistry,
351
384
  maxDepth,
352
385
  skipQueryField,
386
+ reactQueryEnabled,
387
+ tableTypeNames,
353
388
  }));
354
389
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import * as t from 'gql-ast';
8
8
  import { print } from 'graphql';
9
- import { getTableNames, getAllRowsQueryName, getSingleRowQueryName, getCreateMutationName, getUpdateMutationName, getDeleteMutationName, getFilterTypeName, getOrderByTypeName, getScalarFields, ucFirst, } from './utils';
9
+ import { getTableNames, getAllRowsQueryName, getSingleRowQueryName, getCreateMutationName, getUpdateMutationName, getDeleteMutationName, getFilterTypeName, getOrderByTypeName, getScalarFields, getPrimaryKeyInfo, ucFirst, } from './utils';
10
10
  // ============================================================================
11
11
  // Field selection builders
12
12
  // ============================================================================
@@ -106,16 +106,20 @@ export function buildSingleQueryAST(config) {
106
106
  const { table } = config;
107
107
  const queryName = getSingleRowQueryName(table);
108
108
  const scalarFields = getScalarFields(table);
109
- // Variable definitions - assume UUID primary key
109
+ // Get primary key info dynamically from table constraints
110
+ const pkFields = getPrimaryKeyInfo(table);
111
+ // For simplicity, use first PK field (most common case)
112
+ const pkField = pkFields[0];
113
+ // Variable definitions - use dynamic PK field name and type
110
114
  const variableDefinitions = [
111
115
  t.variableDefinition({
112
- variable: t.variable({ name: 'id' }),
113
- type: t.nonNullType({ type: t.namedType({ type: 'UUID' }) }),
116
+ variable: t.variable({ name: pkField.name }),
117
+ type: t.nonNullType({ type: t.namedType({ type: pkField.gqlType }) }),
114
118
  }),
115
119
  ];
116
- // Query arguments
120
+ // Query arguments - use dynamic PK field name
117
121
  const args = [
118
- t.argument({ name: 'id', value: t.variable({ name: 'id' }) }),
122
+ t.argument({ name: pkField.name, value: t.variable({ name: pkField.name }) }),
119
123
  ];
120
124
  // Field selections
121
125
  const fieldSelections = createFieldSelections(scalarFields);
@@ -1,10 +1,12 @@
1
1
  import { generateClientFile } from './client';
2
2
  import { generateTypesFile } from './types';
3
+ import { generateSchemaTypesFile } from './schema-types-generator';
3
4
  import { generateAllQueryHooks } from './queries';
4
5
  import { generateAllMutationHooks } from './mutations';
5
6
  import { generateAllCustomQueryHooks } from './custom-queries';
6
7
  import { generateAllCustomMutationHooks } from './custom-mutations';
7
8
  import { generateQueriesBarrel, generateMutationsBarrel, generateMainBarrel, generateCustomQueriesBarrel, generateCustomMutationsBarrel, } from './barrel';
9
+ import { getTableNames } from './utils';
8
10
  // ============================================================================
9
11
  // Main orchestrator
10
12
  // ============================================================================
@@ -23,25 +25,47 @@ export function generate(options) {
23
25
  // Extract codegen options
24
26
  const maxDepth = config.codegen.maxFieldDepth;
25
27
  const skipQueryField = config.codegen.skipQueryField;
28
+ const reactQueryEnabled = config.reactQuery.enabled;
26
29
  // 1. Generate client.ts
27
30
  files.push({
28
31
  path: 'client.ts',
29
32
  content: generateClientFile(),
30
33
  });
31
- // 2. Generate types.ts
34
+ // Collect table type names for import path resolution
35
+ const tableTypeNames = new Set(tables.map((t) => getTableNames(t).typeName));
36
+ // 2. Generate schema-types.ts for custom operations (if any)
37
+ // NOTE: This must come BEFORE types.ts so that types.ts can import enum types
38
+ let hasSchemaTypes = false;
39
+ let generatedEnumNames = [];
40
+ if (customOperations && customOperations.typeRegistry) {
41
+ const schemaTypesResult = generateSchemaTypesFile({
42
+ typeRegistry: customOperations.typeRegistry,
43
+ tableTypeNames,
44
+ });
45
+ // Only include if there's meaningful content
46
+ if (schemaTypesResult.content.split('\n').length > 10) {
47
+ files.push({
48
+ path: 'schema-types.ts',
49
+ content: schemaTypesResult.content,
50
+ });
51
+ hasSchemaTypes = true;
52
+ generatedEnumNames = schemaTypesResult.generatedEnums || [];
53
+ }
54
+ }
55
+ // 3. Generate types.ts (can now import enums from schema-types)
32
56
  files.push({
33
57
  path: 'types.ts',
34
- content: generateTypesFile(tables),
58
+ content: generateTypesFile(tables, { enumsFromSchemaTypes: generatedEnumNames }),
35
59
  });
36
- // 3. Generate table-based query hooks (queries/*.ts)
37
- const queryHooks = generateAllQueryHooks(tables);
60
+ // 4. Generate table-based query hooks (queries/*.ts)
61
+ const queryHooks = generateAllQueryHooks(tables, { reactQueryEnabled });
38
62
  for (const hook of queryHooks) {
39
63
  files.push({
40
64
  path: `queries/${hook.fileName}`,
41
65
  content: hook.content,
42
66
  });
43
67
  }
44
- // 4. Generate custom query hooks if available
68
+ // 5. Generate custom query hooks if available
45
69
  let customQueryHooks = [];
46
70
  if (customOperations && customOperations.queries.length > 0) {
47
71
  customQueryHooks = generateAllCustomQueryHooks({
@@ -49,6 +73,8 @@ export function generate(options) {
49
73
  typeRegistry: customOperations.typeRegistry,
50
74
  maxDepth,
51
75
  skipQueryField,
76
+ reactQueryEnabled,
77
+ tableTypeNames,
52
78
  });
53
79
  for (const hook of customQueryHooks) {
54
80
  files.push({
@@ -65,7 +91,10 @@ export function generate(options) {
65
91
  : generateQueriesBarrel(tables),
66
92
  });
67
93
  // 6. Generate table-based mutation hooks (mutations/*.ts)
68
- const mutationHooks = generateAllMutationHooks(tables);
94
+ const mutationHooks = generateAllMutationHooks(tables, {
95
+ reactQueryEnabled,
96
+ enumsFromSchemaTypes: generatedEnumNames,
97
+ });
69
98
  for (const hook of mutationHooks) {
70
99
  files.push({
71
100
  path: `mutations/${hook.fileName}`,
@@ -80,6 +109,8 @@ export function generate(options) {
80
109
  typeRegistry: customOperations.typeRegistry,
81
110
  maxDepth,
82
111
  skipQueryField,
112
+ reactQueryEnabled,
113
+ tableTypeNames,
83
114
  });
84
115
  for (const hook of customMutationHooks) {
85
116
  files.push({
@@ -95,10 +126,10 @@ export function generate(options) {
95
126
  ? generateCustomMutationsBarrel(tables, customMutationHooks.map((h) => h.operationName))
96
127
  : generateMutationsBarrel(tables),
97
128
  });
98
- // 9. Generate main index.ts barrel
129
+ // 9. Generate main index.ts barrel (with schema-types if present)
99
130
  files.push({
100
131
  path: 'index.ts',
101
- content: generateMainBarrel(tables),
132
+ content: generateMainBarrel(tables, hasSchemaTypes),
102
133
  });
103
134
  return {
104
135
  files,
@@ -12,19 +12,29 @@ export interface GeneratedMutationFile {
12
12
  fileName: string;
13
13
  content: string;
14
14
  }
15
+ export interface MutationGeneratorOptions {
16
+ /** Whether to generate React Query hooks (default: true for backwards compatibility) */
17
+ reactQueryEnabled?: boolean;
18
+ /** Enum type names that are available from schema-types.ts */
19
+ enumsFromSchemaTypes?: string[];
20
+ }
15
21
  /**
16
22
  * Generate create mutation hook file content using AST
23
+ * When reactQueryEnabled is false, returns null since mutations require React Query
17
24
  */
18
- export declare function generateCreateMutationHook(table: CleanTable): GeneratedMutationFile;
25
+ export declare function generateCreateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
19
26
  /**
20
27
  * Generate update mutation hook file content using AST
28
+ * When reactQueryEnabled is false, returns null since mutations require React Query
21
29
  */
22
- export declare function generateUpdateMutationHook(table: CleanTable): GeneratedMutationFile | null;
30
+ export declare function generateUpdateMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
23
31
  /**
24
32
  * Generate delete mutation hook file content using AST
33
+ * When reactQueryEnabled is false, returns null since mutations require React Query
25
34
  */
26
- export declare function generateDeleteMutationHook(table: CleanTable): GeneratedMutationFile | null;
35
+ export declare function generateDeleteMutationHook(table: CleanTable, options?: MutationGeneratorOptions): GeneratedMutationFile | null;
27
36
  /**
28
37
  * Generate all mutation hook files for all tables
38
+ * When reactQueryEnabled is false, returns empty array since mutations require React Query
29
39
  */
30
- export declare function generateAllMutationHooks(tables: CleanTable[]): GeneratedMutationFile[];
40
+ export declare function generateAllMutationHooks(tables: CleanTable[], options?: MutationGeneratorOptions): GeneratedMutationFile[];