@constructive-io/graphql-codegen 4.0.1 → 4.1.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 (97) hide show
  1. package/cli/handler.d.ts +13 -0
  2. package/cli/handler.js +74 -0
  3. package/cli/index.js +11 -60
  4. package/cli/shared.d.ts +1 -1
  5. package/cli/shared.js +2 -5
  6. package/core/codegen/barrel.d.ts +1 -0
  7. package/core/codegen/barrel.js +5 -2
  8. package/core/codegen/cli/arg-mapper.d.ts +4 -0
  9. package/core/codegen/cli/arg-mapper.js +117 -0
  10. package/core/codegen/cli/command-map-generator.d.ts +16 -0
  11. package/core/codegen/cli/command-map-generator.js +338 -0
  12. package/core/codegen/cli/custom-command-generator.d.ts +8 -0
  13. package/core/codegen/cli/custom-command-generator.js +155 -0
  14. package/core/codegen/cli/docs-generator.d.ts +26 -0
  15. package/core/codegen/cli/docs-generator.js +1399 -0
  16. package/core/codegen/cli/executor-generator.d.ts +11 -0
  17. package/core/codegen/cli/executor-generator.js +217 -0
  18. package/core/codegen/cli/index.d.ts +53 -0
  19. package/core/codegen/cli/index.js +153 -0
  20. package/core/codegen/cli/infra-generator.d.ts +9 -0
  21. package/core/codegen/cli/infra-generator.js +1195 -0
  22. package/core/codegen/cli/table-command-generator.d.ts +7 -0
  23. package/core/codegen/cli/table-command-generator.js +323 -0
  24. package/core/codegen/docs-utils.d.ts +30 -0
  25. package/core/codegen/docs-utils.js +122 -0
  26. package/core/codegen/hooks-docs-generator.d.ts +6 -0
  27. package/core/codegen/hooks-docs-generator.js +468 -0
  28. package/core/codegen/orm/docs-generator.d.ts +6 -0
  29. package/core/codegen/orm/docs-generator.js +416 -0
  30. package/core/codegen/target-docs-generator.d.ts +20 -0
  31. package/core/codegen/target-docs-generator.js +110 -0
  32. package/core/database/index.d.ts +0 -12
  33. package/core/database/index.js +2 -19
  34. package/core/generate.d.ts +34 -2
  35. package/core/generate.js +453 -12
  36. package/core/index.d.ts +0 -2
  37. package/core/index.js +0 -2
  38. package/core/introspect/source/database.js +2 -2
  39. package/core/introspect/source/pgpm-module.js +2 -2
  40. package/core/output/index.d.ts +1 -1
  41. package/core/output/index.js +1 -2
  42. package/core/output/writer.d.ts +0 -10
  43. package/core/output/writer.js +0 -31
  44. package/esm/cli/handler.d.ts +13 -0
  45. package/esm/cli/handler.js +71 -0
  46. package/esm/cli/index.js +11 -60
  47. package/esm/cli/shared.d.ts +1 -1
  48. package/esm/cli/shared.js +2 -5
  49. package/esm/core/codegen/barrel.d.ts +1 -0
  50. package/esm/core/codegen/barrel.js +5 -2
  51. package/esm/core/codegen/cli/arg-mapper.d.ts +4 -0
  52. package/esm/core/codegen/cli/arg-mapper.js +80 -0
  53. package/esm/core/codegen/cli/command-map-generator.d.ts +16 -0
  54. package/esm/core/codegen/cli/command-map-generator.js +301 -0
  55. package/esm/core/codegen/cli/custom-command-generator.d.ts +8 -0
  56. package/esm/core/codegen/cli/custom-command-generator.js +119 -0
  57. package/esm/core/codegen/cli/docs-generator.d.ts +26 -0
  58. package/esm/core/codegen/cli/docs-generator.js +1387 -0
  59. package/esm/core/codegen/cli/executor-generator.d.ts +11 -0
  60. package/esm/core/codegen/cli/executor-generator.js +180 -0
  61. package/esm/core/codegen/cli/index.d.ts +53 -0
  62. package/esm/core/codegen/cli/index.js +128 -0
  63. package/esm/core/codegen/cli/infra-generator.d.ts +9 -0
  64. package/esm/core/codegen/cli/infra-generator.js +1156 -0
  65. package/esm/core/codegen/cli/table-command-generator.d.ts +7 -0
  66. package/esm/core/codegen/cli/table-command-generator.js +287 -0
  67. package/esm/core/codegen/docs-utils.d.ts +30 -0
  68. package/esm/core/codegen/docs-utils.js +112 -0
  69. package/esm/core/codegen/hooks-docs-generator.d.ts +6 -0
  70. package/esm/core/codegen/hooks-docs-generator.js +462 -0
  71. package/esm/core/codegen/orm/docs-generator.d.ts +6 -0
  72. package/esm/core/codegen/orm/docs-generator.js +410 -0
  73. package/esm/core/codegen/target-docs-generator.d.ts +20 -0
  74. package/esm/core/codegen/target-docs-generator.js +105 -0
  75. package/esm/core/database/index.d.ts +0 -12
  76. package/esm/core/database/index.js +1 -17
  77. package/esm/core/generate.d.ts +34 -2
  78. package/esm/core/generate.js +417 -12
  79. package/esm/core/index.d.ts +0 -2
  80. package/esm/core/index.js +0 -2
  81. package/esm/core/introspect/source/database.js +2 -2
  82. package/esm/core/introspect/source/pgpm-module.js +2 -2
  83. package/esm/core/output/index.d.ts +1 -1
  84. package/esm/core/output/index.js +1 -1
  85. package/esm/core/output/writer.d.ts +0 -10
  86. package/esm/core/output/writer.js +0 -30
  87. package/esm/generators/index.d.ts +0 -3
  88. package/esm/generators/index.js +0 -3
  89. package/esm/index.d.ts +4 -3
  90. package/esm/index.js +4 -2
  91. package/esm/types/config.d.ts +78 -0
  92. package/generators/index.d.ts +0 -3
  93. package/generators/index.js +0 -3
  94. package/index.d.ts +4 -3
  95. package/index.js +7 -2
  96. package/package.json +8 -7
  97. package/types/config.d.ts +78 -0
@@ -4,22 +4,27 @@
4
4
  * This is the primary entry point for programmatic usage.
5
5
  * The CLI is a thin wrapper around this function.
6
6
  */
7
+ import * as fs from 'node:fs';
7
8
  import path from 'node:path';
9
+ import { buildClientSchema, printSchema } from 'graphql';
10
+ import { PgpmPackage } from '@pgpmjs/core';
11
+ import { createEphemeralDb } from 'pgsql-client';
12
+ import { deployPgpm } from 'pgsql-seed';
8
13
  import { getConfigOptions } from '../types/config';
9
14
  import { generate as generateReactQueryFiles } from './codegen';
10
15
  import { generateRootBarrel } from './codegen/barrel';
16
+ import { generateCli as generateCliFiles, generateMultiTargetCli } from './codegen/cli';
17
+ import { generateReadme as generateCliReadme, generateAgentsDocs as generateCliAgentsDocs, getCliMcpTools, generateSkills as generateCliSkills, generateMultiTargetReadme, generateMultiTargetAgentsDocs, getMultiTargetCliMcpTools, generateMultiTargetSkills, } from './codegen/cli/docs-generator';
18
+ import { resolveDocsConfig } from './codegen/docs-utils';
19
+ import { generateHooksReadme, generateHooksAgentsDocs, getHooksMcpTools, generateHooksSkills, } from './codegen/hooks-docs-generator';
11
20
  import { generateOrm as generateOrmFiles } from './codegen/orm';
21
+ import { generateOrmReadme, generateOrmAgentsDocs, getOrmMcpTools, generateOrmSkills, } from './codegen/orm/docs-generator';
12
22
  import { generateSharedTypes } from './codegen/shared';
23
+ import { generateTargetReadme, generateCombinedMcpConfig, generateRootRootReadme, } from './codegen/target-docs-generator';
13
24
  import { createSchemaSource, validateSourceOptions } from './introspect';
14
25
  import { writeGeneratedFiles } from './output';
15
26
  import { runCodegenPipeline, validateTablesFound } from './pipeline';
16
- /**
17
- * Main generate function - takes a single config and generates code
18
- *
19
- * This is the primary entry point for programmatic usage.
20
- * For multiple configs, call this function in a loop.
21
- */
22
- export async function generate(options = {}) {
27
+ export async function generate(options = {}, internalOptions) {
23
28
  // Apply defaults to get resolved config
24
29
  const config = getConfigOptions(options);
25
30
  const outputRoot = config.output;
@@ -27,11 +32,12 @@ export async function generate(options = {}) {
27
32
  // ORM is always required when React Query is enabled (hooks delegate to ORM)
28
33
  // This handles minimist setting orm=false when --orm flag is absent
29
34
  const runReactQuery = config.reactQuery ?? false;
30
- const runOrm = runReactQuery || (options.orm !== undefined ? !!options.orm : false);
31
- if (!runReactQuery && !runOrm) {
35
+ const runCli = internalOptions?.skipCli ? false : !!config.cli;
36
+ const runOrm = runReactQuery || !!config.cli || (options.orm !== undefined ? !!options.orm : false);
37
+ if (!options.schemaOnly && !runReactQuery && !runOrm && !runCli) {
32
38
  return {
33
39
  success: false,
34
- message: 'No generators enabled. Use reactQuery: true or orm: true in your config.',
40
+ message: 'No generators enabled. Use reactQuery: true, orm: true, or cli: true in your config.',
35
41
  output: outputRoot,
36
42
  };
37
43
  }
@@ -55,6 +61,39 @@ export async function generate(options = {}) {
55
61
  authorization: options.authorization || config.headers?.Authorization,
56
62
  headers: config.headers,
57
63
  });
64
+ if (options.schemaOnly) {
65
+ try {
66
+ console.log(`Fetching schema from ${source.describe()}...`);
67
+ const { introspection } = await source.fetch();
68
+ const schema = buildClientSchema(introspection);
69
+ const sdl = printSchema(schema);
70
+ if (!sdl.trim()) {
71
+ return {
72
+ success: false,
73
+ message: 'Schema introspection returned empty SDL.',
74
+ output: outputRoot,
75
+ };
76
+ }
77
+ const outDir = path.resolve(options.schemaOnlyOutput || outputRoot || '.');
78
+ await fs.promises.mkdir(outDir, { recursive: true });
79
+ const filename = options.schemaOnlyFilename || 'schema.graphql';
80
+ const filePath = path.join(outDir, filename);
81
+ await fs.promises.writeFile(filePath, sdl, 'utf-8');
82
+ return {
83
+ success: true,
84
+ message: `Schema exported to ${filePath}`,
85
+ output: outDir,
86
+ filesWritten: [filePath],
87
+ };
88
+ }
89
+ catch (err) {
90
+ return {
91
+ success: false,
92
+ message: `Failed to export schema: ${err instanceof Error ? err.message : 'Unknown error'}`,
93
+ output: outputRoot,
94
+ };
95
+ }
96
+ }
58
97
  // Run pipeline
59
98
  let pipelineResult;
60
99
  try {
@@ -136,14 +175,115 @@ export async function generate(options = {}) {
136
175
  path: path.posix.join('orm', file.path),
137
176
  })));
138
177
  }
178
+ // Generate CLI commands
179
+ if (runCli) {
180
+ console.log('Generating CLI commands...');
181
+ const { files } = generateCliFiles({
182
+ tables,
183
+ customOperations: {
184
+ queries: customOperations.queries,
185
+ mutations: customOperations.mutations,
186
+ },
187
+ config,
188
+ });
189
+ filesToWrite.push(...files.map((file) => ({
190
+ path: path.posix.join('cli', file.fileName),
191
+ content: file.content,
192
+ })));
193
+ }
139
194
  // Generate barrel file at output root
140
- // This re-exports from the appropriate subdirectories based on which generators are enabled
141
195
  const barrelContent = generateRootBarrel({
142
196
  hasTypes: bothEnabled,
143
197
  hasHooks: runReactQuery,
144
198
  hasOrm: runOrm,
199
+ hasCli: runCli,
145
200
  });
146
201
  filesToWrite.push({ path: 'index.ts', content: barrelContent });
202
+ // Generate docs for each enabled generator
203
+ const docsConfig = resolveDocsConfig(config.docs);
204
+ const allCustomOps = [
205
+ ...(customOperations.queries ?? []),
206
+ ...(customOperations.mutations ?? []),
207
+ ];
208
+ const allMcpTools = [];
209
+ if (runOrm) {
210
+ if (docsConfig.readme) {
211
+ const readme = generateOrmReadme(tables, allCustomOps);
212
+ filesToWrite.push({ path: path.posix.join('orm', readme.fileName), content: readme.content });
213
+ }
214
+ if (docsConfig.agents) {
215
+ const agents = generateOrmAgentsDocs(tables, allCustomOps);
216
+ filesToWrite.push({ path: path.posix.join('orm', agents.fileName), content: agents.content });
217
+ }
218
+ if (docsConfig.mcp) {
219
+ allMcpTools.push(...getOrmMcpTools(tables, allCustomOps));
220
+ }
221
+ if (docsConfig.skills) {
222
+ for (const skill of generateOrmSkills(tables, allCustomOps)) {
223
+ filesToWrite.push({ path: path.posix.join('orm', skill.fileName), content: skill.content });
224
+ }
225
+ }
226
+ }
227
+ if (runReactQuery) {
228
+ if (docsConfig.readme) {
229
+ const readme = generateHooksReadme(tables, allCustomOps);
230
+ filesToWrite.push({ path: path.posix.join('hooks', readme.fileName), content: readme.content });
231
+ }
232
+ if (docsConfig.agents) {
233
+ const agents = generateHooksAgentsDocs(tables, allCustomOps);
234
+ filesToWrite.push({ path: path.posix.join('hooks', agents.fileName), content: agents.content });
235
+ }
236
+ if (docsConfig.mcp) {
237
+ allMcpTools.push(...getHooksMcpTools(tables, allCustomOps));
238
+ }
239
+ if (docsConfig.skills) {
240
+ for (const skill of generateHooksSkills(tables, allCustomOps)) {
241
+ filesToWrite.push({ path: path.posix.join('hooks', skill.fileName), content: skill.content });
242
+ }
243
+ }
244
+ }
245
+ if (runCli) {
246
+ const toolName = typeof config.cli === 'object' && config.cli?.toolName
247
+ ? config.cli.toolName
248
+ : 'app';
249
+ if (docsConfig.readme) {
250
+ const readme = generateCliReadme(tables, allCustomOps, toolName);
251
+ filesToWrite.push({ path: path.posix.join('cli', readme.fileName), content: readme.content });
252
+ }
253
+ if (docsConfig.agents) {
254
+ const agents = generateCliAgentsDocs(tables, allCustomOps, toolName);
255
+ filesToWrite.push({ path: path.posix.join('cli', agents.fileName), content: agents.content });
256
+ }
257
+ if (docsConfig.mcp) {
258
+ allMcpTools.push(...getCliMcpTools(tables, allCustomOps, toolName));
259
+ }
260
+ if (docsConfig.skills) {
261
+ for (const skill of generateCliSkills(tables, allCustomOps, toolName)) {
262
+ filesToWrite.push({ path: path.posix.join('cli', skill.fileName), content: skill.content });
263
+ }
264
+ }
265
+ }
266
+ // Generate combined mcp.json at output root
267
+ if (docsConfig.mcp && allMcpTools.length > 0) {
268
+ const mcpName = typeof config.cli === 'object' && config.cli?.toolName
269
+ ? config.cli.toolName
270
+ : 'graphql-sdk';
271
+ const mcpFile = generateCombinedMcpConfig(allMcpTools, mcpName);
272
+ filesToWrite.push({ path: mcpFile.fileName, content: mcpFile.content });
273
+ }
274
+ // Generate per-target README at output root
275
+ if (docsConfig.readme) {
276
+ const targetReadme = generateTargetReadme({
277
+ hasOrm: runOrm,
278
+ hasHooks: runReactQuery,
279
+ hasCli: runCli,
280
+ tableCount: tables.length,
281
+ customQueryCount: customOperations.queries.length,
282
+ customMutationCount: customOperations.mutations.length,
283
+ config,
284
+ });
285
+ filesToWrite.push({ path: targetReadme.fileName, content: targetReadme.content });
286
+ }
147
287
  if (!options.dryRun) {
148
288
  const writeResult = await writeGeneratedFiles(filesToWrite, outputRoot, [], {
149
289
  pruneStaleFiles: true,
@@ -158,7 +298,11 @@ export async function generate(options = {}) {
158
298
  }
159
299
  allFilesWritten.push(...(writeResult.filesWritten ?? []));
160
300
  }
161
- const generators = [runReactQuery && 'React Query', runOrm && 'ORM']
301
+ const generators = [
302
+ runReactQuery && 'React Query',
303
+ runOrm && 'ORM',
304
+ runCli && 'CLI',
305
+ ]
162
306
  .filter(Boolean)
163
307
  .join(' and ');
164
308
  return {
@@ -169,5 +313,266 @@ export async function generate(options = {}) {
169
313
  output: outputRoot,
170
314
  tables: tables.map((t) => t.name),
171
315
  filesWritten: allFilesWritten,
316
+ pipelineData: {
317
+ tables,
318
+ customOperations: {
319
+ queries: customOperations.queries,
320
+ mutations: customOperations.mutations,
321
+ },
322
+ },
323
+ };
324
+ }
325
+ export function expandApiNamesToMultiTarget(config) {
326
+ const apiNames = config.db?.apiNames;
327
+ if (!apiNames || apiNames.length <= 1)
328
+ return null;
329
+ const targets = {};
330
+ for (const apiName of apiNames) {
331
+ targets[apiName] = {
332
+ ...config,
333
+ db: {
334
+ ...config.db,
335
+ apiNames: [apiName],
336
+ },
337
+ output: config.output
338
+ ? `${config.output}/${apiName}`
339
+ : `./generated/graphql/${apiName}`,
340
+ };
341
+ }
342
+ return targets;
343
+ }
344
+ export function expandSchemaDirToMultiTarget(config) {
345
+ const schemaDir = config.schemaDir;
346
+ if (!schemaDir)
347
+ return null;
348
+ const resolvedDir = path.resolve(schemaDir);
349
+ if (!fs.existsSync(resolvedDir) || !fs.statSync(resolvedDir).isDirectory()) {
350
+ return null;
351
+ }
352
+ const graphqlFiles = fs.readdirSync(resolvedDir)
353
+ .filter((f) => f.endsWith('.graphql'))
354
+ .sort();
355
+ if (graphqlFiles.length === 0)
356
+ return null;
357
+ const targets = {};
358
+ for (const file of graphqlFiles) {
359
+ const name = path.basename(file, '.graphql');
360
+ targets[name] = {
361
+ ...config,
362
+ schemaDir: undefined,
363
+ schemaFile: path.join(resolvedDir, file),
364
+ output: config.output
365
+ ? `${config.output}/${name}`
366
+ : `./generated/graphql/${name}`,
367
+ };
368
+ }
369
+ return targets;
370
+ }
371
+ function getPgpmSourceKey(pgpm) {
372
+ if (pgpm.modulePath)
373
+ return `module:${path.resolve(pgpm.modulePath)}`;
374
+ if (pgpm.workspacePath && pgpm.moduleName)
375
+ return `workspace:${path.resolve(pgpm.workspacePath)}:${pgpm.moduleName}`;
376
+ return null;
377
+ }
378
+ function getModulePathFromPgpm(pgpm) {
379
+ if (pgpm.modulePath)
380
+ return pgpm.modulePath;
381
+ if (pgpm.workspacePath && pgpm.moduleName) {
382
+ const workspace = new PgpmPackage(pgpm.workspacePath);
383
+ const moduleProject = workspace.getModuleProject(pgpm.moduleName);
384
+ const modulePath = moduleProject.getModulePath();
385
+ if (!modulePath) {
386
+ throw new Error(`Module "${pgpm.moduleName}" not found in workspace`);
387
+ }
388
+ return modulePath;
389
+ }
390
+ throw new Error('Invalid PGPM config: requires modulePath or workspacePath+moduleName');
391
+ }
392
+ async function prepareSharedPgpmSources(configs, cliOverrides) {
393
+ const sharedSources = new Map();
394
+ const pgpmTargetCount = new Map();
395
+ for (const name of Object.keys(configs)) {
396
+ const merged = { ...configs[name], ...(cliOverrides ?? {}) };
397
+ const pgpm = merged.db?.pgpm;
398
+ if (!pgpm)
399
+ continue;
400
+ const key = getPgpmSourceKey(pgpm);
401
+ if (!key)
402
+ continue;
403
+ pgpmTargetCount.set(key, (pgpmTargetCount.get(key) ?? 0) + 1);
404
+ }
405
+ for (const [key, count] of pgpmTargetCount) {
406
+ if (count < 2)
407
+ continue;
408
+ let pgpmConfig;
409
+ for (const name of Object.keys(configs)) {
410
+ const merged = { ...configs[name], ...(cliOverrides ?? {}) };
411
+ const pgpm = merged.db?.pgpm;
412
+ if (pgpm && getPgpmSourceKey(pgpm) === key) {
413
+ pgpmConfig = pgpm;
414
+ break;
415
+ }
416
+ }
417
+ if (!pgpmConfig)
418
+ continue;
419
+ const ephemeralDb = createEphemeralDb({
420
+ prefix: 'codegen_pgpm_shared_',
421
+ verbose: false,
422
+ });
423
+ const modulePath = getModulePathFromPgpm(pgpmConfig);
424
+ await deployPgpm(ephemeralDb.config, modulePath, false);
425
+ sharedSources.set(key, {
426
+ key,
427
+ ephemeralDb,
428
+ deployed: true,
429
+ });
430
+ console.log(`[multi-target] Shared PGPM source deployed once for ${count} targets: ${key}`);
431
+ }
432
+ return sharedSources;
433
+ }
434
+ function applySharedPgpmDb(config, sharedSources) {
435
+ const pgpm = config.db?.pgpm;
436
+ if (!pgpm)
437
+ return config;
438
+ const key = getPgpmSourceKey(pgpm);
439
+ if (!key)
440
+ return config;
441
+ const shared = sharedSources.get(key);
442
+ if (!shared)
443
+ return config;
444
+ const sharedDbConfig = {
445
+ ...config.db,
446
+ pgpm: undefined,
447
+ config: shared.ephemeralDb.config,
448
+ keepDb: true,
449
+ };
450
+ return {
451
+ ...config,
452
+ db: sharedDbConfig,
172
453
  };
173
454
  }
455
+ export async function generateMulti(options) {
456
+ const { configs, cliOverrides, verbose, dryRun, schemaOnly, unifiedCli } = options;
457
+ const names = Object.keys(configs);
458
+ const results = [];
459
+ let hasError = false;
460
+ const targetInfos = [];
461
+ const useUnifiedCli = !schemaOnly && !!unifiedCli && names.length > 1;
462
+ const cliTargets = [];
463
+ const sharedSources = await prepareSharedPgpmSources(configs, cliOverrides);
464
+ try {
465
+ for (const name of names) {
466
+ const baseConfig = {
467
+ ...configs[name],
468
+ ...(cliOverrides ?? {}),
469
+ };
470
+ const targetConfig = applySharedPgpmDb(baseConfig, sharedSources);
471
+ const result = await generate({
472
+ ...targetConfig,
473
+ verbose,
474
+ dryRun,
475
+ schemaOnly,
476
+ schemaOnlyFilename: schemaOnly ? `${name}.graphql` : undefined,
477
+ }, useUnifiedCli ? { skipCli: true } : undefined);
478
+ results.push({ name, result });
479
+ if (!result.success) {
480
+ hasError = true;
481
+ }
482
+ else {
483
+ const resolvedConfig = getConfigOptions(targetConfig);
484
+ const gens = [];
485
+ if (resolvedConfig.reactQuery)
486
+ gens.push('React Query');
487
+ if (resolvedConfig.orm || resolvedConfig.reactQuery || !!resolvedConfig.cli)
488
+ gens.push('ORM');
489
+ if (resolvedConfig.cli)
490
+ gens.push('CLI');
491
+ targetInfos.push({
492
+ name,
493
+ output: resolvedConfig.output,
494
+ endpoint: resolvedConfig.endpoint || undefined,
495
+ generators: gens,
496
+ });
497
+ if (useUnifiedCli && result.pipelineData) {
498
+ const isAuthTarget = name === 'auth';
499
+ cliTargets.push({
500
+ name,
501
+ endpoint: resolvedConfig.endpoint || '',
502
+ ormImportPath: `../${resolvedConfig.output.replace(/^\.\//, '')}/orm`,
503
+ tables: result.pipelineData.tables,
504
+ customOperations: result.pipelineData.customOperations,
505
+ isAuthTarget,
506
+ });
507
+ }
508
+ }
509
+ }
510
+ if (useUnifiedCli && cliTargets.length > 0 && !dryRun) {
511
+ const cliConfig = typeof unifiedCli === 'object' ? unifiedCli : {};
512
+ const toolName = cliConfig.toolName ?? 'app';
513
+ const { files } = generateMultiTargetCli({
514
+ toolName,
515
+ builtinNames: cliConfig.builtinNames,
516
+ targets: cliTargets,
517
+ });
518
+ const cliFilesToWrite = files.map((file) => ({
519
+ path: path.posix.join('cli', file.fileName),
520
+ content: file.content,
521
+ }));
522
+ const firstTargetDocsConfig = names.length > 0 && configs[names[0]]?.docs;
523
+ const docsConfig = resolveDocsConfig(firstTargetDocsConfig);
524
+ const { resolveBuiltinNames } = await import('./codegen/cli');
525
+ const builtinNames = resolveBuiltinNames(cliTargets.map((t) => t.name), cliConfig.builtinNames);
526
+ const docsInput = {
527
+ toolName,
528
+ builtinNames,
529
+ targets: cliTargets.map((t) => ({
530
+ name: t.name,
531
+ endpoint: t.endpoint,
532
+ tables: t.tables,
533
+ customOperations: [
534
+ ...(t.customOperations?.queries ?? []),
535
+ ...(t.customOperations?.mutations ?? []),
536
+ ],
537
+ isAuthTarget: t.isAuthTarget,
538
+ })),
539
+ };
540
+ const allMcpTools = [];
541
+ if (docsConfig.readme) {
542
+ const readme = generateMultiTargetReadme(docsInput);
543
+ cliFilesToWrite.push({ path: path.posix.join('cli', readme.fileName), content: readme.content });
544
+ }
545
+ if (docsConfig.agents) {
546
+ const agents = generateMultiTargetAgentsDocs(docsInput);
547
+ cliFilesToWrite.push({ path: path.posix.join('cli', agents.fileName), content: agents.content });
548
+ }
549
+ if (docsConfig.mcp) {
550
+ allMcpTools.push(...getMultiTargetCliMcpTools(docsInput));
551
+ }
552
+ if (docsConfig.skills) {
553
+ for (const skill of generateMultiTargetSkills(docsInput)) {
554
+ cliFilesToWrite.push({ path: path.posix.join('cli', skill.fileName), content: skill.content });
555
+ }
556
+ }
557
+ if (docsConfig.mcp && allMcpTools.length > 0) {
558
+ const mcpFile = generateCombinedMcpConfig(allMcpTools, toolName);
559
+ cliFilesToWrite.push({ path: path.posix.join('cli', mcpFile.fileName), content: mcpFile.content });
560
+ }
561
+ const { writeGeneratedFiles: writeFiles } = await import('./output');
562
+ await writeFiles(cliFilesToWrite, '.', [], { pruneStaleFiles: false });
563
+ }
564
+ // Generate root-root README if multi-target
565
+ if (names.length > 1 && targetInfos.length > 0 && !dryRun) {
566
+ const rootReadme = generateRootRootReadme(targetInfos);
567
+ const { writeGeneratedFiles: writeFiles } = await import('./output');
568
+ await writeFiles([{ path: rootReadme.fileName, content: rootReadme.content }], '.', [], { pruneStaleFiles: false });
569
+ }
570
+ }
571
+ finally {
572
+ for (const shared of sharedSources.values()) {
573
+ const keepDb = Object.values(configs).some((c) => c.db?.keepDb);
574
+ shared.ephemeralDb.teardown({ keepDb });
575
+ }
576
+ }
577
+ return { results, hasError };
578
+ }
@@ -9,9 +9,7 @@ export { generate } from './generate';
9
9
  export * from './types';
10
10
  export * from './ast';
11
11
  export * from './custom-ast';
12
- /** @deprecated Legacy v4 query builder — use v5 ORM codegen instead */
13
12
  export { MetaObject, QueryBuilder } from './query-builder';
14
- /** @deprecated Legacy v4 meta-object utilities — v5 uses standard introspection */
15
13
  export { convertFromMetaSchema, validateMetaObject } from './meta-object';
16
14
  export * from './config';
17
15
  export * from './codegen';
package/esm/core/index.js CHANGED
@@ -11,10 +11,8 @@ export * from './types';
11
11
  export * from './ast';
12
12
  export * from './custom-ast';
13
13
  // Query builder
14
- /** @deprecated Legacy v4 query builder — use v5 ORM codegen instead */
15
14
  export { MetaObject, QueryBuilder } from './query-builder';
16
15
  // Meta object utilities
17
- /** @deprecated Legacy v4 meta-object utilities — v5 uses standard introspection */
18
16
  export { convertFromMetaSchema, validateMetaObject } from './meta-object';
19
17
  // Configuration loading and resolution
20
18
  export * from './config';
@@ -5,7 +5,7 @@
5
5
  * introspection and converts it to introspection format.
6
6
  */
7
7
  import { buildSchema, introspectionFromSchema } from 'graphql';
8
- import { buildSchemaSDLFromDatabase } from '../../database';
8
+ import { buildSchemaSDL } from 'graphile-schema';
9
9
  import { createDatabasePool, resolveApiSchemas, validateServicesSchemas, } from './api-schemas';
10
10
  import { SchemaSourceError } from './types';
11
11
  /**
@@ -45,7 +45,7 @@ export class DatabaseSchemaSource {
45
45
  // Build SDL from database
46
46
  let sdl;
47
47
  try {
48
- sdl = await buildSchemaSDLFromDatabase({
48
+ sdl = await buildSchemaSDL({
49
49
  database,
50
50
  schemas,
51
51
  });
@@ -12,7 +12,7 @@ import { buildSchema, introspectionFromSchema } from 'graphql';
12
12
  import { getPgPool } from 'pg-cache';
13
13
  import { createEphemeralDb } from 'pgsql-client';
14
14
  import { deployPgpm } from 'pgsql-seed';
15
- import { buildSchemaSDLFromDatabase } from '../../database';
15
+ import { buildSchemaSDL } from 'graphile-schema';
16
16
  import { resolveApiSchemas, validateServicesSchemas } from './api-schemas';
17
17
  import { SchemaSourceError } from './types';
18
18
  /**
@@ -98,7 +98,7 @@ export class PgpmModuleSchemaSource {
98
98
  // Build SDL from the deployed database
99
99
  let sdl;
100
100
  try {
101
- sdl = await buildSchemaSDLFromDatabase({
101
+ sdl = await buildSchemaSDL({
102
102
  database: dbConfig.database,
103
103
  schemas,
104
104
  });
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Output module exports
3
3
  */
4
- export { formatOutput, type GeneratedFile, writeGeneratedFiles, type WriteOptions, type WriteResult, } from './writer';
4
+ export { type GeneratedFile, writeGeneratedFiles, type WriteOptions, type WriteResult, } from './writer';
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Output module exports
3
3
  */
4
- export { formatOutput, writeGeneratedFiles, } from './writer';
4
+ export { writeGeneratedFiles, } from './writer';
@@ -29,13 +29,3 @@ export interface WriteOptions {
29
29
  * @param options - Write options
30
30
  */
31
31
  export declare function writeGeneratedFiles(files: GeneratedFile[], outputDir: string, subdirs: string[], options?: WriteOptions): Promise<WriteResult>;
32
- /**
33
- * Format generated files using oxfmt programmatically
34
- *
35
- * @deprecated Use writeGeneratedFiles with formatFiles option instead.
36
- * This function is kept for backwards compatibility.
37
- */
38
- export declare function formatOutput(outputDir: string): Promise<{
39
- success: boolean;
40
- error?: string;
41
- }>;
@@ -168,33 +168,3 @@ function findTsFiles(dir) {
168
168
  }
169
169
  return files;
170
170
  }
171
- /**
172
- * Format generated files using oxfmt programmatically
173
- *
174
- * @deprecated Use writeGeneratedFiles with formatFiles option instead.
175
- * This function is kept for backwards compatibility.
176
- */
177
- export async function formatOutput(outputDir) {
178
- const formatFn = await getOxfmtFormat();
179
- if (!formatFn) {
180
- return {
181
- success: false,
182
- error: 'oxfmt not available. Install it with: npm install oxfmt',
183
- };
184
- }
185
- const absoluteOutputDir = path.resolve(outputDir);
186
- try {
187
- // Find all .ts files in the output directory
188
- const tsFiles = findTsFiles(absoluteOutputDir);
189
- for (const filePath of tsFiles) {
190
- const content = fs.readFileSync(filePath, 'utf-8');
191
- const formatted = await formatFileContent(path.basename(filePath), content, formatFn);
192
- fs.writeFileSync(filePath, formatted, 'utf-8');
193
- }
194
- return { success: true };
195
- }
196
- catch (err) {
197
- const message = err instanceof Error ? err.message : String(err);
198
- return { success: false, error: message };
199
- }
200
- }
@@ -1,8 +1,5 @@
1
1
  /**
2
2
  * Query and mutation generator exports
3
- *
4
- * @deprecated Legacy v4 generators — use v5 ORM codegen pipeline instead.
5
- * These are retained for backward compatibility with existing v4 consumers.
6
3
  */
7
4
  export { convertToSelectionOptions, getAvailableRelations, isRelationalField, validateFieldSelection, } from './field-selector';
8
5
  export { buildCount, buildFindOne, buildSelect, cleanTableToMetaObject, createASTQueryBuilder, generateIntrospectionSchema, toCamelCasePlural, toOrderByTypeName, } from './select';
@@ -1,8 +1,5 @@
1
1
  /**
2
2
  * Query and mutation generator exports
3
- *
4
- * @deprecated Legacy v4 generators — use v5 ORM codegen pipeline instead.
5
- * These are retained for backward compatibility with existing v4 consumers.
6
3
  */
7
4
  // Field selector utilities
8
5
  export { convertToSelectionOptions, getAvailableRelations, isRelationalField, validateFieldSelection, } from './field-selector';
package/esm/index.d.ts CHANGED
@@ -10,10 +10,11 @@ export * from './core';
10
10
  export * from './generators';
11
11
  export * from './client';
12
12
  export { defineConfig } from './types/config';
13
- export type { GenerateOptions, GenerateResult } from './core/generate';
14
- export { generate } from './core/generate';
13
+ export type { GenerateOptions, GenerateResult, GenerateMultiOptions, GenerateMultiResult } from './core/generate';
14
+ export { generate, generateMulti, expandApiNamesToMultiTarget, expandSchemaDirToMultiTarget } from './core/generate';
15
15
  export { findConfigFile, loadConfigFile } from './core/config';
16
+ export { runCodegenHandler } from './cli/handler';
16
17
  export type { CodegenAnswers } from './cli/shared';
17
18
  export { buildDbConfig, buildGenerateOptions, camelizeArgv, codegenQuestions, filterDefined, flattenDbFields, hasResolvedCodegenSource, hyphenateKeys, normalizeCodegenListOptions, printResult, seedArgvFromConfig, splitCommas, } from './cli/shared';
18
19
  export type { BuildSchemaFromDatabaseOptions, BuildSchemaFromDatabaseResult, } from './core/database';
19
- export { buildSchemaFromDatabase, buildSchemaSDLFromDatabase, } from './core/database';
20
+ export { buildSchemaFromDatabase } from './core/database';
package/esm/index.js CHANGED
@@ -15,8 +15,10 @@ export * from './generators';
15
15
  export * from './client';
16
16
  // Config definition helper
17
17
  export { defineConfig } from './types/config';
18
- export { generate } from './core/generate';
18
+ export { generate, generateMulti, expandApiNamesToMultiTarget, expandSchemaDirToMultiTarget } from './core/generate';
19
19
  // Config utilities
20
20
  export { findConfigFile, loadConfigFile } from './core/config';
21
+ // CLI shared utilities (for packages/cli to import)
22
+ export { runCodegenHandler } from './cli/handler';
21
23
  export { buildDbConfig, buildGenerateOptions, camelizeArgv, codegenQuestions, filterDefined, flattenDbFields, hasResolvedCodegenSource, hyphenateKeys, normalizeCodegenListOptions, printResult, seedArgvFromConfig, splitCommas, } from './cli/shared';
22
- export { buildSchemaFromDatabase, buildSchemaSDLFromDatabase, } from './core/database';
24
+ export { buildSchemaFromDatabase } from './core/database';