@punks/cli 0.1.16 → 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.
- package/AGENTS.md +10 -0
- package/README.md +64 -0
- package/dist/data/AGENTS.md +123 -0
- package/dist/data/catalog/lint.ts +349 -0
- package/dist/data/catalog/packs.ts +44 -0
- package/dist/data/catalog/skills.ts +47 -0
- package/dist/data/catalog/tools.ts +22 -0
- package/dist/data/hooks/format-edited-file.mjs +554 -0
- package/dist/data/hooks/format-edited-file.py +157 -0
- package/dist/data/hooks/format-edited-file.sh +37 -0
- package/dist/data/hooks/require-tests-for-pr.mjs +144 -0
- package/dist/data/scripts/sync-subagents.mjs +420 -0
- package/dist/data/subagents/manifest.mjs +253 -0
- package/dist/index.js +46415 -0
- package/dist/skills/agnostic/backend/backend-domain-structure/SKILL.md +50 -0
- package/dist/skills/agnostic/backend/backend-domain-structure/agents/openai.yaml +4 -0
- package/dist/skills/agnostic/backend/backend-domain-structure/references/layout.md +167 -0
- package/dist/skills/agnostic/backend/backend-recoverable-actions/SKILL.md +49 -0
- package/dist/skills/agnostic/backend/backend-recoverable-actions/agents/openai.yaml +4 -0
- package/dist/skills/agnostic/backend/backend-recoverable-actions/references/strategy-matrix.md +34 -0
- package/dist/skills/agnostic/backend/backend-recoverable-actions/references/test-matrix.md +34 -0
- package/dist/skills/agnostic/docs/docs-maintenance/SKILL.md +193 -0
- package/dist/skills/agnostic/docs/docs-maintenance/agents/openai.yaml +4 -0
- package/dist/skills/agnostic/docs/docs-maintenance/references/concept-pages.md +48 -0
- package/dist/skills/agnostic/docs/docs-maintenance/references/flow-pages.md +41 -0
- package/dist/skills/agnostic/frontend/agent-browser/.clawdhub/origin.json +7 -0
- package/dist/skills/agnostic/frontend/agent-browser/SKILL.md +229 -0
- package/dist/skills/agnostic/frontend/agent-browser/references/authentication.md +202 -0
- package/dist/skills/agnostic/frontend/agent-browser/references/commands.md +259 -0
- package/dist/skills/agnostic/frontend/agent-browser/references/proxy-support.md +188 -0
- package/dist/skills/agnostic/frontend/agent-browser/references/session-management.md +193 -0
- package/dist/skills/agnostic/frontend/agent-browser/references/snapshot-refs.md +194 -0
- package/dist/skills/agnostic/frontend/agent-browser/references/video-recording.md +173 -0
- package/dist/skills/agnostic/frontend/agent-browser/templates/authenticated-session.sh +97 -0
- package/dist/skills/agnostic/frontend/agent-browser/templates/capture-workflow.sh +69 -0
- package/dist/skills/agnostic/frontend/agent-browser/templates/form-automation.sh +62 -0
- package/dist/skills/agnostic/frontend/design-taste-frontend/SKILL.md +226 -0
- package/dist/skills/agnostic/frontend/frontend-domain-structure/SKILL.md +55 -0
- package/dist/skills/agnostic/frontend/frontend-domain-structure/agents/openai.yaml +4 -0
- package/dist/skills/agnostic/frontend/frontend-domain-structure/references/react/structure.md +102 -0
- package/dist/skills/agnostic/frontend/frontend-domain-structure/references/structure.md +257 -0
- package/dist/skills/agnostic/frontend/gpt-taste/SKILL.md +74 -0
- package/dist/skills/agnostic/frontend/image-taste-frontend/SKILL.md +1102 -0
- package/dist/skills/agnostic/frontend/redesign-existing-projects/SKILL.md +178 -0
- package/dist/skills/agnostic/planning/create-plan/REFERENCE.md +37 -0
- package/dist/skills/agnostic/planning/create-plan/SKILL.md +69 -0
- package/dist/skills/agnostic/planning/create-plan/references/backlog-sync.md +44 -0
- package/dist/skills/agnostic/planning/create-plan/references/grill-phase.md +86 -0
- package/dist/skills/agnostic/planning/create-plan/references/plan-schema.md +66 -0
- package/dist/skills/agnostic/planning/create-plan/references/planner-phase.md +39 -0
- package/dist/skills/agnostic/planning/create-plan/references/stop-conditions.md +18 -0
- package/dist/skills/agnostic/planning/create-plan/references/tdd-phase.md +26 -0
- package/dist/skills/agnostic/planning/create-spec/SKILL.md +57 -0
- package/dist/skills/agnostic/planning/create-spec/assets/SPEC-TEMPLATE.md +91 -0
- package/dist/skills/agnostic/planning/create-spec/references/discovery.md +44 -0
- package/dist/skills/agnostic/planning/create-spec/references/folder-naming.md +27 -0
- package/dist/skills/agnostic/planning/create-spec/references/handoff.md +47 -0
- package/dist/skills/agnostic/planning/create-spec/references/questioning.md +41 -0
- package/dist/skills/agnostic/planning/create-spec/references/spec-quality-bar.md +58 -0
- package/dist/skills/agnostic/planning/create-spec/references/wiki-bookkeeping.md +26 -0
- package/dist/skills/agnostic/planning/grill-me/SKILL.md +28 -0
- package/dist/skills/agnostic/planning/implement-spec/SKILL.md +72 -0
- package/dist/skills/agnostic/planning/implement-spec/assets/IMPLEMENTATION-NOTES-TEMPLATE.md +47 -0
- package/dist/skills/agnostic/planning/implement-spec/references/lifecycle.md +149 -0
- package/dist/skills/agnostic/planning/implement-spec/references/parallel-orchestration.md +102 -0
- package/dist/skills/agnostic/planning/implement-spec/references/parallel-worker-brief.md +65 -0
- package/dist/skills/agnostic/planning/implement-spec/references/parallel.md +56 -0
- package/dist/skills/agnostic/planning/implement-spec/references/sequential.md +28 -0
- package/dist/skills/agnostic/planning/swarm-planner/SKILL.md +179 -0
- package/dist/skills/agnostic/quality/simplify/SKILL.md +14 -0
- package/dist/skills/agnostic/quality/tdd/SKILL.md +107 -0
- package/dist/skills/agnostic/quality/tdd/deep-modules.md +33 -0
- package/dist/skills/agnostic/quality/tdd/interface-design.md +31 -0
- package/dist/skills/agnostic/quality/tdd/mocking.md +59 -0
- package/dist/skills/agnostic/quality/tdd/refactoring.md +10 -0
- package/dist/skills/agnostic/quality/tdd/tests.md +61 -0
- package/dist/skills/agnostic/requirements/requirements-grill/SKILL.md +42 -0
- package/dist/skills/agnostic/requirements/requirements-grill/references/artifact-output.md +73 -0
- package/dist/skills/agnostic/requirements/requirements-grill/references/grilling-flow.md +57 -0
- package/dist/skills/agnostic/requirements/requirements-grill/references/wiki-output.md +94 -0
- package/dist/skills/agnostic/requirements/write-backlog/EXAMPLES.md +67 -0
- package/dist/skills/agnostic/requirements/write-backlog/REFERENCE.md +253 -0
- package/dist/skills/agnostic/requirements/write-backlog/SKILL.md +68 -0
- package/dist/skills/agnostic/requirements/write-backlog/assets/concepts/backlog-model.md +69 -0
- package/dist/skills/agnostic/requirements/write-backlog/assets/concepts/story-shape.md +66 -0
- package/dist/skills/agnostic/requirements/write-backlog/assets/providers/azure-devops-create-payload.md +63 -0
- package/dist/skills/agnostic/requirements/write-backlog/assets/providers/github-issues-create-payload.md +48 -0
- package/dist/skills/agnostic/requirements/write-backlog/assets/providers/linear-create-payload.md +76 -0
- package/dist/skills/agnostic/research/improve-codebase-architecture/REFERENCE.md +78 -0
- package/dist/skills/agnostic/research/improve-codebase-architecture/SKILL.md +76 -0
- package/dist/skills/agnostic/research/parallel-research/SKILL.md +68 -0
- package/dist/skills/agnostic/research/parallel-research/agents/openai.yaml +4 -0
- package/dist/skills/agnostic/subagents/swarm-planner/SKILL.md +179 -0
- package/dist/skills/agnostic/write-a-skill/SKILL.md +117 -0
- package/dist/skills/frameworks/better-auth/better-auth-best-practices/SKILL.md +166 -0
- package/dist/skills/frameworks/better-auth/better-auth-security-best-practices/SKILL.MD +432 -0
- package/dist/skills/frameworks/effect/effect-authoring/SKILL.md +116 -0
- package/dist/skills/frameworks/effect/effect-authoring/references/branded-types.md +98 -0
- package/dist/skills/frameworks/effect/effect-authoring/references/effect-atom-patterns.md +257 -0
- package/dist/skills/frameworks/effect/effect-authoring/references/effect-primitives.md +144 -0
- package/dist/skills/frameworks/effect/effect-authoring/references/error-patterns.md +156 -0
- package/dist/skills/frameworks/effect/effect-authoring/references/otel-patterns.md +113 -0
- package/dist/skills/frameworks/effect/effect-authoring/references/test-patterns.md +146 -0
- package/dist/skills/frameworks/effect/effect-backend-structure/SKILL.md +96 -0
- package/dist/skills/frameworks/effect/effect-backend-structure/agents/openai.yaml +4 -0
- package/dist/skills/frameworks/effect/effect-backend-structure/references/layout.md +68 -0
- package/dist/skills/frameworks/effect/effect-best-practices/SKILL.md +517 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/anti-patterns.md +392 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/effect-atom-patterns.md +653 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/error-patterns.md +464 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/language-server.md +287 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/layer-patterns.md +495 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/observability-patterns.md +342 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/rpc-cluster-patterns.md +418 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/schema-patterns.md +353 -0
- package/dist/skills/frameworks/effect/effect-best-practices/references/service-patterns.md +299 -0
- package/dist/skills/frameworks/effect/effect-recoverable-actions/SKILL.md +65 -0
- package/dist/skills/frameworks/effect/effect-recoverable-actions/agents/openai.yaml +4 -0
- package/dist/skills/frameworks/effect/effect-recoverable-actions/references/flow-examples.md +154 -0
- package/dist/skills/frameworks/effect/effect-recoverable-actions/references/source-backed-primitives.md +104 -0
- package/dist/skills/frameworks/effect/effect-recoverable-actions/references/strategy-matrix.md +34 -0
- package/dist/skills/frameworks/effect/effect-recoverable-actions/references/test-matrix.md +36 -0
- package/dist/skills/frameworks/elysia/elysiajs/SKILL.md +475 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/basic.ts +9 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/body-parser.ts +33 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/complex.ts +112 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/cookie.ts +45 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/error.ts +38 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/file.ts +10 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/guard.ts +34 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/map-response.ts +15 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/redirect.ts +6 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/rename.ts +32 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/schema.ts +61 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/state.ts +6 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/upload-file.ts +20 -0
- package/dist/skills/frameworks/elysia/elysiajs/examples/websocket.ts +25 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/ai-sdk.md +92 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/astro.md +59 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/better-auth.md +117 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/cloudflare-worker.md +95 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/deno.md +34 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/drizzle.md +258 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/expo.md +95 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/nextjs.md +103 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/nodejs.md +64 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/nuxt.md +67 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/prisma.md +93 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/react-email.md +134 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/sveltekit.md +53 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/tanstack-start.md +87 -0
- package/dist/skills/frameworks/elysia/elysiajs/integrations/vercel.md +55 -0
- package/dist/skills/frameworks/elysia/elysiajs/patterns/mvc.md +380 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/bearer.md +30 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/cors.md +141 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/cron.md +265 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/graphql-apollo.md +90 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/graphql-yoga.md +87 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/html.md +188 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/jwt.md +197 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/openapi.md +246 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/opentelemetry.md +167 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/server-timing.md +71 -0
- package/dist/skills/frameworks/elysia/elysiajs/plugins/static.md +84 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/bun-fullstack-dev-server.md +129 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/cookie.md +187 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/deployment.md +413 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/eden.md +158 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/lifecycle.md +198 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/macro.md +83 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/plugin.md +207 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/route.md +331 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/testing.md +385 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/validation.md +491 -0
- package/dist/skills/frameworks/elysia/elysiajs/references/websocket.md +250 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/.github/workflows/branch-protection.yml +24 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/.github/workflows/deploy.yml +61 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/AGENTS.md +5958 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/SKILL.md +130 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/api-use-dto-serialization.md +182 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/api-use-interceptors.md +202 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/api-use-pipes.md +205 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/api-versioning.md +191 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/arch-avoid-circular-deps.md +80 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/arch-feature-modules.md +82 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/arch-module-sharing.md +141 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/arch-single-responsibility.md +106 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/arch-use-events.md +108 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/arch-use-repository-pattern.md +97 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/db-avoid-n-plus-one.md +139 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/db-use-migrations.md +129 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/db-use-transactions.md +140 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/devops-graceful-shutdown.md +222 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/devops-use-config-module.md +167 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/devops-use-logging.md +232 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/di-avoid-service-locator.md +104 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/di-interface-segregation.md +165 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/di-liskov-substitution.md +221 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/di-prefer-constructor-injection.md +86 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/di-scope-awareness.md +94 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/di-use-interfaces-tokens.md +101 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/error-handle-async-errors.md +125 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/error-throw-http-exceptions.md +114 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/error-use-exception-filters.md +140 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/micro-use-health-checks.md +226 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/micro-use-patterns.md +167 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/micro-use-queues.md +252 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/perf-async-hooks.md +109 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/perf-lazy-loading.md +121 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/perf-optimize-database.md +131 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/perf-use-caching.md +128 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/security-auth-jwt.md +146 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/security-rate-limiting.md +125 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/security-sanitize-output.md +139 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/security-use-guards.md +135 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/security-validate-all-input.md +150 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/test-e2e-supertest.md +178 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/test-mock-external-services.md +179 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/test-use-testing-module.md +153 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/scripts/build-agents.ts +299 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/scripts/build.sh +16 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/scripts/package-lock.json +237 -0
- package/dist/skills/frameworks/nestjs/nestjs-best-practices/scripts/package.json +15 -0
- package/dist/skills/frameworks/nestjs/nestjs-expert/SKILL.md +208 -0
- package/dist/skills/frameworks/nestjs/nestjs-expert/references/authentication.md +166 -0
- package/dist/skills/frameworks/nestjs/nestjs-expert/references/controllers-routing.md +111 -0
- package/dist/skills/frameworks/nestjs/nestjs-expert/references/dtos-validation.md +153 -0
- package/dist/skills/frameworks/nestjs/nestjs-expert/references/migration-from-express.md +1237 -0
- package/dist/skills/frameworks/nestjs/nestjs-expert/references/services-di.md +140 -0
- package/dist/skills/frameworks/nestjs/nestjs-expert/references/testing-patterns.md +186 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/SKILL.md +153 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/async-patterns.md +87 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/bundling.md +180 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/data-patterns.md +297 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/debug-tricks.md +105 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/directives.md +73 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/error-handling.md +227 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/file-conventions.md +140 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/font.md +245 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/functions.md +108 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/hydration-error.md +91 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/image.md +173 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/metadata.md +301 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/parallel-routes.md +287 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/route-handlers.md +146 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/rsc-boundaries.md +159 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/runtime-selection.md +39 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/scripts.md +141 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/self-hosting.md +371 -0
- package/dist/skills/frameworks/nextjs/next-best-practices/suspense-boundaries.md +67 -0
- package/dist/skills/frameworks/nextjs/next-cache-components/SKILL.md +360 -0
- package/dist/skills/frameworks/react/async-react-patterns/SKILL.md +78 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/AGENTS.md +946 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/SKILL.md +89 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/dist/skills/frameworks/react/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/AGENTS.md +3750 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/SKILL.md +148 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/async-defer-await.md +82 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/dist/skills/frameworks/react/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/.claude-plugin/plugin.json +12 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/SKILL.md +1058 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/assets/example-template.txt +14 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/references/best-practices.md +304 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/references/common-patterns.md +271 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/references/example-reference.md +26 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/references/testing.md +282 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/references/top-errors.md +332 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/references/typescript-patterns.md +291 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/references/v4-to-v5-migration.md +231 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/rules/tanstack-query.md +126 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/scripts/example-script.sh +15 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/custom-hooks-pattern.tsx +281 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/devtools-setup.tsx +248 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/error-boundary.tsx +243 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/package.json +31 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/provider-setup.tsx +50 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/query-client-config.ts +72 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/use-infinite-query.tsx +214 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/use-mutation-basic.tsx +201 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/use-mutation-optimistic.tsx +234 -0
- package/dist/skills/frameworks/tanstack-query/tanstack-query/templates/use-query-basic.tsx +119 -0
- package/dist/skills/frameworks/trpc/tanstack-query/.claude-plugin/plugin.json +12 -0
- package/dist/skills/frameworks/trpc/tanstack-query/SKILL.md +1058 -0
- package/dist/skills/frameworks/trpc/tanstack-query/assets/example-template.txt +14 -0
- package/dist/skills/frameworks/trpc/tanstack-query/references/best-practices.md +304 -0
- package/dist/skills/frameworks/trpc/tanstack-query/references/common-patterns.md +271 -0
- package/dist/skills/frameworks/trpc/tanstack-query/references/example-reference.md +26 -0
- package/dist/skills/frameworks/trpc/tanstack-query/references/testing.md +282 -0
- package/dist/skills/frameworks/trpc/tanstack-query/references/top-errors.md +332 -0
- package/dist/skills/frameworks/trpc/tanstack-query/references/typescript-patterns.md +291 -0
- package/dist/skills/frameworks/trpc/tanstack-query/references/v4-to-v5-migration.md +231 -0
- package/dist/skills/frameworks/trpc/tanstack-query/rules/tanstack-query.md +126 -0
- package/dist/skills/frameworks/trpc/tanstack-query/scripts/example-script.sh +15 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/custom-hooks-pattern.tsx +281 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/devtools-setup.tsx +248 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/error-boundary.tsx +243 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/package.json +31 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/provider-setup.tsx +50 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/query-client-config.ts +72 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/use-infinite-query.tsx +214 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/use-mutation-basic.tsx +201 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/use-mutation-optimistic.tsx +234 -0
- package/dist/skills/frameworks/trpc/tanstack-query/templates/use-query-basic.tsx +119 -0
- package/dist/skills/frameworks/turborepo/turborepo/SKILL.md +914 -0
- package/dist/skills/frameworks/turborepo/turborepo/command/turborepo.md +70 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/best-practices/RULE.md +241 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/best-practices/dependencies.md +246 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/best-practices/packages.md +335 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/best-practices/structure.md +270 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/boundaries/RULE.md +126 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/caching/RULE.md +107 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/caching/gotchas.md +169 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/caching/remote-cache.md +127 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/ci/RULE.md +79 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/ci/github-actions.md +162 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/ci/patterns.md +145 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/ci/vercel.md +103 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/cli/RULE.md +100 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/cli/commands.md +297 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/configuration/RULE.md +211 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/configuration/global-options.md +187 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/configuration/gotchas.md +348 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/configuration/tasks.md +285 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/environment/RULE.md +96 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/environment/gotchas.md +141 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/environment/modes.md +101 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/filtering/RULE.md +148 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/filtering/patterns.md +152 -0
- package/dist/skills/frameworks/turborepo/turborepo/references/watch/RULE.md +99 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/README.md +20 -0
- package/docs/reference/dp-requirements.md +210 -0
- package/docs/runbooks/dp-cli-scaffolding.md +187 -0
- package/package.json +24 -50
- package/.eslintignore +0 -4
- package/.eslintrc +0 -127
- package/.prettierignore +0 -4
- package/.prettierrc +0 -7
- package/bin/abstractions/builder.d.ts +0 -4
- package/bin/abstractions/builder.js +0 -7
- package/bin/builders/dotnet/base.d.ts +0 -18
- package/bin/builders/dotnet/base.js +0 -47
- package/bin/builders/dotnet/entity-configuration/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-configuration/index.js +0 -29
- package/bin/builders/dotnet/entity-converter/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-converter/index.js +0 -29
- package/bin/builders/dotnet/entity-model-create/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-model-create/index.js +0 -29
- package/bin/builders/dotnet/entity-model-dto/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-model-dto/index.js +0 -29
- package/bin/builders/dotnet/entity-model-list-item-dto/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-model-list-item-dto/index.js +0 -29
- package/bin/builders/dotnet/entity-model-update/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-model-update/index.js +0 -29
- package/bin/builders/dotnet/entity-search-parameters/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-search-parameters/index.js +0 -29
- package/bin/builders/dotnet/entity-search-query-builder/index.d.ts +0 -8
- package/bin/builders/dotnet/entity-search-query-builder/index.js +0 -29
- package/bin/builders/dotnet/firestore-connector/index.d.ts +0 -8
- package/bin/builders/dotnet/firestore-connector/index.js +0 -29
- package/bin/builders/dotnet/firestore-mapper/index.d.ts +0 -8
- package/bin/builders/dotnet/firestore-mapper/index.js +0 -29
- package/bin/builders/dotnet/types.d.ts +0 -10
- package/bin/builders/dotnet/types.js +0 -3
- package/bin/commands/entity-add/__test__/dotnet/common.d.ts +0 -2
- package/bin/commands/entity-add/__test__/dotnet/common.js +0 -11
- package/bin/commands/entity-add/__test__/dotnet/render.configuration.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.configuration.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.converter.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.converter.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.firestore-connector.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.firestore-connector.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.firestore-mapper.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.firestore-mapper.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.model-create.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.model-create.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.model-dto.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.model-dto.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.model-list-item-dto.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.model-list-item-dto.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.model-update.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.model-update.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.search-parameters.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.search-parameters.spec.js +0 -22
- package/bin/commands/entity-add/__test__/dotnet/render.search-query.spec.d.ts +0 -1
- package/bin/commands/entity-add/__test__/dotnet/render.search-query.spec.js +0 -22
- package/bin/commands/entity-add/index.d.ts +0 -26
- package/bin/commands/entity-add/index.js +0 -44
- package/bin/logging/index.d.ts +0 -8
- package/bin/logging/index.js +0 -28
- package/bin/providers/rendering/base.d.ts +0 -3
- package/bin/providers/rendering/base.js +0 -7
- package/bin/providers/rendering/factory.d.ts +0 -5
- package/bin/providers/rendering/factory.js +0 -16
- package/bin/providers/rendering/handlebars.d.ts +0 -4
- package/bin/providers/rendering/handlebars.js +0 -16
- package/bin/renderer/index.d.ts +0 -6
- package/bin/renderer/index.js +0 -27
- package/bin/run.d.ts +0 -2
- package/bin/run.js +0 -70
- package/bin/services/folders.d.ts +0 -1
- package/bin/services/folders.js +0 -31
- package/bin/types/commands.d.ts +0 -5
- package/bin/types/commands.js +0 -3
- package/bin/utils/collections.d.ts +0 -1
- package/bin/utils/collections.js +0 -6
- package/bin/utils/files.d.ts +0 -3
- package/bin/utils/files.js +0 -19
- package/bin/utils/strings.d.ts +0 -4
- package/bin/utils/strings.js +0 -22
- package/bin/utils/text.d.ts +0 -1
- package/bin/utils/text.js +0 -8
- package/em-cli +0 -0
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.configuration.spec.ts.snap +0 -49
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.converter.spec.ts.snap +0 -53
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.firestore-connector.spec.ts.snap +0 -20
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.firestore-mapper.spec.ts.snap +0 -29
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.model-create.spec.ts.snap +0 -10
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.model-dto.spec.ts.snap +0 -9
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.model-list-item-dto.spec.ts.snap +0 -11
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.model-update.spec.ts.snap +0 -11
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.search-parameters.spec.ts.snap +0 -29
- package/src/commands/entity-add/__test__/dotnet/__snapshots__/render.search-query.spec.ts.snap +0 -42
- package/templates/dotnet/NewEntity/Configuration/<PluralizedEntity>Configuration.cs.template +0 -48
- package/templates/dotnet/NewEntity/Connectors/<PluralizedEntity>FirestoreConnector.cs.template +0 -15
- package/templates/dotnet/NewEntity/Converters/<PluralizedEntity>Converter.cs.template +0 -48
- package/templates/dotnet/NewEntity/Mappers/<PluralizedEntity>FirestoreMapper.cs.template +0 -25
- package/templates/dotnet/NewEntity/Models/<Entity>CreateInput.cs.template +0 -6
- package/templates/dotnet/NewEntity/Models/<Entity>Dto.cs.template +0 -5
- package/templates/dotnet/NewEntity/Models/<Entity>ListItemDto.cs.template +0 -6
- package/templates/dotnet/NewEntity/Models/<Entity>UpdateInput.cs.template +0 -6
- package/templates/dotnet/NewEntity/Search/<PluralizedEntity>QueryBuilder.cs.template +0 -38
- package/templates/dotnet/NewEntity/Search/<PluralizedEntity>SearchParameters.cs.template +0 -24
- package/tsconfig.json +0 -25
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-best-practices
|
|
3
|
+
description: NestJS best practices and architecture patterns for building production-ready applications. This skill should be used when writing, reviewing, or refactoring NestJS code to ensure proper patterns for modules, dependency injection, security, and performance.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Kadajett
|
|
7
|
+
version: "1.1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# NestJS Best Practices
|
|
11
|
+
|
|
12
|
+
Comprehensive best practices guide for NestJS applications. Contains 40 rules across 10 categories, prioritized by impact to guide automated refactoring and code generation.
|
|
13
|
+
|
|
14
|
+
## When to Apply
|
|
15
|
+
|
|
16
|
+
Reference these guidelines when:
|
|
17
|
+
|
|
18
|
+
- Writing new NestJS modules, controllers, or services
|
|
19
|
+
- Implementing authentication and authorization
|
|
20
|
+
- Reviewing code for architecture and security issues
|
|
21
|
+
- Refactoring existing NestJS codebases
|
|
22
|
+
- Optimizing performance or database queries
|
|
23
|
+
- Building microservices architectures
|
|
24
|
+
|
|
25
|
+
## Rule Categories by Priority
|
|
26
|
+
|
|
27
|
+
| Priority | Category | Impact | Prefix |
|
|
28
|
+
|----------|----------|--------|--------|
|
|
29
|
+
| 1 | Architecture | CRITICAL | `arch-` |
|
|
30
|
+
| 2 | Dependency Injection | CRITICAL | `di-` |
|
|
31
|
+
| 3 | Error Handling | HIGH | `error-` |
|
|
32
|
+
| 4 | Security | HIGH | `security-` |
|
|
33
|
+
| 5 | Performance | HIGH | `perf-` |
|
|
34
|
+
| 6 | Testing | MEDIUM-HIGH | `test-` |
|
|
35
|
+
| 7 | Database & ORM | MEDIUM-HIGH | `db-` |
|
|
36
|
+
| 8 | API Design | MEDIUM | `api-` |
|
|
37
|
+
| 9 | Microservices | MEDIUM | `micro-` |
|
|
38
|
+
| 10 | DevOps & Deployment | LOW-MEDIUM | `devops-` |
|
|
39
|
+
|
|
40
|
+
## Quick Reference
|
|
41
|
+
|
|
42
|
+
### 1. Architecture (CRITICAL)
|
|
43
|
+
|
|
44
|
+
- `arch-avoid-circular-deps` - Avoid circular module dependencies
|
|
45
|
+
- `arch-feature-modules` - Organize by feature, not technical layer
|
|
46
|
+
- `arch-module-sharing` - Proper module exports/imports, avoid duplicate providers
|
|
47
|
+
- `arch-single-responsibility` - Focused services over "god services"
|
|
48
|
+
- `arch-use-repository-pattern` - Abstract database logic for testability
|
|
49
|
+
- `arch-use-events` - Event-driven architecture for decoupling
|
|
50
|
+
|
|
51
|
+
### 2. Dependency Injection (CRITICAL)
|
|
52
|
+
|
|
53
|
+
- `di-avoid-service-locator` - Avoid service locator anti-pattern
|
|
54
|
+
- `di-interface-segregation` - Interface Segregation Principle (ISP)
|
|
55
|
+
- `di-liskov-substitution` - Liskov Substitution Principle (LSP)
|
|
56
|
+
- `di-prefer-constructor-injection` - Constructor over property injection
|
|
57
|
+
- `di-scope-awareness` - Understand singleton/request/transient scopes
|
|
58
|
+
- `di-use-interfaces-tokens` - Use injection tokens for interfaces
|
|
59
|
+
|
|
60
|
+
### 3. Error Handling (HIGH)
|
|
61
|
+
|
|
62
|
+
- `error-use-exception-filters` - Centralized exception handling
|
|
63
|
+
- `error-throw-http-exceptions` - Use NestJS HTTP exceptions
|
|
64
|
+
- `error-handle-async-errors` - Handle async errors properly
|
|
65
|
+
|
|
66
|
+
### 4. Security (HIGH)
|
|
67
|
+
|
|
68
|
+
- `security-auth-jwt` - Secure JWT authentication
|
|
69
|
+
- `security-validate-all-input` - Validate with class-validator
|
|
70
|
+
- `security-use-guards` - Authentication and authorization guards
|
|
71
|
+
- `security-sanitize-output` - Prevent XSS attacks
|
|
72
|
+
- `security-rate-limiting` - Implement rate limiting
|
|
73
|
+
|
|
74
|
+
### 5. Performance (HIGH)
|
|
75
|
+
|
|
76
|
+
- `perf-async-hooks` - Proper async lifecycle hooks
|
|
77
|
+
- `perf-use-caching` - Implement caching strategies
|
|
78
|
+
- `perf-optimize-database` - Optimize database queries
|
|
79
|
+
- `perf-lazy-loading` - Lazy load modules for faster startup
|
|
80
|
+
|
|
81
|
+
### 6. Testing (MEDIUM-HIGH)
|
|
82
|
+
|
|
83
|
+
- `test-use-testing-module` - Use NestJS testing utilities
|
|
84
|
+
- `test-e2e-supertest` - E2E testing with Supertest
|
|
85
|
+
- `test-mock-external-services` - Mock external dependencies
|
|
86
|
+
|
|
87
|
+
### 7. Database & ORM (MEDIUM-HIGH)
|
|
88
|
+
|
|
89
|
+
- `db-use-transactions` - Transaction management
|
|
90
|
+
- `db-avoid-n-plus-one` - Avoid N+1 query problems
|
|
91
|
+
- `db-use-migrations` - Use migrations for schema changes
|
|
92
|
+
|
|
93
|
+
### 8. API Design (MEDIUM)
|
|
94
|
+
|
|
95
|
+
- `api-use-dto-serialization` - DTO and response serialization
|
|
96
|
+
- `api-use-interceptors` - Cross-cutting concerns
|
|
97
|
+
- `api-versioning` - API versioning strategies
|
|
98
|
+
- `api-use-pipes` - Input transformation with pipes
|
|
99
|
+
|
|
100
|
+
### 9. Microservices (MEDIUM)
|
|
101
|
+
|
|
102
|
+
- `micro-use-patterns` - Message and event patterns
|
|
103
|
+
- `micro-use-health-checks` - Health checks for orchestration
|
|
104
|
+
- `micro-use-queues` - Background job processing
|
|
105
|
+
|
|
106
|
+
### 10. DevOps & Deployment (LOW-MEDIUM)
|
|
107
|
+
|
|
108
|
+
- `devops-use-config-module` - Environment configuration
|
|
109
|
+
- `devops-use-logging` - Structured logging
|
|
110
|
+
- `devops-graceful-shutdown` - Zero-downtime deployments
|
|
111
|
+
|
|
112
|
+
## How to Use
|
|
113
|
+
|
|
114
|
+
Read individual rule files for detailed explanations and code examples:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
rules/arch-avoid-circular-deps.md
|
|
118
|
+
rules/security-validate-all-input.md
|
|
119
|
+
rules/_sections.md
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Each rule file contains:
|
|
123
|
+
- Brief explanation of why it matters
|
|
124
|
+
- Incorrect code example with explanation
|
|
125
|
+
- Correct code example with explanation
|
|
126
|
+
- Additional context and references
|
|
127
|
+
|
|
128
|
+
## Full Compiled Document
|
|
129
|
+
|
|
130
|
+
For the complete guide with all rules expanded: `AGENTS.md`
|
package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/api-use-dto-serialization.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use DTOs and Serialization for API Responses
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Response DTOs prevent accidental data exposure and ensure consistency
|
|
5
|
+
tags: api, dto, serialization, class-transformer
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use DTOs and Serialization for API Responses
|
|
9
|
+
|
|
10
|
+
Never return entity objects directly from controllers. Use response DTOs with class-transformer's `@Exclude()` and `@Expose()` decorators to control exactly what data is sent to clients. This prevents accidental exposure of sensitive fields and provides a stable API contract.
|
|
11
|
+
|
|
12
|
+
**Incorrect (returning entities directly or manual spreading):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Return entities directly
|
|
16
|
+
@Controller('users')
|
|
17
|
+
export class UsersController {
|
|
18
|
+
@Get(':id')
|
|
19
|
+
async findOne(@Param('id') id: string): Promise<User> {
|
|
20
|
+
return this.usersService.findById(id);
|
|
21
|
+
// Returns: { id, email, passwordHash, ssn, internalNotes, ... }
|
|
22
|
+
// Exposes sensitive data!
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Manual object spreading (error-prone)
|
|
27
|
+
@Get(':id')
|
|
28
|
+
async findOne(@Param('id') id: string) {
|
|
29
|
+
const user = await this.usersService.findById(id);
|
|
30
|
+
return {
|
|
31
|
+
id: user.id,
|
|
32
|
+
email: user.email,
|
|
33
|
+
name: user.name,
|
|
34
|
+
// Easy to forget to exclude sensitive fields
|
|
35
|
+
// Hard to maintain across endpoints
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Correct (use class-transformer with @Exclude and response DTOs):**
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Enable class-transformer globally
|
|
44
|
+
async function bootstrap() {
|
|
45
|
+
const app = await NestFactory.create(AppModule);
|
|
46
|
+
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
|
|
47
|
+
await app.listen(3000);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Entity with serialization control
|
|
51
|
+
@Entity()
|
|
52
|
+
export class User {
|
|
53
|
+
@PrimaryGeneratedColumn('uuid')
|
|
54
|
+
id: string;
|
|
55
|
+
|
|
56
|
+
@Column()
|
|
57
|
+
email: string;
|
|
58
|
+
|
|
59
|
+
@Column()
|
|
60
|
+
name: string;
|
|
61
|
+
|
|
62
|
+
@Column()
|
|
63
|
+
@Exclude() // Never include in responses
|
|
64
|
+
passwordHash: string;
|
|
65
|
+
|
|
66
|
+
@Column({ nullable: true })
|
|
67
|
+
@Exclude()
|
|
68
|
+
ssn: string;
|
|
69
|
+
|
|
70
|
+
@Column({ default: false })
|
|
71
|
+
@Exclude({ toPlainOnly: true }) // Exclude from response, allow in requests
|
|
72
|
+
isAdmin: boolean;
|
|
73
|
+
|
|
74
|
+
@CreateDateColumn()
|
|
75
|
+
createdAt: Date;
|
|
76
|
+
|
|
77
|
+
@Column()
|
|
78
|
+
@Exclude()
|
|
79
|
+
internalNotes: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Now returning entity is safe
|
|
83
|
+
@Controller('users')
|
|
84
|
+
export class UsersController {
|
|
85
|
+
@Get(':id')
|
|
86
|
+
async findOne(@Param('id') id: string): Promise<User> {
|
|
87
|
+
return this.usersService.findById(id);
|
|
88
|
+
// Returns: { id, email, name, createdAt }
|
|
89
|
+
// Sensitive fields excluded automatically
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// For different response shapes, use explicit DTOs
|
|
94
|
+
export class UserResponseDto {
|
|
95
|
+
@Expose()
|
|
96
|
+
id: string;
|
|
97
|
+
|
|
98
|
+
@Expose()
|
|
99
|
+
email: string;
|
|
100
|
+
|
|
101
|
+
@Expose()
|
|
102
|
+
name: string;
|
|
103
|
+
|
|
104
|
+
@Expose()
|
|
105
|
+
@Transform(({ obj }) => obj.posts?.length || 0)
|
|
106
|
+
postCount: number;
|
|
107
|
+
|
|
108
|
+
constructor(partial: Partial<User>) {
|
|
109
|
+
Object.assign(this, partial);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export class UserDetailResponseDto extends UserResponseDto {
|
|
114
|
+
@Expose()
|
|
115
|
+
createdAt: Date;
|
|
116
|
+
|
|
117
|
+
@Expose()
|
|
118
|
+
@Type(() => PostResponseDto)
|
|
119
|
+
posts: PostResponseDto[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Controller with explicit DTOs
|
|
123
|
+
@Controller('users')
|
|
124
|
+
export class UsersController {
|
|
125
|
+
@Get()
|
|
126
|
+
@SerializeOptions({ type: UserResponseDto })
|
|
127
|
+
async findAll(): Promise<UserResponseDto[]> {
|
|
128
|
+
const users = await this.usersService.findAll();
|
|
129
|
+
return users.map(u => plainToInstance(UserResponseDto, u));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@Get(':id')
|
|
133
|
+
async findOne(@Param('id') id: string): Promise<UserDetailResponseDto> {
|
|
134
|
+
const user = await this.usersService.findByIdWithPosts(id);
|
|
135
|
+
return plainToInstance(UserDetailResponseDto, user, {
|
|
136
|
+
excludeExtraneousValues: true,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Groups for conditional serialization
|
|
142
|
+
export class UserDto {
|
|
143
|
+
@Expose()
|
|
144
|
+
id: string;
|
|
145
|
+
|
|
146
|
+
@Expose()
|
|
147
|
+
name: string;
|
|
148
|
+
|
|
149
|
+
@Expose({ groups: ['admin'] })
|
|
150
|
+
email: string;
|
|
151
|
+
|
|
152
|
+
@Expose({ groups: ['admin'] })
|
|
153
|
+
createdAt: Date;
|
|
154
|
+
|
|
155
|
+
@Expose({ groups: ['admin', 'owner'] })
|
|
156
|
+
settings: UserSettings;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@Controller('users')
|
|
160
|
+
export class UsersController {
|
|
161
|
+
@Get()
|
|
162
|
+
@SerializeOptions({ groups: ['public'] })
|
|
163
|
+
async findAllPublic(): Promise<UserDto[]> {
|
|
164
|
+
// Returns: { id, name }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@Get('admin')
|
|
168
|
+
@UseGuards(AdminGuard)
|
|
169
|
+
@SerializeOptions({ groups: ['admin'] })
|
|
170
|
+
async findAllAdmin(): Promise<UserDto[]> {
|
|
171
|
+
// Returns: { id, name, email, createdAt }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@Get('me')
|
|
175
|
+
@SerializeOptions({ groups: ['owner'] })
|
|
176
|
+
async getProfile(@CurrentUser() user: User): Promise<UserDto> {
|
|
177
|
+
// Returns: { id, name, settings }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Reference: [NestJS Serialization](https://docs.nestjs.com/techniques/serialization)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Interceptors for Cross-Cutting Concerns
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: Interceptors provide clean separation for cross-cutting logic
|
|
5
|
+
tags: api, interceptors, logging, caching
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Interceptors for Cross-Cutting Concerns
|
|
9
|
+
|
|
10
|
+
Interceptors can transform responses, add logging, handle caching, and measure performance without polluting your business logic. They wrap the route handler execution, giving you access to both the request and response streams.
|
|
11
|
+
|
|
12
|
+
**Incorrect (logging and transformation in every method):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Logging in every controller method
|
|
16
|
+
@Controller('users')
|
|
17
|
+
export class UsersController {
|
|
18
|
+
@Get()
|
|
19
|
+
async findAll(): Promise<User[]> {
|
|
20
|
+
const start = Date.now();
|
|
21
|
+
this.logger.log('findAll called');
|
|
22
|
+
|
|
23
|
+
const users = await this.usersService.findAll();
|
|
24
|
+
|
|
25
|
+
this.logger.log(`findAll completed in ${Date.now() - start}ms`);
|
|
26
|
+
return users;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Get(':id')
|
|
30
|
+
async findOne(@Param('id') id: string): Promise<User> {
|
|
31
|
+
const start = Date.now();
|
|
32
|
+
this.logger.log(`findOne called with id: ${id}`);
|
|
33
|
+
|
|
34
|
+
const user = await this.usersService.findOne(id);
|
|
35
|
+
|
|
36
|
+
this.logger.log(`findOne completed in ${Date.now() - start}ms`);
|
|
37
|
+
return user;
|
|
38
|
+
}
|
|
39
|
+
// Repeated in every method!
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Manual response wrapping
|
|
43
|
+
@Get()
|
|
44
|
+
async findAll(): Promise<{ data: User[]; meta: Meta }> {
|
|
45
|
+
const users = await this.usersService.findAll();
|
|
46
|
+
return {
|
|
47
|
+
data: users,
|
|
48
|
+
meta: { timestamp: new Date(), count: users.length },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Correct (use interceptors for cross-cutting concerns):**
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Logging interceptor
|
|
57
|
+
@Injectable()
|
|
58
|
+
export class LoggingInterceptor implements NestInterceptor {
|
|
59
|
+
private readonly logger = new Logger('HTTP');
|
|
60
|
+
|
|
61
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
62
|
+
const request = context.switchToHttp().getRequest();
|
|
63
|
+
const { method, url, body } = request;
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
|
|
66
|
+
return next.handle().pipe(
|
|
67
|
+
tap({
|
|
68
|
+
next: (data) => {
|
|
69
|
+
const response = context.switchToHttp().getResponse();
|
|
70
|
+
this.logger.log(
|
|
71
|
+
`${method} ${url} ${response.statusCode} - ${Date.now() - now}ms`,
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
error: (error) => {
|
|
75
|
+
this.logger.error(
|
|
76
|
+
`${method} ${url} ${error.status || 500} - ${Date.now() - now}ms`,
|
|
77
|
+
error.stack,
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Response transformation interceptor
|
|
86
|
+
@Injectable()
|
|
87
|
+
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
|
|
88
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
|
|
89
|
+
return next.handle().pipe(
|
|
90
|
+
map((data) => ({
|
|
91
|
+
data,
|
|
92
|
+
meta: {
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
path: context.switchToHttp().getRequest().url,
|
|
95
|
+
},
|
|
96
|
+
})),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Timeout interceptor
|
|
102
|
+
@Injectable()
|
|
103
|
+
export class TimeoutInterceptor implements NestInterceptor {
|
|
104
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
105
|
+
return next.handle().pipe(
|
|
106
|
+
timeout(5000),
|
|
107
|
+
catchError((err) => {
|
|
108
|
+
if (err instanceof TimeoutError) {
|
|
109
|
+
throw new RequestTimeoutException('Request timed out');
|
|
110
|
+
}
|
|
111
|
+
throw err;
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Apply globally or per-controller
|
|
118
|
+
@Module({
|
|
119
|
+
providers: [
|
|
120
|
+
{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },
|
|
121
|
+
{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor },
|
|
122
|
+
],
|
|
123
|
+
})
|
|
124
|
+
export class AppModule {}
|
|
125
|
+
|
|
126
|
+
// Or per-controller
|
|
127
|
+
@Controller('users')
|
|
128
|
+
@UseInterceptors(LoggingInterceptor)
|
|
129
|
+
export class UsersController {
|
|
130
|
+
@Get()
|
|
131
|
+
async findAll(): Promise<User[]> {
|
|
132
|
+
// Clean business logic only
|
|
133
|
+
return this.usersService.findAll();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Custom cache interceptor with TTL
|
|
138
|
+
@Injectable()
|
|
139
|
+
export class HttpCacheInterceptor implements NestInterceptor {
|
|
140
|
+
constructor(
|
|
141
|
+
private cacheManager: Cache,
|
|
142
|
+
private reflector: Reflector,
|
|
143
|
+
) {}
|
|
144
|
+
|
|
145
|
+
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
|
|
146
|
+
const request = context.switchToHttp().getRequest();
|
|
147
|
+
|
|
148
|
+
// Only cache GET requests
|
|
149
|
+
if (request.method !== 'GET') {
|
|
150
|
+
return next.handle();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const cacheKey = this.generateKey(request);
|
|
154
|
+
const ttl = this.reflector.get<number>('cacheTTL', context.getHandler()) || 300;
|
|
155
|
+
|
|
156
|
+
const cached = await this.cacheManager.get(cacheKey);
|
|
157
|
+
if (cached) {
|
|
158
|
+
return of(cached);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return next.handle().pipe(
|
|
162
|
+
tap((response) => {
|
|
163
|
+
this.cacheManager.set(cacheKey, response, ttl);
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private generateKey(request: Request): string {
|
|
169
|
+
return `cache:${request.url}:${JSON.stringify(request.query)}`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Usage with custom TTL
|
|
174
|
+
@Get()
|
|
175
|
+
@SetMetadata('cacheTTL', 600)
|
|
176
|
+
@UseInterceptors(HttpCacheInterceptor)
|
|
177
|
+
async findAll(): Promise<User[]> {
|
|
178
|
+
return this.usersService.findAll();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Error mapping interceptor
|
|
182
|
+
@Injectable()
|
|
183
|
+
export class ErrorMappingInterceptor implements NestInterceptor {
|
|
184
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
185
|
+
return next.handle().pipe(
|
|
186
|
+
catchError((error) => {
|
|
187
|
+
if (error instanceof EntityNotFoundError) {
|
|
188
|
+
throw new NotFoundException(error.message);
|
|
189
|
+
}
|
|
190
|
+
if (error instanceof QueryFailedError) {
|
|
191
|
+
if (error.message.includes('duplicate')) {
|
|
192
|
+
throw new ConflictException('Resource already exists');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
throw error;
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Reference: [NestJS Interceptors](https://docs.nestjs.com/interceptors)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Pipes for Input Transformation
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Pipes ensure clean, validated data reaches your handlers
|
|
5
|
+
tags: api, pipes, validation, transformation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Pipes for Input Transformation
|
|
9
|
+
|
|
10
|
+
Use built-in pipes like `ParseIntPipe`, `ParseUUIDPipe`, and `DefaultValuePipe` for common transformations. Create custom pipes for business-specific transformations. Pipes separate validation/transformation logic from controllers.
|
|
11
|
+
|
|
12
|
+
**Incorrect (manual type parsing in handlers):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Manual type parsing in handlers
|
|
16
|
+
@Controller('users')
|
|
17
|
+
export class UsersController {
|
|
18
|
+
@Get(':id')
|
|
19
|
+
async findOne(@Param('id') id: string): Promise<User> {
|
|
20
|
+
// Manual validation in every handler
|
|
21
|
+
const uuid = id.trim();
|
|
22
|
+
if (!isUUID(uuid)) {
|
|
23
|
+
throw new BadRequestException('Invalid UUID');
|
|
24
|
+
}
|
|
25
|
+
return this.usersService.findOne(uuid);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Get()
|
|
29
|
+
async findAll(
|
|
30
|
+
@Query('page') page: string,
|
|
31
|
+
@Query('limit') limit: string,
|
|
32
|
+
): Promise<User[]> {
|
|
33
|
+
// Manual parsing and defaults
|
|
34
|
+
const pageNum = parseInt(page) || 1;
|
|
35
|
+
const limitNum = parseInt(limit) || 10;
|
|
36
|
+
return this.usersService.findAll(pageNum, limitNum);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Type coercion without validation
|
|
41
|
+
@Get()
|
|
42
|
+
async search(@Query('price') price: string): Promise<Product[]> {
|
|
43
|
+
const priceNum = +price; // NaN if invalid, no error
|
|
44
|
+
return this.productsService.findByPrice(priceNum);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Correct (use built-in and custom pipes):**
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// Use built-in pipes for common transformations
|
|
52
|
+
@Controller('users')
|
|
53
|
+
export class UsersController {
|
|
54
|
+
@Get(':id')
|
|
55
|
+
async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {
|
|
56
|
+
// id is guaranteed to be a valid UUID
|
|
57
|
+
return this.usersService.findOne(id);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Get()
|
|
61
|
+
async findAll(
|
|
62
|
+
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
|
|
63
|
+
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
|
|
64
|
+
): Promise<User[]> {
|
|
65
|
+
// Automatic defaults and type conversion
|
|
66
|
+
return this.usersService.findAll(page, limit);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Get('by-status/:status')
|
|
70
|
+
async findByStatus(
|
|
71
|
+
@Param('status', new ParseEnumPipe(UserStatus)) status: UserStatus,
|
|
72
|
+
): Promise<User[]> {
|
|
73
|
+
return this.usersService.findByStatus(status);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Custom pipe for business logic
|
|
78
|
+
@Injectable()
|
|
79
|
+
export class ParseDatePipe implements PipeTransform<string, Date> {
|
|
80
|
+
transform(value: string): Date {
|
|
81
|
+
const date = new Date(value);
|
|
82
|
+
if (isNaN(date.getTime())) {
|
|
83
|
+
throw new BadRequestException('Invalid date format');
|
|
84
|
+
}
|
|
85
|
+
return date;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@Get('reports')
|
|
90
|
+
async getReports(
|
|
91
|
+
@Query('from', ParseDatePipe) from: Date,
|
|
92
|
+
@Query('to', ParseDatePipe) to: Date,
|
|
93
|
+
): Promise<Report[]> {
|
|
94
|
+
return this.reportsService.findBetween(from, to);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Custom transformation pipes
|
|
98
|
+
@Injectable()
|
|
99
|
+
export class NormalizeEmailPipe implements PipeTransform<string, string> {
|
|
100
|
+
transform(value: string): string {
|
|
101
|
+
if (!value) return value;
|
|
102
|
+
return value.trim().toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Parse comma-separated values
|
|
107
|
+
@Injectable()
|
|
108
|
+
export class ParseArrayPipe implements PipeTransform<string, string[]> {
|
|
109
|
+
transform(value: string): string[] {
|
|
110
|
+
if (!value) return [];
|
|
111
|
+
return value.split(',').map((v) => v.trim()).filter(Boolean);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Get('products')
|
|
116
|
+
async findProducts(
|
|
117
|
+
@Query('ids', ParseArrayPipe) ids: string[],
|
|
118
|
+
@Query('email', NormalizeEmailPipe) email: string,
|
|
119
|
+
): Promise<Product[]> {
|
|
120
|
+
// ids is already an array, email is normalized
|
|
121
|
+
return this.productsService.findByIds(ids);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Sanitize HTML input
|
|
125
|
+
@Injectable()
|
|
126
|
+
export class SanitizeHtmlPipe implements PipeTransform<string, string> {
|
|
127
|
+
transform(value: string): string {
|
|
128
|
+
if (!value) return value;
|
|
129
|
+
return sanitizeHtml(value, { allowedTags: [] });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Global validation pipe with transformation
|
|
134
|
+
app.useGlobalPipes(
|
|
135
|
+
new ValidationPipe({
|
|
136
|
+
whitelist: true, // Strip non-DTO properties
|
|
137
|
+
transform: true, // Auto-transform to DTO types
|
|
138
|
+
transformOptions: {
|
|
139
|
+
enableImplicitConversion: true, // Convert query strings to numbers
|
|
140
|
+
},
|
|
141
|
+
forbidNonWhitelisted: true, // Throw on extra properties
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// DTO with transformation decorators
|
|
146
|
+
export class FindProductsDto {
|
|
147
|
+
@IsOptional()
|
|
148
|
+
@Type(() => Number)
|
|
149
|
+
@IsInt()
|
|
150
|
+
@Min(1)
|
|
151
|
+
page?: number = 1;
|
|
152
|
+
|
|
153
|
+
@IsOptional()
|
|
154
|
+
@Type(() => Number)
|
|
155
|
+
@IsInt()
|
|
156
|
+
@Min(1)
|
|
157
|
+
@Max(100)
|
|
158
|
+
limit?: number = 10;
|
|
159
|
+
|
|
160
|
+
@IsOptional()
|
|
161
|
+
@Transform(({ value }) => value?.toLowerCase())
|
|
162
|
+
@IsString()
|
|
163
|
+
search?: string;
|
|
164
|
+
|
|
165
|
+
@IsOptional()
|
|
166
|
+
@Transform(({ value }) => value?.split(','))
|
|
167
|
+
@IsArray()
|
|
168
|
+
@IsString({ each: true })
|
|
169
|
+
categories?: string[];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@Get()
|
|
173
|
+
async findAll(@Query() dto: FindProductsDto): Promise<Product[]> {
|
|
174
|
+
// dto is already transformed and validated
|
|
175
|
+
return this.productsService.findAll(dto);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Pipe error customization
|
|
179
|
+
@Injectable()
|
|
180
|
+
export class CustomParseIntPipe extends ParseIntPipe {
|
|
181
|
+
constructor() {
|
|
182
|
+
super({
|
|
183
|
+
exceptionFactory: (error) =>
|
|
184
|
+
new BadRequestException(`${error} must be a valid integer`),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Or use options on built-in pipes
|
|
190
|
+
@Get(':id')
|
|
191
|
+
async findOne(
|
|
192
|
+
@Param(
|
|
193
|
+
'id',
|
|
194
|
+
new ParseIntPipe({
|
|
195
|
+
errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE,
|
|
196
|
+
exceptionFactory: () => new NotAcceptableException('ID must be numeric'),
|
|
197
|
+
}),
|
|
198
|
+
)
|
|
199
|
+
id: number,
|
|
200
|
+
): Promise<Item> {
|
|
201
|
+
return this.itemsService.findOne(id);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Reference: [NestJS Pipes](https://docs.nestjs.com/pipes)
|