drizzle-auto 1.1.23 → 1.1.25

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