create-charcole 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.github/workflows/release.yml +26 -26
  2. package/CHANGELOG.md +301 -301
  3. package/LICENSE +21 -21
  4. package/README.md +357 -354
  5. package/bin/index.js +494 -444
  6. package/bin/lib/pkgManager.js +49 -49
  7. package/bin/lib/templateHandler.js +33 -33
  8. package/package.json +42 -27
  9. package/packages/swagger/BACKWARD_COMPATIBILITY.md +1 -1
  10. package/packages/swagger/CHANGELOG.md +1 -1
  11. package/packages/swagger/README.md +3 -3
  12. package/packages/swagger/package.json +57 -44
  13. package/packages/swagger/src/index.d.ts +126 -126
  14. package/packages/swagger/src/index.js +12 -12
  15. package/packages/swagger/src/setup.js +100 -100
  16. package/template/js/.env.example +16 -15
  17. package/template/js/README.md +982 -978
  18. package/template/js/basePackage.json +26 -26
  19. package/template/js/src/app.js +81 -81
  20. package/template/js/src/config/constants.js +20 -20
  21. package/template/js/src/config/env.js +26 -26
  22. package/template/js/src/config/swagger.config.js +15 -15
  23. package/template/js/src/lib/swagger/SWAGGER_GUIDE.md +3 -3
  24. package/template/js/src/middlewares/errorHandler.js +180 -180
  25. package/template/js/src/middlewares/requestLogger.js +33 -33
  26. package/template/js/src/middlewares/validateRequest.js +42 -42
  27. package/template/js/src/modules/auth/auth.constants.js +3 -3
  28. package/template/js/src/modules/auth/auth.controller.js +29 -29
  29. package/template/js/src/modules/auth/auth.middlewares.js +19 -19
  30. package/template/js/src/modules/auth/auth.routes.js +131 -131
  31. package/template/js/src/modules/auth/auth.schemas.js +60 -60
  32. package/template/js/src/modules/auth/auth.service.js +67 -67
  33. package/template/js/src/modules/auth/package.json +6 -6
  34. package/template/js/src/modules/health/controller.js +151 -151
  35. package/template/js/src/modules/swagger/package.json +5 -5
  36. package/template/js/src/repositories/user.repo.js +19 -19
  37. package/template/js/src/routes/index.js +25 -25
  38. package/template/js/src/routes/protected.js +57 -57
  39. package/template/js/src/server.js +38 -38
  40. package/template/js/src/utils/AppError.js +182 -182
  41. package/template/js/src/utils/logger.js +73 -73
  42. package/template/js/src/utils/response.js +51 -51
  43. package/template/ts/.env.example +16 -15
  44. package/template/ts/README.md +982 -978
  45. package/template/ts/basePackage.json +36 -36
  46. package/template/ts/build.js +46 -46
  47. package/template/ts/src/app.ts +71 -71
  48. package/template/ts/src/config/constants.ts +27 -27
  49. package/template/ts/src/config/env.ts +40 -40
  50. package/template/ts/src/config/swagger.config.ts +30 -30
  51. package/template/ts/src/lib/swagger/SWAGGER_GUIDE.md +2 -2
  52. package/template/ts/src/middlewares/errorHandler.ts +201 -201
  53. package/template/ts/src/middlewares/requestLogger.ts +38 -38
  54. package/template/ts/src/middlewares/validateRequest.ts +46 -46
  55. package/template/ts/src/modules/auth/auth.constants.ts +6 -6
  56. package/template/ts/src/modules/auth/auth.controller.ts +32 -32
  57. package/template/ts/src/modules/auth/auth.middlewares.ts +46 -46
  58. package/template/ts/src/modules/auth/auth.routes.ts +52 -52
  59. package/template/ts/src/modules/auth/auth.schemas.ts +73 -73
  60. package/template/ts/src/modules/auth/auth.service.ts +106 -106
  61. package/template/ts/src/modules/auth/package.json +10 -10
  62. package/template/ts/src/modules/health/controller.ts +80 -80
  63. package/template/ts/src/modules/swagger/package.json +5 -5
  64. package/template/ts/src/repositories/user.repo.ts +33 -33
  65. package/template/ts/src/routes/index.ts +24 -24
  66. package/template/ts/src/routes/protected.ts +46 -46
  67. package/template/ts/src/server.ts +41 -41
  68. package/template/ts/src/types/express.d.ts +9 -9
  69. package/template/ts/src/utils/AppError.ts +220 -220
  70. package/template/ts/src/utils/logger.ts +55 -55
  71. package/template/ts/src/utils/response.ts +100 -100
  72. package/template/ts/tsconfig.json +26 -26
  73. package/packages/swagger/package-lock.json +0 -1715
  74. package/tmpclaude-1049-cwd +0 -1
  75. package/tmpclaude-3e37-cwd +0 -1
  76. package/tmpclaude-4d73-cwd +0 -1
  77. package/tmpclaude-8a8e-cwd +0 -1
@@ -1,36 +1,36 @@
1
- {
2
- "name": "charcole",
3
- "version": "2.2.0",
4
- "description": "Production-grade Node.js Express API",
5
- "main": "dist/server.js",
6
- "type": "module",
7
- "scripts": {
8
- "start": "node dist/server.js",
9
- "dev": "tsx watch src/server.ts",
10
- "build": "node build.js",
11
- "clean": "rimraf dist",
12
- "prebuild": "npm run clean",
13
- "lint": "echo \"Add linting here\"",
14
- "test": "echo \"Add tests here\""
15
- },
16
- "engines": {
17
- "node": ">=18.0.0"
18
- },
19
- "dependencies": {
20
- "express": "^4.18.2",
21
- "cors": "^2.8.5",
22
- "dotenv": "^16.3.1",
23
- "zod": "^3.25.76"
24
- },
25
- "devDependencies": {
26
- "@types/cors": "^2.8.19",
27
- "@types/express": "^4.17.21",
28
- "@types/node": "^25.0.10",
29
- "esbuild": "^0.24.2",
30
- "rimraf": "^6.0.1",
31
- "tsc-alias": "^1.8.8",
32
- "tsx": "^4.19.2",
33
- "typescript": "^5.9.3"
34
- },
35
- "license": "ISC"
36
- }
1
+ {
2
+ "name": "charcole",
3
+ "version": "2.2.2",
4
+ "description": "Production-grade Node.js Express API",
5
+ "main": "dist/server.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "node dist/server.js",
9
+ "dev": "tsx watch src/server.ts",
10
+ "build": "node build.js",
11
+ "clean": "rimraf dist",
12
+ "prebuild": "npm run clean",
13
+ "lint": "echo \"Add linting here\"",
14
+ "test": "echo \"Add tests here\""
15
+ },
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "dependencies": {
20
+ "express": "^4.18.2",
21
+ "cors": "^2.8.5",
22
+ "dotenv": "^16.3.1",
23
+ "zod": "^3.25.76"
24
+ },
25
+ "devDependencies": {
26
+ "@types/cors": "^2.8.19",
27
+ "@types/express": "^4.17.21",
28
+ "@types/node": "^25.0.10",
29
+ "esbuild": "^0.24.2",
30
+ "rimraf": "^6.0.1",
31
+ "tsc-alias": "^1.8.8",
32
+ "tsx": "^4.19.2",
33
+ "typescript": "^5.9.3"
34
+ },
35
+ "license": "ISC"
36
+ }
@@ -1,46 +1,46 @@
1
- import { build } from "esbuild";
2
- import { glob } from "glob";
3
- import fs from "fs";
4
- import path from "path";
5
-
6
- const entryPoints = await glob("src/**/*.ts");
7
-
8
- // Custom plugin to rewrite .ts imports to .js
9
- const rewriteImportsPlugin = {
10
- name: "rewrite-imports",
11
- setup(build) {
12
- build.onLoad({ filter: /\.ts$/ }, async (args) => {
13
- const contents = await fs.promises.readFile(args.path, "utf8");
14
-
15
- // Replace .ts extensions with .js in imports
16
- const modifiedContents = contents
17
- .replace(/from\s+['"]([^'"]+)\.ts['"]/g, 'from "$1.js"')
18
- .replace(/import\s+['"]([^'"]+)\.ts['"]/g, 'import "$1.js"');
19
-
20
- return {
21
- contents: modifiedContents,
22
- loader: "ts",
23
- };
24
- });
25
- },
26
- };
27
-
28
- await build({
29
- entryPoints,
30
- outdir: "dist",
31
- bundle: false,
32
- platform: "node",
33
- format: "esm",
34
- target: "es2020",
35
- sourcemap: true,
36
- outExtension: { ".js": ".js" },
37
- packages: "external",
38
- plugins: [rewriteImportsPlugin],
39
- })
40
- .then(() => {
41
- console.log("✅ Build completed successfully!");
42
- })
43
- .catch((error) => {
44
- console.error("❌ Build failed:", error);
45
- process.exit(1);
46
- });
1
+ import { build } from "esbuild";
2
+ import { glob } from "glob";
3
+ import fs from "fs";
4
+ import path from "path";
5
+
6
+ const entryPoints = await glob("src/**/*.ts");
7
+
8
+ // Custom plugin to rewrite .ts imports to .js
9
+ const rewriteImportsPlugin = {
10
+ name: "rewrite-imports",
11
+ setup(build) {
12
+ build.onLoad({ filter: /\.ts$/ }, async (args) => {
13
+ const contents = await fs.promises.readFile(args.path, "utf8");
14
+
15
+ // Replace .ts extensions with .js in imports
16
+ const modifiedContents = contents
17
+ .replace(/from\s+['"]([^'"]+)\.ts['"]/g, 'from "$1.js"')
18
+ .replace(/import\s+['"]([^'"]+)\.ts['"]/g, 'import "$1.js"');
19
+
20
+ return {
21
+ contents: modifiedContents,
22
+ loader: "ts",
23
+ };
24
+ });
25
+ },
26
+ };
27
+
28
+ await build({
29
+ entryPoints,
30
+ outdir: "dist",
31
+ bundle: false,
32
+ platform: "node",
33
+ format: "esm",
34
+ target: "es2020",
35
+ sourcemap: true,
36
+ outExtension: { ".js": ".js" },
37
+ packages: "external",
38
+ plugins: [rewriteImportsPlugin],
39
+ })
40
+ .then(() => {
41
+ console.log("✅ Build completed successfully!");
42
+ })
43
+ .catch((error) => {
44
+ console.error("❌ Build failed:", error);
45
+ process.exit(1);
46
+ });
@@ -1,71 +1,71 @@
1
- import express, { Request, Response, NextFunction } from "express";
2
- import cors from "cors";
3
- import { userRepo } from "./repositories/user.repo.ts";
4
- import { env } from "./config/env.ts";
5
- import { requestLogger } from "./middlewares/requestLogger.ts";
6
- import {
7
- errorHandler,
8
- asyncHandler,
9
- NotFoundError,
10
- } from "./middlewares/errorHandler.ts";
11
- import { sendSuccess } from "./utils/response.ts";
12
- import { logger } from "./utils/logger.ts";
13
- import routes from "./routes/index.ts";
14
- import swaggerOptions from "./config/swagger.config.ts";
15
- import { setupSwagger } from "@charcoles/swagger";
16
-
17
- export const app = express();
18
-
19
- app.set("trust proxy", 1);
20
-
21
- app.use(
22
- cors({
23
- origin: env.CORS_ORIGIN,
24
- credentials: true,
25
- methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
26
- allowedHeaders: ["Content-Type", "Authorization"],
27
- }),
28
- );
29
-
30
- app.use(express.json({ limit: "10mb" }));
31
- app.use(express.urlencoded({ extended: true, limit: "10mb" }));
32
-
33
- app.use((req: Request, res: Response, next: NextFunction) => {
34
- req.setTimeout(env.REQUEST_TIMEOUT);
35
- res.setTimeout(env.REQUEST_TIMEOUT);
36
- next();
37
- });
38
-
39
- app.use(requestLogger);
40
-
41
- setupSwagger(app, swaggerOptions);
42
-
43
- app.use("/api", routes);
44
-
45
- app.get(
46
- "/",
47
- asyncHandler(async (_req: Request, res: Response) => {
48
- sendSuccess(
49
- res,
50
- {
51
- message: "Welcome to Charcole API",
52
- version: "2.0.0",
53
- environment: env.NODE_ENV,
54
- },
55
- 200,
56
- "API is online",
57
- );
58
- }),
59
- );
60
-
61
- app.use((req: Request) => {
62
- throw new NotFoundError(`${req.method} ${req.path}`, {
63
- method: req.method,
64
- path: req.path,
65
- });
66
- });
67
-
68
- app.use(errorHandler);
69
-
70
- logger.info("Express app configured successfully");
71
- app.locals.userRepo = userRepo;
1
+ import express, { Request, Response, NextFunction } from "express";
2
+ import cors from "cors";
3
+ import { userRepo } from "./repositories/user.repo.ts";
4
+ import { env } from "./config/env.ts";
5
+ import { requestLogger } from "./middlewares/requestLogger.ts";
6
+ import {
7
+ errorHandler,
8
+ asyncHandler,
9
+ NotFoundError,
10
+ } from "./middlewares/errorHandler.ts";
11
+ import { sendSuccess } from "./utils/response.ts";
12
+ import { logger } from "./utils/logger.ts";
13
+ import routes from "./routes/index.ts";
14
+ import swaggerOptions from "./config/swagger.config.ts";
15
+ import { setupSwagger } from "@charcoles/swagger";
16
+
17
+ export const app = express();
18
+
19
+ app.set("trust proxy", 1);
20
+
21
+ app.use(
22
+ cors({
23
+ origin: env.CORS_ORIGIN,
24
+ credentials: true,
25
+ methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
26
+ allowedHeaders: ["Content-Type", "Authorization"],
27
+ }),
28
+ );
29
+
30
+ app.use(express.json({ limit: "10mb" }));
31
+ app.use(express.urlencoded({ extended: true, limit: "10mb" }));
32
+
33
+ app.use((req: Request, res: Response, next: NextFunction) => {
34
+ req.setTimeout(env.REQUEST_TIMEOUT);
35
+ res.setTimeout(env.REQUEST_TIMEOUT);
36
+ next();
37
+ });
38
+
39
+ app.use(requestLogger);
40
+
41
+ setupSwagger(app, swaggerOptions);
42
+
43
+ app.use("/api", routes);
44
+
45
+ app.get(
46
+ "/",
47
+ asyncHandler(async (_req: Request, res: Response) => {
48
+ sendSuccess(
49
+ res,
50
+ {
51
+ message: "Welcome to Charcole API",
52
+ version: "2.0.0",
53
+ environment: env.NODE_ENV,
54
+ },
55
+ 200,
56
+ "API is online",
57
+ );
58
+ }),
59
+ );
60
+
61
+ app.use((req: Request) => {
62
+ throw new NotFoundError(`${req.method} ${req.path}`, {
63
+ method: req.method,
64
+ path: req.path,
65
+ });
66
+ });
67
+
68
+ app.use(errorHandler);
69
+
70
+ logger.info("Express app configured successfully");
71
+ app.locals.userRepo = userRepo;
@@ -1,27 +1,27 @@
1
- export const HTTP_STATUS = {
2
- OK: 200,
3
- CREATED: 201,
4
- BAD_REQUEST: 400,
5
- UNAUTHORIZED: 401,
6
- FORBIDDEN: 403,
7
- NOT_FOUND: 404,
8
- CONFLICT: 409,
9
- UNPROCESSABLE_ENTITY: 422,
10
- INTERNAL_SERVER_ERROR: 500,
11
- SERVICE_UNAVAILABLE: 503,
12
- } as const;
13
-
14
- export type HttpStatus = typeof HTTP_STATUS;
15
-
16
- export const ERROR_MESSAGES = {
17
- VALIDATION_ERROR: "Validation failed",
18
- NOT_FOUND: "Resource not found",
19
- UNAUTHORIZED: "Unauthorized",
20
- SERVER_ERROR: "Internal server error",
21
- BAD_REQUEST: "Bad request",
22
- } as const;
23
-
24
- export type ErrorMessages = typeof ERROR_MESSAGES;
25
-
26
- export type HttpStatusCode = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS];
27
- export type ErrorMessage = (typeof ERROR_MESSAGES)[keyof typeof ERROR_MESSAGES];
1
+ export const HTTP_STATUS = {
2
+ OK: 200,
3
+ CREATED: 201,
4
+ BAD_REQUEST: 400,
5
+ UNAUTHORIZED: 401,
6
+ FORBIDDEN: 403,
7
+ NOT_FOUND: 404,
8
+ CONFLICT: 409,
9
+ UNPROCESSABLE_ENTITY: 422,
10
+ INTERNAL_SERVER_ERROR: 500,
11
+ SERVICE_UNAVAILABLE: 503,
12
+ } as const;
13
+
14
+ export type HttpStatus = typeof HTTP_STATUS;
15
+
16
+ export const ERROR_MESSAGES = {
17
+ VALIDATION_ERROR: "Validation failed",
18
+ NOT_FOUND: "Resource not found",
19
+ UNAUTHORIZED: "Unauthorized",
20
+ SERVER_ERROR: "Internal server error",
21
+ BAD_REQUEST: "Bad request",
22
+ } as const;
23
+
24
+ export type ErrorMessages = typeof ERROR_MESSAGES;
25
+
26
+ export type HttpStatusCode = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS];
27
+ export type ErrorMessage = (typeof ERROR_MESSAGES)[keyof typeof ERROR_MESSAGES];
@@ -1,40 +1,40 @@
1
- import { z } from "zod";
2
-
3
- const envSchema = z.object({
4
- NODE_ENV: z
5
- .enum(["development", "production", "test"])
6
- .default("development"),
7
- PORT: z.coerce.number().default(3000),
8
- LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
9
- CORS_ORIGIN: z.string().default("*"),
10
- REQUEST_TIMEOUT: z.coerce.number().default(30000),
11
- });
12
-
13
- type EnvSchema = z.infer<typeof envSchema>;
14
-
15
- const parseEnv = (): EnvSchema => {
16
- try {
17
- return envSchema.parse(process.env);
18
- } catch (error) {
19
- if (error instanceof z.ZodError) {
20
- console.error("❌ Invalid environment variables:");
21
- error.errors.forEach((err) => {
22
- console.error(` - ${err.path.join(".")}: ${err.message}`);
23
- });
24
- } else {
25
- console.error("❌ Failed to parse environment variables:", error);
26
- }
27
- process.exit(1);
28
- }
29
- };
30
-
31
- const parsedEnv = parseEnv();
32
-
33
- export const env = {
34
- ...parsedEnv,
35
- isDevelopment: parsedEnv.NODE_ENV === "development",
36
- isProduction: parsedEnv.NODE_ENV === "production",
37
- isTest: parsedEnv.NODE_ENV === "test",
38
- } as const;
39
-
40
- export type Env = typeof env;
1
+ import { z } from "zod";
2
+
3
+ const envSchema = z.object({
4
+ NODE_ENV: z
5
+ .enum(["development", "production", "test"])
6
+ .default("development"),
7
+ PORT: z.coerce.number().default(3000),
8
+ LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
9
+ CORS_ORIGIN: z.string().default("*"),
10
+ REQUEST_TIMEOUT: z.coerce.number().default(30000),
11
+ });
12
+
13
+ type EnvSchema = z.infer<typeof envSchema>;
14
+
15
+ const parseEnv = (): EnvSchema => {
16
+ try {
17
+ return envSchema.parse(process.env);
18
+ } catch (error) {
19
+ if (error instanceof z.ZodError) {
20
+ console.error("❌ Invalid environment variables:");
21
+ error.errors.forEach((err) => {
22
+ console.error(` - ${err.path.join(".")}: ${err.message}`);
23
+ });
24
+ } else {
25
+ console.error("❌ Failed to parse environment variables:", error);
26
+ }
27
+ process.exit(1);
28
+ }
29
+ };
30
+
31
+ const parsedEnv = parseEnv();
32
+
33
+ export const env = {
34
+ ...parsedEnv,
35
+ isDevelopment: parsedEnv.NODE_ENV === "development",
36
+ isProduction: parsedEnv.NODE_ENV === "production",
37
+ isTest: parsedEnv.NODE_ENV === "test",
38
+ } as const;
39
+
40
+ export type Env = typeof env;
@@ -1,30 +1,30 @@
1
- import { registerSchema, loginSchema } from "../modules/auth/auth.schemas.ts";
2
- import { createItemSchema } from "../modules/health/controller.ts";
3
-
4
- const swaggerConfig = {
5
- title: process.env.APP_NAME || "Charcole API",
6
- version: process.env.APP_VERSION || "1.0.0",
7
- description: "Production-ready Node.js Express API",
8
- path: "/api-docs",
9
- servers: [
10
- {
11
- url: process.env.APP_URL || "http://localhost:3000",
12
- description:
13
- process.env.NODE_ENV === "production"
14
- ? "Production server"
15
- : "Development server",
16
- },
17
- ],
18
- // NEW: Auto-register Zod schemas - no more manual duplication!
19
- schemas: {
20
- registerSchema,
21
- loginSchema,
22
- createItemSchema,
23
- },
24
- // Common response templates are included by default
25
- includeCommonResponses: true,
26
- };
27
-
28
- export type SwaggerConfig = typeof swaggerConfig;
29
-
30
- export default swaggerConfig;
1
+ import { registerSchema, loginSchema } from "../modules/auth/auth.schemas.ts";
2
+ import { createItemSchema } from "../modules/health/controller.ts";
3
+
4
+ const swaggerConfig = {
5
+ title: process.env.APP_NAME || "Charcole API",
6
+ version: process.env.APP_VERSION || "1.0.1",
7
+ description: "Production-ready Node.js Express API",
8
+ path: "/api-docs",
9
+ servers: [
10
+ {
11
+ url: process.env.APP_URL || "http://localhost:3000",
12
+ description:
13
+ process.env.NODE_ENV === "production"
14
+ ? "Production server"
15
+ : "Development server",
16
+ },
17
+ ],
18
+ // NEW: Auto-register Zod schemas - no more manual duplication!
19
+ schemas: {
20
+ registerSchema,
21
+ loginSchema,
22
+ createItemSchema,
23
+ },
24
+ // Common response templates are included by default
25
+ includeCommonResponses: true,
26
+ };
27
+
28
+ export type SwaggerConfig = typeof swaggerConfig;
29
+
30
+ export default swaggerConfig;
@@ -18,7 +18,7 @@ import { createItemSchema } from "../modules/health/controller.ts";
18
18
 
19
19
  const swaggerConfig = {
20
20
  title: "My API",
21
- version: "1.0.0",
21
+ version: "1.0.1",
22
22
  // Auto-register schemas - they'll be converted to OpenAPI automatically!
23
23
  schemas: {
24
24
  registerSchema,
@@ -472,7 +472,7 @@ const app = express();
472
472
 
473
473
  setupSwagger(app, {
474
474
  title: "My API",
475
- version: "1.0.0",
475
+ version: "1.0.1",
476
476
  schemas: {
477
477
  mySchema, // Your Zod schemas
478
478
  },