nestjs-ddd-cli 2.2.1 → 3.2.1

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.
Files changed (254) hide show
  1. package/README.md +247 -408
  2. package/ddd.schema.json +111 -0
  3. package/dist/commands/aggregate-validator.d.ts +9 -0
  4. package/dist/commands/aggregate-validator.js +953 -0
  5. package/dist/commands/aggregate-validator.js.map +1 -0
  6. package/dist/commands/ai-assist.d.ts +8 -0
  7. package/dist/commands/ai-assist.js +337 -0
  8. package/dist/commands/ai-assist.js.map +1 -0
  9. package/dist/commands/api-contracts.d.ts +9 -0
  10. package/dist/commands/api-contracts.js +1368 -0
  11. package/dist/commands/api-contracts.js.map +1 -0
  12. package/dist/commands/api-docs.d.ts +8 -0
  13. package/dist/commands/api-docs.js +408 -0
  14. package/dist/commands/api-docs.js.map +1 -0
  15. package/dist/commands/api-versioning.d.ts +11 -0
  16. package/dist/commands/api-versioning.js +643 -0
  17. package/dist/commands/api-versioning.js.map +1 -0
  18. package/dist/commands/audit-logging.d.ts +9 -0
  19. package/dist/commands/audit-logging.js +1129 -0
  20. package/dist/commands/audit-logging.js.map +1 -0
  21. package/dist/commands/batch-generate.d.ts +10 -0
  22. package/dist/commands/batch-generate.js +405 -0
  23. package/dist/commands/batch-generate.js.map +1 -0
  24. package/dist/commands/caching-strategies.d.ts +9 -0
  25. package/dist/commands/caching-strategies.js +874 -0
  26. package/dist/commands/caching-strategies.js.map +1 -0
  27. package/dist/commands/code-analyzer.d.ts +42 -0
  28. package/dist/commands/code-analyzer.js +474 -0
  29. package/dist/commands/code-analyzer.js.map +1 -0
  30. package/dist/commands/database-seeding.d.ts +6 -0
  31. package/dist/commands/database-seeding.js +621 -0
  32. package/dist/commands/database-seeding.js.map +1 -0
  33. package/dist/commands/db-optimization.d.ts +7 -0
  34. package/dist/commands/db-optimization.js +687 -0
  35. package/dist/commands/db-optimization.js.map +1 -0
  36. package/dist/commands/dependency-graph.d.ts +6 -0
  37. package/dist/commands/dependency-graph.js +329 -0
  38. package/dist/commands/dependency-graph.js.map +1 -0
  39. package/dist/commands/doctor-enhanced.d.ts +22 -0
  40. package/dist/commands/doctor-enhanced.js +543 -0
  41. package/dist/commands/doctor-enhanced.js.map +1 -0
  42. package/dist/commands/doctor.d.ts +4 -0
  43. package/dist/commands/doctor.js +151 -0
  44. package/dist/commands/doctor.js.map +1 -0
  45. package/dist/commands/env-manager.d.ts +6 -0
  46. package/dist/commands/env-manager.js +419 -0
  47. package/dist/commands/env-manager.js.map +1 -0
  48. package/dist/commands/event-sourcing-full.d.ts +10 -0
  49. package/dist/commands/event-sourcing-full.js +1107 -0
  50. package/dist/commands/event-sourcing-full.js.map +1 -0
  51. package/dist/commands/feature-flags.d.ts +9 -0
  52. package/dist/commands/feature-flags.js +824 -0
  53. package/dist/commands/feature-flags.js.map +1 -0
  54. package/dist/commands/filter-dsl.d.ts +10 -0
  55. package/dist/commands/filter-dsl.js +1407 -0
  56. package/dist/commands/filter-dsl.js.map +1 -0
  57. package/dist/commands/generate-all.js +485 -32
  58. package/dist/commands/generate-all.js.map +1 -1
  59. package/dist/commands/generate-deployment.d.ts +8 -0
  60. package/dist/commands/generate-deployment.js +746 -0
  61. package/dist/commands/generate-deployment.js.map +1 -0
  62. package/dist/commands/generate-domain-service.d.ts +14 -0
  63. package/dist/commands/generate-domain-service.js +796 -0
  64. package/dist/commands/generate-domain-service.js.map +1 -0
  65. package/dist/commands/generate-entity.js +82 -24
  66. package/dist/commands/generate-entity.js.map +1 -1
  67. package/dist/commands/generate-from-schema.d.ts +56 -0
  68. package/dist/commands/generate-from-schema.js +222 -0
  69. package/dist/commands/generate-from-schema.js.map +1 -0
  70. package/dist/commands/generate-orchestrator.d.ts +14 -0
  71. package/dist/commands/generate-orchestrator.js +887 -0
  72. package/dist/commands/generate-orchestrator.js.map +1 -0
  73. package/dist/commands/generate-repository.d.ts +14 -0
  74. package/dist/commands/generate-repository.js +1019 -0
  75. package/dist/commands/generate-repository.js.map +1 -0
  76. package/dist/commands/generate-shared.d.ts +4 -0
  77. package/dist/commands/generate-shared.js +388 -0
  78. package/dist/commands/generate-shared.js.map +1 -0
  79. package/dist/commands/generate-value-object.d.ts +32 -0
  80. package/dist/commands/generate-value-object.js +700 -0
  81. package/dist/commands/generate-value-object.js.map +1 -0
  82. package/dist/commands/graphql-subscriptions.d.ts +6 -0
  83. package/dist/commands/graphql-subscriptions.js +607 -0
  84. package/dist/commands/graphql-subscriptions.js.map +1 -0
  85. package/dist/commands/graphql-types.d.ts +5 -0
  86. package/dist/commands/graphql-types.js +423 -0
  87. package/dist/commands/graphql-types.js.map +1 -0
  88. package/dist/commands/health-probes-advanced.d.ts +6 -0
  89. package/dist/commands/health-probes-advanced.js +655 -0
  90. package/dist/commands/health-probes-advanced.js.map +1 -0
  91. package/dist/commands/i18n-setup.d.ts +10 -0
  92. package/dist/commands/i18n-setup.js +677 -0
  93. package/dist/commands/i18n-setup.js.map +1 -0
  94. package/dist/commands/init-config.d.ts +6 -0
  95. package/dist/commands/init-config.js +370 -0
  96. package/dist/commands/init-config.js.map +1 -0
  97. package/dist/commands/init-project.js +56 -6
  98. package/dist/commands/init-project.js.map +1 -1
  99. package/dist/commands/interactive-scaffold.d.ts +5 -0
  100. package/dist/commands/interactive-scaffold.js +271 -0
  101. package/dist/commands/interactive-scaffold.js.map +1 -0
  102. package/dist/commands/metrics-prometheus.d.ts +6 -0
  103. package/dist/commands/metrics-prometheus.js +681 -0
  104. package/dist/commands/metrics-prometheus.js.map +1 -0
  105. package/dist/commands/migration-engine.d.ts +6 -0
  106. package/dist/commands/migration-engine.js +446 -0
  107. package/dist/commands/migration-engine.js.map +1 -0
  108. package/dist/commands/migration.d.ts +12 -0
  109. package/dist/commands/migration.js +484 -0
  110. package/dist/commands/migration.js.map +1 -0
  111. package/dist/commands/monorepo.d.ts +8 -0
  112. package/dist/commands/monorepo.js +483 -0
  113. package/dist/commands/monorepo.js.map +1 -0
  114. package/dist/commands/multi-database.d.ts +5 -0
  115. package/dist/commands/multi-database.js +439 -0
  116. package/dist/commands/multi-database.js.map +1 -0
  117. package/dist/commands/observability-tracing.d.ts +10 -0
  118. package/dist/commands/observability-tracing.js +740 -0
  119. package/dist/commands/observability-tracing.js.map +1 -0
  120. package/dist/commands/openapi-export.d.ts +8 -0
  121. package/dist/commands/openapi-export.js +359 -0
  122. package/dist/commands/openapi-export.js.map +1 -0
  123. package/dist/commands/perf-analyzer.d.ts +8 -0
  124. package/dist/commands/perf-analyzer.js +423 -0
  125. package/dist/commands/perf-analyzer.js.map +1 -0
  126. package/dist/commands/rate-limiting.d.ts +10 -0
  127. package/dist/commands/rate-limiting.js +953 -0
  128. package/dist/commands/rate-limiting.js.map +1 -0
  129. package/dist/commands/recipe-plugin.d.ts +56 -0
  130. package/dist/commands/recipe-plugin.js +315 -0
  131. package/dist/commands/recipe-plugin.js.map +1 -0
  132. package/dist/commands/recipe.d.ts +6 -0
  133. package/dist/commands/recipe.js +3941 -0
  134. package/dist/commands/recipe.js.map +1 -0
  135. package/dist/commands/recipes/elasticsearch.recipe.d.ts +1 -0
  136. package/dist/commands/recipes/elasticsearch.recipe.js +761 -0
  137. package/dist/commands/recipes/elasticsearch.recipe.js.map +1 -0
  138. package/dist/commands/recipes/event-sourcing.recipe.d.ts +1 -0
  139. package/dist/commands/recipes/event-sourcing.recipe.js +889 -0
  140. package/dist/commands/recipes/event-sourcing.recipe.js.map +1 -0
  141. package/dist/commands/recipes/index.d.ts +7 -0
  142. package/dist/commands/recipes/index.js +24 -0
  143. package/dist/commands/recipes/index.js.map +1 -0
  144. package/dist/commands/recipes/message-queue.recipe.d.ts +1 -0
  145. package/dist/commands/recipes/message-queue.recipe.js +706 -0
  146. package/dist/commands/recipes/message-queue.recipe.js.map +1 -0
  147. package/dist/commands/recipes/middleware.recipe.d.ts +1 -0
  148. package/dist/commands/recipes/middleware.recipe.js +383 -0
  149. package/dist/commands/recipes/middleware.recipe.js.map +1 -0
  150. package/dist/commands/recipes/multi-tenancy.recipe.d.ts +1 -0
  151. package/dist/commands/recipes/multi-tenancy.recipe.js +520 -0
  152. package/dist/commands/recipes/multi-tenancy.recipe.js.map +1 -0
  153. package/dist/commands/recipes/oauth2.recipe.d.ts +1 -0
  154. package/dist/commands/recipes/oauth2.recipe.js +472 -0
  155. package/dist/commands/recipes/oauth2.recipe.js.map +1 -0
  156. package/dist/commands/recipes/websocket.recipe.d.ts +1 -0
  157. package/dist/commands/recipes/websocket.recipe.js +453 -0
  158. package/dist/commands/recipes/websocket.recipe.js.map +1 -0
  159. package/dist/commands/resilience-patterns.d.ts +13 -0
  160. package/dist/commands/resilience-patterns.js +1029 -0
  161. package/dist/commands/resilience-patterns.js.map +1 -0
  162. package/dist/commands/security-patterns.d.ts +11 -0
  163. package/dist/commands/security-patterns.js +2233 -0
  164. package/dist/commands/security-patterns.js.map +1 -0
  165. package/dist/commands/template-debug.d.ts +27 -0
  166. package/dist/commands/template-debug.js +388 -0
  167. package/dist/commands/template-debug.js.map +1 -0
  168. package/dist/commands/test-factory-full.d.ts +9 -0
  169. package/dist/commands/test-factory-full.js +1570 -0
  170. package/dist/commands/test-factory-full.js.map +1 -0
  171. package/dist/commands/test-scaffold.d.ts +7 -0
  172. package/dist/commands/test-scaffold.js +621 -0
  173. package/dist/commands/test-scaffold.js.map +1 -0
  174. package/dist/index.js +1088 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/templates/ai-context/CLAUDE.md.hbs +158 -0
  177. package/dist/templates/ai-context/conventions.md.hbs +154 -0
  178. package/dist/templates/command/create-command.hbs +6 -14
  179. package/dist/templates/command/delete-command.hbs +19 -0
  180. package/dist/templates/command/update-command.hbs +24 -0
  181. package/dist/templates/controller/controller.hbs +64 -17
  182. package/dist/templates/dto/create-dto.hbs +29 -5
  183. package/dist/templates/dto/filter-dto.hbs +52 -0
  184. package/dist/templates/dto/filter-query.dto.hbs +148 -0
  185. package/dist/templates/dto/paginated-response.dto.hbs +29 -0
  186. package/dist/templates/dto/pagination-query.dto.hbs +30 -0
  187. package/dist/templates/dto/response-dto.hbs +38 -0
  188. package/dist/templates/dto/update-dto.hbs +11 -0
  189. package/dist/templates/entity/entity.hbs +32 -1
  190. package/dist/templates/event/domain-event.hbs +33 -7
  191. package/dist/templates/event/event-handler.hbs +40 -0
  192. package/dist/templates/exception/base-exceptions.hbs +69 -0
  193. package/dist/templates/exception/entity-not-found.exception.hbs +7 -0
  194. package/dist/templates/mapper/mapper.hbs +49 -24
  195. package/dist/templates/module/module.hbs +34 -10
  196. package/dist/templates/orm-entity/orm-entity.hbs +63 -12
  197. package/dist/templates/prisma/prisma-mapper.hbs +71 -0
  198. package/dist/templates/prisma/prisma-repository.hbs +114 -0
  199. package/dist/templates/prisma/prisma-schema.hbs +20 -0
  200. package/dist/templates/prisma/prisma-service.hbs +51 -0
  201. package/dist/templates/query/get-all.query.hbs +50 -0
  202. package/dist/templates/query/get-by-id.query.hbs +31 -0
  203. package/dist/templates/repository/repository.hbs +55 -13
  204. package/dist/templates/resolver/graphql-input.hbs +54 -0
  205. package/dist/templates/resolver/graphql-type.hbs +58 -0
  206. package/dist/templates/resolver/pagination-args.hbs +33 -0
  207. package/dist/templates/resolver/resolver.hbs +62 -0
  208. package/dist/templates/shared/prisma-query-builder.util.hbs +189 -0
  209. package/dist/templates/shared/query-builder.util.hbs +218 -0
  210. package/dist/templates/test/controller.spec.hbs +124 -0
  211. package/dist/templates/test/repository.spec.hbs +158 -0
  212. package/dist/templates/test/usecase.spec.hbs +116 -0
  213. package/dist/templates/usecase/create-usecase.hbs +19 -7
  214. package/dist/templates/usecase/delete-usecase.hbs +17 -0
  215. package/dist/templates/usecase/update-usecase.hbs +31 -0
  216. package/dist/utils/config.utils.d.ts +45 -0
  217. package/dist/utils/config.utils.js +211 -0
  218. package/dist/utils/config.utils.js.map +1 -0
  219. package/dist/utils/error.utils.d.ts +145 -0
  220. package/dist/utils/error.utils.js +422 -0
  221. package/dist/utils/error.utils.js.map +1 -0
  222. package/dist/utils/field.utils.d.ts +54 -0
  223. package/dist/utils/field.utils.js +389 -0
  224. package/dist/utils/field.utils.js.map +1 -0
  225. package/dist/utils/file.utils.d.ts +19 -8
  226. package/dist/utils/file.utils.js +135 -4
  227. package/dist/utils/file.utils.js.map +1 -1
  228. package/dist/utils/idempotency.utils.d.ts +123 -0
  229. package/dist/utils/idempotency.utils.js +444 -0
  230. package/dist/utils/idempotency.utils.js.map +1 -0
  231. package/dist/utils/naming.utils.js +24 -5
  232. package/dist/utils/naming.utils.js.map +1 -1
  233. package/dist/utils/performance.utils.d.ts +37 -0
  234. package/dist/utils/performance.utils.js +158 -0
  235. package/dist/utils/performance.utils.js.map +1 -0
  236. package/dist/utils/relation.utils.d.ts +92 -0
  237. package/dist/utils/relation.utils.js +388 -0
  238. package/dist/utils/relation.utils.js.map +1 -0
  239. package/dist/utils/rollback.utils.d.ts +49 -0
  240. package/dist/utils/rollback.utils.js +306 -0
  241. package/dist/utils/rollback.utils.js.map +1 -0
  242. package/dist/utils/schema.utils.d.ts +123 -0
  243. package/dist/utils/schema.utils.js +419 -0
  244. package/dist/utils/schema.utils.js.map +1 -0
  245. package/dist/utils/security.utils.d.ts +57 -0
  246. package/dist/utils/security.utils.js +315 -0
  247. package/dist/utils/security.utils.js.map +1 -0
  248. package/dist/utils/template-engine.utils.d.ts +80 -0
  249. package/dist/utils/template-engine.utils.js +463 -0
  250. package/dist/utils/template-engine.utils.js.map +1 -0
  251. package/dist/utils/validation-registry.utils.d.ts +160 -0
  252. package/dist/utils/validation-registry.utils.js +526 -0
  253. package/dist/utils/validation-registry.utils.js.map +1 -0
  254. package/package.json +3 -1
@@ -0,0 +1,520 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.applyMultiTenancyRecipe = applyMultiTenancyRecipe;
40
+ const path = __importStar(require("path"));
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const file_utils_1 = require("../../utils/file.utils");
43
+ async function applyMultiTenancyRecipe(basePath) {
44
+ const sharedPath = path.join(basePath, 'src/shared');
45
+ const tenantPath = path.join(sharedPath, 'tenancy');
46
+ await (0, file_utils_1.ensureDir)(tenantPath);
47
+ await (0, file_utils_1.ensureDir)(path.join(tenantPath, 'middleware'));
48
+ await (0, file_utils_1.ensureDir)(path.join(tenantPath, 'guards'));
49
+ await (0, file_utils_1.ensureDir)(path.join(tenantPath, 'decorators'));
50
+ // Tenant context
51
+ const tenantContextContent = `import { AsyncLocalStorage } from "async_hooks";
52
+
53
+ export interface TenantContext {
54
+ tenantId: string;
55
+ tenantSlug?: string;
56
+ tenantName?: string;
57
+ config?: TenantConfig;
58
+ }
59
+
60
+ export interface TenantConfig {
61
+ features: string[];
62
+ limits: {
63
+ maxUsers?: number;
64
+ maxStorage?: number;
65
+ [key: string]: any;
66
+ };
67
+ settings: Record<string, any>;
68
+ }
69
+
70
+ export const tenantStorage = new AsyncLocalStorage<TenantContext>();
71
+
72
+ /**
73
+ * Get current tenant context
74
+ */
75
+ export function getCurrentTenant(): TenantContext | undefined {
76
+ return tenantStorage.getStore();
77
+ }
78
+
79
+ /**
80
+ * Get current tenant ID
81
+ */
82
+ export function getCurrentTenantId(): string | undefined {
83
+ return tenantStorage.getStore()?.tenantId;
84
+ }
85
+
86
+ /**
87
+ * Run code within tenant context
88
+ */
89
+ export function runWithTenant<T>(tenant: TenantContext, fn: () => T): T {
90
+ return tenantStorage.run(tenant, fn);
91
+ }
92
+
93
+ /**
94
+ * Check if feature is enabled for current tenant
95
+ */
96
+ export function hasFeature(feature: string): boolean {
97
+ const tenant = getCurrentTenant();
98
+ return tenant?.config?.features?.includes(feature) ?? false;
99
+ }
100
+ `;
101
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'tenant.context.ts'), tenantContextContent);
102
+ // Tenant middleware
103
+ const tenantMiddlewareContent = `import { Injectable, NestMiddleware, NotFoundException } from "@nestjs/common";
104
+ import { Request, Response, NextFunction } from "express";
105
+ import { tenantStorage, TenantContext } from "../tenant.context";
106
+
107
+ export interface TenantResolver {
108
+ resolve(req: Request): Promise<TenantContext | null>;
109
+ }
110
+
111
+ /**
112
+ * Resolve tenant from subdomain: tenant1.example.com
113
+ */
114
+ @Injectable()
115
+ export class SubdomainTenantResolver implements TenantResolver {
116
+ async resolve(req: Request): Promise<TenantContext | null> {
117
+ const host = req.hostname;
118
+ const parts = host.split(".");
119
+
120
+ if (parts.length < 2) return null;
121
+
122
+ const subdomain = parts[0];
123
+ if (subdomain === "www" || subdomain === "api") return null;
124
+
125
+ // Look up tenant by subdomain (implement your own lookup)
126
+ return this.lookupTenant(subdomain);
127
+ }
128
+
129
+ private async lookupTenant(slug: string): Promise<TenantContext | null> {
130
+ // TODO: Implement actual tenant lookup from database
131
+ // This is a placeholder
132
+ return {
133
+ tenantId: slug,
134
+ tenantSlug: slug,
135
+ tenantName: slug,
136
+ };
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Resolve tenant from header: X-Tenant-ID
142
+ */
143
+ @Injectable()
144
+ export class HeaderTenantResolver implements TenantResolver {
145
+ async resolve(req: Request): Promise<TenantContext | null> {
146
+ const tenantId = req.headers["x-tenant-id"] as string;
147
+ if (!tenantId) return null;
148
+
149
+ return {
150
+ tenantId,
151
+ tenantSlug: tenantId,
152
+ };
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Resolve tenant from URL path: /tenant/:tenantId/...
158
+ */
159
+ @Injectable()
160
+ export class PathTenantResolver implements TenantResolver {
161
+ async resolve(req: Request): Promise<TenantContext | null> {
162
+ const match = req.path.match(/^\\/tenant\\/([^/]+)/);
163
+ if (!match) return null;
164
+
165
+ const tenantId = match[1];
166
+ return {
167
+ tenantId,
168
+ tenantSlug: tenantId,
169
+ };
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Resolve tenant from JWT token
175
+ */
176
+ @Injectable()
177
+ export class JwtTenantResolver implements TenantResolver {
178
+ async resolve(req: Request): Promise<TenantContext | null> {
179
+ const user = (req as any).user;
180
+ if (!user?.tenantId) return null;
181
+
182
+ return {
183
+ tenantId: user.tenantId,
184
+ tenantSlug: user.tenantSlug,
185
+ };
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Main tenant middleware
191
+ */
192
+ @Injectable()
193
+ export class TenantMiddleware implements NestMiddleware {
194
+ constructor(private resolver: TenantResolver) {}
195
+
196
+ async use(req: Request, res: Response, next: NextFunction) {
197
+ const tenant = await this.resolver.resolve(req);
198
+
199
+ if (!tenant) {
200
+ throw new NotFoundException("Tenant not found");
201
+ }
202
+
203
+ // Attach tenant to request
204
+ (req as any).tenant = tenant;
205
+ (req as any).tenantId = tenant.tenantId;
206
+
207
+ // Run remaining middleware in tenant context
208
+ tenantStorage.run(tenant, () => next());
209
+ }
210
+ }
211
+ `;
212
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'middleware/tenant.middleware.ts'), tenantMiddlewareContent);
213
+ // Tenant guard
214
+ const tenantGuardContent = `import {
215
+ Injectable,
216
+ CanActivate,
217
+ ExecutionContext,
218
+ ForbiddenException,
219
+ } from "@nestjs/common";
220
+ import { Reflector } from "@nestjs/core";
221
+ import { getCurrentTenant, hasFeature } from "../tenant.context";
222
+
223
+ export const TENANT_FEATURE_KEY = "tenant_feature";
224
+ export const RequireFeature = (...features: string[]) =>
225
+ Reflect.metadata(TENANT_FEATURE_KEY, features);
226
+
227
+ @Injectable()
228
+ export class TenantFeatureGuard implements CanActivate {
229
+ constructor(private reflector: Reflector) {}
230
+
231
+ canActivate(context: ExecutionContext): boolean {
232
+ const requiredFeatures = this.reflector.getAllAndOverride<string[]>(
233
+ TENANT_FEATURE_KEY,
234
+ [context.getHandler(), context.getClass()]
235
+ );
236
+
237
+ if (!requiredFeatures || requiredFeatures.length === 0) {
238
+ return true;
239
+ }
240
+
241
+ const tenant = getCurrentTenant();
242
+ if (!tenant) {
243
+ throw new ForbiddenException("No tenant context");
244
+ }
245
+
246
+ for (const feature of requiredFeatures) {
247
+ if (!hasFeature(feature)) {
248
+ throw new ForbiddenException(
249
+ \`Feature '\${feature}' is not enabled for this tenant\`
250
+ );
251
+ }
252
+ }
253
+
254
+ return true;
255
+ }
256
+ }
257
+
258
+ @Injectable()
259
+ export class TenantIsolationGuard implements CanActivate {
260
+ canActivate(context: ExecutionContext): boolean {
261
+ const request = context.switchToHttp().getRequest();
262
+ const tenant = getCurrentTenant();
263
+ const user = request.user;
264
+
265
+ if (!tenant) {
266
+ throw new ForbiddenException("No tenant context");
267
+ }
268
+
269
+ // Ensure user belongs to current tenant
270
+ if (user && user.tenantId !== tenant.tenantId) {
271
+ throw new ForbiddenException("Access denied: tenant mismatch");
272
+ }
273
+
274
+ return true;
275
+ }
276
+ }
277
+ `;
278
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'guards/tenant.guard.ts'), tenantGuardContent);
279
+ // Tenant-aware repository
280
+ const tenantRepositoryContent = `import { Repository, SelectQueryBuilder, DeepPartial } from "typeorm";
281
+ import { getCurrentTenantId } from "./tenant.context";
282
+
283
+ /**
284
+ * Base interface for tenant-aware entities
285
+ */
286
+ export interface TenantEntity {
287
+ tenantId: string;
288
+ }
289
+
290
+ /**
291
+ * Tenant-aware repository that automatically filters by tenant
292
+ */
293
+ export class TenantAwareRepository<T extends TenantEntity> {
294
+ constructor(protected readonly repository: Repository<T>) {}
295
+
296
+ /**
297
+ * Get current tenant ID or throw
298
+ */
299
+ protected getTenantId(): string {
300
+ const tenantId = getCurrentTenantId();
301
+ if (!tenantId) {
302
+ throw new Error("No tenant context available");
303
+ }
304
+ return tenantId;
305
+ }
306
+
307
+ /**
308
+ * Create query builder with tenant filter
309
+ */
310
+ createQueryBuilder(alias: string): SelectQueryBuilder<T> {
311
+ return this.repository
312
+ .createQueryBuilder(alias)
313
+ .where(\`\${alias}.tenantId = :tenantId\`, { tenantId: this.getTenantId() });
314
+ }
315
+
316
+ /**
317
+ * Find all entities for current tenant
318
+ */
319
+ async findAll(): Promise<T[]> {
320
+ return this.repository.find({
321
+ where: { tenantId: this.getTenantId() } as any,
322
+ });
323
+ }
324
+
325
+ /**
326
+ * Find one entity by ID for current tenant
327
+ */
328
+ async findById(id: string): Promise<T | null> {
329
+ return this.repository.findOne({
330
+ where: { id, tenantId: this.getTenantId() } as any,
331
+ });
332
+ }
333
+
334
+ /**
335
+ * Create entity with tenant ID
336
+ */
337
+ async create(data: DeepPartial<T>): Promise<T> {
338
+ const entity = this.repository.create({
339
+ ...data,
340
+ tenantId: this.getTenantId(),
341
+ } as DeepPartial<T>);
342
+ return this.repository.save(entity);
343
+ }
344
+
345
+ /**
346
+ * Update entity ensuring tenant isolation
347
+ */
348
+ async update(id: string, data: DeepPartial<T>): Promise<T | null> {
349
+ const existing = await this.findById(id);
350
+ if (!existing) return null;
351
+
352
+ await this.repository.update(
353
+ { id, tenantId: this.getTenantId() } as any,
354
+ data as any
355
+ );
356
+ return this.findById(id);
357
+ }
358
+
359
+ /**
360
+ * Delete entity ensuring tenant isolation
361
+ */
362
+ async delete(id: string): Promise<boolean> {
363
+ const result = await this.repository.delete({
364
+ id,
365
+ tenantId: this.getTenantId(),
366
+ } as any);
367
+ return (result.affected || 0) > 0;
368
+ }
369
+
370
+ /**
371
+ * Count entities for current tenant
372
+ */
373
+ async count(): Promise<number> {
374
+ return this.repository.count({
375
+ where: { tenantId: this.getTenantId() } as any,
376
+ });
377
+ }
378
+ }
379
+ `;
380
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'tenant-aware.repository.ts'), tenantRepositoryContent);
381
+ // Tenant decorator
382
+ const tenantDecoratorContent = `import { createParamDecorator, ExecutionContext, SetMetadata } from "@nestjs/common";
383
+ import { getCurrentTenant, TenantContext } from "../tenant.context";
384
+
385
+ /**
386
+ * Get current tenant from request
387
+ */
388
+ export const CurrentTenant = createParamDecorator(
389
+ (data: keyof TenantContext | undefined, ctx: ExecutionContext) => {
390
+ const request = ctx.switchToHttp().getRequest();
391
+ const tenant = request.tenant || getCurrentTenant();
392
+
393
+ if (!tenant) return null;
394
+ return data ? tenant[data] : tenant;
395
+ }
396
+ );
397
+
398
+ /**
399
+ * Get current tenant ID
400
+ */
401
+ export const TenantId = createParamDecorator(
402
+ (_data: unknown, ctx: ExecutionContext) => {
403
+ const request = ctx.switchToHttp().getRequest();
404
+ return request.tenantId || getCurrentTenant()?.tenantId;
405
+ }
406
+ );
407
+
408
+ /**
409
+ * Mark endpoint as requiring specific tenant features
410
+ */
411
+ export const TENANT_FEATURES = "tenant:features";
412
+ export const RequireTenantFeatures = (...features: string[]) =>
413
+ SetMetadata(TENANT_FEATURES, features);
414
+
415
+ /**
416
+ * Mark endpoint as tenant-isolated
417
+ */
418
+ export const TENANT_ISOLATED = "tenant:isolated";
419
+ export const TenantIsolated = () => SetMetadata(TENANT_ISOLATED, true);
420
+ `;
421
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'decorators/tenant.decorator.ts'), tenantDecoratorContent);
422
+ // Tenant module
423
+ const tenantModuleContent = `import { Module, Global, DynamicModule, MiddlewareConsumer, NestModule } from "@nestjs/common";
424
+ import {
425
+ TenantMiddleware,
426
+ SubdomainTenantResolver,
427
+ HeaderTenantResolver,
428
+ PathTenantResolver,
429
+ TenantResolver,
430
+ } from "./middleware/tenant.middleware";
431
+ import { TenantFeatureGuard, TenantIsolationGuard } from "./guards/tenant.guard";
432
+
433
+ export type TenantStrategy = "subdomain" | "header" | "path" | "jwt";
434
+
435
+ export interface TenantModuleOptions {
436
+ strategy: TenantStrategy;
437
+ excludePaths?: string[];
438
+ }
439
+
440
+ @Global()
441
+ @Module({})
442
+ export class TenantModule implements NestModule {
443
+ static options: TenantModuleOptions;
444
+
445
+ static forRoot(options: TenantModuleOptions): DynamicModule {
446
+ TenantModule.options = options;
447
+
448
+ const resolverProvider = {
449
+ provide: TenantResolver,
450
+ useClass: this.getResolverClass(options.strategy),
451
+ };
452
+
453
+ return {
454
+ module: TenantModule,
455
+ providers: [
456
+ resolverProvider,
457
+ TenantMiddleware,
458
+ TenantFeatureGuard,
459
+ TenantIsolationGuard,
460
+ ],
461
+ exports: [
462
+ TenantMiddleware,
463
+ TenantFeatureGuard,
464
+ TenantIsolationGuard,
465
+ ],
466
+ };
467
+ }
468
+
469
+ private static getResolverClass(strategy: TenantStrategy) {
470
+ switch (strategy) {
471
+ case "subdomain":
472
+ return SubdomainTenantResolver;
473
+ case "header":
474
+ return HeaderTenantResolver;
475
+ case "path":
476
+ return PathTenantResolver;
477
+ default:
478
+ return HeaderTenantResolver;
479
+ }
480
+ }
481
+
482
+ configure(consumer: MiddlewareConsumer) {
483
+ const excludePaths = TenantModule.options?.excludePaths || [
484
+ "/health",
485
+ "/docs",
486
+ "/swagger",
487
+ ];
488
+
489
+ consumer
490
+ .apply(TenantMiddleware)
491
+ .exclude(...excludePaths)
492
+ .forRoutes("*");
493
+ }
494
+ }
495
+ `;
496
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'tenant.module.ts'), tenantModuleContent);
497
+ // Index exports
498
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'index.ts'), `export * from "./tenant.context";
499
+ export * from "./tenant-aware.repository";
500
+ export * from "./tenant.module";
501
+ export * from "./middleware/tenant.middleware";
502
+ export * from "./guards/tenant.guard";
503
+ export * from "./decorators/tenant.decorator";
504
+ `);
505
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'middleware/index.ts'), `export * from "./tenant.middleware";
506
+ `);
507
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'guards/index.ts'), `export * from "./tenant.guard";
508
+ `);
509
+ await (0, file_utils_1.writeFile)(path.join(tenantPath, 'decorators/index.ts'), `export * from "./tenant.decorator";
510
+ `);
511
+ console.log(chalk_1.default.green(' ✓ Tenant context with AsyncLocalStorage'));
512
+ console.log(chalk_1.default.green(' ✓ Multiple tenant resolvers (subdomain, header, path, JWT)'));
513
+ console.log(chalk_1.default.green(' ✓ Tenant middleware'));
514
+ console.log(chalk_1.default.green(' ✓ Tenant feature guard'));
515
+ console.log(chalk_1.default.green(' ✓ Tenant isolation guard'));
516
+ console.log(chalk_1.default.green(' ✓ Tenant-aware repository base class'));
517
+ console.log(chalk_1.default.green(' ✓ Tenant decorators (@CurrentTenant, @TenantId)'));
518
+ console.log(chalk_1.default.green(' ✓ Tenant module with strategy selection'));
519
+ }
520
+ //# sourceMappingURL=multi-tenancy.recipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-tenancy.recipe.js","sourceRoot":"","sources":["../../../src/commands/recipes/multi-tenancy.recipe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function applyOAuth2Recipe(basePath: string): Promise<void>;