@constructive-io/graphql-codegen 2.24.1 → 2.26.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 (38) hide show
  1. package/README.md +403 -279
  2. package/cli/codegen/orm/client-generator.js +475 -136
  3. package/cli/codegen/orm/custom-ops-generator.js +8 -3
  4. package/cli/codegen/orm/model-generator.js +18 -5
  5. package/cli/codegen/orm/select-types.d.ts +33 -0
  6. package/cli/commands/generate-orm.d.ts +14 -0
  7. package/cli/commands/generate-orm.js +160 -44
  8. package/cli/commands/generate.d.ts +22 -0
  9. package/cli/commands/generate.js +195 -55
  10. package/cli/commands/init.js +29 -9
  11. package/cli/index.js +133 -28
  12. package/cli/watch/orchestrator.d.ts +4 -0
  13. package/cli/watch/orchestrator.js +4 -0
  14. package/esm/cli/codegen/orm/client-generator.js +475 -136
  15. package/esm/cli/codegen/orm/custom-ops-generator.js +8 -3
  16. package/esm/cli/codegen/orm/model-generator.js +18 -5
  17. package/esm/cli/codegen/orm/select-types.d.ts +33 -0
  18. package/esm/cli/commands/generate-orm.d.ts +14 -0
  19. package/esm/cli/commands/generate-orm.js +161 -45
  20. package/esm/cli/commands/generate.d.ts +22 -0
  21. package/esm/cli/commands/generate.js +195 -56
  22. package/esm/cli/commands/init.js +29 -9
  23. package/esm/cli/index.js +134 -29
  24. package/esm/cli/watch/orchestrator.d.ts +4 -0
  25. package/esm/cli/watch/orchestrator.js +5 -1
  26. package/esm/types/config.d.ts +39 -2
  27. package/esm/types/config.js +88 -4
  28. package/esm/types/index.d.ts +2 -2
  29. package/esm/types/index.js +1 -1
  30. package/package.json +10 -7
  31. package/types/config.d.ts +39 -2
  32. package/types/config.js +91 -4
  33. package/types/index.d.ts +2 -2
  34. package/types/index.js +2 -1
  35. package/cli/codegen/orm/query-builder.d.ts +0 -161
  36. package/cli/codegen/orm/query-builder.js +0 -366
  37. package/esm/cli/codegen/orm/query-builder.d.ts +0 -161
  38. package/esm/cli/codegen/orm/query-builder.js +0 -353
@@ -159,9 +159,14 @@ function buildOperationMethod(op, operationType) {
159
159
  const optionsParam = t.identifier('options');
160
160
  optionsParam.optional = true;
161
161
  if (selectTypeName) {
162
+ // Use DeepExact<S, SelectType> to enforce strict field validation
163
+ // This catches invalid fields even when mixed with valid ones
162
164
  optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeLiteral([
163
165
  (() => {
164
- const prop = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))));
166
+ const prop = t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([
167
+ t.tsTypeReference(t.identifier('S')),
168
+ t.tsTypeReference(t.identifier(selectTypeName)),
169
+ ]))));
165
170
  prop.optional = true;
166
171
  return prop;
167
172
  })(),
@@ -229,7 +234,7 @@ function generateCustomQueryOpsFile(operations) {
229
234
  // Add imports
230
235
  statements.push(createImportDeclaration('../client', ['OrmClient']));
231
236
  statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument']));
232
- statements.push(createImportDeclaration('../select-types', ['InferSelectResult'], true));
237
+ statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'DeepExact'], true));
233
238
  if (allTypeImports.length > 0) {
234
239
  statements.push(createImportDeclaration('../input-types', allTypeImports, true));
235
240
  }
@@ -267,7 +272,7 @@ function generateCustomMutationOpsFile(operations) {
267
272
  // Add imports
268
273
  statements.push(createImportDeclaration('../client', ['OrmClient']));
269
274
  statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument']));
270
- statements.push(createImportDeclaration('../select-types', ['InferSelectResult'], true));
275
+ statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'DeepExact'], true));
271
276
  if (allTypeImports.length > 0) {
272
277
  statements.push(createImportDeclaration('../input-types', allTypeImports, true));
273
278
  }
@@ -100,7 +100,7 @@ function generateModelFile(table, _useSharedTypes) {
100
100
  ]));
101
101
  statements.push(createImportDeclaration('../select-types', [
102
102
  'ConnectionResult', 'FindManyArgs', 'FindFirstArgs', 'CreateArgs',
103
- 'UpdateArgs', 'DeleteArgs', 'InferSelectResult',
103
+ 'UpdateArgs', 'DeleteArgs', 'InferSelectResult', 'DeepExact',
104
104
  ], true));
105
105
  statements.push(createImportDeclaration('../input-types', [
106
106
  typeName, relationTypeName, selectTypeName, whereTypeName, orderByTypeName,
@@ -114,10 +114,14 @@ function generateModelFile(table, _useSharedTypes) {
114
114
  paramProp.accessibility = 'private';
115
115
  classBody.push(t.classMethod('constructor', t.identifier('constructor'), [paramProp], t.blockStatement([])));
116
116
  // findMany method
117
+ // Use DeepExact<S, SelectType> to enforce strict field validation
117
118
  const findManyParam = t.identifier('args');
118
119
  findManyParam.optional = true;
119
120
  findManyParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('FindManyArgs'), t.tsTypeParameterInstantiation([
120
- t.tsTypeReference(t.identifier('S')),
121
+ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([
122
+ t.tsTypeReference(t.identifier('S')),
123
+ t.tsTypeReference(t.identifier(selectTypeName)),
124
+ ])),
121
125
  t.tsTypeReference(t.identifier(whereTypeName)),
122
126
  t.tsTypeReference(t.identifier(orderByTypeName)),
123
127
  ])));
@@ -152,7 +156,10 @@ function generateModelFile(table, _useSharedTypes) {
152
156
  const findFirstParam = t.identifier('args');
153
157
  findFirstParam.optional = true;
154
158
  findFirstParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation([
155
- t.tsTypeReference(t.identifier('S')),
159
+ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([
160
+ t.tsTypeReference(t.identifier('S')),
161
+ t.tsTypeReference(t.identifier(selectTypeName)),
162
+ ])),
156
163
  t.tsTypeReference(t.identifier(whereTypeName)),
157
164
  ])));
158
165
  const findFirstReturnType = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
@@ -178,7 +185,10 @@ function generateModelFile(table, _useSharedTypes) {
178
185
  // create method
179
186
  const createParam = t.identifier('args');
180
187
  createParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CreateArgs'), t.tsTypeParameterInstantiation([
181
- t.tsTypeReference(t.identifier('S')),
188
+ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([
189
+ t.tsTypeReference(t.identifier('S')),
190
+ t.tsTypeReference(t.identifier(selectTypeName)),
191
+ ])),
182
192
  t.tsIndexedAccessType(t.tsTypeReference(t.identifier(createInputTypeName)), t.tsLiteralType(t.stringLiteral(singularName))),
183
193
  ])));
184
194
  const createReturnType = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([
@@ -204,7 +214,10 @@ function generateModelFile(table, _useSharedTypes) {
204
214
  if (updateMutationName) {
205
215
  const updateParam = t.identifier('args');
206
216
  updateParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('UpdateArgs'), t.tsTypeParameterInstantiation([
207
- t.tsTypeReference(t.identifier('S')),
217
+ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([
218
+ t.tsTypeReference(t.identifier('S')),
219
+ t.tsTypeReference(t.identifier(selectTypeName)),
220
+ ])),
208
221
  t.tsTypeLiteral([t.tsPropertySignature(t.identifier('id'), t.tsTypeAnnotation(t.tsStringKeyword()))]),
209
222
  t.tsTypeReference(t.identifier(patchTypeName)),
210
223
  ])));
@@ -44,6 +44,39 @@ export interface NestedSelectConfig {
44
44
  filter?: Record<string, unknown>;
45
45
  orderBy?: string[];
46
46
  }
47
+ /**
48
+ * Recursively validates select objects, rejecting unknown keys.
49
+ *
50
+ * This type ensures that users can only select fields that actually exist
51
+ * in the GraphQL schema. It returns `never` if any excess keys are found
52
+ * at any nesting level, causing a TypeScript compile error.
53
+ *
54
+ * Why this is needed:
55
+ * TypeScript's excess property checking has a quirk where it only catches
56
+ * invalid fields when they are the ONLY fields. When mixed with valid fields
57
+ * (e.g., `{ id: true, invalidField: true }`), the structural typing allows
58
+ * the excess property through. This type explicitly checks for and rejects
59
+ * such cases.
60
+ *
61
+ * @example
62
+ * // This will cause a type error because 'invalid' doesn't exist:
63
+ * type Result = DeepExact<{ id: true, invalid: true }, { id?: boolean }>;
64
+ * // Result = never (causes assignment error)
65
+ *
66
+ * @example
67
+ * // This works because all fields are valid:
68
+ * type Result = DeepExact<{ id: true }, { id?: boolean; name?: boolean }>;
69
+ * // Result = { id: true }
70
+ */
71
+ export type DeepExact<T, Shape> = T extends Shape ? Exclude<keyof T, keyof Shape> extends never ? {
72
+ [K in keyof T]: K extends keyof Shape ? T[K] extends {
73
+ select: infer NS;
74
+ } ? Shape[K] extends {
75
+ select?: infer ShapeNS;
76
+ } ? {
77
+ select: DeepExact<NS, NonNullable<ShapeNS>>;
78
+ } : T[K] : T[K] : never;
79
+ } : never : never;
47
80
  /**
48
81
  * Infers the result type from a select configuration
49
82
  *
@@ -9,6 +9,8 @@
9
9
  export interface GenerateOrmOptions {
10
10
  /** Path to config file */
11
11
  config?: string;
12
+ /** Named target in a multi-target config */
13
+ target?: string;
12
14
  /** GraphQL endpoint URL (overrides config) */
13
15
  endpoint?: string;
14
16
  /** Path to GraphQL schema file (.graphql) */
@@ -24,9 +26,21 @@ export interface GenerateOrmOptions {
24
26
  /** Skip custom operations (only generate table CRUD) */
25
27
  skipCustomOperations?: boolean;
26
28
  }
29
+ export interface GenerateOrmTargetResult {
30
+ name: string;
31
+ output: string;
32
+ success: boolean;
33
+ message: string;
34
+ tables?: string[];
35
+ customQueries?: string[];
36
+ customMutations?: string[];
37
+ filesWritten?: string[];
38
+ errors?: string[];
39
+ }
27
40
  export interface GenerateOrmResult {
28
41
  success: boolean;
29
42
  message: string;
43
+ targets?: GenerateOrmTargetResult[];
30
44
  tables?: string[];
31
45
  customQueries?: string[];
32
46
  customMutations?: string[];
@@ -19,9 +19,9 @@ const orm_1 = require("../codegen/orm");
19
19
  * Execute the generate-orm command
20
20
  */
21
21
  async function generateOrmCommand(options = {}) {
22
- const log = options.verbose ? console.log : () => { };
23
- // 1. Load config
24
- log('Loading configuration...');
22
+ if (options.verbose) {
23
+ console.log('Loading configuration...');
24
+ }
25
25
  const configResult = await loadConfig(options);
26
26
  if (!configResult.success) {
27
27
  return {
@@ -29,26 +29,73 @@ async function generateOrmCommand(options = {}) {
29
29
  message: configResult.error,
30
30
  };
31
31
  }
32
- const config = configResult.config;
33
- // Use ORM output directory if specified, otherwise default
34
- const outputDir = options.output || config.orm?.output || './generated/orm';
35
- // Log source
36
- if (config.schema) {
37
- log(` Schema: ${config.schema}`);
32
+ const targets = configResult.targets ?? [];
33
+ if (targets.length === 0) {
34
+ return {
35
+ success: false,
36
+ message: 'No targets resolved from configuration.',
37
+ };
38
+ }
39
+ const isMultiTarget = configResult.isMulti ?? targets.length > 1;
40
+ const results = [];
41
+ for (const target of targets) {
42
+ const result = await generateOrmForTarget(target, options, isMultiTarget);
43
+ results.push(result);
44
+ }
45
+ if (!isMultiTarget) {
46
+ const [result] = results;
47
+ return {
48
+ success: result.success,
49
+ message: result.message,
50
+ targets: results,
51
+ tables: result.tables,
52
+ customQueries: result.customQueries,
53
+ customMutations: result.customMutations,
54
+ filesWritten: result.filesWritten,
55
+ errors: result.errors,
56
+ };
38
57
  }
39
- else {
40
- log(` Endpoint: ${config.endpoint}`);
58
+ const successCount = results.filter((result) => result.success).length;
59
+ const failedCount = results.length - successCount;
60
+ const summaryMessage = failedCount === 0
61
+ ? `Generated ORM clients for ${results.length} targets.`
62
+ : `Generated ORM clients for ${successCount} of ${results.length} targets.`;
63
+ return {
64
+ success: failedCount === 0,
65
+ message: summaryMessage,
66
+ targets: results,
67
+ errors: failedCount > 0
68
+ ? results.flatMap((result) => result.errors ?? [])
69
+ : undefined,
70
+ };
71
+ }
72
+ async function generateOrmForTarget(target, options, isMultiTarget) {
73
+ const config = target.config;
74
+ const outputDir = options.output || config.orm?.output || './generated/orm';
75
+ const prefix = isMultiTarget ? `[${target.name}] ` : '';
76
+ const log = options.verbose
77
+ ? (message) => console.log(`${prefix}${message}`)
78
+ : () => { };
79
+ const formatMessage = (message) => isMultiTarget ? `Target "${target.name}": ${message}` : message;
80
+ if (isMultiTarget) {
81
+ console.log(`\nTarget "${target.name}"`);
82
+ const sourceLabel = config.schema
83
+ ? `schema: ${config.schema}`
84
+ : `endpoint: ${config.endpoint}`;
85
+ console.log(` Source: ${sourceLabel}`);
86
+ console.log(` Output: ${outputDir}`);
41
87
  }
42
- log(` Output: ${outputDir}`);
43
- // 2. Create schema source
88
+ // 1. Validate source
44
89
  const sourceValidation = (0, source_1.validateSourceOptions)({
45
90
  endpoint: config.endpoint || undefined,
46
91
  schema: config.schema || undefined,
47
92
  });
48
93
  if (!sourceValidation.valid) {
49
94
  return {
95
+ name: target.name,
96
+ output: outputDir,
50
97
  success: false,
51
- message: sourceValidation.error,
98
+ message: formatMessage(sourceValidation.error),
52
99
  };
53
100
  }
54
101
  const source = (0, source_1.createSchemaSource)({
@@ -57,7 +104,7 @@ async function generateOrmCommand(options = {}) {
57
104
  authorization: options.authorization || config.headers['Authorization'],
58
105
  headers: config.headers,
59
106
  });
60
- // 3. Run the codegen pipeline
107
+ // 2. Run the codegen pipeline
61
108
  let pipelineResult;
62
109
  try {
63
110
  pipelineResult = await (0, shared_1.runCodegenPipeline)({
@@ -69,21 +116,25 @@ async function generateOrmCommand(options = {}) {
69
116
  }
70
117
  catch (err) {
71
118
  return {
119
+ name: target.name,
120
+ output: outputDir,
72
121
  success: false,
73
- message: `Failed to fetch schema: ${err instanceof Error ? err.message : 'Unknown error'}`,
122
+ message: formatMessage(`Failed to fetch schema: ${err instanceof Error ? err.message : 'Unknown error'}`),
74
123
  };
75
124
  }
76
125
  const { tables, customOperations, stats } = pipelineResult;
77
- // 4. Validate tables found
126
+ // 3. Validate tables found
78
127
  const tablesValidation = (0, shared_1.validateTablesFound)(tables);
79
128
  if (!tablesValidation.valid) {
80
129
  return {
130
+ name: target.name,
131
+ output: outputDir,
81
132
  success: false,
82
- message: tablesValidation.error,
133
+ message: formatMessage(tablesValidation.error),
83
134
  };
84
135
  }
85
- // 5. Generate ORM code
86
- console.log('Generating code...');
136
+ // 4. Generate ORM code
137
+ console.log(`${prefix}Generating code...`);
87
138
  const { files: generatedFiles, stats: genStats } = (0, orm_1.generateOrm)({
88
139
  tables,
89
140
  customOperations: {
@@ -93,7 +144,7 @@ async function generateOrmCommand(options = {}) {
93
144
  },
94
145
  config,
95
146
  });
96
- console.log(`Generated ${genStats.totalFiles} files`);
147
+ console.log(`${prefix}Generated ${genStats.totalFiles} files`);
97
148
  log(` ${genStats.tables} table models`);
98
149
  log(` ${genStats.customQueries} custom query operations`);
99
150
  log(` ${genStats.customMutations} custom mutation operations`);
@@ -101,15 +152,17 @@ async function generateOrmCommand(options = {}) {
101
152
  const customMutations = customOperations.mutations.map((m) => m.name);
102
153
  if (options.dryRun) {
103
154
  return {
155
+ name: target.name,
156
+ output: outputDir,
104
157
  success: true,
105
- message: `Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${stats.customQueries + stats.customMutations} custom operations.`,
158
+ message: formatMessage(`Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${stats.customQueries + stats.customMutations} custom operations.`),
106
159
  tables: tables.map((t) => t.name),
107
160
  customQueries,
108
161
  customMutations,
109
162
  filesWritten: generatedFiles.map((f) => f.path),
110
163
  };
111
164
  }
112
- // 6. Write files
165
+ // 5. Write files
113
166
  log('Writing files...');
114
167
  const writeResult = await (0, generate_1.writeGeneratedFiles)(generatedFiles, outputDir, [
115
168
  'models',
@@ -118,23 +171,45 @@ async function generateOrmCommand(options = {}) {
118
171
  ]);
119
172
  if (!writeResult.success) {
120
173
  return {
174
+ name: target.name,
175
+ output: outputDir,
121
176
  success: false,
122
- message: `Failed to write files: ${writeResult.errors?.join(', ')}`,
177
+ message: formatMessage(`Failed to write files: ${writeResult.errors?.join(', ')}`),
123
178
  errors: writeResult.errors,
124
179
  };
125
180
  }
126
181
  const totalOps = customQueries.length + customMutations.length;
127
182
  const customOpsMsg = totalOps > 0 ? ` and ${totalOps} custom operations` : '';
128
183
  return {
184
+ name: target.name,
185
+ output: outputDir,
129
186
  success: true,
130
- message: `Generated ORM client for ${tables.length} tables${customOpsMsg}. Files written to ${outputDir}`,
187
+ message: formatMessage(`Generated ORM client for ${tables.length} tables${customOpsMsg}. Files written to ${outputDir}`),
131
188
  tables: tables.map((t) => t.name),
132
189
  customQueries,
133
190
  customMutations,
134
191
  filesWritten: writeResult.filesWritten,
135
192
  };
136
193
  }
194
+ function buildTargetOverrides(options) {
195
+ const overrides = {};
196
+ if (options.endpoint) {
197
+ overrides.endpoint = options.endpoint;
198
+ overrides.schema = undefined;
199
+ }
200
+ if (options.schema) {
201
+ overrides.schema = options.schema;
202
+ overrides.endpoint = undefined;
203
+ }
204
+ return overrides;
205
+ }
137
206
  async function loadConfig(options) {
207
+ if (options.endpoint && options.schema) {
208
+ return {
209
+ success: false,
210
+ error: 'Cannot use both --endpoint and --schema. Choose one source.',
211
+ };
212
+ }
138
213
  // Find config file
139
214
  let configPath = options.config;
140
215
  if (!configPath) {
@@ -148,29 +223,70 @@ async function loadConfig(options) {
148
223
  }
149
224
  baseConfig = loadResult.config;
150
225
  }
151
- // Override with CLI options
152
- const mergedConfig = {
153
- endpoint: options.endpoint || baseConfig.endpoint,
154
- schema: options.schema || baseConfig.schema,
155
- output: options.output || baseConfig.output,
156
- headers: baseConfig.headers,
157
- tables: baseConfig.tables,
158
- queries: baseConfig.queries,
159
- mutations: baseConfig.mutations,
160
- excludeFields: baseConfig.excludeFields,
161
- hooks: baseConfig.hooks,
162
- postgraphile: baseConfig.postgraphile,
163
- codegen: baseConfig.codegen,
164
- orm: baseConfig.orm,
165
- };
166
- // Validate at least one source is provided
226
+ const overrides = buildTargetOverrides(options);
227
+ if ((0, config_1.isMultiConfig)(baseConfig)) {
228
+ if (Object.keys(baseConfig.targets).length === 0) {
229
+ return {
230
+ success: false,
231
+ error: 'Config file defines no targets.',
232
+ };
233
+ }
234
+ if (!options.target &&
235
+ (options.endpoint || options.schema || options.output)) {
236
+ return {
237
+ success: false,
238
+ error: 'Multiple targets configured. Use --target with --endpoint, --schema, or --output.',
239
+ };
240
+ }
241
+ if (options.target && !baseConfig.targets[options.target]) {
242
+ return {
243
+ success: false,
244
+ error: `Target "${options.target}" not found in config file.`,
245
+ };
246
+ }
247
+ const selectedTargets = options.target
248
+ ? { [options.target]: baseConfig.targets[options.target] }
249
+ : baseConfig.targets;
250
+ const defaults = baseConfig.defaults ?? {};
251
+ const resolvedTargets = [];
252
+ for (const [name, target] of Object.entries(selectedTargets)) {
253
+ let mergedTarget = (0, config_1.mergeConfig)(defaults, target);
254
+ if (options.target && name === options.target) {
255
+ mergedTarget = (0, config_1.mergeConfig)(mergedTarget, overrides);
256
+ }
257
+ if (!mergedTarget.endpoint && !mergedTarget.schema) {
258
+ return {
259
+ success: false,
260
+ error: `Target "${name}" is missing an endpoint or schema.`,
261
+ };
262
+ }
263
+ resolvedTargets.push({
264
+ name,
265
+ config: (0, config_1.resolveConfig)(mergedTarget),
266
+ });
267
+ }
268
+ return {
269
+ success: true,
270
+ targets: resolvedTargets,
271
+ isMulti: true,
272
+ };
273
+ }
274
+ if (options.target) {
275
+ return {
276
+ success: false,
277
+ error: 'Config file does not define targets. Remove --target to continue.',
278
+ };
279
+ }
280
+ const mergedConfig = (0, config_1.mergeConfig)(baseConfig, overrides);
167
281
  if (!mergedConfig.endpoint && !mergedConfig.schema) {
168
282
  return {
169
283
  success: false,
170
284
  error: 'No source specified. Use --endpoint or --schema, or create a config file with "graphql-codegen init".',
171
285
  };
172
286
  }
173
- // Resolve with defaults
174
- const config = (0, config_1.resolveConfig)(mergedConfig);
175
- return { success: true, config };
287
+ return {
288
+ success: true,
289
+ targets: [{ name: 'default', config: (0, config_1.resolveConfig)(mergedConfig) }],
290
+ isMulti: false,
291
+ };
176
292
  }
@@ -1,6 +1,8 @@
1
1
  export interface GenerateOptions {
2
2
  /** Path to config file */
3
3
  config?: string;
4
+ /** Named target in a multi-target config */
5
+ target?: string;
4
6
  /** GraphQL endpoint URL (overrides config) */
5
7
  endpoint?: string;
6
8
  /** Path to GraphQL schema file (.graphql) */
@@ -16,9 +18,21 @@ export interface GenerateOptions {
16
18
  /** Skip custom operations (only generate table CRUD) */
17
19
  skipCustomOperations?: boolean;
18
20
  }
21
+ export interface GenerateTargetResult {
22
+ name: string;
23
+ output: string;
24
+ success: boolean;
25
+ message: string;
26
+ tables?: string[];
27
+ customQueries?: string[];
28
+ customMutations?: string[];
29
+ filesWritten?: string[];
30
+ errors?: string[];
31
+ }
19
32
  export interface GenerateResult {
20
33
  success: boolean;
21
34
  message: string;
35
+ targets?: GenerateTargetResult[];
22
36
  tables?: string[];
23
37
  customQueries?: string[];
24
38
  customMutations?: string[];
@@ -42,3 +56,11 @@ export interface WriteOptions {
42
56
  showProgress?: boolean;
43
57
  }
44
58
  export declare function writeGeneratedFiles(files: GeneratedFile[], outputDir: string, subdirs: string[], options?: WriteOptions): Promise<WriteResult>;
59
+ /**
60
+ * Format generated files using oxfmt
61
+ * Runs oxfmt on the output directory after all files are written
62
+ */
63
+ export declare function formatOutput(outputDir: string): {
64
+ success: boolean;
65
+ error?: string;
66
+ };