spoonfeeder 0.1.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/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/generator/ai-context-assembler.d.ts +4 -0
- package/dist/generator/ai-context-assembler.js +52 -0
- package/dist/generator/ai-context-assembler.js.map +1 -0
- package/dist/generator/env-merger.d.ts +3 -0
- package/dist/generator/env-merger.js +16 -0
- package/dist/generator/env-merger.js.map +1 -0
- package/dist/generator/generator.d.ts +3 -0
- package/dist/generator/generator.js +253 -0
- package/dist/generator/generator.js.map +1 -0
- package/dist/generator/package-json-merger.d.ts +6 -0
- package/dist/generator/package-json-merger.js +29 -0
- package/dist/generator/package-json-merger.js.map +1 -0
- package/dist/generator/post-generate.d.ts +1 -0
- package/dist/generator/post-generate.js +28 -0
- package/dist/generator/post-generate.js.map +1 -0
- package/dist/generator/template-engine.d.ts +4 -0
- package/dist/generator/template-engine.js +20 -0
- package/dist/generator/template-engine.js.map +1 -0
- package/dist/generators/add-recipe/generator.d.ts +3 -0
- package/dist/generators/add-recipe/generator.js +153 -0
- package/dist/generators/add-recipe/generator.js.map +1 -0
- package/dist/generators/list-recipes/generator.d.ts +3 -0
- package/dist/generators/list-recipes/generator.js +58 -0
- package/dist/generators/list-recipes/generator.js.map +1 -0
- package/dist/generators/migrate-recipe/generator.d.ts +9 -0
- package/dist/generators/migrate-recipe/generator.js +90 -0
- package/dist/generators/migrate-recipe/generator.js.map +1 -0
- package/dist/generators/migrate-recipe/migration-guidance.d.ts +1 -0
- package/dist/generators/migrate-recipe/migration-guidance.js +159 -0
- package/dist/generators/migrate-recipe/migration-guidance.js.map +1 -0
- package/dist/generators/remove-recipe/generator.d.ts +3 -0
- package/dist/generators/remove-recipe/generator.js +176 -0
- package/dist/generators/remove-recipe/generator.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/add-ons.d.ts +3 -0
- package/dist/prompts/add-ons.js +29 -0
- package/dist/prompts/add-ons.js.map +1 -0
- package/dist/prompts/ci-cd.d.ts +2 -0
- package/dist/prompts/ci-cd.js +19 -0
- package/dist/prompts/ci-cd.js.map +1 -0
- package/dist/prompts/cloud-provider.d.ts +2 -0
- package/dist/prompts/cloud-provider.js +18 -0
- package/dist/prompts/cloud-provider.js.map +1 -0
- package/dist/prompts/confirmation.d.ts +2 -0
- package/dist/prompts/confirmation.js +29 -0
- package/dist/prompts/confirmation.js.map +1 -0
- package/dist/prompts/deployment.d.ts +2 -0
- package/dist/prompts/deployment.js +20 -0
- package/dist/prompts/deployment.js.map +1 -0
- package/dist/prompts/frontend.d.ts +2 -0
- package/dist/prompts/frontend.js +18 -0
- package/dist/prompts/frontend.js.map +1 -0
- package/dist/prompts/project-name.d.ts +5 -0
- package/dist/prompts/project-name.js +34 -0
- package/dist/prompts/project-name.js.map +1 -0
- package/dist/prompts/project-type.d.ts +2 -0
- package/dist/prompts/project-type.js +22 -0
- package/dist/prompts/project-type.js.map +1 -0
- package/dist/prompts/run-all.d.ts +3 -0
- package/dist/prompts/run-all.js +83 -0
- package/dist/prompts/run-all.js.map +1 -0
- package/dist/prompts/transport.d.ts +2 -0
- package/dist/prompts/transport.js +22 -0
- package/dist/prompts/transport.js.map +1 -0
- package/dist/recipes/definitions.d.ts +2 -0
- package/dist/recipes/definitions.js +3647 -0
- package/dist/recipes/definitions.js.map +1 -0
- package/dist/recipes/recipe.interface.d.ts +8 -0
- package/dist/recipes/recipe.interface.js +2 -0
- package/dist/recipes/recipe.interface.js.map +1 -0
- package/dist/recipes/registry.d.ts +9 -0
- package/dist/recipes/registry.js +23 -0
- package/dist/recipes/registry.js.map +1 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.js +152 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/ai-context-updater.d.ts +6 -0
- package/dist/utils/ai-context-updater.js +50 -0
- package/dist/utils/ai-context-updater.js.map +1 -0
- package/dist/utils/dependency-checker.d.ts +14 -0
- package/dist/utils/dependency-checker.js +27 -0
- package/dist/utils/dependency-checker.js.map +1 -0
- package/dist/utils/env-updater.d.ts +7 -0
- package/dist/utils/env-updater.js +35 -0
- package/dist/utils/env-updater.js.map +1 -0
- package/dist/utils/main-ts-updater.d.ts +37 -0
- package/dist/utils/main-ts-updater.js +144 -0
- package/dist/utils/main-ts-updater.js.map +1 -0
- package/dist/utils/module-updater.d.ts +26 -0
- package/dist/utils/module-updater.js +167 -0
- package/dist/utils/module-updater.js.map +1 -0
- package/dist/utils/recipe-manifest.d.ts +24 -0
- package/dist/utils/recipe-manifest.js +45 -0
- package/dist/utils/recipe-manifest.js.map +1 -0
- package/dist/validation/config-validator.d.ts +13 -0
- package/dist/validation/config-validator.js +50 -0
- package/dist/validation/config-validator.js.map +1 -0
- package/dist/validation/conflict-detector.d.ts +7 -0
- package/dist/validation/conflict-detector.js +37 -0
- package/dist/validation/conflict-detector.js.map +1 -0
- package/generators.json +24 -0
- package/package.json +78 -0
- package/src/generators/add-recipe/schema.json +21 -0
- package/src/generators/list-recipes/schema.json +18 -0
- package/src/generators/migrate-recipe/schema.json +20 -0
- package/src/generators/remove-recipe/schema.json +21 -0
- package/templates/base/.env.example.ejs +3 -0
- package/templates/base/README.md.ejs +20 -0
- package/templates/base/SECURITY.md +36 -0
- package/templates/base/commitlint.config.js +15 -0
- package/templates/base/docs/adr/000-template.md +45 -0
- package/templates/base/docs/adr/001-framework-selection.md +54 -0
- package/templates/base/docs/adr/002-error-handling-strategy.md +36 -0
- package/templates/base/docs/adr/003-database-strategy.md +40 -0
- package/templates/base/docs/adr/004-authentication-architecture.md +44 -0
- package/templates/base/docs/adr/005-deployment-strategy.md +40 -0
- package/templates/base/docs/adr/006-testing-strategy.md +41 -0
- package/templates/base/docs/adr/007-scaffolder-architecture.md +64 -0
- package/templates/base/docs/adr/008-recipe-system.md +47 -0
- package/templates/base/docs/adr/009-standards-compliance.md +54 -0
- package/templates/base/docs/adr/010-ai-context-generation.md +44 -0
- package/templates/base/docs/adr/011-package-versioning.md +44 -0
- package/templates/base/docs/adr/012-monorepo-strategy.md +54 -0
- package/templates/base/docs/adr/013-http-adapter.md +63 -0
- package/templates/base/docs/adr/014-build-toolchain.md +61 -0
- package/templates/base/docs/adr/015-cloud-provider-strategy.md +49 -0
- package/templates/base/docs/adr/016-ci-cd-pipeline-strategy.md +52 -0
- package/templates/base/docs/adr/017-logging-strategy.md +63 -0
- package/templates/base/docs/adr/018-security-architecture.md +61 -0
- package/templates/base/docs/adr/019-project-type-design.md +61 -0
- package/templates/base/docs/adr/020-frontend-framework-support.md +57 -0
- package/templates/base/docs/adr/021-observability-strategy.md +68 -0
- package/templates/base/docs/adr/022-api-design-patterns.md +64 -0
- package/templates/base/docs/adr/023-error-hierarchy.md +54 -0
- package/templates/base/docs/adr/024-request-context-pattern.md +57 -0
- package/templates/base/docs/adr/025-data-patterns.md +58 -0
- package/templates/base/docs/adr/026-migration-strategy.md +57 -0
- package/templates/base/docs/adr/027-dependency-management.md +60 -0
- package/templates/base/docs/adr/028-code-quality-toolchain.md +60 -0
- package/templates/base/docs/adr/029-admin-panel.md +53 -0
- package/templates/base/docs/adr/030-performance-patterns.md +60 -0
- package/templates/base/docs/adr/031-nx-generators-roadmap.md +73 -0
- package/templates/base/docs/runbooks/000-template.md +78 -0
- package/templates/base/docs/sla-slo.md +36 -0
- package/templates/base/dot-editorconfig +12 -0
- package/templates/base/dot-github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/templates/base/dot-github/ISSUE_TEMPLATE/feature_request.md +21 -0
- package/templates/base/dot-github/pull_request_template.md +32 -0
- package/templates/base/dot-gitignore +28 -0
- package/templates/base/dot-husky/commit-msg +1 -0
- package/templates/base/dot-husky/pre-commit +1 -0
- package/templates/base/dot-husky/pre-push +2 -0
- package/templates/base/dot-npmrc +1 -0
- package/templates/base/dot-nvmrc +1 -0
- package/templates/base/dot-prettierrc +8 -0
- package/templates/base/eslint.config.mjs +35 -0
- package/templates/base/jest.config.ts +56 -0
- package/templates/base/nest-cli.json.ejs +10 -0
- package/templates/base/nx.json +3 -0
- package/templates/base/package.json.ejs +78 -0
- package/templates/base/src/app/modules/.gitkeep +0 -0
- package/templates/base/src/config/.gitkeep +0 -0
- package/templates/base/src/infrastructure/database/entities/.gitkeep +0 -0
- package/templates/base/src/infrastructure/database/migrations/.gitkeep +0 -0
- package/templates/base/src/infrastructure/database/repositories/.gitkeep +0 -0
- package/templates/base/src/infrastructure/http/.gitkeep +0 -0
- package/templates/base/src/infrastructure/notifications/.gitkeep +0 -0
- package/templates/base/src/infrastructure/queue/.gitkeep +0 -0
- package/templates/base/src/infrastructure/storage/.gitkeep +0 -0
- package/templates/base/src/shared/constants/error-codes.constant.ts +30 -0
- package/templates/base/src/shared/constants/http.constant.ts +134 -0
- package/templates/base/src/shared/constants/index.ts +2 -0
- package/templates/base/src/shared/decorators/.gitkeep +0 -0
- package/templates/base/src/shared/decorators/api-paginated-response.decorator.ts +10 -0
- package/templates/base/src/shared/errors/application.error.ts +21 -0
- package/templates/base/src/shared/errors/forbidden.error.ts +7 -0
- package/templates/base/src/shared/errors/index.ts +6 -0
- package/templates/base/src/shared/errors/invalid-request.error.ts +7 -0
- package/templates/base/src/shared/errors/not-found.error.ts +7 -0
- package/templates/base/src/shared/errors/requester.error.ts +7 -0
- package/templates/base/src/shared/errors/validation.error.ts +7 -0
- package/templates/base/src/shared/filters/http-exception.filter.ts +139 -0
- package/templates/base/src/shared/guards/.gitkeep +0 -0
- package/templates/base/src/shared/interceptors/interfaces/.gitkeep +0 -0
- package/templates/base/src/shared/interceptors/response.interceptor.ts +22 -0
- package/templates/base/src/shared/middleware/request-timeout.middleware.ts +32 -0
- package/templates/base/src/shared/pipes/parse-uuid.pipe.ts +13 -0
- package/templates/base/src/shared/utils/index.ts +2 -0
- package/templates/base/src/shared/utils/retry.util.ts +32 -0
- package/templates/base/src/shared/utils/sleep.util.ts +3 -0
- package/templates/base/tests/e2e/app.e2e-spec.ts.ejs +25 -0
- package/templates/base/tests/factories/.gitkeep +0 -0
- package/templates/base/tests/helpers/.gitkeep +0 -0
- package/templates/base/tests/integration/.gitkeep +0 -0
- package/templates/base/tests/unit/app.module.spec.ts +11 -0
- package/templates/base/tests/unit/shared/errors/error-hierarchy.spec.ts +69 -0
- package/templates/base/tests/unit/shared/filters/http-exception.filter.spec.ts +188 -0
- package/templates/base/tests/unit/shared/interceptors/response.interceptor.spec.ts +54 -0
- package/templates/base/tests/unit/shared/pipes/parse-uuid.pipe.spec.ts +14 -0
- package/templates/base/tsconfig.build.json +4 -0
- package/templates/base/tsconfig.json.ejs +26 -0
- package/templates/project-types/aws-lambda/package-fragment.json +8 -0
- package/templates/project-types/aws-lambda/src/app.module.ts.ejs +9 -0
- package/templates/project-types/aws-lambda/src/main.ts.ejs +32 -0
- package/templates/project-types/aws-lambda/tests/unit/handler.spec.ts +7 -0
- package/templates/project-types/cli-app/package-fragment.json +8 -0
- package/templates/project-types/cli-app/src/app.module.ts.ejs +11 -0
- package/templates/project-types/cli-app/src/commands/hello.command.ts.ejs +8 -0
- package/templates/project-types/cli-app/src/main.ts.ejs +8 -0
- package/templates/project-types/cli-app/tests/unit/commands/hello.command.spec.ts +17 -0
- package/templates/project-types/full-stack/apps/api/src/app.module.ts.ejs +9 -0
- package/templates/project-types/full-stack/apps/api/src/main.ts.ejs +32 -0
- package/templates/project-types/full-stack/frontend/nextjs/.cursor/rules/nextjs.mdc +12 -0
- package/templates/project-types/full-stack/frontend/nextjs/.github/copilot-instructions.md +23 -0
- package/templates/project-types/full-stack/frontend/nextjs/CLAUDE.md +25 -0
- package/templates/project-types/full-stack/frontend/nextjs/next.config.ts +9 -0
- package/templates/project-types/full-stack/frontend/nextjs/package.json +21 -0
- package/templates/project-types/full-stack/frontend/nextjs/src/app/layout.tsx +9 -0
- package/templates/project-types/full-stack/frontend/nextjs/src/app/page.tsx +8 -0
- package/templates/project-types/full-stack/frontend/nextjs/tsconfig.json +21 -0
- package/templates/project-types/full-stack/frontend/nuxt/.cursor/rules/nuxt.mdc +14 -0
- package/templates/project-types/full-stack/frontend/nuxt/.github/copilot-instructions.md +25 -0
- package/templates/project-types/full-stack/frontend/nuxt/CLAUDE.md +25 -0
- package/templates/project-types/full-stack/frontend/nuxt/app.vue +5 -0
- package/templates/project-types/full-stack/frontend/nuxt/nuxt.config.ts +6 -0
- package/templates/project-types/full-stack/frontend/nuxt/package.json +17 -0
- package/templates/project-types/full-stack/frontend/nuxt/pages/index.vue +3 -0
- package/templates/project-types/full-stack/frontend/nuxt/tsconfig.json +1 -0
- package/templates/project-types/full-stack/frontend/sveltekit/.cursor/rules/sveltekit.mdc +13 -0
- package/templates/project-types/full-stack/frontend/sveltekit/.github/copilot-instructions.md +24 -0
- package/templates/project-types/full-stack/frontend/sveltekit/CLAUDE.md +25 -0
- package/templates/project-types/full-stack/frontend/sveltekit/package.json +17 -0
- package/templates/project-types/full-stack/frontend/sveltekit/src/app.html +5 -0
- package/templates/project-types/full-stack/frontend/sveltekit/src/routes/+page.svelte +1 -0
- package/templates/project-types/full-stack/frontend/sveltekit/svelte.config.js +5 -0
- package/templates/project-types/full-stack/frontend/sveltekit/vite.config.ts +9 -0
- package/templates/project-types/full-stack/frontend/vite-react/.cursor/rules/react.mdc +12 -0
- package/templates/project-types/full-stack/frontend/vite-react/.github/copilot-instructions.md +23 -0
- package/templates/project-types/full-stack/frontend/vite-react/CLAUDE.md +24 -0
- package/templates/project-types/full-stack/frontend/vite-react/index.html +5 -0
- package/templates/project-types/full-stack/frontend/vite-react/package.json +22 -0
- package/templates/project-types/full-stack/frontend/vite-react/src/App.tsx +8 -0
- package/templates/project-types/full-stack/frontend/vite-react/src/main.tsx +9 -0
- package/templates/project-types/full-stack/frontend/vite-react/tsconfig.json +21 -0
- package/templates/project-types/full-stack/frontend/vite-react/vite.config.ts +9 -0
- package/templates/project-types/full-stack/libs/shared-types/package.json.ejs +6 -0
- package/templates/project-types/full-stack/libs/shared-types/src/index.ts.ejs +15 -0
- package/templates/project-types/full-stack/nx.json.ejs +20 -0
- package/templates/project-types/full-stack/package-fragment.json +14 -0
- package/templates/project-types/full-stack/pnpm-workspace.yaml +3 -0
- package/templates/project-types/full-stack/tsconfig.json.ejs +27 -0
- package/templates/project-types/http-api/src/app.module.ts.ejs +9 -0
- package/templates/project-types/http-api/src/main.ts.ejs +30 -0
- package/templates/project-types/http-api/tests/e2e/health.e2e-spec.ts +23 -0
- package/templates/project-types/microservice/package-fragment.json +5 -0
- package/templates/project-types/microservice/src/app.module.ts.ejs +9 -0
- package/templates/project-types/microservice/src/main.ts.ejs +15 -0
- package/templates/project-types/microservice/tests/unit/app.module.spec.ts +9 -0
- package/templates/project-types/monorepo/README.md.ejs +33 -0
- package/templates/project-types/monorepo/apps/api/src/app.module.ts.ejs +9 -0
- package/templates/project-types/monorepo/apps/api/src/main.ts.ejs +30 -0
- package/templates/project-types/monorepo/libs/common/package.json.ejs +6 -0
- package/templates/project-types/monorepo/libs/common/src/index.ts.ejs +17 -0
- package/templates/project-types/monorepo/nx.json.ejs +20 -0
- package/templates/project-types/monorepo/package-fragment.json +13 -0
- package/templates/project-types/monorepo/pnpm-workspace.yaml +3 -0
- package/templates/project-types/monorepo/tsconfig.json.ejs +27 -0
- package/templates/project-types/scheduled-worker/package-fragment.json +5 -0
- package/templates/project-types/scheduled-worker/src/app.module.ts.ejs +13 -0
- package/templates/project-types/scheduled-worker/src/jobs/example.job.ts.ejs +12 -0
- package/templates/project-types/scheduled-worker/src/main.ts.ejs +10 -0
- package/templates/project-types/scheduled-worker/tests/unit/jobs/example.job.spec.ts +17 -0
- package/templates/recipes/adminjs/README.md +145 -0
- package/templates/recipes/adminjs/src/app/modules/admin/admin.module.ts +59 -0
- package/templates/recipes/adminjs/tests/unit/app/modules/admin/admin.module.spec.ts +22 -0
- package/templates/recipes/ai-context/README.md +11 -0
- package/templates/recipes/ai-context/claude/base.md +73 -0
- package/templates/recipes/ai-context/claude/commands/add-command.md +48 -0
- package/templates/recipes/ai-context/claude/commands/add-consumer.md +58 -0
- package/templates/recipes/ai-context/claude/commands/add-endpoint.md +69 -0
- package/templates/recipes/ai-context/claude/commands/add-job.md +42 -0
- package/templates/recipes/ai-context/claude/commands/migrate.md +69 -0
- package/templates/recipes/ai-context/copilot/base.md +71 -0
- package/templates/recipes/ai-context/cursor/auth.mdc +44 -0
- package/templates/recipes/ai-context/cursor/base.mdc +44 -0
- package/templates/recipes/ai-context/cursor/prisma.mdc +43 -0
- package/templates/recipes/ai-context/cursor/swagger.mdc +48 -0
- package/templates/recipes/ai-context/cursor/testing.mdc +58 -0
- package/templates/recipes/ai-context/cursor/typeorm.mdc +49 -0
- package/templates/recipes/api-keys/README.md +44 -0
- package/templates/recipes/api-keys/src/shared/guards/api-key.guard.ts +32 -0
- package/templates/recipes/api-keys/tests/unit/shared/guards/api-key.guard.spec.ts +40 -0
- package/templates/recipes/api-versioning/README.md +63 -0
- package/templates/recipes/audit-trail/README.md +75 -0
- package/templates/recipes/audit-trail/src/shared/decorators/auditable.decorator.ts +4 -0
- package/templates/recipes/audit-trail/src/shared/interceptors/audit.interceptor.ts +46 -0
- package/templates/recipes/audit-trail/tests/unit/shared/interceptors/audit.interceptor.spec.ts +82 -0
- package/templates/recipes/auth-flows/README.md +91 -0
- package/templates/recipes/auth-flows/src/app/modules/auth/auth.controller.ts +44 -0
- package/templates/recipes/auth-flows/src/app/modules/auth/auth.module.ts +11 -0
- package/templates/recipes/auth-flows/src/app/modules/auth/auth.service.ts +194 -0
- package/templates/recipes/auth-flows/src/app/modules/auth/dto/forgot-password.dto.ts +6 -0
- package/templates/recipes/auth-flows/src/app/modules/auth/dto/login.dto.ts +9 -0
- package/templates/recipes/auth-flows/src/app/modules/auth/dto/reset-password.dto.ts +11 -0
- package/templates/recipes/auth-flows/src/app/modules/auth/dto/signup.dto.ts +16 -0
- package/templates/recipes/auth-flows/tests/unit/app/modules/auth/auth.service.spec.ts +13 -0
- package/templates/recipes/bullmq/README.md +54 -0
- package/templates/recipes/bullmq/src/infrastructure/queue/example.processor.ts +25 -0
- package/templates/recipes/bullmq/src/infrastructure/queue/queue.module.ts +26 -0
- package/templates/recipes/bullmq/tests/unit/infrastructure/queue/example.processor.spec.ts +17 -0
- package/templates/recipes/changelog/.changelogrc.json +18 -0
- package/templates/recipes/changelog/README.md +44 -0
- package/templates/recipes/ci-cd/README.md +14 -0
- package/templates/recipes/ci-cd/aws-codepipeline/README.md +30 -0
- package/templates/recipes/ci-cd/aws-codepipeline/buildspec.yml +41 -0
- package/templates/recipes/ci-cd/azure-devops/README.md +32 -0
- package/templates/recipes/ci-cd/azure-devops/azure-pipelines.yml +79 -0
- package/templates/recipes/ci-cd/gcp-cloudbuild/README.md +64 -0
- package/templates/recipes/ci-cd/gcp-cloudbuild/cloudbuild.yaml +53 -0
- package/templates/recipes/ci-cd/github-actions/.github/workflows/cd.yml +35 -0
- package/templates/recipes/ci-cd/github-actions/.github/workflows/ci.yml +62 -0
- package/templates/recipes/ci-cd/github-actions/README.md +29 -0
- package/templates/recipes/circuit-breaker/README.md +45 -0
- package/templates/recipes/circuit-breaker/src/shared/utils/circuit-breaker.ts +100 -0
- package/templates/recipes/circuit-breaker/tests/unit/shared/utils/circuit-breaker.spec.ts +28 -0
- package/templates/recipes/cloud-aws/README.md +20 -0
- package/templates/recipes/cloud-aws/cloudfront/README.md +50 -0
- package/templates/recipes/cloud-aws/cloudfront/src/infrastructure/aws/cloudfront.module.ts +8 -0
- package/templates/recipes/cloud-aws/cloudfront/src/infrastructure/aws/cloudfront.service.ts +29 -0
- package/templates/recipes/cloud-aws/cloudwatch/README.md +40 -0
- package/templates/recipes/cloud-aws/cloudwatch/src/infrastructure/aws/cloudwatch.module.ts +8 -0
- package/templates/recipes/cloud-aws/cloudwatch/src/infrastructure/aws/cloudwatch.service.ts +40 -0
- package/templates/recipes/cloud-aws/cognito/README.md +39 -0
- package/templates/recipes/cloud-aws/cognito/src/infrastructure/aws/cognito.module.ts +8 -0
- package/templates/recipes/cloud-aws/cognito/src/infrastructure/aws/cognito.service.ts +47 -0
- package/templates/recipes/cloud-aws/dynamodb/README.md +48 -0
- package/templates/recipes/cloud-aws/dynamodb/src/infrastructure/aws/dynamodb.module.ts +8 -0
- package/templates/recipes/cloud-aws/dynamodb/src/infrastructure/aws/dynamodb.service.ts +49 -0
- package/templates/recipes/cloud-aws/elasticache/README.md +45 -0
- package/templates/recipes/cloud-aws/elasticache/src/infrastructure/aws/redis.module.ts +8 -0
- package/templates/recipes/cloud-aws/elasticache/src/infrastructure/aws/redis.service.ts +47 -0
- package/templates/recipes/cloud-aws/eventbridge/README.md +46 -0
- package/templates/recipes/cloud-aws/eventbridge/src/infrastructure/aws/eventbridge.module.ts +8 -0
- package/templates/recipes/cloud-aws/eventbridge/src/infrastructure/aws/eventbridge.service.ts +48 -0
- package/templates/recipes/cloud-aws/rds/README.md +59 -0
- package/templates/recipes/cloud-aws/s3/README.md +44 -0
- package/templates/recipes/cloud-aws/s3/src/infrastructure/aws/s3.module.ts +8 -0
- package/templates/recipes/cloud-aws/s3/src/infrastructure/aws/s3.service.ts +42 -0
- package/templates/recipes/cloud-aws/secrets-manager/README.md +38 -0
- package/templates/recipes/cloud-aws/secrets-manager/src/infrastructure/aws/secrets.module.ts +8 -0
- package/templates/recipes/cloud-aws/secrets-manager/src/infrastructure/aws/secrets.service.ts +39 -0
- package/templates/recipes/cloud-aws/sns/README.md +43 -0
- package/templates/recipes/cloud-aws/sns/src/infrastructure/aws/sns.module.ts +8 -0
- package/templates/recipes/cloud-aws/sns/src/infrastructure/aws/sns.service.ts +48 -0
- package/templates/recipes/cloud-aws/sqs/README.md +47 -0
- package/templates/recipes/cloud-aws/sqs/src/infrastructure/aws/sqs.module.ts +8 -0
- package/templates/recipes/cloud-aws/sqs/src/infrastructure/aws/sqs.service.ts +58 -0
- package/templates/recipes/cloud-aws/ssm/README.md +41 -0
- package/templates/recipes/cloud-aws/ssm/src/infrastructure/aws/ssm.module.ts +8 -0
- package/templates/recipes/cloud-aws/ssm/src/infrastructure/aws/ssm.service.ts +54 -0
- package/templates/recipes/cloud-azure/README.md +18 -0
- package/templates/recipes/cloud-azure/app-insights/README.md +50 -0
- package/templates/recipes/cloud-azure/app-insights/src/infrastructure/azure/app-insights.module.ts +11 -0
- package/templates/recipes/cloud-azure/app-insights/src/infrastructure/azure/app-insights.service.ts +38 -0
- package/templates/recipes/cloud-azure/blob-storage/README.md +49 -0
- package/templates/recipes/cloud-azure/blob-storage/src/infrastructure/azure/blob-storage.module.ts +11 -0
- package/templates/recipes/cloud-azure/blob-storage/src/infrastructure/azure/blob-storage.service.ts +64 -0
- package/templates/recipes/cloud-azure/cache/README.md +47 -0
- package/templates/recipes/cloud-azure/cache/src/infrastructure/azure/redis.module.ts +11 -0
- package/templates/recipes/cloud-azure/cache/src/infrastructure/azure/redis.service.ts +41 -0
- package/templates/recipes/cloud-azure/cosmos-db/README.md +57 -0
- package/templates/recipes/cloud-azure/cosmos-db/src/infrastructure/azure/cosmos-db.module.ts +11 -0
- package/templates/recipes/cloud-azure/cosmos-db/src/infrastructure/azure/cosmos-db.service.ts +55 -0
- package/templates/recipes/cloud-azure/entra-id/README.md +42 -0
- package/templates/recipes/cloud-azure/entra-id/src/infrastructure/azure/entra-id.module.ts +11 -0
- package/templates/recipes/cloud-azure/entra-id/src/infrastructure/azure/entra-id.service.ts +55 -0
- package/templates/recipes/cloud-azure/front-door/README.md +48 -0
- package/templates/recipes/cloud-azure/functions/README.md +48 -0
- package/templates/recipes/cloud-azure/key-vault/README.md +42 -0
- package/templates/recipes/cloud-azure/key-vault/src/infrastructure/azure/key-vault.module.ts +11 -0
- package/templates/recipes/cloud-azure/key-vault/src/infrastructure/azure/key-vault.service.ts +27 -0
- package/templates/recipes/cloud-azure/service-bus/README.md +49 -0
- package/templates/recipes/cloud-azure/service-bus/src/infrastructure/azure/service-bus.module.ts +11 -0
- package/templates/recipes/cloud-azure/service-bus/src/infrastructure/azure/service-bus.service.ts +52 -0
- package/templates/recipes/cloud-azure/sql-database/README.md +54 -0
- package/templates/recipes/cloud-gcp/README.md +18 -0
- package/templates/recipes/cloud-gcp/cloud-cdn/README.md +40 -0
- package/templates/recipes/cloud-gcp/cloud-functions/README.md +42 -0
- package/templates/recipes/cloud-gcp/cloud-logging/README.md +42 -0
- package/templates/recipes/cloud-gcp/cloud-logging/src/infrastructure/gcp/logging.module.ts +10 -0
- package/templates/recipes/cloud-gcp/cloud-logging/src/infrastructure/gcp/logging.service.ts +50 -0
- package/templates/recipes/cloud-gcp/cloud-sql/README.md +53 -0
- package/templates/recipes/cloud-gcp/cloud-storage/README.md +43 -0
- package/templates/recipes/cloud-gcp/cloud-storage/src/infrastructure/gcp/storage.module.ts +10 -0
- package/templates/recipes/cloud-gcp/cloud-storage/src/infrastructure/gcp/storage.service.ts +51 -0
- package/templates/recipes/cloud-gcp/firebase-auth/README.md +38 -0
- package/templates/recipes/cloud-gcp/firebase-auth/src/infrastructure/gcp/firebase-auth.module.ts +10 -0
- package/templates/recipes/cloud-gcp/firebase-auth/src/infrastructure/gcp/firebase-auth.service.ts +41 -0
- package/templates/recipes/cloud-gcp/firestore/README.md +46 -0
- package/templates/recipes/cloud-gcp/firestore/src/infrastructure/gcp/firestore.module.ts +10 -0
- package/templates/recipes/cloud-gcp/firestore/src/infrastructure/gcp/firestore.service.ts +44 -0
- package/templates/recipes/cloud-gcp/memorystore/README.md +44 -0
- package/templates/recipes/cloud-gcp/memorystore/src/infrastructure/gcp/redis.module.ts +10 -0
- package/templates/recipes/cloud-gcp/memorystore/src/infrastructure/gcp/redis.service.ts +54 -0
- package/templates/recipes/cloud-gcp/pubsub/README.md +43 -0
- package/templates/recipes/cloud-gcp/pubsub/src/infrastructure/gcp/pubsub.module.ts +10 -0
- package/templates/recipes/cloud-gcp/pubsub/src/infrastructure/gcp/pubsub.service.ts +60 -0
- package/templates/recipes/cloud-gcp/secret-manager/README.md +40 -0
- package/templates/recipes/cloud-gcp/secret-manager/src/infrastructure/gcp/secrets.module.ts +10 -0
- package/templates/recipes/cloud-gcp/secret-manager/src/infrastructure/gcp/secrets.service.ts +41 -0
- package/templates/recipes/config-validation/README.md +61 -0
- package/templates/recipes/config-validation/src/config/env.validation.ts +28 -0
- package/templates/recipes/config-validation/tests/unit/config/env.validation.spec.ts +34 -0
- package/templates/recipes/content-digest/README.md +28 -0
- package/templates/recipes/content-digest/src/shared/guards/content-digest.guard.ts +30 -0
- package/templates/recipes/content-digest/src/shared/interceptors/content-digest.interceptor.ts +23 -0
- package/templates/recipes/content-digest/tests/unit/shared/guards/content-digest.guard.spec.ts +56 -0
- package/templates/recipes/content-digest/tests/unit/shared/interceptors/content-digest.interceptor.spec.ts +54 -0
- package/templates/recipes/correlation-id/README.md +44 -0
- package/templates/recipes/correlation-id/src/shared/middleware/correlation-id.middleware.ts +29 -0
- package/templates/recipes/correlation-id/tests/unit/shared/middleware/correlation-id.spec.ts +5 -0
- package/templates/recipes/cors/README.md +45 -0
- package/templates/recipes/cqrs/README.md +39 -0
- package/templates/recipes/cqrs/src/shared/cqrs/example.command.ts +29 -0
- package/templates/recipes/cqrs/tests/unit/shared/cqrs/example.command.spec.ts +38 -0
- package/templates/recipes/csrf/README.md +55 -0
- package/templates/recipes/data-masking/README.md +85 -0
- package/templates/recipes/data-masking/src/shared/decorators/sensitive.decorator.ts +12 -0
- package/templates/recipes/data-masking/src/shared/utils/mask.util.ts +26 -0
- package/templates/recipes/data-masking/tests/unit/shared/utils/mask.util.spec.ts +21 -0
- package/templates/recipes/database-factories/README.md +48 -0
- package/templates/recipes/database-factories/tests/factories/base.factory.ts +51 -0
- package/templates/recipes/database-seeding/README.md +40 -0
- package/templates/recipes/database-seeding/src/infrastructure/database/seed.ts +61 -0
- package/templates/recipes/database-seeding/tests/unit/infrastructure/database/seed.spec.ts +37 -0
- package/templates/recipes/dead-letter-queue/README.md +79 -0
- package/templates/recipes/dead-letter-queue/src/infrastructure/queue/dlq.service.ts +43 -0
- package/templates/recipes/dead-letter-queue/tests/unit/infrastructure/queue/dlq.service.spec.ts +46 -0
- package/templates/recipes/dependabot-renovate/.github/dependabot.yml +52 -0
- package/templates/recipes/dependabot-renovate/README.md +37 -0
- package/templates/recipes/dependabot-renovate/renovate.json +46 -0
- package/templates/recipes/deploy/README.md +13 -0
- package/templates/recipes/deploy/docker-compose/README.md +38 -0
- package/templates/recipes/deploy/docker-compose/docker-compose.yml +43 -0
- package/templates/recipes/deploy/dockerfile/Dockerfile +30 -0
- package/templates/recipes/deploy/dockerfile/README.md +30 -0
- package/templates/recipes/deploy/dockerfile/dot-dockerignore +9 -0
- package/templates/recipes/deploy/kubernetes/README.md +43 -0
- package/templates/recipes/deploy/kubernetes/k8s/configmap.yaml +7 -0
- package/templates/recipes/deploy/kubernetes/k8s/deployment.yaml +45 -0
- package/templates/recipes/deploy/kubernetes/k8s/hpa.yaml +18 -0
- package/templates/recipes/deploy/kubernetes/k8s/ingress.yaml +23 -0
- package/templates/recipes/deploy/kubernetes/k8s/service.yaml +12 -0
- package/templates/recipes/deploy/serverless-framework/README.md +39 -0
- package/templates/recipes/deploy/serverless-framework/serverless.yml.ejs +22 -0
- package/templates/recipes/deploy/terraform/README.md +99 -0
- package/templates/recipes/deploy/terraform/main.tf.ejs +13 -0
- package/templates/recipes/deploy/terraform/modules/app/alb.tf +34 -0
- package/templates/recipes/deploy/terraform/modules/app/iam.tf +30 -0
- package/templates/recipes/deploy/terraform/modules/app/main.tf +80 -0
- package/templates/recipes/deploy/terraform/modules/app/outputs.tf +11 -0
- package/templates/recipes/deploy/terraform/modules/app/security.tf +37 -0
- package/templates/recipes/deploy/terraform/modules/app/variables.tf +42 -0
- package/templates/recipes/deploy/terraform/outputs.tf +4 -0
- package/templates/recipes/deploy/terraform/variables.tf +11 -0
- package/templates/recipes/devcontainer/.devcontainer/Dockerfile +5 -0
- package/templates/recipes/devcontainer/.devcontainer/devcontainer.json +32 -0
- package/templates/recipes/devcontainer/.devcontainer/docker-compose.yml +62 -0
- package/templates/recipes/devcontainer/README.md +31 -0
- package/templates/recipes/distributed-tracing/README.md +45 -0
- package/templates/recipes/distributed-tracing/src/shared/middleware/trace-propagation.middleware.ts +68 -0
- package/templates/recipes/distributed-tracing/tests/unit/shared/middleware/trace-propagation.middleware.spec.ts +76 -0
- package/templates/recipes/docker-compose-dev/Dockerfile.dev +14 -0
- package/templates/recipes/docker-compose-dev/README.md +57 -0
- package/templates/recipes/docker-compose-dev/docker-compose.dev.yml.ejs +54 -0
- package/templates/recipes/docs-site/README.md +41 -0
- package/templates/recipes/docs-site/docs/.vitepress/config.ts.ejs +53 -0
- package/templates/recipes/dpop/README.md +37 -0
- package/templates/recipes/dpop/src/shared/guards/dpop.guard.ts +61 -0
- package/templates/recipes/dpop/tests/unit/shared/guards/dpop.guard.spec.ts +69 -0
- package/templates/recipes/drizzle-postgres/README.md +68 -0
- package/templates/recipes/drizzle-postgres/drizzle.config.ts +10 -0
- package/templates/recipes/drizzle-postgres/src/infrastructure/database/drizzle.module.ts +25 -0
- package/templates/recipes/drizzle-postgres/src/infrastructure/database/schema/example.ts +9 -0
- package/templates/recipes/drizzle-postgres/src/infrastructure/database/schema/index.ts +3 -0
- package/templates/recipes/drizzle-postgres/tests/unit/infrastructure/database/drizzle.module.spec.ts +35 -0
- package/templates/recipes/env-per-environment/.env.development +17 -0
- package/templates/recipes/env-per-environment/.env.production +20 -0
- package/templates/recipes/env-per-environment/.env.test +17 -0
- package/templates/recipes/env-per-environment/README.md +40 -0
- package/templates/recipes/feature-flags/README.md +45 -0
- package/templates/recipes/feature-flags/src/shared/services/feature-flag.service.ts +48 -0
- package/templates/recipes/feature-flags/tests/unit/shared/services/feature-flag.service.spec.ts +36 -0
- package/templates/recipes/file-upload/README.md +104 -0
- package/templates/recipes/file-upload/src/shared/interceptors/file-upload.interceptor.ts +33 -0
- package/templates/recipes/file-upload/src/shared/pipes/file-validation.pipe.ts +45 -0
- package/templates/recipes/file-upload/tests/unit/shared/pipes/file-validation.pipe.spec.ts +56 -0
- package/templates/recipes/filtering/README.md +43 -0
- package/templates/recipes/filtering/src/shared/dto/filter.dto.ts +49 -0
- package/templates/recipes/filtering/tests/unit/shared/dto/filter.dto.spec.ts +38 -0
- package/templates/recipes/graceful-shutdown/README.md +60 -0
- package/templates/recipes/graceful-shutdown/src/shared/lifecycle/shutdown.service.ts +52 -0
- package/templates/recipes/graceful-shutdown/tests/unit/shared/lifecycle/shutdown.service.spec.ts +52 -0
- package/templates/recipes/graphql-mercurius/README.md +37 -0
- package/templates/recipes/graphql-mercurius/src/infrastructure/graphql/graphql.module.ts +18 -0
- package/templates/recipes/graphql-mercurius/tests/unit/infrastructure/graphql/graphql.module.spec.ts +12 -0
- package/templates/recipes/health-checks/README.md +35 -0
- package/templates/recipes/health-checks/src/shared/health/health.controller.ts +26 -0
- package/templates/recipes/health-checks/src/shared/health/health.module.ts +9 -0
- package/templates/recipes/health-checks/tests/unit/shared/health/health.controller.spec.ts +8 -0
- package/templates/recipes/helmet/README.md +43 -0
- package/templates/recipes/http-caching/README.md +63 -0
- package/templates/recipes/http-caching/src/shared/decorators/cache-control.decorator.ts +18 -0
- package/templates/recipes/http-caching/src/shared/interceptors/cache-control.interceptor.ts +43 -0
- package/templates/recipes/http-caching/tests/unit/shared/interceptors/cache-control.interceptor.spec.ts +44 -0
- package/templates/recipes/i18n/README.md +45 -0
- package/templates/recipes/i18n/src/i18n/en/common.json +7 -0
- package/templates/recipes/i18n/src/i18n/nl/common.json +7 -0
- package/templates/recipes/i18n/src/shared/i18n/i18n.module.ts +17 -0
- package/templates/recipes/i18n/tests/unit/shared/i18n/i18n.module.spec.ts +18 -0
- package/templates/recipes/idempotency/README.md +63 -0
- package/templates/recipes/idempotency/src/shared/decorators/idempotent.decorator.ts +4 -0
- package/templates/recipes/idempotency/src/shared/middleware/idempotency.middleware.ts +63 -0
- package/templates/recipes/idempotency/tests/unit/shared/middleware/idempotency.middleware.spec.ts +56 -0
- package/templates/recipes/json-merge-patch/README.md +53 -0
- package/templates/recipes/json-merge-patch/src/shared/pipes/merge-patch.pipe.ts +11 -0
- package/templates/recipes/json-merge-patch/tests/unit/shared/pipes/merge-patch.pipe.spec.ts +30 -0
- package/templates/recipes/json-patch/README.md +54 -0
- package/templates/recipes/json-patch/src/shared/pipes/json-patch.pipe.ts +39 -0
- package/templates/recipes/json-patch/tests/unit/shared/pipes/json-patch.pipe.spec.ts +37 -0
- package/templates/recipes/jwt-auth/README.md +56 -0
- package/templates/recipes/jwt-auth/src/shared/decorators/current-user.decorator.ts +10 -0
- package/templates/recipes/jwt-auth/src/shared/decorators/public.decorator.ts +4 -0
- package/templates/recipes/jwt-auth/src/shared/guards/jwt-auth.guard.ts +24 -0
- package/templates/recipes/jwt-auth/tests/unit/shared/guards/jwt-auth.guard.spec.ts +9 -0
- package/templates/recipes/kysely/README.md +53 -0
- package/templates/recipes/kysely/src/infrastructure/database/database.module.ts +28 -0
- package/templates/recipes/kysely/src/infrastructure/database/types.ts +17 -0
- package/templates/recipes/kysely/tests/unit/infrastructure/database/database.module.spec.ts +35 -0
- package/templates/recipes/license/LICENSE.agpl3 +59 -0
- package/templates/recipes/license/LICENSE.apache +189 -0
- package/templates/recipes/license/LICENSE.bsd3 +28 -0
- package/templates/recipes/license/LICENSE.gpl3 +22 -0
- package/templates/recipes/license/LICENSE.isc +15 -0
- package/templates/recipes/license/LICENSE.mit +21 -0
- package/templates/recipes/license/LICENSE.mpl2 +24 -0
- package/templates/recipes/license/LICENSE.proprietary +76 -0
- package/templates/recipes/license/LICENSE.unlicensed +13 -0
- package/templates/recipes/license/README.md +53 -0
- package/templates/recipes/load-testing/README.md +77 -0
- package/templates/recipes/load-testing/k6/smoke.js +20 -0
- package/templates/recipes/load-testing/k6/stress.js +22 -0
- package/templates/recipes/mfa-totp/README.md +77 -0
- package/templates/recipes/mfa-totp/src/shared/auth/totp.service.ts +26 -0
- package/templates/recipes/mfa-totp/tests/unit/shared/auth/totp.service.spec.ts +65 -0
- package/templates/recipes/mikro-orm/README.md +22 -0
- package/templates/recipes/mikro-orm/src/infrastructure/database/database.module.ts +8 -0
- package/templates/recipes/mikro-orm/src/infrastructure/database/entities/.gitkeep +0 -0
- package/templates/recipes/mikro-orm/src/infrastructure/database/migrations/.gitkeep +0 -0
- package/templates/recipes/mikro-orm/src/infrastructure/database/mikro-orm.config.ts +20 -0
- package/templates/recipes/mikro-orm/tests/unit/infrastructure/database/mikro-orm.config.spec.ts +53 -0
- package/templates/recipes/mongoose/README.md +42 -0
- package/templates/recipes/mongoose/src/infrastructure/database/database.module.ts.ejs +17 -0
- package/templates/recipes/mongoose/tests/unit/infrastructure/database/database.module.spec.ts +25 -0
- package/templates/recipes/multi-tenancy/README.md +49 -0
- package/templates/recipes/multi-tenancy/src/shared/decorators/tenant.decorator.ts +14 -0
- package/templates/recipes/multi-tenancy/src/shared/middleware/tenant.middleware.ts +49 -0
- package/templates/recipes/multi-tenancy/tests/unit/shared/middleware/tenant.middleware.spec.ts +29 -0
- package/templates/recipes/nodemailer/README.md +55 -0
- package/templates/recipes/nodemailer/src/infrastructure/notifications/mail.module.ts +37 -0
- package/templates/recipes/nodemailer/tests/unit/infrastructure/notifications/mail.module.spec.ts +33 -0
- package/templates/recipes/oauth-apple/README.md +68 -0
- package/templates/recipes/oauth-apple/src/shared/auth/apple-auth.guard.ts +5 -0
- package/templates/recipes/oauth-apple/src/shared/auth/apple.strategy.ts +33 -0
- package/templates/recipes/oauth-apple/tests/unit/shared/auth/apple.strategy.spec.ts +65 -0
- package/templates/recipes/oauth-github/README.md +62 -0
- package/templates/recipes/oauth-github/src/shared/auth/github-auth.guard.ts +5 -0
- package/templates/recipes/oauth-github/src/shared/auth/github.strategy.ts +33 -0
- package/templates/recipes/oauth-github/tests/unit/shared/auth/github.strategy.spec.ts +72 -0
- package/templates/recipes/oauth-google/README.md +64 -0
- package/templates/recipes/oauth-google/src/shared/auth/google-auth.guard.ts +5 -0
- package/templates/recipes/oauth-google/src/shared/auth/google.strategy.ts +33 -0
- package/templates/recipes/oauth-google/tests/unit/shared/auth/google.strategy.spec.ts +72 -0
- package/templates/recipes/oauth2-introspection/README.md +62 -0
- package/templates/recipes/oauth2-introspection/src/shared/guards/token-introspection.guard.ts +77 -0
- package/templates/recipes/oauth2-introspection/tests/unit/shared/guards/token-introspection.guard.spec.ts +99 -0
- package/templates/recipes/opentelemetry/README.md +44 -0
- package/templates/recipes/opentelemetry/src/infrastructure/telemetry/tracing.ts +38 -0
- package/templates/recipes/opentelemetry/tests/unit/infrastructure/telemetry/tracing.spec.ts +23 -0
- package/templates/recipes/pagination/README.md +71 -0
- package/templates/recipes/pagination/src/shared/decorators/paginate.decorator.ts +25 -0
- package/templates/recipes/pagination/src/shared/dto/pagination.dto.ts +64 -0
- package/templates/recipes/pagination/src/shared/interceptors/pagination-link.interceptor.ts +31 -0
- package/templates/recipes/pagination/tests/unit/shared/dto/pagination.dto.spec.ts +56 -0
- package/templates/recipes/passport/README.md +41 -0
- package/templates/recipes/passport/src/shared/guards/local-auth.guard.ts +5 -0
- package/templates/recipes/passport/tests/unit/shared/guards/local-auth.guard.spec.ts +17 -0
- package/templates/recipes/pino/README.md +48 -0
- package/templates/recipes/pino/src/infrastructure/logging/logger.module.ts +39 -0
- package/templates/recipes/pino/tests/unit/infrastructure/logging/logger.module.spec.ts +6 -0
- package/templates/recipes/prefer-header/README.md +92 -0
- package/templates/recipes/prefer-header/src/shared/interceptors/prefer.interceptor.ts +60 -0
- package/templates/recipes/prefer-header/tests/unit/shared/interceptors/prefer.interceptor.spec.ts +44 -0
- package/templates/recipes/prisma/README.md +52 -0
- package/templates/recipes/prisma/prisma/schema.prisma.ejs +8 -0
- package/templates/recipes/prisma/src/infrastructure/database/prisma.service.ts +27 -0
- package/templates/recipes/prisma/tests/unit/infrastructure/database/prisma.service.spec.ts +21 -0
- package/templates/recipes/prometheus/README.md +54 -0
- package/templates/recipes/prometheus/src/infrastructure/metrics/metrics.controller.ts +15 -0
- package/templates/recipes/prometheus/src/infrastructure/metrics/metrics.module.ts +29 -0
- package/templates/recipes/prometheus/tests/unit/infrastructure/metrics/metrics.controller.spec.ts +22 -0
- package/templates/recipes/rabbitmq/README.md +53 -0
- package/templates/recipes/rabbitmq/src/infrastructure/queue/queue.module.ts.ejs +27 -0
- package/templates/recipes/rbac-casl/README.md +69 -0
- package/templates/recipes/rbac-casl/src/shared/auth/casl-ability.factory.ts +43 -0
- package/templates/recipes/rbac-casl/src/shared/decorators/roles.decorator.ts +5 -0
- package/templates/recipes/rbac-casl/src/shared/guards/policies.guard.ts +32 -0
- package/templates/recipes/rbac-casl/src/shared/guards/roles.guard.ts +21 -0
- package/templates/recipes/rbac-casl/tests/unit/shared/auth/casl-ability.factory.spec.ts +6 -0
- package/templates/recipes/redis-cache/README.md +55 -0
- package/templates/recipes/redis-cache/src/infrastructure/cache/cache.module.ts +26 -0
- package/templates/recipes/redis-cache/tests/unit/infrastructure/cache/cache.module.spec.ts +12 -0
- package/templates/recipes/request-context/README.md +57 -0
- package/templates/recipes/request-context/src/shared/context/request-context.module.ts +19 -0
- package/templates/recipes/request-context/tests/unit/shared/context/request-context.module.spec.ts +20 -0
- package/templates/recipes/request-logging/README.md +36 -0
- package/templates/recipes/request-logging/src/shared/middleware/request-logging.middleware.ts +40 -0
- package/templates/recipes/request-logging/tests/unit/shared/middleware/request-logging.middleware.spec.ts +64 -0
- package/templates/recipes/s3-minio/README.md +52 -0
- package/templates/recipes/s3-minio/src/infrastructure/storage/storage.module.ts +10 -0
- package/templates/recipes/s3-minio/src/infrastructure/storage/storage.service.ts +72 -0
- package/templates/recipes/s3-minio/tests/unit/infrastructure/storage/storage.service.spec.ts +23 -0
- package/templates/recipes/sdk-generation/README.md +40 -0
- package/templates/recipes/sdk-generation/openapitools.json +21 -0
- package/templates/recipes/sendgrid/README.md +45 -0
- package/templates/recipes/sendgrid/src/infrastructure/notifications/sendgrid.service.ts +60 -0
- package/templates/recipes/sendgrid/tests/unit/infrastructure/notifications/sendgrid.service.spec.ts +59 -0
- package/templates/recipes/sentry/README.md +49 -0
- package/templates/recipes/sentry/src/infrastructure/sentry/sentry.filter.ts +34 -0
- package/templates/recipes/sentry/src/infrastructure/sentry/sentry.module.ts +23 -0
- package/templates/recipes/sentry/tests/unit/infrastructure/sentry/sentry.filter.spec.ts +50 -0
- package/templates/recipes/seq2/README.md +41 -0
- package/templates/recipes/seq2/src/infrastructure/logging/seq.transport.ts +57 -0
- package/templates/recipes/seq2/tests/unit/infrastructure/logging/seq.transport.spec.ts +53 -0
- package/templates/recipes/serialization-groups/README.md +105 -0
- package/templates/recipes/serialization-groups/src/shared/interceptors/serialize.interceptor.ts +29 -0
- package/templates/recipes/serialization-groups/tests/unit/shared/interceptors/serialize.interceptor.spec.ts +48 -0
- package/templates/recipes/soft-delete/README.md +71 -0
- package/templates/recipes/soft-delete/src/shared/decorators/with-deleted.decorator.ts +4 -0
- package/templates/recipes/soft-delete/src/shared/entities/soft-deletable.entity.ts +20 -0
- package/templates/recipes/soft-delete/tests/unit/shared/entities/soft-deletable.entity.spec.ts +35 -0
- package/templates/recipes/sse/README.md +43 -0
- package/templates/recipes/sse/src/shared/gateways/events.sse.controller.ts +21 -0
- package/templates/recipes/sse/tests/unit/shared/gateways/events.sse.controller.spec.ts +32 -0
- package/templates/recipes/swagger/README.md +42 -0
- package/templates/recipes/swagger/src/main.swagger.ts.ejs +20 -0
- package/templates/recipes/swagger/tests/unit/main.swagger.spec.ts +5 -0
- package/templates/recipes/throttler/README.md +49 -0
- package/templates/recipes/throttler/src/shared/guards/throttle.config.ts +33 -0
- package/templates/recipes/throttler/tests/unit/shared/guards/throttle.spec.ts +5 -0
- package/templates/recipes/transactional-outbox/README.md +98 -0
- package/templates/recipes/transactional-outbox/src/infrastructure/outbox/outbox.entity.ts +30 -0
- package/templates/recipes/transactional-outbox/src/infrastructure/outbox/outbox.service.ts +21 -0
- package/templates/recipes/transactional-outbox/tests/unit/infrastructure/outbox/outbox.service.spec.ts +38 -0
- package/templates/recipes/typeorm-mysql/README.md +48 -0
- package/templates/recipes/typeorm-mysql/src/infrastructure/database/database.module.ts.ejs +24 -0
- package/templates/recipes/typeorm-postgres/README.md +101 -0
- package/templates/recipes/typeorm-postgres/src/infrastructure/database/data-source.ts.ejs +14 -0
- package/templates/recipes/typeorm-postgres/src/infrastructure/database/database.module.ts.ejs +24 -0
- package/templates/recipes/typeorm-postgres/src/infrastructure/database/entities/.gitkeep +0 -0
- package/templates/recipes/typeorm-postgres/src/infrastructure/database/entities/index.ts +7 -0
- package/templates/recipes/typeorm-postgres/src/infrastructure/database/migrations/.gitkeep +0 -0
- package/templates/recipes/typeorm-postgres/src/infrastructure/database/migrations/index.ts +10 -0
- package/templates/recipes/typeorm-postgres/tests/unit/infrastructure/database/database.module.spec.ts +11 -0
- package/templates/recipes/webhooks/README.md +76 -0
- package/templates/recipes/webhooks/src/infrastructure/webhooks/webhook.service.ts +53 -0
- package/templates/recipes/webhooks/tests/unit/infrastructure/webhooks/webhook.service.spec.ts +31 -0
- package/templates/recipes/websockets/README.md +44 -0
- package/templates/recipes/websockets/src/shared/gateways/events.gateway.ts +55 -0
- package/templates/recipes/websockets/tests/unit/shared/gateways/events.gateway.spec.ts +23 -0
- package/templates/recipes/winston/README.md +45 -0
- package/templates/recipes/winston/src/infrastructure/logging/logger.module.ts +34 -0
- package/templates/recipes/winston/tests/unit/infrastructure/logging/logger.module.spec.ts +12 -0
- package/templates/recipes/worker-threads/README.md +71 -0
- package/templates/recipes/worker-threads/src/shared/utils/worker-pool.ts +59 -0
- package/templates/recipes/worker-threads/tests/unit/shared/utils/worker-pool.spec.ts +36 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Authentication and authorization patterns
|
|
3
|
+
globs: ["**/*.guard.ts", "**/*.strategy.ts", "**/*.controller.ts", "**/auth/**"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Auth Rules
|
|
7
|
+
|
|
8
|
+
## Route Protection
|
|
9
|
+
- All routes are protected by default via a global `JwtAuthGuard`
|
|
10
|
+
- Use `@Public()` decorator to mark routes that skip authentication
|
|
11
|
+
- Never disable the global guard — use `@Public()` for open endpoints
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
@Public()
|
|
15
|
+
@Post('login')
|
|
16
|
+
login(@Body() dto: LoginDto) { ... }
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## User Extraction
|
|
20
|
+
- Use `@CurrentUser()` parameter decorator to get the authenticated user
|
|
21
|
+
- Never read from `request.user` directly in controllers
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
@Get('profile')
|
|
25
|
+
getProfile(@CurrentUser() user: JwtPayload) {
|
|
26
|
+
return this.userService.findById(user.sub);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## JWT Flow
|
|
31
|
+
- Access tokens are short-lived (15 minutes default)
|
|
32
|
+
- Refresh tokens are long-lived and stored securely
|
|
33
|
+
- The `/auth/refresh` endpoint issues a new access token from a valid refresh token
|
|
34
|
+
- Revoke refresh tokens on password change or logout
|
|
35
|
+
|
|
36
|
+
## Authorization
|
|
37
|
+
- Use custom guards for role-based or permission-based access
|
|
38
|
+
- Place authorization guards after authentication: `@UseGuards(JwtAuthGuard, RolesGuard)`
|
|
39
|
+
- Use `@Roles()` decorator to declare required roles on handlers
|
|
40
|
+
|
|
41
|
+
## Passwords
|
|
42
|
+
- Hash passwords with bcrypt (minimum 10 rounds)
|
|
43
|
+
- Never log or return password fields
|
|
44
|
+
- Exclude password from entity serialization using `class-transformer` `@Exclude()`
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Base rules for NestJS projects generated by spoonfeeder
|
|
3
|
+
globs: ["**/*.ts"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# NestJS Project Rules
|
|
7
|
+
|
|
8
|
+
## Imports
|
|
9
|
+
- Always use the `@/*` path alias for internal imports (maps to `src/*`)
|
|
10
|
+
- Never use relative paths like `../../`
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
- Follow 3-layer architecture: Controller -> Service -> Repository/Data Access
|
|
14
|
+
- Controllers handle HTTP concerns only — no business logic
|
|
15
|
+
- Services contain business logic — max 200 lines per file
|
|
16
|
+
- No private methods in services — extract to separate injectable classes
|
|
17
|
+
|
|
18
|
+
## Services
|
|
19
|
+
- One class per file, one responsibility per class
|
|
20
|
+
- Use descriptive file suffixes: `.service.ts`, `.processor.ts`, `.importer.ts`, `.scheduler.ts`
|
|
21
|
+
- Class names match file purpose: `FileImporter` not `FileImporterService`
|
|
22
|
+
- Inject dependencies via constructor
|
|
23
|
+
|
|
24
|
+
## DTOs
|
|
25
|
+
- Use `class-validator` decorators for all request DTOs
|
|
26
|
+
- Use `class-transformer` for response serialization
|
|
27
|
+
- Place DTOs in a `dto/` subdirectory within each feature module
|
|
28
|
+
|
|
29
|
+
## Modules
|
|
30
|
+
- Organize by feature domain, not by technical layer
|
|
31
|
+
- Each feature gets its own module, controller, and service
|
|
32
|
+
- Export only what other modules need
|
|
33
|
+
|
|
34
|
+
## Error Handling
|
|
35
|
+
- Use NestJS built-in exceptions (`NotFoundException`, `BadRequestException`, etc.)
|
|
36
|
+
- Never catch exceptions just to rethrow them
|
|
37
|
+
- Let the global exception filter handle unexpected errors
|
|
38
|
+
|
|
39
|
+
## Testing
|
|
40
|
+
- Mock only external dependencies (databases, HTTP clients, third-party SDKs)
|
|
41
|
+
- Never mock code you own
|
|
42
|
+
- Tests live in `tests/` at the project root, mirroring `src/` structure
|
|
43
|
+
- Unit tests: `tests/unit/` with `.spec.ts` suffix
|
|
44
|
+
- Integration tests: `tests/integration/` with `.integration.spec.ts` suffix
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Prisma patterns and conventions
|
|
3
|
+
globs: ["**/prisma/**", "**/*.prisma", "**/*.repository.ts", "**/*.service.ts"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Prisma Rules
|
|
7
|
+
|
|
8
|
+
## Schema Design
|
|
9
|
+
- Define models in `prisma/schema.prisma`
|
|
10
|
+
- Use `@id @default(uuid())` for primary keys
|
|
11
|
+
- Use `@map()` and `@@map()` to control database table/column names
|
|
12
|
+
- Add `createdAt` and `updatedAt` fields to all models
|
|
13
|
+
- Define explicit relation names when a model has multiple relations to the same table
|
|
14
|
+
|
|
15
|
+
```prisma
|
|
16
|
+
model User {
|
|
17
|
+
id String @id @default(uuid())
|
|
18
|
+
email String @unique
|
|
19
|
+
name String
|
|
20
|
+
posts Post[]
|
|
21
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
22
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
23
|
+
|
|
24
|
+
@@map("users")
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Client Usage
|
|
29
|
+
- Inject `PrismaService` (extends `PrismaClient`) via NestJS DI
|
|
30
|
+
- Never instantiate `PrismaClient` directly in application code
|
|
31
|
+
- Use `select` or `include` explicitly — avoid fetching unnecessary relations
|
|
32
|
+
- Use transactions (`prisma.$transaction()`) for multi-step writes
|
|
33
|
+
|
|
34
|
+
## Migrations
|
|
35
|
+
- Run `pnpm prisma migrate dev --name <kebab-case-name>` during development
|
|
36
|
+
- Run `pnpm prisma migrate deploy` in CI/production
|
|
37
|
+
- Never edit generated migration SQL after it has been applied
|
|
38
|
+
- Always run `pnpm prisma generate` after schema changes
|
|
39
|
+
|
|
40
|
+
## Error Handling
|
|
41
|
+
- Catch `PrismaClientKnownRequestError` for constraint violations
|
|
42
|
+
- Use error codes: `P2002` (unique), `P2025` (not found), `P2003` (foreign key)
|
|
43
|
+
- Map Prisma errors to NestJS exceptions in the service layer
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Swagger/OpenAPI conventions
|
|
3
|
+
globs: ["**/*.controller.ts", "**/*.dto.ts"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Swagger Rules
|
|
7
|
+
|
|
8
|
+
## DTOs
|
|
9
|
+
- Add `@ApiProperty()` to every property in request and response DTOs
|
|
10
|
+
- Include `description`, `example`, and `type` in `@ApiProperty()` when not obvious
|
|
11
|
+
- Use `@ApiPropertyOptional()` for optional fields
|
|
12
|
+
- Mark enums with `{ enum: MyEnum }` in the decorator
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
export class CreateUserDto {
|
|
16
|
+
@ApiProperty({ description: 'User email address', example: 'user@example.com' })
|
|
17
|
+
@IsEmail()
|
|
18
|
+
email: string;
|
|
19
|
+
|
|
20
|
+
@ApiPropertyOptional({ description: 'Display name', example: 'Jane Doe' })
|
|
21
|
+
@IsString()
|
|
22
|
+
@IsOptional()
|
|
23
|
+
name?: string;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Controllers
|
|
28
|
+
- Add `@ApiTags('<resource>')` at the class level to group endpoints
|
|
29
|
+
- Add `@ApiOperation({ summary: '...' })` to every handler method
|
|
30
|
+
- Use `@ApiResponse()` for non-2xx responses (400, 401, 404, etc.)
|
|
31
|
+
- Use `@ApiParam()` for path parameters and `@ApiQuery()` for query parameters
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
@ApiTags('users')
|
|
35
|
+
@Controller('users')
|
|
36
|
+
export class UserController {
|
|
37
|
+
@Get(':id')
|
|
38
|
+
@ApiOperation({ summary: 'Get user by ID' })
|
|
39
|
+
@ApiParam({ name: 'id', type: String })
|
|
40
|
+
@ApiResponse({ status: 404, description: 'User not found' })
|
|
41
|
+
findOne(@Param('id') id: string) { ... }
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## General
|
|
46
|
+
- Do not use `@ApiExcludeEndpoint()` unless the route is truly internal
|
|
47
|
+
- Keep operation summaries short (under 60 characters)
|
|
48
|
+
- Use consistent naming: plural nouns for resource tags (e.g. `users`, `invoices`)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Testing patterns and conventions
|
|
3
|
+
globs: ["**/*.spec.ts", "**/*.e2e-spec.ts", "tests/**"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing Rules
|
|
7
|
+
|
|
8
|
+
## File Naming and Location
|
|
9
|
+
- Tests live in `tests/` at the project root, mirroring `src/` structure
|
|
10
|
+
- Unit tests: `tests/unit/<path>/<name>.spec.ts`
|
|
11
|
+
- Integration tests: `tests/integration/<path>/<name>.integration.spec.ts`
|
|
12
|
+
- E2E tests: `tests/e2e/<name>.e2e-spec.ts`
|
|
13
|
+
- Test factories: `tests/factories/<name>.factory.ts`
|
|
14
|
+
|
|
15
|
+
## Unit Tests
|
|
16
|
+
- Test every public method — happy path, edge cases, error paths
|
|
17
|
+
- Mock only external dependencies (database, HTTP, third-party SDKs)
|
|
18
|
+
- Never mock code you own — use real instances
|
|
19
|
+
- Use `Test.createTestingModule()` from `@nestjs/testing`
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
const module = await Test.createTestingModule({
|
|
23
|
+
providers: [
|
|
24
|
+
UserService,
|
|
25
|
+
{ provide: UserRepository, useValue: mockRepository },
|
|
26
|
+
],
|
|
27
|
+
}).compile();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Integration Tests
|
|
31
|
+
- Use Testcontainers to spin up real databases
|
|
32
|
+
- Test the full NestJS module with real providers
|
|
33
|
+
- Focus on data persistence and cross-service interactions
|
|
34
|
+
- Clean up test data between tests
|
|
35
|
+
|
|
36
|
+
## E2E Tests
|
|
37
|
+
- Test the HTTP boundary only — use `supertest` against the running app
|
|
38
|
+
- Verify status codes, response shapes, and headers
|
|
39
|
+
- Do not test internal implementation details
|
|
40
|
+
|
|
41
|
+
## Factories
|
|
42
|
+
- Create test data builders in `tests/factories/`
|
|
43
|
+
- Use the builder pattern with sensible defaults
|
|
44
|
+
- Override only the fields relevant to each test
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
export const createUserFixture = (overrides?: Partial<User>): User => ({
|
|
48
|
+
id: randomUUID(),
|
|
49
|
+
email: 'test@example.com',
|
|
50
|
+
name: 'Test User',
|
|
51
|
+
...overrides,
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## General
|
|
56
|
+
- Avoid `any` in test code — use proper types
|
|
57
|
+
- One `describe` block per method, nested `it` blocks per scenario
|
|
58
|
+
- Clean up side effects (files, database rows) in `afterEach` or `afterAll`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: TypeORM patterns and conventions
|
|
3
|
+
globs: ["**/*.entity.ts", "**/*.repository.ts", "**/migrations/**"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeORM Rules
|
|
7
|
+
|
|
8
|
+
## Entities
|
|
9
|
+
- One entity per file, suffix `.entity.ts`
|
|
10
|
+
- Use `@Entity()` with an explicit table name: `@Entity('users')`
|
|
11
|
+
- Always define a primary key with `@PrimaryGeneratedColumn('uuid')`
|
|
12
|
+
- Use `@Column()` with explicit types for non-string columns
|
|
13
|
+
- Add `@CreateDateColumn()` and `@UpdateDateColumn()` to all entities
|
|
14
|
+
- Define relations with explicit join columns and cascade options
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
@Entity('users')
|
|
18
|
+
export class UserEntity {
|
|
19
|
+
@PrimaryGeneratedColumn('uuid')
|
|
20
|
+
id: string;
|
|
21
|
+
|
|
22
|
+
@Column({ type: 'varchar', length: 255 })
|
|
23
|
+
email: string;
|
|
24
|
+
|
|
25
|
+
@CreateDateColumn()
|
|
26
|
+
createdAt: Date;
|
|
27
|
+
|
|
28
|
+
@UpdateDateColumn()
|
|
29
|
+
updatedAt: Date;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Repositories
|
|
34
|
+
- Use the repository pattern via `@InjectRepository()`
|
|
35
|
+
- Never call `getRepository()` or `getManager()` directly
|
|
36
|
+
- Create custom repository methods for complex queries
|
|
37
|
+
- Use QueryBuilder for queries involving joins or conditions beyond simple `find`
|
|
38
|
+
|
|
39
|
+
## Migrations
|
|
40
|
+
- Never modify an applied migration — create a new one
|
|
41
|
+
- Implement both `up()` and `down()` methods
|
|
42
|
+
- Use `migration:generate` to auto-generate from entity changes
|
|
43
|
+
- Test both directions locally before committing
|
|
44
|
+
|
|
45
|
+
## Queries
|
|
46
|
+
- Use parameterized queries — never interpolate user input into SQL
|
|
47
|
+
- Prefer `findOne({ where: { ... } })` over `findOneBy()` for clarity
|
|
48
|
+
- Use `select` to limit returned columns in performance-sensitive queries
|
|
49
|
+
- Add `@Index()` decorators for frequently queried columns
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# API Key Authentication
|
|
2
|
+
|
|
3
|
+
Custom API key authentication using the `x-api-key` header for NestJS applications.
|
|
4
|
+
|
|
5
|
+
## Links
|
|
6
|
+
|
|
7
|
+
- [NestJS Guards Documentation](https://docs.nestjs.com/guards)
|
|
8
|
+
- [NestJS Security Documentation](https://docs.nestjs.com/security/authentication)
|
|
9
|
+
|
|
10
|
+
## Dependencies
|
|
11
|
+
|
|
12
|
+
| Package | Version | Purpose |
|
|
13
|
+
| ---------------- | --------- | ------------------------------------------------------------ |
|
|
14
|
+
| `@nestjs/common` | `10.4.15` | Core NestJS decorators and guards (included in base) |
|
|
15
|
+
| `@nestjs/config` | `3.3.0` | Configuration module for reading API keys (included in base) |
|
|
16
|
+
|
|
17
|
+
## Environment Variables
|
|
18
|
+
|
|
19
|
+
| Variable | Default | Description |
|
|
20
|
+
| --------- | ------- | ---------------------------------------------------------- |
|
|
21
|
+
| `API_KEY` | — | The API key clients must provide in the `x-api-key` header |
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// Protect a route with API key authentication
|
|
27
|
+
@Controller('webhooks')
|
|
28
|
+
export class WebhookController {
|
|
29
|
+
@UseGuards(ApiKeyGuard)
|
|
30
|
+
@Post('stripe')
|
|
31
|
+
handleStripeWebhook(@Body() payload: any) {
|
|
32
|
+
return { received: true };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Or apply globally in main.ts
|
|
37
|
+
app.useGlobalGuards(app.get(ApiKeyGuard));
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Generated Files
|
|
41
|
+
|
|
42
|
+
| File | Description |
|
|
43
|
+
| ------------------------------------ | ---------------------------------------------------------------------- |
|
|
44
|
+
| `src/shared/guards/api-key.guard.ts` | Guard that validates the `x-api-key` header against configured API key |
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import { FastifyRequest } from 'fastify';
|
|
4
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class ApiKeyGuard implements CanActivate {
|
|
8
|
+
constructor(private readonly configService: ConfigService) {}
|
|
9
|
+
|
|
10
|
+
canActivate(context: ExecutionContext): boolean {
|
|
11
|
+
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
|
12
|
+
const apiKey = request.headers['x-api-key'] as string;
|
|
13
|
+
const validApiKey = this.configService.get<string>('API_KEY');
|
|
14
|
+
|
|
15
|
+
if (!apiKey || !validApiKey) {
|
|
16
|
+
throw new UnauthorizedException('API key is required');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!this.isEqual(apiKey, validApiKey)) {
|
|
20
|
+
throw new UnauthorizedException('Invalid API key');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private isEqual(a: string, b: string): boolean {
|
|
27
|
+
const bufA = Buffer.from(a);
|
|
28
|
+
const bufB = Buffer.from(b);
|
|
29
|
+
if (bufA.length !== bufB.length) return false;
|
|
30
|
+
return timingSafeEqual(bufA, bufB);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import { ApiKeyGuard } from '../../../../src/shared/guards/api-key.guard';
|
|
4
|
+
|
|
5
|
+
describe('ApiKeyGuard', () => {
|
|
6
|
+
let guard: ApiKeyGuard;
|
|
7
|
+
let configService: ConfigService;
|
|
8
|
+
|
|
9
|
+
const createMockContext = (apiKey?: string): ExecutionContext =>
|
|
10
|
+
({
|
|
11
|
+
switchToHttp: () => ({
|
|
12
|
+
getRequest: () => ({
|
|
13
|
+
headers: apiKey !== undefined ? { 'x-api-key': apiKey } : {},
|
|
14
|
+
}),
|
|
15
|
+
}),
|
|
16
|
+
}) as unknown as ExecutionContext;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
configService = { get: jest.fn().mockReturnValue('valid-key') } as unknown as ConfigService;
|
|
20
|
+
guard = new ApiKeyGuard(configService);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should throw UnauthorizedException when x-api-key header is missing', () => {
|
|
24
|
+
const context = createMockContext();
|
|
25
|
+
|
|
26
|
+
expect(() => guard.canActivate(context)).toThrow(UnauthorizedException);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should throw UnauthorizedException when api key does not match', () => {
|
|
30
|
+
const context = createMockContext('wrong-key');
|
|
31
|
+
|
|
32
|
+
expect(() => guard.canActivate(context)).toThrow(UnauthorizedException);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should return true when api key is valid', () => {
|
|
36
|
+
const context = createMockContext('valid-key');
|
|
37
|
+
|
|
38
|
+
expect(guard.canActivate(context)).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# API Versioning
|
|
2
|
+
|
|
3
|
+
URI-based API versioning for NestJS.
|
|
4
|
+
|
|
5
|
+
## Links
|
|
6
|
+
|
|
7
|
+
- [NestJS Versioning](https://docs.nestjs.com/techniques/versioning)
|
|
8
|
+
|
|
9
|
+
## Dependencies
|
|
10
|
+
|
|
11
|
+
No additional dependencies required. Versioning is built into NestJS core.
|
|
12
|
+
|
|
13
|
+
| Package | Version | Purpose |
|
|
14
|
+
| ------- | ------- | -------------------------- |
|
|
15
|
+
| (none) | - | Built-in to `@nestjs/core` |
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Enable versioning in `main.ts`:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { VersioningType } from '@nestjs/common';
|
|
23
|
+
|
|
24
|
+
app.enableVersioning({
|
|
25
|
+
type: VersioningType.URI,
|
|
26
|
+
defaultVersion: '1',
|
|
27
|
+
prefix: 'v',
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use version-specific controllers:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
@Controller({ path: 'users', version: '1' })
|
|
35
|
+
export class UsersV1Controller {
|
|
36
|
+
@Get()
|
|
37
|
+
findAll() {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Controller({ path: 'users', version: '2' })
|
|
43
|
+
export class UsersV2Controller {
|
|
44
|
+
@Get()
|
|
45
|
+
findAll() {
|
|
46
|
+
return { items: [], total: 0 };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or version individual routes:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
@Version('2')
|
|
55
|
+
@Get()
|
|
56
|
+
findAllV2() { return { items: [], total: 0 }; }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Generated Files
|
|
60
|
+
|
|
61
|
+
| File | Description |
|
|
62
|
+
| ------ | ------------------------------------------- |
|
|
63
|
+
| (none) | Applied directly in `main.ts` configuration |
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Audit Trail
|
|
2
|
+
|
|
3
|
+
Record entity changes with user, action, and diff for compliance and debugging.
|
|
4
|
+
|
|
5
|
+
## Links
|
|
6
|
+
|
|
7
|
+
- [NestJS Interceptors](https://docs.nestjs.com/interceptors)
|
|
8
|
+
- [TypeORM Subscribers](https://typeorm.io/listeners-and-subscribers#what-is-a-subscriber)
|
|
9
|
+
|
|
10
|
+
## Dependencies
|
|
11
|
+
|
|
12
|
+
None — uses NestJS interceptors and decorators.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Interceptor Approach
|
|
17
|
+
|
|
18
|
+
Apply `AuditInterceptor` globally or per-controller to log mutating requests:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { AuditInterceptor } from '@/shared/interceptors/audit.interceptor';
|
|
22
|
+
|
|
23
|
+
@Module({
|
|
24
|
+
providers: [
|
|
25
|
+
{
|
|
26
|
+
provide: APP_INTERCEPTOR,
|
|
27
|
+
useClass: AuditInterceptor,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
})
|
|
31
|
+
export class AppModule {}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or apply per-controller:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
@UseInterceptors(AuditInterceptor)
|
|
38
|
+
@Controller('users')
|
|
39
|
+
export class UsersController {}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use the `@Auditable()` decorator to mark controllers or handlers for selective auditing:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
@Auditable()
|
|
46
|
+
@Controller('orders')
|
|
47
|
+
export class OrdersController {}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### TypeORM Subscriber Approach
|
|
51
|
+
|
|
52
|
+
For entity-level auditing, use a TypeORM subscriber:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
@EventSubscriber()
|
|
56
|
+
export class AuditSubscriber implements EntitySubscriberInterface {
|
|
57
|
+
afterUpdate(event: UpdateEvent<any>): void {
|
|
58
|
+
// Log old vs new values from event.databaseEntity and event.entity
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Compliance Use Cases
|
|
64
|
+
|
|
65
|
+
- **GDPR**: Track who accessed or modified personal data
|
|
66
|
+
- **SOC 2**: Maintain change logs for audit controls
|
|
67
|
+
- **HIPAA**: Record access to protected health information
|
|
68
|
+
- **Financial**: Immutable trail for transaction modifications
|
|
69
|
+
|
|
70
|
+
## Generated Files
|
|
71
|
+
|
|
72
|
+
| File | Description |
|
|
73
|
+
| ---------------------------------------------- | ------------------------------------------------------- |
|
|
74
|
+
| `src/shared/interceptors/audit.interceptor.ts` | Interceptor that records mutating request audit entries |
|
|
75
|
+
| `src/shared/decorators/auditable.decorator.ts` | Metadata decorator to mark auditable controllers |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { tap } from 'rxjs/operators';
|
|
4
|
+
import { FastifyRequest } from 'fastify';
|
|
5
|
+
|
|
6
|
+
export interface AuditEntry {
|
|
7
|
+
userId: string | null;
|
|
8
|
+
action: string;
|
|
9
|
+
entityName: string;
|
|
10
|
+
entityId: string;
|
|
11
|
+
changes: Record<string, { from: unknown; to: unknown }>;
|
|
12
|
+
timestamp: Date;
|
|
13
|
+
ip: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class AuditInterceptor implements NestInterceptor {
|
|
18
|
+
// TODO: Replace this in-memory store with a persistent backend (database, message queue).
|
|
19
|
+
// This implementation is for development only — entries are lost on restart and unbounded in memory.
|
|
20
|
+
private readonly entries: AuditEntry[] = [];
|
|
21
|
+
|
|
22
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
|
|
23
|
+
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
|
24
|
+
const method = request.method;
|
|
25
|
+
|
|
26
|
+
return next.handle().pipe(
|
|
27
|
+
tap((response) => {
|
|
28
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
29
|
+
this.entries.push({
|
|
30
|
+
userId: (request as any).user?.sub ?? null,
|
|
31
|
+
action: method,
|
|
32
|
+
entityName: context.getClass().name,
|
|
33
|
+
entityId: (request.params as any)?.id ?? 'unknown',
|
|
34
|
+
changes: {},
|
|
35
|
+
timestamp: new Date(),
|
|
36
|
+
ip: request.ip,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getEntries(): AuditEntry[] {
|
|
44
|
+
return [...this.entries];
|
|
45
|
+
}
|
|
46
|
+
}
|
package/templates/recipes/audit-trail/tests/unit/shared/interceptors/audit.interceptor.spec.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuditInterceptor,
|
|
3
|
+
AuditEntry,
|
|
4
|
+
} from '../../../../src/shared/interceptors/audit.interceptor';
|
|
5
|
+
import { ExecutionContext, CallHandler } from '@nestjs/common';
|
|
6
|
+
import { of } from 'rxjs';
|
|
7
|
+
|
|
8
|
+
function createMockExecutionContext(overrides: {
|
|
9
|
+
method: string;
|
|
10
|
+
params?: Record<string, string>;
|
|
11
|
+
user?: { sub: string };
|
|
12
|
+
ip?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}): ExecutionContext {
|
|
15
|
+
return {
|
|
16
|
+
switchToHttp: () => ({
|
|
17
|
+
getRequest: () => ({
|
|
18
|
+
method: overrides.method,
|
|
19
|
+
params: overrides.params ?? {},
|
|
20
|
+
user: overrides.user,
|
|
21
|
+
ip: overrides.ip ?? '127.0.0.1',
|
|
22
|
+
}),
|
|
23
|
+
}),
|
|
24
|
+
getClass: () => ({ name: overrides.className ?? 'TestController' }),
|
|
25
|
+
} as unknown as ExecutionContext;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function createMockCallHandler(response: unknown = {}): CallHandler {
|
|
29
|
+
return { handle: () => of(response) };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('AuditInterceptor', () => {
|
|
33
|
+
let interceptor: AuditInterceptor;
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
interceptor = new AuditInterceptor();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should capture an audit entry for a POST request', (done) => {
|
|
40
|
+
const context = createMockExecutionContext({
|
|
41
|
+
method: 'POST',
|
|
42
|
+
params: { id: '42' },
|
|
43
|
+
user: { sub: 'user-1' },
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
interceptor.intercept(context, createMockCallHandler()).subscribe(() => {
|
|
47
|
+
const entries = interceptor.getEntries();
|
|
48
|
+
|
|
49
|
+
expect(entries).toHaveLength(1);
|
|
50
|
+
expect(entries[0]).toEqual(
|
|
51
|
+
expect.objectContaining({
|
|
52
|
+
action: 'POST',
|
|
53
|
+
entityId: '42',
|
|
54
|
+
userId: 'user-1',
|
|
55
|
+
entityName: 'TestController',
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
done();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should not capture an entry for a GET request', (done) => {
|
|
63
|
+
const context = createMockExecutionContext({ method: 'GET' });
|
|
64
|
+
|
|
65
|
+
interceptor.intercept(context, createMockCallHandler()).subscribe(() => {
|
|
66
|
+
expect(interceptor.getEntries()).toHaveLength(0);
|
|
67
|
+
done();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should default userId to null when no user is present', (done) => {
|
|
72
|
+
const context = createMockExecutionContext({ method: 'DELETE' });
|
|
73
|
+
|
|
74
|
+
interceptor.intercept(context, createMockCallHandler()).subscribe(() => {
|
|
75
|
+
const entries = interceptor.getEntries();
|
|
76
|
+
|
|
77
|
+
expect(entries).toHaveLength(1);
|
|
78
|
+
expect(entries[0].userId).toBeNull();
|
|
79
|
+
done();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|