zuro-cli 0.0.2-beta.13 → 0.0.2-beta.15

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # zuro-cli
2
2
 
3
- A CLI for scaffolding backend foundations and modules in your project.
3
+ A CLI for adding production-ready backend modules you own, without framework lock-in.
4
4
 
5
5
  ## init
6
6
 
@@ -31,6 +31,7 @@ npx zuro-cli add auth
31
31
  Available modules include:
32
32
 
33
33
  - `database`
34
+ - `uploads`
34
35
  - `auth`
35
36
  - `validator`
36
37
  - `error-handler`
@@ -0,0 +1,322 @@
1
+ import {
2
+ escapeRegex
3
+ } from "./chunk-VMOTWTER.mjs";
4
+
5
+ // src/handlers/database.handler.ts
6
+ import path from "path";
7
+ import fs from "fs-extra";
8
+ import prompts from "prompts";
9
+ import chalk from "chalk";
10
+ var DATABASE_MODULE_MAP = {
11
+ drizzle: {
12
+ postgresql: "database-pg",
13
+ mysql: "database-mysql"
14
+ },
15
+ prisma: {
16
+ postgresql: "database-prisma-pg",
17
+ mysql: "database-prisma-mysql"
18
+ }
19
+ };
20
+ var DEFAULT_DATABASE_URLS = {
21
+ "database-pg": "postgresql://postgres@localhost:5432/mydb",
22
+ "database-mysql": "mysql://root@localhost:3306/mydb",
23
+ "database-prisma-pg": "postgresql://postgres@localhost:5432/mydb",
24
+ "database-prisma-mysql": "mysql://root@localhost:3306/mydb"
25
+ };
26
+ function parseDatabaseDialect(value) {
27
+ const normalized = value?.trim().toLowerCase();
28
+ if (!normalized) {
29
+ return null;
30
+ }
31
+ if (normalized === "pg" || normalized === "postgres" || normalized === "postgresql" || normalized === "database-pg" || normalized === "drizzle-pg" || normalized === "drizzle-postgres" || normalized === "database-drizzle-pg") {
32
+ return "database-pg";
33
+ }
34
+ if (normalized === "mysql" || normalized === "database-mysql" || normalized === "drizzle-mysql" || normalized === "database-drizzle-mysql") {
35
+ return "database-mysql";
36
+ }
37
+ if (normalized === "database-prisma-pg" || normalized === "prisma-pg" || normalized === "prisma-postgres" || normalized === "prisma-postgresql" || normalized === "database-prisma") {
38
+ return "database-prisma-pg";
39
+ }
40
+ if (normalized === "database-prisma-mysql" || normalized === "prisma-mysql") {
41
+ return "database-prisma-mysql";
42
+ }
43
+ return null;
44
+ }
45
+ function isDatabaseModule(moduleName) {
46
+ return moduleName === "database-pg" || moduleName === "database-mysql" || moduleName === "database-prisma-pg" || moduleName === "database-prisma-mysql";
47
+ }
48
+ function isDrizzleDatabaseModule(moduleName) {
49
+ return moduleName === "database-pg" || moduleName === "database-mysql";
50
+ }
51
+ function getDatabaseSelection(moduleName) {
52
+ if (moduleName === "database-pg") {
53
+ return { orm: "drizzle", dialect: "postgresql" };
54
+ }
55
+ if (moduleName === "database-mysql") {
56
+ return { orm: "drizzle", dialect: "mysql" };
57
+ }
58
+ if (moduleName === "database-prisma-pg") {
59
+ return { orm: "prisma", dialect: "postgresql" };
60
+ }
61
+ return { orm: "prisma", dialect: "mysql" };
62
+ }
63
+ function getDatabaseModule(orm, dialect) {
64
+ return DATABASE_MODULE_MAP[orm][dialect];
65
+ }
66
+ function parsePrismaProvider(schemaContent) {
67
+ const match = schemaContent.match(/provider\s*=\s*"([^"]+)"/);
68
+ if (!match) {
69
+ return null;
70
+ }
71
+ const provider = match[1]?.trim().toLowerCase();
72
+ if (provider === "mysql") {
73
+ return "mysql";
74
+ }
75
+ if (provider === "postgresql" || provider === "postgres") {
76
+ return "postgresql";
77
+ }
78
+ return null;
79
+ }
80
+ function validateDatabaseUrl(rawUrl, moduleName) {
81
+ const { dialect } = getDatabaseSelection(moduleName);
82
+ const dbUrl = rawUrl.trim();
83
+ if (!dbUrl) {
84
+ throw new Error("Database URL cannot be empty.");
85
+ }
86
+ let parsed;
87
+ try {
88
+ parsed = new URL(dbUrl);
89
+ } catch {
90
+ throw new Error(`Invalid database URL: '${dbUrl}'.`);
91
+ }
92
+ const protocol = parsed.protocol.toLowerCase();
93
+ if (dialect === "postgresql" && protocol !== "postgresql:" && protocol !== "postgres:") {
94
+ throw new Error("PostgreSQL URL must start with postgres:// or postgresql://");
95
+ }
96
+ if (dialect === "mysql" && protocol !== "mysql:") {
97
+ throw new Error("MySQL URL must start with mysql://");
98
+ }
99
+ return dbUrl;
100
+ }
101
+ async function detectInstalledDatabaseDialect(projectRoot, srcDir) {
102
+ const dbIndexPath = path.join(projectRoot, srcDir, "db", "index.ts");
103
+ const prismaSchemaPath = path.join(projectRoot, "prisma", "schema.prisma");
104
+ if (fs.existsSync(dbIndexPath)) {
105
+ const content = await fs.readFile(dbIndexPath, "utf-8");
106
+ if (content.includes("drizzle-orm/node-postgres") || content.includes(`from "pg"`)) {
107
+ return "database-pg";
108
+ }
109
+ if (content.includes("drizzle-orm/mysql2") || content.includes(`from "mysql2`)) {
110
+ return "database-mysql";
111
+ }
112
+ if (content.includes(`from "@prisma/client"`)) {
113
+ if (fs.existsSync(prismaSchemaPath)) {
114
+ const schemaContent = await fs.readFile(prismaSchemaPath, "utf-8");
115
+ const dialect = parsePrismaProvider(schemaContent);
116
+ if (dialect === "mysql") {
117
+ return "database-prisma-mysql";
118
+ }
119
+ }
120
+ return "database-prisma-pg";
121
+ }
122
+ }
123
+ if (fs.existsSync(prismaSchemaPath)) {
124
+ const schemaContent = await fs.readFile(prismaSchemaPath, "utf-8");
125
+ const dialect = parsePrismaProvider(schemaContent);
126
+ if (dialect === "mysql") {
127
+ return "database-prisma-mysql";
128
+ }
129
+ return "database-prisma-pg";
130
+ }
131
+ return null;
132
+ }
133
+ async function backupDatabaseFiles(projectRoot, srcDir) {
134
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
135
+ const backupRoot = path.join(projectRoot, ".zuro", "backups", `database-${timestamp}`);
136
+ const candidates = [
137
+ path.join(projectRoot, srcDir, "db", "index.ts"),
138
+ path.join(projectRoot, "drizzle.config.ts"),
139
+ path.join(projectRoot, "prisma", "schema.prisma")
140
+ ];
141
+ let copied = false;
142
+ for (const filePath of candidates) {
143
+ if (!fs.existsSync(filePath)) {
144
+ continue;
145
+ }
146
+ const relativePath = path.relative(projectRoot, filePath);
147
+ const backupPath = path.join(backupRoot, relativePath);
148
+ await fs.ensureDir(path.dirname(backupPath));
149
+ await fs.copyFile(filePath, backupPath);
150
+ copied = true;
151
+ }
152
+ return copied ? backupRoot : null;
153
+ }
154
+ function databaseLabel(moduleName) {
155
+ const selection = getDatabaseSelection(moduleName);
156
+ const ormLabel = selection.orm === "drizzle" ? "Drizzle" : "Prisma";
157
+ const dialectLabel = selection.dialect === "postgresql" ? "PostgreSQL" : "MySQL";
158
+ return `${ormLabel} (${dialectLabel})`;
159
+ }
160
+ function getDatabaseSetupHint(moduleName, dbUrl) {
161
+ try {
162
+ const parsed = new URL(dbUrl);
163
+ const dbName = parsed.pathname.replace(/^\/+/, "") || "mydb";
164
+ if (moduleName === "database-pg") {
165
+ return `createdb ${dbName}`;
166
+ }
167
+ return `mysql -e "CREATE DATABASE IF NOT EXISTS ${dbName};"`;
168
+ } catch {
169
+ return moduleName === "database-pg" ? "createdb <database_name>" : `mysql -e "CREATE DATABASE IF NOT EXISTS <database_name>;"`;
170
+ }
171
+ }
172
+ async function ensureSchemaExport(projectRoot, srcDir, schemaFileName) {
173
+ const schemaIndexPath = path.join(projectRoot, srcDir, "db", "schema", "index.ts");
174
+ if (!await fs.pathExists(schemaIndexPath)) {
175
+ return;
176
+ }
177
+ const exportLine = `export * from "./${schemaFileName}";`;
178
+ const content = await fs.readFile(schemaIndexPath, "utf-8");
179
+ const normalized = content.replace(/\r\n/g, "\n");
180
+ const exportPattern = new RegExp(
181
+ `^\\s*export\\s*\\*\\s*from\\s*["']\\./${escapeRegex(schemaFileName)}["'];?\\s*$`,
182
+ "m"
183
+ );
184
+ if (exportPattern.test(normalized)) {
185
+ return;
186
+ }
187
+ let next = normalized.replace(/^\s*export\s*\{\s*\};?\s*$/m, "").trimEnd();
188
+ if (next.length > 0) {
189
+ next += "\n\n";
190
+ }
191
+ next += `${exportLine}
192
+ `;
193
+ await fs.writeFile(schemaIndexPath, next);
194
+ }
195
+ async function promptDatabaseConfig(initialModuleName, projectRoot, srcDir) {
196
+ let resolvedModuleName;
197
+ if (initialModuleName === "database") {
198
+ const ormResponse = await prompts({
199
+ type: "select",
200
+ name: "orm",
201
+ message: "Which ORM?",
202
+ choices: [
203
+ { title: "Drizzle", value: "drizzle" },
204
+ { title: "Prisma", value: "prisma" }
205
+ ],
206
+ initial: 0
207
+ });
208
+ if (!ormResponse.orm) {
209
+ console.log(chalk.yellow("Operation cancelled."));
210
+ return null;
211
+ }
212
+ const variantResponse = await prompts({
213
+ type: "select",
214
+ name: "dialect",
215
+ message: "Which database dialect?",
216
+ choices: [
217
+ { title: "PostgreSQL", value: "postgresql" },
218
+ { title: "MySQL", value: "mysql" }
219
+ ]
220
+ });
221
+ if (!variantResponse.dialect) {
222
+ console.log(chalk.yellow("Operation cancelled."));
223
+ return null;
224
+ }
225
+ resolvedModuleName = getDatabaseModule(ormResponse.orm, variantResponse.dialect);
226
+ } else {
227
+ const parsed = parseDatabaseDialect(initialModuleName);
228
+ if (!parsed) {
229
+ throw new Error(`Unsupported database module '${initialModuleName}'.`);
230
+ }
231
+ resolvedModuleName = parsed;
232
+ }
233
+ const { orm: selectedOrm, dialect: selectedDialect } = getDatabaseSelection(resolvedModuleName);
234
+ let databaseBackupPath = null;
235
+ const installedDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);
236
+ if (installedDialect && installedDialect !== resolvedModuleName) {
237
+ console.log(
238
+ chalk.yellow(
239
+ `
240
+ \u26A0 Existing database setup detected: ${databaseLabel(installedDialect)}.`
241
+ )
242
+ );
243
+ console.log(
244
+ chalk.yellow(
245
+ ` Switching to ${databaseLabel(resolvedModuleName)} will overwrite db files and drizzle config.
246
+ `
247
+ )
248
+ );
249
+ const switchResponse = await prompts({
250
+ type: "confirm",
251
+ name: "proceed",
252
+ message: "Continue and switch database dialect?",
253
+ initial: false
254
+ });
255
+ if (!switchResponse.proceed) {
256
+ console.log(chalk.yellow("Operation cancelled."));
257
+ return null;
258
+ }
259
+ databaseBackupPath = await backupDatabaseFiles(projectRoot, srcDir);
260
+ }
261
+ const defaultUrl = DEFAULT_DATABASE_URLS[resolvedModuleName];
262
+ console.log(chalk.dim(` Tip: Leave blank to use ${defaultUrl}
263
+ `));
264
+ const response = await prompts({
265
+ type: "text",
266
+ name: "dbUrl",
267
+ message: "Database URL",
268
+ initial: ""
269
+ });
270
+ if (response.dbUrl === void 0) {
271
+ console.log(chalk.yellow("Operation cancelled."));
272
+ return null;
273
+ }
274
+ const enteredUrl = response.dbUrl?.trim() || "";
275
+ const usedDefaultDbUrl = enteredUrl.length === 0;
276
+ const customDbUrl = validateDatabaseUrl(enteredUrl || defaultUrl, resolvedModuleName);
277
+ return {
278
+ resolvedModuleName,
279
+ selectedOrm,
280
+ selectedDialect,
281
+ customDbUrl,
282
+ usedDefaultDbUrl,
283
+ databaseBackupPath
284
+ };
285
+ }
286
+ function printDatabaseHints(moduleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath) {
287
+ if (databaseBackupPath) {
288
+ console.log(chalk.blue(`\u2139 Backup created at: ${databaseBackupPath}
289
+ `));
290
+ }
291
+ if (usedDefaultDbUrl) {
292
+ console.log(chalk.yellow("\u2139 Review DATABASE_URL in .env if your local DB config differs."));
293
+ }
294
+ const setupHint = getDatabaseSetupHint(
295
+ moduleName,
296
+ customDbUrl || DEFAULT_DATABASE_URLS[moduleName]
297
+ );
298
+ console.log(chalk.yellow(`\u2139 Ensure DB exists: ${setupHint}`));
299
+ if (isDrizzleDatabaseModule(moduleName)) {
300
+ console.log(chalk.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
301
+ return;
302
+ }
303
+ console.log(chalk.yellow("\u2139 Run migrations: npx prisma migrate dev --name init"));
304
+ console.log(chalk.yellow("\u2139 Generate client: npx prisma generate"));
305
+ }
306
+
307
+ export {
308
+ DEFAULT_DATABASE_URLS,
309
+ parseDatabaseDialect,
310
+ isDatabaseModule,
311
+ isDrizzleDatabaseModule,
312
+ getDatabaseSelection,
313
+ validateDatabaseUrl,
314
+ detectInstalledDatabaseDialect,
315
+ backupDatabaseFiles,
316
+ databaseLabel,
317
+ getDatabaseSetupHint,
318
+ ensureSchemaExport,
319
+ promptDatabaseConfig,
320
+ printDatabaseHints
321
+ };
322
+ //# sourceMappingURL=chunk-R3MGV5UR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers/database.handler.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs-extra\";\nimport prompts from \"prompts\";\nimport chalk from \"chalk\";\nimport { escapeRegex } from \"../utils/code-inject\";\n\nexport type DatabaseOrm = \"drizzle\" | \"prisma\";\nexport type DatabaseDialect = \"postgresql\" | \"mysql\";\nexport type DatabaseModuleName =\n | \"database-pg\"\n | \"database-mysql\"\n | \"database-prisma-pg\"\n | \"database-prisma-mysql\";\n\nconst DATABASE_MODULE_MAP: Record<DatabaseOrm, Record<DatabaseDialect, DatabaseModuleName>> = {\n drizzle: {\n postgresql: \"database-pg\",\n mysql: \"database-mysql\",\n },\n prisma: {\n postgresql: \"database-prisma-pg\",\n mysql: \"database-prisma-mysql\",\n },\n};\n\nexport const DEFAULT_DATABASE_URLS: Record<DatabaseModuleName, string> = {\n \"database-pg\": \"postgresql://postgres@localhost:5432/mydb\",\n \"database-mysql\": \"mysql://root@localhost:3306/mydb\",\n \"database-prisma-pg\": \"postgresql://postgres@localhost:5432/mydb\",\n \"database-prisma-mysql\": \"mysql://root@localhost:3306/mydb\",\n};\n\nexport function parseDatabaseDialect(value?: string): DatabaseModuleName | null {\n const normalized = value?.trim().toLowerCase();\n if (!normalized) {\n return null;\n }\n\n if (\n normalized === \"pg\"\n || normalized === \"postgres\"\n || normalized === \"postgresql\"\n || normalized === \"database-pg\"\n || normalized === \"drizzle-pg\"\n || normalized === \"drizzle-postgres\"\n || normalized === \"database-drizzle-pg\"\n ) {\n return \"database-pg\";\n }\n\n if (\n normalized === \"mysql\"\n || normalized === \"database-mysql\"\n || normalized === \"drizzle-mysql\"\n || normalized === \"database-drizzle-mysql\"\n ) {\n return \"database-mysql\";\n }\n\n if (\n normalized === \"database-prisma-pg\"\n || normalized === \"prisma-pg\"\n || normalized === \"prisma-postgres\"\n || normalized === \"prisma-postgresql\"\n || normalized === \"database-prisma\"\n ) {\n return \"database-prisma-pg\";\n }\n\n if (normalized === \"database-prisma-mysql\" || normalized === \"prisma-mysql\") {\n return \"database-prisma-mysql\";\n }\n\n return null;\n}\n\nexport function isDatabaseModule(moduleName: string): moduleName is DatabaseModuleName {\n return (\n moduleName === \"database-pg\"\n || moduleName === \"database-mysql\"\n || moduleName === \"database-prisma-pg\"\n || moduleName === \"database-prisma-mysql\"\n );\n}\n\nexport function isDrizzleDatabaseModule(moduleName: string): moduleName is \"database-pg\" | \"database-mysql\" {\n return moduleName === \"database-pg\" || moduleName === \"database-mysql\";\n}\n\nexport function getDatabaseSelection(moduleName: DatabaseModuleName): { orm: DatabaseOrm; dialect: DatabaseDialect } {\n if (moduleName === \"database-pg\") {\n return { orm: \"drizzle\", dialect: \"postgresql\" };\n }\n\n if (moduleName === \"database-mysql\") {\n return { orm: \"drizzle\", dialect: \"mysql\" };\n }\n\n if (moduleName === \"database-prisma-pg\") {\n return { orm: \"prisma\", dialect: \"postgresql\" };\n }\n\n return { orm: \"prisma\", dialect: \"mysql\" };\n}\n\nfunction getDatabaseModule(orm: DatabaseOrm, dialect: DatabaseDialect): DatabaseModuleName {\n return DATABASE_MODULE_MAP[orm][dialect];\n}\n\nfunction parsePrismaProvider(schemaContent: string): DatabaseDialect | null {\n const match = schemaContent.match(/provider\\s*=\\s*\"([^\"]+)\"/);\n if (!match) {\n return null;\n }\n\n const provider = match[1]?.trim().toLowerCase();\n if (provider === \"mysql\") {\n return \"mysql\";\n }\n\n if (provider === \"postgresql\" || provider === \"postgres\") {\n return \"postgresql\";\n }\n\n return null;\n}\n\nexport function validateDatabaseUrl(rawUrl: string, moduleName: DatabaseModuleName) {\n const { dialect } = getDatabaseSelection(moduleName);\n const dbUrl = rawUrl.trim();\n if (!dbUrl) {\n throw new Error(\"Database URL cannot be empty.\");\n }\n\n let parsed: URL;\n try {\n parsed = new URL(dbUrl);\n } catch {\n throw new Error(`Invalid database URL: '${dbUrl}'.`);\n }\n\n const protocol = parsed.protocol.toLowerCase();\n if (dialect === \"postgresql\" && protocol !== \"postgresql:\" && protocol !== \"postgres:\") {\n throw new Error(\"PostgreSQL URL must start with postgres:// or postgresql://\");\n }\n\n if (dialect === \"mysql\" && protocol !== \"mysql:\") {\n throw new Error(\"MySQL URL must start with mysql://\");\n }\n\n return dbUrl;\n}\n\nexport async function detectInstalledDatabaseDialect(projectRoot: string, srcDir: string): Promise<DatabaseModuleName | null> {\n const dbIndexPath = path.join(projectRoot, srcDir, \"db\", \"index.ts\");\n const prismaSchemaPath = path.join(projectRoot, \"prisma\", \"schema.prisma\");\n\n if (fs.existsSync(dbIndexPath)) {\n const content = await fs.readFile(dbIndexPath, \"utf-8\");\n if (content.includes(\"drizzle-orm/node-postgres\") || content.includes(`from \"pg\"`)) {\n return \"database-pg\";\n }\n\n if (content.includes(\"drizzle-orm/mysql2\") || content.includes(`from \"mysql2`)) {\n return \"database-mysql\";\n }\n\n if (content.includes(`from \"@prisma/client\"`)) {\n if (fs.existsSync(prismaSchemaPath)) {\n const schemaContent = await fs.readFile(prismaSchemaPath, \"utf-8\");\n const dialect = parsePrismaProvider(schemaContent);\n if (dialect === \"mysql\") {\n return \"database-prisma-mysql\";\n }\n }\n\n return \"database-prisma-pg\";\n }\n }\n\n if (fs.existsSync(prismaSchemaPath)) {\n const schemaContent = await fs.readFile(prismaSchemaPath, \"utf-8\");\n const dialect = parsePrismaProvider(schemaContent);\n if (dialect === \"mysql\") {\n return \"database-prisma-mysql\";\n }\n\n return \"database-prisma-pg\";\n }\n\n return null;\n}\n\nexport async function backupDatabaseFiles(projectRoot: string, srcDir: string): Promise<string | null> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupRoot = path.join(projectRoot, \".zuro\", \"backups\", `database-${timestamp}`);\n const candidates = [\n path.join(projectRoot, srcDir, \"db\", \"index.ts\"),\n path.join(projectRoot, \"drizzle.config.ts\"),\n path.join(projectRoot, \"prisma\", \"schema.prisma\"),\n ];\n\n let copied = false;\n for (const filePath of candidates) {\n if (!fs.existsSync(filePath)) {\n continue;\n }\n\n const relativePath = path.relative(projectRoot, filePath);\n const backupPath = path.join(backupRoot, relativePath);\n await fs.ensureDir(path.dirname(backupPath));\n await fs.copyFile(filePath, backupPath);\n copied = true;\n }\n\n return copied ? backupRoot : null;\n}\n\nexport function databaseLabel(moduleName: DatabaseModuleName) {\n const selection = getDatabaseSelection(moduleName);\n const ormLabel = selection.orm === \"drizzle\" ? \"Drizzle\" : \"Prisma\";\n const dialectLabel = selection.dialect === \"postgresql\" ? \"PostgreSQL\" : \"MySQL\";\n return `${ormLabel} (${dialectLabel})`;\n}\n\nexport function getDatabaseSetupHint(moduleName: DatabaseModuleName, dbUrl: string) {\n try {\n const parsed = new URL(dbUrl);\n const dbName = parsed.pathname.replace(/^\\/+/, \"\") || \"mydb\";\n\n if (moduleName === \"database-pg\") {\n return `createdb ${dbName}`;\n }\n\n return `mysql -e \"CREATE DATABASE IF NOT EXISTS ${dbName};\"`;\n } catch {\n return moduleName === \"database-pg\"\n ? \"createdb <database_name>\"\n : `mysql -e \"CREATE DATABASE IF NOT EXISTS <database_name>;\"`;\n }\n}\n\nexport async function ensureSchemaExport(projectRoot: string, srcDir: string, schemaFileName: string) {\n const schemaIndexPath = path.join(projectRoot, srcDir, \"db\", \"schema\", \"index.ts\");\n if (!await fs.pathExists(schemaIndexPath)) {\n return;\n }\n\n const exportLine = `export * from \"./${schemaFileName}\";`;\n const content = await fs.readFile(schemaIndexPath, \"utf-8\");\n const normalized = content.replace(/\\r\\n/g, \"\\n\");\n const exportPattern = new RegExp(\n `^\\\\s*export\\\\s*\\\\*\\\\s*from\\\\s*[\"']\\\\./${escapeRegex(schemaFileName)}[\"'];?\\\\s*$`,\n \"m\"\n );\n\n if (exportPattern.test(normalized)) {\n return;\n }\n\n let next = normalized\n .replace(/^\\s*export\\s*\\{\\s*\\};?\\s*$/m, \"\")\n .trimEnd();\n\n if (next.length > 0) {\n next += \"\\n\\n\";\n }\n\n next += `${exportLine}\\n`;\n await fs.writeFile(schemaIndexPath, next);\n}\n\nexport interface DatabasePromptResult {\n resolvedModuleName: DatabaseModuleName;\n selectedOrm: DatabaseOrm;\n selectedDialect: DatabaseDialect;\n customDbUrl: string | undefined;\n usedDefaultDbUrl: boolean;\n databaseBackupPath: string | null;\n}\n\n/**\n * Runs all database-specific interactive prompts and validation.\n * Returns null if the user cancels.\n */\nexport async function promptDatabaseConfig(\n initialModuleName: string,\n projectRoot: string,\n srcDir: string,\n): Promise<DatabasePromptResult | null> {\n let resolvedModuleName: DatabaseModuleName;\n\n if (initialModuleName === \"database\") {\n const ormResponse = await prompts({\n type: \"select\",\n name: \"orm\",\n message: \"Which ORM?\",\n choices: [\n { title: \"Drizzle\", value: \"drizzle\" },\n { title: \"Prisma\", value: \"prisma\" },\n ],\n initial: 0,\n });\n\n if (!ormResponse.orm) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n const variantResponse = await prompts({\n type: \"select\",\n name: \"dialect\",\n message: \"Which database dialect?\",\n choices: [\n { title: \"PostgreSQL\", value: \"postgresql\" },\n { title: \"MySQL\", value: \"mysql\" },\n ],\n });\n\n if (!variantResponse.dialect) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n resolvedModuleName = getDatabaseModule(ormResponse.orm as DatabaseOrm, variantResponse.dialect as DatabaseDialect);\n } else {\n const parsed = parseDatabaseDialect(initialModuleName);\n if (!parsed) {\n throw new Error(`Unsupported database module '${initialModuleName}'.`);\n }\n\n resolvedModuleName = parsed;\n }\n\n const { orm: selectedOrm, dialect: selectedDialect } = getDatabaseSelection(resolvedModuleName);\n\n // Check for dialect switch\n let databaseBackupPath: string | null = null;\n const installedDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);\n\n if (installedDialect && installedDialect !== resolvedModuleName) {\n console.log(\n chalk.yellow(\n `\\n⚠ Existing database setup detected: ${databaseLabel(installedDialect)}.`\n )\n );\n console.log(\n chalk.yellow(\n ` Switching to ${databaseLabel(resolvedModuleName)} will overwrite db files and drizzle config.\\n`\n )\n );\n\n const switchResponse = await prompts({\n type: \"confirm\",\n name: \"proceed\",\n message: \"Continue and switch database dialect?\",\n initial: false,\n });\n\n if (!switchResponse.proceed) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n databaseBackupPath = await backupDatabaseFiles(projectRoot, srcDir);\n }\n\n // Prompt for database URL\n const defaultUrl = DEFAULT_DATABASE_URLS[resolvedModuleName];\n console.log(chalk.dim(` Tip: Leave blank to use ${defaultUrl}\\n`));\n\n const response = await prompts({\n type: \"text\",\n name: \"dbUrl\",\n message: \"Database URL\",\n initial: \"\",\n });\n\n if (response.dbUrl === undefined) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n const enteredUrl = response.dbUrl?.trim() || \"\";\n const usedDefaultDbUrl = enteredUrl.length === 0;\n const customDbUrl = validateDatabaseUrl(enteredUrl || defaultUrl, resolvedModuleName);\n\n return {\n resolvedModuleName,\n selectedOrm,\n selectedDialect,\n customDbUrl,\n usedDefaultDbUrl,\n databaseBackupPath,\n };\n}\n\n/**\n * Prints post-install hints for database modules.\n */\nexport function printDatabaseHints(\n moduleName: DatabaseModuleName,\n customDbUrl: string | undefined,\n usedDefaultDbUrl: boolean,\n databaseBackupPath: string | null,\n) {\n if (databaseBackupPath) {\n console.log(chalk.blue(`ℹ Backup created at: ${databaseBackupPath}\\n`));\n }\n\n if (usedDefaultDbUrl) {\n console.log(chalk.yellow(\"ℹ Review DATABASE_URL in .env if your local DB config differs.\"));\n }\n\n const setupHint = getDatabaseSetupHint(\n moduleName,\n customDbUrl || DEFAULT_DATABASE_URLS[moduleName]\n );\n console.log(chalk.yellow(`ℹ Ensure DB exists: ${setupHint}`));\n if (isDrizzleDatabaseModule(moduleName)) {\n console.log(chalk.yellow(\"ℹ Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate\"));\n return;\n }\n\n console.log(chalk.yellow(\"ℹ Run migrations: npx prisma migrate dev --name init\"));\n console.log(chalk.yellow(\"ℹ Generate client: npx prisma generate\"));\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,37 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/utils/code-inject.ts
9
+ function escapeRegex(value) {
10
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11
+ }
12
+ function appendImport(source, line) {
13
+ if (source.includes(line)) {
14
+ return { source, inserted: true };
15
+ }
16
+ const importRegex = /^import .+ from .+;?\s*$/gm;
17
+ let lastImportIndex = 0;
18
+ let match;
19
+ while ((match = importRegex.exec(source)) !== null) {
20
+ lastImportIndex = match.index + match[0].length;
21
+ }
22
+ if (lastImportIndex <= 0) {
23
+ return { source, inserted: false };
24
+ }
25
+ return {
26
+ source: source.slice(0, lastImportIndex) + `
27
+ ${line}` + source.slice(lastImportIndex),
28
+ inserted: true
29
+ };
30
+ }
31
+
32
+ export {
33
+ __require,
34
+ escapeRegex,
35
+ appendImport
36
+ };
37
+ //# sourceMappingURL=chunk-VMOTWTER.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/code-inject.ts"],"sourcesContent":["/**\n * Escapes special regex characters in a string so it can be used\n * as a literal pattern inside a RegExp.\n */\nexport function escapeRegex(value: string) {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Inserts an import statement after the last existing import in the source.\n * Returns `{ source, inserted }` — `inserted` is false only when no\n * existing imports could be found (malformed file).\n *\n * If the import already exists, returns the original source with `inserted: true`.\n */\nexport function appendImport(source: string, line: string) {\n if (source.includes(line)) {\n return { source, inserted: true };\n }\n\n const importRegex = /^import .+ from .+;?\\s*$/gm;\n let lastImportIndex = 0;\n let match;\n\n while ((match = importRegex.exec(source)) !== null) {\n lastImportIndex = match.index + match[0].length;\n }\n\n if (lastImportIndex <= 0) {\n return { source, inserted: false };\n }\n\n return {\n source: source.slice(0, lastImportIndex) + `\\n${line}` + source.slice(lastImportIndex),\n inserted: true,\n };\n}\n"],"mappings":";;;;;;;;AAIO,SAAS,YAAY,OAAe;AACvC,SAAO,MAAM,QAAQ,uBAAuB,MAAM;AACtD;AASO,SAAS,aAAa,QAAgB,MAAc;AACvD,MAAI,OAAO,SAAS,IAAI,GAAG;AACvB,WAAO,EAAE,QAAQ,UAAU,KAAK;AAAA,EACpC;AAEA,QAAM,cAAc;AACpB,MAAI,kBAAkB;AACtB,MAAI;AAEJ,UAAQ,QAAQ,YAAY,KAAK,MAAM,OAAO,MAAM;AAChD,sBAAkB,MAAM,QAAQ,MAAM,CAAC,EAAE;AAAA,EAC7C;AAEA,MAAI,mBAAmB,GAAG;AACtB,WAAO,EAAE,QAAQ,UAAU,MAAM;AAAA,EACrC;AAEA,SAAO;AAAA,IACH,QAAQ,OAAO,MAAM,GAAG,eAAe,IAAI;AAAA,EAAK,IAAI,KAAK,OAAO,MAAM,eAAe;AAAA,IACrF,UAAU;AAAA,EACd;AACJ;","names":[]}
@@ -0,0 +1,87 @@
1
+ import {
2
+ __require,
3
+ appendImport
4
+ } from "./chunk-VMOTWTER.mjs";
5
+
6
+ // src/handlers/docs.handler.ts
7
+ import path from "path";
8
+ import fs from "fs-extra";
9
+ async function isDocsModuleInstalled(projectRoot, srcDir) {
10
+ return await fs.pathExists(path.join(projectRoot, srcDir, "lib", "openapi.ts"));
11
+ }
12
+ async function injectDocsRoutes(projectRoot, srcDir) {
13
+ const routeIndexPath = path.join(projectRoot, srcDir, "routes", "index.ts");
14
+ const routeImport = `import docsRoutes from "./docs.routes";`;
15
+ const routeMountPattern = /rootRouter\.use\(\s*["']\/docs["']\s*,\s*docsRoutes\s*\)/;
16
+ if (await fs.pathExists(routeIndexPath)) {
17
+ let routeContent = await fs.readFile(routeIndexPath, "utf-8");
18
+ let routeModified = false;
19
+ const importResult = appendImport(routeContent, routeImport);
20
+ if (!importResult.inserted) {
21
+ return false;
22
+ }
23
+ if (importResult.source !== routeContent) {
24
+ routeContent = importResult.source;
25
+ routeModified = true;
26
+ }
27
+ if (!routeMountPattern.test(routeContent)) {
28
+ const routeSetup = `
29
+ // API docs
30
+ rootRouter.use("/docs", docsRoutes);
31
+ `;
32
+ const exportMatch = routeContent.match(/export default rootRouter;?\s*$/m);
33
+ if (!exportMatch || exportMatch.index === void 0) {
34
+ return false;
35
+ }
36
+ routeContent = routeContent.slice(0, exportMatch.index) + routeSetup + "\n" + routeContent.slice(exportMatch.index);
37
+ routeModified = true;
38
+ }
39
+ if (routeModified) {
40
+ await fs.writeFile(routeIndexPath, routeContent);
41
+ }
42
+ return true;
43
+ }
44
+ const appPath = path.join(projectRoot, srcDir, "app.ts");
45
+ if (!await fs.pathExists(appPath)) {
46
+ return false;
47
+ }
48
+ let appContent = await fs.readFile(appPath, "utf-8");
49
+ let appModified = false;
50
+ const appImportResult = appendImport(appContent, `import docsRoutes from "./routes/docs.routes";`);
51
+ if (!appImportResult.inserted) {
52
+ return false;
53
+ }
54
+ if (appImportResult.source !== appContent) {
55
+ appContent = appImportResult.source;
56
+ appModified = true;
57
+ }
58
+ const hasMount = /app\.use\(\s*["']\/api\/docs["']\s*,\s*docsRoutes\s*\)/.test(appContent);
59
+ if (!hasMount) {
60
+ const setup = `
61
+ // API docs
62
+ app.use("/api/docs", docsRoutes);
63
+ `;
64
+ const exportMatch = appContent.match(/export default app;?\s*$/m);
65
+ if (!exportMatch || exportMatch.index === void 0) {
66
+ return false;
67
+ }
68
+ appContent = appContent.slice(0, exportMatch.index) + setup + "\n" + appContent.slice(exportMatch.index);
69
+ appModified = true;
70
+ }
71
+ if (appModified) {
72
+ await fs.writeFile(appPath, appContent);
73
+ }
74
+ return true;
75
+ }
76
+ function printDocsHints() {
77
+ const chalk = __require("chalk");
78
+ console.log(chalk.yellow("\u2139 API docs available at: /api/docs"));
79
+ console.log(chalk.yellow("\u2139 OpenAPI spec available at: /api/docs/openapi.json"));
80
+ }
81
+
82
+ export {
83
+ isDocsModuleInstalled,
84
+ injectDocsRoutes,
85
+ printDocsHints
86
+ };
87
+ //# sourceMappingURL=chunk-YBAO5SKK.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers/docs.handler.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs-extra\";\nimport { appendImport } from \"../utils/code-inject\";\n\n/**\n * Checks if the docs module is installed by looking for lib/openapi.ts.\n */\nexport async function isDocsModuleInstalled(projectRoot: string, srcDir: string): Promise<boolean> {\n return await fs.pathExists(path.join(projectRoot, srcDir, \"lib\", \"openapi.ts\"));\n}\n\n/**\n * Injects docs routes into routes/index.ts or app.ts.\n */\nexport async function injectDocsRoutes(projectRoot: string, srcDir: string): Promise<boolean> {\n const routeIndexPath = path.join(projectRoot, srcDir, \"routes\", \"index.ts\");\n const routeImport = `import docsRoutes from \"./docs.routes\";`;\n const routeMountPattern = /rootRouter\\.use\\(\\s*[\"']\\/docs[\"']\\s*,\\s*docsRoutes\\s*\\)/;\n\n if (await fs.pathExists(routeIndexPath)) {\n let routeContent = await fs.readFile(routeIndexPath, \"utf-8\");\n let routeModified = false;\n\n const importResult = appendImport(routeContent, routeImport);\n if (!importResult.inserted) {\n return false;\n }\n\n if (importResult.source !== routeContent) {\n routeContent = importResult.source;\n routeModified = true;\n }\n\n if (!routeMountPattern.test(routeContent)) {\n const routeSetup = `\\n// API docs\\nrootRouter.use(\"/docs\", docsRoutes);\\n`;\n const exportMatch = routeContent.match(/export default rootRouter;?\\s*$/m);\n if (!exportMatch || exportMatch.index === undefined) {\n return false;\n }\n\n routeContent = routeContent.slice(0, exportMatch.index) + routeSetup + \"\\n\" + routeContent.slice(exportMatch.index);\n routeModified = true;\n }\n\n if (routeModified) {\n await fs.writeFile(routeIndexPath, routeContent);\n }\n\n return true;\n }\n\n const appPath = path.join(projectRoot, srcDir, \"app.ts\");\n if (!await fs.pathExists(appPath)) {\n return false;\n }\n\n let appContent = await fs.readFile(appPath, \"utf-8\");\n let appModified = false;\n\n const appImportResult = appendImport(appContent, `import docsRoutes from \"./routes/docs.routes\";`);\n if (!appImportResult.inserted) {\n return false;\n }\n\n if (appImportResult.source !== appContent) {\n appContent = appImportResult.source;\n appModified = true;\n }\n\n const hasMount = /app\\.use\\(\\s*[\"']\\/api\\/docs[\"']\\s*,\\s*docsRoutes\\s*\\)/.test(appContent);\n if (!hasMount) {\n const setup = `\\n// API docs\\napp.use(\"/api/docs\", docsRoutes);\\n`;\n const exportMatch = appContent.match(/export default app;?\\s*$/m);\n if (!exportMatch || exportMatch.index === undefined) {\n return false;\n }\n\n appContent = appContent.slice(0, exportMatch.index) + setup + \"\\n\" + appContent.slice(exportMatch.index);\n appModified = true;\n }\n\n if (appModified) {\n await fs.writeFile(appPath, appContent);\n }\n\n return true;\n}\n\n/**\n * Prints post-install hints for the docs module.\n */\nexport function printDocsHints() {\n const chalk = require(\"chalk\");\n console.log(chalk.yellow(\"ℹ API docs available at: /api/docs\"));\n console.log(chalk.yellow(\"ℹ OpenAPI spec available at: /api/docs/openapi.json\"));\n}\n"],"mappings":";;;;;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAMf,eAAsB,sBAAsB,aAAqB,QAAkC;AAC/F,SAAO,MAAM,GAAG,WAAW,KAAK,KAAK,aAAa,QAAQ,OAAO,YAAY,CAAC;AAClF;AAKA,eAAsB,iBAAiB,aAAqB,QAAkC;AAC1F,QAAM,iBAAiB,KAAK,KAAK,aAAa,QAAQ,UAAU,UAAU;AAC1E,QAAM,cAAc;AACpB,QAAM,oBAAoB;AAE1B,MAAI,MAAM,GAAG,WAAW,cAAc,GAAG;AACrC,QAAI,eAAe,MAAM,GAAG,SAAS,gBAAgB,OAAO;AAC5D,QAAI,gBAAgB;AAEpB,UAAM,eAAe,aAAa,cAAc,WAAW;AAC3D,QAAI,CAAC,aAAa,UAAU;AACxB,aAAO;AAAA,IACX;AAEA,QAAI,aAAa,WAAW,cAAc;AACtC,qBAAe,aAAa;AAC5B,sBAAgB;AAAA,IACpB;AAEA,QAAI,CAAC,kBAAkB,KAAK,YAAY,GAAG;AACvC,YAAM,aAAa;AAAA;AAAA;AAAA;AACnB,YAAM,cAAc,aAAa,MAAM,kCAAkC;AACzE,UAAI,CAAC,eAAe,YAAY,UAAU,QAAW;AACjD,eAAO;AAAA,MACX;AAEA,qBAAe,aAAa,MAAM,GAAG,YAAY,KAAK,IAAI,aAAa,OAAO,aAAa,MAAM,YAAY,KAAK;AAClH,sBAAgB;AAAA,IACpB;AAEA,QAAI,eAAe;AACf,YAAM,GAAG,UAAU,gBAAgB,YAAY;AAAA,IACnD;AAEA,WAAO;AAAA,EACX;AAEA,QAAM,UAAU,KAAK,KAAK,aAAa,QAAQ,QAAQ;AACvD,MAAI,CAAC,MAAM,GAAG,WAAW,OAAO,GAAG;AAC/B,WAAO;AAAA,EACX;AAEA,MAAI,aAAa,MAAM,GAAG,SAAS,SAAS,OAAO;AACnD,MAAI,cAAc;AAElB,QAAM,kBAAkB,aAAa,YAAY,gDAAgD;AACjG,MAAI,CAAC,gBAAgB,UAAU;AAC3B,WAAO;AAAA,EACX;AAEA,MAAI,gBAAgB,WAAW,YAAY;AACvC,iBAAa,gBAAgB;AAC7B,kBAAc;AAAA,EAClB;AAEA,QAAM,WAAW,yDAAyD,KAAK,UAAU;AACzF,MAAI,CAAC,UAAU;AACX,UAAM,QAAQ;AAAA;AAAA;AAAA;AACd,UAAM,cAAc,WAAW,MAAM,2BAA2B;AAChE,QAAI,CAAC,eAAe,YAAY,UAAU,QAAW;AACjD,aAAO;AAAA,IACX;AAEA,iBAAa,WAAW,MAAM,GAAG,YAAY,KAAK,IAAI,QAAQ,OAAO,WAAW,MAAM,YAAY,KAAK;AACvG,kBAAc;AAAA,EAClB;AAEA,MAAI,aAAa;AACb,UAAM,GAAG,UAAU,SAAS,UAAU;AAAA,EAC1C;AAEA,SAAO;AACX;AAKO,SAAS,iBAAiB;AAC7B,QAAM,QAAQ,UAAQ,OAAO;AAC7B,UAAQ,IAAI,MAAM,OAAO,yCAAoC,CAAC;AAC9D,UAAQ,IAAI,MAAM,OAAO,0DAAqD,CAAC;AACnF;","names":[]}
@@ -0,0 +1,32 @@
1
+ import {
2
+ DEFAULT_DATABASE_URLS,
3
+ backupDatabaseFiles,
4
+ databaseLabel,
5
+ detectInstalledDatabaseDialect,
6
+ ensureSchemaExport,
7
+ getDatabaseSelection,
8
+ getDatabaseSetupHint,
9
+ isDatabaseModule,
10
+ isDrizzleDatabaseModule,
11
+ parseDatabaseDialect,
12
+ printDatabaseHints,
13
+ promptDatabaseConfig,
14
+ validateDatabaseUrl
15
+ } from "./chunk-R3MGV5UR.mjs";
16
+ import "./chunk-VMOTWTER.mjs";
17
+ export {
18
+ DEFAULT_DATABASE_URLS,
19
+ backupDatabaseFiles,
20
+ databaseLabel,
21
+ detectInstalledDatabaseDialect,
22
+ ensureSchemaExport,
23
+ getDatabaseSelection,
24
+ getDatabaseSetupHint,
25
+ isDatabaseModule,
26
+ isDrizzleDatabaseModule,
27
+ parseDatabaseDialect,
28
+ printDatabaseHints,
29
+ promptDatabaseConfig,
30
+ validateDatabaseUrl
31
+ };
32
+ //# sourceMappingURL=database.handler-5OUD6XJZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,12 @@
1
+ import {
2
+ injectDocsRoutes,
3
+ isDocsModuleInstalled,
4
+ printDocsHints
5
+ } from "./chunk-YBAO5SKK.mjs";
6
+ import "./chunk-VMOTWTER.mjs";
7
+ export {
8
+ injectDocsRoutes,
9
+ isDocsModuleInstalled,
10
+ printDocsHints
11
+ };
12
+ //# sourceMappingURL=docs.handler-JL3ZIVJQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}