@constructive-io/cli 0.0.2

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 (122) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +412 -0
  3. package/commands/add.d.ts +7 -0
  4. package/commands/add.js +86 -0
  5. package/commands/admin-users/add.d.ts +4 -0
  6. package/commands/admin-users/add.js +89 -0
  7. package/commands/admin-users/bootstrap.d.ts +4 -0
  8. package/commands/admin-users/bootstrap.js +50 -0
  9. package/commands/admin-users/remove.d.ts +4 -0
  10. package/commands/admin-users/remove.js +82 -0
  11. package/commands/admin-users.d.ts +4 -0
  12. package/commands/admin-users.js +68 -0
  13. package/commands/analyze.d.ts +4 -0
  14. package/commands/analyze.js +21 -0
  15. package/commands/clear.d.ts +3 -0
  16. package/commands/clear.js +59 -0
  17. package/commands/deploy.d.ts +4 -0
  18. package/commands/deploy.js +146 -0
  19. package/commands/docker.d.ts +3 -0
  20. package/commands/docker.js +194 -0
  21. package/commands/env.d.ts +4 -0
  22. package/commands/env.js +124 -0
  23. package/commands/export.d.ts +3 -0
  24. package/commands/export.js +129 -0
  25. package/commands/extension.d.ts +4 -0
  26. package/commands/extension.js +48 -0
  27. package/commands/init/index.d.ts +7 -0
  28. package/commands/init/index.js +47 -0
  29. package/commands/init/module.d.ts +4 -0
  30. package/commands/init/module.js +71 -0
  31. package/commands/init/workspace.d.ts +4 -0
  32. package/commands/init/workspace.js +52 -0
  33. package/commands/install.d.ts +4 -0
  34. package/commands/install.js +37 -0
  35. package/commands/kill.d.ts +3 -0
  36. package/commands/kill.js +107 -0
  37. package/commands/migrate/deps.d.ts +4 -0
  38. package/commands/migrate/deps.js +186 -0
  39. package/commands/migrate/init.d.ts +4 -0
  40. package/commands/migrate/init.js +65 -0
  41. package/commands/migrate/list.d.ts +4 -0
  42. package/commands/migrate/list.js +85 -0
  43. package/commands/migrate/status.d.ts +4 -0
  44. package/commands/migrate/status.js +94 -0
  45. package/commands/migrate.d.ts +4 -0
  46. package/commands/migrate.js +69 -0
  47. package/commands/package.d.ts +3 -0
  48. package/commands/package.js +65 -0
  49. package/commands/plan.d.ts +3 -0
  50. package/commands/plan.js +62 -0
  51. package/commands/remove.d.ts +3 -0
  52. package/commands/remove.js +42 -0
  53. package/commands/rename.d.ts +4 -0
  54. package/commands/rename.js +35 -0
  55. package/commands/revert.d.ts +3 -0
  56. package/commands/revert.js +107 -0
  57. package/commands/tag.d.ts +6 -0
  58. package/commands/tag.js +168 -0
  59. package/commands/verify.d.ts +3 -0
  60. package/commands/verify.js +85 -0
  61. package/commands.d.ts +6 -0
  62. package/commands.js +113 -0
  63. package/dist/README.md +412 -0
  64. package/dist/package.json +64 -0
  65. package/esm/commands/add.js +51 -0
  66. package/esm/commands/admin-users/add.js +87 -0
  67. package/esm/commands/admin-users/bootstrap.js +48 -0
  68. package/esm/commands/admin-users/remove.js +80 -0
  69. package/esm/commands/admin-users.js +63 -0
  70. package/esm/commands/analyze.js +16 -0
  71. package/esm/commands/clear.js +54 -0
  72. package/esm/commands/deploy.js +144 -0
  73. package/esm/commands/docker.js +192 -0
  74. package/esm/commands/env.js +122 -0
  75. package/esm/commands/export.js +127 -0
  76. package/esm/commands/extension.js +46 -0
  77. package/esm/commands/init/index.js +42 -0
  78. package/esm/commands/init/module.js +68 -0
  79. package/esm/commands/init/workspace.js +46 -0
  80. package/esm/commands/install.js +35 -0
  81. package/esm/commands/kill.js +105 -0
  82. package/esm/commands/migrate/deps.js +184 -0
  83. package/esm/commands/migrate/init.js +63 -0
  84. package/esm/commands/migrate/list.js +83 -0
  85. package/esm/commands/migrate/status.js +92 -0
  86. package/esm/commands/migrate.js +64 -0
  87. package/esm/commands/package.js +63 -0
  88. package/esm/commands/plan.js +60 -0
  89. package/esm/commands/remove.js +40 -0
  90. package/esm/commands/rename.js +30 -0
  91. package/esm/commands/revert.js +105 -0
  92. package/esm/commands/tag.js +133 -0
  93. package/esm/commands/verify.js +83 -0
  94. package/esm/commands.js +105 -0
  95. package/esm/index.js +48 -0
  96. package/esm/package.js +26 -0
  97. package/esm/utils/argv.js +92 -0
  98. package/esm/utils/cli-error.js +48 -0
  99. package/esm/utils/database.js +78 -0
  100. package/esm/utils/deployed-changes.js +68 -0
  101. package/esm/utils/display.js +58 -0
  102. package/esm/utils/index.js +6 -0
  103. package/esm/utils/module-utils.js +51 -0
  104. package/index.d.ts +25 -0
  105. package/index.js +87 -0
  106. package/package.d.ts +1 -0
  107. package/package.js +29 -0
  108. package/package.json +64 -0
  109. package/utils/argv.d.ts +46 -0
  110. package/utils/argv.js +100 -0
  111. package/utils/cli-error.d.ts +8 -0
  112. package/utils/cli-error.js +52 -0
  113. package/utils/database.d.ts +21 -0
  114. package/utils/database.js +83 -0
  115. package/utils/deployed-changes.d.ts +4 -0
  116. package/utils/deployed-changes.js +72 -0
  117. package/utils/display.d.ts +3 -0
  118. package/utils/display.js +66 -0
  119. package/utils/index.d.ts +6 -0
  120. package/utils/index.js +22 -0
  121. package/utils/module-utils.d.ts +8 -0
  122. package/utils/module-utils.js +54 -0
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const child_process_1 = require("child_process");
4
+ const pg_env_1 = require("pg-env");
5
+ const envUsageText = `
6
+ Environment Command:
7
+
8
+ constructive env [OPTIONS] [COMMAND...]
9
+
10
+ Manage PostgreSQL environment variables with profile support.
11
+
12
+ Profiles:
13
+ (default) Use local Postgres development profile
14
+ --supabase Use Supabase local development profile
15
+
16
+ Modes:
17
+ No command Print export statements for shell evaluation
18
+ With command Execute command with environment variables applied
19
+
20
+ Options:
21
+ --help, -h Show this help message
22
+ --supabase Use Supabase profile instead of default Postgres
23
+
24
+ Examples:
25
+ constructive env Print default Postgres env exports
26
+ constructive env --supabase Print Supabase env exports
27
+ eval "$(constructive env)" Load default Postgres env into shell
28
+ eval "$(constructive env --supabase)" Load Supabase env into shell
29
+ constructive env lql deploy --database db1 Run command with default Postgres env
30
+ constructive env createdb mydb Run command with default Postgres env
31
+ constructive env --supabase lql deploy --database db1 Run command with Supabase env
32
+ constructive env --supabase createdb mydb Run command with Supabase env
33
+ `;
34
+ const SUPABASE_PROFILE = {
35
+ host: 'localhost',
36
+ port: 54322,
37
+ user: 'supabase_admin',
38
+ password: 'postgres',
39
+ database: 'postgres'
40
+ };
41
+ const DEFAULT_PROFILE = {
42
+ ...pg_env_1.defaultPgConfig
43
+ };
44
+ function configToEnvVars(config) {
45
+ return {
46
+ PGHOST: config.host,
47
+ PGPORT: String(config.port),
48
+ PGUSER: config.user,
49
+ PGPASSWORD: config.password,
50
+ PGDATABASE: config.database
51
+ };
52
+ }
53
+ function printExports(config) {
54
+ const envVars = configToEnvVars(config);
55
+ for (const [key, value] of Object.entries(envVars)) {
56
+ const escapedValue = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
57
+ console.log(`export ${key}="${escapedValue}"`);
58
+ }
59
+ }
60
+ function executeCommand(config, command, args) {
61
+ return new Promise((resolve, reject) => {
62
+ const envVars = configToEnvVars(config);
63
+ const env = {
64
+ ...process.env,
65
+ ...envVars
66
+ };
67
+ const child = (0, child_process_1.spawn)(command, args, {
68
+ env,
69
+ stdio: 'inherit',
70
+ shell: false
71
+ });
72
+ child.on('error', (error) => {
73
+ reject(error);
74
+ });
75
+ child.on('close', (code) => {
76
+ resolve(code ?? 0);
77
+ });
78
+ });
79
+ }
80
+ exports.default = async (argv, _prompter) => {
81
+ if (argv.help || argv.h) {
82
+ console.log(envUsageText);
83
+ process.exit(0);
84
+ }
85
+ const useSupabase = argv.supabase === true || typeof argv.supabase === 'string';
86
+ const profile = useSupabase ? SUPABASE_PROFILE : DEFAULT_PROFILE;
87
+ const rawArgs = process.argv.slice(2);
88
+ let envIndex = rawArgs.findIndex(arg => arg === 'env');
89
+ if (envIndex === -1) {
90
+ envIndex = 0;
91
+ }
92
+ const argsAfterEnv = rawArgs.slice(envIndex + 1);
93
+ const supabaseIndex = argsAfterEnv.findIndex(arg => arg === '--supabase');
94
+ let commandArgs;
95
+ if (supabaseIndex !== -1) {
96
+ commandArgs = argsAfterEnv.slice(supabaseIndex + 1);
97
+ }
98
+ else {
99
+ commandArgs = argsAfterEnv;
100
+ }
101
+ commandArgs = commandArgs.filter(arg => arg !== '--cwd' && !arg.startsWith('--cwd='));
102
+ const cwdIndex = commandArgs.findIndex(arg => arg === '--cwd');
103
+ if (cwdIndex !== -1 && cwdIndex + 1 < commandArgs.length) {
104
+ commandArgs.splice(cwdIndex, 2);
105
+ }
106
+ if (commandArgs.length === 0) {
107
+ printExports(profile);
108
+ return;
109
+ }
110
+ const [command, ...args] = commandArgs;
111
+ try {
112
+ const exitCode = await executeCommand(profile, command, args);
113
+ process.exit(exitCode);
114
+ }
115
+ catch (error) {
116
+ if (error instanceof Error) {
117
+ console.error(`Error executing command: ${error.message}`);
118
+ }
119
+ else {
120
+ console.error(`Error executing command: ${String(error)}`);
121
+ }
122
+ process.exit(1);
123
+ }
124
+ };
@@ -0,0 +1,3 @@
1
+ import { CLIOptions, Inquirerer } from 'inquirerer';
2
+ declare const _default: (argv: Partial<Record<string, any>>, prompter: Inquirerer, _options: CLIOptions) => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@launchql/core");
4
+ const env_1 = require("@launchql/env");
5
+ const types_1 = require("@launchql/types");
6
+ const path_1 = require("path");
7
+ const pg_cache_1 = require("pg-cache");
8
+ const exportUsageText = `
9
+ Export Command:
10
+
11
+ constructive export [OPTIONS]
12
+
13
+ Export database migrations from existing databases.
14
+
15
+ Options:
16
+ --help, -h Show this help message
17
+ --author <name> Project author (default: from git config)
18
+ --extensionName <name> Extension name
19
+ --metaExtensionName <name> Meta extension name (default: svc)
20
+ --cwd <directory> Working directory (default: current directory)
21
+
22
+ Examples:
23
+ constructive export Export migrations from selected database
24
+ `;
25
+ exports.default = async (argv, prompter, _options) => {
26
+ // Show usage if explicitly requested
27
+ if (argv.help || argv.h) {
28
+ console.log(exportUsageText);
29
+ process.exit(0);
30
+ }
31
+ const { email, username } = (0, types_1.getGitConfigInfo)();
32
+ const cwd = argv.cwd ?? process.cwd();
33
+ const project = new core_1.LaunchQLPackage(cwd);
34
+ project.ensureWorkspace();
35
+ project.resetCwd(project.workspacePath);
36
+ const options = (0, env_1.getEnvOptions)();
37
+ const db = await (0, pg_cache_1.getPgPool)({
38
+ database: 'postgres'
39
+ });
40
+ const databasesResult = await db.query(`
41
+ SELECT datname FROM pg_catalog.pg_database
42
+ WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
43
+ AND datname !~ '^pg_';
44
+ `);
45
+ let databases;
46
+ ({ databases } = await prompter.prompt(argv, [
47
+ {
48
+ type: 'checkbox',
49
+ name: 'databases',
50
+ message: 'Select a database',
51
+ options: databasesResult.rows.map(row => row.datname),
52
+ required: true
53
+ }
54
+ ]));
55
+ const dbname = databases.filter(d => d.selected).map(d => d.value)[0];
56
+ const selectedDb = await (0, pg_cache_1.getPgPool)({
57
+ database: dbname
58
+ });
59
+ const dbsResult = await selectedDb.query(`
60
+ SELECT id, name FROM collections_public.database;
61
+ `);
62
+ let database_ids;
63
+ ({ database_ids } = await prompter.prompt({}, [
64
+ {
65
+ type: 'checkbox',
66
+ name: 'database_ids',
67
+ message: 'Select database_id(s)',
68
+ options: dbsResult.rows.map(db => db.name),
69
+ required: true
70
+ }
71
+ ]));
72
+ const dbInfo = {
73
+ dbname,
74
+ database_ids: database_ids.map(did => dbsResult.rows.find(db => db.name === did.name).id)
75
+ };
76
+ const { author, extensionName, metaExtensionName } = await prompter.prompt(argv, [
77
+ {
78
+ type: 'text',
79
+ name: 'author',
80
+ message: 'Project author',
81
+ default: `${username} <${email}>`,
82
+ required: true
83
+ },
84
+ {
85
+ type: 'text',
86
+ name: 'extensionName',
87
+ message: 'Extension name',
88
+ default: dbInfo.database_ids[0],
89
+ required: true
90
+ },
91
+ {
92
+ type: 'text',
93
+ name: 'metaExtensionName',
94
+ message: 'Meta extension name',
95
+ default: 'svc',
96
+ required: true
97
+ }
98
+ ]);
99
+ const schemasResult = await selectedDb.query(`SELECT * FROM collections_public.schema WHERE database_id = $1`, [dbInfo.database_ids[0]]);
100
+ const { schema_names } = await prompter.prompt({}, [
101
+ {
102
+ type: 'checkbox',
103
+ name: 'schema_names',
104
+ message: 'Select schema_name(s)',
105
+ options: schemasResult.rows.map(s => s.schema_name),
106
+ default: schemasResult.rows.map(s => s.schema_name),
107
+ required: true
108
+ }
109
+ ]);
110
+ const outdir = (0, path_1.resolve)(project.workspacePath, 'packages/');
111
+ await (0, core_1.exportMigrations)({
112
+ project,
113
+ options,
114
+ dbInfo,
115
+ author,
116
+ schema_names,
117
+ outdir,
118
+ extensionName,
119
+ metaExtensionName
120
+ });
121
+ console.log(`
122
+
123
+ |||
124
+ (o o)
125
+ ooO--(_)--Ooo-
126
+
127
+ ✨ finished!
128
+ `);
129
+ };
@@ -0,0 +1,4 @@
1
+ import { CLIOptions, Inquirerer } from 'inquirerer';
2
+ import { ParsedArgs } from 'minimist';
3
+ declare const _default: (argv: Partial<ParsedArgs>, prompter: Inquirerer, _options: CLIOptions) => Promise<void>;
4
+ export default _default;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@launchql/core");
4
+ const extensionUsageText = `
5
+ Extension Command:
6
+
7
+ constructive extension [OPTIONS]
8
+
9
+ Manage module dependencies.
10
+
11
+ Options:
12
+ --help, -h Show this help message
13
+ --cwd <directory> Working directory (default: current directory)
14
+
15
+ Examples:
16
+ constructive extension Manage dependencies for current module
17
+ `;
18
+ exports.default = async (argv, prompter, _options) => {
19
+ // Show usage if explicitly requested
20
+ if (argv.help || argv.h) {
21
+ console.log(extensionUsageText);
22
+ process.exit(0);
23
+ }
24
+ const { cwd = process.cwd() } = argv;
25
+ const project = new core_1.LaunchQLPackage(cwd);
26
+ if (!project.isInModule()) {
27
+ throw new Error('You must run this command inside a LaunchQL module.');
28
+ }
29
+ const info = project.getModuleInfo();
30
+ const installed = project.getRequiredModules();
31
+ const available = project.getAvailableModules();
32
+ const filtered = available.filter(name => name !== info.extname);
33
+ const questions = [
34
+ {
35
+ name: 'extensions',
36
+ message: 'Which modules does this one depend on?',
37
+ type: 'checkbox',
38
+ allowCustomOptions: true,
39
+ options: filtered,
40
+ default: installed
41
+ }
42
+ ];
43
+ const answers = await prompter.prompt(argv, questions);
44
+ const selected = answers.extensions
45
+ .filter(opt => opt.selected)
46
+ .map(opt => opt.name);
47
+ project.setModuleDependencies(selected);
48
+ };
@@ -0,0 +1,7 @@
1
+ import { CLIOptions, Inquirerer } from 'inquirerer';
2
+ declare const _default: (argv: Partial<Record<string, any>>, prompter: Inquirerer, _options: CLIOptions) => Promise<{
3
+ [x: string]: any;
4
+ } | {
5
+ cwd: string;
6
+ }>;
7
+ export default _default;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const module_1 = __importDefault(require("./module"));
7
+ const workspace_1 = __importDefault(require("./workspace"));
8
+ const initUsageText = `
9
+ Init Command:
10
+
11
+ constructive init [OPTIONS]
12
+
13
+ Initialize constructive workspace or module.
14
+
15
+ Options:
16
+ --help, -h Show this help message
17
+ --workspace Initialize workspace instead of module
18
+ --cwd <directory> Working directory (default: current directory)
19
+ --repo <repo> Use templates from GitHub repository (e.g., owner/repo)
20
+ --template-path <path> Use templates from local path
21
+ --from-branch <branch> Specify branch when using --repo (default: main)
22
+
23
+ Examples:
24
+ constructive init Initialize new module in existing workspace
25
+ constructive init --workspace Initialize new workspace
26
+ constructive init --repo owner/repo Use templates from GitHub repository
27
+ constructive init --template-path ./custom-templates Use templates from local path
28
+ constructive init --repo owner/repo --from-branch develop Use specific branch
29
+ `;
30
+ exports.default = async (argv, prompter, _options) => {
31
+ // Show usage if explicitly requested
32
+ if (argv.help || argv.h) {
33
+ console.log(initUsageText);
34
+ process.exit(0);
35
+ }
36
+ return handlePromptFlow(argv, prompter);
37
+ };
38
+ async function handlePromptFlow(argv, prompter) {
39
+ const { workspace } = argv;
40
+ switch (workspace) {
41
+ case true:
42
+ return (0, workspace_1.default)(argv, prompter);
43
+ case false:
44
+ default:
45
+ return (0, module_1.default)(argv, prompter);
46
+ }
47
+ }
@@ -0,0 +1,4 @@
1
+ import { Inquirerer } from 'inquirerer';
2
+ export default function runModuleSetup(argv: Partial<Record<string, any>>, prompter: Inquirerer): Promise<{
3
+ [x: string]: any;
4
+ }>;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = runModuleSetup;
4
+ const core_1 = require("@launchql/core");
5
+ const logger_1 = require("@launchql/logger");
6
+ const types_1 = require("@launchql/types");
7
+ const log = new logger_1.Logger('module-init');
8
+ async function runModuleSetup(argv, prompter) {
9
+ const { email, username } = (0, types_1.getGitConfigInfo)();
10
+ const { cwd = process.cwd() } = argv;
11
+ const project = new core_1.LaunchQLPackage(cwd);
12
+ if (!project.workspacePath) {
13
+ log.error('Not inside a LaunchQL workspace.');
14
+ throw types_1.errors.NOT_IN_WORKSPACE({});
15
+ }
16
+ if (!project.isInsideAllowedDirs(cwd) && !project.isInWorkspace() && !project.isParentOfAllowedDirs(cwd)) {
17
+ log.error('You must be inside the workspace root or a parent directory of modules (like packages/).');
18
+ throw types_1.errors.NOT_IN_WORKSPACE_MODULE({});
19
+ }
20
+ const availExtensions = project.getAvailableModules();
21
+ const moduleQuestions = [
22
+ {
23
+ name: 'MODULENAME',
24
+ message: 'Enter the module name',
25
+ required: true,
26
+ type: 'text',
27
+ },
28
+ {
29
+ name: 'extensions',
30
+ message: 'Which extensions?',
31
+ options: availExtensions,
32
+ type: 'checkbox',
33
+ allowCustomOptions: true,
34
+ required: true,
35
+ },
36
+ ];
37
+ const answers = await prompter.prompt(argv, moduleQuestions);
38
+ const modName = (0, core_1.sluggify)(answers.MODULENAME);
39
+ const extensions = answers.extensions
40
+ .filter((opt) => opt.selected)
41
+ .map((opt) => opt.name);
42
+ // Determine template source
43
+ let templateSource;
44
+ if (argv.repo) {
45
+ templateSource = {
46
+ type: 'github',
47
+ path: argv.repo,
48
+ branch: argv.fromBranch
49
+ };
50
+ log.info(`Loading templates from GitHub repository: ${argv.repo}`);
51
+ }
52
+ else if (argv.templatePath) {
53
+ templateSource = {
54
+ type: 'local',
55
+ path: argv.templatePath
56
+ };
57
+ log.info(`Loading templates from local path: ${argv.templatePath}`);
58
+ }
59
+ project.initModule({
60
+ ...argv,
61
+ ...answers,
62
+ name: modName,
63
+ // @ts-ignore
64
+ USERFULLNAME: username,
65
+ USEREMAIL: email,
66
+ extensions,
67
+ templateSource
68
+ });
69
+ log.success(`Initialized module: ${modName}`);
70
+ return { ...argv, ...answers };
71
+ }
@@ -0,0 +1,4 @@
1
+ import { Inquirerer } from 'inquirerer';
2
+ export default function runWorkspaceSetup(argv: Partial<Record<string, any>>, prompter: Inquirerer): Promise<{
3
+ cwd: string;
4
+ }>;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = runWorkspaceSetup;
7
+ const core_1 = require("@launchql/core");
8
+ const logger_1 = require("@launchql/logger");
9
+ // @ts-ignore - TypeScript module resolution issue with @launchql/templatizer
10
+ const templatizer_1 = require("@launchql/templatizer");
11
+ const fs_1 = require("fs");
12
+ const path_1 = __importDefault(require("path"));
13
+ const log = new logger_1.Logger('workspace-init');
14
+ async function runWorkspaceSetup(argv, prompter) {
15
+ const workspaceQuestions = [
16
+ {
17
+ name: 'name',
18
+ message: 'Enter workspace name',
19
+ required: true,
20
+ type: 'text',
21
+ }
22
+ ];
23
+ const answers = await prompter.prompt(argv, workspaceQuestions);
24
+ const { cwd } = argv;
25
+ const targetPath = path_1.default.join(cwd, (0, core_1.sluggify)(answers.name));
26
+ (0, fs_1.mkdirSync)(targetPath, { recursive: true });
27
+ log.success(`Created workspace directory: ${targetPath}`);
28
+ // Determine template source
29
+ let templates = templatizer_1.workspaceTemplate;
30
+ if (argv.repo) {
31
+ const source = {
32
+ type: 'github',
33
+ path: argv.repo,
34
+ branch: argv.fromBranch
35
+ };
36
+ log.info(`Loading templates from GitHub repository: ${argv.repo}`);
37
+ const compiledTemplates = (0, templatizer_1.loadTemplates)(source, 'workspace');
38
+ templates = compiledTemplates.map((t) => t.render);
39
+ }
40
+ else if (argv.templatePath) {
41
+ const source = {
42
+ type: 'local',
43
+ path: argv.templatePath
44
+ };
45
+ log.info(`Loading templates from local path: ${argv.templatePath}`);
46
+ const compiledTemplates = (0, templatizer_1.loadTemplates)(source, 'workspace');
47
+ templates = compiledTemplates.map((t) => t.render);
48
+ }
49
+ (0, templatizer_1.writeRenderedTemplates)(templates, targetPath, { ...argv, ...answers });
50
+ log.success('Workspace templates rendered.');
51
+ return { ...argv, ...answers, cwd: targetPath };
52
+ }
@@ -0,0 +1,4 @@
1
+ import { CLIOptions, Inquirerer } from 'inquirerer';
2
+ import { ParsedArgs } from 'minimist';
3
+ declare const _default: (argv: Partial<ParsedArgs>, prompter: Inquirerer, _options: CLIOptions) => Promise<void>;
4
+ export default _default;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@launchql/core");
4
+ const installUsageText = `
5
+ Install Command:
6
+
7
+ constructive install <package>...
8
+
9
+ Install database modules into current module.
10
+
11
+ Arguments:
12
+ package One or more package names to install
13
+
14
+ Options:
15
+ --help, -h Show this help message
16
+ --cwd <directory> Working directory (default: current directory)
17
+
18
+ Examples:
19
+ constructive install @pgpm/base32 Install single package
20
+ constructive install @pgpm/base32 @pgpm/utils Install multiple packages
21
+ `;
22
+ exports.default = async (argv, prompter, _options) => {
23
+ // Show usage if explicitly requested
24
+ if (argv.help || argv.h) {
25
+ console.log(installUsageText);
26
+ process.exit(0);
27
+ }
28
+ const { cwd = process.cwd() } = argv;
29
+ const project = new core_1.LaunchQLPackage(cwd);
30
+ if (!project.isInModule()) {
31
+ throw new Error('You must run this command inside a LaunchQL module.');
32
+ }
33
+ if (argv._.length === 0) {
34
+ throw new Error('You must provide a package name to install, e.g. `@pgpm/base32`');
35
+ }
36
+ await project.installModules(...argv._);
37
+ };
@@ -0,0 +1,3 @@
1
+ import { CLIOptions, Inquirerer } from 'inquirerer';
2
+ declare const _default: (argv: Partial<Record<string, any>>, prompter: Inquirerer, _options: CLIOptions) => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const logger_1 = require("@launchql/logger");
4
+ const pg_cache_1 = require("pg-cache");
5
+ const log = new logger_1.Logger('db-kill');
6
+ const killUsageText = `
7
+ Kill Command:
8
+
9
+ constructive kill [OPTIONS]
10
+
11
+ Terminate database connections and optionally drop databases.
12
+
13
+ Options:
14
+ --help, -h Show this help message
15
+ --drop Drop databases after killing connections (default: true)
16
+ --no-drop Only kill connections, don't drop databases
17
+ --pattern <pattern> Pattern to match database names (supports SQL LIKE syntax)
18
+ --cwd <directory> Working directory (default: current directory)
19
+
20
+ Examples:
21
+ constructive kill Kill connections and drop selected databases (interactive)
22
+ constructive kill --no-drop Only kill connections, preserve databases (interactive)
23
+ constructive kill --pattern test_% Kill connections to databases matching 'test_%' pattern
24
+ constructive kill --pattern %dev --no-drop Kill connections to databases ending with 'dev' but don't drop
25
+ `;
26
+ exports.default = async (argv, prompter, _options) => {
27
+ // Show usage if explicitly requested
28
+ if (argv.help || argv.h) {
29
+ console.log(killUsageText);
30
+ process.exit(0);
31
+ }
32
+ const db = await (0, pg_cache_1.getPgPool)({
33
+ database: 'postgres'
34
+ });
35
+ let selectedDbNames;
36
+ if (argv.pattern) {
37
+ // Pattern mode: automatically find databases matching the pattern
38
+ const databasesResult = await db.query(`
39
+ SELECT datname FROM pg_catalog.pg_database
40
+ WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
41
+ AND datname !~ '^pg_' AND datname LIKE $1;
42
+ `, [argv.pattern]);
43
+ if (!databasesResult.rows.length) {
44
+ log.info(`â„šī¸ No databases found matching pattern "${argv.pattern}". Exiting.`);
45
+ return;
46
+ }
47
+ selectedDbNames = databasesResult.rows.map(row => row.datname);
48
+ log.info(`đŸŽ¯ Found ${selectedDbNames.length} database(s) matching pattern "${argv.pattern}": ${selectedDbNames.join(', ')}`);
49
+ }
50
+ else {
51
+ // Interactive mode: prompt user to select databases
52
+ const databasesResult = await db.query(`
53
+ SELECT datname FROM pg_catalog.pg_database
54
+ WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
55
+ AND datname !~ '^pg_';
56
+ `);
57
+ if (!databasesResult.rows.length) {
58
+ log.info('â„šī¸ No databases found to process. Exiting.');
59
+ return;
60
+ }
61
+ let databases;
62
+ ({ databases } = await prompter.prompt(argv, [
63
+ {
64
+ type: 'checkbox',
65
+ name: 'databases',
66
+ message: 'Select database(s) to terminate connections and optionally drop',
67
+ options: databasesResult.rows.map(row => row.datname),
68
+ required: true
69
+ }
70
+ ]));
71
+ selectedDbNames = databases.filter(d => d.selected).map(d => d.value);
72
+ }
73
+ const actionText = argv.drop === false ? 'kill connections to' : 'kill connections and DROP';
74
+ const patternText = argv.pattern ? ` (matched by pattern "${argv.pattern}")` : '';
75
+ const { yes } = await prompter.prompt(argv, [
76
+ {
77
+ type: 'confirm',
78
+ name: 'yes',
79
+ message: `Are you sure you want to ${actionText}: ${selectedDbNames.join(', ')}${patternText}?`,
80
+ default: false
81
+ }
82
+ ]);
83
+ if (!yes) {
84
+ log.info('❌ Aborted. No actions were taken.');
85
+ return;
86
+ }
87
+ for (const dbname of selectedDbNames) {
88
+ const killResult = await db.query(`
89
+ SELECT pg_terminate_backend(pid)
90
+ FROM pg_stat_activity
91
+ WHERE datname = $1 AND pid <> pg_backend_pid();
92
+ `, [dbname]);
93
+ log.warn(`💀 Terminated ${killResult.rowCount} connection(s) to "${dbname}".`);
94
+ if (argv.drop === false) {
95
+ log.info(`âš ī¸ Skipping DROP for "${dbname}" due to --no-drop flag.`);
96
+ continue;
97
+ }
98
+ try {
99
+ await db.query(`DROP DATABASE "${dbname}";`);
100
+ log.success(`đŸ—‘ī¸ Dropped database "${dbname}" successfully.`);
101
+ }
102
+ catch (err) {
103
+ log.error(`❌ Failed to drop "${dbname}": ${err.message}`);
104
+ }
105
+ }
106
+ log.success('✅ Done processing databases.');
107
+ };
@@ -0,0 +1,4 @@
1
+ import { CLIOptions, Inquirerer } from 'inquirerer';
2
+ import { ParsedArgs } from 'minimist';
3
+ declare const _default: (argv: Partial<ParsedArgs>, prompter: Inquirerer, options: CLIOptions) => Promise<void>;
4
+ export default _default;