@punks/cli 0.1.15 → 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 -51
- 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,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Optimize Database Queries
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Database queries are typically the largest source of latency
|
|
5
|
+
tags: performance, database, queries, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Optimize Database Queries
|
|
9
|
+
|
|
10
|
+
Select only needed columns, use proper indexes, avoid over-fetching relations, and consider query performance when designing your data access. Most API slowness traces back to inefficient database queries.
|
|
11
|
+
|
|
12
|
+
**Incorrect (over-fetching data and missing indexes):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Select everything when you need few fields
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class UsersService {
|
|
18
|
+
async findAllEmails(): Promise<string[]> {
|
|
19
|
+
const users = await this.repo.find();
|
|
20
|
+
// Fetches ALL columns for ALL users
|
|
21
|
+
return users.map((u) => u.email);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async getUserSummary(id: string): Promise<UserSummary> {
|
|
25
|
+
const user = await this.repo.findOne({
|
|
26
|
+
where: { id },
|
|
27
|
+
relations: ['posts', 'posts.comments', 'posts.comments.author', 'followers'],
|
|
28
|
+
});
|
|
29
|
+
// Over-fetches massive relation tree
|
|
30
|
+
return { name: user.name, postCount: user.posts.length };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// No indexes on frequently queried columns
|
|
35
|
+
@Entity()
|
|
36
|
+
export class Order {
|
|
37
|
+
@Column()
|
|
38
|
+
userId: string; // No index - full table scan on every lookup
|
|
39
|
+
|
|
40
|
+
@Column()
|
|
41
|
+
status: string; // No index - slow status filtering
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Correct (select only needed data with proper indexes):**
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Select only needed columns
|
|
49
|
+
@Injectable()
|
|
50
|
+
export class UsersService {
|
|
51
|
+
async findAllEmails(): Promise<string[]> {
|
|
52
|
+
const users = await this.repo.find({
|
|
53
|
+
select: ['email'], // Only fetch email column
|
|
54
|
+
});
|
|
55
|
+
return users.map((u) => u.email);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Use QueryBuilder for complex selections
|
|
59
|
+
async getUserSummary(id: string): Promise<UserSummary> {
|
|
60
|
+
return this.repo
|
|
61
|
+
.createQueryBuilder('user')
|
|
62
|
+
.select('user.name', 'name')
|
|
63
|
+
.addSelect('COUNT(post.id)', 'postCount')
|
|
64
|
+
.leftJoin('user.posts', 'post')
|
|
65
|
+
.where('user.id = :id', { id })
|
|
66
|
+
.groupBy('user.id')
|
|
67
|
+
.getRawOne();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Fetch relations only when needed
|
|
71
|
+
async getFullProfile(id: string): Promise<User> {
|
|
72
|
+
return this.repo.findOne({
|
|
73
|
+
where: { id },
|
|
74
|
+
relations: ['posts'], // Only immediate relation
|
|
75
|
+
select: {
|
|
76
|
+
id: true,
|
|
77
|
+
name: true,
|
|
78
|
+
email: true,
|
|
79
|
+
posts: {
|
|
80
|
+
id: true,
|
|
81
|
+
title: true,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Add indexes on frequently queried columns
|
|
89
|
+
@Entity()
|
|
90
|
+
@Index(['userId'])
|
|
91
|
+
@Index(['status'])
|
|
92
|
+
@Index(['createdAt'])
|
|
93
|
+
@Index(['userId', 'status']) // Composite index for common query pattern
|
|
94
|
+
export class Order {
|
|
95
|
+
@PrimaryGeneratedColumn('uuid')
|
|
96
|
+
id: string;
|
|
97
|
+
|
|
98
|
+
@Column()
|
|
99
|
+
userId: string;
|
|
100
|
+
|
|
101
|
+
@Column()
|
|
102
|
+
status: string;
|
|
103
|
+
|
|
104
|
+
@CreateDateColumn()
|
|
105
|
+
createdAt: Date;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Always paginate large datasets
|
|
109
|
+
@Injectable()
|
|
110
|
+
export class OrdersService {
|
|
111
|
+
async findAll(page = 1, limit = 20): Promise<PaginatedResult<Order>> {
|
|
112
|
+
const [items, total] = await this.repo.findAndCount({
|
|
113
|
+
skip: (page - 1) * limit,
|
|
114
|
+
take: limit,
|
|
115
|
+
order: { createdAt: 'DESC' },
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
items,
|
|
120
|
+
meta: {
|
|
121
|
+
page,
|
|
122
|
+
limit,
|
|
123
|
+
total,
|
|
124
|
+
totalPages: Math.ceil(total / limit),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Reference: [TypeORM Query Builder](https://typeorm.io/select-query-builder)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Caching Strategically
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Dramatically reduces database load and response times
|
|
5
|
+
tags: performance, caching, redis, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Caching Strategically
|
|
9
|
+
|
|
10
|
+
Implement caching for expensive operations, frequently accessed data, and external API calls. Use NestJS CacheModule with appropriate TTLs and cache invalidation strategies. Don't cache everything - focus on high-impact areas.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no caching or caching everything):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// No caching for expensive, repeated queries
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class ProductsService {
|
|
18
|
+
async getPopular(): Promise<Product[]> {
|
|
19
|
+
// Runs complex aggregation query EVERY request
|
|
20
|
+
return this.productsRepo
|
|
21
|
+
.createQueryBuilder('p')
|
|
22
|
+
.leftJoin('p.orders', 'o')
|
|
23
|
+
.select('p.*, COUNT(o.id) as orderCount')
|
|
24
|
+
.groupBy('p.id')
|
|
25
|
+
.orderBy('orderCount', 'DESC')
|
|
26
|
+
.limit(20)
|
|
27
|
+
.getMany();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Cache everything without thought
|
|
32
|
+
@Injectable()
|
|
33
|
+
export class UsersService {
|
|
34
|
+
@CacheKey('users')
|
|
35
|
+
@CacheTTL(3600)
|
|
36
|
+
@UseInterceptors(CacheInterceptor)
|
|
37
|
+
async findAll(): Promise<User[]> {
|
|
38
|
+
// Caching user list for 1 hour is wrong if data changes frequently
|
|
39
|
+
return this.usersRepo.find();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Correct (strategic caching with proper invalidation):**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Setup caching module
|
|
48
|
+
@Module({
|
|
49
|
+
imports: [
|
|
50
|
+
CacheModule.registerAsync({
|
|
51
|
+
imports: [ConfigModule],
|
|
52
|
+
inject: [ConfigService],
|
|
53
|
+
useFactory: (config: ConfigService) => ({
|
|
54
|
+
stores: [
|
|
55
|
+
new KeyvRedis(config.get('REDIS_URL')),
|
|
56
|
+
],
|
|
57
|
+
ttl: 60 * 1000, // Default 60s
|
|
58
|
+
}),
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
export class AppModule {}
|
|
63
|
+
|
|
64
|
+
// Manual caching for granular control
|
|
65
|
+
@Injectable()
|
|
66
|
+
export class ProductsService {
|
|
67
|
+
constructor(
|
|
68
|
+
@Inject(CACHE_MANAGER) private cache: Cache,
|
|
69
|
+
private productsRepo: ProductRepository,
|
|
70
|
+
) {}
|
|
71
|
+
|
|
72
|
+
async getPopular(): Promise<Product[]> {
|
|
73
|
+
const cacheKey = 'products:popular';
|
|
74
|
+
|
|
75
|
+
// Try cache first
|
|
76
|
+
const cached = await this.cache.get<Product[]>(cacheKey);
|
|
77
|
+
if (cached) return cached;
|
|
78
|
+
|
|
79
|
+
// Cache miss - fetch and cache
|
|
80
|
+
const products = await this.fetchPopularProducts();
|
|
81
|
+
await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL
|
|
82
|
+
return products;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Invalidate cache on changes
|
|
86
|
+
async updateProduct(id: string, dto: UpdateProductDto): Promise<Product> {
|
|
87
|
+
const product = await this.productsRepo.save({ id, ...dto });
|
|
88
|
+
await this.cache.del('products:popular'); // Invalidate
|
|
89
|
+
return product;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Decorator-based caching with auto-interceptor
|
|
94
|
+
@Controller('categories')
|
|
95
|
+
@UseInterceptors(CacheInterceptor)
|
|
96
|
+
export class CategoriesController {
|
|
97
|
+
@Get()
|
|
98
|
+
@CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change
|
|
99
|
+
findAll(): Promise<Category[]> {
|
|
100
|
+
return this.categoriesService.findAll();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Get(':id')
|
|
104
|
+
@CacheTTL(60 * 1000) // 1 minute
|
|
105
|
+
@CacheKey('category')
|
|
106
|
+
findOne(@Param('id') id: string): Promise<Category> {
|
|
107
|
+
return this.categoriesService.findOne(id);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Event-based cache invalidation
|
|
112
|
+
@Injectable()
|
|
113
|
+
export class CacheInvalidationService {
|
|
114
|
+
constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}
|
|
115
|
+
|
|
116
|
+
@OnEvent('product.created')
|
|
117
|
+
@OnEvent('product.updated')
|
|
118
|
+
@OnEvent('product.deleted')
|
|
119
|
+
async invalidateProductCaches(event: ProductEvent) {
|
|
120
|
+
await Promise.all([
|
|
121
|
+
this.cache.del('products:popular'),
|
|
122
|
+
this.cache.del(`product:${event.productId}`),
|
|
123
|
+
]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Reference: [NestJS Caching](https://docs.nestjs.com/techniques/caching)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Implement Secure JWT Authentication
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: Essential for secure APIs
|
|
5
|
+
tags: security, jwt, authentication, tokens
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Implement Secure JWT Authentication
|
|
9
|
+
|
|
10
|
+
Use `@nestjs/jwt` with `@nestjs/passport` for authentication. Store secrets securely, use appropriate token lifetimes, implement refresh tokens, and validate tokens properly. Never expose sensitive data in JWT payloads.
|
|
11
|
+
|
|
12
|
+
**Incorrect (insecure JWT implementation):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Hardcode secrets
|
|
16
|
+
@Module({
|
|
17
|
+
imports: [
|
|
18
|
+
JwtModule.register({
|
|
19
|
+
secret: 'my-secret-key', // Exposed in code
|
|
20
|
+
signOptions: { expiresIn: '7d' }, // Too long
|
|
21
|
+
}),
|
|
22
|
+
],
|
|
23
|
+
})
|
|
24
|
+
export class AuthModule {}
|
|
25
|
+
|
|
26
|
+
// Store sensitive data in JWT
|
|
27
|
+
async login(user: User): Promise<{ accessToken: string }> {
|
|
28
|
+
const payload = {
|
|
29
|
+
sub: user.id,
|
|
30
|
+
email: user.email,
|
|
31
|
+
password: user.password, // NEVER include password!
|
|
32
|
+
ssn: user.ssn, // NEVER include sensitive data!
|
|
33
|
+
isAdmin: user.isAdmin, // Can be tampered if not verified
|
|
34
|
+
};
|
|
35
|
+
return { accessToken: this.jwtService.sign(payload) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Skip token validation
|
|
39
|
+
@Injectable()
|
|
40
|
+
export class JwtStrategy extends PassportStrategy(Strategy) {
|
|
41
|
+
constructor() {
|
|
42
|
+
super({
|
|
43
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
44
|
+
secretOrKey: 'my-secret',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async validate(payload: any): Promise<any> {
|
|
49
|
+
return payload; // No validation of user existence
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Correct (secure JWT with refresh tokens):**
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Secure JWT configuration
|
|
58
|
+
@Module({
|
|
59
|
+
imports: [
|
|
60
|
+
JwtModule.registerAsync({
|
|
61
|
+
imports: [ConfigModule],
|
|
62
|
+
inject: [ConfigService],
|
|
63
|
+
useFactory: (config: ConfigService) => ({
|
|
64
|
+
secret: config.get<string>('JWT_SECRET'),
|
|
65
|
+
signOptions: {
|
|
66
|
+
expiresIn: '15m', // Short-lived access tokens
|
|
67
|
+
issuer: config.get<string>('JWT_ISSUER'),
|
|
68
|
+
audience: config.get<string>('JWT_AUDIENCE'),
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
}),
|
|
72
|
+
PassportModule.register({ defaultStrategy: 'jwt' }),
|
|
73
|
+
],
|
|
74
|
+
})
|
|
75
|
+
export class AuthModule {}
|
|
76
|
+
|
|
77
|
+
// Minimal JWT payload
|
|
78
|
+
@Injectable()
|
|
79
|
+
export class AuthService {
|
|
80
|
+
async login(user: User): Promise<TokenResponse> {
|
|
81
|
+
// Only include necessary, non-sensitive data
|
|
82
|
+
const payload: JwtPayload = {
|
|
83
|
+
sub: user.id,
|
|
84
|
+
email: user.email,
|
|
85
|
+
roles: user.roles,
|
|
86
|
+
iat: Math.floor(Date.now() / 1000),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const accessToken = this.jwtService.sign(payload);
|
|
90
|
+
const refreshToken = await this.createRefreshToken(user.id);
|
|
91
|
+
|
|
92
|
+
return { accessToken, refreshToken, expiresIn: 900 };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private async createRefreshToken(userId: string): Promise<string> {
|
|
96
|
+
const token = randomBytes(32).toString('hex');
|
|
97
|
+
const hashedToken = await bcrypt.hash(token, 10);
|
|
98
|
+
|
|
99
|
+
await this.refreshTokenRepo.save({
|
|
100
|
+
userId,
|
|
101
|
+
token: hashedToken,
|
|
102
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return token;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Proper JWT strategy with validation
|
|
110
|
+
@Injectable()
|
|
111
|
+
export class JwtStrategy extends PassportStrategy(Strategy) {
|
|
112
|
+
constructor(
|
|
113
|
+
private config: ConfigService,
|
|
114
|
+
private usersService: UsersService,
|
|
115
|
+
) {
|
|
116
|
+
super({
|
|
117
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
118
|
+
secretOrKey: config.get<string>('JWT_SECRET'),
|
|
119
|
+
ignoreExpiration: false,
|
|
120
|
+
issuer: config.get<string>('JWT_ISSUER'),
|
|
121
|
+
audience: config.get<string>('JWT_AUDIENCE'),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async validate(payload: JwtPayload): Promise<User> {
|
|
126
|
+
// Verify user still exists and is active
|
|
127
|
+
const user = await this.usersService.findById(payload.sub);
|
|
128
|
+
|
|
129
|
+
if (!user || !user.isActive) {
|
|
130
|
+
throw new UnauthorizedException('User not found or inactive');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Verify token wasn't issued before password change
|
|
134
|
+
if (user.passwordChangedAt) {
|
|
135
|
+
const tokenIssuedAt = new Date(payload.iat * 1000);
|
|
136
|
+
if (tokenIssuedAt < user.passwordChangedAt) {
|
|
137
|
+
throw new UnauthorizedException('Token invalidated by password change');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return user;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Reference: [NestJS Authentication](https://docs.nestjs.com/security/authentication)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Implement Rate Limiting
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Protects against abuse and ensures fair resource usage
|
|
5
|
+
tags: security, rate-limiting, throttler, protection
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Implement Rate Limiting
|
|
9
|
+
|
|
10
|
+
Use `@nestjs/throttler` to limit request rates per client. Apply different limits for different endpoints - stricter for auth endpoints, more relaxed for read operations. Consider using Redis for distributed rate limiting in clustered deployments.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no rate limiting on sensitive endpoints):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// No rate limiting on sensitive endpoints
|
|
16
|
+
@Controller('auth')
|
|
17
|
+
export class AuthController {
|
|
18
|
+
@Post('login')
|
|
19
|
+
async login(@Body() dto: LoginDto): Promise<TokenResponse> {
|
|
20
|
+
// Attackers can brute-force credentials
|
|
21
|
+
return this.authService.login(dto);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Post('forgot-password')
|
|
25
|
+
async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {
|
|
26
|
+
// Can be abused to spam users with emails
|
|
27
|
+
return this.authService.sendResetEmail(dto.email);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Same limits for all endpoints
|
|
32
|
+
@UseGuards(ThrottlerGuard)
|
|
33
|
+
@Controller('api')
|
|
34
|
+
export class ApiController {
|
|
35
|
+
@Get('public-data')
|
|
36
|
+
async getPublic() {} // Should allow more requests
|
|
37
|
+
|
|
38
|
+
@Post('process-payment')
|
|
39
|
+
async payment() {} // Should be more restrictive
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Correct (configured throttler with endpoint-specific limits):**
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Configure throttler globally with multiple limits
|
|
47
|
+
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
|
|
48
|
+
|
|
49
|
+
@Module({
|
|
50
|
+
imports: [
|
|
51
|
+
ThrottlerModule.forRoot([
|
|
52
|
+
{
|
|
53
|
+
name: 'short',
|
|
54
|
+
ttl: 1000, // 1 second
|
|
55
|
+
limit: 3, // 3 requests per second
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'medium',
|
|
59
|
+
ttl: 10000, // 10 seconds
|
|
60
|
+
limit: 20, // 20 requests per 10 seconds
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'long',
|
|
64
|
+
ttl: 60000, // 1 minute
|
|
65
|
+
limit: 100, // 100 requests per minute
|
|
66
|
+
},
|
|
67
|
+
]),
|
|
68
|
+
],
|
|
69
|
+
providers: [
|
|
70
|
+
{
|
|
71
|
+
provide: APP_GUARD,
|
|
72
|
+
useClass: ThrottlerGuard,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
})
|
|
76
|
+
export class AppModule {}
|
|
77
|
+
|
|
78
|
+
// Override limits per endpoint
|
|
79
|
+
@Controller('auth')
|
|
80
|
+
export class AuthController {
|
|
81
|
+
@Post('login')
|
|
82
|
+
@Throttle({ short: { limit: 5, ttl: 60000 } }) // 5 attempts per minute
|
|
83
|
+
async login(@Body() dto: LoginDto): Promise<TokenResponse> {
|
|
84
|
+
return this.authService.login(dto);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@Post('forgot-password')
|
|
88
|
+
@Throttle({ short: { limit: 3, ttl: 3600000 } }) // 3 per hour
|
|
89
|
+
async forgotPassword(@Body() dto: ForgotPasswordDto): Promise<void> {
|
|
90
|
+
return this.authService.sendResetEmail(dto.email);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Skip throttling for certain routes
|
|
95
|
+
@Controller('health')
|
|
96
|
+
export class HealthController {
|
|
97
|
+
@Get()
|
|
98
|
+
@SkipThrottle()
|
|
99
|
+
check(): string {
|
|
100
|
+
return 'OK';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Custom throttle per user type
|
|
105
|
+
@Injectable()
|
|
106
|
+
export class CustomThrottlerGuard extends ThrottlerGuard {
|
|
107
|
+
protected async getTracker(req: Request): Promise<string> {
|
|
108
|
+
// Use user ID if authenticated, IP otherwise
|
|
109
|
+
return req.user?.id || req.ip;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected async getLimit(context: ExecutionContext): Promise<number> {
|
|
113
|
+
const request = context.switchToHttp().getRequest();
|
|
114
|
+
|
|
115
|
+
// Higher limits for authenticated users
|
|
116
|
+
if (request.user) {
|
|
117
|
+
return request.user.isPremium ? 1000 : 200;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return 50; // Anonymous users
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Reference: [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Sanitize Output to Prevent XSS
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: XSS vulnerabilities can compromise user sessions and data
|
|
5
|
+
tags: security, xss, sanitization, html
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Sanitize Output to Prevent XSS
|
|
9
|
+
|
|
10
|
+
While NestJS APIs typically return JSON (which browsers don't execute), XSS risks exist when rendering HTML, storing user content, or when frontend frameworks improperly handle API responses. Sanitize user-generated content before storage and use proper Content-Type headers.
|
|
11
|
+
|
|
12
|
+
**Incorrect (storing raw HTML without sanitization):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Store raw HTML from users
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class CommentsService {
|
|
18
|
+
async create(dto: CreateCommentDto): Promise<Comment> {
|
|
19
|
+
// User can inject: <script>steal(document.cookie)</script>
|
|
20
|
+
return this.repo.save({
|
|
21
|
+
content: dto.content, // Raw, unsanitized
|
|
22
|
+
authorId: dto.authorId,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Return HTML without sanitization
|
|
28
|
+
@Controller('pages')
|
|
29
|
+
export class PagesController {
|
|
30
|
+
@Get(':slug')
|
|
31
|
+
@Header('Content-Type', 'text/html')
|
|
32
|
+
async getPage(@Param('slug') slug: string): Promise<string> {
|
|
33
|
+
const page = await this.pagesService.findBySlug(slug);
|
|
34
|
+
// If page.content contains user input, XSS is possible
|
|
35
|
+
return `<html><body>${page.content}</body></html>`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Reflect user input in errors
|
|
40
|
+
@Get(':id')
|
|
41
|
+
async findOne(@Param('id') id: string): Promise<User> {
|
|
42
|
+
const user = await this.repo.findOne({ where: { id } });
|
|
43
|
+
if (!user) {
|
|
44
|
+
// XSS if id contains malicious content and error is rendered
|
|
45
|
+
throw new NotFoundException(`User ${id} not found`);
|
|
46
|
+
}
|
|
47
|
+
return user;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Correct (sanitize content and use proper headers):**
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Sanitize HTML content before storage
|
|
55
|
+
import * as sanitizeHtml from 'sanitize-html';
|
|
56
|
+
|
|
57
|
+
@Injectable()
|
|
58
|
+
export class CommentsService {
|
|
59
|
+
private readonly sanitizeOptions: sanitizeHtml.IOptions = {
|
|
60
|
+
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
|
|
61
|
+
allowedAttributes: {
|
|
62
|
+
a: ['href', 'title'],
|
|
63
|
+
},
|
|
64
|
+
allowedSchemes: ['http', 'https', 'mailto'],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
async create(dto: CreateCommentDto): Promise<Comment> {
|
|
68
|
+
return this.repo.save({
|
|
69
|
+
content: sanitizeHtml(dto.content, this.sanitizeOptions),
|
|
70
|
+
authorId: dto.authorId,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Use validation pipe to strip HTML
|
|
76
|
+
import { Transform } from 'class-transformer';
|
|
77
|
+
|
|
78
|
+
export class CreatePostDto {
|
|
79
|
+
@IsString()
|
|
80
|
+
@MaxLength(1000)
|
|
81
|
+
@Transform(({ value }) => sanitizeHtml(value, { allowedTags: [] }))
|
|
82
|
+
title: string;
|
|
83
|
+
|
|
84
|
+
@IsString()
|
|
85
|
+
@Transform(({ value }) =>
|
|
86
|
+
sanitizeHtml(value, {
|
|
87
|
+
allowedTags: ['p', 'br', 'b', 'i', 'a'],
|
|
88
|
+
allowedAttributes: { a: ['href'] },
|
|
89
|
+
}),
|
|
90
|
+
)
|
|
91
|
+
content: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Set proper Content-Type headers
|
|
95
|
+
@Controller('api')
|
|
96
|
+
export class ApiController {
|
|
97
|
+
@Get('data')
|
|
98
|
+
@Header('Content-Type', 'application/json')
|
|
99
|
+
async getData(): Promise<DataResponse> {
|
|
100
|
+
// JSON response - browser won't execute scripts
|
|
101
|
+
return this.service.getData();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Sanitize error messages
|
|
106
|
+
@Get(':id')
|
|
107
|
+
async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {
|
|
108
|
+
const user = await this.repo.findOne({ where: { id } });
|
|
109
|
+
if (!user) {
|
|
110
|
+
// UUID validation ensures safe format
|
|
111
|
+
throw new NotFoundException('User not found');
|
|
112
|
+
}
|
|
113
|
+
return user;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Use Helmet for CSP headers
|
|
117
|
+
import helmet from 'helmet';
|
|
118
|
+
|
|
119
|
+
async function bootstrap() {
|
|
120
|
+
const app = await NestFactory.create(AppModule);
|
|
121
|
+
|
|
122
|
+
app.use(
|
|
123
|
+
helmet({
|
|
124
|
+
contentSecurityPolicy: {
|
|
125
|
+
directives: {
|
|
126
|
+
defaultSrc: ["'self'"],
|
|
127
|
+
scriptSrc: ["'self'"],
|
|
128
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
129
|
+
imgSrc: ["'self'", 'data:', 'https:'],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
await app.listen(3000);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Reference: [OWASP XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
|