@constructive-io/graphql-codegen 2.22.1 → 2.23.1

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 (99) hide show
  1. package/cli/codegen/barrel.d.ts +5 -1
  2. package/cli/codegen/barrel.js +13 -11
  3. package/cli/codegen/index.d.ts +3 -3
  4. package/cli/codegen/index.js +15 -9
  5. package/cli/codegen/orm/client-generator.js +3 -2
  6. package/cli/codegen/orm/custom-ops-generator.js +17 -4
  7. package/cli/codegen/orm/input-types-generator.js +129 -18
  8. package/cli/codegen/orm/model-generator.js +2 -1
  9. package/cli/codegen/orm/query-builder.d.ts +1 -1
  10. package/cli/codegen/orm/query-builder.js +2 -2
  11. package/cli/codegen/schema-types-generator.js +5 -5
  12. package/cli/codegen/utils.d.ts +6 -1
  13. package/cli/codegen/utils.js +23 -8
  14. package/cli/commands/generate-orm.d.ts +5 -3
  15. package/cli/commands/generate-orm.js +65 -84
  16. package/cli/commands/generate.d.ts +2 -0
  17. package/cli/commands/generate.js +66 -87
  18. package/cli/commands/shared.d.ts +74 -0
  19. package/cli/commands/shared.js +88 -0
  20. package/cli/index.js +75 -45
  21. package/cli/introspect/index.d.ts +8 -5
  22. package/cli/introspect/index.js +19 -7
  23. package/cli/introspect/infer-tables.d.ts +51 -0
  24. package/cli/introspect/infer-tables.js +550 -0
  25. package/cli/introspect/source/endpoint.d.ts +34 -0
  26. package/cli/introspect/source/endpoint.js +35 -0
  27. package/cli/introspect/source/file.d.ts +20 -0
  28. package/cli/introspect/source/file.js +103 -0
  29. package/cli/introspect/source/index.d.ts +48 -0
  30. package/cli/introspect/source/index.js +72 -0
  31. package/cli/introspect/source/types.d.ts +58 -0
  32. package/cli/introspect/source/types.js +27 -0
  33. package/cli/introspect/transform.d.ts +5 -6
  34. package/cli/introspect/transform.js +0 -173
  35. package/cli/watch/cache.d.ts +3 -4
  36. package/cli/watch/cache.js +6 -10
  37. package/cli/watch/poller.d.ts +1 -2
  38. package/cli/watch/poller.js +27 -45
  39. package/cli/watch/types.d.ts +0 -3
  40. package/core/ast.js +4 -4
  41. package/core/query-builder.js +12 -12
  42. package/esm/cli/codegen/barrel.d.ts +5 -1
  43. package/esm/cli/codegen/barrel.js +13 -11
  44. package/esm/cli/codegen/index.d.ts +3 -3
  45. package/esm/cli/codegen/index.js +18 -12
  46. package/esm/cli/codegen/orm/client-generator.js +3 -2
  47. package/esm/cli/codegen/orm/custom-ops-generator.js +18 -5
  48. package/esm/cli/codegen/orm/input-types-generator.js +130 -19
  49. package/esm/cli/codegen/orm/model-generator.js +3 -2
  50. package/esm/cli/codegen/orm/query-builder.d.ts +1 -1
  51. package/esm/cli/codegen/orm/query-builder.js +2 -2
  52. package/esm/cli/codegen/schema-types-generator.js +6 -6
  53. package/esm/cli/codegen/utils.d.ts +6 -1
  54. package/esm/cli/codegen/utils.js +22 -8
  55. package/esm/cli/commands/generate-orm.d.ts +5 -3
  56. package/esm/cli/commands/generate-orm.js +65 -84
  57. package/esm/cli/commands/generate.d.ts +2 -0
  58. package/esm/cli/commands/generate.js +66 -87
  59. package/esm/cli/commands/shared.d.ts +74 -0
  60. package/esm/cli/commands/shared.js +84 -0
  61. package/esm/cli/index.js +76 -46
  62. package/esm/cli/introspect/index.d.ts +8 -5
  63. package/esm/cli/introspect/index.js +10 -3
  64. package/esm/cli/introspect/infer-tables.d.ts +51 -0
  65. package/esm/cli/introspect/infer-tables.js +547 -0
  66. package/esm/cli/introspect/source/endpoint.d.ts +34 -0
  67. package/esm/cli/introspect/source/endpoint.js +31 -0
  68. package/esm/cli/introspect/source/file.d.ts +20 -0
  69. package/esm/cli/introspect/source/file.js +66 -0
  70. package/esm/cli/introspect/source/index.d.ts +48 -0
  71. package/esm/cli/introspect/source/index.js +54 -0
  72. package/esm/cli/introspect/source/types.d.ts +58 -0
  73. package/esm/cli/introspect/source/types.js +23 -0
  74. package/esm/cli/introspect/transform.d.ts +5 -6
  75. package/esm/cli/introspect/transform.js +0 -172
  76. package/esm/cli/watch/cache.d.ts +3 -4
  77. package/esm/cli/watch/cache.js +7 -11
  78. package/esm/cli/watch/poller.d.ts +1 -2
  79. package/esm/cli/watch/poller.js +28 -46
  80. package/esm/cli/watch/types.d.ts +0 -3
  81. package/esm/core/ast.js +4 -4
  82. package/esm/core/query-builder.js +12 -12
  83. package/esm/generators/mutations.js +3 -3
  84. package/esm/generators/select.js +7 -7
  85. package/esm/types/config.d.ts +21 -5
  86. package/esm/types/config.js +2 -1
  87. package/generators/mutations.js +3 -3
  88. package/generators/select.js +7 -7
  89. package/package.json +5 -3
  90. package/types/config.d.ts +21 -5
  91. package/types/config.js +2 -1
  92. package/cli/introspect/fetch-meta.d.ts +0 -31
  93. package/cli/introspect/fetch-meta.js +0 -108
  94. package/cli/introspect/meta-query.d.ts +0 -111
  95. package/cli/introspect/meta-query.js +0 -191
  96. package/esm/cli/introspect/fetch-meta.d.ts +0 -31
  97. package/esm/cli/introspect/fetch-meta.js +0 -104
  98. package/esm/cli/introspect/meta-query.d.ts +0 -111
  99. package/esm/cli/introspect/meta-query.js +0 -188
@@ -1,20 +1,17 @@
1
1
  /**
2
- * Generate command - introspects endpoint and generates SDK
2
+ * Generate command - generates SDK from GraphQL schema
3
3
  *
4
4
  * This command:
5
- * 1. Fetches _meta query for table-based CRUD operations
6
- * 2. Fetches __schema introspection for ALL operations
7
- * 3. Filters out table operations from custom operations to avoid duplicates
8
- * 4. Generates hooks for both table CRUD and custom operations
5
+ * 1. Fetches schema from endpoint or loads from file
6
+ * 2. Infers table metadata from introspection (replaces _meta)
7
+ * 3. Generates hooks for both table CRUD and custom operations
9
8
  */
10
9
  import * as fs from 'node:fs';
11
10
  import * as path from 'node:path';
12
11
  import * as prettier from 'prettier';
13
12
  import { resolveConfig } from '../../types/config';
14
- import { fetchMeta, validateEndpoint } from '../introspect/fetch-meta';
15
- import { fetchSchema } from '../introspect/fetch-schema';
16
- import { transformMetaToCleanTables, filterTables, } from '../introspect/transform';
17
- import { transformSchemaToOperations, filterOperations, getTableOperationNames, getCustomOperations, } from '../introspect/transform-schema';
13
+ import { createSchemaSource, validateSourceOptions, } from '../introspect/source';
14
+ import { runCodegenPipeline, validateTablesFound } from './shared';
18
15
  import { findConfigFile, loadConfigFile } from './init';
19
16
  import { generate } from '../codegen';
20
17
  /**
@@ -32,110 +29,90 @@ export async function generateCommand(options = {}) {
32
29
  };
33
30
  }
34
31
  const config = configResult.config;
35
- log(` Endpoint: ${config.endpoint}`);
32
+ // Log source
33
+ if (config.schema) {
34
+ log(` Schema: ${config.schema}`);
35
+ }
36
+ else {
37
+ log(` Endpoint: ${config.endpoint}`);
38
+ }
36
39
  log(` Output: ${config.output}`);
37
- // 2. Validate endpoint
38
- const endpointValidation = validateEndpoint(config.endpoint);
39
- if (!endpointValidation.valid) {
40
+ // 2. Create schema source
41
+ const sourceValidation = validateSourceOptions({
42
+ endpoint: config.endpoint || undefined,
43
+ schema: config.schema || undefined,
44
+ });
45
+ if (!sourceValidation.valid) {
40
46
  return {
41
47
  success: false,
42
- message: `Invalid endpoint: ${endpointValidation.error}`,
48
+ message: sourceValidation.error,
43
49
  };
44
50
  }
45
- // Build authorization header if provided
46
- const authHeader = options.authorization || config.headers['Authorization'];
47
- // 3. Fetch _meta for table-based operations
48
- log('Fetching schema metadata (_meta)...');
49
- const metaResult = await fetchMeta({
50
- endpoint: config.endpoint,
51
- authorization: authHeader,
51
+ const source = createSchemaSource({
52
+ endpoint: config.endpoint || undefined,
53
+ schema: config.schema || undefined,
54
+ authorization: options.authorization || config.headers['Authorization'],
52
55
  headers: config.headers,
53
- timeout: 30000,
54
56
  });
55
- if (!metaResult.success) {
57
+ // 3. Run the codegen pipeline
58
+ let pipelineResult;
59
+ try {
60
+ pipelineResult = await runCodegenPipeline({
61
+ source,
62
+ config,
63
+ verbose: options.verbose,
64
+ skipCustomOperations: options.skipCustomOperations,
65
+ });
66
+ }
67
+ catch (err) {
56
68
  return {
57
69
  success: false,
58
- message: `Failed to fetch _meta: ${metaResult.error}`,
70
+ message: `Failed to fetch schema: ${err instanceof Error ? err.message : 'Unknown error'}`,
59
71
  };
60
72
  }
61
- // 4. Transform to CleanTable[]
62
- log('Transforming table schema...');
63
- let tables = transformMetaToCleanTables(metaResult.data);
64
- log(` Found ${tables.length} tables`);
65
- // 5. Filter tables
66
- tables = filterTables(tables, config.tables.include, config.tables.exclude);
67
- log(` After filtering: ${tables.length} tables`);
68
- if (tables.length === 0) {
73
+ const { tables, customOperations, stats } = pipelineResult;
74
+ // 4. Validate tables found
75
+ const tablesValidation = validateTablesFound(tables);
76
+ if (!tablesValidation.valid) {
69
77
  return {
70
78
  success: false,
71
- message: 'No tables found after filtering. Check your include/exclude patterns.',
79
+ message: tablesValidation.error,
72
80
  };
73
81
  }
74
- // Get table operation names for filtering custom operations
75
- const tableOperationNames = getTableOperationNames(tables);
76
- // 6. Fetch __schema for custom operations (unless skipped)
77
- let customQueries = [];
78
- let customMutations = [];
79
- let customOperationsData;
80
- if (!options.skipCustomOperations) {
81
- log('Fetching schema introspection (__schema)...');
82
- const schemaResult = await fetchSchema({
83
- endpoint: config.endpoint,
84
- authorization: authHeader,
85
- headers: config.headers,
86
- timeout: 30000,
87
- });
88
- if (schemaResult.success && schemaResult.data) {
89
- log('Transforming custom operations...');
90
- // Transform to CleanOperation[]
91
- const { queries: allQueries, mutations: allMutations, typeRegistry } = transformSchemaToOperations(schemaResult.data);
92
- log(` Found ${allQueries.length} queries and ${allMutations.length} mutations total`);
93
- // Filter by config include/exclude
94
- const filteredQueries = filterOperations(allQueries, config.queries.include, config.queries.exclude);
95
- const filteredMutations = filterOperations(allMutations, config.mutations.include, config.mutations.exclude);
96
- log(` After config filtering: ${filteredQueries.length} queries, ${filteredMutations.length} mutations`);
97
- // Remove table operations (already handled by table generators)
98
- const customQueriesOps = getCustomOperations(filteredQueries, tableOperationNames);
99
- const customMutationsOps = getCustomOperations(filteredMutations, tableOperationNames);
100
- log(` Custom operations: ${customQueriesOps.length} queries, ${customMutationsOps.length} mutations`);
101
- customQueries = customQueriesOps.map((q) => q.name);
102
- customMutations = customMutationsOps.map((m) => m.name);
103
- customOperationsData = {
104
- queries: customQueriesOps,
105
- mutations: customMutationsOps,
106
- typeRegistry,
107
- };
108
- }
109
- else {
110
- log(` Warning: Could not fetch __schema: ${schemaResult.error}`);
111
- log(' Continuing with table-only generation...');
112
- }
113
- }
114
- // 7. Generate code
82
+ // 5. Generate code
115
83
  console.log('Generating code...');
116
- const { files: generatedFiles, stats } = generate({
84
+ const { files: generatedFiles, stats: genStats } = generate({
117
85
  tables,
118
- customOperations: customOperationsData,
86
+ customOperations: {
87
+ queries: customOperations.queries,
88
+ mutations: customOperations.mutations,
89
+ typeRegistry: customOperations.typeRegistry,
90
+ },
119
91
  config,
120
92
  });
121
- console.log(`Generated ${stats.totalFiles} files`);
122
- log(` ${stats.queryHooks} table query hooks`);
123
- log(` ${stats.mutationHooks} table mutation hooks`);
124
- log(` ${stats.customQueryHooks} custom query hooks`);
125
- log(` ${stats.customMutationHooks} custom mutation hooks`);
93
+ console.log(`Generated ${genStats.totalFiles} files`);
94
+ log(` ${genStats.queryHooks} table query hooks`);
95
+ log(` ${genStats.mutationHooks} table mutation hooks`);
96
+ log(` ${genStats.customQueryHooks} custom query hooks`);
97
+ log(` ${genStats.customMutationHooks} custom mutation hooks`);
98
+ const customQueries = customOperations.queries.map((q) => q.name);
99
+ const customMutations = customOperations.mutations.map((m) => m.name);
126
100
  if (options.dryRun) {
127
101
  return {
128
102
  success: true,
129
- message: `Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${customQueries.length + customMutations.length} custom operations.`,
103
+ message: `Dry run complete. Would generate ${generatedFiles.length} files for ${tables.length} tables and ${stats.customQueries + stats.customMutations} custom operations.`,
130
104
  tables: tables.map((t) => t.name),
131
105
  customQueries,
132
106
  customMutations,
133
107
  filesWritten: generatedFiles.map((f) => f.path),
134
108
  };
135
109
  }
136
- // 8. Write files
110
+ // 6. Write files
137
111
  log('Writing files...');
138
- const writeResult = await writeGeneratedFiles(generatedFiles, config.output, ['queries', 'mutations']);
112
+ const writeResult = await writeGeneratedFiles(generatedFiles, config.output, [
113
+ 'queries',
114
+ 'mutations',
115
+ ]);
139
116
  if (!writeResult.success) {
140
117
  return {
141
118
  success: false,
@@ -170,7 +147,8 @@ async function loadConfig(options) {
170
147
  }
171
148
  // Override with CLI options
172
149
  const mergedConfig = {
173
- endpoint: options.endpoint || baseConfig.endpoint || '',
150
+ endpoint: options.endpoint || baseConfig.endpoint,
151
+ schema: options.schema || baseConfig.schema,
174
152
  output: options.output || baseConfig.output,
175
153
  headers: baseConfig.headers,
176
154
  tables: baseConfig.tables,
@@ -181,10 +159,11 @@ async function loadConfig(options) {
181
159
  postgraphile: baseConfig.postgraphile,
182
160
  codegen: baseConfig.codegen,
183
161
  };
184
- if (!mergedConfig.endpoint) {
162
+ // Validate at least one source is provided
163
+ if (!mergedConfig.endpoint && !mergedConfig.schema) {
185
164
  return {
186
165
  success: false,
187
- error: 'No endpoint specified. Use --endpoint or create a config file with "graphql-codegen init".',
166
+ error: 'No source specified. Use --endpoint or --schema, or create a config file with "graphql-codegen init".',
188
167
  };
189
168
  }
190
169
  // Resolve with defaults
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Shared Codegen Pipeline
3
+ *
4
+ * Consolidates the common logic between generate and generate-orm commands.
5
+ * Handles:
6
+ * - Schema fetching from source (endpoint or file)
7
+ * - Table inference from introspection
8
+ * - Operation transformation
9
+ * - Filtering
10
+ */
11
+ import type { ResolvedConfig } from '../../types/config';
12
+ import type { CleanTable, CleanOperation, TypeRegistry } from '../../types/schema';
13
+ import type { SchemaSource } from '../introspect/source';
14
+ export interface CodegenPipelineOptions {
15
+ /**
16
+ * Schema source (endpoint or file)
17
+ */
18
+ source: SchemaSource;
19
+ /**
20
+ * Resolved configuration
21
+ */
22
+ config: ResolvedConfig;
23
+ /**
24
+ * Enable verbose logging
25
+ */
26
+ verbose?: boolean;
27
+ /**
28
+ * Skip custom operations (only generate table CRUD)
29
+ */
30
+ skipCustomOperations?: boolean;
31
+ }
32
+ export interface CodegenPipelineResult {
33
+ /**
34
+ * Inferred table metadata
35
+ */
36
+ tables: CleanTable[];
37
+ /**
38
+ * Custom operations (queries and mutations not covered by tables)
39
+ */
40
+ customOperations: {
41
+ queries: CleanOperation[];
42
+ mutations: CleanOperation[];
43
+ typeRegistry: TypeRegistry;
44
+ };
45
+ /**
46
+ * Statistics about what was found
47
+ */
48
+ stats: {
49
+ totalTables: number;
50
+ filteredTables: number;
51
+ totalQueries: number;
52
+ totalMutations: number;
53
+ customQueries: number;
54
+ customMutations: number;
55
+ };
56
+ }
57
+ /**
58
+ * Run the unified codegen pipeline
59
+ *
60
+ * This replaces the duplicated logic in generate.ts and generate-orm.ts:
61
+ * 1. Fetch introspection from source (endpoint or file)
62
+ * 2. Infer tables from introspection (replaces _meta query)
63
+ * 3. Transform to operations
64
+ * 4. Filter by config
65
+ * 5. Separate table operations from custom operations
66
+ */
67
+ export declare function runCodegenPipeline(options: CodegenPipelineOptions): Promise<CodegenPipelineResult>;
68
+ /**
69
+ * Validate that tables were found
70
+ */
71
+ export declare function validateTablesFound(tables: CleanTable[]): {
72
+ valid: boolean;
73
+ error?: string;
74
+ };
@@ -0,0 +1,84 @@
1
+ import { inferTablesFromIntrospection } from '../introspect/infer-tables';
2
+ import { filterTables } from '../introspect/transform';
3
+ import { transformSchemaToOperations, filterOperations, getTableOperationNames, getCustomOperations, } from '../introspect/transform-schema';
4
+ // ============================================================================
5
+ // Main Pipeline
6
+ // ============================================================================
7
+ /**
8
+ * Run the unified codegen pipeline
9
+ *
10
+ * This replaces the duplicated logic in generate.ts and generate-orm.ts:
11
+ * 1. Fetch introspection from source (endpoint or file)
12
+ * 2. Infer tables from introspection (replaces _meta query)
13
+ * 3. Transform to operations
14
+ * 4. Filter by config
15
+ * 5. Separate table operations from custom operations
16
+ */
17
+ export async function runCodegenPipeline(options) {
18
+ const { source, config, verbose = false, skipCustomOperations = false, } = options;
19
+ const log = verbose ? console.log : () => { };
20
+ // 1. Fetch introspection from source
21
+ log(`Fetching schema from ${source.describe()}...`);
22
+ const { introspection } = await source.fetch();
23
+ // 2. Infer tables from introspection (replaces _meta)
24
+ log('Inferring table metadata from schema...');
25
+ let tables = inferTablesFromIntrospection(introspection);
26
+ const totalTables = tables.length;
27
+ log(` Found ${totalTables} tables`);
28
+ // 3. Filter tables by config
29
+ tables = filterTables(tables, config.tables.include, config.tables.exclude);
30
+ const filteredTables = tables.length;
31
+ log(` After filtering: ${filteredTables} tables`);
32
+ // 4. Transform introspection to operations
33
+ log('Transforming operations...');
34
+ const { queries: allQueries, mutations: allMutations, typeRegistry, } = transformSchemaToOperations(introspection);
35
+ const totalQueries = allQueries.length;
36
+ const totalMutations = allMutations.length;
37
+ log(` Found ${totalQueries} queries and ${totalMutations} mutations total`);
38
+ // 5. Get table operation names for filtering custom ops
39
+ const tableOperationNames = getTableOperationNames(tables);
40
+ // 6. Filter and separate custom operations
41
+ let customQueries = [];
42
+ let customMutations = [];
43
+ if (!skipCustomOperations) {
44
+ // Filter by config include/exclude
45
+ const filteredQueries = filterOperations(allQueries, config.queries.include, config.queries.exclude);
46
+ const filteredMutations = filterOperations(allMutations, config.mutations.include, config.mutations.exclude);
47
+ log(` After config filtering: ${filteredQueries.length} queries, ${filteredMutations.length} mutations`);
48
+ // Remove table operations (already handled by table generators)
49
+ customQueries = getCustomOperations(filteredQueries, tableOperationNames);
50
+ customMutations = getCustomOperations(filteredMutations, tableOperationNames);
51
+ log(` Custom operations: ${customQueries.length} queries, ${customMutations.length} mutations`);
52
+ }
53
+ return {
54
+ tables,
55
+ customOperations: {
56
+ queries: customQueries,
57
+ mutations: customMutations,
58
+ typeRegistry,
59
+ },
60
+ stats: {
61
+ totalTables,
62
+ filteredTables,
63
+ totalQueries,
64
+ totalMutations,
65
+ customQueries: customQueries.length,
66
+ customMutations: customMutations.length,
67
+ },
68
+ };
69
+ }
70
+ // ============================================================================
71
+ // Validation Helpers
72
+ // ============================================================================
73
+ /**
74
+ * Validate that tables were found
75
+ */
76
+ export function validateTablesFound(tables) {
77
+ if (tables.length === 0) {
78
+ return {
79
+ valid: false,
80
+ error: 'No tables found after filtering. Check your include/exclude patterns.',
81
+ };
82
+ }
83
+ return { valid: true };
84
+ }
package/esm/cli/index.js CHANGED
@@ -6,7 +6,7 @@ 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 { resolveConfig, } from '../types/config';
10
10
  const program = new Command();
11
11
  /**
12
12
  * Load configuration for watch mode, merging CLI options with config file
@@ -21,7 +21,7 @@ async function loadWatchConfig(options) {
21
21
  if (configPath) {
22
22
  const loadResult = await loadConfigFile(configPath);
23
23
  if (!loadResult.success) {
24
- console.error('', loadResult.error);
24
+ console.error('x', loadResult.error);
25
25
  return null;
26
26
  }
27
27
  baseConfig = loadResult.config;
@@ -42,21 +42,23 @@ async function loadWatchConfig(options) {
42
42
  watch: {
43
43
  ...baseConfig.watch,
44
44
  // CLI options override config
45
- ...(options.pollInterval !== undefined && { pollInterval: options.pollInterval }),
45
+ ...(options.pollInterval !== undefined && {
46
+ pollInterval: options.pollInterval,
47
+ }),
46
48
  ...(options.debounce !== undefined && { debounce: options.debounce }),
47
49
  ...(options.touch !== undefined && { touchFile: options.touch }),
48
50
  ...(options.clear !== undefined && { clearScreen: options.clear }),
49
51
  },
50
52
  };
51
53
  if (!mergedConfig.endpoint) {
52
- console.error(' No endpoint specified. Use --endpoint or create a config file with "graphql-codegen init".');
54
+ console.error('x No endpoint specified. Use --endpoint or create a config file with "graphql-codegen init".');
53
55
  return null;
54
56
  }
55
57
  return resolveConfig(mergedConfig);
56
58
  }
57
59
  program
58
60
  .name('graphql-codegen')
59
- .description('CLI for generating GraphQL SDK from PostGraphile endpoints')
61
+ .description('CLI for generating GraphQL SDK from PostGraphile endpoints or schema files')
60
62
  .version('2.17.48');
61
63
  // Init command
62
64
  program
@@ -74,19 +76,20 @@ program
74
76
  output: options.output,
75
77
  });
76
78
  if (result.success) {
77
- console.log('', result.message);
79
+ console.log('[ok]', result.message);
78
80
  }
79
81
  else {
80
- console.error('', result.message);
82
+ console.error('x', result.message);
81
83
  process.exit(1);
82
84
  }
83
85
  });
84
86
  // Generate command
85
87
  program
86
88
  .command('generate')
87
- .description('Generate SDK from GraphQL endpoint')
89
+ .description('Generate SDK from GraphQL endpoint or schema file')
88
90
  .option('-c, --config <path>', 'Path to config file')
89
91
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL (overrides config)')
92
+ .option('-s, --schema <path>', 'Path to GraphQL schema file (.graphql)')
90
93
  .option('-o, --output <dir>', 'Output directory (overrides config)')
91
94
  .option('-a, --authorization <header>', 'Authorization header value')
92
95
  .option('-v, --verbose', 'Verbose output', false)
@@ -97,8 +100,17 @@ program
97
100
  .option('--touch <file>', 'File to touch on schema change')
98
101
  .option('--no-clear', 'Do not clear terminal on regeneration')
99
102
  .action(async (options) => {
100
- // Watch mode
103
+ // Validate source options
104
+ if (options.endpoint && options.schema) {
105
+ console.error('x Cannot use both --endpoint and --schema. Choose one source.');
106
+ process.exit(1);
107
+ }
108
+ // Watch mode (only for endpoint)
101
109
  if (options.watch) {
110
+ if (options.schema) {
111
+ console.error('x Watch mode is only supported with --endpoint, not --schema.');
112
+ process.exit(1);
113
+ }
102
114
  const config = await loadWatchConfig(options);
103
115
  if (!config) {
104
116
  process.exit(1);
@@ -116,13 +128,14 @@ program
116
128
  const result = await generateCommand({
117
129
  config: options.config,
118
130
  endpoint: options.endpoint,
131
+ schema: options.schema,
119
132
  output: options.output,
120
133
  authorization: options.authorization,
121
134
  verbose: options.verbose,
122
135
  dryRun: options.dryRun,
123
136
  });
124
137
  if (result.success) {
125
- console.log('', result.message);
138
+ console.log('[ok]', result.message);
126
139
  if (result.tables && result.tables.length > 0) {
127
140
  console.log('\nTables:');
128
141
  result.tables.forEach((t) => console.log(` - ${t}`));
@@ -133,7 +146,7 @@ program
133
146
  }
134
147
  }
135
148
  else {
136
- console.error('', result.message);
149
+ console.error('x', result.message);
137
150
  if (result.errors) {
138
151
  result.errors.forEach((e) => console.error(' -', e));
139
152
  }
@@ -143,9 +156,10 @@ program
143
156
  // Generate ORM command
144
157
  program
145
158
  .command('generate-orm')
146
- .description('Generate Prisma-like ORM client from GraphQL endpoint')
159
+ .description('Generate Prisma-like ORM client from GraphQL endpoint or schema file')
147
160
  .option('-c, --config <path>', 'Path to config file')
148
161
  .option('-e, --endpoint <url>', 'GraphQL endpoint URL (overrides config)')
162
+ .option('-s, --schema <path>', 'Path to GraphQL schema file (.graphql)')
149
163
  .option('-o, --output <dir>', 'Output directory (overrides config)', './generated/orm')
150
164
  .option('-a, --authorization <header>', 'Authorization header value')
151
165
  .option('-v, --verbose', 'Verbose output', false)
@@ -157,8 +171,17 @@ program
157
171
  .option('--touch <file>', 'File to touch on schema change')
158
172
  .option('--no-clear', 'Do not clear terminal on regeneration')
159
173
  .action(async (options) => {
160
- // Watch mode
174
+ // Validate source options
175
+ if (options.endpoint && options.schema) {
176
+ console.error('x Cannot use both --endpoint and --schema. Choose one source.');
177
+ process.exit(1);
178
+ }
179
+ // Watch mode (only for endpoint)
161
180
  if (options.watch) {
181
+ if (options.schema) {
182
+ console.error('x Watch mode is only supported with --endpoint, not --schema.');
183
+ process.exit(1);
184
+ }
162
185
  const config = await loadWatchConfig(options);
163
186
  if (!config) {
164
187
  process.exit(1);
@@ -177,6 +200,7 @@ program
177
200
  const result = await generateOrmCommand({
178
201
  config: options.config,
179
202
  endpoint: options.endpoint,
203
+ schema: options.schema,
180
204
  output: options.output,
181
205
  authorization: options.authorization,
182
206
  verbose: options.verbose,
@@ -184,7 +208,7 @@ program
184
208
  skipCustomOperations: options.skipCustomOperations,
185
209
  });
186
210
  if (result.success) {
187
- console.log('', result.message);
211
+ console.log('[ok]', result.message);
188
212
  if (result.tables && result.tables.length > 0) {
189
213
  console.log('\nTables:');
190
214
  result.tables.forEach((t) => console.log(` - ${t}`));
@@ -203,54 +227,60 @@ program
203
227
  }
204
228
  }
205
229
  else {
206
- console.error('', result.message);
230
+ console.error('x', result.message);
207
231
  if (result.errors) {
208
232
  result.errors.forEach((e) => console.error(' -', e));
209
233
  }
210
234
  process.exit(1);
211
235
  }
212
236
  });
213
- // Introspect command (for debugging)
237
+ // Introspect command (for debugging) - uses the new inference system
214
238
  program
215
239
  .command('introspect')
216
- .description('Introspect a GraphQL endpoint and print table info')
217
- .requiredOption('-e, --endpoint <url>', 'GraphQL endpoint URL')
240
+ .description('Introspect a GraphQL endpoint or schema file and print table info')
241
+ .option('-e, --endpoint <url>', 'GraphQL endpoint URL')
242
+ .option('-s, --schema <path>', 'Path to GraphQL schema file (.graphql)')
218
243
  .option('-a, --authorization <header>', 'Authorization header value')
219
244
  .option('--json', 'Output as JSON', false)
220
245
  .action(async (options) => {
221
- const { fetchMeta, validateEndpoint } = await import('./introspect/fetch-meta');
222
- const { transformMetaToCleanTables, getTableNames } = await import('./introspect/transform');
223
- // Validate endpoint
224
- const validation = validateEndpoint(options.endpoint);
225
- if (!validation.valid) {
226
- console.error('✗ Invalid endpoint:', validation.error);
246
+ // Validate source options
247
+ if (!options.endpoint && !options.schema) {
248
+ console.error('x Either --endpoint or --schema must be provided.');
227
249
  process.exit(1);
228
250
  }
229
- console.log('Fetching schema from', options.endpoint, '...');
230
- const result = await fetchMeta({
231
- endpoint: options.endpoint,
232
- authorization: options.authorization,
233
- });
234
- if (!result.success) {
235
- console.error('✗ Failed to fetch schema:', result.error);
251
+ if (options.endpoint && options.schema) {
252
+ console.error('x Cannot use both --endpoint and --schema. Choose one source.');
236
253
  process.exit(1);
237
254
  }
238
- const tables = transformMetaToCleanTables(result.data);
239
- const tableNames = getTableNames(tables);
240
- if (options.json) {
241
- console.log(JSON.stringify(tables, null, 2));
242
- }
243
- else {
244
- console.log(`\n✓ Found ${tables.length} tables:\n`);
245
- tableNames.forEach((name) => {
246
- const table = tables.find((t) => t.name === name);
247
- const fieldCount = table.fields.length;
248
- const relationCount = table.relations.belongsTo.length +
249
- table.relations.hasOne.length +
250
- table.relations.hasMany.length +
251
- table.relations.manyToMany.length;
252
- console.log(` ${name} (${fieldCount} fields, ${relationCount} relations)`);
255
+ const { createSchemaSource } = await import('./introspect/source');
256
+ const { inferTablesFromIntrospection } = await import('./introspect/infer-tables');
257
+ try {
258
+ const source = createSchemaSource({
259
+ endpoint: options.endpoint,
260
+ schema: options.schema,
261
+ authorization: options.authorization,
253
262
  });
263
+ console.log('Fetching schema from', source.describe(), '...');
264
+ const { introspection } = await source.fetch();
265
+ const tables = inferTablesFromIntrospection(introspection);
266
+ if (options.json) {
267
+ console.log(JSON.stringify(tables, null, 2));
268
+ }
269
+ else {
270
+ console.log(`\n[ok] Found ${tables.length} tables:\n`);
271
+ tables.forEach((table) => {
272
+ const fieldCount = table.fields.length;
273
+ const relationCount = table.relations.belongsTo.length +
274
+ table.relations.hasOne.length +
275
+ table.relations.hasMany.length +
276
+ table.relations.manyToMany.length;
277
+ console.log(` ${table.name} (${fieldCount} fields, ${relationCount} relations)`);
278
+ });
279
+ }
280
+ }
281
+ catch (err) {
282
+ console.error('x Failed to introspect schema:', err instanceof Error ? err.message : err);
283
+ process.exit(1);
254
284
  }
255
285
  });
256
286
  program.parse();
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Introspection module exports
3
3
  */
4
- export { META_QUERY } from './meta-query';
5
- export type { MetaQueryResponse, MetaTable, MetaField, MetaFieldType, MetaConstraint, MetaForeignKeyConstraint, MetaTableQuery, MetaTableInflection, MetaBelongsToRelation, MetaHasRelation, MetaManyToManyRelation, MetaTableRelations, } from './meta-query';
6
- export { fetchMeta, validateEndpoint } from './fetch-meta';
7
- export type { FetchMetaOptions, FetchMetaResult } from './fetch-meta';
8
- export { transformMetaToCleanTables, getTableNames, findTable, filterTables, } from './transform';
4
+ export { inferTablesFromIntrospection } from './infer-tables';
5
+ export type { InferTablesOptions } from './infer-tables';
6
+ export { singularize, pluralize } from 'inflekt';
7
+ export { createSchemaSource, validateSourceOptions, EndpointSchemaSource, FileSchemaSource, SchemaSourceError, } from './source';
8
+ export type { SchemaSource, SchemaSourceResult, CreateSchemaSourceOptions, } from './source';
9
+ export { fetchSchema } from './fetch-schema';
10
+ export type { FetchSchemaOptions, FetchSchemaResult } from './fetch-schema';
11
+ export { getTableNames, findTable, filterTables } from './transform';
@@ -1,6 +1,13 @@
1
1
  /**
2
2
  * Introspection module exports
3
3
  */
4
- export { META_QUERY } from './meta-query';
5
- export { fetchMeta, validateEndpoint } from './fetch-meta';
6
- export { transformMetaToCleanTables, getTableNames, findTable, filterTables, } from './transform';
4
+ // Table inference from introspection
5
+ export { inferTablesFromIntrospection } from './infer-tables';
6
+ // Pluralization utilities (from inflekt)
7
+ export { singularize, pluralize } from 'inflekt';
8
+ // Schema sources
9
+ export { createSchemaSource, validateSourceOptions, EndpointSchemaSource, FileSchemaSource, SchemaSourceError, } from './source';
10
+ // Schema fetching (still used by watch mode)
11
+ export { fetchSchema } from './fetch-schema';
12
+ // Transform utilities (only filterTables, getTableNames, findTable are still useful)
13
+ export { getTableNames, findTable, filterTables } from './transform';