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 @@
|
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgflow",
|
|
3
|
-
"version": "0.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.
|
|
27
|
+
"@pgflow/core": "0.1.3"
|
|
28
28
|
},
|
|
29
29
|
"publishConfig": {
|
|
30
30
|
"access": "public",
|