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.
Files changed (2) hide show
  1. package/index.js +154 -160
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,191 +1,185 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * 🍵 Drizzle-Auto v2.3 - Flexible Config + Fail-Fast
5
- * 🔹 Detects any drizzle.config.{js,ts,mjs,cjs,mts} automatically
6
- * 🔹 Stops immediately if config or schema missing
7
- * 🔹 Beginner-friendly logs
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
- // ---------------- COLORS ----------------
22
+ // ---------------------- COLORS & UI ----------------------
18
23
  const T = {
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",
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
- let spinInterval;
29
- const frames = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
30
- let spinIdx = 0;
33
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
34
+ let spinIdx = 0, spinInterval;
31
35
 
32
- function startLoading(msg){
33
- stopLoading();
34
- spinInterval = setInterval(()=>{
35
- process.stdout.write(`\r${T.cyan}${frames[spinIdx++ % frames.length]}${T.reset} ${T.bold}${msg}${T.reset} `);
36
- },80);
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
- if(spinInterval){
41
- clearInterval(spinInterval);
42
- spinInterval=null;
43
- process.stdout.write("\r\x1b[K");
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
- // ---------------- FIND ANY DRIZZLE CONFIG ----------------
63
- function findConfig(){
64
- const allowedExts = ["js","ts","mjs","cjs","mts"];
65
- const rootFiles = fs.readdirSync(process.cwd());
66
- for(const file of rootFiles){
67
- const match = file.match(/^drizzle\.config\.(.+)$/i);
68
- if(match && allowedExts.includes(match[1])) return path.join(process.cwd(),file);
69
- }
70
- return null;
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
- // ---------------- CHECK SCHEMA ----------------
74
- function checkSchema(configPath){
75
- try{
76
- const cfg = require(configPath);
77
- const schemaPath = cfg.default?.schema || cfg.schema;
78
- if(!schemaPath){
79
- console.log(`${T.yellow}⚠ Warning: schema not defined in ${path.basename(configPath)}${T.reset}`);
80
- return true; // optional
81
- }
82
- const fullSchema = path.resolve(path.dirname(configPath), schemaPath);
83
- if(!fs.existsSync(fullSchema)){
84
- console.log(`${T.red}❌ ERROR: Schema file missing → ${fullSchema}${T.reset}`);
85
- process.exit(1);
86
- }
87
- return true;
88
- } catch(e){
89
- console.log(`${T.red}❌ ERROR: Cannot read config → ${e.message}${T.reset}`);
90
- process.exit(1);
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
- // ---------------- RUN SHELL COMMAND ----------------
95
- function runCommand(cmd,label){
96
- return new Promise((resolve,reject)=>{
97
- startLoading(label);
98
- const child = spawn(cmd,{shell:true,stdio:["inherit","inherit","pipe"]});
99
- let stderrData = "";
100
- child.stderr.on("data", d=>{
101
- stderrData += d.toString();
102
- process.stderr.write(d);
103
- });
104
- child.on("close", code=>{
105
- stopLoading();
106
- const hasError = code!==0 || /ErrorEvent|ENOTFOUND|failed to connect/i.test(stderrData);
107
- if(hasError) reject(new Error(`${label} failed!`));
108
- else resolve();
109
- });
110
- child.on("error", err=>{
111
- stopLoading();
112
- reject(err);
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
- // ---------------- ENGINE ----------------
118
- let isRunning=false;
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
- // ---------------- MAIN ----------------
152
- addTeaScript();
153
-
154
- const skipWatcher = process.argv.includes("--postinstall");
155
-
156
- if(skipWatcher){
157
- triggerEngine();
158
- }else{
159
- triggerEngine();
160
-
161
- try{
162
- const chokidar = require("chokidar");
163
- let timer;
164
- chokidar.watch(".",{
165
- ignored:[/node_modules/,/\.git/,/\.next/,/dist/,/out/],
166
- ignoreInitial:true,
167
- usePolling:true,
168
- interval:300
169
- }).on("all",(e,f)=>{
170
- clearTimeout(timer);
171
- timer=setTimeout(()=>{
172
- console.log(`${T.gray}🔄 Change detected, re-running...${T.reset}`);
173
- triggerEngine();
174
- },500);
175
- });
176
- console.log(`${T.gray}👀 Watching for changes in config/schema...${T.reset}`);
177
- } catch(e){
178
- console.log(`${T.yellow}⚠ Chokidar not installed. Watch mode disabled.${T.reset}`);
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
- // ---------------- GLOBAL ERROR CATCH ----------------
183
- process.on("uncaughtException",err=>{
184
- console.log(`${T.red}🛡️ UNCAUGHT ERROR: ${err.message}${T.reset}`);
185
- process.exit(1);
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
- console.log(`${T.yellow}Shutting down Drizzle-Auto...${T.reset}`);
190
- process.exit(0);
182
+ process.on("SIGINT", () => {
183
+ console.log(\n${T.yellow}Shutting down Drizzle-Auto...${T.reset});
184
+ process.exit(0);
191
185
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drizzle-auto",
3
- "version": "1.1.26",
3
+ "version": "1.1.28",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "pavel ahmmed hridoy",