drizzle-auto 1.1.28 → 1.1.30
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 +127 -140
- 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.2 - Strict Validation Edition
|
|
5
|
+
* 🔹 Checks Config -> Checks Schema -> Runs Drizzle
|
|
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,83 @@ try { require("ts-node").register({ transpileOnly: true }); } catch (e) {}
|
|
|
21
16
|
|
|
22
17
|
// ---------------------- COLORS & UI ----------------------
|
|
23
18
|
const T = {
|
|
24
|
-
reset: "\x1b[0m",
|
|
25
|
-
|
|
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", bold: "\x1b[1m", cyan: "\x1b[38;5;51m",
|
|
20
|
+
lime: "\x1b[38;5;118m", yellow: "\x1b[38;5;226m", red: "\x1b[38;5;196m", gray: "\x1b[38;5;244m",
|
|
31
21
|
};
|
|
32
22
|
|
|
33
23
|
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
34
24
|
let spinIdx = 0, spinInterval;
|
|
35
25
|
|
|
36
26
|
function startLoading(msg) {
|
|
37
|
-
stopLoading();
|
|
38
|
-
spinInterval = setInterval(() => {
|
|
39
|
-
process.stdout.write(
|
|
40
|
-
}, 80);
|
|
27
|
+
stopLoading();
|
|
28
|
+
spinInterval = setInterval(() => {
|
|
29
|
+
process.stdout.write(`\r${T.cyan}${frames[spinIdx++ % frames.length]}${T.reset} ${T.bold}${msg}${T.reset} `);
|
|
30
|
+
}, 80);
|
|
41
31
|
}
|
|
42
32
|
|
|
43
33
|
function stopLoading() {
|
|
44
|
-
if (spinInterval) {
|
|
45
|
-
|
|
46
|
-
spinInterval = null;
|
|
47
|
-
process.stdout.write("\r\x1b[K");
|
|
48
|
-
}
|
|
34
|
+
if (spinInterval) { clearInterval(spinInterval); spinInterval = null; }
|
|
35
|
+
process.stdout.write("\r\x1b[K");
|
|
49
36
|
}
|
|
50
37
|
|
|
51
|
-
// ----------------------
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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) {}
|
|
64
|
-
}
|
|
65
|
-
|
|
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 = "";
|
|
38
|
+
// ---------------------- VALIDATION LOGIC ----------------------
|
|
39
|
+
function validateProject() {
|
|
40
|
+
const root = process.cwd();
|
|
41
|
+
const configFiles = ["drizzle.config.ts", "drizzle.config.js", "drizzle.config.mjs", "drizzle.config.cjs"];
|
|
42
|
+
const configPath = configFiles.map(f => path.join(root, f)).find(f => fs.existsSync(f));
|
|
74
43
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
process.stderr.write(str); // Still show the error to the user
|
|
79
|
-
});
|
|
44
|
+
if (!configPath) {
|
|
45
|
+
return { ok: false, msg: "drizzle.config.[ts|js] not found in root." };
|
|
46
|
+
}
|
|
80
47
|
|
|
81
|
-
|
|
82
|
-
|
|
48
|
+
try {
|
|
49
|
+
// Basic regex check for schema path to avoid complex module loading issues
|
|
50
|
+
const configContent = fs.readFileSync(configPath, "utf8");
|
|
51
|
+
const schemaMatch = configContent.match(/schema:\s*["'](.+?)["']/);
|
|
83
52
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
reject(err);
|
|
99
|
-
});
|
|
53
|
+
if (!schemaMatch) {
|
|
54
|
+
return { ok: false, msg: `Could not find 'schema' path inside ${path.basename(configPath)}` };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const schemaPath = path.resolve(root, schemaMatch[1]);
|
|
58
|
+
if (!fs.existsSync(schemaPath)) {
|
|
59
|
+
return { ok: false, msg: `Schema file defined in config does not exist: ${schemaMatch[1]}` };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { ok: true, config: configPath, schema: schemaPath };
|
|
63
|
+
} catch (e) {
|
|
64
|
+
return { ok: false, msg: `Error reading config: ${e.message}` };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
100
67
|
|
|
101
|
-
|
|
68
|
+
// ---------------------- COMMAND RUNNER (STRICT) ----------------------
|
|
69
|
+
function runCommand(cmd, label) {
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
startLoading(label);
|
|
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);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
child.on("close", (code) => {
|
|
82
|
+
stopLoading();
|
|
83
|
+
const hasDriverError = /ErrorEvent|ENOTFOUND|failed to connect|syscall: 'getaddrinfo'/i.test(errorOutput);
|
|
84
|
+
if (code !== 0 || hasDriverError) {
|
|
85
|
+
reject(new Error(`${label} failed.`));
|
|
86
|
+
} else {
|
|
87
|
+
resolve();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
child.on("error", (err) => {
|
|
92
|
+
stopLoading();
|
|
93
|
+
reject(err);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
// ---------------------- ENGINE ----------------------
|
|
@@ -106,80 +100,73 @@ let isRunning = false;
|
|
|
106
100
|
let isBroken = false;
|
|
107
101
|
|
|
108
102
|
async function triggerEngine() {
|
|
109
|
-
if (isRunning) return;
|
|
110
|
-
isRunning = true;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
}
|
|
103
|
+
if (isRunning) return;
|
|
104
|
+
isRunning = true;
|
|
105
|
+
|
|
106
|
+
// 1. Check Config and Schema first
|
|
107
|
+
const check = validateProject();
|
|
108
|
+
if (!check.ok) {
|
|
109
|
+
console.log(`\n${T.red}${T.bold}📁 CONFIG ERROR:${T.reset} ${check.msg}`);
|
|
110
|
+
isBroken = true;
|
|
111
|
+
isRunning = false;
|
|
112
|
+
if (process.argv.includes('--postinstall')) process.exit(1);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const steps = [
|
|
118
|
+
{ name: "Integrity Check", cmd: "npx drizzle-kit check" },
|
|
119
|
+
{ name: "Generate Migration", cmd: "npx drizzle-kit generate" },
|
|
120
|
+
{ name: "Push Database", cmd: "npx drizzle-kit push" }
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
for (const step of steps) {
|
|
124
|
+
await runCommand(step.cmd, step.name);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log(`\n${T.lime}${T.bold}🎉 SUCCESS: Database synced.${T.reset}\n`);
|
|
128
|
+
isBroken = false;
|
|
129
|
+
} catch (err) {
|
|
130
|
+
isBroken = true;
|
|
131
|
+
console.log(`\n${T.red}${T.bold}🚨 CRITICAL STOP:${T.reset} ${err.message}`);
|
|
132
|
+
console.log(`${T.yellow}Execution paused. Fix the code/connection and save to retry.${T.reset}\n`);
|
|
133
|
+
if (process.argv.includes('--postinstall')) process.exit(1);
|
|
134
|
+
} finally {
|
|
135
|
+
isRunning = false;
|
|
136
|
+
}
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
// ---------------------- MAIN / WATCHER ----------------------
|
|
141
|
-
injectScript();
|
|
142
|
-
|
|
143
140
|
const skipWatcher = process.argv.includes('--postinstall');
|
|
144
141
|
|
|
145
142
|
if (skipWatcher) {
|
|
146
|
-
triggerEngine();
|
|
143
|
+
triggerEngine();
|
|
147
144
|
} else {
|
|
148
|
-
triggerEngine();
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
} catch (e) {
|
|
172
|
-
console.log(${T.yellow}⚠ Chokidar not found. Watch mode disabled.${T.reset});
|
|
173
|
-
}
|
|
145
|
+
triggerEngine();
|
|
146
|
+
try {
|
|
147
|
+
const chokidar = require("chokidar");
|
|
148
|
+
let debounceTimer;
|
|
149
|
+
const watcher = chokidar.watch(".", {
|
|
150
|
+
ignored: [/node_modules/, /\.git/, /\.next/, /dist/],
|
|
151
|
+
ignoreInitial: true,
|
|
152
|
+
usePolling: true,
|
|
153
|
+
interval: 300
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
watcher.on("all", (event, path) => {
|
|
157
|
+
clearTimeout(debounceTimer);
|
|
158
|
+
debounceTimer = setTimeout(() => {
|
|
159
|
+
if (isBroken) console.log(`${T.cyan}🔄 Fix detected in ${path}. Retrying...${T.reset}`);
|
|
160
|
+
triggerEngine();
|
|
161
|
+
}, 500);
|
|
162
|
+
});
|
|
163
|
+
console.log(`${T.gray}👀 Watching for schema changes...${T.reset}`);
|
|
164
|
+
} catch (e) {
|
|
165
|
+
console.log(`${T.yellow}⚠ Chokidar not found. Watch mode disabled.${T.reset}`);
|
|
166
|
+
}
|
|
174
167
|
}
|
|
175
168
|
|
|
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);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
169
|
process.on("SIGINT", () => {
|
|
183
|
-
console.log(
|
|
184
|
-
process.exit(0);
|
|
185
|
-
});
|
|
170
|
+
console.log(`\n${T.yellow}Shutting down...${T.reset}`);
|
|
171
|
+
process.exit(0);
|
|
172
|
+
});
|