@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
package/esm/cli/index.js CHANGED
@@ -6,7 +6,18 @@ import { initCommand, findConfigFile, loadConfigFile } from './commands/init';
6
6
  import { generateCommand } from './commands/generate';
7
7
  import { generateOrmCommand } from './commands/generate-orm';
8
8
  import { startWatch } from './watch';
9
- import { resolveConfig, } from '../types/config';
9
+ import { isMultiConfig, mergeConfig, resolveConfig, } from '../types/config';
10
+ /**
11
+ * Format duration in a human-readable way
12
+ * - Under 1 second: show milliseconds (e.g., "123ms")
13
+ * - Over 1 second: show seconds with 2 decimal places (e.g., "1.23s")
14
+ */
15
+ function formatDuration(ms) {
16
+ if (ms < 1000) {
17
+ return `${Math.round(ms)}ms`;
18
+ }
19
+ return `${(ms / 1000).toFixed(2)}s`;
20
+ }
10
21
  const program = new Command();
11
22
  /**
12
23
  * Load configuration for watch mode, merging CLI options with config file
@@ -26,23 +37,27 @@ async function loadWatchConfig(options) {
26
37
  }
27
38
  baseConfig = loadResult.config;
28
39
  }
29
- // Merge CLI options with config
30
- const mergedConfig = {
31
- endpoint: options.endpoint || baseConfig.endpoint || '',
32
- output: baseConfig.output,
33
- headers: baseConfig.headers,
34
- tables: baseConfig.tables,
35
- queries: baseConfig.queries,
36
- mutations: baseConfig.mutations,
37
- excludeFields: baseConfig.excludeFields,
38
- hooks: baseConfig.hooks,
39
- postgraphile: baseConfig.postgraphile,
40
- codegen: baseConfig.codegen,
41
- orm: baseConfig.orm,
42
- reactQuery: baseConfig.reactQuery,
40
+ if (isMultiConfig(baseConfig)) {
41
+ if (!options.target) {
42
+ console.error('x Watch mode requires --target when using multiple targets.');
43
+ return null;
44
+ }
45
+ if (!baseConfig.targets[options.target]) {
46
+ console.error(`x Target "${options.target}" not found in config file.`);
47
+ return null;
48
+ }
49
+ }
50
+ else if (options.target) {
51
+ console.error('x Config file does not define targets. Remove --target.');
52
+ return null;
53
+ }
54
+ const sourceOverrides = {};
55
+ if (options.endpoint) {
56
+ sourceOverrides.endpoint = options.endpoint;
57
+ sourceOverrides.schema = undefined;
58
+ }
59
+ const watchOverrides = {
43
60
  watch: {
44
- ...baseConfig.watch,
45
- // CLI options override config
46
61
  ...(options.pollInterval !== undefined && {
47
62
  pollInterval: options.pollInterval,
48
63
  }),
@@ -51,11 +66,26 @@ async function loadWatchConfig(options) {
51
66
  ...(options.clear !== undefined && { clearScreen: options.clear }),
52
67
  },
53
68
  };
54
- if (!mergedConfig.endpoint) {
55
- console.error('x No endpoint specified. Use --endpoint or create a config file with "graphql-codegen init".');
69
+ let mergedTarget;
70
+ if (isMultiConfig(baseConfig)) {
71
+ const defaults = baseConfig.defaults ?? {};
72
+ const targetConfig = baseConfig.targets[options.target];
73
+ mergedTarget = mergeConfig(defaults, targetConfig);
74
+ }
75
+ else {
76
+ mergedTarget = baseConfig;
77
+ }
78
+ mergedTarget = mergeConfig(mergedTarget, sourceOverrides);
79
+ mergedTarget = mergeConfig(mergedTarget, watchOverrides);
80
+ if (!mergedTarget.endpoint) {
81
+ console.error('x No endpoint specified. Watch mode only supports live endpoints.');
82
+ return null;
83
+ }
84
+ if (mergedTarget.schema) {
85
+ console.error('x Watch mode is only supported with an endpoint, not schema.');
56
86
  return null;
57
87
  }
58
- return resolveConfig(mergedConfig);
88
+ return resolveConfig(mergedTarget);
59
89
  }
60
90
  program
61
91
  .name('graphql-codegen')
@@ -70,17 +100,19 @@ program
70
100
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL to pre-populate')
71
101
  .option('-o, --output <dir>', 'Output directory to pre-populate', './generated')
72
102
  .action(async (options) => {
103
+ const startTime = performance.now();
73
104
  const result = await initCommand({
74
105
  directory: options.directory,
75
106
  force: options.force,
76
107
  endpoint: options.endpoint,
77
108
  output: options.output,
78
109
  });
110
+ const duration = formatDuration(performance.now() - startTime);
79
111
  if (result.success) {
80
- console.log('[ok]', result.message);
112
+ console.log('[ok]', result.message, `(${duration})`);
81
113
  }
82
114
  else {
83
- console.error('x', result.message);
115
+ console.error('x', result.message, `(${duration})`);
84
116
  process.exit(1);
85
117
  }
86
118
  });
@@ -89,6 +121,7 @@ program
89
121
  .command('generate')
90
122
  .description('Generate SDK from GraphQL endpoint or schema file')
91
123
  .option('-c, --config <path>', 'Path to config file')
124
+ .option('-t, --target <name>', 'Target name in config file')
92
125
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL (overrides config)')
93
126
  .option('-s, --schema <path>', 'Path to GraphQL schema file (.graphql)')
94
127
  .option('-o, --output <dir>', 'Output directory (overrides config)')
@@ -101,6 +134,7 @@ program
101
134
  .option('--touch <file>', 'File to touch on schema change')
102
135
  .option('--no-clear', 'Do not clear terminal on regeneration')
103
136
  .action(async (options) => {
137
+ const startTime = performance.now();
104
138
  // Validate source options
105
139
  if (options.endpoint && options.schema) {
106
140
  console.error('x Cannot use both --endpoint and --schema. Choose one source.');
@@ -121,6 +155,8 @@ program
121
155
  generatorType: 'generate',
122
156
  verbose: options.verbose,
123
157
  authorization: options.authorization,
158
+ configPath: options.config,
159
+ target: options.target,
124
160
  outputDir: options.output,
125
161
  });
126
162
  return;
@@ -128,6 +164,7 @@ program
128
164
  // Normal one-shot generation
129
165
  const result = await generateCommand({
130
166
  config: options.config,
167
+ target: options.target,
131
168
  endpoint: options.endpoint,
132
169
  schema: options.schema,
133
170
  output: options.output,
@@ -135,8 +172,34 @@ program
135
172
  verbose: options.verbose,
136
173
  dryRun: options.dryRun,
137
174
  });
175
+ const duration = formatDuration(performance.now() - startTime);
176
+ const targetResults = result.targets ?? [];
177
+ const hasNamedTargets = targetResults.length > 0 &&
178
+ (targetResults.length > 1 || targetResults[0]?.name !== 'default');
179
+ if (hasNamedTargets) {
180
+ console.log(result.success ? '[ok]' : 'x', result.message);
181
+ targetResults.forEach((target) => {
182
+ const status = target.success ? '[ok]' : 'x';
183
+ console.log(`\n${status} ${target.message}`);
184
+ if (target.tables && target.tables.length > 0) {
185
+ console.log(' Tables:');
186
+ target.tables.forEach((table) => console.log(` - ${table}`));
187
+ }
188
+ if (target.filesWritten && target.filesWritten.length > 0) {
189
+ console.log(' Files written:');
190
+ target.filesWritten.forEach((file) => console.log(` - ${file}`));
191
+ }
192
+ if (!target.success && target.errors) {
193
+ target.errors.forEach((error) => console.error(` - ${error}`));
194
+ }
195
+ });
196
+ if (!result.success) {
197
+ process.exit(1);
198
+ }
199
+ return;
200
+ }
138
201
  if (result.success) {
139
- console.log('[ok]', result.message);
202
+ console.log('[ok]', result.message, `(${duration})`);
140
203
  if (result.tables && result.tables.length > 0) {
141
204
  console.log('\nTables:');
142
205
  result.tables.forEach((t) => console.log(` - ${t}`));
@@ -147,7 +210,7 @@ program
147
210
  }
148
211
  }
149
212
  else {
150
- console.error('x', result.message);
213
+ console.error('x', result.message, `(${duration})`);
151
214
  if (result.errors) {
152
215
  result.errors.forEach((e) => console.error(' -', e));
153
216
  }
@@ -159,9 +222,10 @@ program
159
222
  .command('generate-orm')
160
223
  .description('Generate Prisma-like ORM client from GraphQL endpoint or schema file')
161
224
  .option('-c, --config <path>', 'Path to config file')
225
+ .option('-t, --target <name>', 'Target name in config file')
162
226
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL (overrides config)')
163
227
  .option('-s, --schema <path>', 'Path to GraphQL schema file (.graphql)')
164
- .option('-o, --output <dir>', 'Output directory (overrides config)', './generated/orm')
228
+ .option('-o, --output <dir>', 'Output directory (overrides config)')
165
229
  .option('-a, --authorization <header>', 'Authorization header value')
166
230
  .option('-v, --verbose', 'Verbose output', false)
167
231
  .option('--dry-run', 'Dry run - show what would be generated without writing files', false)
@@ -172,6 +236,7 @@ program
172
236
  .option('--touch <file>', 'File to touch on schema change')
173
237
  .option('--no-clear', 'Do not clear terminal on regeneration')
174
238
  .action(async (options) => {
239
+ const startTime = performance.now();
175
240
  // Validate source options
176
241
  if (options.endpoint && options.schema) {
177
242
  console.error('x Cannot use both --endpoint and --schema. Choose one source.');
@@ -192,6 +257,8 @@ program
192
257
  generatorType: 'generate-orm',
193
258
  verbose: options.verbose,
194
259
  authorization: options.authorization,
260
+ configPath: options.config,
261
+ target: options.target,
195
262
  outputDir: options.output,
196
263
  skipCustomOperations: options.skipCustomOperations,
197
264
  });
@@ -200,6 +267,7 @@ program
200
267
  // Normal one-shot generation
201
268
  const result = await generateOrmCommand({
202
269
  config: options.config,
270
+ target: options.target,
203
271
  endpoint: options.endpoint,
204
272
  schema: options.schema,
205
273
  output: options.output,
@@ -208,8 +276,42 @@ program
208
276
  dryRun: options.dryRun,
209
277
  skipCustomOperations: options.skipCustomOperations,
210
278
  });
279
+ const duration = formatDuration(performance.now() - startTime);
280
+ const targetResults = result.targets ?? [];
281
+ const hasNamedTargets = targetResults.length > 0 &&
282
+ (targetResults.length > 1 || targetResults[0]?.name !== 'default');
283
+ if (hasNamedTargets) {
284
+ console.log(result.success ? '[ok]' : 'x', result.message);
285
+ targetResults.forEach((target) => {
286
+ const status = target.success ? '[ok]' : 'x';
287
+ console.log(`\n${status} ${target.message}`);
288
+ if (target.tables && target.tables.length > 0) {
289
+ console.log(' Tables:');
290
+ target.tables.forEach((table) => console.log(` - ${table}`));
291
+ }
292
+ if (target.customQueries && target.customQueries.length > 0) {
293
+ console.log(' Custom Queries:');
294
+ target.customQueries.forEach((query) => console.log(` - ${query}`));
295
+ }
296
+ if (target.customMutations && target.customMutations.length > 0) {
297
+ console.log(' Custom Mutations:');
298
+ target.customMutations.forEach((mutation) => console.log(` - ${mutation}`));
299
+ }
300
+ if (target.filesWritten && target.filesWritten.length > 0) {
301
+ console.log(' Files written:');
302
+ target.filesWritten.forEach((file) => console.log(` - ${file}`));
303
+ }
304
+ if (!target.success && target.errors) {
305
+ target.errors.forEach((error) => console.error(` - ${error}`));
306
+ }
307
+ });
308
+ if (!result.success) {
309
+ process.exit(1);
310
+ }
311
+ return;
312
+ }
211
313
  if (result.success) {
212
- console.log('[ok]', result.message);
314
+ console.log('[ok]', result.message, `(${duration})`);
213
315
  if (result.tables && result.tables.length > 0) {
214
316
  console.log('\nTables:');
215
317
  result.tables.forEach((t) => console.log(` - ${t}`));
@@ -228,7 +330,7 @@ program
228
330
  }
229
331
  }
230
332
  else {
231
- console.error('x', result.message);
333
+ console.error('x', result.message, `(${duration})`);
232
334
  if (result.errors) {
233
335
  result.errors.forEach((e) => console.error(' -', e));
234
336
  }
@@ -244,6 +346,7 @@ program
244
346
  .option('-a, --authorization <header>', 'Authorization header value')
245
347
  .option('--json', 'Output as JSON', false)
246
348
  .action(async (options) => {
349
+ const startTime = performance.now();
247
350
  // Validate source options
248
351
  if (!options.endpoint && !options.schema) {
249
352
  console.error('x Either --endpoint or --schema must be provided.');
@@ -264,11 +367,12 @@ program
264
367
  console.log('Fetching schema from', source.describe(), '...');
265
368
  const { introspection } = await source.fetch();
266
369
  const tables = inferTablesFromIntrospection(introspection);
370
+ const duration = formatDuration(performance.now() - startTime);
267
371
  if (options.json) {
268
372
  console.log(JSON.stringify(tables, null, 2));
269
373
  }
270
374
  else {
271
- console.log(`\n[ok] Found ${tables.length} tables:\n`);
375
+ console.log(`\n[ok] Found ${tables.length} tables (${duration}):\n`);
272
376
  tables.forEach((table) => {
273
377
  const fieldCount = table.fields.length;
274
378
  const relationCount = table.relations.belongsTo.length +
@@ -280,7 +384,8 @@ program
280
384
  }
281
385
  }
282
386
  catch (err) {
283
- console.error('x Failed to introspect schema:', err instanceof Error ? err.message : err);
387
+ const duration = formatDuration(performance.now() - startTime);
388
+ console.error('x Failed to introspect schema:', err instanceof Error ? err.message : err, `(${duration})`);
284
389
  process.exit(1);
285
390
  }
286
391
  });
@@ -10,6 +10,10 @@ export interface WatchOrchestratorOptions {
10
10
  generatorType: GeneratorType;
11
11
  verbose: boolean;
12
12
  authorization?: string;
13
+ /** Config file path for regeneration */
14
+ configPath?: string;
15
+ /** Target name for multi-target configs */
16
+ target?: string;
13
17
  /** Override output directory (for ORM) */
14
18
  outputDir?: string;
15
19
  /** Skip custom operations flag */
@@ -6,7 +6,7 @@
6
6
  import { SchemaPoller } from './poller';
7
7
  import { debounce } from './debounce';
8
8
  import { generateCommand } from '../commands/generate';
9
- import { generateOrmCommand } from '../commands/generate-orm';
9
+ import { generateOrmCommand, } from '../commands/generate-orm';
10
10
  /**
11
11
  * Main watch orchestrator class
12
12
  */
@@ -134,6 +134,8 @@ export class WatchOrchestrator {
134
134
  let result;
135
135
  if (this.options.generatorType === 'generate') {
136
136
  result = await generateCommand({
137
+ config: this.options.configPath,
138
+ target: this.options.target,
137
139
  endpoint: this.options.config.endpoint,
138
140
  output: this.options.outputDir ?? this.options.config.output,
139
141
  authorization: this.options.authorization,
@@ -143,6 +145,8 @@ export class WatchOrchestrator {
143
145
  }
144
146
  else {
145
147
  result = await generateOrmCommand({
148
+ config: this.options.configPath,
149
+ target: this.options.target,
146
150
  endpoint: this.options.config.endpoint,
147
151
  output: this.options.outputDir ?? this.options.config.orm?.output,
148
152
  authorization: this.options.authorization,
@@ -57,9 +57,10 @@ export interface QueryKeyConfig {
57
57
  generateMutationKeys?: boolean;
58
58
  }
59
59
  /**
60
- * Main configuration for graphql-codegen
60
+ * Target configuration for graphql-codegen
61
+ * Represents a single schema source and output destination.
61
62
  */
62
- export interface GraphQLSDKConfig {
63
+ export interface GraphQLSDKConfigTarget {
63
64
  /**
64
65
  * GraphQL endpoint URL for live introspection
65
66
  * Either endpoint or schema must be provided
@@ -179,6 +180,23 @@ export interface GraphQLSDKConfig {
179
180
  */
180
181
  watch?: WatchConfig;
181
182
  }
183
+ /**
184
+ * Multi-target configuration for graphql-codegen
185
+ */
186
+ export interface GraphQLSDKMultiConfig {
187
+ /**
188
+ * Shared defaults applied to every target
189
+ */
190
+ defaults?: GraphQLSDKConfigTarget;
191
+ /**
192
+ * Named target configurations
193
+ */
194
+ targets: Record<string, GraphQLSDKConfigTarget>;
195
+ }
196
+ /**
197
+ * Main configuration type for graphql-codegen
198
+ */
199
+ export type GraphQLSDKConfig = GraphQLSDKConfigTarget | GraphQLSDKMultiConfig;
182
200
  /**
183
201
  * Watch mode configuration options
184
202
  *
@@ -299,7 +317,26 @@ export declare const DEFAULT_ORM_CONFIG: {
299
317
  * Helper function to define configuration with type checking
300
318
  */
301
319
  export declare function defineConfig(config: GraphQLSDKConfig): GraphQLSDKConfig;
320
+ /**
321
+ * Resolved target configuration helper
322
+ */
323
+ export interface ResolvedTargetConfig {
324
+ name: string;
325
+ config: ResolvedConfig;
326
+ }
327
+ /**
328
+ * Type guard for multi-target configs
329
+ */
330
+ export declare function isMultiConfig(config: GraphQLSDKConfig): config is GraphQLSDKMultiConfig;
331
+ /**
332
+ * Merge two target configs (defaults + overrides)
333
+ */
334
+ export declare function mergeConfig(base: GraphQLSDKConfigTarget, overrides: GraphQLSDKConfigTarget): GraphQLSDKConfigTarget;
302
335
  /**
303
336
  * Resolve configuration by applying defaults
304
337
  */
305
338
  export declare function resolveConfig(config: GraphQLSDKConfig): ResolvedConfig;
339
+ /**
340
+ * Resolve all targets in a multi-target config
341
+ */
342
+ export declare function resolveConfigTargets(config: GraphQLSDKMultiConfig): ResolvedTargetConfig[];
@@ -71,10 +71,80 @@ export const DEFAULT_ORM_CONFIG = {
71
71
  export function defineConfig(config) {
72
72
  return config;
73
73
  }
74
+ /**
75
+ * Type guard for multi-target configs
76
+ */
77
+ export function isMultiConfig(config) {
78
+ const targets = config.targets;
79
+ return typeof targets === 'object' && targets !== null;
80
+ }
81
+ /**
82
+ * Merge two target configs (defaults + overrides)
83
+ */
84
+ export function mergeConfig(base, overrides) {
85
+ const headers = base.headers || overrides.headers
86
+ ? { ...(base.headers ?? {}), ...(overrides.headers ?? {}) }
87
+ : undefined;
88
+ const tables = base.tables || overrides.tables
89
+ ? { ...(base.tables ?? {}), ...(overrides.tables ?? {}) }
90
+ : undefined;
91
+ const queries = base.queries || overrides.queries
92
+ ? { ...(base.queries ?? {}), ...(overrides.queries ?? {}) }
93
+ : undefined;
94
+ const mutations = base.mutations || overrides.mutations
95
+ ? { ...(base.mutations ?? {}), ...(overrides.mutations ?? {}) }
96
+ : undefined;
97
+ const hooks = base.hooks || overrides.hooks
98
+ ? { ...(base.hooks ?? {}), ...(overrides.hooks ?? {}) }
99
+ : undefined;
100
+ const postgraphile = base.postgraphile || overrides.postgraphile
101
+ ? { ...(base.postgraphile ?? {}), ...(overrides.postgraphile ?? {}) }
102
+ : undefined;
103
+ const codegen = base.codegen || overrides.codegen
104
+ ? { ...(base.codegen ?? {}), ...(overrides.codegen ?? {}) }
105
+ : undefined;
106
+ const orm = base.orm || overrides.orm
107
+ ? { ...(base.orm ?? {}), ...(overrides.orm ?? {}) }
108
+ : undefined;
109
+ const reactQuery = base.reactQuery || overrides.reactQuery
110
+ ? { ...(base.reactQuery ?? {}), ...(overrides.reactQuery ?? {}) }
111
+ : undefined;
112
+ const queryKeys = base.queryKeys || overrides.queryKeys
113
+ ? {
114
+ ...(base.queryKeys ?? {}),
115
+ ...(overrides.queryKeys ?? {}),
116
+ relationships: {
117
+ ...(base.queryKeys?.relationships ?? {}),
118
+ ...(overrides.queryKeys?.relationships ?? {}),
119
+ },
120
+ }
121
+ : undefined;
122
+ const watch = base.watch || overrides.watch
123
+ ? { ...(base.watch ?? {}), ...(overrides.watch ?? {}) }
124
+ : undefined;
125
+ return {
126
+ ...base,
127
+ ...overrides,
128
+ headers,
129
+ tables,
130
+ queries,
131
+ mutations,
132
+ hooks,
133
+ postgraphile,
134
+ codegen,
135
+ orm,
136
+ reactQuery,
137
+ queryKeys,
138
+ watch,
139
+ };
140
+ }
74
141
  /**
75
142
  * Resolve configuration by applying defaults
76
143
  */
77
144
  export function resolveConfig(config) {
145
+ if (isMultiConfig(config)) {
146
+ throw new Error('Multi-target config cannot be resolved with resolveConfig(). Use resolveConfigTargets().');
147
+ }
78
148
  return {
79
149
  endpoint: config.endpoint ?? '',
80
150
  schema: config.schema ?? null,
@@ -116,10 +186,14 @@ export function resolveConfig(config) {
116
186
  },
117
187
  queryKeys: {
118
188
  style: config.queryKeys?.style ?? DEFAULT_QUERY_KEY_CONFIG.style,
119
- relationships: config.queryKeys?.relationships ?? DEFAULT_QUERY_KEY_CONFIG.relationships,
120
- generateScopedKeys: config.queryKeys?.generateScopedKeys ?? DEFAULT_QUERY_KEY_CONFIG.generateScopedKeys,
121
- generateCascadeHelpers: config.queryKeys?.generateCascadeHelpers ?? DEFAULT_QUERY_KEY_CONFIG.generateCascadeHelpers,
122
- generateMutationKeys: config.queryKeys?.generateMutationKeys ?? DEFAULT_QUERY_KEY_CONFIG.generateMutationKeys,
189
+ relationships: config.queryKeys?.relationships ??
190
+ DEFAULT_QUERY_KEY_CONFIG.relationships,
191
+ generateScopedKeys: config.queryKeys?.generateScopedKeys ??
192
+ DEFAULT_QUERY_KEY_CONFIG.generateScopedKeys,
193
+ generateCascadeHelpers: config.queryKeys?.generateCascadeHelpers ??
194
+ DEFAULT_QUERY_KEY_CONFIG.generateCascadeHelpers,
195
+ generateMutationKeys: config.queryKeys?.generateMutationKeys ??
196
+ DEFAULT_QUERY_KEY_CONFIG.generateMutationKeys,
123
197
  },
124
198
  watch: {
125
199
  pollInterval: config.watch?.pollInterval ?? DEFAULT_WATCH_CONFIG.pollInterval,
@@ -129,3 +203,13 @@ export function resolveConfig(config) {
129
203
  },
130
204
  };
131
205
  }
206
+ /**
207
+ * Resolve all targets in a multi-target config
208
+ */
209
+ export function resolveConfigTargets(config) {
210
+ const defaults = config.defaults ?? {};
211
+ return Object.entries(config.targets).map(([name, target]) => ({
212
+ name,
213
+ config: resolveConfig(mergeConfig(defaults, target)),
214
+ }));
215
+ }
@@ -5,5 +5,5 @@ export type { CleanTable, CleanField, CleanFieldType, CleanRelations, CleanBelon
5
5
  export type { PageInfo, ConnectionResult, QueryOptions, OrderByItem, FilterOperator, FieldFilter, RelationalFilter, Filter, } from './query';
6
6
  export type { MutationOptions, CreateInput, UpdateInput, DeleteInput, MutationResult, } from './mutation';
7
7
  export type { SimpleFieldSelection, FieldSelectionPreset, FieldSelection, SelectionOptions, } from './selection';
8
- export type { GraphQLSDKConfig, ResolvedConfig, } from './config';
9
- export { defineConfig, resolveConfig, DEFAULT_CONFIG } from './config';
8
+ export type { GraphQLSDKConfig, GraphQLSDKConfigTarget, GraphQLSDKMultiConfig, ResolvedConfig, ResolvedTargetConfig, } from './config';
9
+ export { defineConfig, resolveConfig, resolveConfigTargets, DEFAULT_CONFIG, } from './config';
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Type exports for @constructive-io/graphql-codegen
3
3
  */
4
- export { defineConfig, resolveConfig, DEFAULT_CONFIG } from './config';
4
+ export { defineConfig, resolveConfig, resolveConfigTargets, DEFAULT_CONFIG, } from './config';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-codegen",
3
- "version": "2.24.1",
3
+ "version": "2.26.0",
4
4
  "description": "CLI-based GraphQL SDK generator for PostGraphile endpoints with React Query hooks",
5
5
  "keywords": [
6
6
  "graphql",
@@ -39,25 +39,28 @@
39
39
  "build:dev": "makage build --dev",
40
40
  "dev": "ts-node ./src/index.ts",
41
41
  "lint": "eslint . --fix",
42
+ "fmt": "oxfmt",
43
+ "fmt:check": "oxfmt --check",
42
44
  "test": "jest --passWithNoTests",
43
45
  "test:watch": "jest --watch",
44
- "example:codegen:sdk": "node dist/cli/index.js generate --endpoint http://api.localhost:3000/graphql --output examples/output/generated-sdk",
45
- "example:codegen:orm": "node dist/cli/index.js generate-orm --endpoint http://api.localhost:3000/graphql --output examples/output/generated-orm",
46
+ "example:codegen:sdk": "tsx src/cli/index.ts generate --config examples/multi-target.config.ts",
47
+ "example:codegen:orm": "tsx src/cli/index.ts generate-orm --config examples/multi-target.config.ts",
46
48
  "example:codegen:sdk:schema": "node dist/cli/index.js generate --schema examples/example.schema.graphql --output examples/output/generated-sdk-schema",
47
49
  "example:codegen:orm:schema": "node dist/cli/index.js generate-orm --schema examples/example.schema.graphql --output examples/output/generated-orm-schema",
48
50
  "example:sdk": "tsx examples/react-query-sdk.ts",
49
- "example:orm": "tsx examples/orm-sdk.ts"
51
+ "example:orm": "tsx examples/orm-sdk.ts",
52
+ "example:sdk:typecheck": "tsc --noEmit --jsx react --esModuleInterop --skipLibCheck --moduleResolution node examples/react-hooks-test.tsx"
50
53
  },
51
54
  "dependencies": {
52
55
  "@babel/generator": "^7.28.5",
53
56
  "@babel/types": "^7.28.5",
54
57
  "ajv": "^8.17.1",
55
58
  "commander": "^12.1.0",
56
- "gql-ast": "^2.4.6",
59
+ "gql-ast": "^2.5.0",
57
60
  "graphql": "15.10.1",
58
61
  "inflekt": "^0.2.0",
59
62
  "jiti": "^2.6.1",
60
- "prettier": "^3.7.4"
63
+ "oxfmt": "^0.13.0"
61
64
  },
62
65
  "peerDependencies": {
63
66
  "@tanstack/react-query": "^5.0.0",
@@ -83,5 +86,5 @@
83
86
  "tsx": "^4.21.0",
84
87
  "typescript": "^5.9.3"
85
88
  },
86
- "gitHead": "3e08dd946ec5eab57c296e8df180f1dcb1d06514"
89
+ "gitHead": "b7abf2fdaf0a827d79a80c9c0a29e5c960f227df"
87
90
  }
package/types/config.d.ts CHANGED
@@ -57,9 +57,10 @@ export interface QueryKeyConfig {
57
57
  generateMutationKeys?: boolean;
58
58
  }
59
59
  /**
60
- * Main configuration for graphql-codegen
60
+ * Target configuration for graphql-codegen
61
+ * Represents a single schema source and output destination.
61
62
  */
62
- export interface GraphQLSDKConfig {
63
+ export interface GraphQLSDKConfigTarget {
63
64
  /**
64
65
  * GraphQL endpoint URL for live introspection
65
66
  * Either endpoint or schema must be provided
@@ -179,6 +180,23 @@ export interface GraphQLSDKConfig {
179
180
  */
180
181
  watch?: WatchConfig;
181
182
  }
183
+ /**
184
+ * Multi-target configuration for graphql-codegen
185
+ */
186
+ export interface GraphQLSDKMultiConfig {
187
+ /**
188
+ * Shared defaults applied to every target
189
+ */
190
+ defaults?: GraphQLSDKConfigTarget;
191
+ /**
192
+ * Named target configurations
193
+ */
194
+ targets: Record<string, GraphQLSDKConfigTarget>;
195
+ }
196
+ /**
197
+ * Main configuration type for graphql-codegen
198
+ */
199
+ export type GraphQLSDKConfig = GraphQLSDKConfigTarget | GraphQLSDKMultiConfig;
182
200
  /**
183
201
  * Watch mode configuration options
184
202
  *
@@ -299,7 +317,26 @@ export declare const DEFAULT_ORM_CONFIG: {
299
317
  * Helper function to define configuration with type checking
300
318
  */
301
319
  export declare function defineConfig(config: GraphQLSDKConfig): GraphQLSDKConfig;
320
+ /**
321
+ * Resolved target configuration helper
322
+ */
323
+ export interface ResolvedTargetConfig {
324
+ name: string;
325
+ config: ResolvedConfig;
326
+ }
327
+ /**
328
+ * Type guard for multi-target configs
329
+ */
330
+ export declare function isMultiConfig(config: GraphQLSDKConfig): config is GraphQLSDKMultiConfig;
331
+ /**
332
+ * Merge two target configs (defaults + overrides)
333
+ */
334
+ export declare function mergeConfig(base: GraphQLSDKConfigTarget, overrides: GraphQLSDKConfigTarget): GraphQLSDKConfigTarget;
302
335
  /**
303
336
  * Resolve configuration by applying defaults
304
337
  */
305
338
  export declare function resolveConfig(config: GraphQLSDKConfig): ResolvedConfig;
339
+ /**
340
+ * Resolve all targets in a multi-target config
341
+ */
342
+ export declare function resolveConfigTargets(config: GraphQLSDKMultiConfig): ResolvedTargetConfig[];