drizzle-auto 1.1.26 → 1.1.28
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.
- package/index.js +154 -160
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,191 +1,185 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
|
|
5
|
+
🍵 Drizzle-Auto v2.1 - Fail-Fast Edition
|
|
6
|
+
|
|
7
|
+
🔹 Strictly catches WebSocket/Neon ENOTFOUND errors
|
|
8
|
+
|
|
9
|
+
🔹 Stops immediately on any failure
|
|
10
|
+
|
|
11
|
+
🔹 Debounced watcher to prevent race conditions
|
|
12
|
+
*/
|
|
13
|
+
|
|
9
14
|
|
|
10
15
|
const { spawn } = require("child_process");
|
|
11
16
|
const path = require("path");
|
|
12
17
|
const fs = require("fs");
|
|
13
18
|
|
|
14
|
-
// TS support
|
|
15
|
-
try { require("ts-node").register({ transpileOnly: true }); } catch(e){}
|
|
19
|
+
// Optional TS support for config parsing
|
|
20
|
+
try { require("ts-node").register({ transpileOnly: true }); } catch (e) {}
|
|
16
21
|
|
|
17
|
-
//
|
|
22
|
+
// ---------------------- COLORS & UI ----------------------
|
|
18
23
|
const T = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
reset: "\x1b[0m",
|
|
25
|
+
bold: "\x1b[1m",
|
|
26
|
+
cyan: "\x1b[38;5;51m",
|
|
27
|
+
lime: "\x1b[38;5;118m",
|
|
28
|
+
yellow: "\x1b[38;5;226m",
|
|
29
|
+
red: "\x1b[38;5;196m",
|
|
30
|
+
gray: "\x1b[38;5;244m",
|
|
26
31
|
};
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
let spinIdx = 0;
|
|
33
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
34
|
+
let spinIdx = 0, spinInterval;
|
|
31
35
|
|
|
32
|
-
function startLoading(msg){
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
function startLoading(msg) {
|
|
37
|
+
stopLoading();
|
|
38
|
+
spinInterval = setInterval(() => {
|
|
39
|
+
process.stdout.write(\r${T.cyan}${frames[spinIdx++ % frames.length]}${T.reset} ${T.bold}${msg}${T.reset} );
|
|
40
|
+
}, 80);
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
function stopLoading(){
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
43
|
+
function stopLoading() {
|
|
44
|
+
if (spinInterval) {
|
|
45
|
+
clearInterval(spinInterval);
|
|
46
|
+
spinInterval = null;
|
|
47
|
+
process.stdout.write("\r\x1b[K");
|
|
45
48
|
}
|
|
46
|
-
|
|
47
|
-
// ---------------- ADD "tea" SCRIPT ----------------
|
|
48
|
-
const PKG = path.join(process.cwd(),"package.json");
|
|
49
|
-
function addTeaScript(){
|
|
50
|
-
if(!fs.existsSync(PKG)) return;
|
|
51
|
-
try{
|
|
52
|
-
const pkg = JSON.parse(fs.readFileSync(PKG,"utf8"));
|
|
53
|
-
pkg.scripts ||= {};
|
|
54
|
-
if(!pkg.scripts.tea){
|
|
55
|
-
pkg.scripts.tea = "drizzle-auto";
|
|
56
|
-
fs.writeFileSync(PKG, JSON.stringify(pkg,null,2));
|
|
57
|
-
console.log(`${T.lime}🍵 Added script → npm run tea${T.reset}`);
|
|
58
|
-
}
|
|
59
|
-
} catch(e){}
|
|
60
49
|
}
|
|
61
50
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
51
|
+
// ---------------------- UTILS ----------------------
|
|
52
|
+
const PKG_PATH = path.join(process.cwd(), "package.json");
|
|
53
|
+
function injectScript() {
|
|
54
|
+
if (!fs.existsSync(PKG_PATH)) return;
|
|
55
|
+
try {
|
|
56
|
+
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, "utf8"));
|
|
57
|
+
pkg.scripts ||= {};
|
|
58
|
+
if (!pkg.scripts.tea) {
|
|
59
|
+
pkg.scripts.tea = "drizzle-auto";
|
|
60
|
+
fs.writeFileSync(PKG_PATH, JSON.stringify(pkg, null, 2));
|
|
61
|
+
console.log(${T.lime}🍵 Script added → npm run tea${T.reset});
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {}
|
|
71
64
|
}
|
|
72
65
|
|
|
73
|
-
//
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
66
|
+
// ---------------------- COMMAND RUNNER (STRICT) ----------------------
|
|
67
|
+
function runCommand(cmd, label) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
startLoading(label);
|
|
70
|
+
|
|
71
|
+
// We pipe stderr to catch hidden WebSocket/Neon errors that don't trigger exit codes
|
|
72
|
+
const child = spawn(cmd, { shell: true, stdio: ["inherit", "inherit", "pipe"] });
|
|
73
|
+
let errorOutput = "";
|
|
74
|
+
|
|
75
|
+
child.stderr.on("data", (data) => {
|
|
76
|
+
const str = data.toString();
|
|
77
|
+
errorOutput += str;
|
|
78
|
+
process.stderr.write(str); // Still show the error to the user
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
child.on("close", (code) => {
|
|
82
|
+
stopLoading();
|
|
83
|
+
|
|
84
|
+
// Strict failure check:
|
|
85
|
+
// 1. Non-zero exit code
|
|
86
|
+
// 2. Presence of "ErrorEvent" or "ENOTFOUND" in stderr (Neon driver specific)
|
|
87
|
+
const hasDriverError = /ErrorEvent|ENOTFOUND|failed to connect/i.test(errorOutput);
|
|
88
|
+
|
|
89
|
+
if (code !== 0 || hasDriverError) {
|
|
90
|
+
reject(new Error(`${label} failed execution.`));
|
|
91
|
+
} else {
|
|
92
|
+
resolve();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
child.on("error", (err) => {
|
|
97
|
+
stopLoading();
|
|
98
|
+
reject(err);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
});
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
// ---------------------- ENGINE ----------------------
|
|
105
|
+
let isRunning = false;
|
|
106
|
+
let isBroken = false;
|
|
107
|
+
|
|
108
|
+
async function triggerEngine() {
|
|
109
|
+
if (isRunning) return;
|
|
110
|
+
isRunning = true;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const steps = [
|
|
114
|
+
{ name: "Integrity Check", cmd: "npx drizzle-kit check" },
|
|
115
|
+
{ name: "Generate Migration", cmd: "npx drizzle-kit generate" },
|
|
116
|
+
{ name: "Push Database", cmd: "npx drizzle-kit push" }
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
for (const step of steps) {
|
|
120
|
+
await runCommand(step.cmd, step.name);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log(`\n${T.lime}${T.bold}🎉 SUCCESS: Database synced.${T.reset}\n`);
|
|
124
|
+
isBroken = false;
|
|
125
|
+
|
|
126
|
+
} catch (err) {
|
|
127
|
+
isBroken = true;
|
|
128
|
+
console.log(\n${T.red}${T.bold}🚨 CRITICAL STOP:${T.reset} ${err.message});
|
|
129
|
+
console.log(${T.yellow}Execution paused. Fix the error and save any file to retry.${T.reset}\n);
|
|
130
|
+
|
|
131
|
+
if (process.argv.includes('--postinstall')) {
|
|
132
|
+
process.exit(1);
|
|
115
133
|
}
|
|
116
134
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
async function triggerEngine(){
|
|
120
|
-
if(isRunning) return;
|
|
121
|
-
isRunning=true;
|
|
122
|
-
|
|
123
|
-
const configPath = findConfig();
|
|
124
|
-
if(!configPath){
|
|
125
|
-
console.log(`${T.red}❌ ERROR: No drizzle config found!${T.reset}`);
|
|
126
|
-
console.log(`${T.yellow}Expected one of: drizzle.config.js/ts/mjs/cjs/mts${T.reset}`);
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
checkSchema(configPath);
|
|
131
|
-
|
|
132
|
-
const steps = [
|
|
133
|
-
{name:"Integrity Check", cmd:"npx drizzle-kit check"},
|
|
134
|
-
{name:"Generate Migration", cmd:"npx drizzle-kit generate"},
|
|
135
|
-
{name:"Push Database", cmd:"npx drizzle-kit push"}
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
try{
|
|
139
|
-
for(const step of steps){
|
|
140
|
-
await runCommand(step.cmd,step.name);
|
|
141
|
-
}
|
|
142
|
-
console.log(`\n${T.lime}🎉 SUCCESS: All steps finished.${T.reset}\n`);
|
|
143
|
-
} catch(e){
|
|
144
|
-
console.log(`\n${T.red}🚨 CRITICAL STOP: ${e.message}${T.reset}`);
|
|
145
|
-
process.exit(1);
|
|
146
|
-
} finally{
|
|
147
|
-
isRunning=false;
|
|
148
|
-
}
|
|
135
|
+
} finally {
|
|
136
|
+
isRunning = false;
|
|
149
137
|
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------- MAIN / WATCHER ----------------------
|
|
141
|
+
injectScript();
|
|
142
|
+
|
|
143
|
+
const skipWatcher = process.argv.includes('--postinstall');
|
|
150
144
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
145
|
+
if (skipWatcher) {
|
|
146
|
+
triggerEngine();
|
|
147
|
+
} else {
|
|
148
|
+
triggerEngine();
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const chokidar = require("chokidar");
|
|
152
|
+
let debounceTimer;
|
|
153
|
+
|
|
154
|
+
const watcher = chokidar.watch(".", {
|
|
155
|
+
ignored: [/node_modules/, /\.git/, /\.next/, /dist/, /out/],
|
|
156
|
+
ignoreInitial: true,
|
|
157
|
+
usePolling: true,
|
|
158
|
+
interval: 300
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
watcher.on("all", (event, path) => {
|
|
162
|
+
clearTimeout(debounceTimer);
|
|
163
|
+
debounceTimer = setTimeout(() => {
|
|
164
|
+
if (isBroken) console.log(`${T.cyan}🔄 Change detected in ${path}. Retrying...${T.reset}`);
|
|
165
|
+
triggerEngine();
|
|
166
|
+
}, 500);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
console.log(`${T.gray}👀 Watching for schema changes...${T.reset}`);
|
|
170
|
+
|
|
171
|
+
} catch (e) {
|
|
172
|
+
console.log(${T.yellow}⚠ Chokidar not found. Watch mode disabled.${T.reset});
|
|
173
|
+
}
|
|
180
174
|
}
|
|
181
175
|
|
|
182
|
-
//
|
|
183
|
-
process.on("uncaughtException",err=>{
|
|
184
|
-
|
|
185
|
-
|
|
176
|
+
// ---------------------- GLOBAL ERROR CATCH ----------------------
|
|
177
|
+
process.on("uncaughtException", (err) => {
|
|
178
|
+
console.log(\n${T.red}${T.bold}🛡️ UNCAUGHT ERROR:${T.reset} ${err.message});
|
|
179
|
+
process.exit(1);
|
|
186
180
|
});
|
|
187
181
|
|
|
188
|
-
process.on("SIGINT",()=>{
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
process.on("SIGINT", () => {
|
|
183
|
+
console.log(\n${T.yellow}Shutting down Drizzle-Auto...${T.reset});
|
|
184
|
+
process.exit(0);
|
|
191
185
|
});
|