use only TOiR.domain.dsl like single source of truth for generation, update context for pinned .gitignore

This commit is contained in:
MaKarin
2026-03-21 17:14:37 +03:00
parent 8d6875f4b0
commit 7e6b76cef2
18 changed files with 394 additions and 1759 deletions

View File

@@ -35,15 +35,47 @@ Follow:
---
# Step 1 — Parse DSL
# Input Contract
Read DSL inputs and extract:
Required input:
- `domain/*.dsl`
Optional extension input:
- `overrides/api-overrides.dsl`
Optional extension layout:
```text
overrides/
api-overrides.dsl
```
Rules:
- Parse `domain/*.dsl` as the only authoritative DSL input.
- Generate DTOs and REST API contracts automatically from the parsed domain model.
- The generator must work when `overrides/api-overrides.dsl` is absent.
- Optional overrides may refine derived API behavior but must not redefine entities, attributes, primary keys, foreign keys, relations, or enums.
- Supplemental DTO/API DSL inputs must not participate in backend parsing, dependency resolution, or backend generation decisions.
- Do not read standalone DTO or API DSL files.
---
# Step 1 — Parse Domain DSL
Read `domain/*.dsl` and extract:
- entities
- attributes, including the actual primary key attribute per entity
- enums
- foreign keys
All entities, attributes, primary keys, foreign keys, relations, and enums used by the backend pipeline must come from the parsed domain DSL.
If `overrides/api-overrides.dsl` exists, process it only after the domain model has been parsed and only as optional non-authoritative metadata.
The generator must treat auth as default runtime infrastructure, not as a DSL feature toggle.
---
@@ -89,6 +121,12 @@ Generate backend source artifacts:
- sanitize update payload before Prisma
- remove `id`, the entity primary key, and readonly attributes from `data`
- do not pass the raw request body directly to `prisma.*.update()`
6. **List/query sorting methods**:
- use only actual model field names in ORM `orderBy`
- if the API exposes synthetic `id` for React Admin but the real primary key is different, map incoming `_sort=id` to the real primary key field before building `orderBy`
- apply this rule to every entity with a non-`id` primary key
All backend DTOs and REST endpoints are derived artifacts. The generator must not require or parse separate DTO/API DSL documents.
Use mapping rules from `backend/prisma-rules.md`:
@@ -223,3 +261,4 @@ Run runtime, auth, and contract checks from `generation/post-generation-validati
- protected routes reject unauthenticated requests with `401`
- authenticated users with insufficient role receive `403`
- React Admin-compatible API responses include `id` for every record
- natural-key entities translate React Admin `_sort=id` to the real primary key field

View File

@@ -2,6 +2,8 @@
This document describes the **developer workflow** for running a generated fullstack application locally. The generator must produce a project that supports this workflow so the app is **fully runnable** after generation, including authentication.
This workflow assumes the project was generated from `domain/*.dsl` plus optional non-duplicating overrides only. Developers must not need to prepare separate DTO/API/UI DSL inputs before running the app.
---
# Prerequisites

View File

@@ -12,7 +12,35 @@ Follow:
---
# Step 1 — Parse DSL
# Input Contract
Required input:
- `domain/*.dsl`
Optional extension input:
- `overrides/ui-overrides.dsl`
Optional extension layout:
```text
overrides/
ui-overrides.dsl
```
Rules:
- Parse `domain/*.dsl` as the only authoritative DSL input.
- Generate React Admin resources, views, and field mappings automatically from the parsed domain model.
- The generator must work when `overrides/ui-overrides.dsl` is absent.
- Optional overrides may refine derived UI behavior but must not redefine entities, attributes, primary keys, foreign keys, relations, or enums.
- Supplemental UI DSL inputs must not participate in frontend parsing, dependency resolution, or frontend generation decisions.
- Do not read a standalone UI DSL file.
---
# Step 1 — Parse Domain DSL
Extract:
@@ -20,6 +48,10 @@ Extract:
- attributes
- relation fields required for reference inputs and reference displays
All frontend resources, fields, references, routes, and type-driven widget choices must be derived from the parsed domain DSL before optional overrides are considered.
If `overrides/ui-overrides.dsl` exists, process it only after the domain model has been parsed and only as optional non-authoritative metadata.
The generator must treat auth as default frontend infrastructure rather than as an optional feature.
---
@@ -54,7 +86,7 @@ For each entity create:
- `EntityEdit.tsx`
- `EntityShow.tsx`
Resource generation must remain compatible with the auth-aware shared request seam.
Resource generation must remain compatible with the auth-aware shared request seam and must be derived from domain metadata rather than a separate UI DSL.
---
@@ -62,6 +94,14 @@ Resource generation must remain compatible with the auth-aware shared request se
Map DSL attributes to React Admin components according to existing field rules and relation semantics.
Minimum type mapping:
- `string`, `text` -> `TextInput`, `TextField`
- `integer`, `decimal` -> `NumberInput`, `NumberField`
- `date` -> `DateInput`, `DateField`
- `enum` -> `SelectInput`
- foreign key -> `ReferenceInput`, `ReferenceField`
Reference/resource lookups must continue to flow through the same shared authenticated request layer used by the main CRUD resources.
---
@@ -98,6 +138,11 @@ Generate Keycloak frontend integration with these required rules:
- Authorization Code + PKCE with `S256`
- initialize Keycloak before React render
- provide a React Admin `authProvider`
- derive `authProvider.getIdentity()` from token claims already present in the parsed token
- prefer `sub`, `preferred_username`, `email`, and `name` for identity resolution
- do not call `keycloak.loadUserProfile()` by default
- do not rely on the Keycloak `/account` endpoint for baseline CRUD/admin generation
- avoid the `/account` request entirely by default rather than broadening Keycloak CORS behavior
- distinguish auth failures from authorization failures:
- `401` -> force logout / re-authentication
- `403` -> do not re-authenticate; surface access denied / permission error

View File

@@ -6,6 +6,17 @@ After generating the backend or fullstack application, run these checks to ensur
# Validation Checklist
## Source-of-truth input contract
- [ ] Fullstack generation succeeds when `domain/*.dsl` is the only required DSL input.
- [ ] The generator can produce backend and frontend outputs from `domain/*.dsl` alone before optional overrides are considered.
- [ ] DTO, API, and UI artifacts are derived automatically from the domain model, keys, relations, and enums.
- [ ] Optional override files are not required for a successful generation run.
- [ ] Optional overrides, if present, refine only derived API/UI output and do not duplicate the domain model.
- [ ] No generator step depends on duplicated domain structures outside `domain/*.dsl`.
**Failure symptoms:** generation requires extra DSL inputs, generated layers drift from the domain model, or the pipeline fails when override files are absent.
## 1. Frontend and backend env files
- [ ] `server/.env.example` exists and documents:
@@ -27,7 +38,22 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 2. Keycloak realm artifact
## 2. Git ignore hygiene
- [ ] Root `.gitignore` exists.
- [ ] `server/.gitignore` exists.
- [ ] `client/.gitignore` exists.
- [ ] Generated gitignore rules exclude local dependency directories such as `node_modules/`.
- [ ] Generated gitignore rules exclude build artifacts such as `dist/` and `dist-ssr/`.
- [ ] Generated gitignore rules exclude local env files such as `.env`, `.env.local`, and `.env.*.local`.
- [ ] Generated gitignore rules exclude `coverage/` and `*.tsbuildinfo`.
- [ ] Generated gitignore rules do **not** exclude committed project artifacts such as source files, docs, and `.env.example`.
**Failure symptoms:** `npm install`, local builds, or local env setup explode git status with thousands of files that should remain untracked.
---
## 3. Keycloak realm artifact
- [ ] A root-level generated Keycloak realm import artifact exists.
- [ ] If the repository default filename `toir-realm.json` is not used, the project-specific equivalent is documented consistently across bootstrap and workflow docs.
@@ -50,7 +76,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 3. Frontend auth files and behavior
## 4. Frontend auth files and behavior
- [ ] Generated frontend includes:
- `client/src/config/env.ts`
@@ -65,6 +91,9 @@ After generating the backend or fullstack application, run these checks to ensur
- [ ] No custom in-app username/password login form is generated.
- [ ] `Authorization Code + PKCE (S256)` is encoded in the frontend auth flow.
- [ ] `client/src/dataProvider.ts` or the documented shared request seam injects `Authorization: Bearer <access_token>` into all API requests.
- [ ] `authProvider.getIdentity()` derives identity from parsed token claims such as `sub`, `preferred_username`, `email`, and `name`.
- [ ] Generated frontend auth code does not call `keycloak.loadUserProfile()`.
- [ ] Generated frontend auth code does not rely on the Keycloak `/account` endpoint for baseline CRUD/admin generation.
- [ ] Token refresh is concurrency-safe:
- one shared in-flight refresh operation
- no parallel refresh stampede
@@ -77,7 +106,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 4. Backend auth files and behavior
## 5. Backend auth files and behavior
- [ ] Generated backend includes:
- `server/src/auth/auth.module.ts`
@@ -100,7 +129,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 5. CRUD protection and RBAC defaults
## 6. CRUD protection and RBAC defaults
- [ ] `/health` is public.
- [ ] Each generated CRUD controller method other than explicit public routes is protected by the generated auth/RBAC infrastructure.
@@ -115,7 +144,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 6. PrismaService implementation
## 7. PrismaService implementation
- [ ] A `PrismaService` (or equivalent) class exists and extends `PrismaClient`.
- [ ] It implements `OnModuleInit` and calls `await this.$connect()` in `onModuleInit()`.
@@ -127,7 +156,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 7. Prisma client lifecycle
## 8. Prisma client lifecycle
- [ ] `package.json` includes a script that runs Prisma client generation:
- either `"postinstall": "prisma generate"` (or `npx prisma generate`)
@@ -138,7 +167,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 8. Database migration
## 9. Database migration
- [ ] Migration workflow is documented.
- [ ] Instruction to run `npx prisma migrate dev` exists after first generation or schema change.
@@ -148,7 +177,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 9. REST route parameters
## 10. REST route parameters
- [ ] For each entity, path parameters use the correct primary key name from the DSL.
- [ ] Entity with PK `id` uses `/:id`.
@@ -160,18 +189,20 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 10. DTO type mapping and React Admin ID compatibility
## 11. DTO type mapping and React Admin ID compatibility
- [ ] DSL `decimal` maps to DTO/API `string`.
- [ ] DSL `date` maps to DTO/API `string` (ISO) or equivalent string serialization.
- [ ] Every API response object contains a field named `id`.
- [ ] If the entity primary key is not named `id`, the response maps the primary key to `id`.
- [ ] For entities with non-`id` primary keys, backend list/query logic translates React Admin `_sort=id` to the real primary key field.
- [ ] Generated ORM `orderBy` clauses never reference synthetic `id` when the underlying model field does not exist.
**Failure symptoms:** serialization issues for decimals/dates, or React Admin cannot identify records.
---
## 11. Update payload sanitization
## 12. Update payload sanitization
- [ ] Update endpoints do not pass `id` or the primary key in Prisma `data`.
- [ ] Generated update methods remove `id`, the entity primary key, and readonly attributes before calling `prisma.*.update()`.
@@ -180,7 +211,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 12. Database runtime
## 13. Database runtime
- [ ] `docker-compose.yml` exists at the project root.
- [ ] It defines a PostgreSQL service with image `postgres:16`, port `5432`, and credentials matching `DATABASE_URL`.
@@ -191,7 +222,7 @@ After generating the backend or fullstack application, run these checks to ensur
---
## 13. Migrations, seed, and health endpoint
## 14. Migrations, seed, and health endpoint
- [ ] `npx prisma migrate dev` runs successfully from `server/`.
- [ ] Seed script exists at `server/prisma/seed.ts` (or equivalent).
@@ -209,9 +240,11 @@ After generating the backend or fullstack application, run these checks to ensur
| --- | --- |
| Frontend env | `client/.env.example` with required Vite auth vars |
| Backend env | `server/.env.example` with DB, CORS, and Keycloak vars |
| Git ignore | Root/server/client `.gitignore` exclude local-only artifacts |
| Fail-fast config | Startup fails when required auth env is missing |
| Realm artifact | Root generated realm import artifact with self-contained auth setup |
| Frontend auth | `keycloak.ts`, `authProvider.ts`, authenticated `dataProvider.ts` |
| Frontend identity | Token-claim based `getIdentity()`; no `loadUserProfile()` / `/account` dependency |
| Backend auth | `AuthModule`, guards, decorators, typed principal |
| JWKS strategy | explicit URL -> discovery -> certs fallback |
| Role source | `realm_access.roles` only |
@@ -224,6 +257,7 @@ After generating the backend or fullstack application, run these checks to ensur
| Prisma lifecycle | `OnModuleInit` + `$connect()`, no `beforeExit` |
| Update sanitization | Strip `id` / PK / readonly before Prisma update |
| React Admin `id` | Every record includes `id` |
| Natural-key sorting | Map React Admin `_sort=id` to the real primary key field |
| Database runtime | PostgreSQL compose exists and starts |
---
@@ -231,6 +265,7 @@ After generating the backend or fullstack application, run these checks to ensur
# Integration with generation pipeline
1. Backend and frontend generation must produce artifacts that satisfy the above by default.
2. Runtime bootstrap must include Keycloak realm import/verification before app startup.
3. After generation, run this checklist manually or via an automated script.
4. If any check fails, update the generator context so future runs pass without manual repair.
2. The generator must successfully build the fullstack app from `domain/*.dsl` alone; optional overrides may refine output but cannot be required.
3. Runtime bootstrap must include Keycloak realm import/verification before app startup.
4. After generation, run this checklist manually or via an automated script.
5. If any check fails, update the generator context so future runs pass without manual repair.

View File

@@ -9,6 +9,8 @@ The generator must produce a **runnable development environment** consisting of:
- frontend SPA
- PostgreSQL database
Runtime bootstrap assumes the application was generated from `domain/*.dsl` plus optional non-duplicating overrides only. There is no separate DTO/API/UI DSL bootstrap step.
The generator must also produce the runtime artifacts required to bootstrap auth from zero, including a root-level Keycloak realm import artifact. The repository default example filename is `toir-realm.json`, but future generations must allow a project-specific equivalent.
---

View File

@@ -80,13 +80,35 @@ npm install keycloak-js
Generation pipeline order:
1. **Parse DSL** — Read domain, DTO, API, and UI DSL files.
1. **Parse DSL** — Read `domain/*.dsl` as the single required input. If present, optional override files under `overrides/` may be applied after domain parsing, but DTO/API/UI DSL files must not be required.
2. **Run CLI scaffolding** — Create `server` with NestJS CLI and `client` with Vite CLI; install runtime and auth dependencies listed above.
3. **Code generation** — Generate Prisma schema, NestJS modules/DTOs/PrismaService/auth infrastructure, and React Admin resources/auth integration.
4. **Runtime infrastructure** — Generate backend/frontend `.env.example`, runtime config files, lifecycle scripts, and a root-level Keycloak realm import artifact (repository default example filename: `toir-realm.json`).
4. **Runtime infrastructure** — Generate backend/frontend `.env.example`, root/package `.gitignore` files, runtime config files, lifecycle scripts, and a root-level Keycloak realm import artifact (repository default example filename: `toir-realm.json`).
5. **Database runtime** — Generate `docker-compose.yml` in project root with PostgreSQL service (`postgres`, image `postgres:16`, port `5432:5432`).
6. **Migration** — Apply schema with `npx prisma migrate dev`.
7. **Seed** — Populate minimal development data with `npx prisma db seed`.
8. **Validation** — Run checks from `generation/post-generation-validation.md`, including auth validation and realm-template validation.
Scaffolding (steps 12) must be done with the CLI. Steps 38 must be generated from the DSL and the project context documents, including the auth-specific context in `auth/*.md`.
Scaffolding (steps 12) must be done with the CLI. Steps 38 must be generated from `domain/*.dsl`, optional non-duplicating overrides in `overrides/`, and the project context documents, including the auth-specific context in `auth/*.md`.
There is no separate DTO/API/UI DSL preparation step in the scaffolding workflow.
## Git ignore rules
The generated project must include:
- root `.gitignore`
- `server/.gitignore`
- `client/.gitignore`
These files must keep local-only artifacts out of git, including at minimum:
- `node_modules/`
- `dist/`
- `dist-ssr/`
- `coverage/`
- `*.tsbuildinfo`
- `.env`
- `.env.local`
- `.env.*.local`
The generator must not ignore committed source, documentation, or `.env.example` files.

View File

@@ -2,6 +2,9 @@
When the DSL changes, regeneration must preserve the default auth-enabled runtime rather than falling back to CRUD-only output.
`domain/*.dsl` remains the single required source of truth for regeneration. DTOs, API contracts, and React Admin resources must be re-derived from it on every run. Optional overrides in `overrides/api-overrides.dsl` and `overrides/ui-overrides.dsl` may be applied after derivation, but they must never duplicate or redefine the domain model.
Regeneration must not resurrect or depend on supplemental DTO/API/UI DSL inputs. Every derived layer must be recalculated from `domain/*.dsl` plus optional non-duplicating overrides only.
## Required regeneration sequence
1. Regenerate `prisma/schema.prisma`.
@@ -23,13 +26,17 @@ When the DSL changes, regeneration must preserve the default auth-enabled runtim
- `App.tsx` auth wiring
- `main.tsx` init-before-render flow
7. Regenerate backend and frontend `.env.example` files so the auth env contract stays in sync.
8. Regenerate the root-level Keycloak realm import artifact. The repository default example filename is `toir-realm.json`, but the generator must allow a project-specific equivalent.
9. Re-run post-generation validation, including:
8. Regenerate root/package `.gitignore` files so local-only artifacts remain out of git after regeneration.
9. Regenerate the root-level Keycloak realm import artifact. The repository default example filename is `toir-realm.json`, but the generator must allow a project-specific equivalent.
10. Re-run post-generation validation, including:
- gitignore coverage for dependency, build, env, coverage, and tsbuildinfo artifacts
- auth dependency checks
- fail-fast env checks
- token-claim based identity with no `loadUserProfile()` / `/account` dependency
- `/health` public check
- unauthenticated protected route -> `401`
- insufficient role -> `403`
- natural-key `_sort=id` mapping checks
- realm-template validation
## Guardrails