appinit-templates 1.0.0

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 (41) hide show
  1. package/base/common/,gitignore.ejs +33 -0
  2. package/base/common/.prettierrc.ejs +5 -0
  3. package/base/common/db.ejs +33 -0
  4. package/base/common/env.ejs +4 -0
  5. package/base/common/src/middleware/asyncHandler.ejs +5 -0
  6. package/base/common/src/routes/health.routes.ejs +12 -0
  7. package/base/common/src/routes/index.ejs +12 -0
  8. package/base/common/src/utils/ApiError.ejs +9 -0
  9. package/base/common/src/utils/logger.ejs +25 -0
  10. package/base/javascript/.prettierrc.ejs +1 -0
  11. package/base/javascript/Readme.md.ejs +135 -0
  12. package/base/javascript/eslint.config.js.ejs +26 -0
  13. package/base/javascript/src/config/db.js.ejs +34 -0
  14. package/base/javascript/src/config/env.js.ejs +37 -0
  15. package/base/javascript/src/controllers/health.controller.js.ejs +40 -0
  16. package/base/javascript/src/index.js.ejs +54 -0
  17. package/base/javascript/src/middleware/asyncHandler.js.ejs +1 -0
  18. package/base/javascript/src/middleware/errorHandler.js.ejs +11 -0
  19. package/base/javascript/src/routes/health.routes.js.ejs +1 -0
  20. package/base/javascript/src/routes/index.js.ejs +7 -0
  21. package/base/javascript/src/utils/ApiError.js.ejs +1 -0
  22. package/base/javascript/src/utils/logger.js.ejs +1 -0
  23. package/base/typescript/.prettierrc.ejs +1 -0
  24. package/base/typescript/Readme.md.ejs +141 -0
  25. package/base/typescript/eslint.config.js.ejs +29 -0
  26. package/base/typescript/src/config/db.ts.ejs +38 -0
  27. package/base/typescript/src/config/env.ts.ejs +37 -0
  28. package/base/typescript/src/controllers/health.controller.ts.ejs +50 -0
  29. package/base/typescript/src/index.ts.ejs +54 -0
  30. package/base/typescript/src/middleware/errorHandler.ts.ejs +20 -0
  31. package/base/typescript/src/routes/health.routes.ts.ejs +1 -0
  32. package/base/typescript/src/routes/index.ts.ejs +7 -0
  33. package/base/typescript/src/utils/ApiError.ts.ejs +12 -0
  34. package/base/typescript/src/utils/logger.ts.ejs +1 -0
  35. package/base/websockets/socket.io/javascript/index.js.ejs +87 -0
  36. package/base/websockets/socket.io/typescript/index.ts.ejs +81 -0
  37. package/base/websockets/ws/javascript/index.js.ejs +86 -0
  38. package/base/websockets/ws/typescript/index.ts.ejs +88 -0
  39. package/features/docker/base/javascript/Dockerfile.ejs +19 -0
  40. package/features/docker/base/typescript/Dockerfile.ejs +41 -0
  41. package/package.json +9 -0
@@ -0,0 +1,29 @@
1
+ import js from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ export default tseslint.config(
5
+ js.configs.recommended,
6
+ ...tseslint.configs.recommended,
7
+ {
8
+ files: ["**/*.ts"],
9
+ languageOptions: {
10
+ ecmaVersion: 2022,
11
+ sourceType: "module",
12
+ parser: tseslint.parser,
13
+ parserOptions: {
14
+ project: "./tsconfig.json",
15
+ },
16
+ },
17
+ rules: {
18
+ "@typescript-eslint/no-explicit-any": "warn",
19
+ "@typescript-eslint/no-unused-vars": [
20
+ "warn",
21
+ {
22
+ argsIgnorePattern: "^_",
23
+ varsIgnorePattern: "^_",
24
+ },
25
+ ],
26
+ "no-console": "off",
27
+ },
28
+ },
29
+ );
@@ -0,0 +1,38 @@
1
+ <%_ if (database === 'mongo') { _%>
2
+ import mongoose from "mongoose";
3
+
4
+ const connectDb = async (): Promise<void> => {
5
+ try {
6
+ if (mongoose.connection.readyState >= 1) return;
7
+ const conn = await mongoose.connect(process.env.MONGODB_URI);
8
+ console.log(`MongoDB Connected: ${conn.connection.host}`);
9
+ } catch (error) {
10
+ console.error(
11
+ `MongoDB Connection Error: ${error instanceof Error ? error.message : error}`
12
+ );
13
+ process.exit(1);
14
+ }
15
+ };
16
+
17
+ export default connectDb;
18
+ <%_ } else if (database === 'postgresql_prisma') { _%>
19
+ import { PrismaClient } from "../../generated/prisma/client";
20
+
21
+ declare global {
22
+ var prisma: PrismaClient | undefined;
23
+ }
24
+
25
+ const prisma = global.prisma ?? new PrismaClient();
26
+
27
+ if (process.env.NODE_ENV !== "production") {
28
+ global.prisma = prisma;
29
+ }
30
+
31
+ const connectDb = async (): Promise<void> => {
32
+ await prisma.$connect();
33
+ console.log("PostgreSQL Connected");
34
+ };
35
+
36
+ export { prisma, connectDb };
37
+ export default prisma;
38
+ <%_ } _%>
@@ -0,0 +1,37 @@
1
+ import dotenv from "dotenv";
2
+ import { z } from "zod";
3
+
4
+ dotenv.config();
5
+
6
+ const envSchema = z.object({
7
+ NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
8
+ PORT: z.coerce.number().default(3000),
9
+ <%_ if (database === 'mongo') { _%>
10
+ MONGODB_URI: z.string().min(1, "MongoDB URI is required"),
11
+ <%_ } else if (database === 'postgresql_prisma') { _%>
12
+ DATABASE_URL: z.string().min(1, "Database URL is required"),
13
+ <%_ } _%>
14
+ JWT_SECRET: z.string().min(1, "JWT secret is required"),
15
+ JWT_EXPIRES_IN: z.string().default("7d"),
16
+ });
17
+
18
+ const parsed = envSchema.safeParse(process.env);
19
+
20
+ if (!parsed.success) {
21
+ console.error("Invalid environment variables:", parsed.error.format());
22
+ process.exit(1);
23
+ }
24
+
25
+ export const config = {
26
+ nodeEnv: parsed.data.NODE_ENV,
27
+ port: parsed.data.PORT,
28
+ <%_ if (database === 'mongo') { _%>
29
+ mongoUri: parsed.data.MONGODB_URI,
30
+ <%_ } else if (database === 'postgresql_prisma') { _%>
31
+ databaseUrl: parsed.data.DATABASE_URL,
32
+ <%_ } _%>
33
+ jwt: {
34
+ secret: parsed.data.JWT_SECRET,
35
+ expiresIn: parsed.data.JWT_EXPIRES_IN,
36
+ },
37
+ };
@@ -0,0 +1,50 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ <%_ if (database === 'mongo') { _%>
3
+ import mongoose from "mongoose";
4
+ <%_ } else if (database === 'postgresql_prisma') { _%>
5
+ import prisma from "../config/db";
6
+ <%_ } _%>
7
+
8
+ /**
9
+ * Health Check Controller
10
+ * Checks the status of the server and the <%= database %> connection
11
+ */
12
+ export const getHealth = async (_req: Request, res: Response, next: NextFunction): Promise<void> => {
13
+ try {
14
+ const uptime = process.uptime();
15
+ <%_ if (database === 'none') { _%>
16
+ const dbStatus: "disconnected" = "disconnected";
17
+ const isHealthy = true;
18
+ <%_ } else { _%>
19
+ let dbStatus: "connected" | "disconnected" | "error" = "disconnected";
20
+
21
+ <%_ if (database === 'mongo') { _%>
22
+ // Check Mongoose connection state (1 = connected)
23
+ dbStatus = mongoose.connection.readyState === 1 ? "connected" : "disconnected";
24
+
25
+ <%_ } else if (database === 'postgresql_prisma') { _%>
26
+ try {
27
+ await prisma.$queryRaw`SELECT 1`;
28
+ dbStatus = "connected";
29
+ } catch (error) {
30
+ dbStatus = "error";
31
+ }
32
+ <%_ } _%>
33
+
34
+ const isHealthy = dbStatus === "connected";
35
+ <%_ } _%>
36
+
37
+ res.status(isHealthy ? 200 : 503).json({
38
+ success: true,
39
+ data: {
40
+ status: "ok",
41
+ timestamp: new Date().toISOString(),
42
+ uptime: `${Math.floor(uptime)}s`,
43
+ database: dbStatus,
44
+ },
45
+ });
46
+ } catch (error) {
47
+ // Since we removed asyncHandler, we manually pass the error to the global handler
48
+ next(error);
49
+ }
50
+ };
@@ -0,0 +1,54 @@
1
+ // typescript index file
2
+ import express, { type Request, type Response, type NextFunction } from "express";
3
+ import helmet from "helmet";
4
+ import cors from "cors";
5
+ import routes from "./routes/index";
6
+ import errorHandler from "./middleware/errorHandler";
7
+ import { logger } from "./utils/logger";
8
+ import { config } from "./config/env";
9
+
10
+ <%_ if (database === 'postgresql_prisma') { _%>
11
+ // Prisma note: in prisma/schema.prisma use:
12
+ // provider = "prisma-client-js"
13
+ // output = "../../generated/prisma"
14
+ <%_ } _%>
15
+
16
+ const app = express();
17
+ const port = config.port;
18
+
19
+ app.use(express.json());
20
+ app.use(helmet());
21
+ app.use(cors());
22
+ app.use(express.urlencoded({ extended: true }));
23
+
24
+ // Call connectDb() from ./config/db to connect to your database.
25
+
26
+ // logger middleware
27
+ app.use((req: Request, res: Response, next: NextFunction) => {
28
+ logger.info({
29
+ ip: req.ip,
30
+ method: req.method,
31
+ path: req.path,
32
+ });
33
+ next();
34
+ });
35
+
36
+ app.use("/api", routes);
37
+
38
+ app.use(errorHandler);
39
+
40
+ app.listen(port, () => {
41
+ logger.info(`Server running on port ${port}`);
42
+ logger.info(`Environment: ${config.nodeEnv}`);
43
+ });
44
+
45
+ process.on("SIGINT", () => {
46
+ console.log("SIGINT received, forcefully stopping the server");
47
+ process.exit(0);
48
+ });
49
+ process.on("SIGTERM", () => {
50
+ console.log("SIGTERM received, gracefully stopping the server");
51
+ process.exit(0);
52
+ });
53
+
54
+ export default app;
@@ -0,0 +1,20 @@
1
+ import type { NextFunction, Request, Response } from "express";
2
+ import ApiError from "../utils/ApiError";
3
+
4
+ type ErrorWithStatus = ApiError | (Error & { statusCode?: number });
5
+
6
+ const errorHandler = (
7
+ err: ErrorWithStatus,
8
+ _req: Request,
9
+ res: Response,
10
+ _next: NextFunction,
11
+ ) => {
12
+ const statusCode = err.statusCode || 500;
13
+ const message = err.message || "Internal Server Error";
14
+
15
+ res.status(statusCode).json({
16
+ success: false,
17
+ message: message,
18
+ });
19
+ };
20
+ export default errorHandler;
@@ -0,0 +1 @@
1
+ <%- await include("../../../common/src/routes/health.routes.ejs") %>
@@ -0,0 +1,7 @@
1
+ <%_ if (database === 'postgresql_prisma') { _%>
2
+ // Prisma note: in prisma/schema.prisma use:
3
+ // provider = "prisma-client-js"
4
+ // output = "../generated/prisma"
5
+ <%_ } _%>
6
+
7
+ <%- await include("../../../common/src/routes/index.ejs") %>
@@ -0,0 +1,12 @@
1
+ class ApiError extends Error {
2
+ statusCode: number;
3
+ success: boolean;
4
+
5
+ constructor(statusCode: number, message: string) {
6
+ super(message);
7
+ this.statusCode = statusCode;
8
+ this.success = false;
9
+ }
10
+ }
11
+
12
+ export default ApiError;
@@ -0,0 +1 @@
1
+ <%- await include("../../../common/src/utils/logger.ejs") %>
@@ -0,0 +1,87 @@
1
+ import express from "express";
2
+ import helmet from "helmet";
3
+ import cors from "cors";
4
+ import { createServer } from "http";
5
+ import { Server } from "socket.io";
6
+ import routes from "./src/routes/index.js";
7
+ import errorHandler from "./src/middleware/errorHandler.js";
8
+ import { logger } from "./src/utils/logger.js";
9
+ import { config } from "./src/config/env.js";
10
+
11
+ <%_ if (database === 'postgresql_prisma') { _%>
12
+ // Prisma note: in prisma/schema.prisma use:
13
+ // provider = "prisma-client-js"
14
+ // output = "../generated/prisma"
15
+ <%_ } _%>
16
+
17
+ const app = express();
18
+ const port = config.port;
19
+
20
+ // 1. Create the HTTP Server
21
+ const http_server = createServer(app);
22
+
23
+ // 2. Initialize Socket.io on the HTTP Server
24
+ const socket_io_server = new Server(http_server, {
25
+ cors: {
26
+ origin: "*",
27
+ }
28
+ });
29
+
30
+ // Middleware
31
+ app.use(express.json());
32
+ app.use(helmet());
33
+ app.use(cors());
34
+ app.use(express.urlencoded({ extended: true }));
35
+
36
+ app.use((req, res, next) => {
37
+ logger.info({
38
+ method: req.method,
39
+ path: req.path,
40
+ ip: req.ip,
41
+ });
42
+ next();
43
+ });
44
+
45
+ // Routes
46
+ app.use("/api", routes);
47
+ app.use(errorHandler);
48
+
49
+ // 3. Socket.io Event Handling
50
+ socket_io_server.on("connection", (socket) => {
51
+ console.log(`Client connected: ${socket.id}`);
52
+
53
+ // 'data' is already an object here—no JSON.parse needed
54
+ socket.on("message", (data) => {
55
+ if (data.type === "ping") {
56
+ // No JSON.stringify needed
57
+ socket.send({ type: "pong" });
58
+ }
59
+ });
60
+
61
+ socket.on("disconnect", () => {
62
+ console.log(`Client disconnected: ${socket.id}`);
63
+ });
64
+
65
+ socket.on("error", (err) => {
66
+ console.error("Socket Error:", err);
67
+ });
68
+ });
69
+
70
+ // 4. Start the server using http_server
71
+ http_server.listen(port, () => {
72
+ logger.info(`Server running on port ${port}`);
73
+ logger.info(`Environment: ${config.nodeEnv}`);
74
+ });
75
+
76
+ // Graceful Shutdown
77
+ const shutdown = () => {
78
+ console.log("Stopping server...");
79
+ http_server.close(() => {
80
+ process.exit(0);
81
+ });
82
+ };
83
+
84
+ process.on("SIGINT", shutdown);
85
+ process.on("SIGTERM", shutdown);
86
+
87
+ export default http_server;
@@ -0,0 +1,81 @@
1
+ // typescript index file with a socket.io server
2
+ import express, { type Request, type Response, type NextFunction } from "express";
3
+ import helmet from "helmet";
4
+ import cors from "cors";
5
+ import routes from "./routes/index";
6
+ import errorHandler from "./middleware/errorHandler";
7
+ import { logger } from "./utils/logger";
8
+ import { config } from "./config/env";
9
+ import { createServer } from "http";
10
+ import { Server } from "socket.io";
11
+
12
+ <%_ if (database === 'postgresql_prisma') { _%>
13
+ // Prisma note: in prisma/schema.prisma use:
14
+ // provider = "prisma-client-js"
15
+ // output = "../generated/prisma"
16
+ <%_ } _%>
17
+
18
+ const app = express();
19
+ const port = config.port;
20
+ const http_server = createServer(app);
21
+ const socket_io_server = new Server(http_server, {
22
+ cors: {
23
+ origin: "*",
24
+ }
25
+ });
26
+
27
+ app.use(express.json());
28
+ app.use(helmet());
29
+ app.use(cors());
30
+ app.use(express.urlencoded({ extended: true }));
31
+
32
+ // logger middleware
33
+ app.use((req: Request, res: Response, next: NextFunction) => {
34
+ logger.info({
35
+ ip: req.ip,
36
+ method: req.method,
37
+ path: req.path,
38
+ });
39
+ next();
40
+ });
41
+
42
+ app.use("/api", routes);
43
+
44
+ app.use(errorHandler);
45
+
46
+ http_server.listen(port, () => {
47
+ logger.info(`Server running on port ${port}`);
48
+ logger.info(`Environment: ${config.nodeEnv}`);
49
+ });
50
+
51
+ socket_io_server.on("connection", (socket) => {
52
+ logger.info(`Client connected: ${socket.id}`);
53
+
54
+ // Socket.io auto-parses incoming JSON into an object
55
+ socket.on("message", (data) => {
56
+ if (data.type === "ping") {
57
+ // Socket.io auto-stringifies outgoing objects
58
+ socket.emit("message", { type: "pong" });
59
+ }
60
+ });
61
+
62
+ socket.on("error", (err) => {
63
+ logger.error(`Socket error: ${err.message}`);
64
+ });
65
+
66
+ socket.on("disconnect", (reason) => {
67
+ logger.info(`Client disconnected: ${socket.id} (Reason: ${reason})`);
68
+ });
69
+ });
70
+
71
+ process.on("SIGINT", () => {
72
+ console.log("SIGINT received, forcefully stopping the server");
73
+ process.exit(0);
74
+ });
75
+ process.on("SIGTERM", () => {
76
+ console.log("SIGTERM received, gracefully stopping the server");
77
+ process.exit(0);
78
+ });
79
+
80
+
81
+ export default http_server;
@@ -0,0 +1,86 @@
1
+ // javascript index file with WebSocketServer
2
+ import express from "express";
3
+ import helmet from "helmet";
4
+ import cors from "cors";
5
+ import router from "./src/routes/index.js";
6
+ import errorHandler from "./src/middleware/errorHandler.js";
7
+ import { logger } from "./src/utils/logger.js";
8
+ import { config } from "./src/config/env.js";
9
+ import { createServer } from "http";
10
+ import { WebSocketServer } from "ws";
11
+
12
+ <%_ if (database === 'postgresql_prisma') { _%>
13
+ // Prisma note: in prisma/schema.prisma use:
14
+ // provider = "prisma-client-js"
15
+ // output = "../generated/prisma"
16
+ <%_ } _%>
17
+
18
+ const app = express();
19
+ const port = config.port;
20
+ const http_server = createServer(app);
21
+ const wss = new WebSocketServer({ server: http_server });
22
+
23
+ app.use(express.json());
24
+ app.use(helmet());
25
+ app.use(cors());
26
+ app.use(express.urlencoded({ extended: true }));
27
+ // Call connectDb() from ./src/config/db.js to connect to your database and also update the env vars with database url
28
+
29
+ // logger middleware
30
+ app.use((req, res, next) => {
31
+ logger.info({
32
+ method: req.method,
33
+ path: req.path,
34
+ ip: req.ip,
35
+ });
36
+ next();
37
+ });
38
+
39
+ app.use("/api", router);
40
+
41
+ app.use(errorHandler);
42
+
43
+ http_server.listen(port, () => {
44
+ logger.info(`Server running on port ${port}`);
45
+ logger.info(`Environment: ${config.nodeEnv}`);
46
+ });
47
+
48
+ process.on("SIGINT", () => {
49
+ console.log("SIGINT received, forcefully stopping the server");
50
+ process.exit(0);
51
+ });
52
+ process.on("SIGTERM", () => {
53
+ console.log("SIGTERM received, gracefully stopping the server");
54
+ process.exit(0);
55
+ });
56
+
57
+ wss.on("connection", (socket) => {
58
+ socket.on("message", (data) => {
59
+ const message = data.toString();
60
+ try {
61
+ const msg = JSON.parse(message);
62
+ if (msg.type === "ping") {
63
+ socket.send(
64
+ JSON.stringify({
65
+ type: "Pong",
66
+ }),
67
+ );
68
+ return;
69
+ }
70
+ } catch (error) {
71
+ socket.send(
72
+ JSON.stringify({
73
+ type: "error",
74
+ message:
75
+ "incorrect format for sending message, Only use Json for messages",
76
+ }),
77
+ );
78
+ socket.close();
79
+ return;
80
+ }
81
+ });
82
+ socket.on("error", console.error);
83
+ socket.on("close", () => console.log(`client disconnected`));
84
+ });
85
+
86
+ export default http_server;
@@ -0,0 +1,88 @@
1
+ // typescript index file with a websocket server
2
+ import express, { type Request, type Response, type NextFunction } from "express";
3
+ import helmet from "helmet";
4
+ import cors from "cors";
5
+ import routes from "./routes/index";
6
+ import errorHandler from "./middleware/errorHandler";
7
+ import { logger } from "./utils/logger";
8
+ import { config } from "./config/env";
9
+ import { createServer } from "http";
10
+ import { WebSocketServer } from "ws";
11
+
12
+ <%_ if (database === 'postgresql_prisma') { _%>
13
+ // Prisma note: in prisma/schema.prisma use:
14
+ // provider = "prisma-client-js"
15
+ // output = "../generated/prisma"
16
+ <%_ } _%>
17
+
18
+ const app = express();
19
+ const port = config.port;
20
+ const http_server = createServer(app);
21
+ const wss = new WebSocketServer({ server: http_server });
22
+ app.use(express.json());
23
+ app.use(helmet());
24
+ app.use(cors());
25
+ app.use(express.urlencoded({ extended: true }));
26
+
27
+ // logger middleware
28
+ app.use((req: Request, res: Response, next: NextFunction) => {
29
+ logger.info({
30
+ ip: req.ip,
31
+ method: req.method,
32
+ path: req.path,
33
+ });
34
+ next();
35
+ });
36
+
37
+ app.use("/api", routes);
38
+
39
+ app.use(errorHandler);
40
+
41
+ http_server.listen(port, () => {
42
+ logger.info(`Server running on port ${port}`);
43
+ logger.info(`Environment: ${config.nodeEnv}`);
44
+ });
45
+
46
+ wss.on("connection", (socket) => {
47
+ console.log("Client connected");
48
+ // Basic Error Handling for the socket
49
+ socket.on("error", console.error);
50
+ socket.on("message", (message: string) => {
51
+ try {
52
+ const msg = JSON.parse(message);
53
+ if (msg.type === "ping") {
54
+ socket.send(
55
+ JSON.stringify({
56
+ type: "pong",
57
+ }),
58
+ );
59
+ return;
60
+ }
61
+ } catch (error) {
62
+ socket.send(
63
+ JSON.stringify({
64
+ type: "error",
65
+ message:
66
+ "Incorrect format for sending message, Only use Json_Format for sending messages",
67
+ }),
68
+ );
69
+ socket.close();
70
+ console.log(error);
71
+ }
72
+ });
73
+ socket.on("close", () => {
74
+ console.log("Client Disconnected");
75
+ });
76
+ });
77
+
78
+ process.on("SIGINT", () => {
79
+ console.log("SIGINT received, forcefully stopping the server");
80
+ process.exit(0);
81
+ });
82
+ process.on("SIGTERM", () => {
83
+ console.log("SIGTERM received, gracefully stopping the server");
84
+ process.exit(0);
85
+ });
86
+
87
+
88
+ export default http_server;
@@ -0,0 +1,19 @@
1
+ FROM node:22-alpine AS builder
2
+ WORKDIR /app
3
+ COPY ./package*.json ./
4
+ RUN npm install
5
+ COPY . .
6
+
7
+ RUN npm prune --omit=dev
8
+
9
+ FROM node:22-alpine AS runner
10
+ WORKDIR /app
11
+ ENV NODE_ENV=production
12
+
13
+ COPY --from=builder /app/package*.json ./
14
+ COPY --from=builder /app/node_modules ./node_modules
15
+ COPY --from=builder /app ./
16
+
17
+ EXPOSE 3000
18
+
19
+ CMD [ "node","./index.js" ]
@@ -0,0 +1,41 @@
1
+ # --- Stage 1: Build Stage ---
2
+ FROM node:22-alpine AS builder
3
+ WORKDIR /app
4
+
5
+ # 1. Install dependencies first (for better caching)
6
+ COPY package*.json ./
7
+ RUN npm install
8
+
9
+ # 2. Copy source and tsconfig
10
+ COPY . .
11
+
12
+ # 3. Build the TypeScript code (assumes 'build' script runs 'tsc')
13
+ RUN npm run build
14
+
15
+ # 4. Prune devDependencies (removes tsc and @types)
16
+ RUN npm prune --production
17
+
18
+
19
+ # --- Stage 2: Production Runner ---
20
+ FROM node:22-alpine AS runner
21
+ WORKDIR /app
22
+
23
+ # Set production environment
24
+ ENV NODE_ENV=production
25
+
26
+ # 1. Copy only the production node_modules from builder
27
+ COPY --from=builder /app/node_modules ./node_modules
28
+
29
+ # 2. Copy the compiled JS files (usually from a 'dist' or 'build' folder)
30
+ COPY --from=builder /app/dist ./dist
31
+
32
+ # 3. Copy package.json (needed for "type": "module" and start scripts)
33
+ COPY --from=builder /app/package*.json ./
34
+
35
+ # 4. Security: Run as non-root user
36
+ USER node
37
+
38
+ EXPOSE 4000
39
+
40
+ # 5. Run the compiled code
41
+ CMD ["node", "dist/index.js"]
package/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "appinit-templates",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "files": [
6
+ "base",
7
+ "features"
8
+ ]
9
+ }