@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,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Service Locator Anti-Pattern
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Hides dependencies and breaks testability
|
|
5
|
+
tags: dependency-injection, anti-patterns, testing
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Avoid Service Locator Anti-Pattern
|
|
9
|
+
|
|
10
|
+
Avoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.
|
|
11
|
+
|
|
12
|
+
**Incorrect (service locator anti-pattern):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Use ModuleRef to get dependencies dynamically
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class OrdersService {
|
|
18
|
+
constructor(private moduleRef: ModuleRef) {}
|
|
19
|
+
|
|
20
|
+
async createOrder(dto: CreateOrderDto): Promise<Order> {
|
|
21
|
+
// Dependencies are hidden - not visible in constructor
|
|
22
|
+
const usersService = this.moduleRef.get(UsersService);
|
|
23
|
+
const inventoryService = this.moduleRef.get(InventoryService);
|
|
24
|
+
const paymentService = this.moduleRef.get(PaymentService);
|
|
25
|
+
|
|
26
|
+
const user = await usersService.findOne(dto.userId);
|
|
27
|
+
// ... rest of logic
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Global singleton container
|
|
32
|
+
class ServiceContainer {
|
|
33
|
+
private static instance: ServiceContainer;
|
|
34
|
+
private services = new Map<string, any>();
|
|
35
|
+
|
|
36
|
+
static getInstance(): ServiceContainer {
|
|
37
|
+
if (!this.instance) {
|
|
38
|
+
this.instance = new ServiceContainer();
|
|
39
|
+
}
|
|
40
|
+
return this.instance;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get<T>(key: string): T {
|
|
44
|
+
return this.services.get(key);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Correct (constructor injection with explicit dependencies):**
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Use constructor injection - dependencies are explicit
|
|
53
|
+
@Injectable()
|
|
54
|
+
export class OrdersService {
|
|
55
|
+
constructor(
|
|
56
|
+
private usersService: UsersService,
|
|
57
|
+
private inventoryService: InventoryService,
|
|
58
|
+
private paymentService: PaymentService,
|
|
59
|
+
) {}
|
|
60
|
+
|
|
61
|
+
async createOrder(dto: CreateOrderDto): Promise<Order> {
|
|
62
|
+
const user = await this.usersService.findOne(dto.userId);
|
|
63
|
+
const inventory = await this.inventoryService.check(dto.items);
|
|
64
|
+
// Dependencies are clear and testable
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Easy to test with mocks
|
|
69
|
+
describe('OrdersService', () => {
|
|
70
|
+
let service: OrdersService;
|
|
71
|
+
|
|
72
|
+
beforeEach(async () => {
|
|
73
|
+
const module = await Test.createTestingModule({
|
|
74
|
+
providers: [
|
|
75
|
+
OrdersService,
|
|
76
|
+
{ provide: UsersService, useValue: mockUsersService },
|
|
77
|
+
{ provide: InventoryService, useValue: mockInventoryService },
|
|
78
|
+
{ provide: PaymentService, useValue: mockPaymentService },
|
|
79
|
+
],
|
|
80
|
+
}).compile();
|
|
81
|
+
|
|
82
|
+
service = module.get(OrdersService);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// VALID: Factory pattern for dynamic instantiation
|
|
87
|
+
@Injectable()
|
|
88
|
+
export class HandlerFactory {
|
|
89
|
+
constructor(private moduleRef: ModuleRef) {}
|
|
90
|
+
|
|
91
|
+
getHandler(type: string): Handler {
|
|
92
|
+
switch (type) {
|
|
93
|
+
case 'email':
|
|
94
|
+
return this.moduleRef.get(EmailHandler);
|
|
95
|
+
case 'sms':
|
|
96
|
+
return this.moduleRef.get(SmsHandler);
|
|
97
|
+
default:
|
|
98
|
+
return this.moduleRef.get(DefaultHandler);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Reference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Apply Interface Segregation Principle
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Reduces coupling and improves testability by 30-50%
|
|
5
|
+
tags: dependency-injection, interfaces, solid, isp
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Apply Interface Segregation Principle
|
|
9
|
+
|
|
10
|
+
Clients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating "fat" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones.
|
|
11
|
+
|
|
12
|
+
**Incorrect (fat interface forcing unused dependencies):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Fat interface - forces all consumers to depend on everything
|
|
16
|
+
interface NotificationService {
|
|
17
|
+
sendEmail(to: string, subject: string, body: string): Promise<void>;
|
|
18
|
+
sendSms(phone: string, message: string): Promise<void>;
|
|
19
|
+
sendPush(userId: string, notification: PushPayload): Promise<void>;
|
|
20
|
+
sendSlack(channel: string, message: string): Promise<void>;
|
|
21
|
+
logNotification(type: string, payload: any): Promise<void>;
|
|
22
|
+
getDeliveryStatus(id: string): Promise<DeliveryStatus>;
|
|
23
|
+
retryFailed(id: string): Promise<void>;
|
|
24
|
+
scheduleNotification(dto: ScheduleDto): Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Consumer only needs email, but must mock everything for tests
|
|
28
|
+
@Injectable()
|
|
29
|
+
export class OrdersService {
|
|
30
|
+
constructor(
|
|
31
|
+
private notifications: NotificationService, // Depends on 8 methods, uses 1
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
async confirmOrder(order: Order): Promise<void> {
|
|
35
|
+
await this.notifications.sendEmail(
|
|
36
|
+
order.customer.email,
|
|
37
|
+
'Order Confirmed',
|
|
38
|
+
`Your order ${order.id} has been confirmed.`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Testing is painful - must mock unused methods
|
|
44
|
+
const mockNotificationService = {
|
|
45
|
+
sendEmail: jest.fn(),
|
|
46
|
+
sendSms: jest.fn(), // Never used, but required
|
|
47
|
+
sendPush: jest.fn(), // Never used, but required
|
|
48
|
+
sendSlack: jest.fn(), // Never used, but required
|
|
49
|
+
logNotification: jest.fn(), // Never used, but required
|
|
50
|
+
getDeliveryStatus: jest.fn(), // Never used, but required
|
|
51
|
+
retryFailed: jest.fn(), // Never used, but required
|
|
52
|
+
scheduleNotification: jest.fn(), // Never used, but required
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Correct (segregated interfaces by capability):**
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Segregated interfaces - each focused on one capability
|
|
60
|
+
interface EmailSender {
|
|
61
|
+
sendEmail(to: string, subject: string, body: string): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface SmsSender {
|
|
65
|
+
sendSms(phone: string, message: string): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface PushSender {
|
|
69
|
+
sendPush(userId: string, notification: PushPayload): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface NotificationLogger {
|
|
73
|
+
logNotification(type: string, payload: any): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface NotificationScheduler {
|
|
77
|
+
scheduleNotification(dto: ScheduleDto): Promise<string>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Implementation can implement multiple interfaces
|
|
81
|
+
@Injectable()
|
|
82
|
+
export class NotificationService implements EmailSender, SmsSender, PushSender {
|
|
83
|
+
async sendEmail(to: string, subject: string, body: string): Promise<void> {
|
|
84
|
+
// Email implementation
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async sendSms(phone: string, message: string): Promise<void> {
|
|
88
|
+
// SMS implementation
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async sendPush(userId: string, notification: PushPayload): Promise<void> {
|
|
92
|
+
// Push implementation
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Or separate implementations
|
|
97
|
+
@Injectable()
|
|
98
|
+
export class SendGridEmailService implements EmailSender {
|
|
99
|
+
async sendEmail(to: string, subject: string, body: string): Promise<void> {
|
|
100
|
+
// SendGrid-specific implementation
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Consumer depends only on what it needs
|
|
105
|
+
@Injectable()
|
|
106
|
+
export class OrdersService {
|
|
107
|
+
constructor(
|
|
108
|
+
@Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency
|
|
109
|
+
) {}
|
|
110
|
+
|
|
111
|
+
async confirmOrder(order: Order): Promise<void> {
|
|
112
|
+
await this.emailSender.sendEmail(
|
|
113
|
+
order.customer.email,
|
|
114
|
+
'Order Confirmed',
|
|
115
|
+
`Your order ${order.id} has been confirmed.`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Testing is simple - only mock what's used
|
|
121
|
+
const mockEmailSender: EmailSender = {
|
|
122
|
+
sendEmail: jest.fn(),
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Module registration with tokens
|
|
126
|
+
export const EMAIL_SENDER = Symbol('EMAIL_SENDER');
|
|
127
|
+
export const SMS_SENDER = Symbol('SMS_SENDER');
|
|
128
|
+
|
|
129
|
+
@Module({
|
|
130
|
+
providers: [
|
|
131
|
+
{ provide: EMAIL_SENDER, useClass: SendGridEmailService },
|
|
132
|
+
{ provide: SMS_SENDER, useClass: TwilioSmsService },
|
|
133
|
+
],
|
|
134
|
+
exports: [EMAIL_SENDER, SMS_SENDER],
|
|
135
|
+
})
|
|
136
|
+
export class NotificationModule {}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Combining interfaces when needed:**
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Sometimes a consumer legitimately needs multiple capabilities
|
|
143
|
+
interface EmailAndSmsSender extends EmailSender, SmsSender {}
|
|
144
|
+
|
|
145
|
+
// Or use intersection types
|
|
146
|
+
type MultiChannelSender = EmailSender & SmsSender & PushSender;
|
|
147
|
+
|
|
148
|
+
// Consumer that genuinely needs multiple channels
|
|
149
|
+
@Injectable()
|
|
150
|
+
export class AlertService {
|
|
151
|
+
constructor(
|
|
152
|
+
@Inject(MULTI_CHANNEL_SENDER)
|
|
153
|
+
private sender: EmailSender & SmsSender,
|
|
154
|
+
) {}
|
|
155
|
+
|
|
156
|
+
async sendCriticalAlert(user: User, message: string): Promise<void> {
|
|
157
|
+
await Promise.all([
|
|
158
|
+
this.sender.sendEmail(user.email, 'Critical Alert', message),
|
|
159
|
+
this.sender.sendSms(user.phone, message),
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Reference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle)
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Honor Liskov Substitution Principle
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Ensures implementations are truly interchangeable without breaking callers
|
|
5
|
+
tags: dependency-injection, inheritance, solid, lsp
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Honor Liskov Substitution Principle
|
|
9
|
+
|
|
10
|
+
Subtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations.
|
|
11
|
+
|
|
12
|
+
**Incorrect (implementation violates the contract):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Base interface with clear contract
|
|
16
|
+
interface PaymentGateway {
|
|
17
|
+
/**
|
|
18
|
+
* Charges the specified amount.
|
|
19
|
+
* @returns PaymentResult on success
|
|
20
|
+
* @throws PaymentFailedException on payment failure
|
|
21
|
+
*/
|
|
22
|
+
charge(amount: number, currency: string): Promise<PaymentResult>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Production implementation - follows the contract
|
|
26
|
+
@Injectable()
|
|
27
|
+
export class StripeService implements PaymentGateway {
|
|
28
|
+
async charge(amount: number, currency: string): Promise<PaymentResult> {
|
|
29
|
+
const response = await this.stripe.charges.create({ amount, currency });
|
|
30
|
+
return { success: true, transactionId: response.id, amount };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Mock that violates LSP - different behavior!
|
|
35
|
+
@Injectable()
|
|
36
|
+
export class MockPaymentService implements PaymentGateway {
|
|
37
|
+
async charge(amount: number, currency: string): Promise<PaymentResult> {
|
|
38
|
+
// VIOLATION 1: Throws for valid input (contract says return PaymentResult)
|
|
39
|
+
if (amount > 1000) {
|
|
40
|
+
throw new Error('Mock does not support large amounts');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// VIOLATION 2: Returns null instead of PaymentResult
|
|
44
|
+
if (currency !== 'USD') {
|
|
45
|
+
return null as any; // Real service would convert or reject properly
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// VIOLATION 3: Missing required field
|
|
49
|
+
return { success: true } as PaymentResult; // Missing transactionId!
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Consumer trusts the contract
|
|
54
|
+
@Injectable()
|
|
55
|
+
export class OrdersService {
|
|
56
|
+
constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}
|
|
57
|
+
|
|
58
|
+
async checkout(order: Order): Promise<void> {
|
|
59
|
+
const result = await this.payment.charge(order.total, order.currency);
|
|
60
|
+
// These fail with MockPaymentService:
|
|
61
|
+
await this.saveTransaction(result.transactionId); // undefined!
|
|
62
|
+
await this.sendReceipt(result); // might be null!
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Correct (implementations honor the contract):**
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// Well-defined interface with documented behavior
|
|
71
|
+
interface PaymentGateway {
|
|
72
|
+
/**
|
|
73
|
+
* Charges the specified amount.
|
|
74
|
+
* @param amount - Amount in smallest currency unit (cents)
|
|
75
|
+
* @param currency - ISO 4217 currency code
|
|
76
|
+
* @returns PaymentResult with transactionId, success status, and amount
|
|
77
|
+
* @throws PaymentFailedException if charge is declined
|
|
78
|
+
* @throws InvalidCurrencyException if currency is not supported
|
|
79
|
+
*/
|
|
80
|
+
charge(amount: number, currency: string): Promise<PaymentResult>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Refunds a previous charge.
|
|
84
|
+
* @throws TransactionNotFoundException if transactionId is invalid
|
|
85
|
+
*/
|
|
86
|
+
refund(transactionId: string, amount?: number): Promise<RefundResult>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Production implementation
|
|
90
|
+
@Injectable()
|
|
91
|
+
export class StripeService implements PaymentGateway {
|
|
92
|
+
async charge(amount: number, currency: string): Promise<PaymentResult> {
|
|
93
|
+
try {
|
|
94
|
+
const response = await this.stripe.charges.create({ amount, currency });
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
transactionId: response.id,
|
|
98
|
+
amount: response.amount,
|
|
99
|
+
};
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error.type === 'card_error') {
|
|
102
|
+
throw new PaymentFailedException(error.message);
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async refund(transactionId: string, amount?: number): Promise<RefundResult> {
|
|
109
|
+
// Implementation...
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Mock that honors LSP - same contract, same behavior shape
|
|
114
|
+
@Injectable()
|
|
115
|
+
export class MockPaymentService implements PaymentGateway {
|
|
116
|
+
private transactions = new Map<string, PaymentResult>();
|
|
117
|
+
|
|
118
|
+
async charge(amount: number, currency: string): Promise<PaymentResult> {
|
|
119
|
+
// Honor the contract: validate currency like real service would
|
|
120
|
+
if (!['USD', 'EUR', 'GBP'].includes(currency)) {
|
|
121
|
+
throw new InvalidCurrencyException(`Unsupported currency: ${currency}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Simulate decline for specific test scenarios
|
|
125
|
+
if (amount === 99999) {
|
|
126
|
+
throw new PaymentFailedException('Card declined (test scenario)');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Return same shape as production
|
|
130
|
+
const result: PaymentResult = {
|
|
131
|
+
success: true,
|
|
132
|
+
transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`,
|
|
133
|
+
amount,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
this.transactions.set(result.transactionId, result);
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async refund(transactionId: string, amount?: number): Promise<RefundResult> {
|
|
141
|
+
// Honor the contract: throw if transaction not found
|
|
142
|
+
if (!this.transactions.has(transactionId)) {
|
|
143
|
+
throw new TransactionNotFoundException(transactionId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
refundId: `refund_${transactionId}`,
|
|
149
|
+
amount: amount ?? this.transactions.get(transactionId)!.amount,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Consumer can swap implementations safely
|
|
155
|
+
@Injectable()
|
|
156
|
+
export class OrdersService {
|
|
157
|
+
constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {}
|
|
158
|
+
|
|
159
|
+
async checkout(order: Order): Promise<Order> {
|
|
160
|
+
try {
|
|
161
|
+
const result = await this.payment.charge(order.total, order.currency);
|
|
162
|
+
// Works with both StripeService and MockPaymentService
|
|
163
|
+
order.transactionId = result.transactionId;
|
|
164
|
+
order.status = 'paid';
|
|
165
|
+
return order;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof PaymentFailedException) {
|
|
168
|
+
order.status = 'payment_failed';
|
|
169
|
+
return order;
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Testing LSP compliance:**
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// Shared test suite that any implementation must pass
|
|
181
|
+
function testPaymentGatewayContract(
|
|
182
|
+
createGateway: () => PaymentGateway,
|
|
183
|
+
) {
|
|
184
|
+
describe('PaymentGateway contract', () => {
|
|
185
|
+
let gateway: PaymentGateway;
|
|
186
|
+
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
gateway = createGateway();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('returns PaymentResult with all required fields', async () => {
|
|
192
|
+
const result = await gateway.charge(1000, 'USD');
|
|
193
|
+
expect(result).toHaveProperty('success');
|
|
194
|
+
expect(result).toHaveProperty('transactionId');
|
|
195
|
+
expect(result).toHaveProperty('amount');
|
|
196
|
+
expect(typeof result.transactionId).toBe('string');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('throws InvalidCurrencyException for unsupported currency', async () => {
|
|
200
|
+
await expect(gateway.charge(1000, 'INVALID'))
|
|
201
|
+
.rejects.toThrow(InvalidCurrencyException);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('throws TransactionNotFoundException for invalid refund', async () => {
|
|
205
|
+
await expect(gateway.refund('nonexistent'))
|
|
206
|
+
.rejects.toThrow(TransactionNotFoundException);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Run against all implementations
|
|
212
|
+
describe('StripeService', () => {
|
|
213
|
+
testPaymentGatewayContract(() => new StripeService(mockStripeClient));
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('MockPaymentService', () => {
|
|
217
|
+
testPaymentGatewayContract(() => new MockPaymentService());
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Reference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)
|
package/dist/skills/frameworks/nestjs/nestjs-best-practices/rules/di-prefer-constructor-injection.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefer Constructor Injection
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: Required for proper DI and testing
|
|
5
|
+
tags: dependency-injection, constructor, testing
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prefer Constructor Injection
|
|
9
|
+
|
|
10
|
+
Always use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.
|
|
11
|
+
|
|
12
|
+
**Incorrect (property injection with hidden dependencies):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Property injection - avoid unless necessary
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class UsersService {
|
|
18
|
+
@Inject()
|
|
19
|
+
private userRepo: UserRepository; // Hidden dependency
|
|
20
|
+
|
|
21
|
+
@Inject('CONFIG')
|
|
22
|
+
private config: ConfigType; // Also hidden
|
|
23
|
+
|
|
24
|
+
async findAll() {
|
|
25
|
+
return this.userRepo.find();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Problems:
|
|
30
|
+
// 1. Dependencies not visible in constructor
|
|
31
|
+
// 2. Service can be instantiated without dependencies in tests
|
|
32
|
+
// 3. TypeScript can't enforce dependency types at instantiation
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Correct (constructor injection with explicit dependencies):**
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Constructor injection - explicit and testable
|
|
39
|
+
@Injectable()
|
|
40
|
+
export class UsersService {
|
|
41
|
+
constructor(
|
|
42
|
+
private readonly userRepo: UserRepository,
|
|
43
|
+
@Inject('CONFIG') private readonly config: ConfigType,
|
|
44
|
+
) {}
|
|
45
|
+
|
|
46
|
+
async findAll(): Promise<User[]> {
|
|
47
|
+
return this.userRepo.find();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Testing is straightforward
|
|
52
|
+
describe('UsersService', () => {
|
|
53
|
+
let service: UsersService;
|
|
54
|
+
let mockRepo: jest.Mocked<UserRepository>;
|
|
55
|
+
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
mockRepo = {
|
|
58
|
+
find: jest.fn(),
|
|
59
|
+
save: jest.fn(),
|
|
60
|
+
} as any;
|
|
61
|
+
|
|
62
|
+
service = new UsersService(mockRepo, { dbUrl: 'test' });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should find all users', async () => {
|
|
66
|
+
mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);
|
|
67
|
+
const result = await service.findAll();
|
|
68
|
+
expect(result).toHaveLength(1);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Only use property injection for optional dependencies
|
|
73
|
+
@Injectable()
|
|
74
|
+
export class LoggingService {
|
|
75
|
+
@Optional()
|
|
76
|
+
@Inject('ANALYTICS')
|
|
77
|
+
private analytics?: AnalyticsService;
|
|
78
|
+
|
|
79
|
+
log(message: string) {
|
|
80
|
+
console.log(message);
|
|
81
|
+
this.analytics?.track('log', message); // Optional enhancement
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Reference: [NestJS Providers](https://docs.nestjs.com/providers)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Understand Provider Scopes
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: Prevents data leaks and performance issues
|
|
5
|
+
tags: dependency-injection, scopes, request-context
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Understand Provider Scopes
|
|
9
|
+
|
|
10
|
+
NestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing.
|
|
11
|
+
|
|
12
|
+
**Incorrect (wrong scope usage):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Request-scoped when not needed (performance hit)
|
|
16
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
17
|
+
export class UsersService {
|
|
18
|
+
// This creates a new instance for EVERY request
|
|
19
|
+
// All dependencies also become request-scoped
|
|
20
|
+
async findAll() {
|
|
21
|
+
return this.userRepo.find();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Singleton with mutable request state
|
|
26
|
+
@Injectable() // Default: singleton
|
|
27
|
+
export class RequestContextService {
|
|
28
|
+
private userId: string; // DANGER: Shared across all requests!
|
|
29
|
+
|
|
30
|
+
setUser(userId: string) {
|
|
31
|
+
this.userId = userId; // Overwrites for all concurrent requests
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getUser() {
|
|
35
|
+
return this.userId; // Returns wrong user!
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Correct (appropriate scope for each use case):**
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Singleton for stateless services (default, most common)
|
|
44
|
+
@Injectable()
|
|
45
|
+
export class UsersService {
|
|
46
|
+
constructor(private readonly userRepo: UserRepository) {}
|
|
47
|
+
|
|
48
|
+
async findById(id: string): Promise<User> {
|
|
49
|
+
return this.userRepo.findOne({ where: { id } });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Request-scoped ONLY when you need request context
|
|
54
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
55
|
+
export class RequestContextService {
|
|
56
|
+
private userId: string;
|
|
57
|
+
|
|
58
|
+
setUser(userId: string) {
|
|
59
|
+
this.userId = userId;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getUser(): string {
|
|
63
|
+
return this.userId;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Better: Use NestJS built-in request context
|
|
68
|
+
import { REQUEST } from '@nestjs/core';
|
|
69
|
+
import { Request } from 'express';
|
|
70
|
+
|
|
71
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
72
|
+
export class AuditService {
|
|
73
|
+
constructor(@Inject(REQUEST) private request: Request) {}
|
|
74
|
+
|
|
75
|
+
log(action: string) {
|
|
76
|
+
console.log(`User ${this.request.user?.id} performed ${action}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Best: Use ClsModule for async context (no scope bubble-up)
|
|
81
|
+
import { ClsService } from 'nestjs-cls';
|
|
82
|
+
|
|
83
|
+
@Injectable() // Stays singleton!
|
|
84
|
+
export class AuditService {
|
|
85
|
+
constructor(private cls: ClsService) {}
|
|
86
|
+
|
|
87
|
+
log(action: string) {
|
|
88
|
+
const userId = this.cls.get('userId');
|
|
89
|
+
console.log(`User ${userId} performed ${action}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Reference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes)
|