tribunal-kit 6.0.1 → 7.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.
- package/.agent/ARCHITECTURE.md +9 -6
- package/.agent/agents/accessibility-reviewer.md +23 -16
- package/.agent/agents/ai-code-reviewer.md +25 -21
- package/.agent/agents/anti-pattern-reviewer.md +12 -7
- package/.agent/agents/api-architect.md +1 -0
- package/.agent/agents/backend-specialist.md +23 -22
- package/.agent/agents/cloud-engineer.md +1 -0
- package/.agent/agents/code-archaeologist.md +1 -0
- package/.agent/agents/complexity-reviewer.md +10 -7
- package/.agent/agents/database-architect.md +3 -2
- package/.agent/agents/db-latency-auditor.md +6 -2
- package/.agent/agents/debugger.md +3 -2
- package/.agent/agents/dependency-reviewer.md +1 -0
- package/.agent/agents/devops-engineer.md +9 -8
- package/.agent/agents/documentation-writer.md +6 -5
- package/.agent/agents/explorer-agent.md +1 -0
- package/.agent/agents/frontend-reviewer.md +21 -17
- package/.agent/agents/frontend-specialist.md +23 -20
- package/.agent/agents/game-developer.md +1 -0
- package/.agent/agents/interaction-reviewer.md +10 -5
- package/.agent/agents/logic-reviewer.md +3 -0
- package/.agent/agents/minimalist-reviewer.md +6 -0
- package/.agent/agents/mobile-developer.md +7 -6
- package/.agent/agents/mobile-reviewer.md +14 -13
- package/.agent/agents/orchestrator.md +3 -0
- package/.agent/agents/penetration-tester.md +1 -0
- package/.agent/agents/performance-optimizer.md +6 -5
- package/.agent/agents/performance-reviewer.md +6 -6
- package/.agent/agents/pipeline-reviewer.md +144 -0
- package/.agent/agents/precedence-reviewer.md +1 -0
- package/.agent/agents/product-manager.md +1 -0
- package/.agent/agents/product-owner.md +1 -0
- package/.agent/agents/product-reviewer.md +12 -7
- package/.agent/agents/project-planner.md +3 -0
- package/.agent/agents/qa-automation-engineer.md +50 -43
- package/.agent/agents/resilience-reviewer.md +1 -0
- package/.agent/agents/schema-reviewer.md +1 -0
- package/.agent/agents/security-auditor.md +24 -24
- package/.agent/agents/seo-specialist.md +33 -23
- package/.agent/agents/sql-reviewer.md +5 -4
- package/.agent/agents/supervisor-agent.md +12 -2
- package/.agent/agents/swarm-worker-contracts.md +13 -13
- package/.agent/agents/swarm-worker-registry.md +51 -51
- package/.agent/agents/system-architect.md +1 -0
- package/.agent/agents/test-coverage-reviewer.md +25 -22
- package/.agent/agents/test-engineer.md +12 -11
- package/.agent/agents/throughput-optimizer.md +45 -40
- package/.agent/agents/type-safety-reviewer.md +5 -4
- package/.agent/agents/ui-ux-auditor.md +6 -4
- package/.agent/agents/ui-visual-auditor.md +3 -0
- package/.agent/agents/ux-reviewer.md +11 -6
- package/.agent/agents/visual-reviewer.md +11 -6
- package/.agent/agents/vitals-reviewer.md +5 -4
- package/.agent/history/memory/.memory.idx +1532 -1
- package/.agent/history/memory/MEMORY.md +102 -1
- package/.agent/mcp_config.json +1 -6
- package/.agent/routing_index.json +2033 -263
- package/.agent/rules/GEMINI.md +11 -9
- package/.agent/rules/GEMINI_PLANNER.md +19 -21
- package/.agent/scripts/_colors.js +52 -54
- package/.agent/scripts/_utils.js +52 -53
- package/.agent/scripts/auto_preview.js +39 -43
- package/.agent/scripts/bundle_analyzer.js +47 -60
- package/.agent/scripts/case_law_manager.js +200 -294
- package/.agent/scripts/checklist.js +73 -94
- package/.agent/scripts/cicd_validator.js +305 -0
- package/.agent/scripts/compile_router.py +4 -111
- package/.agent/scripts/context_broker.js +317 -412
- package/.agent/scripts/contract_engine.js +402 -0
- package/.agent/scripts/dependency_analyzer.js +112 -124
- package/.agent/scripts/graph_builder.js +75 -103
- package/.agent/scripts/graph_visualizer.js +10 -12
- package/.agent/scripts/graph_zoom.js +38 -55
- package/.agent/scripts/guardrail_engine.js +195 -145
- package/.agent/scripts/impact_classifier.js +55 -30
- package/.agent/scripts/inner_loop_validator.js +108 -159
- package/.agent/scripts/integrity_manifest.js +59 -67
- package/.agent/scripts/lint_runner.js +65 -75
- package/.agent/scripts/marathon_harness.js +131 -208
- package/.agent/scripts/migrate_skills_frontmatter.py +4 -63
- package/.agent/scripts/minify_context.js +29 -33
- package/.agent/scripts/minimal_change_engine.js +95 -87
- package/.agent/scripts/mutation_runner.js +46 -62
- package/.agent/scripts/pipeline_engine.js +320 -209
- package/.agent/scripts/prompt_compiler.js +118 -93
- package/.agent/scripts/schema_validator.js +73 -121
- package/.agent/scripts/security_scan.js +88 -117
- package/.agent/scripts/session_manager.js +52 -69
- package/.agent/scripts/signal_detector.js +24 -22
- package/.agent/scripts/skill_evolution.js +184 -245
- package/.agent/scripts/skill_integrator.js +35 -48
- package/.agent/scripts/socratic_gate_policy.js +15 -12
- package/.agent/scripts/strengthen_skills.js +29 -33
- package/.agent/scripts/swarm_dispatcher.js +120 -151
- package/.agent/scripts/test_runner.js +54 -56
- package/.agent/scripts/token_budget_broker.js +9 -7
- package/.agent/scripts/trace_engine.js +126 -0
- package/.agent/scripts/verify_all.js +76 -93
- package/.agent/scripts/visual_audit.js +16 -21
- package/.agent/skill_topic_map.json +22 -3
- package/.agent/skills/12-principles-of-animation/SKILL.md +7 -0
- package/.agent/skills/60fps-animation/SKILL.md +7 -0
- package/.agent/skills/accessible-animation/SKILL.md +5 -1
- package/.agent/skills/adapt/SKILL.md +7 -1
- package/.agent/skills/advanced-rag-pipelines/SKILL.md +1 -0
- package/.agent/skills/agent-organizer/SKILL.md +1 -0
- package/.agent/skills/agentic-patterns/SKILL.md +33 -25
- package/.agent/skills/agentic-workflows-2026/SKILL.md +6 -2
- package/.agent/skills/ai-app-hardening/SKILL.md +9 -8
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +7 -6
- package/.agent/skills/animation-on-scroll/SKILL.md +24 -15
- package/.agent/skills/animation-systems/SKILL.md +11 -10
- package/.agent/skills/antfu-conventions/SKILL.md +7 -2
- package/.agent/skills/api-patterns/SKILL.md +14 -10
- package/.agent/skills/api-security-auditor/SKILL.md +15 -14
- package/.agent/skills/app-builder/SKILL.md +1 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +2 -2
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +12 -12
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +3 -3
- package/.agent/skills/apple-design/SKILL.md +8 -7
- package/.agent/skills/architecture/SKILL.md +3 -2
- package/.agent/skills/audit-and-fix/SKILL.md +8 -5
- package/.agent/skills/authentication-best-practices/SKILL.md +9 -8
- package/.agent/skills/backend-security-expert/SKILL.md +1 -0
- package/.agent/skills/baseline-ui/SKILL.md +8 -7
- package/.agent/skills/bash-linux/SKILL.md +1 -0
- package/.agent/skills/behavioral-modes/SKILL.md +1 -0
- package/.agent/skills/better-colors/SKILL.md +11 -7
- package/.agent/skills/better-typography/SKILL.md +30 -16
- package/.agent/skills/better-ui/SKILL.md +17 -10
- package/.agent/skills/bolder/SKILL.md +6 -0
- package/.agent/skills/brainstorming/SKILL.md +6 -4
- package/.agent/skills/browser-native-ai/SKILL.md +5 -4
- package/.agent/skills/build-primitive/SKILL.md +5 -0
- package/.agent/skills/building-native-ui/SKILL.md +1 -0
- package/.agent/skills/cicd-pro/SKILL.md +2 -1
- package/.agent/skills/clarify/SKILL.md +19 -14
- package/.agent/skills/clean-code/SKILL.md +16 -15
- package/.agent/skills/cloud-architect/SKILL.md +1 -0
- package/.agent/skills/cobejs/SKILL.md +7 -9
- package/.agent/skills/code-review-checklist/SKILL.md +1 -0
- package/.agent/skills/codebase-design/SKILL.md +7 -2
- package/.agent/skills/colorize/SKILL.md +8 -3
- package/.agent/skills/compact-landing/SKILL.md +8 -3
- package/.agent/skills/company-logos/SKILL.md +6 -0
- package/.agent/skills/config-validator/SKILL.md +10 -9
- package/.agent/skills/containerization-pro/SKILL.md +6 -5
- package/.agent/skills/context-engineering-pro/SKILL.md +5 -4
- package/.agent/skills/create-design-md/SKILL.md +13 -5
- package/.agent/skills/critique/SKILL.md +10 -8
- package/.agent/skills/csharp-developer/SKILL.md +1 -0
- package/.agent/skills/data-validation-schemas/SKILL.md +28 -23
- package/.agent/skills/database-design/SKILL.md +2 -1
- package/.agent/skills/delight/SKILL.md +6 -0
- package/.agent/skills/deployment-procedures/SKILL.md +2 -1
- package/.agent/skills/design-lab/SKILL.md +5 -0
- package/.agent/skills/devops-engineer/SKILL.md +3 -2
- package/.agent/skills/devops-incident-responder/SKILL.md +2 -1
- package/.agent/skills/diagnosing-bugs/SKILL.md +12 -2
- package/.agent/skills/distill/SKILL.md +7 -2
- package/.agent/skills/documentation-templates/SKILL.md +3 -2
- package/.agent/skills/domain-modeling/SKILL.md +12 -4
- package/.agent/skills/duckdb-analytical-sql/SKILL.md +7 -3
- package/.agent/skills/edge-ai-mobile/SKILL.md +5 -1
- package/.agent/skills/edge-computing/SKILL.md +9 -8
- package/.agent/skills/emil-design-eng/SKILL.md +43 -27
- package/.agent/skills/error-resilience/SKILL.md +34 -16
- package/.agent/skills/expo-router-v4/SKILL.md +2 -1
- package/.agent/skills/extract-design-system/SKILL.md +12 -11
- package/.agent/skills/fabel-protocol/SKILL.md +24 -17
- package/.agent/skills/fixing-accessibility/SKILL.md +23 -16
- package/.agent/skills/fixing-metadata/SKILL.md +24 -13
- package/.agent/skills/fixing-motion-performance/SKILL.md +7 -5
- package/.agent/skills/framer-motion-expert/SKILL.md +29 -15
- package/.agent/skills/frontend-design/SKILL.md +21 -13
- package/.agent/skills/frontend-security-expert/SKILL.md +1 -0
- package/.agent/skills/game-design-expert/SKILL.md +1 -0
- package/.agent/skills/game-engineering-expert/SKILL.md +1 -0
- package/.agent/skills/generative-ui-expert/SKILL.md +3 -2
- package/.agent/skills/geo-fundamentals/SKILL.md +2 -1
- package/.agent/skills/git-pro/SKILL.md +10 -9
- package/.agent/skills/github-operations/SKILL.md +2 -1
- package/.agent/skills/gpt-taste/SKILL.md +7 -0
- package/.agent/skills/gsap-core/SKILL.md +26 -22
- package/.agent/skills/gsap-frameworks/SKILL.md +6 -5
- package/.agent/skills/gsap-performance/SKILL.md +4 -3
- package/.agent/skills/gsap-plugins/SKILL.md +34 -33
- package/.agent/skills/gsap-react/SKILL.md +9 -8
- package/.agent/skills/gsap-scrolltrigger/SKILL.md +34 -33
- package/.agent/skills/gsap-timeline/SKILL.md +18 -15
- package/.agent/skills/gsap-utils/SKILL.md +25 -24
- package/.agent/skills/harden/SKILL.md +9 -3
- package/.agent/skills/harness-protocol/SKILL.md +1 -0
- package/.agent/skills/i18n-localization/SKILL.md +10 -9
- package/.agent/skills/impeccable/SKILL.md +5 -0
- package/.agent/skills/improve-codebase-architecture/SKILL.md +10 -5
- package/.agent/skills/improve-ui/SKILL.md +12 -5
- package/.agent/skills/intelligent-routing/SKILL.md +7 -6
- package/.agent/skills/knowledge-graph/SKILL.md +1 -0
- package/.agent/skills/landing-page/SKILL.md +1 -0
- package/.agent/skills/lint-and-validate/SKILL.md +9 -4
- package/.agent/skills/llm-engineering/SKILL.md +55 -54
- package/.agent/skills/local-first/SKILL.md +20 -19
- package/.agent/skills/local-first-architecture/SKILL.md +6 -1
- package/.agent/skills/lottie-animation/SKILL.md +7 -3
- package/.agent/skills/marquee-loop/SKILL.md +1 -0
- package/.agent/skills/masked-reveal/SKILL.md +17 -4
- package/.agent/skills/mcp-builder/SKILL.md +13 -10
- package/.agent/skills/micro-interaction/SKILL.md +20 -5
- package/.agent/skills/mobile-design/SKILL.md +26 -15
- package/.agent/skills/monorepo-management/SKILL.md +3 -2
- package/.agent/skills/morphing-icons/SKILL.md +16 -6
- package/.agent/skills/motion-engineering/SKILL.md +19 -15
- package/.agent/skills/nextjs-react-expert/SKILL.md +26 -21
- package/.agent/skills/nodejs-best-practices/SKILL.md +89 -76
- package/.agent/skills/observability/SKILL.md +45 -44
- package/.agent/skills/opentelemetry-observability/SKILL.md +2 -1
- package/.agent/skills/page-transition-animation/SKILL.md +6 -3
- package/.agent/skills/parallel-agents/SKILL.md +7 -2
- package/.agent/skills/performance-profiling/SKILL.md +24 -13
- package/.agent/skills/plan-writing/SKILL.md +1 -0
- package/.agent/skills/platform-engineer/SKILL.md +3 -2
- package/.agent/skills/platform-engineering-opentofu/SKILL.md +1 -0
- package/.agent/skills/playwright-ai-e2e/SKILL.md +3 -2
- package/.agent/skills/playwright-best-practices/SKILL.md +29 -28
- package/.agent/skills/polish/SKILL.md +13 -12
- package/.agent/skills/powershell-windows/SKILL.md +1 -0
- package/.agent/skills/pricing-page/SKILL.md +8 -3
- package/.agent/skills/product-aware-heuristics/SKILL.md +122 -72
- package/.agent/skills/progressive-blur/SKILL.md +9 -4
- package/.agent/skills/project-idioms/SKILL.md +1 -0
- package/.agent/skills/property-based-testing/SKILL.md +10 -12
- package/.agent/skills/python-patterns/SKILL.md +1 -0
- package/.agent/skills/python-pro/SKILL.md +1 -0
- package/.agent/skills/quieter/SKILL.md +6 -0
- package/.agent/skills/react-doctor/SKILL.md +6 -0
- package/.agent/skills/react-specialist/SKILL.md +32 -28
- package/.agent/skills/readme-builder/SKILL.md +1 -0
- package/.agent/skills/realtime-patterns/SKILL.md +34 -33
- package/.agent/skills/red-team-tactics/SKILL.md +1 -0
- package/.agent/skills/redesign-skill/SKILL.md +6 -0
- package/.agent/skills/review-animations/SKILL.md +5 -2
- package/.agent/skills/review-animations/STANDARDS.md +37 -23
- package/.agent/skills/rust-pro/SKILL.md +1 -0
- package/.agent/skills/seo-fundamentals/SKILL.md +4 -3
- package/.agent/skills/server-management/SKILL.md +1 -0
- package/.agent/skills/shadcn-ui-expert/SKILL.md +28 -22
- package/.agent/skills/shape/SKILL.md +9 -2
- package/.agent/skills/skill-creator/SKILL.md +1 -0
- package/.agent/skills/soft-skill/SKILL.md +6 -0
- package/.agent/skills/sounds-on-the-web/SKILL.md +1 -0
- package/.agent/skills/sql-pro/SKILL.md +1 -0
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +1 -0
- package/.agent/skills/svg-animation/SKILL.md +16 -7
- package/.agent/skills/swiftui-expert/SKILL.md +1 -0
- package/.agent/skills/swiss-design/SKILL.md +5 -0
- package/.agent/skills/system-design-pro/SKILL.md +1 -0
- package/.agent/skills/systematic-debugging/SKILL.md +1 -0
- package/.agent/skills/tailwind-patterns/SKILL.md +25 -11
- package/.agent/skills/taste-skill/SKILL.md +6 -0
- package/.agent/skills/tdd-workflow/SKILL.md +5 -0
- package/.agent/skills/test-result-analyzer/SKILL.md +1 -0
- package/.agent/skills/testing-patterns/SKILL.md +123 -115
- package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +5 -0
- package/.agent/skills/thinking-protocol/SKILL.md +3 -0
- package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +9 -8
- package/.agent/skills/transitions-dev/SKILL.md +19 -6
- package/.agent/skills/trend-researcher/SKILL.md +1 -0
- package/.agent/skills/typescript-advanced/SKILL.md +23 -18
- package/.agent/skills/typeset/SKILL.md +10 -1
- package/.agent/skills/ui-reasoning-engine/SKILL.md +67 -48
- package/.agent/skills/ui-skill-packs/SKILL.md +6 -1
- package/.agent/skills/ui-skills-root/SKILL.md +39 -38
- package/.agent/skills/ui-ux-pro-max/SKILL.md +34 -26
- package/.agent/skills/ui-ux-researcher/SKILL.md +1 -0
- package/.agent/skills/vector-search-pgvector/SKILL.md +3 -2
- package/.agent/skills/vue-expert/SKILL.md +22 -19
- package/.agent/skills/vulnerability-scanner/SKILL.md +22 -21
- package/.agent/skills/web-accessibility-auditor/SKILL.md +2 -1
- package/.agent/skills/web-design-guidelines/SKILL.md +16 -10
- package/.agent/skills/web-quality-audit/SKILL.md +5 -0
- package/.agent/skills/webapp-testing/SKILL.md +14 -13
- package/.agent/skills/webgpu-performance/SKILL.md +1 -0
- package/.agent/skills/whimsy-injector/SKILL.md +9 -5
- package/.agent/skills/workflow-optimizer/SKILL.md +1 -0
- package/.agent/skills/zero-trust-passkeys/SKILL.md +11 -6
- package/.agent/templates/DESIGN.md +119 -110
- package/.agent/templates/design-tokens.json +9 -6
- package/.agent/workflows/acf.md +1 -0
- package/.agent/workflows/api-tester.md +1 -0
- package/.agent/workflows/audit.md +1 -0
- package/.agent/workflows/brainstorm.md +1 -0
- package/.agent/workflows/changelog.md +1 -0
- package/.agent/workflows/contract.md +60 -0
- package/.agent/workflows/create.md +1 -0
- package/.agent/workflows/debug.md +1 -0
- package/.agent/workflows/deploy.md +1 -0
- package/.agent/workflows/enhance.md +1 -0
- package/.agent/workflows/fix-ci.md +89 -0
- package/.agent/workflows/fix.md +1 -0
- package/.agent/workflows/generate.md +1 -0
- package/.agent/workflows/marathon.md +1 -0
- package/.agent/workflows/migrate.md +1 -0
- package/.agent/workflows/minimal.md +9 -7
- package/.agent/workflows/orchestrate.md +1 -0
- package/.agent/workflows/performance-benchmarker.md +1 -0
- package/.agent/workflows/pipeline.md +26 -21
- package/.agent/workflows/plan.md +1 -0
- package/.agent/workflows/preview.md +1 -0
- package/.agent/workflows/refactor.md +3 -2
- package/.agent/workflows/review-ai.md +4 -3
- package/.agent/workflows/review.md +1 -0
- package/.agent/workflows/session.md +1 -0
- package/.agent/workflows/status.md +1 -0
- package/.agent/workflows/strengthen-skills.md +1 -0
- package/.agent/workflows/super-prompt.md +1 -0
- package/.agent/workflows/swarm.md +15 -3
- package/.agent/workflows/test.md +8 -7
- package/.agent/workflows/tribunal-backend.md +3 -2
- package/.agent/workflows/tribunal-cicd.md +104 -0
- package/.agent/workflows/tribunal-database.md +1 -0
- package/.agent/workflows/tribunal-frontend.md +8 -6
- package/.agent/workflows/tribunal-full.md +2 -1
- package/.agent/workflows/tribunal-mobile.md +6 -5
- package/.agent/workflows/tribunal-performance.md +1 -0
- package/.agent/workflows/tribunal-speed.md +1 -0
- package/.agent/workflows/tribunal-ui.md +29 -21
- package/.agent/workflows/ui-ux-pro-max.md +19 -15
- package/CONTRIBUTING.md +3 -3
- package/README.md +94 -44
- package/SECURITY.md +10 -10
- package/bin/mcp-server.js +385 -252
- package/bin/tribunal-kit.js +29 -30
- package/bin/wrapper.js +40 -46
- package/dist/cli.js +6 -0
- package/dist/commands/case.js +2 -0
- package/dist/commands/contract.js +440 -0
- package/dist/commands/status.js +6 -0
- package/dist/index.d.ts +8 -1
- package/package.json +13 -12
- package/scripts/benchmark.js +62 -86
- package/scripts/changelog.js +67 -73
- package/scripts/fix-vbc.js +13 -15
- package/scripts/fix-vbc.ps1 +2 -118
- package/scripts/stress_benchmark.js +358 -0
- package/scripts/sync-version.js +71 -41
- package/scripts/validate-payload.js +23 -30
- package/scripts/visual_audit.js +23 -13
|
@@ -20,6 +20,7 @@ scripts-binding:
|
|
|
20
20
|
## Mandatory Pre-Flight Context Inspection
|
|
21
21
|
|
|
22
22
|
Before engineering Next.js 15+ App Router code, you MUST inspect:
|
|
23
|
+
|
|
23
24
|
1. Client Boundary rules (`"use client"` vs Server Components) → Use Server Components by default; add `"use client"` only for interactivity/hooks
|
|
24
25
|
2. Server Actions Validation (Section 68) → Always include `"use server"` and validate form inputs with Zod (`Schema.safeParse`)
|
|
25
26
|
3. Next.js 15 Caching & Dynamic APIs (Section 106) → `fetch()` is UNCACHED by default in Next.js 15; `cookies()` and `headers()` are `async`
|
|
@@ -64,7 +65,7 @@ app/
|
|
|
64
65
|
export default function Page() {
|
|
65
66
|
return (
|
|
66
67
|
<ClientSidebar>
|
|
67
|
-
{
|
|
68
|
+
{' '}
|
|
68
69
|
{/* "use client" */}
|
|
69
70
|
<ServerStats /> {/* Server: zero JS bundle, fetches DB */}
|
|
70
71
|
</ClientSidebar>
|
|
@@ -77,19 +78,19 @@ export default function Page() {
|
|
|
77
78
|
## Server Actions (Mutations)
|
|
78
79
|
|
|
79
80
|
```tsx
|
|
80
|
-
|
|
81
|
-
import { revalidatePath } from
|
|
82
|
-
import { z } from
|
|
81
|
+
'use server';
|
|
82
|
+
import { revalidatePath } from 'next/cache';
|
|
83
|
+
import { z } from 'zod';
|
|
83
84
|
|
|
84
85
|
const Schema = z.object({ name: z.string().min(2) });
|
|
85
86
|
|
|
86
87
|
export async function createUser(prevState: any, formData: FormData) {
|
|
87
88
|
// ❌ TRAP: ALWAYS validate formData. Never trust client input.
|
|
88
|
-
const parsed = Schema.safeParse({ name: formData.get(
|
|
89
|
+
const parsed = Schema.safeParse({ name: formData.get('name') });
|
|
89
90
|
if (!parsed.success) return { errors: parsed.error.flatten().fieldErrors };
|
|
90
91
|
|
|
91
92
|
await db.user.create({ data: parsed.data });
|
|
92
|
-
revalidatePath(
|
|
93
|
+
revalidatePath('/users'); // Clears cache so next render shows new user
|
|
93
94
|
return { success: true };
|
|
94
95
|
}
|
|
95
96
|
```
|
|
@@ -97,9 +98,9 @@ export async function createUser(prevState: any, formData: FormData) {
|
|
|
97
98
|
Client usage (React 19):
|
|
98
99
|
|
|
99
100
|
```tsx
|
|
100
|
-
|
|
101
|
-
import { useActionState } from
|
|
102
|
-
import { createUser } from
|
|
101
|
+
'use client';
|
|
102
|
+
import { useActionState } from 'react';
|
|
103
|
+
import { createUser } from './actions';
|
|
103
104
|
|
|
104
105
|
export function UserForm() {
|
|
105
106
|
const [state, formAction, isPending] = useActionState(createUser, null);
|
|
@@ -120,13 +121,17 @@ export function UserForm() {
|
|
|
120
121
|
```tsx
|
|
121
122
|
// Next.js 15 caching defaults
|
|
122
123
|
const dynamic = await fetch(url); // 15 default: NO CACHE
|
|
123
|
-
const static = await fetch(url, { cache:
|
|
124
|
+
const static = await fetch(url, { cache: 'force-cache' }); // Static
|
|
124
125
|
const isr = await fetch(url, { next: { revalidate: 3600 } }); // Revalidate every hour
|
|
125
|
-
const tagged = await fetch(url, { next: { tags: [
|
|
126
|
+
const tagged = await fetch(url, { next: { tags: ['user-1'] } }); // On-demand via revalidateTag()
|
|
126
127
|
|
|
127
128
|
// DB calls without fetch
|
|
128
|
-
import { unstable_cache } from
|
|
129
|
-
const getCachedUser = unstable_cache(
|
|
129
|
+
import { unstable_cache } from 'next/cache';
|
|
130
|
+
const getCachedUser = unstable_cache(
|
|
131
|
+
async id => db.user.findUnique({ where: { id } }),
|
|
132
|
+
['user-cache-key'],
|
|
133
|
+
{ revalidate: 60, tags: ['users'] },
|
|
134
|
+
);
|
|
130
135
|
```
|
|
131
136
|
|
|
132
137
|
### Waterfall Elimination
|
|
@@ -160,11 +165,11 @@ PPR static-generates the route shell and streams dynamic parts.
|
|
|
160
165
|
export default { experimental: { ppr: true } };
|
|
161
166
|
|
|
162
167
|
// Any component reading cookies/headers inside a Suspense boundary becomes a dynamic hole
|
|
163
|
-
import { cookies } from
|
|
168
|
+
import { cookies } from 'next/headers';
|
|
164
169
|
|
|
165
170
|
async function Cart() {
|
|
166
171
|
const c = await cookies(); // Next.js 15 cookies are async!
|
|
167
|
-
const cartId = c.get(
|
|
172
|
+
const cartId = c.get('cartId');
|
|
168
173
|
}
|
|
169
174
|
|
|
170
175
|
export default function Page() {
|
|
@@ -185,18 +190,18 @@ export default function Page() {
|
|
|
185
190
|
|
|
186
191
|
```typescript
|
|
187
192
|
// middleware.ts (Root of project)
|
|
188
|
-
import { NextResponse } from
|
|
189
|
-
import type { NextRequest } from
|
|
193
|
+
import { NextResponse } from 'next/server';
|
|
194
|
+
import type { NextRequest } from 'next/server';
|
|
190
195
|
|
|
191
196
|
export function middleware(req: NextRequest) {
|
|
192
|
-
const token = req.cookies.get(
|
|
193
|
-
if (!token && req.nextUrl.pathname.startsWith(
|
|
194
|
-
return NextResponse.redirect(new URL(
|
|
197
|
+
const token = req.cookies.get('auth-token');
|
|
198
|
+
if (!token && req.nextUrl.pathname.startsWith('/dashboard')) {
|
|
199
|
+
return NextResponse.redirect(new URL('/login', req.url));
|
|
195
200
|
}
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
export const config = {
|
|
199
|
-
matcher: [
|
|
204
|
+
matcher: ['/dashboard/:path*'], // Strict matcher is critical for performance
|
|
200
205
|
};
|
|
201
206
|
```
|
|
202
207
|
|
|
@@ -20,6 +20,7 @@ scripts-binding:
|
|
|
20
20
|
## Mandatory Pre-Flight Context Inspection
|
|
21
21
|
|
|
22
22
|
Before building Node.js services or middleware, you MUST inspect:
|
|
23
|
+
|
|
23
24
|
1. Node Built-in Import Protocol (Section 34) → Use explicit `node:` prefix (`import fs from "node:fs/promises"`); ban legacy CommonJS `require()`
|
|
24
25
|
2. Mandatory Process Exit on Uncaught Exception (Section 170) → Ensure process exits (`process.exit(1)`) after `uncaughtException` to prevent running in corrupted state
|
|
25
26
|
3. Startup Environment Validation (Section 373) → Validate environment variables at app startup with Zod; crash immediately if required secrets are missing
|
|
@@ -40,10 +41,10 @@ Before building Node.js services or middleware, you MUST inspect:
|
|
|
40
41
|
|
|
41
42
|
```typescript
|
|
42
43
|
// ✅ ESM imports (modern Node.js)
|
|
43
|
-
import { readFile, writeFile } from
|
|
44
|
-
import { join, resolve } from
|
|
45
|
-
import { createServer } from
|
|
46
|
-
import { EventEmitter } from
|
|
44
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
45
|
+
import { join, resolve } from 'node:path';
|
|
46
|
+
import { createServer } from 'node:http';
|
|
47
|
+
import { EventEmitter } from 'node:events';
|
|
47
48
|
|
|
48
49
|
// ❌ HALLUCINATION TRAP: Use node: protocol prefix for built-in modules
|
|
49
50
|
// ❌ import fs from "fs"; ← ambiguous (could be npm package)
|
|
@@ -54,7 +55,7 @@ import { EventEmitter } from "node:events";
|
|
|
54
55
|
// ✅ import fs from "node:fs/promises"; ← ESM
|
|
55
56
|
|
|
56
57
|
// Dynamic imports (for conditional loading)
|
|
57
|
-
const module = await import(
|
|
58
|
+
const module = await import('./heavy-module.js');
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
---
|
|
@@ -76,13 +77,13 @@ const module = await import("./heavy-module.js");
|
|
|
76
77
|
### Fastify (Recommended)
|
|
77
78
|
|
|
78
79
|
```typescript
|
|
79
|
-
import Fastify from
|
|
80
|
-
import { z } from
|
|
80
|
+
import Fastify from 'fastify';
|
|
81
|
+
import { z } from 'zod';
|
|
81
82
|
|
|
82
83
|
const app = Fastify({
|
|
83
84
|
logger: {
|
|
84
|
-
level: process.env.LOG_LEVEL ??
|
|
85
|
-
transport: process.env.NODE_ENV ===
|
|
85
|
+
level: process.env.LOG_LEVEL ?? 'info',
|
|
86
|
+
transport: process.env.NODE_ENV === 'development' ? { target: 'pino-pretty' } : undefined,
|
|
86
87
|
},
|
|
87
88
|
});
|
|
88
89
|
|
|
@@ -90,22 +91,22 @@ const app = Fastify({
|
|
|
90
91
|
const CreateUserSchema = z.object({
|
|
91
92
|
name: z.string().min(2).max(100),
|
|
92
93
|
email: z.string().email(),
|
|
93
|
-
role: z.enum([
|
|
94
|
+
role: z.enum(['admin', 'user']).default('user'),
|
|
94
95
|
});
|
|
95
96
|
|
|
96
97
|
type CreateUserBody = z.infer<typeof CreateUserSchema>;
|
|
97
98
|
|
|
98
99
|
app.post<{ Body: CreateUserBody }>(
|
|
99
|
-
|
|
100
|
+
'/users',
|
|
100
101
|
{
|
|
101
102
|
schema: {
|
|
102
103
|
body: {
|
|
103
|
-
type:
|
|
104
|
-
required: [
|
|
104
|
+
type: 'object',
|
|
105
|
+
required: ['name', 'email'],
|
|
105
106
|
properties: {
|
|
106
|
-
name: { type:
|
|
107
|
-
email: { type:
|
|
108
|
-
role: { type:
|
|
107
|
+
name: { type: 'string', minLength: 2 },
|
|
108
|
+
email: { type: 'string', format: 'email' },
|
|
109
|
+
role: { type: 'string', enum: ['admin', 'user'] },
|
|
109
110
|
},
|
|
110
111
|
},
|
|
111
112
|
},
|
|
@@ -120,7 +121,7 @@ app.post<{ Body: CreateUserBody }>(
|
|
|
120
121
|
// Graceful shutdown
|
|
121
122
|
const start = async () => {
|
|
122
123
|
try {
|
|
123
|
-
await app.listen({ port: 3000, host:
|
|
124
|
+
await app.listen({ port: 3000, host: '0.0.0.0' });
|
|
124
125
|
} catch (err) {
|
|
125
126
|
app.log.error(err);
|
|
126
127
|
process.exit(1);
|
|
@@ -133,29 +134,29 @@ start();
|
|
|
133
134
|
### Hono (Edge-First)
|
|
134
135
|
|
|
135
136
|
```typescript
|
|
136
|
-
import { Hono } from
|
|
137
|
-
import { cors } from
|
|
138
|
-
import { logger } from
|
|
139
|
-
import { zValidator } from
|
|
140
|
-
import { z } from
|
|
137
|
+
import { Hono } from 'hono';
|
|
138
|
+
import { cors } from 'hono/cors';
|
|
139
|
+
import { logger } from 'hono/logger';
|
|
140
|
+
import { zValidator } from '@hono/zod-validator';
|
|
141
|
+
import { z } from 'zod';
|
|
141
142
|
|
|
142
143
|
const app = new Hono();
|
|
143
144
|
|
|
144
|
-
app.use(
|
|
145
|
-
app.use(
|
|
145
|
+
app.use('*', logger());
|
|
146
|
+
app.use('*', cors({ origin: 'https://myapp.com' }));
|
|
146
147
|
|
|
147
148
|
const createUserSchema = z.object({
|
|
148
149
|
name: z.string().min(2),
|
|
149
150
|
email: z.string().email(),
|
|
150
151
|
});
|
|
151
152
|
|
|
152
|
-
app.post(
|
|
153
|
-
const body = c.req.valid(
|
|
153
|
+
app.post('/users', zValidator('json', createUserSchema), async c => {
|
|
154
|
+
const body = c.req.valid('json');
|
|
154
155
|
const user = await createUser(body);
|
|
155
156
|
return c.json(user, 201);
|
|
156
157
|
});
|
|
157
158
|
|
|
158
|
-
app.get(
|
|
159
|
+
app.get('/health', c => c.json({ status: 'ok' }));
|
|
159
160
|
|
|
160
161
|
export default app; // works in Node, Deno, Bun, Cloudflare Workers
|
|
161
162
|
```
|
|
@@ -168,15 +169,15 @@ export default app; // works in Node, Deno, Bun, Cloudflare Workers
|
|
|
168
169
|
|
|
169
170
|
```typescript
|
|
170
171
|
// ✅ MANDATORY: Handle unhandled rejections and exceptions
|
|
171
|
-
process.on(
|
|
172
|
-
console.error(
|
|
172
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
173
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
173
174
|
// Log to error tracking service (Sentry, etc.)
|
|
174
175
|
// Gracefully shutdown
|
|
175
176
|
process.exit(1);
|
|
176
177
|
});
|
|
177
178
|
|
|
178
|
-
process.on(
|
|
179
|
-
console.error(
|
|
179
|
+
process.on('uncaughtException', error => {
|
|
180
|
+
console.error('Uncaught Exception:', error);
|
|
180
181
|
// Log to error tracking service
|
|
181
182
|
process.exit(1); // MUST exit — state is corrupted
|
|
182
183
|
});
|
|
@@ -194,18 +195,18 @@ export class AppError extends Error {
|
|
|
194
195
|
constructor(
|
|
195
196
|
message: string,
|
|
196
197
|
public statusCode: number = 500,
|
|
197
|
-
public code: string =
|
|
198
|
+
public code: string = 'INTERNAL_ERROR',
|
|
198
199
|
public isOperational: boolean = true,
|
|
199
200
|
) {
|
|
200
201
|
super(message);
|
|
201
|
-
this.name =
|
|
202
|
+
this.name = 'AppError';
|
|
202
203
|
Error.captureStackTrace(this, this.constructor);
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
export class NotFoundError extends AppError {
|
|
207
208
|
constructor(resource: string, id: string) {
|
|
208
|
-
super(`${resource} '${id}' not found`, 404,
|
|
209
|
+
super(`${resource} '${id}' not found`, 404, 'NOT_FOUND');
|
|
209
210
|
}
|
|
210
211
|
}
|
|
211
212
|
|
|
@@ -214,13 +215,13 @@ export class ValidationError extends AppError {
|
|
|
214
215
|
message: string,
|
|
215
216
|
public errors: Record<string, string[]> = {},
|
|
216
217
|
) {
|
|
217
|
-
super(message, 400,
|
|
218
|
+
super(message, 400, 'VALIDATION_ERROR');
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
export class UnauthorizedError extends AppError {
|
|
222
|
-
constructor(message =
|
|
223
|
-
super(message, 401,
|
|
223
|
+
constructor(message = 'Authentication required') {
|
|
224
|
+
super(message, 401, 'UNAUTHORIZED');
|
|
224
225
|
}
|
|
225
226
|
}
|
|
226
227
|
|
|
@@ -232,9 +233,9 @@ app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
|
232
233
|
});
|
|
233
234
|
} else {
|
|
234
235
|
// Programmer error — log and return generic message
|
|
235
|
-
console.error(
|
|
236
|
+
console.error('Unexpected error:', err);
|
|
236
237
|
res.status(500).json({
|
|
237
|
-
error: { code:
|
|
238
|
+
error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' },
|
|
238
239
|
});
|
|
239
240
|
}
|
|
240
241
|
});
|
|
@@ -262,13 +263,17 @@ const [users, posts, stats] = await Promise.all([
|
|
|
262
263
|
// Total: ~200ms
|
|
263
264
|
|
|
264
265
|
// Promise.allSettled — when some can fail
|
|
265
|
-
const results = await Promise.allSettled([
|
|
266
|
+
const results = await Promise.allSettled([
|
|
267
|
+
fetchCriticalData(),
|
|
268
|
+
fetchOptionalData(),
|
|
269
|
+
fetchAnalytics(),
|
|
270
|
+
]);
|
|
266
271
|
|
|
267
272
|
for (const result of results) {
|
|
268
|
-
if (result.status ===
|
|
273
|
+
if (result.status === 'fulfilled') {
|
|
269
274
|
process(result.value);
|
|
270
275
|
} else {
|
|
271
|
-
console.error(
|
|
276
|
+
console.error('Failed:', result.reason);
|
|
272
277
|
}
|
|
273
278
|
}
|
|
274
279
|
```
|
|
@@ -276,7 +281,10 @@ for (const result of results) {
|
|
|
276
281
|
### Retry Pattern
|
|
277
282
|
|
|
278
283
|
```typescript
|
|
279
|
-
async function withRetry<T>(
|
|
284
|
+
async function withRetry<T>(
|
|
285
|
+
fn: () => Promise<T>,
|
|
286
|
+
options: { maxRetries?: number; baseDelay?: number; maxDelay?: number } = {},
|
|
287
|
+
): Promise<T> {
|
|
280
288
|
const { maxRetries = 3, baseDelay = 1000, maxDelay = 10000 } = options;
|
|
281
289
|
|
|
282
290
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
@@ -288,14 +296,14 @@ async function withRetry<T>(fn: () => Promise<T>, options: { maxRetries?: number
|
|
|
288
296
|
const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
|
|
289
297
|
const jitter = delay * (0.5 + Math.random() * 0.5);
|
|
290
298
|
console.warn(`Attempt ${attempt + 1} failed, retrying in ${jitter}ms`);
|
|
291
|
-
await new Promise(
|
|
299
|
+
await new Promise(resolve => setTimeout(resolve, jitter));
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
|
-
throw new Error(
|
|
302
|
+
throw new Error('Unreachable');
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
// Usage:
|
|
298
|
-
const data = await withRetry(() => fetch(
|
|
306
|
+
const data = await withRetry(() => fetch('https://api.flaky.com/data'), {
|
|
299
307
|
maxRetries: 3,
|
|
300
308
|
baseDelay: 500,
|
|
301
309
|
});
|
|
@@ -312,7 +320,7 @@ async function fetchWithTimeout(url: string, timeoutMs = 5000): Promise<Response
|
|
|
312
320
|
const response = await fetch(url, { signal: controller.signal });
|
|
313
321
|
return response;
|
|
314
322
|
} catch (error) {
|
|
315
|
-
if (error instanceof DOMException && error.name ===
|
|
323
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
316
324
|
throw new Error(`Request to ${url} timed out after ${timeoutMs}ms`);
|
|
317
325
|
}
|
|
318
326
|
throw error;
|
|
@@ -327,9 +335,9 @@ async function fetchWithTimeout(url: string, timeoutMs = 5000): Promise<Response
|
|
|
327
335
|
## Streaming
|
|
328
336
|
|
|
329
337
|
```typescript
|
|
330
|
-
import { Readable, Transform, pipeline } from
|
|
331
|
-
import { createReadStream, createWriteStream } from
|
|
332
|
-
import { createGzip } from
|
|
338
|
+
import { Readable, Transform, pipeline } from 'node:stream/promises';
|
|
339
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
340
|
+
import { createGzip } from 'node:zlib';
|
|
333
341
|
|
|
334
342
|
// Stream large file processing (no memory issues)
|
|
335
343
|
async function processLargeCSV(inputPath: string, outputPath: string) {
|
|
@@ -340,20 +348,25 @@ async function processLargeCSV(inputPath: string, outputPath: string) {
|
|
|
340
348
|
},
|
|
341
349
|
});
|
|
342
350
|
|
|
343
|
-
await pipeline(
|
|
351
|
+
await pipeline(
|
|
352
|
+
createReadStream(inputPath),
|
|
353
|
+
transform,
|
|
354
|
+
createGzip(),
|
|
355
|
+
createWriteStream(outputPath),
|
|
356
|
+
);
|
|
344
357
|
}
|
|
345
358
|
|
|
346
359
|
// Streaming HTTP response
|
|
347
|
-
app.get(
|
|
348
|
-
res.setHeader(
|
|
349
|
-
res.setHeader(
|
|
360
|
+
app.get('/export', async (req, res) => {
|
|
361
|
+
res.setHeader('Content-Type', 'text/csv');
|
|
362
|
+
res.setHeader('Content-Disposition', 'attachment; filename=export.csv');
|
|
350
363
|
|
|
351
|
-
const cursor = db.collection(
|
|
352
|
-
cursor.on(
|
|
364
|
+
const cursor = db.collection('users').find().stream();
|
|
365
|
+
cursor.on('data', user => {
|
|
353
366
|
res.write(`${user.id},${user.name},${user.email}\n`);
|
|
354
367
|
});
|
|
355
|
-
cursor.on(
|
|
356
|
-
cursor.on(
|
|
368
|
+
cursor.on('end', () => res.end());
|
|
369
|
+
cursor.on('error', err => {
|
|
357
370
|
console.error(err);
|
|
358
371
|
res.status(500).end();
|
|
359
372
|
});
|
|
@@ -370,16 +383,16 @@ app.get("/export", async (req, res) => {
|
|
|
370
383
|
|
|
371
384
|
```typescript
|
|
372
385
|
// config.ts — centralized, validated configuration
|
|
373
|
-
import { z } from
|
|
386
|
+
import { z } from 'zod';
|
|
374
387
|
|
|
375
388
|
const envSchema = z.object({
|
|
376
|
-
NODE_ENV: z.enum([
|
|
389
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
377
390
|
PORT: z.coerce.number().default(3000),
|
|
378
391
|
DATABASE_URL: z.string().url(),
|
|
379
392
|
REDIS_URL: z.string().url().optional(),
|
|
380
393
|
JWT_SECRET: z.string().min(32),
|
|
381
|
-
CORS_ORIGIN: z.string().default(
|
|
382
|
-
LOG_LEVEL: z.enum([
|
|
394
|
+
CORS_ORIGIN: z.string().default('http://localhost:5173'),
|
|
395
|
+
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
383
396
|
});
|
|
384
397
|
|
|
385
398
|
export const config = envSchema.parse(process.env);
|
|
@@ -398,27 +411,27 @@ export const config = envSchema.parse(process.env);
|
|
|
398
411
|
## Security Hardening
|
|
399
412
|
|
|
400
413
|
```typescript
|
|
401
|
-
import helmet from
|
|
402
|
-
import rateLimit from
|
|
414
|
+
import helmet from 'helmet';
|
|
415
|
+
import rateLimit from 'express-rate-limit';
|
|
403
416
|
|
|
404
417
|
// Security headers
|
|
405
418
|
app.use(helmet());
|
|
406
419
|
|
|
407
420
|
// Rate limiting
|
|
408
421
|
app.use(
|
|
409
|
-
|
|
422
|
+
'/api/',
|
|
410
423
|
rateLimit({
|
|
411
424
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
412
425
|
max: 100, // 100 requests per window
|
|
413
426
|
standardHeaders: true,
|
|
414
427
|
legacyHeaders: false,
|
|
415
|
-
message: { error:
|
|
428
|
+
message: { error: 'Too many requests, try again later' },
|
|
416
429
|
}),
|
|
417
430
|
);
|
|
418
431
|
|
|
419
432
|
// Auth rate limiting (stricter)
|
|
420
433
|
app.use(
|
|
421
|
-
|
|
434
|
+
'/api/auth/',
|
|
422
435
|
rateLimit({
|
|
423
436
|
windowMs: 15 * 60 * 1000,
|
|
424
437
|
max: 5, // only 5 login attempts per 15 min
|
|
@@ -426,7 +439,7 @@ app.use(
|
|
|
426
439
|
);
|
|
427
440
|
|
|
428
441
|
// Input validation (ALWAYS validate)
|
|
429
|
-
app.post(
|
|
442
|
+
app.post('/api/users', async (req, res, next) => {
|
|
430
443
|
try {
|
|
431
444
|
const data = CreateUserSchema.parse(req.body); // Zod validates
|
|
432
445
|
const user = await createUser(data);
|
|
@@ -441,12 +454,12 @@ app.post("/api/users", async (req, res, next) => {
|
|
|
441
454
|
// ✅ db.query("SELECT * FROM users WHERE id = $1", [req.params.id]);
|
|
442
455
|
|
|
443
456
|
// Path traversal prevention
|
|
444
|
-
import { resolve, normalize } from
|
|
457
|
+
import { resolve, normalize } from 'node:path';
|
|
445
458
|
|
|
446
459
|
function safePath(userInput: string, baseDir: string): string {
|
|
447
460
|
const resolved = resolve(baseDir, normalize(userInput));
|
|
448
461
|
if (!resolved.startsWith(baseDir)) {
|
|
449
|
-
throw new Error(
|
|
462
|
+
throw new Error('Path traversal detected');
|
|
450
463
|
}
|
|
451
464
|
return resolved;
|
|
452
465
|
}
|
|
@@ -473,29 +486,29 @@ async function gracefulShutdown(signal: string) {
|
|
|
473
486
|
|
|
474
487
|
// Allow in-flight requests 10s to complete
|
|
475
488
|
setTimeout(() => {
|
|
476
|
-
console.error(
|
|
489
|
+
console.error('Forceful shutdown after timeout');
|
|
477
490
|
process.exit(1);
|
|
478
491
|
}, 10000);
|
|
479
492
|
|
|
480
493
|
process.exit(0);
|
|
481
494
|
}
|
|
482
495
|
|
|
483
|
-
process.on(
|
|
484
|
-
process.on(
|
|
496
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
497
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
485
498
|
```
|
|
486
499
|
|
|
487
500
|
### Worker Threads (CPU-Bound)
|
|
488
501
|
|
|
489
502
|
```typescript
|
|
490
|
-
import { Worker, isMainThread, parentPort, workerData } from
|
|
503
|
+
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
|
|
491
504
|
|
|
492
505
|
if (isMainThread) {
|
|
493
506
|
// Main thread — offload CPU work
|
|
494
507
|
function runWorker(data: unknown): Promise<unknown> {
|
|
495
508
|
return new Promise((resolve, reject) => {
|
|
496
509
|
const worker = new Worker(new URL(import.meta.url), { workerData: data });
|
|
497
|
-
worker.on(
|
|
498
|
-
worker.on(
|
|
510
|
+
worker.on('message', resolve);
|
|
511
|
+
worker.on('error', reject);
|
|
499
512
|
});
|
|
500
513
|
}
|
|
501
514
|
|