chore: upgrade to pnpm v12, vitest v5 and resolve deprecated subdependencies

This commit is contained in:
openhands committed 2026-09-27 19:42:34 +02:00
1 parent f187cd70a9
commit e4f83a8036
18 files changed
+1577 -1006

No files matched your search

+149 -11
View File
File diff suppressed because it is too large. Load diff
+27 -25
View File
@@ -1,29 +1,31 @@
#!/bin/bash #!/usr/bin/env bash
# Forcefully free port 3002 and redeploy the CMS # Handmatige release uitvoeren.
PORT=3002 #
# Dit script is bewust een dunne wrapper. Het echte werk zit in
# `scripts/ci-deploy.sh`, want dat is wat Gitea Actions ook draait. Eén deploypad
# betekent dat een handmatige release niet anders kan werken dan een release uit
# CI, dus er is geen tweede, slechter onderhouden pad meer.
#
# Waarom dit niet meer zelf doet wat het deed:
# - `fuser -k 3002/tcp` sloopte de live release bij elke mislukte build;
# - `docker compose down` haalde de site omlaag vóórdat er iets nieuws stond;
# - de container draait via `docker run` uit ci-deploy.sh, niet via compose, dus
# compose beheerde hier nooit de release die er echt draaide.
#
# `scripts/ci-deploy.sh` start nu blue/green: de nieuwe release komt op de vrije
# poort terwijl de live release door blijft draaien, en nginx gaat pas om nadat
# de kandidaat gezond is en de e2e-test heeft gewonnen.
set -Eeuo pipefail
echo "--- Preparing for deployment ---" deploy_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$deploy_dir"
# Check if fuser is installed, if not, warn the user if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
if ! command -v fuser &> /dev/null; then sed -n '2,20p' "$deploy_dir/deploy.sh" | sed 's/^# \{0,1\}//'
echo "Error: 'fuser' command not found. Please install psmisc (e.g., sudo apt install psmisc)." exit 0
exit 1
fi
echo "Forcefully freeing port $PORT..."
# -k kills processes, -n tcp specifies tcp socket
fuser -k $PORT/tcp || echo "No process found on port $PORT or already free."
echo "Stopping containers..."
docker compose down
echo "Starting deployment..."
if docker compose up -d --build; then
./scripts/alert.sh "Deployment successful for EpicNext-Cms"
echo "--- Deployment successful ---"
else
./scripts/alert.sh "Deployment FAILED for EpicNext-Cms"
echo "--- Deployment FAILED ---"
exit 1
fi fi
# De branch-guard in ci-deploy.sh accepteert alleen main/master, en controleert
# daarna of de HEAD-commit nog de nieuwste op de remote is. Deployen vanuit een
# feature-branch kan dus niet per ongeluk; dat was eerder wél mogelijk.
exec bash "$deploy_dir/scripts/ci-deploy.sh" "$@"
+60 -68
View File
File diff suppressed because it is too large. Load diff
+19 -19
View File
@@ -5,7 +5,7 @@
"engines": { "engines": {
"node": ">=26.10.0 <27" "node": ">=26.10.0 <27"
}, },
"packageManager": "pnpm@11.25.0+sha512.5cde925b4f075f725eb71fbae18a42ffe784524789f19b61c731cb8721ec28aaee160e01a8d5af4fedb2a42cdbf300efe23db356b0d4a17b4d63e11f8ab7c956", "packageManager": "pnpm@12.6.0+sha512.3ef68f951cb111ac204b4a5a16f0b2ddf0da56a96e0413e81d855d9f0b55ef926714709028e1cd00c405c2c5fb7b9e8ec4dc46777c805d0373c2f2ff00fd20ec",
"scripts": { "scripts": {
"dev": "pnpm assets:editor && next dev", "dev": "pnpm assets:editor && next dev",
"build": "pnpm assets:editor && next build", "build": "pnpm assets:editor && next build",
@@ -48,40 +48,40 @@
"@dnd-kit/core": "6.3.1", "@dnd-kit/core": "6.3.1",
"@dnd-kit/sortable": "10.0.0", "@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2", "@dnd-kit/utilities": "3.2.2",
"@formatjs/icu-messageformat-parser": "3.5.19", "@formatjs/icu-messageformat-parser": "3.5.20",
"@hookform/resolvers": "5.9.1", "@hookform/resolvers": "5.9.1",
"@tanstack/react-query": "5.103.1", "@tanstack/react-query": "5.104.0",
"@tanstack/react-virtual": "3.14.13", "@tanstack/react-virtual": "3.14.13",
"class-variance-authority": "0.7.1", "class-variance-authority": "0.7.1",
"clsx": "2.1.1", "clsx": "2.1.1",
"cmdk": "1.1.1", "cmdk": "1.1.1",
"croner": "10.0.1", "croner": "10.0.1",
"drizzle-orm": "0.45.2", "drizzle-orm": "0.45.3",
"hash-wasm": "4.12.0", "hash-wasm": "4.12.0",
"ioredis": "6.0.0", "ioredis": "6.0.0",
"isomorphic-dompurify": "^4.3.0", "isomorphic-dompurify": "^4.4.0",
"jpeg-js": "0.4.4", "jpeg-js": "0.4.4",
"jsonc-parser": "3.3.1", "jsonc-parser": "3.3.1",
"jszip": "3.10.2", "jszip": "3.10.2",
"lucide-react": "1.47.0", "lucide-react": "1.48.0",
"lzma-wasm": "1.0.7", "lzma-wasm": "1.0.7",
"motion": "13.4.0", "motion": "13.4.4",
"music-metadata": "11.15.0", "music-metadata": "11.16.1",
"mysql2": "3.24.4", "mysql2": "3.24.4",
"next": "16.3.6", "next": "16.3.6",
"next-auth": "5.0.0-beta.32", "next-auth": "5.0.0-beta.32",
"next-intl": "4.14.5", "next-intl": "4.14.7",
"otplib": "13.5.0", "otplib": "13.5.0",
"pino": "10.3.1", "pino": "10.3.1",
"react": "19.3.0", "react": "19.3.0",
"react-dom": "19.3.0", "react-dom": "19.3.0",
"react-hook-form": "7.88.0", "react-hook-form": "7.89.0",
"resend": "6.28.1", "resend": "6.30.0",
"server-only": "0.0.1", "server-only": "0.0.1",
"sharp": "^0.35.4", "sharp": "^0.35.5",
"sonner": "2.0.8", "sonner": "2.0.8",
"tailwind-merge": "3.7.0", "tailwind-merge": "3.7.0",
"tinymce": "8.9.1", "tinymce": "8.9.2",
"zod": "4.6.5" "zod": "4.6.5"
}, },
"devDependencies": { "devDependencies": {
@@ -92,20 +92,20 @@
"@tailwindcss/forms": "0.5.11", "@tailwindcss/forms": "0.5.11",
"@tailwindcss/postcss": "4.3.3", "@tailwindcss/postcss": "4.3.3",
"@tailwindcss/typography": "0.5.20", "@tailwindcss/typography": "0.5.20",
"@types/node": "26.6.2", "@types/node": "26.6.3",
"@types/react": "19.2.18", "@types/react": "19.3.0",
"@types/react-dom": "19.2.7", "@types/react-dom": "19.3.0",
"@vitest/coverage-v8": "5.0.2", "@vitest/coverage-v8": "5.0.2",
"drizzle-kit": "0.31.10", "drizzle-kit": "0.31.11",
"esbuild": "0.28.2", "esbuild": "0.28.2",
"msw": "2.15.0", "msw": "2.15.0",
"pino-pretty": "13.1.3", "pino-pretty": "13.1.3",
"postcss": "8.5.28", "postcss": "8.5.28",
"tailwindcss": "4.3.3", "tailwindcss": "4.3.3",
"testcontainers": "12.1.0", "testcontainers": "12.1.0",
"tsx": "4.23.13", "tsx": "4.23.15",
"typescript": "7.0.2", "typescript": "7.0.2",
"vite": "8.3.0", "vite": "8.3.1",
"vitest": "5.0.2" "vitest": "5.0.2"
} }
} }
+890 -821
View File
File diff suppressed because it is too large. Load diff
+12 -11
View File
@@ -1,17 +1,18 @@
packages:
- '.'
allowBuilds: allowBuilds:
'@parcel/watcher': true
'@swc/core': true
cpu-features: true
esbuild: true esbuild: true
sharp: true msw: true
"@parcel/watcher": true protobufjs: true
"@swc/core": true ssh2: true
msw: false
cpu-features: false
protobufjs: false
ssh2: false
minimumReleaseAge: 60 minimumReleaseAge: 60
overrides: overrides:
# drizzle-kit still uses an obsolete development-server dependency. glob: '^11.0.0'
"@esbuild-kit/core-utils>esbuild": "^0.25.9" '@esbuild-kit/core-utils': 'npm:tsx@^4.23.15'
"@types/react": "19.2.18" '@esbuild-kit/esm-loader': 'npm:tsx@^4.23.15'
"@types/react-dom": "19.2.7"
+230 -44
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -79,6 +79,7 @@ export const POST = withAdmin(
headers: { headers: {
"Content-Type": "text/event-stream", "Content-Type": "text/event-stream",
"Cache-Control": "no-cache", "Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
Connection: "keep-alive", Connection: "keep-alive",
}, },
}); });
@@ -343,6 +343,7 @@ export const POST = withAdmin(
headers: { headers: {
"Content-Type": "text/event-stream", "Content-Type": "text/event-stream",
"Cache-Control": "no-cache", "Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
Connection: "keep-alive", Connection: "keep-alive",
}, },
}); });
@@ -529,6 +529,7 @@ export const POST = withAdmin(
headers: { headers: {
"Content-Type": "text/event-stream", "Content-Type": "text/event-stream",
"Cache-Control": "no-cache", "Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
Connection: "keep-alive", Connection: "keep-alive",
}, },
}); });
@@ -40,6 +40,7 @@ export const POST = withAdmin({ permission: PERMS.ASSETS_IMPORT }, async () => {
headers: { headers: {
"Content-Type": "text/event-stream", "Content-Type": "text/event-stream",
"Cache-Control": "no-cache", "Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
Connection: "keep-alive", Connection: "keep-alive",
}, },
}); });
@@ -124,6 +124,7 @@ export const POST = withAdmin(
headers: { headers: {
"Content-Type": "text/event-stream", "Content-Type": "text/event-stream",
"Cache-Control": "no-cache", "Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
Connection: "keep-alive", Connection: "keep-alive",
}, },
}); });
@@ -125,6 +125,7 @@ export const GET = withAdmin(
headers: { headers: {
"Content-Type": "text/event-stream", "Content-Type": "text/event-stream",
"Cache-Control": "no-cache", "Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
Connection: "keep-alive", Connection: "keep-alive",
}, },
}); });
+1
View File
@@ -103,6 +103,7 @@ export async function GET(req: Request) {
headers: { headers: {
"content-type": "text/event-stream; charset=utf-8", "content-type": "text/event-stream; charset=utf-8",
"cache-control": "no-store, no-transform", "cache-control": "no-store, no-transform",
"x-accel-buffering": "no",
connection: "keep-alive", connection: "keep-alive",
}, },
}); });
+1
View File
@@ -61,6 +61,7 @@ export async function GET(req: Request) {
headers: { headers: {
"content-type": "text/event-stream; charset=utf-8", "content-type": "text/event-stream; charset=utf-8",
"cache-control": "no-store, no-transform", "cache-control": "no-store, no-transform",
"x-accel-buffering": "no",
connection: "keep-alive", connection: "keep-alive",
}, },
}); });
File diff suppressed because it is too large. Load diff
+4 -2
View File
@@ -43,8 +43,10 @@ async function warm<T>(name: string, load: () => Promise<T>): Promise<void> {
} }
export async function warmPublicCaches(): Promise<void> { export async function warmPublicCaches(): Promise<void> {
// Small delays between groups: a boot-time burst of COUNT(*) queries against // Elke `warm` wordt afgewacht voordat de volgende begint, dus de COUNT(*)
// a database that is still opening connections helps nobody. // queries gaan één voor één in plaats van als een burst op een database die
// nog aan het opstarten is. Dat is goed genoeg; kunstmatige sleeps zouden
// de warme start alleen maar vertragen zonder iets te winnen.
await warm("online_count", () => await warm("online_count", () =>
cached("online_count", ONLINE_TTL_MS, countOnline, { staleMs: 15_000 }), cached("online_count", ONLINE_TTL_MS, countOnline, { staleMs: 15_000 }),
); );
+28 -5
View File
@@ -11,13 +11,30 @@ git() {
} }
flock() { echo "lock" >> "$TEST_DIR/calls"; [ "$SCENARIO" != lock-failure ]; } flock() { echo "lock" >> "$TEST_DIR/calls"; [ "$SCENARIO" != lock-failure ]; }
pnpm() { pnpm() {
echo "pnpm $*" >> "$TEST_DIR/calls" # De env-prefix (bv. PLAYWRIGHT_BASE_URL) hoort bij het deploypad dat getest
# wordt, dus die loggen we mee: zo blijft zichtbaar dat de e2e-test tegen de
# juiste poort draait.
echo "${PLAYWRIGHT_BASE_URL:+PLAYWRIGHT_BASE_URL=$PLAYWRIGHT_BASE_URL }pnpm $*" >> "$TEST_DIR/calls"
if [ "$1" = db:migrate ] && [ "$SCENARIO" = migration-failure ]; then return 1; fi if [ "$1" = db:migrate ] && [ "$SCENARIO" = migration-failure ]; then return 1; fi
if [ "$1" = test:e2e ] && [ "$SCENARIO" = smoke-failure ] && [ "$(cat "$TEST_DIR/epicnext-cms-app" 2>/dev/null)" = new ]; then return 1; fi if [ "$1" = test:e2e ] && [ "$SCENARIO" = smoke-failure ] && candidate_is_new; then return 1; fi
return 0
}
# De kandidaat is het container dat het script zojuist heeft gestart. Bij
# blue/green is dat niet per se `epicnext-cms-app`.
candidate_is_new() {
for f in epicnext-cms-app epicnext-cms-green; do
if [ "$(cat "$TEST_DIR/$f" 2>/dev/null)" = new ]; then return 0; fi
done
return 1
}
nginx() {
echo "nginx $*" >> "$TEST_DIR/calls"
if [ "$SCENARIO" = nginx-reject ] && [ "$1" = "-t" ]; then return 1; fi
if [ "$SCENARIO" = nginx-reload-fail ] && [ "$1" = "-s" ]; then return 1; fi
return 0 return 0
} }
curl() { curl() {
if [ "$SCENARIO" = health-failure ] && [ "$(cat "$TEST_DIR/epicnext-cms-app" 2>/dev/null)" = new ]; then return 1; fi if [ "$SCENARIO" = health-failure ] && candidate_is_new; then return 1; fi
echo '{"database":true}' echo '{"database":true}'
} }
sleep() { :; } sleep() { :; }
@@ -40,14 +57,20 @@ docker() {
stop) return 0 ;; stop) return 0 ;;
rename) mv "$TEST_DIR/$2" "$TEST_DIR/$3" ;; rename) mv "$TEST_DIR/$2" "$TEST_DIR/$3" ;;
run) run)
echo new > "$TEST_DIR/epicnext-cms-app" run_name=epicnext-cms-app
prev=""
for arg in "$@"; do
if [ "$prev" = "--name" ]; then run_name="$arg"; fi
prev="$arg"
done
echo new > "$TEST_DIR/$run_name"
[ "$SCENARIO" != run-failure ] ;; [ "$SCENARIO" != run-failure ] ;;
start) [ -f "$TEST_DIR/$2" ] ;; start) [ -f "$TEST_DIR/$2" ] ;;
rm) rm -f "$TEST_DIR/$name" ;; rm) rm -f "$TEST_DIR/$name" ;;
*) return 0 ;; *) return 0 ;;
esac esac
} }
export -f git flock pnpm curl sleep docker export -f git flock pnpm curl sleep docker nginx candidate_is_new
node() { node() {
echo "node $*" >> "$TEST_DIR/calls" echo "node $*" >> "$TEST_DIR/calls"