drizzle-auto 1.1.3 → 1.1.5
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 +144 -131
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,155 +1,168 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
3
|
const chokidar = require("chokidar");
|
|
6
4
|
const { spawn } = require("child_process");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
7
7
|
|
|
8
|
-
/*
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/* =============================================================
|
|
9
|
+
1. UI & COLOR CONFIGURATION (NeonPulse Style)
|
|
10
|
+
============================================================= */
|
|
11
11
|
const C = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
reset: "\x1b[0m",
|
|
13
|
+
bold: "\x1b[1m",
|
|
14
|
+
red: "\x1b[31m",
|
|
15
|
+
green: "\x1b[32m",
|
|
16
|
+
yellow: "\x1b[33m",
|
|
17
|
+
blue: "\x1b[34m",
|
|
18
|
+
magenta: "\x1b[35m",
|
|
19
|
+
cyan: "\x1b[36m",
|
|
20
|
+
dim: "\x1b[90m"
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
23
|
+
const PALETTE = [C.red, C.green, C.yellow, C.blue, C.magenta, C.cyan];
|
|
24
|
+
let colorIndex = 0;
|
|
25
|
+
const nextColor = () => PALETTE[colorIndex++ % PALETTE.length];
|
|
26
|
+
|
|
27
|
+
/* =============================================================
|
|
28
|
+
2. AUTOMATIC PACKAGE.JSON INJECTION (The "tea" Script)
|
|
29
|
+
============================================================= */
|
|
30
|
+
function injectTeaScript() {
|
|
31
|
+
const pkgPath = path.join(process.cwd(), "package.json");
|
|
32
|
+
if (fs.existsSync(pkgPath)) {
|
|
33
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
34
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
35
|
+
if (!pkg.scripts.tea) {
|
|
36
|
+
pkg.scripts.tea = "drizzle-auto";
|
|
37
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
38
|
+
console.log(`${C.green}✔ Added "tea": "drizzle-auto" to package.json${C.reset}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
function findConfig() {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
for (const e of exts) {
|
|
58
|
-
if (f.name === `drizzle.config.${e}`) {
|
|
59
|
-
found = p;
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
43
|
+
/* =============================================================
|
|
44
|
+
3. DEEP INFRASTRUCTURE JUSTIFICATION (Recursive Search)
|
|
45
|
+
============================================================= */
|
|
46
|
+
function findConfig(dir = process.cwd()) {
|
|
47
|
+
const files = fs.readdirSync(dir);
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
const fullPath = path.join(dir, file);
|
|
50
|
+
if (IGNORED_REGEX.test(fullPath)) continue;
|
|
51
|
+
|
|
52
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
53
|
+
const found = findConfig(fullPath);
|
|
54
|
+
if (found) return found;
|
|
55
|
+
} else if (/^drizzle\.config\.(js|mjs|cjs|ts|mts)$/.test(file)) {
|
|
56
|
+
return fullPath;
|
|
62
57
|
}
|
|
63
|
-
}
|
|
64
58
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return found;
|
|
59
|
+
return null;
|
|
68
60
|
}
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
function run(cmd, args) {
|
|
74
|
-
return new Promise((resolve) => {
|
|
75
|
-
let stderr = "";
|
|
62
|
+
function justifyInfrastructure() {
|
|
63
|
+
const configPath = findConfig();
|
|
64
|
+
if (!configPath) return { ok: false, msg: "Missing drizzle.config.*" };
|
|
76
65
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
66
|
+
try {
|
|
67
|
+
const content = fs.readFileSync(configPath, "utf8");
|
|
68
|
+
const schemaMatch = content.match(/schema:\s*["'](.+?)["']/);
|
|
69
|
+
if (schemaMatch && schemaMatch[1]) {
|
|
70
|
+
const schemaPath = path.resolve(path.dirname(configPath), schemaMatch[1]);
|
|
71
|
+
if (!fs.existsSync(schemaPath)) return { ok: false, msg: `Schema missing at ${schemaMatch[1]}` };
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return { ok: false, msg: "Config unreadable" };
|
|
75
|
+
}
|
|
76
|
+
return { ok: true, path: configPath };
|
|
77
|
+
}
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
/* =============================================================
|
|
80
|
+
4. SAFE COMMAND EXECUTION (Step-by-Step Chain)
|
|
81
|
+
============================================================= */
|
|
82
|
+
function execute(args, name) {
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
let hasError = false;
|
|
85
|
+
console.log(`${C.cyan}⏳ ${name}...${C.reset}`);
|
|
86
|
+
|
|
87
|
+
const child = spawn("npx", args, { shell: true, stdio: ["inherit", "pipe", "pipe"] });
|
|
88
|
+
|
|
89
|
+
child.stdout.on("data", (data) => {
|
|
90
|
+
const out = data.toString();
|
|
91
|
+
// Filter noise from Cloud DB pullers
|
|
92
|
+
if (!out.includes('Pulling schema')) process.stdout.write(`${C.dim}${out}${C.reset}`);
|
|
93
|
+
if (/error|failed|ENOTFOUND|ECONNREFUSED/i.test(out)) hasError = true;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
child.stderr.on("data", (data) => {
|
|
97
|
+
process.stderr.write(`${C.red}${data}${C.reset}`);
|
|
98
|
+
hasError = true;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
child.on("close", (code) => resolve(code === 0 && !hasError));
|
|
87
102
|
});
|
|
88
|
-
});
|
|
89
103
|
}
|
|
90
104
|
|
|
91
|
-
/*
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log(`${C.magenta}▶ drizzle-kit generate${C.reset}`);
|
|
110
|
-
const gen = await run("npx", ["drizzle-kit", "generate"]);
|
|
111
|
-
if (!gen.ok) {
|
|
112
|
-
console.log(`${C.red}🛑 Generate failed — watcher alive${C.reset}`);
|
|
113
|
-
busy = false;
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
console.log(`${C.magenta}▶ drizzle-kit push${C.reset}`);
|
|
118
|
-
const push = await run("npx", ["drizzle-kit", "push"]);
|
|
119
|
-
|
|
120
|
-
if (!push.ok) {
|
|
121
|
-
console.log(`\n${C.red}${C.bold}🚨 PUSH FAILED${C.reset}`);
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
push.stderr.includes("ENOTFOUND") ||
|
|
125
|
-
push.stderr.includes("websocket")
|
|
126
|
-
) {
|
|
127
|
-
console.log(
|
|
128
|
-
`${C.yellow}⚠ Neon / network issue detected${C.reset}\n` +
|
|
129
|
-
`${C.dim}• Check internet\n` +
|
|
130
|
-
`• Check DATABASE_URL\n` +
|
|
131
|
-
`• Neon endpoint may be sleeping${C.reset}`
|
|
132
|
-
);
|
|
105
|
+
/* =============================================================
|
|
106
|
+
5. DYNAMIC WORKFLOW MANAGER
|
|
107
|
+
============================================================= */
|
|
108
|
+
let isRunning = false;
|
|
109
|
+
async function startWorkflow(trigger) {
|
|
110
|
+
if (isRunning) return;
|
|
111
|
+
isRunning = true;
|
|
112
|
+
|
|
113
|
+
console.log(`\n${nextColor()}⚡ Triggered: ${trigger}${C.reset}`);
|
|
114
|
+
|
|
115
|
+
// Step 2: Justify Files
|
|
116
|
+
const infra = justifyInfrastructure();
|
|
117
|
+
if (!infra.ok) {
|
|
118
|
+
console.log(`${C.red}🛑 Infrastructure Failure: ${infra.msg}${C.reset}`);
|
|
119
|
+
isRunning = false;
|
|
120
|
+
return;
|
|
133
121
|
}
|
|
134
122
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
123
|
+
// Step 3 & 4: Sequence (Generate -> Push)
|
|
124
|
+
const steps = [
|
|
125
|
+
{ name: "Generating Schema", args: ["drizzle-kit", "generate"] },
|
|
126
|
+
{ name: "Pushing to Database", args: ["drizzle-kit", "push"] }
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
for (const step of steps) {
|
|
130
|
+
const ok = await execute(step.args, step.name);
|
|
131
|
+
if (!ok) {
|
|
132
|
+
console.log(`${C.red}${C.bold}✖ Pipeline Halted. Check logs above.${C.reset}`);
|
|
133
|
+
isRunning = false;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
139
137
|
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
console.log(`${C.green}${C.bold}✨ Success: All steps completed.${C.reset}`);
|
|
139
|
+
isRunning = false;
|
|
142
140
|
}
|
|
143
141
|
|
|
144
|
-
/*
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
142
|
+
/* =============================================================
|
|
143
|
+
6. UNIVERSAL WATCHER & INITIALIZATION
|
|
144
|
+
============================================================= */
|
|
145
|
+
const IGNORED_REGEX = /(node_modules|\.git|\.next|dist)/;
|
|
146
|
+
|
|
147
|
+
injectTeaScript();
|
|
148
|
+
|
|
149
|
+
const watcher = chokidar.watch(".", {
|
|
150
|
+
ignored: IGNORED_REGEX,
|
|
151
|
+
persistent: true,
|
|
152
|
+
ignoreInitial: true,
|
|
153
|
+
usePolling: true,
|
|
154
|
+
interval: 300
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
watcher.on("all", (event, file) => {
|
|
158
|
+
startWorkflow(`${event.toUpperCase()} -> ${path.basename(file)}`);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Initial startup audit
|
|
162
|
+
startWorkflow("Initial System Audit");
|
|
163
|
+
|
|
164
|
+
// Crash Protection
|
|
165
|
+
process.on("uncaughtException", (err) => {
|
|
166
|
+
console.log(`${C.red}🛡️ Shield: ${err.message}${C.reset}`);
|
|
167
|
+
isRunning = false;
|
|
168
|
+
});
|