zuro-cli 0.0.2-beta.12 → 0.0.2-beta.13
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/dist/index.js +257 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +257 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -690,7 +690,8 @@ var BLOCK_SIGNATURES = {
|
|
|
690
690
|
"error-handler": "lib/errors.ts",
|
|
691
691
|
logger: "lib/logger.ts",
|
|
692
692
|
auth: "lib/auth.ts",
|
|
693
|
-
mailer: "lib/mailer.ts"
|
|
693
|
+
mailer: "lib/mailer.ts",
|
|
694
|
+
docs: "lib/openapi.ts"
|
|
694
695
|
};
|
|
695
696
|
var resolveDependencies = async (moduleDependencies, cwd) => {
|
|
696
697
|
if (!moduleDependencies || moduleDependencies.length === 0) {
|
|
@@ -841,6 +842,25 @@ function getModuleDocsPath(moduleName) {
|
|
|
841
842
|
function escapeRegex(value) {
|
|
842
843
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
843
844
|
}
|
|
845
|
+
function appendImport(source, line) {
|
|
846
|
+
if (source.includes(line)) {
|
|
847
|
+
return { source, inserted: true };
|
|
848
|
+
}
|
|
849
|
+
const importRegex = /^import .+ from .+;?\s*$/gm;
|
|
850
|
+
let lastImportIndex = 0;
|
|
851
|
+
let match;
|
|
852
|
+
while ((match = importRegex.exec(source)) !== null) {
|
|
853
|
+
lastImportIndex = match.index + match[0].length;
|
|
854
|
+
}
|
|
855
|
+
if (lastImportIndex <= 0) {
|
|
856
|
+
return { source, inserted: false };
|
|
857
|
+
}
|
|
858
|
+
return {
|
|
859
|
+
source: source.slice(0, lastImportIndex) + `
|
|
860
|
+
${line}` + source.slice(lastImportIndex),
|
|
861
|
+
inserted: true
|
|
862
|
+
};
|
|
863
|
+
}
|
|
844
864
|
async function hasEnvVariable(projectRoot, key) {
|
|
845
865
|
const envPath = import_path6.default.join(projectRoot, ".env");
|
|
846
866
|
if (!await import_fs_extra6.default.pathExists(envPath)) {
|
|
@@ -878,6 +898,12 @@ async function ensureSchemaExport(projectRoot, srcDir, schemaFileName) {
|
|
|
878
898
|
`;
|
|
879
899
|
await import_fs_extra6.default.writeFile(schemaIndexPath, next);
|
|
880
900
|
}
|
|
901
|
+
async function isDocsModuleInstalled(projectRoot, srcDir) {
|
|
902
|
+
return await import_fs_extra6.default.pathExists(import_path6.default.join(projectRoot, srcDir, "lib", "openapi.ts"));
|
|
903
|
+
}
|
|
904
|
+
async function isAuthModuleInstalled(projectRoot, srcDir) {
|
|
905
|
+
return await import_fs_extra6.default.pathExists(import_path6.default.join(projectRoot, srcDir, "lib", "auth.ts"));
|
|
906
|
+
}
|
|
881
907
|
async function injectErrorHandler(projectRoot, srcDir) {
|
|
882
908
|
const appPath = import_path6.default.join(projectRoot, srcDir, "app.ts");
|
|
883
909
|
if (!import_fs_extra6.default.existsSync(appPath)) {
|
|
@@ -940,25 +966,6 @@ async function injectAuthRoutes(projectRoot, srcDir) {
|
|
|
940
966
|
const routeIndexUserImport = `import userRoutes from "./user.routes";`;
|
|
941
967
|
const appUserImport = `import userRoutes from "./routes/user.routes";`;
|
|
942
968
|
let appModified = false;
|
|
943
|
-
const appendImport = (source, line) => {
|
|
944
|
-
if (source.includes(line)) {
|
|
945
|
-
return { source, inserted: true };
|
|
946
|
-
}
|
|
947
|
-
const importRegex = /^import .+ from .+;?\s*$/gm;
|
|
948
|
-
let lastImportIndex = 0;
|
|
949
|
-
let match;
|
|
950
|
-
while ((match = importRegex.exec(source)) !== null) {
|
|
951
|
-
lastImportIndex = match.index + match[0].length;
|
|
952
|
-
}
|
|
953
|
-
if (lastImportIndex <= 0) {
|
|
954
|
-
return { source, inserted: false };
|
|
955
|
-
}
|
|
956
|
-
return {
|
|
957
|
-
source: source.slice(0, lastImportIndex) + `
|
|
958
|
-
${line}` + source.slice(lastImportIndex),
|
|
959
|
-
inserted: true
|
|
960
|
-
};
|
|
961
|
-
};
|
|
962
969
|
for (const importLine of [authHandlerImport, authImport]) {
|
|
963
970
|
const next = appendImport(appContent, importLine);
|
|
964
971
|
if (!next.inserted) {
|
|
@@ -1044,7 +1051,177 @@ app.use("/api/users", userRoutes);
|
|
|
1044
1051
|
}
|
|
1045
1052
|
return true;
|
|
1046
1053
|
}
|
|
1047
|
-
|
|
1054
|
+
async function injectDocsRoutes(projectRoot, srcDir) {
|
|
1055
|
+
const routeIndexPath = import_path6.default.join(projectRoot, srcDir, "routes", "index.ts");
|
|
1056
|
+
const routeImport = `import docsRoutes from "./docs.routes";`;
|
|
1057
|
+
const routeMountPattern = /rootRouter\.use\(\s*["']\/docs["']\s*,\s*docsRoutes\s*\)/;
|
|
1058
|
+
if (await import_fs_extra6.default.pathExists(routeIndexPath)) {
|
|
1059
|
+
let routeContent = await import_fs_extra6.default.readFile(routeIndexPath, "utf-8");
|
|
1060
|
+
let routeModified = false;
|
|
1061
|
+
const importResult = appendImport(routeContent, routeImport);
|
|
1062
|
+
if (!importResult.inserted) {
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
if (importResult.source !== routeContent) {
|
|
1066
|
+
routeContent = importResult.source;
|
|
1067
|
+
routeModified = true;
|
|
1068
|
+
}
|
|
1069
|
+
if (!routeMountPattern.test(routeContent)) {
|
|
1070
|
+
const routeSetup = `
|
|
1071
|
+
// API docs
|
|
1072
|
+
rootRouter.use("/docs", docsRoutes);
|
|
1073
|
+
`;
|
|
1074
|
+
const exportMatch = routeContent.match(/export default rootRouter;?\s*$/m);
|
|
1075
|
+
if (!exportMatch || exportMatch.index === void 0) {
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
routeContent = routeContent.slice(0, exportMatch.index) + routeSetup + "\n" + routeContent.slice(exportMatch.index);
|
|
1079
|
+
routeModified = true;
|
|
1080
|
+
}
|
|
1081
|
+
if (routeModified) {
|
|
1082
|
+
await import_fs_extra6.default.writeFile(routeIndexPath, routeContent);
|
|
1083
|
+
}
|
|
1084
|
+
return true;
|
|
1085
|
+
}
|
|
1086
|
+
const appPath = import_path6.default.join(projectRoot, srcDir, "app.ts");
|
|
1087
|
+
if (!await import_fs_extra6.default.pathExists(appPath)) {
|
|
1088
|
+
return false;
|
|
1089
|
+
}
|
|
1090
|
+
let appContent = await import_fs_extra6.default.readFile(appPath, "utf-8");
|
|
1091
|
+
let appModified = false;
|
|
1092
|
+
const appImportResult = appendImport(appContent, `import docsRoutes from "./routes/docs.routes";`);
|
|
1093
|
+
if (!appImportResult.inserted) {
|
|
1094
|
+
return false;
|
|
1095
|
+
}
|
|
1096
|
+
if (appImportResult.source !== appContent) {
|
|
1097
|
+
appContent = appImportResult.source;
|
|
1098
|
+
appModified = true;
|
|
1099
|
+
}
|
|
1100
|
+
const hasMount = /app\.use\(\s*["']\/api\/docs["']\s*,\s*docsRoutes\s*\)/.test(appContent);
|
|
1101
|
+
if (!hasMount) {
|
|
1102
|
+
const setup = `
|
|
1103
|
+
// API docs
|
|
1104
|
+
app.use("/api/docs", docsRoutes);
|
|
1105
|
+
`;
|
|
1106
|
+
const exportMatch = appContent.match(/export default app;?\s*$/m);
|
|
1107
|
+
if (!exportMatch || exportMatch.index === void 0) {
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
appContent = appContent.slice(0, exportMatch.index) + setup + "\n" + appContent.slice(exportMatch.index);
|
|
1111
|
+
appModified = true;
|
|
1112
|
+
}
|
|
1113
|
+
if (appModified) {
|
|
1114
|
+
await import_fs_extra6.default.writeFile(appPath, appContent);
|
|
1115
|
+
}
|
|
1116
|
+
return true;
|
|
1117
|
+
}
|
|
1118
|
+
async function injectAuthDocs(projectRoot, srcDir) {
|
|
1119
|
+
const openApiPath = import_path6.default.join(projectRoot, srcDir, "lib", "openapi.ts");
|
|
1120
|
+
if (!await import_fs_extra6.default.pathExists(openApiPath)) {
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
const authMarker = "// ZURO_AUTH_DOCS";
|
|
1124
|
+
let content = await import_fs_extra6.default.readFile(openApiPath, "utf-8");
|
|
1125
|
+
if (content.includes(authMarker)) {
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
const moduleDocsEndMarker = "// ZURO_DOCS_MODULES_END";
|
|
1129
|
+
if (!content.includes(moduleDocsEndMarker)) {
|
|
1130
|
+
return false;
|
|
1131
|
+
}
|
|
1132
|
+
const authBlock = `
|
|
1133
|
+
const authSignUpSchema = z.object({
|
|
1134
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1135
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1136
|
+
name: z.string().min(1).optional().openapi({ example: "Dev User" }),
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
const authSignInSchema = z.object({
|
|
1140
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1141
|
+
password: z.string().min(8).openapi({ example: "strong-password" }),
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
const authUserSchema = z.object({
|
|
1145
|
+
id: z.string().openapi({ example: "user_123" }),
|
|
1146
|
+
email: z.string().email().openapi({ example: "dev@company.com" }),
|
|
1147
|
+
name: z.string().nullable().openapi({ example: "Dev User" }),
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
${authMarker}
|
|
1151
|
+
registry.registerPath({
|
|
1152
|
+
method: "post",
|
|
1153
|
+
path: "/api/auth/sign-up/email",
|
|
1154
|
+
tags: ["Auth"],
|
|
1155
|
+
summary: "Register using email and password",
|
|
1156
|
+
request: {
|
|
1157
|
+
body: {
|
|
1158
|
+
content: {
|
|
1159
|
+
"application/json": {
|
|
1160
|
+
schema: authSignUpSchema,
|
|
1161
|
+
},
|
|
1162
|
+
},
|
|
1163
|
+
},
|
|
1164
|
+
},
|
|
1165
|
+
responses: {
|
|
1166
|
+
200: { description: "Registration successful" },
|
|
1167
|
+
},
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
registry.registerPath({
|
|
1171
|
+
method: "post",
|
|
1172
|
+
path: "/api/auth/sign-in/email",
|
|
1173
|
+
tags: ["Auth"],
|
|
1174
|
+
summary: "Sign in using email and password",
|
|
1175
|
+
request: {
|
|
1176
|
+
body: {
|
|
1177
|
+
content: {
|
|
1178
|
+
"application/json": {
|
|
1179
|
+
schema: authSignInSchema,
|
|
1180
|
+
},
|
|
1181
|
+
},
|
|
1182
|
+
},
|
|
1183
|
+
},
|
|
1184
|
+
responses: {
|
|
1185
|
+
200: { description: "Sign in successful" },
|
|
1186
|
+
401: { description: "Invalid credentials" },
|
|
1187
|
+
},
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
registry.registerPath({
|
|
1191
|
+
method: "post",
|
|
1192
|
+
path: "/api/auth/sign-out",
|
|
1193
|
+
tags: ["Auth"],
|
|
1194
|
+
summary: "Sign out current user",
|
|
1195
|
+
responses: {
|
|
1196
|
+
200: { description: "Sign out successful" },
|
|
1197
|
+
},
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
registry.registerPath({
|
|
1201
|
+
method: "get",
|
|
1202
|
+
path: "/api/users/me",
|
|
1203
|
+
tags: ["Auth"],
|
|
1204
|
+
summary: "Get current authenticated user",
|
|
1205
|
+
security: [{ bearerAuth: [] }],
|
|
1206
|
+
responses: {
|
|
1207
|
+
200: {
|
|
1208
|
+
description: "Current user",
|
|
1209
|
+
content: {
|
|
1210
|
+
"application/json": {
|
|
1211
|
+
schema: z.object({ user: authUserSchema }),
|
|
1212
|
+
},
|
|
1213
|
+
},
|
|
1214
|
+
},
|
|
1215
|
+
401: { description: "Not authenticated" },
|
|
1216
|
+
},
|
|
1217
|
+
});
|
|
1218
|
+
`;
|
|
1219
|
+
content = content.replace(moduleDocsEndMarker, `${authBlock}
|
|
1220
|
+
${moduleDocsEndMarker}`);
|
|
1221
|
+
await import_fs_extra6.default.writeFile(openApiPath, content);
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
var add = async (moduleName, options = {}) => {
|
|
1048
1225
|
const projectRoot = process.cwd();
|
|
1049
1226
|
const projectConfig = await readZuroConfig(projectRoot);
|
|
1050
1227
|
if (!projectConfig) {
|
|
@@ -1069,6 +1246,7 @@ var add = async (moduleName) => {
|
|
|
1069
1246
|
let customSmtpVars;
|
|
1070
1247
|
let usedDefaultSmtp = false;
|
|
1071
1248
|
let mailerProvider = "smtp";
|
|
1249
|
+
let shouldInstallDocsForAuth = false;
|
|
1072
1250
|
if (resolvedModuleName === "database") {
|
|
1073
1251
|
const variantResponse = await (0, import_prompts2.default)({
|
|
1074
1252
|
type: "select",
|
|
@@ -1226,6 +1404,26 @@ var add = async (moduleName) => {
|
|
|
1226
1404
|
}
|
|
1227
1405
|
}
|
|
1228
1406
|
}
|
|
1407
|
+
if (resolvedModuleName === "auth") {
|
|
1408
|
+
const docsInstalled = await isDocsModuleInstalled(projectRoot, srcDir);
|
|
1409
|
+
if (!docsInstalled) {
|
|
1410
|
+
if (options.yes) {
|
|
1411
|
+
shouldInstallDocsForAuth = true;
|
|
1412
|
+
} else {
|
|
1413
|
+
const docsResponse = await (0, import_prompts2.default)({
|
|
1414
|
+
type: "confirm",
|
|
1415
|
+
name: "installDocs",
|
|
1416
|
+
message: "Install API docs module (Scalar + OpenAPI) too?",
|
|
1417
|
+
initial: true
|
|
1418
|
+
});
|
|
1419
|
+
if (docsResponse.installDocs === void 0) {
|
|
1420
|
+
console.log(import_chalk4.default.yellow("Operation cancelled."));
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
shouldInstallDocsForAuth = docsResponse.installDocs;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1229
1427
|
const pm = resolvePackageManager(projectRoot);
|
|
1230
1428
|
const spinner = (0, import_ora2.default)(`Checking registry for ${resolvedModuleName}...`).start();
|
|
1231
1429
|
let currentStep = "package manager preflight";
|
|
@@ -1308,6 +1506,16 @@ var add = async (moduleName) => {
|
|
|
1308
1506
|
} else {
|
|
1309
1507
|
spinner.warn("Could not configure routes automatically");
|
|
1310
1508
|
}
|
|
1509
|
+
const docsInstalled = await isDocsModuleInstalled(projectRoot, srcDir);
|
|
1510
|
+
if (docsInstalled) {
|
|
1511
|
+
spinner.start("Adding auth endpoints to API docs...");
|
|
1512
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
1513
|
+
if (authDocsInjected) {
|
|
1514
|
+
spinner.succeed("Auth endpoints added to API docs");
|
|
1515
|
+
} else {
|
|
1516
|
+
spinner.warn("Could not update API docs automatically");
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1311
1519
|
}
|
|
1312
1520
|
if (resolvedModuleName === "error-handler") {
|
|
1313
1521
|
spinner.start("Configuring error handler in app.ts...");
|
|
@@ -1318,6 +1526,25 @@ var add = async (moduleName) => {
|
|
|
1318
1526
|
spinner.warn("Could not find app.ts - error handler needs manual setup");
|
|
1319
1527
|
}
|
|
1320
1528
|
}
|
|
1529
|
+
if (resolvedModuleName === "docs") {
|
|
1530
|
+
spinner.start("Configuring docs routes...");
|
|
1531
|
+
const injected = await injectDocsRoutes(projectRoot, srcDir);
|
|
1532
|
+
if (injected) {
|
|
1533
|
+
spinner.succeed("Docs routes configured");
|
|
1534
|
+
} else {
|
|
1535
|
+
spinner.warn("Could not configure docs routes automatically");
|
|
1536
|
+
}
|
|
1537
|
+
const authInstalled = await isAuthModuleInstalled(projectRoot, srcDir);
|
|
1538
|
+
if (authInstalled) {
|
|
1539
|
+
spinner.start("Adding auth endpoints to API docs...");
|
|
1540
|
+
const authDocsInjected = await injectAuthDocs(projectRoot, srcDir);
|
|
1541
|
+
if (authDocsInjected) {
|
|
1542
|
+
spinner.succeed("Auth endpoints added to API docs");
|
|
1543
|
+
} else {
|
|
1544
|
+
spinner.warn("Could not update API docs automatically");
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1321
1548
|
let envConfigKey = resolvedModuleName;
|
|
1322
1549
|
if (resolvedModuleName === "mailer" && mailerProvider === "resend") {
|
|
1323
1550
|
envConfigKey = "mailer-resend";
|
|
@@ -1382,6 +1609,14 @@ var add = async (moduleName) => {
|
|
|
1382
1609
|
console.log(import_chalk4.default.yellow("\u2139 Review SMTP configuration in .env to ensure values are correct."));
|
|
1383
1610
|
}
|
|
1384
1611
|
}
|
|
1612
|
+
if (resolvedModuleName === "docs") {
|
|
1613
|
+
console.log(import_chalk4.default.yellow("\u2139 API docs available at: /api/docs"));
|
|
1614
|
+
console.log(import_chalk4.default.yellow("\u2139 OpenAPI spec available at: /api/docs/openapi.json"));
|
|
1615
|
+
}
|
|
1616
|
+
if (resolvedModuleName === "auth" && shouldInstallDocsForAuth) {
|
|
1617
|
+
console.log(import_chalk4.default.blue("\n\u2139 Installing API docs module..."));
|
|
1618
|
+
await add("docs", { yes: true });
|
|
1619
|
+
}
|
|
1385
1620
|
} catch (error) {
|
|
1386
1621
|
spinner.fail(import_chalk4.default.red(`Failed during ${currentStep}.`));
|
|
1387
1622
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1396,6 +1631,6 @@ ${import_chalk4.default.bold("Retry:")}`);
|
|
|
1396
1631
|
var program = new import_commander.Command();
|
|
1397
1632
|
program.name("zuro-cli").description("Zuro CLI tool").version("0.0.1");
|
|
1398
1633
|
program.command("init").description("Initialize a new Zuro project").action(init);
|
|
1399
|
-
program.command("add <module>").description("Add a module to your project").action(add);
|
|
1634
|
+
program.command("add <module>").description("Add a module to your project").action((module2, options) => add(module2, options));
|
|
1400
1635
|
program.parse(process.argv);
|
|
1401
1636
|
//# sourceMappingURL=index.js.map
|