pgpm 0.0.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 (121) hide show
  1. package/README.md +416 -0
  2. package/commands/add.d.ts +7 -0
  3. package/commands/add.js +86 -0
  4. package/commands/admin-users/add.d.ts +4 -0
  5. package/commands/admin-users/add.js +89 -0
  6. package/commands/admin-users/bootstrap.d.ts +4 -0
  7. package/commands/admin-users/bootstrap.js +50 -0
  8. package/commands/admin-users/remove.d.ts +4 -0
  9. package/commands/admin-users/remove.js +82 -0
  10. package/commands/admin-users.d.ts +4 -0
  11. package/commands/admin-users.js +68 -0
  12. package/commands/analyze.d.ts +4 -0
  13. package/commands/analyze.js +21 -0
  14. package/commands/clear.d.ts +3 -0
  15. package/commands/clear.js +59 -0
  16. package/commands/deploy.d.ts +4 -0
  17. package/commands/deploy.js +146 -0
  18. package/commands/explorer.d.ts +3 -0
  19. package/commands/explorer.js +94 -0
  20. package/commands/export.d.ts +3 -0
  21. package/commands/export.js +129 -0
  22. package/commands/extension.d.ts +4 -0
  23. package/commands/extension.js +48 -0
  24. package/commands/init/index.d.ts +7 -0
  25. package/commands/init/index.js +47 -0
  26. package/commands/init/module.d.ts +4 -0
  27. package/commands/init/module.js +71 -0
  28. package/commands/init/workspace.d.ts +4 -0
  29. package/commands/init/workspace.js +52 -0
  30. package/commands/install.d.ts +4 -0
  31. package/commands/install.js +37 -0
  32. package/commands/kill.d.ts +3 -0
  33. package/commands/kill.js +107 -0
  34. package/commands/migrate/deps.d.ts +4 -0
  35. package/commands/migrate/deps.js +186 -0
  36. package/commands/migrate/init.d.ts +4 -0
  37. package/commands/migrate/init.js +65 -0
  38. package/commands/migrate/list.d.ts +4 -0
  39. package/commands/migrate/list.js +85 -0
  40. package/commands/migrate/status.d.ts +4 -0
  41. package/commands/migrate/status.js +94 -0
  42. package/commands/migrate.d.ts +4 -0
  43. package/commands/migrate.js +69 -0
  44. package/commands/package.d.ts +3 -0
  45. package/commands/package.js +65 -0
  46. package/commands/plan.d.ts +3 -0
  47. package/commands/plan.js +62 -0
  48. package/commands/remove.d.ts +3 -0
  49. package/commands/remove.js +42 -0
  50. package/commands/rename.d.ts +4 -0
  51. package/commands/rename.js +35 -0
  52. package/commands/revert.d.ts +3 -0
  53. package/commands/revert.js +107 -0
  54. package/commands/server.d.ts +3 -0
  55. package/commands/server.js +187 -0
  56. package/commands/tag.d.ts +6 -0
  57. package/commands/tag.js +168 -0
  58. package/commands/verify.d.ts +3 -0
  59. package/commands/verify.js +85 -0
  60. package/commands.d.ts +5 -0
  61. package/commands.js +118 -0
  62. package/dist/README.md +416 -0
  63. package/dist/package.json +77 -0
  64. package/esm/commands/add.js +51 -0
  65. package/esm/commands/admin-users/add.js +87 -0
  66. package/esm/commands/admin-users/bootstrap.js +48 -0
  67. package/esm/commands/admin-users/remove.js +80 -0
  68. package/esm/commands/admin-users.js +63 -0
  69. package/esm/commands/analyze.js +16 -0
  70. package/esm/commands/clear.js +54 -0
  71. package/esm/commands/deploy.js +144 -0
  72. package/esm/commands/explorer.js +92 -0
  73. package/esm/commands/export.js +127 -0
  74. package/esm/commands/extension.js +46 -0
  75. package/esm/commands/init/index.js +42 -0
  76. package/esm/commands/init/module.js +68 -0
  77. package/esm/commands/init/workspace.js +46 -0
  78. package/esm/commands/install.js +35 -0
  79. package/esm/commands/kill.js +105 -0
  80. package/esm/commands/migrate/deps.js +184 -0
  81. package/esm/commands/migrate/init.js +63 -0
  82. package/esm/commands/migrate/list.js +83 -0
  83. package/esm/commands/migrate/status.js +92 -0
  84. package/esm/commands/migrate.js +64 -0
  85. package/esm/commands/package.js +63 -0
  86. package/esm/commands/plan.js +60 -0
  87. package/esm/commands/remove.js +40 -0
  88. package/esm/commands/rename.js +30 -0
  89. package/esm/commands/revert.js +105 -0
  90. package/esm/commands/server.js +185 -0
  91. package/esm/commands/tag.js +133 -0
  92. package/esm/commands/verify.js +83 -0
  93. package/esm/commands.js +111 -0
  94. package/esm/index.js +20 -0
  95. package/esm/package.js +26 -0
  96. package/esm/utils/argv.js +92 -0
  97. package/esm/utils/cli-error.js +48 -0
  98. package/esm/utils/database.js +78 -0
  99. package/esm/utils/deployed-changes.js +68 -0
  100. package/esm/utils/display.js +58 -0
  101. package/esm/utils/index.js +3 -0
  102. package/esm/utils/module-utils.js +51 -0
  103. package/index.d.ts +3 -0
  104. package/index.js +23 -0
  105. package/package.d.ts +1 -0
  106. package/package.js +29 -0
  107. package/package.json +77 -0
  108. package/utils/argv.d.ts +46 -0
  109. package/utils/argv.js +100 -0
  110. package/utils/cli-error.d.ts +8 -0
  111. package/utils/cli-error.js +52 -0
  112. package/utils/database.d.ts +21 -0
  113. package/utils/database.js +83 -0
  114. package/utils/deployed-changes.d.ts +4 -0
  115. package/utils/deployed-changes.js +72 -0
  116. package/utils/display.d.ts +3 -0
  117. package/utils/display.js +66 -0
  118. package/utils/index.d.ts +3 -0
  119. package/utils/index.js +19 -0
  120. package/utils/module-utils.d.ts +8 -0
  121. package/utils/module-utils.js +54 -0
@@ -0,0 +1,83 @@
1
+ import { LaunchQLPackage } from '@launchql/core';
2
+ import { Logger } from '@launchql/logger';
3
+ import { getEnvOptions } from '@launchql/env';
4
+ import { getPgEnvOptions } from 'pg-env';
5
+ import { getTargetDatabase } from '../utils';
6
+ import { selectDeployedChange, selectDeployedPackage } from '../utils/deployed-changes';
7
+ import { cliExitWithError } from '../utils/cli-error';
8
+ const log = new Logger('verify');
9
+ const verifyUsageText = `
10
+ LaunchQL Verify Command:
11
+
12
+ lql verify [OPTIONS]
13
+
14
+ Verify database state matches expected migrations.
15
+
16
+ Options:
17
+ --help, -h Show this help message
18
+ --recursive Verify recursively through dependencies
19
+ --package <name> Verify specific package
20
+ --to <target> Verify up to specific change or tag
21
+ --to Interactive selection of deployed changes
22
+ --cwd <directory> Working directory (default: current directory)
23
+
24
+ Examples:
25
+ lql verify Verify current database state
26
+ lql verify --package mypackage Verify specific package
27
+ lql verify --to Interactive selection from deployed changes
28
+ `;
29
+ export default async (argv, prompter, _options) => {
30
+ // Show usage if explicitly requested
31
+ if (argv.help || argv.h) {
32
+ console.log(verifyUsageText);
33
+ process.exit(0);
34
+ }
35
+ const database = await getTargetDatabase(argv, prompter, {
36
+ message: 'Select database'
37
+ });
38
+ const questions = [
39
+ {
40
+ name: 'recursive',
41
+ type: 'confirm',
42
+ message: 'Deploy recursively through dependencies?',
43
+ useDefault: true,
44
+ default: true,
45
+ required: false
46
+ },
47
+ ];
48
+ let { recursive, cwd } = await prompter.prompt(argv, questions);
49
+ log.debug(`Using current directory: ${cwd}`);
50
+ let packageName;
51
+ if (recursive && argv.to !== true) {
52
+ packageName = await selectDeployedPackage(database, argv, prompter, log, 'verify');
53
+ if (!packageName) {
54
+ await cliExitWithError('No package found to verify');
55
+ }
56
+ }
57
+ const project = new LaunchQLPackage(cwd);
58
+ const opts = getEnvOptions({
59
+ pg: getPgEnvOptions({ database })
60
+ });
61
+ let target;
62
+ if (argv.to === true) {
63
+ target = await selectDeployedChange(database, argv, prompter, log, 'verify');
64
+ if (!target) {
65
+ await cliExitWithError('No target selected, operation cancelled');
66
+ }
67
+ }
68
+ else if (packageName && argv.to) {
69
+ target = `${packageName}:${argv.to}`;
70
+ }
71
+ else if (packageName) {
72
+ target = packageName;
73
+ }
74
+ else if (argv.package && argv.to) {
75
+ target = `${argv.package}:${argv.to}`;
76
+ }
77
+ else if (argv.package) {
78
+ target = argv.package;
79
+ }
80
+ await project.verify(opts, target, recursive);
81
+ log.success('Verify complete.');
82
+ return argv;
83
+ };
@@ -0,0 +1,111 @@
1
+ import { teardownPgPools } from 'pg-cache';
2
+ // Commands
3
+ import add from './commands/add';
4
+ import adminUsers from './commands/admin-users';
5
+ import clear from './commands/clear';
6
+ import deploy from './commands/deploy';
7
+ import explorer from './commands/explorer';
8
+ import _export from './commands/export';
9
+ import extension from './commands/extension';
10
+ import init from './commands/init';
11
+ import install from './commands/install';
12
+ import kill from './commands/kill';
13
+ import migrate from './commands/migrate';
14
+ import _package from './commands/package';
15
+ import plan from './commands/plan';
16
+ import remove from './commands/remove';
17
+ import revert from './commands/revert';
18
+ import server from './commands/server';
19
+ import tag from './commands/tag';
20
+ import verify from './commands/verify';
21
+ import analyze from './commands/analyze';
22
+ import renameCmd from './commands/rename';
23
+ import { readAndParsePackageJson } from './package';
24
+ import { extractFirst, usageText } from './utils';
25
+ import { cliExitWithError } from './utils/cli-error';
26
+ const withPgTeardown = (fn, skipTeardown = false) => async (...args) => {
27
+ try {
28
+ await fn(...args);
29
+ }
30
+ finally {
31
+ if (!skipTeardown) {
32
+ await teardownPgPools();
33
+ }
34
+ }
35
+ };
36
+ const createCommandMap = (skipPgTeardown = false) => {
37
+ const pgt = (fn) => withPgTeardown(fn, skipPgTeardown);
38
+ return {
39
+ add,
40
+ 'admin-users': pgt(adminUsers),
41
+ clear: pgt(clear),
42
+ deploy: pgt(deploy),
43
+ verify: pgt(verify),
44
+ revert: pgt(revert),
45
+ remove: pgt(remove),
46
+ init: pgt(init),
47
+ extension: pgt(extension),
48
+ plan: pgt(plan),
49
+ export: pgt(_export),
50
+ package: pgt(_package),
51
+ tag: pgt(tag),
52
+ kill: pgt(kill),
53
+ install: pgt(install),
54
+ migrate: pgt(migrate),
55
+ analyze: pgt(analyze),
56
+ rename: pgt(renameCmd),
57
+ // These manage their own connection lifecycles
58
+ server,
59
+ explorer
60
+ };
61
+ };
62
+ export const commands = async (argv, prompter, options) => {
63
+ if (argv.version || argv.v) {
64
+ const pkg = readAndParsePackageJson();
65
+ console.log(pkg.version);
66
+ process.exit(0);
67
+ }
68
+ let { first: command, newArgv } = extractFirst(argv);
69
+ // Show usage if explicitly requested but no command specified
70
+ if ((argv.help || argv.h || command === 'help') && !command) {
71
+ console.log(usageText);
72
+ process.exit(0);
73
+ }
74
+ // Show usage for help command specifically
75
+ if (command === 'help') {
76
+ console.log(usageText);
77
+ process.exit(0);
78
+ }
79
+ const commandMap = createCommandMap(options?.skipPgTeardown);
80
+ // Prompt if no command provided
81
+ if (!command) {
82
+ const answer = await prompter.prompt(argv, [
83
+ {
84
+ type: 'autocomplete',
85
+ name: 'command',
86
+ message: 'What do you want to do?',
87
+ options: Object.keys(commandMap)
88
+ }
89
+ ]);
90
+ command = answer.command;
91
+ }
92
+ // Prompt for working directory
93
+ newArgv = await prompter.prompt(newArgv, [
94
+ {
95
+ type: 'text',
96
+ name: 'cwd',
97
+ message: 'Working directory',
98
+ required: false,
99
+ default: process.cwd(),
100
+ useDefault: true
101
+ }
102
+ ]);
103
+ const commandFn = commandMap[command];
104
+ if (!commandFn) {
105
+ console.log(usageText);
106
+ await cliExitWithError(`Unknown command: ${command}`);
107
+ }
108
+ await commandFn(newArgv, prompter, options);
109
+ prompter.close();
110
+ return argv;
111
+ };
package/esm/index.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { CLI } from 'inquirerer';
3
+ import { commands } from './commands';
4
+ export const options = {
5
+ minimistOpts: {
6
+ alias: {
7
+ v: 'version',
8
+ h: 'help'
9
+ }
10
+ }
11
+ };
12
+ const app = new CLI(commands, options);
13
+ app.run().then(() => {
14
+ // all done!
15
+ }).catch(error => {
16
+ // Should not reach here with the new CLI error handling pattern
17
+ // But keep as fallback for unexpected errors
18
+ console.error('Unexpected error:', error);
19
+ process.exit(1);
20
+ });
package/esm/package.js ADDED
@@ -0,0 +1,26 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ // need to search due to the dist/ folder and src/, etc.
4
+ function findPackageJson(currentDir) {
5
+ const filePath = join(currentDir, 'package.json');
6
+ // Check if package.json exists in the current directory
7
+ if (existsSync(filePath)) {
8
+ return filePath;
9
+ }
10
+ // Get the parent directory
11
+ const parentDir = dirname(currentDir);
12
+ // If reached the root directory, package.json is not found
13
+ if (parentDir === currentDir) {
14
+ throw new Error('package.json not found in any parent directory');
15
+ }
16
+ // Recursively look in the parent directory
17
+ return findPackageJson(parentDir);
18
+ }
19
+ export function readAndParsePackageJson() {
20
+ // Start searching from the current directory
21
+ const pkgPath = findPackageJson(__dirname);
22
+ // Read and parse the package.json
23
+ const str = readFileSync(pkgPath, 'utf8');
24
+ const pkg = JSON.parse(str);
25
+ return pkg;
26
+ }
@@ -0,0 +1,92 @@
1
+ import { Logger } from '@launchql/logger';
2
+ const log = new Logger('argv-utils');
3
+ export const extractFirst = (argv) => {
4
+ const first = argv._?.[0];
5
+ const newArgv = {
6
+ ...argv,
7
+ _: argv._?.slice(1) ?? []
8
+ };
9
+ return { first, newArgv };
10
+ };
11
+ /**
12
+ * Validates and normalizes common CLI arguments
13
+ */
14
+ export function validateCommonArgs(argv) {
15
+ const validated = {
16
+ ...argv,
17
+ cwd: argv.cwd || process.cwd(),
18
+ _: argv._ || []
19
+ };
20
+ const booleanFlags = ['recursive', 'yes', 'tx', 'fast', 'logOnly', 'createdb', 'usePlan', 'cache', 'drop', 'all', 'summary', 'help', 'h'];
21
+ for (const flag of booleanFlags) {
22
+ if (argv[flag] !== undefined && typeof argv[flag] !== 'boolean') {
23
+ log.warn(`--${flag} flag should be boolean, converting to true`);
24
+ validated[flag] = true;
25
+ }
26
+ }
27
+ const stringFlags = ['package', 'to', 'database'];
28
+ for (const flag of stringFlags) {
29
+ if (argv[flag] !== undefined && typeof argv[flag] !== 'string') {
30
+ log.warn(`--${flag} should be a string, converting`);
31
+ validated[flag] = String(argv[flag]);
32
+ }
33
+ }
34
+ return validated;
35
+ }
36
+ /**
37
+ * Checks if required flags are provided when certain conditions are met
38
+ */
39
+ export function validateFlagDependencies(argv) {
40
+ if (argv.to && !argv.package && !argv.recursive) {
41
+ log.warn('--to flag provided without --package or --recursive. Target may not work as expected.');
42
+ }
43
+ if (argv.package && argv.recursive) {
44
+ if (argv.package.includes(':')) {
45
+ log.warn('--package should not contain ":" when using --recursive. Use --to for target specification.');
46
+ }
47
+ }
48
+ }
49
+ /**
50
+ * Logs the effective CLI arguments for debugging
51
+ */
52
+ export function logEffectiveArgs(argv, commandName) {
53
+ const relevantArgs = {
54
+ cwd: argv.cwd,
55
+ database: argv.database,
56
+ package: argv.package,
57
+ to: argv.to,
58
+ recursive: argv.recursive,
59
+ yes: argv.yes,
60
+ tx: argv.tx,
61
+ fast: argv.fast,
62
+ logOnly: argv.logOnly,
63
+ createdb: argv.createdb,
64
+ usePlan: argv.usePlan,
65
+ cache: argv.cache,
66
+ drop: argv.drop,
67
+ all: argv.all,
68
+ summary: argv.summary
69
+ };
70
+ const definedArgs = Object.fromEntries(Object.entries(relevantArgs).filter(([_, value]) => value !== undefined));
71
+ if (Object.keys(definedArgs).length > 1) { // More than just cwd
72
+ log.debug(`${commandName} effective arguments:`, definedArgs);
73
+ }
74
+ }
75
+ /**
76
+ * Constructs a deployment target string from package and to arguments
77
+ */
78
+ export function constructTarget(argv, packageName) {
79
+ if (packageName && argv.to) {
80
+ return `${packageName}:${argv.to}`;
81
+ }
82
+ else if (packageName) {
83
+ return packageName;
84
+ }
85
+ else if (argv.package && argv.to) {
86
+ return `${argv.package}:${argv.to}`;
87
+ }
88
+ else if (argv.package) {
89
+ return argv.package;
90
+ }
91
+ return undefined;
92
+ }
@@ -0,0 +1,48 @@
1
+ import { LaunchQLError } from '@launchql/types';
2
+ import { Logger } from '@launchql/logger';
3
+ import { teardownPgPools } from 'pg-cache';
4
+ const log = new Logger('cli');
5
+ /**
6
+ * CLI error utility that logs error information and exits with code 1.
7
+ * Provides consistent error handling and user experience across all CLI commands.
8
+ *
9
+ * IMPORTANT: This function properly cleans up PostgreSQL connections before exiting.
10
+ */
11
+ export const cliExitWithError = async (error, context) => {
12
+ if (error instanceof LaunchQLError) {
13
+ // For LaunchQLError instances, use structured logging
14
+ log.error(`Error: ${error.message}`);
15
+ // Log additional context if available
16
+ if (error.context && Object.keys(error.context).length > 0) {
17
+ log.debug('Error context:', error.context);
18
+ }
19
+ // Log any additional context provided
20
+ if (context) {
21
+ log.debug('Additional context:', context);
22
+ }
23
+ }
24
+ else if (error instanceof Error) {
25
+ // For generic Error instances
26
+ log.error(`Error: ${error.message}`);
27
+ if (context) {
28
+ log.debug('Context:', context);
29
+ }
30
+ }
31
+ else if (typeof error === 'string') {
32
+ // For simple string messages
33
+ log.error(`Error: ${error}`);
34
+ if (context) {
35
+ log.debug('Context:', context);
36
+ }
37
+ }
38
+ // Perform cleanup before exiting
39
+ try {
40
+ await teardownPgPools();
41
+ log.debug('Database connections cleaned up');
42
+ }
43
+ catch (cleanupError) {
44
+ log.warn('Failed to cleanup database connections:', cleanupError);
45
+ // Don't let cleanup errors prevent the exit
46
+ }
47
+ process.exit(1);
48
+ };
@@ -0,0 +1,78 @@
1
+ import { getPgPool } from 'pg-cache';
2
+ import { getPgEnvOptions } from 'pg-env';
3
+ /**
4
+ * Get list of available databases from PostgreSQL
5
+ */
6
+ export async function getAvailableDatabases(options = {}) {
7
+ const { excludeTemplates = true, excludePostgres = true, excludeSystemDbs = true } = options;
8
+ const db = await getPgPool({
9
+ database: 'postgres'
10
+ });
11
+ let query = `
12
+ SELECT datname FROM pg_catalog.pg_database
13
+ WHERE 1=1
14
+ `;
15
+ if (excludeTemplates) {
16
+ query += ` AND datistemplate = FALSE`;
17
+ }
18
+ if (excludePostgres) {
19
+ query += ` AND datname NOT IN ('postgres')`;
20
+ }
21
+ if (excludeSystemDbs) {
22
+ query += ` AND datname !~ '^pg_'`;
23
+ }
24
+ query += ` ORDER BY datname`;
25
+ const result = await db.query(query);
26
+ return result.rows.map((row) => row.datname);
27
+ }
28
+ /**
29
+ * Prompt user to select a database
30
+ */
31
+ export async function selectDatabase(argv, prompter, options = {}) {
32
+ const { message = 'Select target database', multiple = false } = options;
33
+ // Check if database is already specified
34
+ if (!multiple && (argv.db || argv.database)) {
35
+ return argv.db || argv.database;
36
+ }
37
+ // Get available databases
38
+ const databases = await getAvailableDatabases(options);
39
+ if (databases.length === 0) {
40
+ throw new Error('No databases found');
41
+ }
42
+ // If only one database and not forcing selection, use it
43
+ if (!multiple && databases.length === 1 && !argv.interactive) {
44
+ return databases[0];
45
+ }
46
+ // Prompt for selection
47
+ const answer = await prompter.prompt(argv, [
48
+ {
49
+ type: multiple ? 'checkbox' : 'autocomplete',
50
+ name: 'database',
51
+ message,
52
+ options: databases,
53
+ required: true
54
+ }
55
+ ]);
56
+ return answer.database;
57
+ }
58
+ /**
59
+ * Get target database with fallback to environment
60
+ */
61
+ export async function getTargetDatabase(argv, prompter, options = {}) {
62
+ // If database is specified in args, use it
63
+ if (argv.db || argv.database) {
64
+ return argv.db || argv.database;
65
+ }
66
+ // Try to select from available databases
67
+ try {
68
+ return await selectDatabase(argv, prompter, options);
69
+ }
70
+ catch (error) {
71
+ // Fall back to environment database
72
+ const pgEnv = getPgEnvOptions();
73
+ if (pgEnv.database) {
74
+ return pgEnv.database;
75
+ }
76
+ throw new Error('No database specified and no default database found');
77
+ }
78
+ }
@@ -0,0 +1,68 @@
1
+ import { LaunchQLMigrate } from '@launchql/core';
2
+ import { getPgEnvOptions } from 'pg-env';
3
+ export async function selectDeployedChange(database, argv, prompter, log, action = 'revert') {
4
+ const pgEnv = getPgEnvOptions({ database });
5
+ const client = new LaunchQLMigrate(pgEnv);
6
+ let selectedPackage;
7
+ if (argv.package) {
8
+ selectedPackage = argv.package;
9
+ }
10
+ else {
11
+ const packageStatuses = await client.status();
12
+ if (packageStatuses.length === 0) {
13
+ log.warn('No deployed packages found in database');
14
+ return undefined;
15
+ }
16
+ const packageAnswer = await prompter.prompt(argv, [{
17
+ type: 'autocomplete',
18
+ name: 'package',
19
+ message: `Select package to ${action} from:`,
20
+ options: packageStatuses.map(status => ({
21
+ name: status.package,
22
+ value: status.package,
23
+ description: `${status.totalDeployed} changes, last: ${status.lastChange}`
24
+ }))
25
+ }]);
26
+ selectedPackage = packageAnswer.package;
27
+ }
28
+ const deployedChanges = await client.getDeployedChanges(database, selectedPackage);
29
+ if (deployedChanges.length === 0) {
30
+ log.warn(`No deployed changes found for package ${selectedPackage}`);
31
+ return undefined;
32
+ }
33
+ const changeAnswer = await prompter.prompt(argv, [{
34
+ type: 'autocomplete',
35
+ name: 'change',
36
+ message: `Select change to ${action} to in ${selectedPackage}:`,
37
+ options: deployedChanges.map(change => ({
38
+ name: change.change_name,
39
+ value: change.change_name,
40
+ description: `Deployed: ${new Date(change.deployed_at).toLocaleString()}`
41
+ }))
42
+ }]);
43
+ const selectedChange = changeAnswer.change;
44
+ return `${selectedPackage}:${selectedChange}`;
45
+ }
46
+ export async function selectDeployedPackage(database, argv, prompter, log, action = 'revert') {
47
+ if (argv.package) {
48
+ return argv.package;
49
+ }
50
+ const pgEnv = getPgEnvOptions({ database });
51
+ const client = new LaunchQLMigrate(pgEnv);
52
+ const packageStatuses = await client.status();
53
+ if (packageStatuses.length === 0) {
54
+ log.warn('No deployed packages found in database');
55
+ return undefined;
56
+ }
57
+ const packageAnswer = await prompter.prompt(argv, [{
58
+ type: 'autocomplete',
59
+ name: 'package',
60
+ message: `Select package to ${action}:`,
61
+ options: packageStatuses.map(status => ({
62
+ name: status.package,
63
+ value: status.package,
64
+ description: `${status.totalDeployed} changes, last: ${status.lastChange}`
65
+ }))
66
+ }]);
67
+ return packageAnswer.package;
68
+ }
@@ -0,0 +1,58 @@
1
+ import chalk from 'chalk';
2
+ import { readAndParsePackageJson } from '../package';
3
+ // Function to display the version information
4
+ export function displayVersion() {
5
+ const pkg = readAndParsePackageJson();
6
+ console.log(chalk.green(`Name: ${pkg.name}`));
7
+ console.log(chalk.blue(`Version: ${pkg.version}`));
8
+ }
9
+ export const usageText = `
10
+ Usage: lql <command> [options]
11
+ launchql <command> [options]
12
+
13
+ Core Database Operations:
14
+ add Add database changes to plans and create SQL files
15
+ deploy Deploy database changes and migrations
16
+ verify Verify database state and migrations
17
+ revert Revert database changes and migrations
18
+
19
+ Project Management:
20
+ init Initialize LaunchQL workspace or module
21
+ extension Manage module dependencies
22
+ plan Generate module deployment plans
23
+ package Package module for distribution
24
+
25
+ Development Tools:
26
+ server Start LaunchQL GraphQL server
27
+ explorer Launch GraphiQL explorer interface
28
+ export Export database migrations from existing databases
29
+
30
+ Database Administration:
31
+ kill Terminate database connections and optionally drop databases
32
+ install Install LaunchQL modules
33
+ tag Add tags to changes for versioning
34
+
35
+ Migration Tools:
36
+ migrate Migration management subcommands
37
+ init Initialize migration tracking
38
+ status Show migration status
39
+ list List all changes
40
+ deps Show change dependencies
41
+
42
+ Global Options:
43
+ -h, --help Display this help information
44
+ -v, --version Display version information
45
+ --cwd <directory> Working directory (default: current directory)
46
+
47
+ Individual Command Help:
48
+ lql <command> --help Display detailed help for specific command
49
+ lql <command> -h Display detailed help for specific command
50
+
51
+ Examples:
52
+ lql deploy --help Show deploy command options
53
+ lql server --port 8080 Start server on port 8080
54
+ lql init --workspace Initialize new workspace
55
+ `;
56
+ export function displayUsage() {
57
+ console.log(usageText);
58
+ }
@@ -0,0 +1,3 @@
1
+ export * from './argv';
2
+ export * from './database';
3
+ export * from './display';
@@ -0,0 +1,51 @@
1
+ import { LaunchQLPackage } from '@launchql/core';
2
+ import { errors } from '@launchql/types';
3
+ /**
4
+ * Handle package selection for operations that need a specific package
5
+ * Returns the selected package name, or undefined if validation fails or no packages exist
6
+ */
7
+ export async function selectPackage(argv, prompter, cwd, operationName, log) {
8
+ const pkg = new LaunchQLPackage(cwd);
9
+ const modules = await pkg.getModules();
10
+ const moduleNames = modules.map(mod => mod.getModuleName());
11
+ // Check if any modules exist
12
+ if (!moduleNames.length) {
13
+ const errorMsg = 'No modules found in the specified directory.';
14
+ if (log) {
15
+ log.error(errorMsg);
16
+ return undefined;
17
+ }
18
+ else {
19
+ prompter.close();
20
+ throw errors.NOT_FOUND({}, errorMsg);
21
+ }
22
+ }
23
+ // If a specific package was provided, validate it
24
+ if (argv.package) {
25
+ const packageName = argv.package;
26
+ if (log)
27
+ log.info(`Using specified package: ${packageName}`);
28
+ if (!moduleNames.includes(packageName)) {
29
+ const errorMsg = `Package '${packageName}' not found. Available packages: ${moduleNames.join(', ')}`;
30
+ if (log) {
31
+ log.error(errorMsg);
32
+ return undefined;
33
+ }
34
+ else {
35
+ throw errors.NOT_FOUND({}, errorMsg);
36
+ }
37
+ }
38
+ return packageName;
39
+ }
40
+ // Interactive selection
41
+ const { package: selectedPackage } = await prompter.prompt(argv, [{
42
+ type: 'autocomplete',
43
+ name: 'package',
44
+ message: `Choose a package to ${operationName}`,
45
+ options: moduleNames,
46
+ required: true
47
+ }]);
48
+ if (log)
49
+ log.info(`Selected package: ${selectedPackage}`);
50
+ return selectedPackage;
51
+ }
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { CLIOptions } from 'inquirerer';
3
+ export declare const options: Partial<CLIOptions>;
package/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.options = void 0;
5
+ const inquirerer_1 = require("inquirerer");
6
+ const commands_1 = require("./commands");
7
+ exports.options = {
8
+ minimistOpts: {
9
+ alias: {
10
+ v: 'version',
11
+ h: 'help'
12
+ }
13
+ }
14
+ };
15
+ const app = new inquirerer_1.CLI(commands_1.commands, exports.options);
16
+ app.run().then(() => {
17
+ // all done!
18
+ }).catch(error => {
19
+ // Should not reach here with the new CLI error handling pattern
20
+ // But keep as fallback for unexpected errors
21
+ console.error('Unexpected error:', error);
22
+ process.exit(1);
23
+ });
package/package.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function readAndParsePackageJson(): any;