nestjs-ddd-cli 3.2.2 ā 3.3.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.
- package/README.md +118 -9
- package/ddd.schema.json +11 -1
- package/dist/commands/doctor-enhanced.d.ts +1 -0
- package/dist/commands/doctor-enhanced.js +94 -79
- package/dist/commands/doctor-enhanced.js.map +1 -1
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/doctor.js +68 -21
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/generate-all.js +54 -28
- package/dist/commands/generate-all.js.map +1 -1
- package/dist/commands/generate-controller.d.ts +1 -0
- package/dist/commands/generate-controller.js +71 -0
- package/dist/commands/generate-controller.js.map +1 -0
- package/dist/commands/generate-domain-service.d.ts +1 -1
- package/dist/commands/generate-domain-service.js +6 -4
- package/dist/commands/generate-domain-service.js.map +1 -1
- package/dist/commands/generate-dto.d.ts +4 -0
- package/dist/commands/generate-dto.js +162 -0
- package/dist/commands/generate-dto.js.map +1 -0
- package/dist/commands/generate-entity.js +7 -2
- package/dist/commands/generate-entity.js.map +1 -1
- package/dist/commands/generate-event.js +10 -4
- package/dist/commands/generate-event.js.map +1 -1
- package/dist/commands/generate-migration-deployment.d.ts +15 -0
- package/dist/commands/generate-migration-deployment.js +326 -0
- package/dist/commands/generate-migration-deployment.js.map +1 -0
- package/dist/commands/generate-migration-deployment.spec.d.ts +1 -0
- package/dist/commands/generate-migration-deployment.spec.js +337 -0
- package/dist/commands/generate-migration-deployment.spec.js.map +1 -0
- package/dist/commands/generate-module.js +4 -2
- package/dist/commands/generate-module.js.map +1 -1
- package/dist/commands/generate-query.js +23 -4
- package/dist/commands/generate-query.js.map +1 -1
- package/dist/commands/generate-repository.d.ts +1 -1
- package/dist/commands/generate-repository.js +17 -34
- package/dist/commands/generate-repository.js.map +1 -1
- package/dist/commands/generate-service.js +19 -4
- package/dist/commands/generate-service.js.map +1 -1
- package/dist/commands/generate-usecase.js +12 -31
- package/dist/commands/generate-usecase.js.map +1 -1
- package/dist/commands/generate-value-object.js +3 -14
- package/dist/commands/generate-value-object.js.map +1 -1
- package/dist/commands/init-project.d.ts +3 -0
- package/dist/commands/init-project.js +19 -9
- package/dist/commands/init-project.js.map +1 -1
- package/dist/commands/interactive-scaffold.js +10 -0
- package/dist/commands/interactive-scaffold.js.map +1 -1
- package/dist/commands/migration.d.ts +3 -0
- package/dist/commands/migration.js +83 -48
- package/dist/commands/migration.js.map +1 -1
- package/dist/commands/recipe.d.ts +2 -0
- package/dist/commands/recipe.js +229 -85
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipes/banklink-connector-contract.recipe.d.ts +1 -0
- package/dist/commands/recipes/banklink-connector-contract.recipe.js +161 -0
- package/dist/commands/recipes/banklink-connector-contract.recipe.js.map +1 -0
- package/dist/commands/recipes/bounded-json-http.recipe.d.ts +2 -0
- package/dist/commands/recipes/bounded-json-http.recipe.js +53 -0
- package/dist/commands/recipes/bounded-json-http.recipe.js.map +1 -0
- package/dist/commands/recipes/business-reference-identifiers.recipe.d.ts +1 -0
- package/dist/commands/recipes/business-reference-identifiers.recipe.js +223 -0
- package/dist/commands/recipes/business-reference-identifiers.recipe.js.map +1 -0
- package/dist/commands/recipes/event-backbone.recipe.d.ts +1 -0
- package/dist/commands/recipes/event-backbone.recipe.js +1118 -0
- package/dist/commands/recipes/event-backbone.recipe.js.map +1 -0
- package/dist/commands/recipes/external-projection-worker.recipe.d.ts +6 -0
- package/dist/commands/recipes/external-projection-worker.recipe.js +211 -0
- package/dist/commands/recipes/external-projection-worker.recipe.js.map +1 -0
- package/dist/commands/recipes/index.d.ts +11 -0
- package/dist/commands/recipes/index.js +11 -0
- package/dist/commands/recipes/index.js.map +1 -1
- package/dist/commands/recipes/jest-cjs-compat.recipe.d.ts +3 -0
- package/dist/commands/recipes/jest-cjs-compat.recipe.js +130 -0
- package/dist/commands/recipes/jest-cjs-compat.recipe.js.map +1 -0
- package/dist/commands/recipes/joi-env.recipe.js +1 -1
- package/dist/commands/recipes/kafka-consumer.recipe.d.ts +1 -0
- package/dist/commands/recipes/kafka-consumer.recipe.js +65 -0
- package/dist/commands/recipes/kafka-consumer.recipe.js.map +1 -0
- package/dist/commands/recipes/oauth2-client-credentials.recipe.d.ts +1 -0
- package/dist/commands/recipes/oauth2-client-credentials.recipe.js +60 -0
- package/dist/commands/recipes/oauth2-client-credentials.recipe.js.map +1 -0
- package/dist/commands/recipes/oauth2-token-introspection.recipe.d.ts +1 -0
- package/dist/commands/recipes/oauth2-token-introspection.recipe.js +67 -0
- package/dist/commands/recipes/oauth2-token-introspection.recipe.js.map +1 -0
- package/dist/commands/recipes/oidc-bff-session.recipe.d.ts +1 -0
- package/dist/commands/recipes/oidc-bff-session.recipe.js +57 -0
- package/dist/commands/recipes/oidc-bff-session.recipe.js.map +1 -0
- package/dist/commands/recipes/oidc-dashboard.recipe.d.ts +1 -0
- package/dist/commands/recipes/oidc-dashboard.recipe.js +463 -0
- package/dist/commands/recipes/oidc-dashboard.recipe.js.map +1 -0
- package/dist/commands/recipes/oidc-resource-server.recipe.d.ts +1 -0
- package/dist/commands/recipes/oidc-resource-server.recipe.js +85 -0
- package/dist/commands/recipes/oidc-resource-server.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-context.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-context.recipe.js +229 -0
- package/dist/commands/recipes/platform-context.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-parc-authorization.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-parc-authorization.recipe.js +339 -0
- package/dist/commands/recipes/platform-parc-authorization.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-parc-bound-authorization.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-parc-bound-authorization.recipe.js +56 -0
- package/dist/commands/recipes/platform-parc-bound-authorization.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-service-access-request-context.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-service-access-request-context.recipe.js +103 -0
- package/dist/commands/recipes/platform-service-access-request-context.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-service-runtime.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-service-runtime.recipe.js +415 -0
- package/dist/commands/recipes/platform-service-runtime.recipe.js.map +1 -0
- package/dist/index.js +77 -9
- package/dist/index.js.map +1 -1
- package/dist/templates/ai-context/CLAUDE.md.hbs +39 -3
- package/dist/templates/ai-context/conventions.md.hbs +13 -8
- package/dist/templates/command/create-command.hbs +8 -3
- package/dist/templates/command/delete-command.hbs +1 -1
- package/dist/templates/command/update-command.hbs +1 -1
- package/dist/templates/controller/application-controller.hbs +7 -0
- package/dist/templates/controller/controller.hbs +6 -0
- package/dist/templates/dto/create-dto.hbs +17 -5
- package/dist/templates/dto/filter-dto.hbs +1 -1
- package/dist/templates/dto/response-dto.hbs +1 -1
- package/dist/templates/entity/entity.hbs +4 -0
- package/dist/templates/mapper/mapper.hbs +5 -1
- package/dist/templates/orm-entity/orm-entity.hbs +9 -1
- package/dist/templates/prisma/prisma-mapper.hbs +6 -0
- package/dist/templates/prisma/prisma-repository.hbs +57 -5
- package/dist/templates/prisma/prisma-schema.hbs +2 -0
- package/dist/templates/query/get-all.query.hbs +10 -2
- package/dist/templates/recipes/bounded-json-http/README.md.hbs +36 -0
- package/dist/templates/recipes/external-projection-worker/README.md.hbs +157 -0
- package/dist/templates/recipes/external-projection-worker/canonical-json.ts.hbs +123 -0
- package/dist/templates/recipes/external-projection-worker/enqueuer.ts.hbs +167 -0
- package/dist/templates/recipes/external-projection-worker/index.ts.hbs +6 -0
- package/dist/templates/recipes/external-projection-worker/migration.ts.hbs +88 -0
- package/dist/templates/recipes/external-projection-worker/module.ts.hbs +36 -0
- package/dist/templates/recipes/external-projection-worker/store.ts.hbs +354 -0
- package/dist/templates/recipes/external-projection-worker/types.ts.hbs +134 -0
- package/dist/templates/recipes/external-projection-worker/worker.ts.hbs +296 -0
- package/dist/templates/recipes/kafka-consumer/README.md.hbs +50 -0
- package/dist/templates/recipes/kafka-consumer/kafka-consumer.ts.hbs +135 -0
- package/dist/templates/recipes/kafka-consumer/migration.ts.hbs +17 -0
- package/dist/templates/recipes/oauth2-client-credentials/README.md.hbs +40 -0
- package/dist/templates/recipes/oauth2-client-credentials/client.spec.ts.hbs +234 -0
- package/dist/templates/recipes/oauth2-client-credentials/client.ts.hbs +210 -0
- package/dist/templates/recipes/oauth2-client-credentials/index.ts.hbs +2 -0
- package/dist/templates/recipes/oauth2-client-credentials/module.ts.hbs +58 -0
- package/dist/templates/recipes/oauth2-token-introspection/README.md.hbs +158 -0
- package/dist/templates/recipes/oauth2-token-introspection/client.spec.ts.hbs +350 -0
- package/dist/templates/recipes/oauth2-token-introspection/client.ts.hbs +241 -0
- package/dist/templates/recipes/oauth2-token-introspection/http-client.spec.ts.hbs +411 -0
- package/dist/templates/recipes/oauth2-token-introspection/http-client.ts.hbs +343 -0
- package/dist/templates/recipes/oauth2-token-introspection/index.ts.hbs +3 -0
- package/dist/templates/recipes/oauth2-token-introspection/module.ts.hbs +58 -0
- package/dist/templates/recipes/oidc-bff-session/README.md.hbs +96 -0
- package/dist/templates/recipes/oidc-bff-session/client.test.ts.hbs +663 -0
- package/dist/templates/recipes/oidc-bff-session/client.ts.hbs +635 -0
- package/dist/templates/recipes/oidc-bff-session/index.ts.hbs +3 -0
- package/dist/templates/recipes/oidc-bff-session/store.integration.test.ts.hbs +480 -0
- package/dist/templates/recipes/oidc-bff-session/store.test.ts.hbs +87 -0
- package/dist/templates/recipes/oidc-bff-session/store.ts.hbs +549 -0
- package/dist/templates/recipes/oidc-bff-session/valkey.test.ts.hbs +158 -0
- package/dist/templates/recipes/oidc-bff-session/valkey.ts.hbs +148 -0
- package/dist/templates/recipes/oidc-resource-server/README.md.hbs +74 -0
- package/dist/templates/recipes/oidc-resource-server/index.ts.hbs +2 -0
- package/dist/templates/recipes/oidc-resource-server/module.ts.hbs +51 -0
- package/dist/templates/recipes/oidc-resource-server/verifier.spec.ts.hbs +186 -0
- package/dist/templates/recipes/oidc-resource-server/verifier.ts.hbs +440 -0
- package/dist/templates/recipes/platform-parc-bound-authorization/README.md.hbs +92 -0
- package/dist/templates/recipes/platform-parc-bound-authorization/client.ts.hbs +142 -0
- package/dist/templates/recipes/platform-service-access-request-context/docs.md.hbs +61 -0
- package/dist/templates/recipes/platform-service-access-request-context/env.example.hbs +7 -0
- package/dist/templates/recipes/platform-service-access-request-context/index.ts.hbs +7 -0
- package/dist/templates/recipes/platform-service-access-request-context/request-context-headers.ts.hbs +61 -0
- package/dist/templates/recipes/platform-service-access-request-context/request-context-introspection.parser.ts.hbs +132 -0
- package/dist/templates/recipes/platform-service-access-request-context/request-context-introspection.types.ts.hbs +40 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.client.ts.hbs +45 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.config.ts.hbs +142 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.guard.ts.hbs +77 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.module.ts.hbs +17 -0
- package/dist/templates/repository/repository.hbs +42 -4
- package/dist/templates/resolver/graphql-input.hbs +23 -6
- package/dist/templates/resolver/resolver.hbs +4 -0
- package/dist/templates/service/domain-service.hbs +2 -2
- package/dist/templates/test/controller.spec.hbs +5 -1
- package/dist/templates/test/prisma-repository.spec.hbs +192 -0
- package/dist/templates/test/repository.spec.hbs +40 -1
- package/dist/templates/test/usecase.spec.hbs +23 -4
- package/dist/templates/usecase/application-usecase.hbs +17 -0
- package/dist/templates/usecase/create-usecase.hbs +34 -3
- package/dist/templates/usecase/update-usecase.hbs +15 -1
- package/dist/utils/config.utils.d.ts +2 -0
- package/dist/utils/config.utils.js +14 -2
- package/dist/utils/config.utils.js.map +1 -1
- package/dist/utils/dependency.utils.d.ts +16 -2
- package/dist/utils/dependency.utils.js +100 -12
- package/dist/utils/dependency.utils.js.map +1 -1
- package/dist/utils/field.utils.d.ts +6 -0
- package/dist/utils/field.utils.js +50 -8
- package/dist/utils/field.utils.js.map +1 -1
- package/dist/utils/field.utils.spec.d.ts +1 -0
- package/dist/utils/field.utils.spec.js +38 -0
- package/dist/utils/field.utils.spec.js.map +1 -0
- package/dist/utils/file.utils.d.ts +23 -1
- package/dist/utils/file.utils.js +37 -1
- package/dist/utils/file.utils.js.map +1 -1
- package/dist/utils/naming.utils.js +5 -4
- package/dist/utils/naming.utils.js.map +1 -1
- package/dist/utils/static-recipe.utils.d.ts +4 -0
- package/dist/utils/static-recipe.utils.js +148 -0
- package/dist/utils/static-recipe.utils.js.map +1 -0
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -28,8 +28,8 @@ npm install -g nestjs-ddd-cli
|
|
|
28
28
|
# Initialize a new project
|
|
29
29
|
ddd init my-project
|
|
30
30
|
|
|
31
|
-
# Generate
|
|
32
|
-
ddd scaffold
|
|
31
|
+
# Generate a financial-safe aggregate without a generic DELETE operation
|
|
32
|
+
ddd scaffold Invoice -m billing --fields "amount:money tenantId:uuid:serverOwned" --no-delete
|
|
33
33
|
|
|
34
34
|
# Apply authentication recipe
|
|
35
35
|
ddd recipe auth-jwt --install-deps
|
|
@@ -41,6 +41,48 @@ ddd security-patterns
|
|
|
41
41
|
ddd observability
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## OAuth2 token introspection
|
|
45
|
+
|
|
46
|
+
Generate a bounded RFC7662 client and an owning Nest module:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
ddd recipe oauth2-token-introspection --path <service> --dry-run
|
|
50
|
+
ddd recipe oauth2-token-introspection --path <service>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The client rechecks the provider on every call and returns immutable evidence.
|
|
54
|
+
The consuming service supplies principal/session/permission policy. HTTPS is the
|
|
55
|
+
default; verified private HTTP requires explicit opt-in. See the
|
|
56
|
+
[generated operator guide](src/templates/recipes/oauth2-token-introspection/README.md.hbs)
|
|
57
|
+
for async registration, limits, failure handling and provider compatibility.
|
|
58
|
+
|
|
59
|
+
## Framework-independent bounded JSON transport
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
ddd recipe bounded-json-http --path <application> --dry-run
|
|
63
|
+
ddd recipe bounded-json-http --path <application>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Emit the same fixed-origin HTTP client without a Nest module or dependencies.
|
|
67
|
+
The consumer owns its server-only wrapper, exact response validation and domain
|
|
68
|
+
admission. GET, form POST and JSON POST/PATCH/PUT have bounded responses and
|
|
69
|
+
deadlines, with no redirects or retries. See the
|
|
70
|
+
[generated guide](src/templates/recipes/bounded-json-http/README.md.hbs).
|
|
71
|
+
|
|
72
|
+
## Confidential OIDC BFF sessions
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
ddd recipe oidc-bff-session --path <app> --dry-run
|
|
76
|
+
ddd recipe oidc-bff-session --path <app>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This emits provider-neutral verified authorization-code/S256 transport, opaque
|
|
80
|
+
Lua sessions and durable token-retirement leases. Exact issuer/endpoints,
|
|
81
|
+
credentials, cookie/routes, domain admission and worker scheduling remain the
|
|
82
|
+
consumer's responsibility. Runtime dependencies and storage custody controls
|
|
83
|
+
are explicit in the [generated guide](src/templates/recipes/oidc-bff-session/README.md.hbs).
|
|
84
|
+
The recipe never installs dependencies or rewrites application configuration.
|
|
85
|
+
|
|
44
86
|
## Commands
|
|
45
87
|
|
|
46
88
|
### Core Commands
|
|
@@ -48,11 +90,15 @@ ddd observability
|
|
|
48
90
|
| Command | Description |
|
|
49
91
|
|---------|-------------|
|
|
50
92
|
| `ddd init <name>` | Initialize new NestJS project with DDD structure |
|
|
51
|
-
| `ddd scaffold <entity> -m <module>` | Generate complete
|
|
93
|
+
| `ddd scaffold <entity> -m <module>` | Generate a complete aggregate stack |
|
|
52
94
|
| `ddd generate <type> <name> -m <module>` | Generate individual components |
|
|
53
95
|
| `ddd recipe [name]` | Apply common patterns (auth, caching, etc.) |
|
|
54
96
|
| `ddd shared` | Generate shared utilities and base classes |
|
|
55
97
|
|
|
98
|
+
```bash
|
|
99
|
+
ddd generate dto Product -m inventory --kind create
|
|
100
|
+
```
|
|
101
|
+
|
|
56
102
|
### Enterprise Commands
|
|
57
103
|
|
|
58
104
|
| Command | Description |
|
|
@@ -80,7 +126,7 @@ ddd observability
|
|
|
80
126
|
Generate complete typed entities, DTOs, and migrations:
|
|
81
127
|
|
|
82
128
|
```bash
|
|
83
|
-
ddd scaffold Product -m inventory --fields "name:string price:
|
|
129
|
+
ddd scaffold Product -m inventory --fields "name:string price:money:optional sku:string:unique"
|
|
84
130
|
```
|
|
85
131
|
|
|
86
132
|
**Field Format:** `name:type:modifier1:modifier2`
|
|
@@ -89,10 +135,37 @@ ddd scaffold Product -m inventory --fields "name:string price:number:optional sk
|
|
|
89
135
|
|-------|-----------|
|
|
90
136
|
| `string`, `text` | `optional` |
|
|
91
137
|
| `number`, `int`, `float`, `decimal` | `unique` |
|
|
138
|
+
| `money` (exact decimal-backed TypeScript string) | `serverOwned` (`internal` alias) |
|
|
92
139
|
| `boolean` | `relation` |
|
|
93
140
|
| `date`, `datetime`, `timestamp` | `OneToOne`, `OneToMany`, `ManyToOne`, `ManyToMany` |
|
|
94
141
|
| `uuid`, `json`, `enum` | |
|
|
95
142
|
|
|
143
|
+
Use `money` for financial values that must not pass through JavaScript floating-point
|
|
144
|
+
numbers. It generates `string` in domain, TypeORM, request, and response types,
|
|
145
|
+
decimal-string validation and OpenAPI metadata, plus `decimal`/Prisma `Decimal`
|
|
146
|
+
storage. Existing `decimal` fields remain TypeScript `number` for compatibility.
|
|
147
|
+
|
|
148
|
+
Use `serverOwned` for fields populated from trusted application context rather than
|
|
149
|
+
the request body:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
ddd scaffold Invoice -m billing \
|
|
153
|
+
--fields "amount:money tenantId:uuid:serverOwned bookId:uuid:serverOwned"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Server-owned fields remain in domain, persistence, mapper, and response generation,
|
|
157
|
+
but are omitted from create/update request DTOs and request mass-assignment. The
|
|
158
|
+
generated create command accepts a second typed `serverOwnedFields` argument and
|
|
159
|
+
fails closed until the controller/application layer supplies it from authenticated
|
|
160
|
+
context.
|
|
161
|
+
|
|
162
|
+
For an aggregate with no generic deletion capability:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
ddd scaffold LedgerEntry -m ledger --fields "amount:money" --no-delete
|
|
166
|
+
# Equivalent project-wide default: features.delete = false in .dddrc.json
|
|
167
|
+
```
|
|
168
|
+
|
|
96
169
|
**Relations:**
|
|
97
170
|
```bash
|
|
98
171
|
# ManyToOne relation
|
|
@@ -140,6 +213,11 @@ ddd recipe
|
|
|
140
213
|
|
|
141
214
|
# Apply with dependency installation
|
|
142
215
|
ddd recipe auth-jwt --install-deps
|
|
216
|
+
ddd recipe event-backbone --install-deps
|
|
217
|
+
ddd recipe oidc-dashboard --install-deps
|
|
218
|
+
ddd recipe platform-service-access-request-context
|
|
219
|
+
ddd recipe external-projection-worker --migration-timestamp 1790000000000 --install-deps
|
|
220
|
+
ddd recipe jest-cjs-compat --path /path/to/service --dry-run
|
|
143
221
|
```
|
|
144
222
|
|
|
145
223
|
| Recipe | Description |
|
|
@@ -149,6 +227,15 @@ ddd recipe auth-jwt --install-deps
|
|
|
149
227
|
| `soft-delete` | Base entity and repository with soft delete |
|
|
150
228
|
| `audit-log` | Entity change tracking with sensitive data masking |
|
|
151
229
|
| `caching` | Redis caching with decorators |
|
|
230
|
+
| `event-backbone` | Postgres event store/outbox source of truth with Pulsar transport |
|
|
231
|
+
| `business-reference-identifiers` | Human-readable sidecar references beside canonical IDs |
|
|
232
|
+
| `platform-service-runtime` | Service manifest, capabilities, actions, events, dependencies, and health contract |
|
|
233
|
+
| `platform-service-access-request-context` | Fail-closed Service Access lease introspection before route-specific PARC authorization |
|
|
234
|
+
| `banklink-connector-contract` | BankLink NestJS control-plane and Go sidecar connector boundary contract |
|
|
235
|
+
| `oidc-dashboard` | OIDC broker integration for internal dashboards and admin APIs |
|
|
236
|
+
| `external-projection-worker` | Postgres-backed external projection intents with atomic enqueue, canonical idempotency, fenced leases, and bounded retry outcomes |
|
|
237
|
+
| `jest-cjs-compat` | Node/Jest-only CommonJS transformation of ESM Nest configuration; production TypeScript and dependency versions stay intact |
|
|
238
|
+
| `bounded-json-http` | Fixed-origin GET/form/JSON transport without framework dependencies or domain admission policy |
|
|
152
239
|
|
|
153
240
|
## Generated Structure
|
|
154
241
|
|
|
@@ -192,13 +279,15 @@ Create `.dddrc.json` in your project root:
|
|
|
192
279
|
"database": "postgres",
|
|
193
280
|
"naming": {
|
|
194
281
|
"table": "snake_case",
|
|
195
|
-
"dto": "
|
|
282
|
+
"dto": "camelCase",
|
|
196
283
|
"file": "kebab-case"
|
|
197
284
|
},
|
|
198
285
|
"features": {
|
|
199
286
|
"swagger": true,
|
|
200
287
|
"pagination": true,
|
|
288
|
+
"delete": true,
|
|
201
289
|
"softDelete": true,
|
|
290
|
+
"hardDelete": false,
|
|
202
291
|
"timestamps": true,
|
|
203
292
|
"tests": false,
|
|
204
293
|
"events": false
|
|
@@ -211,6 +300,23 @@ Create `.dddrc.json` in your project root:
|
|
|
211
300
|
}
|
|
212
301
|
```
|
|
213
302
|
|
|
303
|
+
`features.delete` controls whether aggregate scaffolds emit the generic DELETE
|
|
304
|
+
controller route, delete command/use case, and repository `delete`/`hardDelete`
|
|
305
|
+
surface. It defaults to `true` for compatibility. Set it to `false`, or pass
|
|
306
|
+
`--no-delete` to `ddd scaffold`/`ddd generate all`, for a non-destructive aggregate
|
|
307
|
+
scaffold. This does not remove existing generated files and does not disable
|
|
308
|
+
soft-delete columns or active-row filtering.
|
|
309
|
+
|
|
310
|
+
`features.softDelete` controls the generated domain field, ORM column or Prisma
|
|
311
|
+
field, active-row filters, and repository delete behavior. When it is `false`,
|
|
312
|
+
none of the generated code references `deletedAt`/`deleted_at` and the ordinary
|
|
313
|
+
`delete` method performs the ORM's normal delete operation.
|
|
314
|
+
|
|
315
|
+
`features.hardDelete` is disabled by default. Set it to `true` only when a
|
|
316
|
+
soft-delete repository must expose a separately named `hardDelete` compatibility
|
|
317
|
+
helper. It never changes the ordinary `delete` method, and it is omitted when
|
|
318
|
+
`softDelete` is disabled because ordinary deletion is already physical.
|
|
319
|
+
|
|
214
320
|
## Real-World Examples
|
|
215
321
|
|
|
216
322
|
### E-commerce with Security
|
|
@@ -224,8 +330,8 @@ ddd security-patterns
|
|
|
224
330
|
ddd recipe auth-jwt --install-deps
|
|
225
331
|
|
|
226
332
|
# Domain modules
|
|
227
|
-
ddd scaffold Product -m inventory --fields "name:string price:
|
|
228
|
-
ddd scaffold Order -m orders --fields "total:
|
|
333
|
+
ddd scaffold Product -m inventory --fields "name:string price:money sku:string:unique stock:int"
|
|
334
|
+
ddd scaffold Order -m orders --fields "total:money status:enum:pending,paid,shipped customerId:uuid"
|
|
229
335
|
ddd scaffold Customer -m customers --fields "email:string:unique name:string"
|
|
230
336
|
|
|
231
337
|
# Enterprise features
|
|
@@ -247,7 +353,7 @@ ddd metrics-prometheus
|
|
|
247
353
|
ddd health-probes
|
|
248
354
|
|
|
249
355
|
# Domain
|
|
250
|
-
ddd scaffold Transaction -m payments --fields "amount:
|
|
356
|
+
ddd scaffold Transaction -m payments --fields "amount:money currency:string status:enum:pending,completed,failed"
|
|
251
357
|
ddd circuit-breaker
|
|
252
358
|
```
|
|
253
359
|
|
|
@@ -282,7 +388,7 @@ All generated APIs follow RESTful conventions with security:
|
|
|
282
388
|
| GET | `/entities/:id` | Get single entity |
|
|
283
389
|
| POST | `/entities` | Create new entity |
|
|
284
390
|
| PUT | `/entities/:id` | Update entity |
|
|
285
|
-
| DELETE | `/entities/:id` | Soft delete entity |
|
|
391
|
+
| DELETE | `/entities/:id` | Soft delete entity (omitted with `--no-delete`) |
|
|
286
392
|
|
|
287
393
|
### Pagination
|
|
288
394
|
|
|
@@ -338,6 +444,9 @@ npm link
|
|
|
338
444
|
|
|
339
445
|
# Test locally
|
|
340
446
|
ddd scaffold Test -m test-module --fields "name:string"
|
|
447
|
+
|
|
448
|
+
# External projection database proof (requires a disposable PostgreSQL URL)
|
|
449
|
+
EXTERNAL_PROJECTION_TEST_DATABASE_URL=postgres://... bun run test:external-projection-postgres
|
|
341
450
|
```
|
|
342
451
|
|
|
343
452
|
## License
|
package/ddd.schema.json
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dto": {
|
|
30
30
|
"type": "string",
|
|
31
31
|
"enum": ["snake_case", "camelCase"],
|
|
32
|
-
"default": "
|
|
32
|
+
"default": "camelCase",
|
|
33
33
|
"description": "DTO property naming convention"
|
|
34
34
|
},
|
|
35
35
|
"file": {
|
|
@@ -54,11 +54,21 @@
|
|
|
54
54
|
"default": true,
|
|
55
55
|
"description": "Include pagination support in list queries"
|
|
56
56
|
},
|
|
57
|
+
"delete": {
|
|
58
|
+
"type": "boolean",
|
|
59
|
+
"default": true,
|
|
60
|
+
"description": "Generate the generic aggregate deletion surface"
|
|
61
|
+
},
|
|
57
62
|
"softDelete": {
|
|
58
63
|
"type": "boolean",
|
|
59
64
|
"default": true,
|
|
60
65
|
"description": "Use soft delete pattern"
|
|
61
66
|
},
|
|
67
|
+
"hardDelete": {
|
|
68
|
+
"type": "boolean",
|
|
69
|
+
"default": false,
|
|
70
|
+
"description": "Generate an explicit hardDelete compatibility helper when softDelete is enabled"
|
|
71
|
+
},
|
|
62
72
|
"timestamps": {
|
|
63
73
|
"type": "boolean",
|
|
64
74
|
"default": true,
|
|
@@ -40,7 +40,10 @@ exports.runEnhancedDoctor = runEnhancedDoctor;
|
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const config_utils_1 = require("../utils/config.utils");
|
|
44
|
+
const doctor_1 = require("./doctor");
|
|
43
45
|
async function runEnhancedDoctor(basePath, options = {}) {
|
|
46
|
+
const doctorPaths = await resolveDoctorPaths(basePath, options.app);
|
|
44
47
|
const report = {
|
|
45
48
|
passed: 0,
|
|
46
49
|
warnings: 0,
|
|
@@ -49,14 +52,14 @@ async function runEnhancedDoctor(basePath, options = {}) {
|
|
|
49
52
|
};
|
|
50
53
|
console.log(chalk_1.default.bold.blue('\n𩺠Running Enhanced DDD Doctor...\n'));
|
|
51
54
|
// Run all diagnostic checks
|
|
52
|
-
await checkProjectStructure(
|
|
53
|
-
await checkDddPatterns(
|
|
54
|
-
await checkTypeScriptStrictness(
|
|
55
|
-
await checkNamingConventions(
|
|
56
|
-
await checkTestCoverage(
|
|
57
|
-
await checkImportPatterns(
|
|
58
|
-
await checkEntityIntegrity(
|
|
59
|
-
await checkModuleBoundaries(
|
|
55
|
+
await checkProjectStructure(doctorPaths, report);
|
|
56
|
+
await checkDddPatterns(doctorPaths.modulesPath, report);
|
|
57
|
+
await checkTypeScriptStrictness(doctorPaths.projectRoot, report);
|
|
58
|
+
await checkNamingConventions(doctorPaths.modulesPath, report);
|
|
59
|
+
await checkTestCoverage(doctorPaths.modulesPath, report);
|
|
60
|
+
await checkImportPatterns(doctorPaths.modulesPath, report);
|
|
61
|
+
await checkEntityIntegrity(doctorPaths.modulesPath, report);
|
|
62
|
+
await checkModuleBoundaries(doctorPaths.modulesPath, report);
|
|
60
63
|
// Print results
|
|
61
64
|
printReport(report, options.verbose);
|
|
62
65
|
// Auto-fix if requested
|
|
@@ -65,26 +68,32 @@ async function runEnhancedDoctor(basePath, options = {}) {
|
|
|
65
68
|
}
|
|
66
69
|
return report;
|
|
67
70
|
}
|
|
68
|
-
async function
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
async function resolveDoctorPaths(basePath, app) {
|
|
72
|
+
const projectRoot = await (0, doctor_1.resolveNestProjectPath)(basePath, app);
|
|
73
|
+
const config = await (0, config_utils_1.loadConfig)(projectRoot);
|
|
74
|
+
return {
|
|
75
|
+
projectRoot,
|
|
76
|
+
modulesPath: path.resolve(projectRoot, config.paths.modules),
|
|
77
|
+
sharedPath: path.resolve(projectRoot, config.paths.shared),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function checkProjectStructure(paths, report) {
|
|
81
|
+
const requiredDirs = [paths.modulesPath, paths.sharedPath];
|
|
73
82
|
const recommendedDirs = [
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
83
|
+
path.join(paths.sharedPath, 'domain'),
|
|
84
|
+
path.join(paths.sharedPath, 'infrastructure'),
|
|
85
|
+
path.join(paths.sharedPath, 'application'),
|
|
77
86
|
];
|
|
78
|
-
for (const
|
|
79
|
-
const
|
|
87
|
+
for (const fullPath of requiredDirs) {
|
|
88
|
+
const displayPath = displayProjectPath(paths.projectRoot, fullPath);
|
|
80
89
|
if (!fs.existsSync(fullPath)) {
|
|
81
90
|
report.errors++;
|
|
82
91
|
report.results.push({
|
|
83
92
|
category: 'Structure',
|
|
84
93
|
rule: 'required-directory',
|
|
85
94
|
severity: 'error',
|
|
86
|
-
message: `Missing required directory: ${
|
|
87
|
-
suggestion: `Run: mkdir -p ${
|
|
95
|
+
message: `Missing required directory: ${displayPath}`,
|
|
96
|
+
suggestion: `Run: mkdir -p ${fullPath}`,
|
|
88
97
|
autoFixable: true,
|
|
89
98
|
});
|
|
90
99
|
}
|
|
@@ -92,30 +101,34 @@ async function checkProjectStructure(basePath, report) {
|
|
|
92
101
|
report.passed++;
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
|
-
for (const
|
|
96
|
-
const
|
|
104
|
+
for (const fullPath of recommendedDirs) {
|
|
105
|
+
const displayPath = displayProjectPath(paths.projectRoot, fullPath);
|
|
97
106
|
if (!fs.existsSync(fullPath)) {
|
|
98
107
|
report.warnings++;
|
|
99
108
|
report.results.push({
|
|
100
109
|
category: 'Structure',
|
|
101
110
|
rule: 'recommended-directory',
|
|
102
111
|
severity: 'warning',
|
|
103
|
-
message: `Missing recommended directory: ${
|
|
104
|
-
suggestion: `Consider adding ${
|
|
112
|
+
message: `Missing recommended directory: ${displayPath}`,
|
|
113
|
+
suggestion: `Consider adding ${displayPath} for better DDD organization`,
|
|
105
114
|
});
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
117
|
}
|
|
109
|
-
|
|
110
|
-
const
|
|
118
|
+
function displayProjectPath(projectRoot, targetPath) {
|
|
119
|
+
const relativePath = path.relative(projectRoot, targetPath);
|
|
120
|
+
return relativePath && !relativePath.startsWith('..') ? relativePath : targetPath;
|
|
121
|
+
}
|
|
122
|
+
async function checkDddPatterns(modulesPath, report) {
|
|
111
123
|
if (!fs.existsSync(modulesPath))
|
|
112
124
|
return;
|
|
113
|
-
const modules = fs
|
|
125
|
+
const modules = fs
|
|
126
|
+
.readdirSync(modulesPath)
|
|
127
|
+
.filter((f) => fs.statSync(path.join(modulesPath, f)).isDirectory());
|
|
114
128
|
for (const moduleName of modules) {
|
|
115
129
|
const modulePath = path.join(modulesPath, moduleName);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!fs.existsSync(domainPath)) {
|
|
130
|
+
const domainPaths = getDomainLayerPaths(modulePath);
|
|
131
|
+
if (domainPaths.length === 0) {
|
|
119
132
|
report.warnings++;
|
|
120
133
|
report.results.push({
|
|
121
134
|
category: 'DDD Pattern',
|
|
@@ -123,22 +136,29 @@ async function checkDddPatterns(basePath, report) {
|
|
|
123
136
|
severity: 'warning',
|
|
124
137
|
message: `Module "${moduleName}" missing domain layer`,
|
|
125
138
|
file: modulePath,
|
|
126
|
-
suggestion: 'Add domain/
|
|
139
|
+
suggestion: 'Add application/domain/ (CLI layout) or domain/ (classic layout) with domain types',
|
|
127
140
|
});
|
|
128
141
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
for (const domainPath of domainPaths) {
|
|
143
|
+
// Check for entity immutability patterns
|
|
144
|
+
const entitiesPath = path.join(domainPath, 'entities');
|
|
145
|
+
if (fs.existsSync(entitiesPath)) {
|
|
146
|
+
await checkEntityImmutability(entitiesPath, report);
|
|
147
|
+
}
|
|
148
|
+
// Check for proper aggregate roots
|
|
149
|
+
await checkAggregateRoots(domainPath, report);
|
|
133
150
|
}
|
|
134
|
-
// Check for proper aggregate roots
|
|
135
|
-
await checkAggregateRoots(domainPath, moduleName, report);
|
|
136
151
|
// Check repository interface in domain layer
|
|
137
|
-
await checkRepositoryPattern(modulePath, moduleName, report);
|
|
152
|
+
await checkRepositoryPattern(modulePath, moduleName, domainPaths, report);
|
|
138
153
|
}
|
|
139
154
|
}
|
|
140
|
-
|
|
141
|
-
|
|
155
|
+
function getDomainLayerPaths(modulePath) {
|
|
156
|
+
return ['application/domain', 'domain']
|
|
157
|
+
.map((relativePath) => path.join(modulePath, relativePath))
|
|
158
|
+
.filter((domainPath) => fs.existsSync(domainPath));
|
|
159
|
+
}
|
|
160
|
+
async function checkEntityImmutability(entitiesPath, report) {
|
|
161
|
+
const files = fs.readdirSync(entitiesPath).filter((f) => f.endsWith('.ts'));
|
|
142
162
|
for (const file of files) {
|
|
143
163
|
const filePath = path.join(entitiesPath, file);
|
|
144
164
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
@@ -172,7 +192,7 @@ async function checkEntityImmutability(entitiesPath, moduleName, report) {
|
|
|
172
192
|
}
|
|
173
193
|
}
|
|
174
194
|
}
|
|
175
|
-
async function checkAggregateRoots(domainPath,
|
|
195
|
+
async function checkAggregateRoots(domainPath, report) {
|
|
176
196
|
if (!fs.existsSync(domainPath))
|
|
177
197
|
return;
|
|
178
198
|
const files = getAllTypeScriptFiles(domainPath);
|
|
@@ -180,10 +200,8 @@ async function checkAggregateRoots(domainPath, moduleName, report) {
|
|
|
180
200
|
const content = fs.readFileSync(file, 'utf-8');
|
|
181
201
|
// Check if entity extends AggregateRoot when it should
|
|
182
202
|
if (content.includes('class ') && content.includes('Entity')) {
|
|
183
|
-
const hasAggregateRoot = content.includes('extends AggregateRoot') ||
|
|
184
|
-
|
|
185
|
-
const hasDomainEvents = content.includes('domainEvents') ||
|
|
186
|
-
content.includes('addDomainEvent');
|
|
203
|
+
const hasAggregateRoot = content.includes('extends AggregateRoot') || content.includes('extends BaseEntity');
|
|
204
|
+
const hasDomainEvents = content.includes('domainEvents') || content.includes('addDomainEvent');
|
|
187
205
|
if (hasDomainEvents && !hasAggregateRoot) {
|
|
188
206
|
report.warnings++;
|
|
189
207
|
report.results.push({
|
|
@@ -198,15 +216,12 @@ async function checkAggregateRoots(domainPath, moduleName, report) {
|
|
|
198
216
|
}
|
|
199
217
|
}
|
|
200
218
|
}
|
|
201
|
-
async function checkRepositoryPattern(modulePath, moduleName, report) {
|
|
202
|
-
const domainPath = path.join(modulePath, 'domain');
|
|
219
|
+
async function checkRepositoryPattern(modulePath, moduleName, domainPaths, report) {
|
|
203
220
|
const infraPath = path.join(modulePath, 'infrastructure');
|
|
204
221
|
// Check for repository interface in domain layer
|
|
205
|
-
const domainRepoFiles =
|
|
206
|
-
? getAllTypeScriptFiles(domainPath).filter(f => f.includes('repository'))
|
|
207
|
-
: [];
|
|
222
|
+
const domainRepoFiles = domainPaths.flatMap((domainPath) => getAllTypeScriptFiles(domainPath).filter((file) => file.includes('repository')));
|
|
208
223
|
const infraRepoFiles = fs.existsSync(infraPath)
|
|
209
|
-
? getAllTypeScriptFiles(infraPath).filter(f => f.includes('repository'))
|
|
224
|
+
? getAllTypeScriptFiles(infraPath).filter((f) => f.includes('repository'))
|
|
210
225
|
: [];
|
|
211
226
|
if (infraRepoFiles.length > 0 && domainRepoFiles.length === 0) {
|
|
212
227
|
report.warnings++;
|
|
@@ -270,8 +285,7 @@ async function checkTypeScriptStrictness(basePath, report) {
|
|
|
270
285
|
});
|
|
271
286
|
}
|
|
272
287
|
}
|
|
273
|
-
async function checkNamingConventions(
|
|
274
|
-
const modulesPath = path.join(basePath, 'src/modules');
|
|
288
|
+
async function checkNamingConventions(modulesPath, report) {
|
|
275
289
|
if (!fs.existsSync(modulesPath))
|
|
276
290
|
return;
|
|
277
291
|
const files = getAllTypeScriptFiles(modulesPath);
|
|
@@ -295,8 +309,9 @@ async function checkNamingConventions(basePath, report) {
|
|
|
295
309
|
if (classMatch) {
|
|
296
310
|
for (const match of classMatch) {
|
|
297
311
|
const className = match.replace('export class ', '');
|
|
312
|
+
const firstCharacter = className[0];
|
|
298
313
|
// Check PascalCase
|
|
299
|
-
if (
|
|
314
|
+
if (firstCharacter && firstCharacter !== firstCharacter.toUpperCase()) {
|
|
300
315
|
report.errors++;
|
|
301
316
|
report.results.push({
|
|
302
317
|
category: 'Naming',
|
|
@@ -310,17 +325,17 @@ async function checkNamingConventions(basePath, report) {
|
|
|
310
325
|
}
|
|
311
326
|
}
|
|
312
327
|
}
|
|
313
|
-
async function checkTestCoverage(
|
|
314
|
-
const modulesPath = path.join(basePath, 'src/modules');
|
|
328
|
+
async function checkTestCoverage(modulesPath, report) {
|
|
315
329
|
if (!fs.existsSync(modulesPath))
|
|
316
330
|
return;
|
|
317
|
-
const modules = fs
|
|
331
|
+
const modules = fs
|
|
332
|
+
.readdirSync(modulesPath)
|
|
333
|
+
.filter((f) => fs.statSync(path.join(modulesPath, f)).isDirectory());
|
|
318
334
|
for (const moduleName of modules) {
|
|
319
335
|
const modulePath = path.join(modulesPath, moduleName);
|
|
320
|
-
const sourceFiles = getAllTypeScriptFiles(modulePath).filter(f => !f.includes('.spec.ts') && !f.includes('.test.ts'));
|
|
321
|
-
const testFiles = getAllTypeScriptFiles(modulePath).filter(f => f.includes('.spec.ts') || f.includes('.test.ts'));
|
|
336
|
+
const sourceFiles = getAllTypeScriptFiles(modulePath).filter((f) => !f.includes('.spec.ts') && !f.includes('.test.ts'));
|
|
322
337
|
// Check for service tests
|
|
323
|
-
const services = sourceFiles.filter(f => f.includes('.service.ts'));
|
|
338
|
+
const services = sourceFiles.filter((f) => f.includes('.service.ts'));
|
|
324
339
|
for (const service of services) {
|
|
325
340
|
const testFile = service.replace('.service.ts', '.service.spec.ts');
|
|
326
341
|
if (!fs.existsSync(testFile)) {
|
|
@@ -336,7 +351,7 @@ async function checkTestCoverage(basePath, report) {
|
|
|
336
351
|
}
|
|
337
352
|
}
|
|
338
353
|
// Check for domain entity tests
|
|
339
|
-
const entities = sourceFiles.filter(f => f.includes('.entity.ts'));
|
|
354
|
+
const entities = sourceFiles.filter((f) => f.includes('.entity.ts'));
|
|
340
355
|
for (const entity of entities) {
|
|
341
356
|
const testFile = entity.replace('.entity.ts', '.entity.spec.ts');
|
|
342
357
|
if (!fs.existsSync(testFile)) {
|
|
@@ -351,17 +366,15 @@ async function checkTestCoverage(basePath, report) {
|
|
|
351
366
|
}
|
|
352
367
|
}
|
|
353
368
|
}
|
|
354
|
-
async function checkImportPatterns(
|
|
355
|
-
const modulesPath = path.join(basePath, 'src/modules');
|
|
369
|
+
async function checkImportPatterns(modulesPath, report) {
|
|
356
370
|
if (!fs.existsSync(modulesPath))
|
|
357
371
|
return;
|
|
358
|
-
const modules = fs
|
|
372
|
+
const modules = fs
|
|
373
|
+
.readdirSync(modulesPath)
|
|
374
|
+
.filter((f) => fs.statSync(path.join(modulesPath, f)).isDirectory());
|
|
359
375
|
for (const moduleName of modules) {
|
|
360
376
|
const modulePath = path.join(modulesPath, moduleName);
|
|
361
|
-
const
|
|
362
|
-
if (!fs.existsSync(domainPath))
|
|
363
|
-
continue;
|
|
364
|
-
const domainFiles = getAllTypeScriptFiles(domainPath);
|
|
377
|
+
const domainFiles = getDomainLayerPaths(modulePath).flatMap((domainPath) => getAllTypeScriptFiles(domainPath));
|
|
365
378
|
for (const file of domainFiles) {
|
|
366
379
|
const content = fs.readFileSync(file, 'utf-8');
|
|
367
380
|
// Check for infrastructure imports in domain layer (violation)
|
|
@@ -395,11 +408,10 @@ async function checkImportPatterns(basePath, report) {
|
|
|
395
408
|
}
|
|
396
409
|
}
|
|
397
410
|
}
|
|
398
|
-
async function checkEntityIntegrity(
|
|
399
|
-
const modulesPath = path.join(basePath, 'src/modules');
|
|
411
|
+
async function checkEntityIntegrity(modulesPath, report) {
|
|
400
412
|
if (!fs.existsSync(modulesPath))
|
|
401
413
|
return;
|
|
402
|
-
const files = getAllTypeScriptFiles(modulesPath).filter(f => f.includes('.entity.ts'));
|
|
414
|
+
const files = getAllTypeScriptFiles(modulesPath).filter((f) => f.includes('.entity.ts'));
|
|
403
415
|
for (const file of files) {
|
|
404
416
|
const content = fs.readFileSync(file, 'utf-8');
|
|
405
417
|
// Check for ID field
|
|
@@ -430,11 +442,12 @@ async function checkEntityIntegrity(basePath, report) {
|
|
|
430
442
|
}
|
|
431
443
|
}
|
|
432
444
|
}
|
|
433
|
-
async function checkModuleBoundaries(
|
|
434
|
-
const modulesPath = path.join(basePath, 'src/modules');
|
|
445
|
+
async function checkModuleBoundaries(modulesPath, report) {
|
|
435
446
|
if (!fs.existsSync(modulesPath))
|
|
436
447
|
return;
|
|
437
|
-
const modules = fs
|
|
448
|
+
const modules = fs
|
|
449
|
+
.readdirSync(modulesPath)
|
|
450
|
+
.filter((f) => fs.statSync(path.join(modulesPath, f)).isDirectory());
|
|
438
451
|
for (const moduleName of modules) {
|
|
439
452
|
const modulePath = path.join(modulesPath, moduleName);
|
|
440
453
|
const files = getAllTypeScriptFiles(modulePath);
|
|
@@ -482,7 +495,7 @@ function getAllTypeScriptFiles(dir) {
|
|
|
482
495
|
return files;
|
|
483
496
|
}
|
|
484
497
|
async function applyFixes(report) {
|
|
485
|
-
const fixableResults = report.results.filter(r => r.autoFixable);
|
|
498
|
+
const fixableResults = report.results.filter((r) => r.autoFixable);
|
|
486
499
|
if (fixableResults.length === 0) {
|
|
487
500
|
console.log(chalk_1.default.gray('\nNo auto-fixable issues found.'));
|
|
488
501
|
return;
|
|
@@ -508,10 +521,12 @@ function printReport(report, verbose) {
|
|
|
508
521
|
for (const [category, results] of byCategory) {
|
|
509
522
|
console.log(chalk_1.default.bold(`\nš ${category}`));
|
|
510
523
|
for (const result of results) {
|
|
511
|
-
const icon = result.severity === 'error' ? 'ā' :
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
result.severity === 'warning'
|
|
524
|
+
const icon = result.severity === 'error' ? 'ā' : result.severity === 'warning' ? 'ā ļø' : 'ā¹ļø';
|
|
525
|
+
const color = result.severity === 'error'
|
|
526
|
+
? chalk_1.default.red
|
|
527
|
+
: result.severity === 'warning'
|
|
528
|
+
? chalk_1.default.yellow
|
|
529
|
+
: chalk_1.default.gray;
|
|
515
530
|
console.log(color(` ${icon} ${result.message}`));
|
|
516
531
|
if (verbose) {
|
|
517
532
|
if (result.file) {
|