zuro-cli 0.0.2-beta.12 → 0.0.2-beta.14

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.
@@ -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,216 @@
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 DEFAULT_DATABASE_URLS = {
11
+ "database-pg": "postgresql://postgres@localhost:5432/mydb",
12
+ "database-mysql": "mysql://root@localhost:3306/mydb"
13
+ };
14
+ function parseDatabaseDialect(value) {
15
+ const normalized = value?.trim().toLowerCase();
16
+ if (!normalized) {
17
+ return null;
18
+ }
19
+ if (normalized === "pg" || normalized === "postgres" || normalized === "postgresql" || normalized === "database-pg") {
20
+ return "database-pg";
21
+ }
22
+ if (normalized === "mysql" || normalized === "database-mysql") {
23
+ return "database-mysql";
24
+ }
25
+ return null;
26
+ }
27
+ function isDatabaseModule(moduleName) {
28
+ return moduleName === "database-pg" || moduleName === "database-mysql";
29
+ }
30
+ function validateDatabaseUrl(rawUrl, moduleName) {
31
+ const dbUrl = rawUrl.trim();
32
+ if (!dbUrl) {
33
+ throw new Error("Database URL cannot be empty.");
34
+ }
35
+ let parsed;
36
+ try {
37
+ parsed = new URL(dbUrl);
38
+ } catch {
39
+ throw new Error(`Invalid database URL: '${dbUrl}'.`);
40
+ }
41
+ const protocol = parsed.protocol.toLowerCase();
42
+ if (moduleName === "database-pg" && protocol !== "postgresql:" && protocol !== "postgres:") {
43
+ throw new Error("PostgreSQL URL must start with postgres:// or postgresql://");
44
+ }
45
+ if (moduleName === "database-mysql" && protocol !== "mysql:") {
46
+ throw new Error("MySQL URL must start with mysql://");
47
+ }
48
+ return dbUrl;
49
+ }
50
+ async function detectInstalledDatabaseDialect(projectRoot, srcDir) {
51
+ const dbIndexPath = path.join(projectRoot, srcDir, "db", "index.ts");
52
+ if (!fs.existsSync(dbIndexPath)) {
53
+ return null;
54
+ }
55
+ const content = await fs.readFile(dbIndexPath, "utf-8");
56
+ if (content.includes("drizzle-orm/node-postgres") || content.includes(`from "pg"`)) {
57
+ return "database-pg";
58
+ }
59
+ if (content.includes("drizzle-orm/mysql2") || content.includes(`from "mysql2`)) {
60
+ return "database-mysql";
61
+ }
62
+ return null;
63
+ }
64
+ async function backupDatabaseFiles(projectRoot, srcDir) {
65
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
66
+ const backupRoot = path.join(projectRoot, ".zuro", "backups", `database-${timestamp}`);
67
+ const candidates = [
68
+ path.join(projectRoot, srcDir, "db", "index.ts"),
69
+ path.join(projectRoot, "drizzle.config.ts")
70
+ ];
71
+ let copied = false;
72
+ for (const filePath of candidates) {
73
+ if (!fs.existsSync(filePath)) {
74
+ continue;
75
+ }
76
+ const relativePath = path.relative(projectRoot, filePath);
77
+ const backupPath = path.join(backupRoot, relativePath);
78
+ await fs.ensureDir(path.dirname(backupPath));
79
+ await fs.copyFile(filePath, backupPath);
80
+ copied = true;
81
+ }
82
+ return copied ? backupRoot : null;
83
+ }
84
+ function databaseLabel(moduleName) {
85
+ return moduleName === "database-pg" ? "PostgreSQL" : "MySQL";
86
+ }
87
+ function getDatabaseSetupHint(moduleName, dbUrl) {
88
+ try {
89
+ const parsed = new URL(dbUrl);
90
+ const dbName = parsed.pathname.replace(/^\/+/, "") || "mydb";
91
+ if (moduleName === "database-pg") {
92
+ return `createdb ${dbName}`;
93
+ }
94
+ return `mysql -e "CREATE DATABASE IF NOT EXISTS ${dbName};"`;
95
+ } catch {
96
+ return moduleName === "database-pg" ? "createdb <database_name>" : `mysql -e "CREATE DATABASE IF NOT EXISTS <database_name>;"`;
97
+ }
98
+ }
99
+ async function ensureSchemaExport(projectRoot, srcDir, schemaFileName) {
100
+ const schemaIndexPath = path.join(projectRoot, srcDir, "db", "schema", "index.ts");
101
+ if (!await fs.pathExists(schemaIndexPath)) {
102
+ return;
103
+ }
104
+ const exportLine = `export * from "./${schemaFileName}";`;
105
+ const content = await fs.readFile(schemaIndexPath, "utf-8");
106
+ const normalized = content.replace(/\r\n/g, "\n");
107
+ const exportPattern = new RegExp(
108
+ `^\\s*export\\s*\\*\\s*from\\s*["']\\./${escapeRegex(schemaFileName)}["'];?\\s*$`,
109
+ "m"
110
+ );
111
+ if (exportPattern.test(normalized)) {
112
+ return;
113
+ }
114
+ let next = normalized.replace(/^\s*export\s*\{\s*\};?\s*$/m, "").trimEnd();
115
+ if (next.length > 0) {
116
+ next += "\n\n";
117
+ }
118
+ next += `${exportLine}
119
+ `;
120
+ await fs.writeFile(schemaIndexPath, next);
121
+ }
122
+ async function promptDatabaseConfig(initialModuleName, projectRoot, srcDir) {
123
+ let resolvedModuleName;
124
+ if (initialModuleName === "database") {
125
+ const variantResponse = await prompts({
126
+ type: "select",
127
+ name: "variant",
128
+ message: "Which database dialect?",
129
+ choices: [
130
+ { title: "PostgreSQL", value: "database-pg" },
131
+ { title: "MySQL", value: "database-mysql" }
132
+ ]
133
+ });
134
+ if (!variantResponse.variant) {
135
+ console.log(chalk.yellow("Operation cancelled."));
136
+ return null;
137
+ }
138
+ resolvedModuleName = variantResponse.variant;
139
+ } else {
140
+ resolvedModuleName = initialModuleName;
141
+ }
142
+ let databaseBackupPath = null;
143
+ const installedDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);
144
+ if (installedDialect && installedDialect !== resolvedModuleName) {
145
+ console.log(
146
+ chalk.yellow(
147
+ `
148
+ \u26A0 Existing database setup detected: ${databaseLabel(installedDialect)}.`
149
+ )
150
+ );
151
+ console.log(
152
+ chalk.yellow(
153
+ ` Switching to ${databaseLabel(resolvedModuleName)} will overwrite db files and drizzle config.
154
+ `
155
+ )
156
+ );
157
+ const switchResponse = await prompts({
158
+ type: "confirm",
159
+ name: "proceed",
160
+ message: "Continue and switch database dialect?",
161
+ initial: false
162
+ });
163
+ if (!switchResponse.proceed) {
164
+ console.log(chalk.yellow("Operation cancelled."));
165
+ return null;
166
+ }
167
+ databaseBackupPath = await backupDatabaseFiles(projectRoot, srcDir);
168
+ }
169
+ const defaultUrl = DEFAULT_DATABASE_URLS[resolvedModuleName];
170
+ console.log(chalk.dim(` Tip: Leave blank to use ${defaultUrl}
171
+ `));
172
+ const response = await prompts({
173
+ type: "text",
174
+ name: "dbUrl",
175
+ message: "Database URL",
176
+ initial: ""
177
+ });
178
+ if (response.dbUrl === void 0) {
179
+ console.log(chalk.yellow("Operation cancelled."));
180
+ return null;
181
+ }
182
+ const enteredUrl = response.dbUrl?.trim() || "";
183
+ const usedDefaultDbUrl = enteredUrl.length === 0;
184
+ const customDbUrl = validateDatabaseUrl(enteredUrl || defaultUrl, resolvedModuleName);
185
+ return { resolvedModuleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath };
186
+ }
187
+ function printDatabaseHints(moduleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath) {
188
+ if (databaseBackupPath) {
189
+ console.log(chalk.blue(`\u2139 Backup created at: ${databaseBackupPath}
190
+ `));
191
+ }
192
+ if (usedDefaultDbUrl) {
193
+ console.log(chalk.yellow("\u2139 Review DATABASE_URL in .env if your local DB config differs."));
194
+ }
195
+ const setupHint = getDatabaseSetupHint(
196
+ moduleName,
197
+ customDbUrl || DEFAULT_DATABASE_URLS[moduleName]
198
+ );
199
+ console.log(chalk.yellow(`\u2139 Ensure DB exists: ${setupHint}`));
200
+ console.log(chalk.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
201
+ }
202
+
203
+ export {
204
+ DEFAULT_DATABASE_URLS,
205
+ parseDatabaseDialect,
206
+ isDatabaseModule,
207
+ validateDatabaseUrl,
208
+ detectInstalledDatabaseDialect,
209
+ backupDatabaseFiles,
210
+ databaseLabel,
211
+ getDatabaseSetupHint,
212
+ ensureSchemaExport,
213
+ promptDatabaseConfig,
214
+ printDatabaseHints
215
+ };
216
+ //# sourceMappingURL=chunk-W36ZIR4Y.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 DatabaseModuleName = \"database-pg\" | \"database-mysql\";\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};\n\nexport function parseDatabaseDialect(value?: string): DatabaseModuleName | null {\n const normalized = value?.trim().toLowerCase();\n if (!normalized) {\n return null;\n }\n\n if (normalized === \"pg\" || normalized === \"postgres\" || normalized === \"postgresql\" || normalized === \"database-pg\") {\n return \"database-pg\";\n }\n\n if (normalized === \"mysql\" || normalized === \"database-mysql\") {\n return \"database-mysql\";\n }\n\n return null;\n}\n\nexport function isDatabaseModule(moduleName: string): moduleName is DatabaseModuleName {\n return moduleName === \"database-pg\" || moduleName === \"database-mysql\";\n}\n\nexport function validateDatabaseUrl(rawUrl: string, moduleName: DatabaseModuleName) {\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 (moduleName === \"database-pg\" && protocol !== \"postgresql:\" && protocol !== \"postgres:\") {\n throw new Error(\"PostgreSQL URL must start with postgres:// or postgresql://\");\n }\n\n if (moduleName === \"database-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 if (!fs.existsSync(dbIndexPath)) {\n return null;\n }\n\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 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 ];\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 return moduleName === \"database-pg\" ? \"PostgreSQL\" : \"MySQL\";\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 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 variantResponse = await prompts({\n type: \"select\",\n name: \"variant\",\n message: \"Which database dialect?\",\n choices: [\n { title: \"PostgreSQL\", value: \"database-pg\" },\n { title: \"MySQL\", value: \"database-mysql\" },\n ],\n });\n\n if (!variantResponse.variant) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n resolvedModuleName = variantResponse.variant;\n } else {\n resolvedModuleName = initialModuleName as DatabaseModuleName;\n }\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 { resolvedModuleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath };\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 console.log(chalk.yellow(\"ℹ Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate\"));\n}\n"],"mappings":"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","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,28 @@
1
+ import {
2
+ DEFAULT_DATABASE_URLS,
3
+ backupDatabaseFiles,
4
+ databaseLabel,
5
+ detectInstalledDatabaseDialect,
6
+ ensureSchemaExport,
7
+ getDatabaseSetupHint,
8
+ isDatabaseModule,
9
+ parseDatabaseDialect,
10
+ printDatabaseHints,
11
+ promptDatabaseConfig,
12
+ validateDatabaseUrl
13
+ } from "./chunk-W36ZIR4Y.mjs";
14
+ import "./chunk-VMOTWTER.mjs";
15
+ export {
16
+ DEFAULT_DATABASE_URLS,
17
+ backupDatabaseFiles,
18
+ databaseLabel,
19
+ detectInstalledDatabaseDialect,
20
+ ensureSchemaExport,
21
+ getDatabaseSetupHint,
22
+ isDatabaseModule,
23
+ parseDatabaseDialect,
24
+ printDatabaseHints,
25
+ promptDatabaseConfig,
26
+ validateDatabaseUrl
27
+ };
28
+ //# sourceMappingURL=database.handler-D7EVXRJX.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":[]}