Skip to content

Commit b59df81

Browse files
committed
Merge branch 'next' of github.com:devforth/adminforth into next
2 parents 73c19f3 + 2657ee3 commit b59df81

11 files changed

Lines changed: 1086 additions & 40 deletions

File tree

AGENTS.md

Lines changed: 212 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,212 @@
1-
Follow SOLID principles
2-
Use DRY and avoid duplication
3-
Keep the code KISS unless complexity is required
4-
Avoid unnecessary abstractions.
5-
Cover edge cases and clear error handling
1+
2+
# AGENTS.md
3+
4+
## General engineering rules
5+
6+
Write code as if the system contracts are already defined and trusted.
7+
8+
Do not add defensive checks “just in case” when the contract, type, schema, backend response, or controlled internal API already guarantees the shape of the data.
9+
10+
Prefer simplicity, clarity, and consistency over paranoia.
11+
12+
Follow these principles strictly:
13+
14+
- DRY
15+
- SOLID
16+
- YAGNI
17+
- Keep code minimal
18+
- Trust typed contracts
19+
- Avoid duplicate validation
20+
- Avoid speculative fallback logic
21+
- Avoid inline regex literals when reused or non-trivial
22+
23+
---
24+
25+
## Trust the contract
26+
27+
If backend and frontend are part of the same system, and the backend explicitly guarantees a response shape, do **not** re-validate every field on the frontend.
28+
29+
Bad:
30+
- backend returns a strict object
31+
- frontend then checks every field for `undefined`, wrong type, empty string, wrong enum, etc.
32+
- frontend adds fallback branches for impossible states
33+
34+
Good:
35+
- backend owns validation
36+
- shared types or schemas define the contract
37+
- frontend consumes the contract directly
38+
- frontend only handles real UI states, not imaginary protocol corruption
39+
40+
Do not treat trusted internal responses as untrusted external input.
41+
42+
Only add extra runtime validation when:
43+
- data comes from a truly external/untrusted source
44+
- the contract is unknown or unstable
45+
- there is an explicit requirement for hardening
46+
- security boundaries require validation
47+
- corrupted legacy data is known to exist in production
48+
49+
If none of the above is true, do not add redundant guards.
50+
51+
---
52+
53+
## No duplicate validation
54+
55+
Validation must live in one clear place.
56+
57+
Prefer this order:
58+
1. Boundary validation
59+
2. Domain validation
60+
3. UI rendering without re-checking the same invariants
61+
62+
Examples:
63+
- Validate request payload on the backend boundary
64+
- Validate forms at form/input level
65+
- Validate DB input before persistence if needed
66+
- Do not re-check the same rule again deeper in the stack unless there is a real new boundary
67+
68+
If a field is already guaranteed by type/schema/backend, do not validate it again in consumers.
69+
70+
---
71+
72+
## Frontend rules
73+
74+
When rendering data from a trusted backend:
75+
- do not check every property manually
76+
- do not write `if (!obj || !obj.a || !obj.b || !obj.c)` chains for guaranteed objects
77+
- do not silently swallow impossible states
78+
- do not invent fallback values for required fields unless product requirements explicitly ask for fallback UI
79+
80+
Prefer:
81+
- strong TypeScript types
82+
- narrow, explicit UI states
83+
- a small number of meaningful guards at actual boundaries
84+
85+
Allowed:
86+
- loading state
87+
- not-found state
88+
- permission-denied state
89+
- explicitly documented optional fields
90+
91+
Not allowed:
92+
- repetitive property-by-property paranoia checks for required response fields
93+
94+
---
95+
96+
## Backend rules
97+
98+
Backend should enforce the contract once, clearly and centrally.
99+
100+
- Validate incoming external input at the boundary
101+
- Normalize data once
102+
- Return stable, typed responses
103+
- Do not scatter the same checks across controllers, services, and callers
104+
- Do not add branches for impossible states without evidence
105+
106+
When a response format is defined, keep it strict and predictable so consumers do not need defensive coding.
107+
108+
---
109+
110+
## Regex rules
111+
112+
Do not inline non-trivial regexes directly inside business logic.
113+
114+
Bad:
115+
- inline regex literals scattered through the codebase
116+
- repeating the same regex multiple times
117+
- unreadable regex embedded inside conditions
118+
119+
Good:
120+
- define regex once as a named constant
121+
- place reusable regexes in a dedicated constants/module file
122+
- give regexes semantic names
123+
- compile once when appropriate
124+
125+
Example:
126+
- `const EMAIL_RE = /.../`
127+
- `const SLUG_RE = /.../`
128+
- `const STRIP_HTML_RE = /.../g`
129+
130+
If regex is used more than once or is not instantly obvious, extract it.
131+
132+
---
133+
134+
## No speculative coding
135+
136+
Do not add code for hypothetical scenarios unless they are documented requirements or known production realities.
137+
138+
Avoid:
139+
- “in case backend changes”
140+
- “in case this field comes null someday”
141+
- “in case this enum gets other values”
142+
- “in case this internal method returns something unexpected”
143+
144+
If such a risk is real, document it and solve it at the right boundary, not everywhere.
145+
146+
---
147+
148+
## Error handling
149+
150+
Error handling must be intentional, not reflexive.
151+
152+
Handle:
153+
- real network failures
154+
- documented optionality
155+
- known domain errors
156+
- permission/auth errors
157+
- user-caused invalid input
158+
- external system failures
159+
160+
Do not handle:
161+
- impossible states already excluded by the contract
162+
- contradictions to compile-time types without evidence
163+
- redundant field-level checks for trusted internal data
164+
165+
For impossible states, prefer failing loudly in development rather than hiding the issue with fallback logic.
166+
167+
---
168+
169+
## Code review rules for agents
170+
171+
Before writing extra guards, ask:
172+
173+
1. Is this input external and untrusted?
174+
2. Is this invariant already guaranteed by types/schema/backend?
175+
3. Am I repeating validation that already exists elsewhere?
176+
4. Is this a real production case or just imagination?
177+
5. Would this code make the system clearer, or only noisier?
178+
179+
If the invariant is already guaranteed, do not add the guard.
180+
181+
---
182+
183+
## Preferred style
184+
185+
Prefer:
186+
- shared types
187+
- schema-driven boundaries
188+
- single-source-of-truth validation
189+
- concise functions
190+
- explicit contracts
191+
- named helpers/constants
192+
- reusable compiled regex constants
193+
194+
Avoid:
195+
- defensive clutter
196+
- repeated null/undefined chains for required fields
197+
- duplicated business rules
198+
- inline complicated regex
199+
- fallback-on-fallback logic
200+
- broad “safety” code without a concrete reason
201+
202+
---
203+
204+
## Decision rule
205+
206+
When choosing between:
207+
- trusting a well-defined internal contract
208+
- or adding more defensive checks
209+
210+
choose trusting the contract, unless there is a clear boundary, documented risk, or real evidence that extra validation is needed.
211+
212+
Less code, fewer duplicate checks, clearer ownership.

adminforth/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import MysqlConnector from './dataConnectors/mysql.js';
55
import SQLiteConnector from './dataConnectors/sqlite.js';
66
import CodeInjector from './modules/codeInjector.js';
77
import ExpressServer from './servers/express.js';
8+
import OpenApiRegistry from './servers/openapi.js';
89
// import FastifyServer from './servers/fastify.js';
910
import { ADMINFORTH_VERSION, listify, suggestIfTypo, RateLimiter, RAMLock, getClientIp, isProbablyUUIDColumn, convertPeriodToSeconds, hookResponseError } from './modules/utils.js';
1011
import {
@@ -116,6 +117,7 @@ class AdminForth implements IAdminForth {
116117
// fastify: FastifyServer;
117118
auth: AdminForthAuth;
118119
codeInjector: CodeInjector;
120+
openApi: OpenApiRegistry;
119121
connectors: {
120122
[dataSourceId: string]: IAdminForthDataSourceConnectorBase,
121123
};
@@ -169,6 +171,10 @@ class AdminForth implements IAdminForth {
169171
afLogger.trace('🔧 Creating AdminForthRestAPI...');
170172
this.restApi = new AdminForthRestAPI(this);
171173
afLogger.trace('🔧 AdminForthRestAPI created');
174+
175+
afLogger.trace('🔧 Creating OpenApiRegistry...');
176+
this.openApi = new OpenApiRegistry(this);
177+
afLogger.trace('🔧 OpenApiRegistry created');
172178

173179
afLogger.trace('🔧 Creating SocketBroker...');
174180
this.websocket = new SocketBroker(this);

0 commit comments

Comments
 (0)