forgedev 1.4.0 → 1.4.1
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/package.json +1 -1
- package/src/chainproof-bridge.js +9 -2
- package/src/ci-mode.js +3 -4
- package/src/composer.js +228 -242
- package/src/doctor-checks-chainproof.js +14 -11
- package/src/doctor-checks.js +3 -19
- package/src/index.js +6 -34
- package/src/recommender.js +319 -319
- package/src/uat-generator.js +105 -93
- package/src/utils.js +245 -214
- package/templates/auth/jwt-custom/backend/app/api/auth.py.template +39 -45
- package/templates/auth/jwt-custom/backend/app/core/security.py.template +44 -37
- package/templates/backend/express/package.json.template +35 -33
- package/templates/backend/express/src/lib/prisma.ts.template +32 -0
- package/templates/backend/express/src/routes/health.ts.template +35 -27
- package/templates/backend/fastapi/backend/app/main.py.template +67 -60
- package/templates/backend/fastapi/backend/requirements.txt.template +18 -16
- package/templates/backend/hono/package.json.template +33 -31
- package/templates/backend/hono/src/lib/prisma.ts.template +32 -0
- package/templates/backend/hono/src/routes/health.ts.template +38 -27
- package/templates/base/.gitignore.template +32 -32
- package/templates/claude-code/commands/workflows.md +52 -52
- package/templates/database/prisma-postgres/prisma/schema.prisma.template +19 -18
- package/templates/database/sqlalchemy-postgres/backend/alembic/env.py.template +39 -40
- package/templates/database/sqlalchemy-postgres/backend/alembic.ini.template +38 -36
- package/templates/database/sqlalchemy-postgres/backend/app/db/session.py.template +50 -48
- package/templates/frontend/nextjs/package.json.template +45 -43
- package/templates/frontend/remix/package.json.template +41 -39
- package/templates/infra/docker/.dockerignore.template +16 -0
- package/templates/infra/docker-compose/docker-compose.yml.template +22 -19
- package/templates/infra/github-actions/.github/workflows/ci.yml.template +61 -52
- package/templates/infra/k8s/k8s/deployment.yml.template +70 -70
- package/templates/infra/k8s/k8s/hpa.yml.template +24 -24
- package/templates/infra/k8s/k8s/ingress.yml.template +26 -26
- package/templates/infra/k8s/k8s/kustomization.yml.template +13 -13
- package/templates/infra/k8s/k8s/namespace.yml.template +4 -4
- package/templates/infra/k8s/k8s/networkpolicy.yml.template +41 -41
- package/templates/infra/k8s/k8s/secrets.yml.template +10 -10
- package/templates/infra/k8s/k8s/service.yml.template +15 -15
- package/templates/testing/load/k6/README.md.template +48 -48
- package/templates/testing/load/k6/load-test.js.template +57 -57
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{PROJECT_NAME}}-server",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "tsx watch src/index.ts",
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"start": "node dist/index.js",
|
|
10
|
-
"lint": "eslint .",
|
|
11
|
-
"typecheck": "tsc --noEmit",
|
|
12
|
-
"test": "vitest run",
|
|
13
|
-
"test:watch": "vitest",
|
|
14
|
-
"db:push": "prisma db push",
|
|
15
|
-
"db:
|
|
16
|
-
"db:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"@types/
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"typecheck": "tsc --noEmit",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest",
|
|
14
|
+
"db:push": "prisma db push",
|
|
15
|
+
"db:migrate": "prisma migrate dev",
|
|
16
|
+
"db:migrate:deploy": "prisma migrate deploy",
|
|
17
|
+
"db:studio": "prisma studio",
|
|
18
|
+
"db:generate": "prisma generate"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"express": "^5.1.0",
|
|
22
|
+
"cors": "^2.8.5",
|
|
23
|
+
"@prisma/client": "^6.6.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "^5.8.3",
|
|
27
|
+
"@types/node": "^22.14.0",
|
|
28
|
+
"@types/express": "^5.0.0",
|
|
29
|
+
"@types/cors": "^2.8.17",
|
|
30
|
+
"tsx": "^4.19.0",
|
|
31
|
+
"eslint": "^9.25.0",
|
|
32
|
+
"prisma": "^6.6.0",
|
|
33
|
+
"vitest": "^3.1.1"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client';
|
|
2
|
+
|
|
3
|
+
const MAX_RETRIES = 3;
|
|
4
|
+
const RETRY_DELAY_MS = 1000;
|
|
5
|
+
|
|
6
|
+
async function connectWithRetry(client: PrismaClient, retries = MAX_RETRIES): Promise<void> {
|
|
7
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
8
|
+
try {
|
|
9
|
+
await client.$connect();
|
|
10
|
+
return;
|
|
11
|
+
} catch (error) {
|
|
12
|
+
if (attempt === retries) throw error;
|
|
13
|
+
const delay = RETRY_DELAY_MS * Math.pow(2, attempt - 1);
|
|
14
|
+
console.warn(`Database connection attempt ${attempt} failed. Retrying in ${delay}ms...`);
|
|
15
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const prisma = new PrismaClient({
|
|
21
|
+
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Graceful shutdown
|
|
25
|
+
const shutdownHandler = async () => {
|
|
26
|
+
await prisma.$disconnect();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
process.on('SIGTERM', shutdownHandler);
|
|
30
|
+
process.on('SIGINT', shutdownHandler);
|
|
31
|
+
|
|
32
|
+
export { connectWithRetry };
|
|
@@ -1,27 +1,35 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
healthRouter.get('/healthz', async (_req, res) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { prisma } from '../lib/prisma';
|
|
3
|
+
|
|
4
|
+
export const healthRouter = Router();
|
|
5
|
+
|
|
6
|
+
// Liveness probe — always returns ok if the process is running
|
|
7
|
+
healthRouter.get('/health', (_req, res) => {
|
|
8
|
+
res.json({
|
|
9
|
+
status: 'ok',
|
|
10
|
+
timestamp: new Date().toISOString(),
|
|
11
|
+
uptime: process.uptime(),
|
|
12
|
+
service: '{{PROJECT_NAME}}',
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Readiness probe — verifies database connectivity
|
|
17
|
+
healthRouter.get('/healthz', async (_req, res) => {
|
|
18
|
+
let dbStatus = 'disconnected';
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await prisma.$queryRaw`SELECT 1`;
|
|
22
|
+
dbStatus = 'connected';
|
|
23
|
+
} catch {
|
|
24
|
+
dbStatus = 'disconnected';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const status = dbStatus === 'connected' ? 'ok' : 'degraded';
|
|
28
|
+
const statusCode = dbStatus === 'connected' ? 200 : 503;
|
|
29
|
+
|
|
30
|
+
res.status(statusCode).json({
|
|
31
|
+
status,
|
|
32
|
+
service: '{{PROJECT_NAME}}',
|
|
33
|
+
checks: { database: dbStatus },
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -1,60 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
app.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
|
|
5
|
+
from fastapi import FastAPI, Request
|
|
6
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
7
|
+
from fastapi.responses import JSONResponse
|
|
8
|
+
from pythonjsonlogger.json import JsonFormatter
|
|
9
|
+
|
|
10
|
+
from app.api.health import router as health_router
|
|
11
|
+
from app.portal.router import router as portal_router
|
|
12
|
+
from app.core.config import settings
|
|
13
|
+
from app.core.errors import AppError
|
|
14
|
+
|
|
15
|
+
# Structured JSON logging
|
|
16
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
17
|
+
handler.setFormatter(JsonFormatter("%(asctime)s %(name)s %(levelname)s %(message)s"))
|
|
18
|
+
logging.basicConfig(level=logging.INFO, handlers=[handler])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@asynccontextmanager
|
|
22
|
+
async def lifespan(app: FastAPI):
|
|
23
|
+
# Startup: connect to database with retry
|
|
24
|
+
from app.db.session import engine, connect_with_retry
|
|
25
|
+
|
|
26
|
+
await connect_with_retry(engine)
|
|
27
|
+
yield
|
|
28
|
+
# Shutdown: close database connections gracefully
|
|
29
|
+
await engine.dispose()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
app = FastAPI(
|
|
33
|
+
title="{{PROJECT_NAME_PASCAL}}",
|
|
34
|
+
version="0.1.0",
|
|
35
|
+
lifespan=lifespan,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# CORS
|
|
39
|
+
app.add_middleware(
|
|
40
|
+
CORSMiddleware,
|
|
41
|
+
allow_origins=settings.cors_origins,
|
|
42
|
+
allow_credentials=True,
|
|
43
|
+
allow_methods=["*"],
|
|
44
|
+
allow_headers=["*"],
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Routes
|
|
48
|
+
app.include_router(health_router, tags=["health"])
|
|
49
|
+
app.include_router(portal_router)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Global exception handler - never leak stack traces
|
|
53
|
+
@app.exception_handler(AppError)
|
|
54
|
+
async def app_error_handler(request: Request, exc: AppError):
|
|
55
|
+
return JSONResponse(
|
|
56
|
+
status_code=exc.status_code,
|
|
57
|
+
content={"error": {"code": exc.code, "message": exc.message}},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@app.exception_handler(Exception)
|
|
62
|
+
async def global_exception_handler(request: Request, exc: Exception):
|
|
63
|
+
logging.exception("Unhandled exception")
|
|
64
|
+
return JSONResponse(
|
|
65
|
+
status_code=500,
|
|
66
|
+
content={"error": {"code": "INTERNAL_ERROR", "message": "An unexpected error occurred"}},
|
|
67
|
+
)
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
fastapi>=0.115.0
|
|
2
|
-
uvicorn[standard]>=0.34.0
|
|
3
|
-
sqlalchemy[asyncio]>=2.0.38
|
|
4
|
-
asyncpg>=0.30.0
|
|
5
|
-
pydantic>=2.11.0
|
|
6
|
-
pydantic-settings>=2.8.0
|
|
7
|
-
alembic>=1.15.0
|
|
8
|
-
python-jose[cryptography]>=3.4.0
|
|
9
|
-
passlib[bcrypt]>=1.7.4
|
|
10
|
-
httpx>=0.28.0
|
|
11
|
-
pytest>=8.3.0
|
|
12
|
-
pytest-asyncio>=0.25.0
|
|
13
|
-
markdown>=3.7
|
|
14
|
-
bleach>=6.2.0
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
fastapi>=0.115.0
|
|
2
|
+
uvicorn[standard]>=0.34.0
|
|
3
|
+
sqlalchemy[asyncio]>=2.0.38
|
|
4
|
+
asyncpg>=0.30.0
|
|
5
|
+
pydantic>=2.11.0
|
|
6
|
+
pydantic-settings>=2.8.0
|
|
7
|
+
alembic>=1.15.0
|
|
8
|
+
python-jose[cryptography]>=3.4.0
|
|
9
|
+
passlib[bcrypt]>=1.7.4
|
|
10
|
+
httpx>=0.28.0
|
|
11
|
+
pytest>=8.3.0
|
|
12
|
+
pytest-asyncio>=0.25.0
|
|
13
|
+
markdown>=3.7
|
|
14
|
+
bleach>=6.2.0
|
|
15
|
+
# v3 uses: from pythonjsonlogger.json import JsonFormatter (v2 used pythonjsonlogger.jsonlogger)
|
|
16
|
+
python-json-logger>=3.2.0
|
|
17
|
+
ruff>=0.11.0
|
|
18
|
+
pyright>=1.1.400
|
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{PROJECT_NAME}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "tsx watch src/index.ts",
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"start": "node dist/index.js",
|
|
10
|
-
"lint": "eslint .",
|
|
11
|
-
"typecheck": "tsc --noEmit",
|
|
12
|
-
"test": "vitest run",
|
|
13
|
-
"test:watch": "vitest",
|
|
14
|
-
"db:push": "prisma db push",
|
|
15
|
-
"db:
|
|
16
|
-
"db:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"typecheck": "tsc --noEmit",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest",
|
|
14
|
+
"db:push": "prisma db push",
|
|
15
|
+
"db:migrate": "prisma migrate dev",
|
|
16
|
+
"db:migrate:deploy": "prisma migrate deploy",
|
|
17
|
+
"db:studio": "prisma studio",
|
|
18
|
+
"db:generate": "prisma generate"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"hono": "^4.7.0",
|
|
22
|
+
"@hono/node-server": "^1.14.0",
|
|
23
|
+
"@prisma/client": "^6.6.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "^5.8.3",
|
|
27
|
+
"@types/node": "^22.14.0",
|
|
28
|
+
"tsx": "^4.19.0",
|
|
29
|
+
"eslint": "^9.25.0",
|
|
30
|
+
"prisma": "^6.6.0",
|
|
31
|
+
"vitest": "^3.1.1"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client';
|
|
2
|
+
|
|
3
|
+
const MAX_RETRIES = 3;
|
|
4
|
+
const RETRY_DELAY_MS = 1000;
|
|
5
|
+
|
|
6
|
+
async function connectWithRetry(client: PrismaClient, retries = MAX_RETRIES): Promise<void> {
|
|
7
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
8
|
+
try {
|
|
9
|
+
await client.$connect();
|
|
10
|
+
return;
|
|
11
|
+
} catch (error) {
|
|
12
|
+
if (attempt === retries) throw error;
|
|
13
|
+
const delay = RETRY_DELAY_MS * Math.pow(2, attempt - 1);
|
|
14
|
+
console.warn(`Database connection attempt ${attempt} failed. Retrying in ${delay}ms...`);
|
|
15
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const prisma = new PrismaClient({
|
|
21
|
+
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Graceful shutdown
|
|
25
|
+
const shutdownHandler = async () => {
|
|
26
|
+
await prisma.$disconnect();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
process.on('SIGTERM', shutdownHandler);
|
|
30
|
+
process.on('SIGINT', shutdownHandler);
|
|
31
|
+
|
|
32
|
+
export { connectWithRetry };
|
|
@@ -1,27 +1,38 @@
|
|
|
1
|
-
import { Hono } from 'hono';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
healthRoutes.get('/healthz', async (c) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { prisma } from '../lib/prisma';
|
|
3
|
+
|
|
4
|
+
export const healthRoutes = new Hono();
|
|
5
|
+
|
|
6
|
+
// Liveness probe — always returns ok if the process is running
|
|
7
|
+
healthRoutes.get('/health', (c) => {
|
|
8
|
+
return c.json({
|
|
9
|
+
status: 'ok',
|
|
10
|
+
timestamp: new Date().toISOString(),
|
|
11
|
+
uptime: process.uptime(),
|
|
12
|
+
service: '{{PROJECT_NAME}}',
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Readiness probe — verifies database connectivity
|
|
17
|
+
healthRoutes.get('/healthz', async (c) => {
|
|
18
|
+
let dbStatus = 'disconnected';
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await prisma.$queryRaw`SELECT 1`;
|
|
22
|
+
dbStatus = 'connected';
|
|
23
|
+
} catch {
|
|
24
|
+
dbStatus = 'disconnected';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const status = dbStatus === 'connected' ? 'ok' : 'degraded';
|
|
28
|
+
const statusCode = dbStatus === 'connected' ? 200 : 503;
|
|
29
|
+
|
|
30
|
+
return c.json(
|
|
31
|
+
{
|
|
32
|
+
status,
|
|
33
|
+
service: '{{PROJECT_NAME}}',
|
|
34
|
+
checks: { database: dbStatus },
|
|
35
|
+
},
|
|
36
|
+
statusCode,
|
|
37
|
+
);
|
|
38
|
+
});
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
# Dependencies
|
|
2
|
-
node_modules/
|
|
3
|
-
|
|
4
|
-
# Environment
|
|
5
|
-
.env
|
|
6
|
-
.env.local
|
|
7
|
-
.env.*.local
|
|
8
|
-
|
|
9
|
-
# Build output
|
|
10
|
-
dist/
|
|
11
|
-
.next/
|
|
12
|
-
out/
|
|
13
|
-
build/
|
|
14
|
-
|
|
15
|
-
# IDE
|
|
16
|
-
.vscode/
|
|
17
|
-
.idea/
|
|
18
|
-
*.swp
|
|
19
|
-
*.swo
|
|
20
|
-
|
|
21
|
-
# OS
|
|
22
|
-
.DS_Store
|
|
23
|
-
Thumbs.db
|
|
24
|
-
|
|
25
|
-
# Testing
|
|
26
|
-
coverage/
|
|
27
|
-
test-results/
|
|
28
|
-
playwright-report/
|
|
29
|
-
|
|
30
|
-
# Kubernetes secrets (never commit real credentials)
|
|
31
|
-
k8s/secrets.yml
|
|
32
|
-
{{EXTRA_IGNORES}}
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Environment
|
|
5
|
+
.env
|
|
6
|
+
.env.local
|
|
7
|
+
.env.*.local
|
|
8
|
+
|
|
9
|
+
# Build output
|
|
10
|
+
dist/
|
|
11
|
+
.next/
|
|
12
|
+
out/
|
|
13
|
+
build/
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.vscode/
|
|
17
|
+
.idea/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# Testing
|
|
26
|
+
coverage/
|
|
27
|
+
test-results/
|
|
28
|
+
playwright-report/
|
|
29
|
+
|
|
30
|
+
# Kubernetes secrets (never commit real credentials)
|
|
31
|
+
k8s/secrets.yml
|
|
32
|
+
{{EXTRA_IGNORES}}
|