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