pgflow 0.1.1 → 0.1.3

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.
@@ -0,0 +1,4 @@
1
+ import { type Command } from 'commander';
2
+ declare const _default: (program: Command) => void;
3
+ export default _default;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/compile/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;kCAkChB,OAAO;AAAhC,wBA4EE"}
@@ -0,0 +1,133 @@
1
+ import chalk from 'chalk';
2
+ import { intro, log, spinner, note } from '@clack/prompts';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import { spawn } from 'child_process';
6
+ import { fileURLToPath } from 'url';
7
+ // Get the directory name in ES modules
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ /**
11
+ * Formats a command and its arguments for display with syntax highlighting
12
+ * Each argument is displayed on a separate line for better readability
13
+ */
14
+ function formatCommand(command, args) {
15
+ const cmd = chalk.cyan(command);
16
+ const formattedArgs = args.map((arg) => {
17
+ // Highlight import map and file paths differently
18
+ if (arg.startsWith('--import-map=')) {
19
+ const [flag, value] = arg.split('=');
20
+ return ` ${chalk.yellow(flag)}=${chalk.green(value)}`;
21
+ }
22
+ else if (arg.startsWith('--')) {
23
+ return ` ${chalk.yellow(arg)}`;
24
+ }
25
+ else if (arg.endsWith('.ts') || arg.endsWith('.json')) {
26
+ return ` ${chalk.green(arg)}`;
27
+ }
28
+ return ` ${chalk.white(arg)}`;
29
+ });
30
+ return `$ ${cmd}\n${formattedArgs.join('\n')}`;
31
+ }
32
+ export default (program) => {
33
+ program
34
+ .command('compile')
35
+ .description('Compiles a TypeScript-defined flow into SQL migration')
36
+ .argument('<flowPath>', 'Path to the flow TypeScript file')
37
+ .option('--deno-json <denoJsonPath>', 'Path to deno.json with valid importMap')
38
+ .action(async (flowPath, options) => {
39
+ intro('pgflow - Compile Flow to SQL');
40
+ try {
41
+ // Resolve paths
42
+ const resolvedFlowPath = path.resolve(process.cwd(), flowPath);
43
+ // Only resolve denoJsonPath if it's provided
44
+ let resolvedDenoJsonPath;
45
+ if (options.denoJson) {
46
+ resolvedDenoJsonPath = path.resolve(process.cwd(), options.denoJson);
47
+ // Validate deno.json path if provided
48
+ if (!fs.existsSync(resolvedDenoJsonPath)) {
49
+ log.error(`deno.json file not found: ${resolvedDenoJsonPath}`);
50
+ process.exit(1);
51
+ }
52
+ }
53
+ // Validate flow path
54
+ if (!fs.existsSync(resolvedFlowPath)) {
55
+ log.error(`Flow file not found: ${resolvedFlowPath}`);
56
+ process.exit(1);
57
+ }
58
+ // Find the internal_compile.ts script
59
+ const internalCompileScript = path.resolve(__dirname, '../../../deno/internal_compile.ts');
60
+ // Create migrations directory if it doesn't exist
61
+ const migrationsDir = path.resolve(process.cwd(), 'migrations');
62
+ if (!fs.existsSync(migrationsDir)) {
63
+ fs.mkdirSync(migrationsDir, { recursive: true });
64
+ log.info(`Created migrations directory: ${migrationsDir}`);
65
+ }
66
+ // Generate timestamp for migration file
67
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '_');
68
+ const migrationFileName = `pgflow_${timestamp}.sql`;
69
+ const migrationFilePath = path.join(migrationsDir, migrationFileName);
70
+ // Run the compilation
71
+ const s = spinner();
72
+ s.start(`Compiling flow: ${path.basename(resolvedFlowPath)}`);
73
+ const compiledSql = await runDenoCompilation(internalCompileScript, resolvedFlowPath, resolvedDenoJsonPath);
74
+ // Write the SQL to a migration file
75
+ fs.writeFileSync(migrationFilePath, compiledSql);
76
+ s.stop(`Successfully compiled flow to SQL`);
77
+ log.success(`Migration file created: ${migrationFilePath}`);
78
+ }
79
+ catch (error) {
80
+ log.error(`Compilation failed: ${error instanceof Error ? error.message : String(error)}`);
81
+ process.exit(1);
82
+ }
83
+ });
84
+ };
85
+ /**
86
+ * Runs the Deno compilation script and returns the compiled SQL
87
+ */
88
+ async function runDenoCompilation(scriptPath, flowPath, denoJsonPath) {
89
+ return new Promise((resolve, reject) => {
90
+ // Validate input paths
91
+ if (!scriptPath) {
92
+ return reject(new Error('Internal script path is required'));
93
+ }
94
+ if (!flowPath) {
95
+ return reject(new Error('Flow path is required'));
96
+ }
97
+ // Build the command arguments array
98
+ const args = ['run', '--allow-read', '--allow-net', '--allow-env'];
99
+ // Only add the import-map argument if denoJsonPath is provided and valid
100
+ if (denoJsonPath && typeof denoJsonPath === 'string') {
101
+ args.push(`--import-map=${denoJsonPath}`);
102
+ }
103
+ // Add the script path and flow path
104
+ args.push(scriptPath, flowPath);
105
+ // Log the command for debugging with colored output
106
+ note(formatCommand('deno', args), 'Compile in Deno');
107
+ const deno = spawn('deno', args);
108
+ let stdout = '';
109
+ let stderr = '';
110
+ deno.stdout.on('data', (data) => {
111
+ stdout += data.toString();
112
+ });
113
+ deno.stderr.on('data', (data) => {
114
+ stderr += data.toString();
115
+ });
116
+ deno.on('close', (code) => {
117
+ if (code === 0) {
118
+ if (stdout.trim().length === 0) {
119
+ reject(new Error('Compilation produced no output'));
120
+ }
121
+ else {
122
+ resolve(stdout);
123
+ }
124
+ }
125
+ else {
126
+ reject(new Error(`Deno process exited with code ${code}${stderr ? `\n${stderr}` : ''}`));
127
+ }
128
+ });
129
+ deno.on('error', (err) => {
130
+ reject(new Error(`Failed to start Deno process: ${err.message}. Make sure Deno is installed.`));
131
+ });
132
+ });
133
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * internal_compile.ts
3
+ *
4
+ * This script is executed by Deno to compile a Flow into SQL statements.
5
+ * It takes a path to a flow file as an argument, imports the default export,
6
+ * and passes it to compileFlow() from the DSL package.
7
+ */
8
+
9
+ // Get the flow file path from command line arguments
10
+ const flowFilePath = Deno.args[0];
11
+
12
+ if (!flowFilePath) {
13
+ console.error('Error: No flow file path provided');
14
+ Deno.exit(1);
15
+ }
16
+
17
+ try {
18
+ // Dynamically import the flow file
19
+ const flowModule = await import(`file://${flowFilePath}`);
20
+
21
+ // Check if there's a default export
22
+ if (!flowModule.default) {
23
+ console.error(`Error: No default export found in ${flowFilePath}`);
24
+ Deno.exit(1);
25
+ }
26
+
27
+ // Import the DSL module
28
+ // The import map in deno.json will resolve this import
29
+ const dslModule = await import('npm:@pgflow/dsl');
30
+
31
+ // Debug available exports
32
+ console.error('Available exports from @pgflow/dsl:', Object.keys(dslModule));
33
+
34
+ // Get the flow instance
35
+ const flow = flowModule.default;
36
+
37
+ let compileFlow;
38
+ const sqlStatements;
39
+
40
+ // Try different ways to access the compileFlow function
41
+ if (typeof dslModule.compileFlow === 'function') {
42
+ // Direct export
43
+ compileFlow = dslModule.compileFlow;
44
+ } else if (
45
+ dslModule.default &&
46
+ typeof dslModule.default.compileFlow === 'function'
47
+ ) {
48
+ // Default export with compileFlow as a property
49
+ compileFlow = dslModule.default.compileFlow;
50
+ } else {
51
+ // Try to import the compile-flow module directly
52
+ try {
53
+ const compileFlowModule = await import(
54
+ 'npm:@pgflow/dsl/dist/compile-flow.js'
55
+ );
56
+ if (typeof compileFlowModule.compileFlow === 'function') {
57
+ compileFlow = compileFlowModule.compileFlow;
58
+ } else if (
59
+ compileFlowModule.default &&
60
+ typeof compileFlowModule.default === 'function'
61
+ ) {
62
+ compileFlow = compileFlowModule.default;
63
+ }
64
+ } catch (importError) {
65
+ console.error(
66
+ 'Failed to import compile-flow module:',
67
+ importError.message
68
+ );
69
+
70
+ // Try another path
71
+ try {
72
+ const altModule = await import('npm:@pgflow/dsl/src/compile-flow.js');
73
+ if (typeof altModule.compileFlow === 'function') {
74
+ compileFlow = altModule.compileFlow;
75
+ } else if (
76
+ altModule.default &&
77
+ typeof altModule.default === 'function'
78
+ ) {
79
+ compileFlow = altModule.default;
80
+ }
81
+ } catch (altError) {
82
+ console.error(
83
+ 'Failed to import alternative compile-flow module:',
84
+ altError.message
85
+ );
86
+ }
87
+ }
88
+ }
89
+
90
+ // Check if we found a valid compileFlow function
91
+ if (typeof compileFlow !== 'function') {
92
+ console.error('Error: compileFlow function not found in @pgflow/dsl');
93
+ console.error('Available exports:', Object.keys(dslModule));
94
+ Deno.exit(1);
95
+ }
96
+
97
+ // Compile the flow to SQL
98
+ sqlStatements = compileFlow(flow);
99
+
100
+ // Output the SQL statements to stdout
101
+ console.log(sqlStatements.join('\n'));
102
+ } catch (error) {
103
+ console.error(`Error compiling flow: ${error.message}`);
104
+ Deno.exit(1);
105
+ }
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'url';
4
4
  import { readFileSync } from 'fs';
5
5
  import { dirname, join } from 'path';
6
6
  import installCommand from './commands/install/index.js';
7
+ import compileCommand from './commands/compile/index.js';
7
8
  // Create a function to handle errors
8
9
  const errorHandler = (error) => {
9
10
  console.error('Error:', error instanceof Error ? error.message : String(error));
@@ -38,7 +39,31 @@ program
38
39
  }
39
40
  throw err;
40
41
  });
42
+ // Register commands
41
43
  installCommand(program);
44
+ compileCommand(program);
45
+ import chalk from 'chalk';
46
+ // Tokyo Night inspired colors
47
+ // const p = chalk.hex('#7aa2f7'); // blue-violet
48
+ const g = chalk.hex('#9ece6a'); // vibrant green
49
+ const f = chalk.hex('#bb9af7'); // light purple/pink
50
+ const l = chalk.hex('#2ac3de'); // bright teal/cyan
51
+ // const o = chalk.hex('#ff9e64'); // orange
52
+ // const w = chalk.hex('#f7768e'); // magenta/pink
53
+ const banner = [
54
+ ` ${l('__ _')} `,
55
+ ` ${g('_ __ __ _')} ${l('/ _| | _____ __')} `,
56
+ ` ${g("| '_ \\ / _'")} ${l('| |_| |/ _ \\ \\ /\\ / /')} `,
57
+ ` ${g('| |_) | (_|')} ${l('| _| | (_) \\ V V /')} `,
58
+ ` ${g('| .__/ \\__,')} ${l('|_| |_|\\___/ \\_/\\_/')} `,
59
+ ` ${g('|_| |___/')}`,
60
+ ``,
61
+ ` ${f('Postgres-native Workflow Engine')}`,
62
+ ].join('\n');
63
+ console.log(banner);
64
+ console.log();
65
+ console.log();
66
+ console.log();
42
67
  // Use a promise-aware approach to parse arguments
43
68
  async function main() {
44
69
  try {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgflow",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "typings": "./dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgflow",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "typings": "./dist/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  "chalk": "^5.4.1",
25
25
  "commander": "^13.1.0",
26
26
  "toml-patch": "^0.2.3",
27
- "@pgflow/core": "0.1.1"
27
+ "@pgflow/core": "0.1.3"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public",