@constructive-io/cli 0.0.3 → 5.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 (130) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +346 -153
  3. package/commands/{analyze.d.ts → codegen.d.ts} +2 -2
  4. package/commands/codegen.js +131 -0
  5. package/commands/explorer.js +94 -0
  6. package/commands/get-graphql-schema.js +73 -0
  7. package/commands/server.js +187 -0
  8. package/commands.d.ts +0 -1
  9. package/commands.js +42 -61
  10. package/esm/commands/codegen.js +126 -0
  11. package/esm/commands/explorer.js +92 -0
  12. package/esm/commands/get-graphql-schema.js +71 -0
  13. package/esm/commands/server.js +185 -0
  14. package/esm/commands.js +41 -59
  15. package/esm/index.js +16 -36
  16. package/esm/utils/display.js +26 -23
  17. package/esm/utils/index.js +3 -6
  18. package/index.d.ts +0 -22
  19. package/index.js +16 -72
  20. package/package.json +50 -25
  21. package/utils/display.d.ts +1 -1
  22. package/utils/display.js +26 -23
  23. package/utils/index.d.ts +3 -6
  24. package/utils/index.js +7 -20
  25. package/commands/add.d.ts +0 -7
  26. package/commands/add.js +0 -86
  27. package/commands/admin-users/add.d.ts +0 -4
  28. package/commands/admin-users/add.js +0 -89
  29. package/commands/admin-users/bootstrap.d.ts +0 -4
  30. package/commands/admin-users/bootstrap.js +0 -50
  31. package/commands/admin-users/remove.d.ts +0 -4
  32. package/commands/admin-users/remove.js +0 -82
  33. package/commands/admin-users.d.ts +0 -4
  34. package/commands/admin-users.js +0 -68
  35. package/commands/analyze.js +0 -21
  36. package/commands/clear.d.ts +0 -3
  37. package/commands/clear.js +0 -59
  38. package/commands/deploy.d.ts +0 -4
  39. package/commands/deploy.js +0 -146
  40. package/commands/docker.d.ts +0 -3
  41. package/commands/docker.js +0 -194
  42. package/commands/env.d.ts +0 -4
  43. package/commands/env.js +0 -124
  44. package/commands/export.js +0 -129
  45. package/commands/extension.js +0 -48
  46. package/commands/init/index.d.ts +0 -7
  47. package/commands/init/index.js +0 -47
  48. package/commands/init/module.d.ts +0 -4
  49. package/commands/init/module.js +0 -71
  50. package/commands/init/workspace.d.ts +0 -4
  51. package/commands/init/workspace.js +0 -52
  52. package/commands/install.d.ts +0 -4
  53. package/commands/install.js +0 -37
  54. package/commands/kill.js +0 -107
  55. package/commands/migrate/deps.d.ts +0 -4
  56. package/commands/migrate/deps.js +0 -186
  57. package/commands/migrate/init.d.ts +0 -4
  58. package/commands/migrate/init.js +0 -65
  59. package/commands/migrate/list.d.ts +0 -4
  60. package/commands/migrate/list.js +0 -85
  61. package/commands/migrate/status.d.ts +0 -4
  62. package/commands/migrate/status.js +0 -94
  63. package/commands/migrate.d.ts +0 -4
  64. package/commands/migrate.js +0 -69
  65. package/commands/package.d.ts +0 -3
  66. package/commands/package.js +0 -65
  67. package/commands/plan.d.ts +0 -3
  68. package/commands/plan.js +0 -62
  69. package/commands/remove.d.ts +0 -3
  70. package/commands/remove.js +0 -42
  71. package/commands/rename.d.ts +0 -4
  72. package/commands/rename.js +0 -35
  73. package/commands/revert.d.ts +0 -3
  74. package/commands/revert.js +0 -107
  75. package/commands/tag.d.ts +0 -6
  76. package/commands/tag.js +0 -168
  77. package/commands/verify.d.ts +0 -3
  78. package/commands/verify.js +0 -85
  79. package/dist/README.md +0 -412
  80. package/dist/package.json +0 -64
  81. package/esm/commands/add.js +0 -51
  82. package/esm/commands/admin-users/add.js +0 -87
  83. package/esm/commands/admin-users/bootstrap.js +0 -48
  84. package/esm/commands/admin-users/remove.js +0 -80
  85. package/esm/commands/admin-users.js +0 -63
  86. package/esm/commands/analyze.js +0 -16
  87. package/esm/commands/clear.js +0 -54
  88. package/esm/commands/deploy.js +0 -144
  89. package/esm/commands/docker.js +0 -192
  90. package/esm/commands/env.js +0 -122
  91. package/esm/commands/export.js +0 -127
  92. package/esm/commands/extension.js +0 -46
  93. package/esm/commands/init/index.js +0 -42
  94. package/esm/commands/init/module.js +0 -68
  95. package/esm/commands/init/workspace.js +0 -46
  96. package/esm/commands/install.js +0 -35
  97. package/esm/commands/kill.js +0 -105
  98. package/esm/commands/migrate/deps.js +0 -184
  99. package/esm/commands/migrate/init.js +0 -63
  100. package/esm/commands/migrate/list.js +0 -83
  101. package/esm/commands/migrate/status.js +0 -92
  102. package/esm/commands/migrate.js +0 -64
  103. package/esm/commands/package.js +0 -63
  104. package/esm/commands/plan.js +0 -60
  105. package/esm/commands/remove.js +0 -40
  106. package/esm/commands/rename.js +0 -30
  107. package/esm/commands/revert.js +0 -105
  108. package/esm/commands/tag.js +0 -133
  109. package/esm/commands/verify.js +0 -83
  110. package/esm/package.js +0 -26
  111. package/esm/utils/argv.js +0 -92
  112. package/esm/utils/cli-error.js +0 -48
  113. package/esm/utils/database.js +0 -78
  114. package/esm/utils/deployed-changes.js +0 -68
  115. package/esm/utils/module-utils.js +0 -51
  116. package/package.d.ts +0 -1
  117. package/package.js +0 -29
  118. package/utils/argv.d.ts +0 -46
  119. package/utils/argv.js +0 -100
  120. package/utils/cli-error.d.ts +0 -8
  121. package/utils/cli-error.js +0 -52
  122. package/utils/database.d.ts +0 -21
  123. package/utils/database.js +0 -83
  124. package/utils/deployed-changes.d.ts +0 -4
  125. package/utils/deployed-changes.js +0 -72
  126. package/utils/module-utils.d.ts +0 -8
  127. package/utils/module-utils.js +0 -54
  128. /package/commands/{export.d.ts → explorer.d.ts} +0 -0
  129. /package/commands/{extension.d.ts → get-graphql-schema.d.ts} +0 -0
  130. /package/commands/{kill.d.ts → server.d.ts} +0 -0
@@ -0,0 +1,126 @@
1
+ import { promises as fs } from 'fs';
2
+ import { join } from 'path';
3
+ import yaml from 'js-yaml';
4
+ import { runCodegen, defaultGraphQLCodegenOptions, mergeGraphQLCodegenOptions } from '@constructive-io/graphql-codegen';
5
+ import { fetchEndpointSchemaSDL } from '@constructive-io/graphql-server';
6
+ const usage = `
7
+ Constructive GraphQL Codegen:
8
+
9
+ cnc codegen [OPTIONS]
10
+
11
+ Options:
12
+ --help, -h Show this help message
13
+ --config <path> Config file (json|yaml)
14
+ --schema <path> Schema SDL file path
15
+ --endpoint <url> GraphQL endpoint to fetch schema via introspection
16
+ --headerHost <host> Optional Host header to send with endpoint requests
17
+ --auth <token> Optional Authorization header value (e.g., "Bearer 123")
18
+ --header "Name: Value" Optional HTTP header; repeat to add multiple headers
19
+ --out <dir> Output root directory (default: graphql/codegen/dist)
20
+ --format <gql|ts> Document format (default: gql)
21
+ --convention <style> Filename convention (dashed|underscore|camelcase|camelUpper)
22
+ --emitTypes <bool> Emit types (default: true)
23
+ --emitOperations <bool> Emit operations (default: true)
24
+ --emitSdk <bool> Emit sdk (default: true)
25
+ --allowQuery <name> Only generate for this root field (repeatable)
26
+ --excludeQuery <name> Exclude this root field (repeatable)
27
+ --excludePattern <regex> Exclude fields matching regex (repeatable)
28
+ `;
29
+ function parseBool(v, d) {
30
+ if (v === undefined)
31
+ return d;
32
+ if (typeof v === 'boolean')
33
+ return v;
34
+ const s = String(v).toLowerCase();
35
+ if (s === 'true')
36
+ return true;
37
+ if (s === 'false')
38
+ return false;
39
+ return d;
40
+ }
41
+ async function loadConfig(path) {
42
+ const content = await fs.readFile(path, 'utf8');
43
+ if (/\.ya?ml$/i.test(path))
44
+ return yaml.load(content);
45
+ if (/\.json$/i.test(path))
46
+ return JSON.parse(content);
47
+ return {};
48
+ }
49
+ export default async (argv, _prompter, _options) => {
50
+ if (argv.help || argv.h) {
51
+ console.log(usage);
52
+ process.exit(0);
53
+ }
54
+ const cwd = argv.cwd || process.cwd();
55
+ const configPath = argv.config || '';
56
+ let fileOpts = {};
57
+ if (configPath)
58
+ fileOpts = await loadConfig(configPath);
59
+ const overrides = {};
60
+ if (argv.schema)
61
+ overrides.input = { ...(overrides.input || {}), schema: String(argv.schema) };
62
+ if (argv.endpoint)
63
+ overrides.input = { ...(overrides.input || {}), endpoint: String(argv.endpoint) };
64
+ const headerHost = argv.headerHost ?? '';
65
+ const auth = argv.auth ?? '';
66
+ const headerArg = argv.header;
67
+ const headerList = Array.isArray(headerArg) ? headerArg : headerArg ? [headerArg] : [];
68
+ const headers = {};
69
+ if (auth)
70
+ headers['Authorization'] = auth;
71
+ for (const h of headerList) {
72
+ const idx = typeof h === 'string' ? h.indexOf(':') : -1;
73
+ if (idx <= 0)
74
+ continue;
75
+ const name = h.slice(0, idx).trim();
76
+ const value = h.slice(idx + 1).trim();
77
+ if (!name)
78
+ continue;
79
+ headers[name] = value;
80
+ }
81
+ if (Object.keys(headers).length)
82
+ overrides.input = { ...(overrides.input || {}), headers };
83
+ if (argv.out)
84
+ overrides.output = { ...(overrides.output || {}), root: String(argv.out) };
85
+ if (argv.format)
86
+ overrides.documents = { ...(overrides.documents || {}), format: String(argv.format) };
87
+ if (argv.convention)
88
+ overrides.documents = { ...(overrides.documents || {}), convention: String(argv.convention) };
89
+ const allowQueryArg = argv.allowQuery;
90
+ const excludeQueryArg = argv.excludeQuery;
91
+ const excludePatternArg = argv.excludePattern;
92
+ const allowQueries = Array.isArray(allowQueryArg) ? allowQueryArg : allowQueryArg ? [String(allowQueryArg)] : [];
93
+ const excludeQueries = Array.isArray(excludeQueryArg) ? excludeQueryArg : excludeQueryArg ? [String(excludeQueryArg)] : [];
94
+ const excludePatterns = Array.isArray(excludePatternArg) ? excludePatternArg : excludePatternArg ? [String(excludePatternArg)] : [];
95
+ if (allowQueries.length || excludeQueries.length || excludePatterns.length) {
96
+ overrides.documents = { ...(overrides.documents || {}), allowQueries, excludeQueries, excludePatterns };
97
+ }
98
+ const emitTypes = parseBool(argv.emitTypes, true);
99
+ const emitOperations = parseBool(argv.emitOperations, true);
100
+ const emitSdk = parseBool(argv.emitSdk, true);
101
+ overrides.features = { emitTypes, emitOperations, emitSdk };
102
+ const merged = mergeGraphQLCodegenOptions(defaultGraphQLCodegenOptions, fileOpts);
103
+ const finalOptions = mergeGraphQLCodegenOptions(merged, overrides);
104
+ if (finalOptions.input.endpoint && headerHost) {
105
+ const opts = {};
106
+ if (headerHost)
107
+ opts.headerHost = headerHost;
108
+ if (auth)
109
+ opts.auth = auth;
110
+ if (Object.keys(headers).length)
111
+ opts.headers = headers;
112
+ const sdl = await fetchEndpointSchemaSDL(String(finalOptions.input.endpoint), opts);
113
+ const tmpSchemaPath = join(cwd, '.constructive-codegen-schema.graphql');
114
+ await fs.writeFile(tmpSchemaPath, sdl, 'utf8');
115
+ finalOptions.input.schema = tmpSchemaPath;
116
+ finalOptions.input.endpoint = '';
117
+ }
118
+ const hasSchema = !!finalOptions.input.schema && String(finalOptions.input.schema).trim() !== '';
119
+ const hasEndpoint = !!finalOptions.input.endpoint && String(finalOptions.input.endpoint).trim() !== '';
120
+ if (!hasSchema && !hasEndpoint) {
121
+ console.error('Missing --schema or --endpoint or config.input');
122
+ process.exit(1);
123
+ }
124
+ const result = await runCodegen(finalOptions, cwd);
125
+ console.log(`Generated at ${join(result.root)}`);
126
+ };
@@ -0,0 +1,92 @@
1
+ import { getEnvOptions } from '@constructive-io/graphql-env';
2
+ import { GraphQLExplorer as explorer } from '@constructive-io/graphql-explorer';
3
+ import { Logger } from '@pgpmjs/logger';
4
+ const log = new Logger('explorer');
5
+ const explorerUsageText = `
6
+ Constructive GraphQL Explorer:
7
+
8
+ cnc explorer [OPTIONS]
9
+
10
+ Launch GraphiQL explorer interface.
11
+
12
+ Options:
13
+ --help, -h Show this help message
14
+ --port <number> Server port (default: 5555)
15
+ --origin <url> CORS origin URL (default: http://localhost:3000)
16
+ --simpleInflection Use simple inflection (default: true)
17
+ --oppositeBaseNames Use opposite base names (default: false)
18
+ --postgis Enable PostGIS extension (default: true)
19
+ --cwd <directory> Working directory (default: current directory)
20
+
21
+ Examples:
22
+ cnc explorer Launch explorer with defaults
23
+ cnc explorer --origin http://localhost:4000 Launch explorer with custom origin
24
+ `;
25
+ const questions = [
26
+ {
27
+ name: 'simpleInflection',
28
+ message: 'Use simple inflection?',
29
+ type: 'confirm',
30
+ required: false,
31
+ default: true,
32
+ useDefault: true
33
+ },
34
+ {
35
+ name: 'oppositeBaseNames',
36
+ message: 'Use opposite base names?',
37
+ type: 'confirm',
38
+ required: false,
39
+ default: false,
40
+ useDefault: true
41
+ },
42
+ {
43
+ name: 'postgis',
44
+ message: 'Enable PostGIS extension?',
45
+ type: 'confirm',
46
+ required: false,
47
+ default: true,
48
+ useDefault: true
49
+ },
50
+ {
51
+ name: 'port',
52
+ message: 'Development server port',
53
+ type: 'number',
54
+ required: false,
55
+ default: 5555,
56
+ useDefault: true
57
+ },
58
+ {
59
+ name: 'origin',
60
+ message: 'CORS origin URL',
61
+ type: 'text',
62
+ required: false,
63
+ default: 'http://localhost:3000',
64
+ useDefault: true
65
+ }
66
+ ];
67
+ export default async (argv, prompter, _options) => {
68
+ // Show usage if explicitly requested
69
+ if (argv.help || argv.h) {
70
+ console.log(explorerUsageText);
71
+ process.exit(0);
72
+ }
73
+ log.info('🔧 Constructive GraphQL Explorer Configuration:\n');
74
+ const { oppositeBaseNames, origin, port, postgis, simpleInflection } = await prompter.prompt(argv, questions);
75
+ const options = getEnvOptions({
76
+ features: {
77
+ oppositeBaseNames,
78
+ simpleInflection,
79
+ postgis
80
+ },
81
+ server: {
82
+ origin,
83
+ port
84
+ }
85
+ });
86
+ log.success('✅ Selected Configuration:');
87
+ for (const [key, value] of Object.entries(options)) {
88
+ log.debug(`${key}: ${JSON.stringify(value)}`);
89
+ }
90
+ log.success('🚀 Launching Explorer...\n');
91
+ explorer(options);
92
+ };
@@ -0,0 +1,71 @@
1
+ import { promises as fs } from 'fs';
2
+ import { buildSchemaSDL, fetchEndpointSchemaSDL } from '@constructive-io/graphql-server';
3
+ const usage = `
4
+ Constructive Get GraphQL Schema:
5
+
6
+ cnc get-graphql-schema [OPTIONS]
7
+
8
+ Options:
9
+ --help, -h Show this help message
10
+ --database <name> Database name (default: constructive)
11
+ --schemas <list> Comma-separated schemas to include
12
+ --endpoint <url> GraphQL endpoint to fetch schema via introspection
13
+ --headerHost <host> Optional Host header to send with endpoint requests
14
+ --auth <token> Optional Authorization header value (e.g., "Bearer 123")
15
+ --header "Name: Value" Optional HTTP header; repeat to add multiple headers
16
+ --out <path> Output file path (default: print to stdout)
17
+ `;
18
+ const defaultSchemas = [
19
+ 'collections_public',
20
+ 'dashboard_public'
21
+ ];
22
+ export default async (argv, prompter, _options) => {
23
+ if (argv.help || argv.h) {
24
+ console.log(usage);
25
+ process.exit(0);
26
+ }
27
+ const endpoint = argv.endpoint ?? '';
28
+ const headerHost = argv.headerHost ?? '';
29
+ const auth = argv.auth ?? '';
30
+ const database = argv.database ?? 'constructive';
31
+ const schemasArg = argv.schemas ?? defaultSchemas.join(',');
32
+ const out = argv.out ?? '';
33
+ const headerArg = argv.header;
34
+ const headerList = Array.isArray(headerArg) ? headerArg : headerArg ? [headerArg] : [];
35
+ const headers = {};
36
+ for (const h of headerList) {
37
+ const idx = typeof h === 'string' ? h.indexOf(':') : -1;
38
+ if (idx <= 0)
39
+ continue;
40
+ const name = h.slice(0, idx).trim();
41
+ const value = h.slice(idx + 1).trim();
42
+ if (!name)
43
+ continue;
44
+ headers[name] = value;
45
+ }
46
+ const schemas = schemasArg.split(',').map(s => s.trim()).filter(Boolean);
47
+ let sdl;
48
+ if (endpoint) {
49
+ const opts = {};
50
+ if (headerHost)
51
+ opts.headerHost = headerHost;
52
+ if (auth)
53
+ opts.auth = auth;
54
+ if (Object.keys(headers).length)
55
+ opts.headers = headers;
56
+ sdl = await fetchEndpointSchemaSDL(endpoint, opts);
57
+ }
58
+ else {
59
+ // The server package already depends on postgraphile and graphql,
60
+ // and exporting a reusable programmatic builder from there
61
+ // avoids adding new dependencies to cli and prevents duplication.
62
+ sdl = await buildSchemaSDL({ database, schemas });
63
+ }
64
+ if (out) {
65
+ await fs.writeFile(out, sdl, 'utf8');
66
+ console.log(`Wrote schema SDL to ${out}`);
67
+ }
68
+ else {
69
+ process.stdout.write(sdl + '\n');
70
+ }
71
+ };
@@ -0,0 +1,185 @@
1
+ import { getEnvOptions } from '@constructive-io/graphql-env';
2
+ import { Logger } from '@pgpmjs/logger';
3
+ import { GraphQLServer as server } from '@constructive-io/graphql-server';
4
+ import { getPgPool } from 'pg-cache';
5
+ const log = new Logger('server');
6
+ const serverUsageText = `
7
+ Constructive GraphQL Server:
8
+
9
+ cnc server [OPTIONS]
10
+
11
+ Start Constructive GraphQL development server.
12
+
13
+ Options:
14
+ --help, -h Show this help message
15
+ --port <number> Server port (default: 5555)
16
+ --simpleInflection Use simple inflection (default: true)
17
+ --oppositeBaseNames Use opposite base names (default: false)
18
+ --postgis Enable PostGIS extension (default: true)
19
+ --metaApi Enable Meta API (default: true)
20
+ --cwd <directory> Working directory (default: current directory)
21
+
22
+ Examples:
23
+ cnc server Start server with defaults
24
+ cnc server --port 8080 Start server on custom port
25
+ cnc server --no-postgis Start server without PostGIS
26
+ `;
27
+ const questions = [
28
+ {
29
+ name: 'simpleInflection',
30
+ message: 'Use simple inflection?',
31
+ type: 'confirm',
32
+ required: false,
33
+ default: true,
34
+ useDefault: true
35
+ },
36
+ {
37
+ name: 'oppositeBaseNames',
38
+ message: 'Use opposite base names?',
39
+ type: 'confirm',
40
+ required: false,
41
+ default: false,
42
+ useDefault: true
43
+ },
44
+ {
45
+ name: 'postgis',
46
+ message: 'Enable PostGIS extension?',
47
+ type: 'confirm',
48
+ required: false,
49
+ default: true,
50
+ useDefault: true
51
+ },
52
+ {
53
+ name: 'metaApi',
54
+ message: 'Enable Meta API?',
55
+ type: 'confirm',
56
+ required: false,
57
+ default: true,
58
+ useDefault: true
59
+ },
60
+ {
61
+ name: 'origin',
62
+ message: 'CORS origin (exact URL or *)',
63
+ type: 'text',
64
+ required: false,
65
+ // no default to avoid accidentally opening up CORS; pass explicitly or via env
66
+ },
67
+ {
68
+ name: 'port',
69
+ message: 'Development server port',
70
+ type: 'number',
71
+ required: false,
72
+ default: 5555,
73
+ useDefault: true
74
+ }
75
+ ];
76
+ export default async (argv, prompter, _options) => {
77
+ // Show usage if explicitly requested
78
+ if (argv.help || argv.h) {
79
+ console.log(serverUsageText);
80
+ process.exit(0);
81
+ }
82
+ log.info('🔧 Constructive GraphQL Server Configuration:\n');
83
+ let selectedDb = process.env.PGDATABASE;
84
+ if (!selectedDb) {
85
+ const db = await getPgPool({ database: 'postgres' });
86
+ const result = await db.query(`
87
+ SELECT datname FROM pg_database
88
+ WHERE datistemplate = false AND datname NOT IN ('postgres')
89
+ AND datname !~ '^pg_'
90
+ ORDER BY datname;
91
+ `);
92
+ const dbChoices = result.rows.map(row => row.datname);
93
+ const { database } = await prompter.prompt(argv, [
94
+ {
95
+ type: 'autocomplete',
96
+ name: 'database',
97
+ message: 'Select the database to use',
98
+ options: dbChoices,
99
+ required: true
100
+ }
101
+ ]);
102
+ selectedDb = database;
103
+ log.info(`📌 Using database: "${selectedDb}"`);
104
+ }
105
+ const { oppositeBaseNames, port, postgis, simpleInflection, metaApi, origin } = await prompter.prompt(argv, questions);
106
+ // Warn when passing CORS override via CLI, especially in production
107
+ if (origin && origin.trim().length) {
108
+ const env = (process.env.NODE_ENV || 'development').toLowerCase();
109
+ if (env === 'production') {
110
+ if (origin.trim() === '*') {
111
+ log.warn('CORS wildcard ("*") provided via --origin in production: this effectively disables CORS and is not recommended. Prefer per-API CORS via meta schema.');
112
+ }
113
+ else {
114
+ log.warn(`CORS override (origin=${origin.trim()}) provided via --origin in production. Prefer per-API CORS via meta schema.`);
115
+ }
116
+ }
117
+ }
118
+ let selectedSchemas = [];
119
+ let authRole;
120
+ let roleName;
121
+ if (!metaApi) {
122
+ const db = await getPgPool({ database: selectedDb });
123
+ const result = await db.query(`
124
+ SELECT nspname
125
+ FROM pg_namespace
126
+ WHERE nspname NOT IN ('pg_catalog', 'information_schema')
127
+ ORDER BY nspname;
128
+ `);
129
+ const schemaChoices = result.rows.map(row => ({
130
+ name: row.nspname,
131
+ value: row.nspname,
132
+ selected: true
133
+ }));
134
+ const { schemas } = await prompter.prompt(argv, [
135
+ {
136
+ type: 'checkbox',
137
+ name: 'schemas',
138
+ message: 'Select schemas to expose',
139
+ options: schemaChoices,
140
+ required: true
141
+ }
142
+ ]);
143
+ selectedSchemas = schemas.filter(s => s.selected).map(s => s.value);
144
+ const { authRole: selectedAuthRole, roleName: selectedRoleName } = await prompter.prompt(argv, [
145
+ {
146
+ type: 'autocomplete',
147
+ name: 'authRole',
148
+ message: 'Select the authentication role',
149
+ options: ['postgres', 'authenticated', 'anonymous'],
150
+ required: true
151
+ },
152
+ {
153
+ type: 'autocomplete',
154
+ name: 'roleName',
155
+ message: 'Enter the default role name:',
156
+ options: ['postgres', 'authenticated', 'anonymous'],
157
+ required: true
158
+ }
159
+ ]);
160
+ authRole = selectedAuthRole;
161
+ roleName = selectedRoleName;
162
+ }
163
+ const options = getEnvOptions({
164
+ pg: { database: selectedDb },
165
+ features: {
166
+ oppositeBaseNames,
167
+ simpleInflection,
168
+ postgis
169
+ },
170
+ api: {
171
+ enableMetaApi: metaApi,
172
+ ...(metaApi === false && { exposedSchemas: selectedSchemas, authRole, roleName })
173
+ },
174
+ server: {
175
+ port,
176
+ ...(origin ? { origin } : {})
177
+ }
178
+ });
179
+ log.success('✅ Selected Configuration:');
180
+ for (const [key, value] of Object.entries(options)) {
181
+ log.debug(`${key}: ${JSON.stringify(value)}`);
182
+ }
183
+ log.success('🚀 Launching Server...\n');
184
+ server(options);
185
+ };
package/esm/commands.js CHANGED
@@ -1,78 +1,59 @@
1
- import { teardownPgPools } from 'pg-cache';
2
- import add from './commands/add';
3
- import adminUsers from './commands/admin-users';
4
- import analyze from './commands/analyze';
5
- import clear from './commands/clear';
6
- import deploy from './commands/deploy';
7
- import docker from './commands/docker';
8
- import env from './commands/env';
9
- import _export from './commands/export';
10
- import extension from './commands/extension';
11
- import init from './commands/init';
12
- import install from './commands/install';
13
- import kill from './commands/kill';
14
- import migrate from './commands/migrate';
15
- import _package from './commands/package';
16
- import plan from './commands/plan';
17
- import remove from './commands/remove';
18
- import renameCmd from './commands/rename';
19
- import revert from './commands/revert';
20
- import tag from './commands/tag';
21
- import verify from './commands/verify';
22
- import { readAndParsePackageJson } from './package';
23
- import { extractFirst, usageText } from './utils';
24
- import { cliExitWithError } from './utils/cli-error';
25
- const withPgTeardown = (fn, skipTeardown = false) => async (...args) => {
26
- try {
27
- await fn(...args);
28
- }
29
- finally {
30
- if (!skipTeardown) {
31
- await teardownPgPools();
32
- }
33
- }
34
- };
35
- export const createConstructiveCommandMap = (skipPgTeardown = false) => {
36
- const pgt = (fn) => withPgTeardown(fn, skipPgTeardown);
1
+ import { findAndRequirePackageJson } from 'find-and-require-package-json';
2
+ import { checkForUpdates, createInitUsageText, createPgpmCommandMap } from 'pgpm';
3
+ import codegen from './commands/codegen';
4
+ import explorer from './commands/explorer';
5
+ import getGraphqlSchema from './commands/get-graphql-schema';
6
+ import server from './commands/server';
7
+ import { cliExitWithError, extractFirst, usageText } from './utils';
8
+ const createCommandMap = (skipPgTeardown = false) => {
9
+ const pgpmCommands = createPgpmCommandMap(skipPgTeardown);
37
10
  return {
38
- add,
39
- 'admin-users': pgt(adminUsers),
40
- clear: pgt(clear),
41
- deploy: pgt(deploy),
42
- docker,
43
- env,
44
- verify: pgt(verify),
45
- revert: pgt(revert),
46
- remove: pgt(remove),
47
- init: pgt(init),
48
- extension: pgt(extension),
49
- plan: pgt(plan),
50
- export: pgt(_export),
51
- package: pgt(_package),
52
- tag: pgt(tag),
53
- kill: pgt(kill),
54
- install: pgt(install),
55
- migrate: pgt(migrate),
56
- analyze: pgt(analyze),
57
- rename: pgt(renameCmd)
11
+ ...pgpmCommands,
12
+ server,
13
+ explorer,
14
+ 'get-graphql-schema': getGraphqlSchema,
15
+ codegen
58
16
  };
59
17
  };
60
18
  export const commands = async (argv, prompter, options) => {
19
+ let { first: command, newArgv } = extractFirst(argv);
20
+ // Run update check early so it shows on help/version paths too
21
+ try {
22
+ const pkg = findAndRequirePackageJson(__dirname);
23
+ await checkForUpdates({
24
+ command: command || 'help',
25
+ pkgName: pkg.name,
26
+ pkgVersion: pkg.version,
27
+ toolName: 'constructive',
28
+ key: pkg.name,
29
+ updateCommand: `Run npm i -g ${pkg.name}@latest to upgrade.`
30
+ });
31
+ }
32
+ catch {
33
+ // ignore update check failures
34
+ }
61
35
  if (argv.version || argv.v) {
62
- const pkg = readAndParsePackageJson();
36
+ const pkg = findAndRequirePackageJson(__dirname);
63
37
  console.log(pkg.version);
64
38
  process.exit(0);
65
39
  }
66
- let { first: command, newArgv } = extractFirst(argv);
40
+ // Show usage if explicitly requested but no command specified
67
41
  if ((argv.help || argv.h || command === 'help') && !command) {
68
42
  console.log(usageText);
69
43
  process.exit(0);
70
44
  }
45
+ // Show usage for help command specifically
71
46
  if (command === 'help') {
72
47
  console.log(usageText);
73
48
  process.exit(0);
74
49
  }
75
- const commandMap = createConstructiveCommandMap(options?.skipPgTeardown);
50
+ // Command-specific help for init
51
+ if (command === 'init' && (argv.help || argv.h)) {
52
+ console.log(createInitUsageText('constructive', 'Constructive'));
53
+ process.exit(0);
54
+ }
55
+ const commandMap = createCommandMap(options?.skipPgTeardown);
56
+ // Prompt if no command provided
76
57
  if (!command) {
77
58
  const answer = await prompter.prompt(argv, [
78
59
  {
@@ -84,6 +65,7 @@ export const commands = async (argv, prompter, options) => {
84
65
  ]);
85
66
  command = answer.command;
86
67
  }
68
+ // Prompt for working directory
87
69
  newArgv = await prompter.prompt(newArgv, [
88
70
  {
89
71
  type: 'text',
package/esm/index.js CHANGED
@@ -2,28 +2,13 @@
2
2
  import { readFileSync } from 'fs';
3
3
  import { CLI } from 'inquirerer';
4
4
  import { join } from 'path';
5
- import { commands, createConstructiveCommandMap } from './commands';
6
- export { createConstructiveCommandMap };
7
- export { default as add } from './commands/add';
8
- export { default as adminUsers } from './commands/admin-users';
9
- export { default as analyze } from './commands/analyze';
10
- export { default as clear } from './commands/clear';
11
- export { default as deploy } from './commands/deploy';
12
- export { default as docker } from './commands/docker';
13
- export { default as env } from './commands/env';
14
- export { default as _export } from './commands/export';
15
- export { default as extension } from './commands/extension';
16
- export { default as install } from './commands/install';
17
- export { default as kill } from './commands/kill';
18
- export { default as migrate } from './commands/migrate';
19
- export { default as _package } from './commands/package';
20
- export { default as plan } from './commands/plan';
21
- export { default as remove } from './commands/remove';
22
- export { default as renameCmd } from './commands/rename';
23
- export { default as revert } from './commands/revert';
24
- export { default as tag } from './commands/tag';
25
- export { default as verify } from './commands/verify';
26
- export * from './utils';
5
+ import { commands } from './commands';
6
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
7
+ const pkgPath = join(__dirname, 'package.json');
8
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
9
+ console.log(pkg.version);
10
+ process.exit(0);
11
+ }
27
12
  export const options = {
28
13
  minimistOpts: {
29
14
  alias: {
@@ -32,17 +17,12 @@ export const options = {
32
17
  }
33
18
  }
34
19
  };
35
- if (require.main === module) {
36
- if (process.argv.includes('--version') || process.argv.includes('-v')) {
37
- const pkgPath = join(__dirname, 'package.json');
38
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
39
- console.log(pkg.version);
40
- process.exit(0);
41
- }
42
- const app = new CLI(commands, options);
43
- app.run().then(() => {
44
- }).catch(error => {
45
- console.error('Unexpected error:', error);
46
- process.exit(1);
47
- });
48
- }
20
+ const app = new CLI(commands, options);
21
+ app.run().then(() => {
22
+ // all done!
23
+ }).catch(error => {
24
+ // Should not reach here with the new CLI error handling pattern
25
+ // But keep as fallback for unexpected errors
26
+ console.error('Unexpected error:', error);
27
+ process.exit(1);
28
+ });