tina4-nodejs 3.0.0-rc.2
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/BENCHMARK_REPORT.md +96 -0
- package/CARBONAH.md +140 -0
- package/CLAUDE.md +599 -0
- package/COMPARISON.md +194 -0
- package/README.md +595 -0
- package/package.json +59 -0
- package/packages/cli/src/bin.ts +110 -0
- package/packages/cli/src/commands/init.ts +194 -0
- package/packages/cli/src/commands/migrate.ts +96 -0
- package/packages/cli/src/commands/migrateCreate.ts +59 -0
- package/packages/cli/src/commands/routes.ts +61 -0
- package/packages/cli/src/commands/serve.ts +58 -0
- package/packages/cli/src/commands/test.ts +83 -0
- package/packages/core/gallery/auth/meta.json +1 -0
- package/packages/core/gallery/auth/src/routes/api/gallery/auth/login/post.ts +22 -0
- package/packages/core/gallery/auth/src/routes/api/gallery/auth/verify/get.ts +16 -0
- package/packages/core/gallery/auth/src/routes/gallery/auth/get.ts +97 -0
- package/packages/core/gallery/database/meta.json +1 -0
- package/packages/core/gallery/database/src/routes/api/gallery/db/notes/get.ts +13 -0
- package/packages/core/gallery/database/src/routes/api/gallery/db/notes/post.ts +17 -0
- package/packages/core/gallery/database/src/routes/api/gallery/db/tables/get.ts +23 -0
- package/packages/core/gallery/error-overlay/meta.json +1 -0
- package/packages/core/gallery/error-overlay/src/routes/api/gallery/crash/get.ts +17 -0
- package/packages/core/gallery/orm/meta.json +1 -0
- package/packages/core/gallery/orm/src/routes/api/gallery/products/get.ts +12 -0
- package/packages/core/gallery/orm/src/routes/api/gallery/products/post.ts +7 -0
- package/packages/core/gallery/queue/meta.json +1 -0
- package/packages/core/gallery/queue/src/routes/api/gallery/queue/produce/post.ts +16 -0
- package/packages/core/gallery/queue/src/routes/api/gallery/queue/status/get.ts +10 -0
- package/packages/core/gallery/rest-api/meta.json +1 -0
- package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/get.ts +6 -0
- package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/post.ts +7 -0
- package/packages/core/gallery/templates/meta.json +1 -0
- package/packages/core/gallery/templates/src/routes/gallery/page/get.ts +15 -0
- package/packages/core/gallery/templates/src/templates/gallery_page.twig +257 -0
- package/packages/core/public/css/tina4.css +2463 -0
- package/packages/core/public/css/tina4.min.css +1 -0
- package/packages/core/public/favicon.ico +0 -0
- package/packages/core/public/images/logo.svg +5 -0
- package/packages/core/public/images/tina4-logo-icon.webp +0 -0
- package/packages/core/public/js/frond.min.js +420 -0
- package/packages/core/public/js/tina4-dev-admin.min.js +327 -0
- package/packages/core/public/js/tina4.min.js +93 -0
- package/packages/core/public/swagger/index.html +90 -0
- package/packages/core/public/swagger/oauth2-redirect.html +63 -0
- package/packages/core/src/ai.ts +359 -0
- package/packages/core/src/api.ts +248 -0
- package/packages/core/src/auth.ts +287 -0
- package/packages/core/src/cache.ts +121 -0
- package/packages/core/src/constants.ts +48 -0
- package/packages/core/src/container.ts +90 -0
- package/packages/core/src/devAdmin.ts +2024 -0
- package/packages/core/src/devMailbox.ts +316 -0
- package/packages/core/src/dotenv.ts +172 -0
- package/packages/core/src/errorOverlay.test.ts +122 -0
- package/packages/core/src/errorOverlay.ts +278 -0
- package/packages/core/src/events.ts +112 -0
- package/packages/core/src/fakeData.ts +309 -0
- package/packages/core/src/graphql.ts +812 -0
- package/packages/core/src/health.ts +31 -0
- package/packages/core/src/htmlElement.ts +172 -0
- package/packages/core/src/i18n.ts +136 -0
- package/packages/core/src/index.ts +88 -0
- package/packages/core/src/logger.ts +226 -0
- package/packages/core/src/messenger.ts +822 -0
- package/packages/core/src/middleware.ts +138 -0
- package/packages/core/src/queue.ts +481 -0
- package/packages/core/src/queueBackends/kafkaBackend.ts +348 -0
- package/packages/core/src/queueBackends/rabbitmqBackend.ts +479 -0
- package/packages/core/src/rateLimiter.ts +107 -0
- package/packages/core/src/request.ts +189 -0
- package/packages/core/src/response.ts +146 -0
- package/packages/core/src/routeDiscovery.ts +87 -0
- package/packages/core/src/router.ts +398 -0
- package/packages/core/src/scss.ts +366 -0
- package/packages/core/src/server.ts +610 -0
- package/packages/core/src/service.ts +380 -0
- package/packages/core/src/session.ts +480 -0
- package/packages/core/src/sessionHandlers/mongoHandler.ts +286 -0
- package/packages/core/src/sessionHandlers/valkeyHandler.ts +184 -0
- package/packages/core/src/static.ts +58 -0
- package/packages/core/src/testing.ts +233 -0
- package/packages/core/src/types.ts +98 -0
- package/packages/core/src/watcher.ts +37 -0
- package/packages/core/src/websocket.ts +408 -0
- package/packages/core/src/wsdl.ts +546 -0
- package/packages/core/templates/errors/302.twig +14 -0
- package/packages/core/templates/errors/401.twig +9 -0
- package/packages/core/templates/errors/403.twig +29 -0
- package/packages/core/templates/errors/404.twig +29 -0
- package/packages/core/templates/errors/500.twig +38 -0
- package/packages/core/templates/errors/502.twig +9 -0
- package/packages/core/templates/errors/503.twig +12 -0
- package/packages/core/templates/errors/base.twig +37 -0
- package/packages/frond/src/engine.ts +1475 -0
- package/packages/frond/src/index.ts +2 -0
- package/packages/orm/src/adapters/firebird.ts +455 -0
- package/packages/orm/src/adapters/mssql.ts +440 -0
- package/packages/orm/src/adapters/mysql.ts +355 -0
- package/packages/orm/src/adapters/postgres.ts +362 -0
- package/packages/orm/src/adapters/sqlite.ts +270 -0
- package/packages/orm/src/autoCrud.ts +231 -0
- package/packages/orm/src/baseModel.ts +536 -0
- package/packages/orm/src/database.ts +321 -0
- package/packages/orm/src/fakeData.ts +118 -0
- package/packages/orm/src/index.ts +49 -0
- package/packages/orm/src/migration.ts +392 -0
- package/packages/orm/src/model.ts +56 -0
- package/packages/orm/src/query.ts +113 -0
- package/packages/orm/src/seeder.ts +120 -0
- package/packages/orm/src/sqlTranslation.ts +272 -0
- package/packages/orm/src/types.ts +110 -0
- package/packages/orm/src/validation.ts +93 -0
- package/packages/swagger/src/generator.ts +189 -0
- package/packages/swagger/src/index.ts +2 -0
- package/packages/swagger/src/ui.ts +48 -0
- package/skills/tina4-developer.skill +0 -0
- package/skills/tina4-js.skill +0 -0
- package/skills/tina4-maintainer.skill +0 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
# CLAUDE.md — AI Developer Guide for tina4-nodejs (v3.0.0)
|
|
2
|
+
|
|
3
|
+
> This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
|
|
4
|
+
|
|
5
|
+
## What This Project Is
|
|
6
|
+
|
|
7
|
+
Tina4 for Node.js/TypeScript v3.0.0 — a convention-over-configuration structural paradigm. **Not a framework.** The developer writes TypeScript; Tina4 is invisible infrastructure.
|
|
8
|
+
|
|
9
|
+
The philosophy: zero ceremony, batteries included, file system as source of truth.
|
|
10
|
+
|
|
11
|
+
## Repository Layout
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
tina4-nodejs/
|
|
15
|
+
packages/
|
|
16
|
+
cli/ # tina4nodejs CLI (npx tina4nodejs init, npx tina4nodejs serve)
|
|
17
|
+
core/ # HTTP server, router, route discovery, middleware, events, AI, testing
|
|
18
|
+
src/
|
|
19
|
+
ai.ts # AI coding tool detection and context scaffolding
|
|
20
|
+
auth.ts # Authentication helpers
|
|
21
|
+
cache.ts # In-memory caching
|
|
22
|
+
constants.ts # HTTP status codes and content type constants
|
|
23
|
+
devAdmin.ts # Dev toolbar + admin dashboard (replaces floating button)
|
|
24
|
+
devMailbox.ts # Dev mailbox for local email testing
|
|
25
|
+
dotenv.ts # .env file loading
|
|
26
|
+
errorOverlay.ts # Rich debug error overlay (Catppuccin Mocha theme)
|
|
27
|
+
events.ts # Observer-pattern event system
|
|
28
|
+
fakeData.ts # Core fake data generator (PRNG-based, zero deps)
|
|
29
|
+
graphql.ts # GraphQL engine
|
|
30
|
+
health.ts # Health check endpoint
|
|
31
|
+
htmlElement.ts # Programmatic HTML element builder
|
|
32
|
+
i18n.ts # Internationalization / localization
|
|
33
|
+
logger.ts # Structured logging
|
|
34
|
+
messenger.ts # Messaging system
|
|
35
|
+
queue.ts # Queue system
|
|
36
|
+
rateLimiter.ts # Rate limiting middleware
|
|
37
|
+
scss.ts # SCSS compilation
|
|
38
|
+
service.ts # Service layer helpers
|
|
39
|
+
session.ts # Session management
|
|
40
|
+
testing.ts # Inline testing framework (attach tests to functions)
|
|
41
|
+
websocket.ts # WebSocket support
|
|
42
|
+
wsdl.ts # WSDL / SOAP support
|
|
43
|
+
orm/ # Database adapters, models, auto-CRUD, query builder, seeding
|
|
44
|
+
src/
|
|
45
|
+
adapters/
|
|
46
|
+
sqlite.ts # SQLite via better-sqlite3 (default)
|
|
47
|
+
postgres.ts # PostgreSQL adapter
|
|
48
|
+
mysql.ts # MySQL adapter
|
|
49
|
+
mssql.ts # MSSQL / SQL Server adapter
|
|
50
|
+
firebird.ts # Firebird adapter
|
|
51
|
+
baseModel.ts # Base model class
|
|
52
|
+
fakeData.ts # ORM-aware fake data (extends core, field-type heuristics)
|
|
53
|
+
seeder.ts # Database seeding (seedTable, seedOrm)
|
|
54
|
+
sqlTranslation.ts # Cross-engine SQL translator + query cache
|
|
55
|
+
swagger/ # OpenAPI spec generator, Swagger UI
|
|
56
|
+
twig/ # Optional Twig template engine
|
|
57
|
+
test/
|
|
58
|
+
run-all.ts # Test runner — executes all 43 test files
|
|
59
|
+
integration.ts # Full integration test
|
|
60
|
+
*.test.ts # 42 individual test files covering all subsystems
|
|
61
|
+
plan/
|
|
62
|
+
FEATURES.md # Feature tracking and roadmap
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This is an **npm workspaces monorepo**. All packages are in `packages/*`.
|
|
66
|
+
|
|
67
|
+
## Tech Stack
|
|
68
|
+
|
|
69
|
+
- **Language:** TypeScript (strict mode, ES2022 target, Node16 module resolution)
|
|
70
|
+
- **Runtime:** Node.js 20+ (ESM only, `"type": "module"` everywhere)
|
|
71
|
+
- **HTTP:** Native `node:http` — no Express, no Fastify
|
|
72
|
+
- **Database:** SQLite via `better-sqlite3` (default), with adapters for Postgres, MySQL, MSSQL/SQL Server, and Firebird
|
|
73
|
+
- **Templates:** Twig via `twig` npm package (optional)
|
|
74
|
+
- **Dev tooling:** `tsx` for runtime TS execution, `esbuild` for builds
|
|
75
|
+
- **Testing:** 43 test files via `tsx test/run-all.ts`
|
|
76
|
+
|
|
77
|
+
## Key Commands
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm install # Install all workspace dependencies
|
|
81
|
+
npm test # Run all 43 test files via test/run-all.ts
|
|
82
|
+
npm run build # Build all packages to dist/
|
|
83
|
+
npm run clean # Remove all dist/ directories
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## How to Run Locally
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx tsx packages/cli/src/bin.ts serve # Start server from monorepo root
|
|
90
|
+
npx tsx packages/cli/src/bin.ts --help # CLI help
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Or create a test project:
|
|
94
|
+
```bash
|
|
95
|
+
npx tsx packages/cli/src/bin.ts init my-test
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Package Details
|
|
99
|
+
|
|
100
|
+
### @tina4/core (`packages/core/`)
|
|
101
|
+
The HTTP foundation. Handles request/response lifecycle, route matching, middleware, events, AI context, error overlays, HTML building, and inline testing.
|
|
102
|
+
|
|
103
|
+
**Key files:**
|
|
104
|
+
- `server.ts` — Server startup, integrates ORM + Swagger + Twig on boot
|
|
105
|
+
- `router.ts` — Pattern matching with `{id}` dynamic params and `{...slug}` catch-all
|
|
106
|
+
- `routeDiscovery.ts` — Scans `src/routes/` recursively, maps files to endpoints (converts `[id]` dirs to `{id}` URL patterns)
|
|
107
|
+
- `request.ts` — Wraps `IncomingMessage`, adds `.params`, `.query`, `.body`
|
|
108
|
+
- `response.ts` — Wraps `ServerResponse`, adds `.json()`, `.html()`, `.status()`, `.send()`, `.redirect()`
|
|
109
|
+
- `middleware.ts` — Chain runner, built-in CORS and request logger
|
|
110
|
+
- `static.ts` — Serves files from `public/` with MIME type detection
|
|
111
|
+
- `watcher.ts` — `fs.watch` for hot-reload in dev mode
|
|
112
|
+
- `types.ts` — All shared type definitions (`Tina4Request`, `Tina4Response`, `RouteHandler`, etc.)
|
|
113
|
+
- `events.ts` — Observer-pattern event system (`Events.on`, `emit`, `once`, `off`, `clear`)
|
|
114
|
+
- `ai.ts` — AI coding tool detection and context scaffolding (`detectAi`, `installAiContext`, `aiStatusReport`)
|
|
115
|
+
- `errorOverlay.ts` — Rich debug error page for dev mode (`renderErrorOverlay`, `renderProductionError`, `isDebugMode`)
|
|
116
|
+
- `htmlElement.ts` — Programmatic HTML builder (`HtmlElement`, `htmlElement`, `addHtmlHelpers`)
|
|
117
|
+
- `testing.ts` — Inline testing framework (`tests`, `assertEqual`, `assertThrows`, `runAllTests`)
|
|
118
|
+
- `fakeData.ts` — Core fake data generator (names, emails, addresses, UUIDs, etc.)
|
|
119
|
+
- `constants.ts` — HTTP status codes (`HTTP_OK`, `HTTP_NOT_FOUND`, etc.) and content types (`APPLICATION_JSON`, `TEXT_HTML`, etc.)
|
|
120
|
+
- `devAdmin.ts` — Dev toolbar (fixed bottom bar injected into HTML pages) and admin dashboard at `/_dev/`
|
|
121
|
+
- `auth.ts` — Authentication helpers
|
|
122
|
+
- `cache.ts` — In-memory caching
|
|
123
|
+
- `session.ts` — Session management with pluggable handlers
|
|
124
|
+
- `websocket.ts` — WebSocket support
|
|
125
|
+
- `queue.ts` — Queue system with pluggable backends
|
|
126
|
+
- `graphql.ts` — GraphQL engine
|
|
127
|
+
- `i18n.ts` — Internationalization / localization
|
|
128
|
+
- `logger.ts` — Structured logging
|
|
129
|
+
- `rateLimiter.ts` — Rate limiting middleware
|
|
130
|
+
- `dotenv.ts` — `.env` file loading
|
|
131
|
+
- `health.ts` — Health check endpoint
|
|
132
|
+
- `scss.ts` — SCSS compilation
|
|
133
|
+
- `messenger.ts` — Messaging system
|
|
134
|
+
- `service.ts` — Service layer helpers
|
|
135
|
+
- `wsdl.ts` — WSDL / SOAP support
|
|
136
|
+
|
|
137
|
+
### @tina4/orm (`packages/orm/`)
|
|
138
|
+
Database layer with auto-CRUD generation, seeding, fake data, and SQL translation.
|
|
139
|
+
|
|
140
|
+
**Key files:**
|
|
141
|
+
- `database.ts` — Adapter manager, `initDatabase()` factory
|
|
142
|
+
- `adapters/sqlite.ts` — `better-sqlite3` implementation of `DatabaseAdapter` interface
|
|
143
|
+
- `adapters/postgres.ts` — PostgreSQL adapter
|
|
144
|
+
- `adapters/mysql.ts` — MySQL adapter
|
|
145
|
+
- `adapters/mssql.ts` — MSSQL / SQL Server adapter (`mssql` or `sqlserver` scheme)
|
|
146
|
+
- `adapters/firebird.ts` — Firebird adapter
|
|
147
|
+
- `model.ts` — Discovers models from `src/models/`, reads `static tableName` and `static fields`
|
|
148
|
+
- `migration.ts` — Schema sync on startup (creates tables, adds columns, warns on destructive changes)
|
|
149
|
+
- `autoCrud.ts` — Generates GET/POST/PUT/DELETE route handlers for each model
|
|
150
|
+
- `query.ts` — Builds SQL from `?filter[field]=value`, `?sort=-name`, `?page=2&limit=10`
|
|
151
|
+
- `validation.ts` — Validates request bodies against model field definitions
|
|
152
|
+
- `types.ts` — `FieldDefinition`, `ModelDefinition`, `DatabaseAdapter`, `QueryOptions`
|
|
153
|
+
- `fakeData.ts` — ORM-aware fake data extending core (adds `forField()` with column-name heuristics)
|
|
154
|
+
- `seeder.ts` — Database seeding (`seedTable` for raw SQL, `seedOrm` for model-based)
|
|
155
|
+
- `sqlTranslation.ts` — Cross-engine SQL translator (`SQLTranslator`) and TTL query cache (`QueryCache`)
|
|
156
|
+
|
|
157
|
+
### @tina4/swagger (`packages/swagger/`)
|
|
158
|
+
Auto-generates OpenAPI 3.0 docs.
|
|
159
|
+
|
|
160
|
+
**Key files:**
|
|
161
|
+
- `generator.ts` — Produces OpenAPI spec from route table + model definitions
|
|
162
|
+
- `ui.ts` — Serves Swagger UI HTML (CDN-based) at `/swagger` and spec at `/swagger/openapi.json`
|
|
163
|
+
|
|
164
|
+
### @tina4/twig (`packages/twig/`)
|
|
165
|
+
Optional server-side template rendering.
|
|
166
|
+
|
|
167
|
+
**Key files:**
|
|
168
|
+
- `engine.ts` — Wraps the `twig` npm package, `renderTemplate(path, data)`
|
|
169
|
+
- `middleware.ts` — Adds `res.render(template, data)` to response objects
|
|
170
|
+
|
|
171
|
+
### tina4 CLI (`packages/cli/`)
|
|
172
|
+
Developer-facing CLI commands.
|
|
173
|
+
|
|
174
|
+
**Key files:**
|
|
175
|
+
- `bin.ts` — Entry point, command dispatch (`init`, `serve`, `--help`)
|
|
176
|
+
- `commands/init.ts` — Scaffolds a new project directory with sample files
|
|
177
|
+
- `commands/serve.ts` — Starts dev server with hot-reload via `@tina4/core`
|
|
178
|
+
|
|
179
|
+
## Module: Events (`packages/core/src/events.ts`)
|
|
180
|
+
|
|
181
|
+
Observer-pattern event system for decoupled communication. All methods are static on the `Events` class. Listeners run synchronously in priority order (higher priority first). One-time listeners auto-remove after firing.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { Events } from "@tina4/core";
|
|
185
|
+
|
|
186
|
+
// Register a listener (optional priority — higher runs first)
|
|
187
|
+
Events.on("user.created", (user) => {
|
|
188
|
+
console.log(`Welcome ${(user as any).name}!`);
|
|
189
|
+
}, 10);
|
|
190
|
+
|
|
191
|
+
// One-time listener (auto-removes after first fire)
|
|
192
|
+
Events.once("app.ready", () => console.log("App started!"));
|
|
193
|
+
|
|
194
|
+
// Emit an event — returns array of listener results
|
|
195
|
+
const results = Events.emit("user.created", { name: "Alice" });
|
|
196
|
+
|
|
197
|
+
// Remove a specific listener or all listeners for an event
|
|
198
|
+
Events.off("user.created", specificHandler);
|
|
199
|
+
Events.off("user.created"); // removes all
|
|
200
|
+
|
|
201
|
+
// Introspection
|
|
202
|
+
Events.listeners("user.created"); // callback[]
|
|
203
|
+
Events.events(); // all registered event names
|
|
204
|
+
|
|
205
|
+
// Clear everything
|
|
206
|
+
Events.clear();
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Module: AI (`packages/core/src/ai.ts`)
|
|
210
|
+
|
|
211
|
+
Detects AI coding tools (Claude Code, Cursor, Copilot, Windsurf, Aider, Cline, Codex) by checking for their config files/directories. Can scaffold a universal Tina4 context document into each tool's expected location.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { detectAi, installAiContext, aiStatusReport } from "@tina4/core";
|
|
215
|
+
|
|
216
|
+
// Detect which AI tools are present in a project directory
|
|
217
|
+
const tools = detectAi(".");
|
|
218
|
+
// → [{ name: "claude-code", description: "Claude Code (Anthropic CLI)",
|
|
219
|
+
// configFile: "CLAUDE.md", status: "detected" }, ...]
|
|
220
|
+
|
|
221
|
+
// Install context files for all detected tools (creates CLAUDE.md, .cursorules, etc.)
|
|
222
|
+
const created = installAiContext(".", { force: false });
|
|
223
|
+
// → ["CLAUDE.md", ".cursorules"]
|
|
224
|
+
|
|
225
|
+
// Install for ALL known tools, not just detected ones
|
|
226
|
+
import { installAllAiContext } from "@tina4/core";
|
|
227
|
+
installAllAiContext(".", true); // force overwrite
|
|
228
|
+
|
|
229
|
+
// Print a human-readable status report
|
|
230
|
+
console.log(aiStatusReport("."));
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Module: Error Overlay (`packages/core/src/errorOverlay.ts`)
|
|
234
|
+
|
|
235
|
+
Rich HTML error page for development mode. Uses Catppuccin Mocha colour palette, shows syntax-highlighted source context around the error line, stack trace with source preview, request details, and environment info. Controlled by `TINA4_DEBUG` env var.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { renderErrorOverlay, renderProductionError, isDebugMode } from "@tina4/core";
|
|
239
|
+
|
|
240
|
+
// In a route error handler:
|
|
241
|
+
try {
|
|
242
|
+
await handler(req, res);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
const html = isDebugMode()
|
|
245
|
+
? renderErrorOverlay(err as Error, req) // full debug overlay
|
|
246
|
+
: renderProductionError(500, "Internal Server Error"); // safe production page
|
|
247
|
+
res.html(html, 500);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// isDebugMode() returns true when TINA4_DEBUG is "true"
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Module: HtmlElement (`packages/core/src/htmlElement.ts`)
|
|
254
|
+
|
|
255
|
+
Programmatic HTML builder that avoids string concatenation. Three usage patterns: direct construction, builder-pattern functions, and helper injection.
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { HtmlElement, htmlElement, addHtmlHelpers } from "@tina4/core";
|
|
259
|
+
|
|
260
|
+
// Direct construction
|
|
261
|
+
const el = new HtmlElement("div", { class: "card" }, ["Hello"]);
|
|
262
|
+
el.toString(); // '<div class="card">Hello</div>'
|
|
263
|
+
|
|
264
|
+
// Builder pattern — returns a callable that accepts attrs/children
|
|
265
|
+
const div = htmlElement("div");
|
|
266
|
+
const card = div({ class: "card" }, "Hello");
|
|
267
|
+
card.toString(); // '<div class="card">Hello</div>'
|
|
268
|
+
|
|
269
|
+
// Nesting
|
|
270
|
+
const page = htmlElement("div")(
|
|
271
|
+
htmlElement("h1")("Title"),
|
|
272
|
+
htmlElement("p")("Body text"),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Helper injection — adds _div, _p, _a, _span, etc. to an object
|
|
276
|
+
const h: Record<string, any> = {};
|
|
277
|
+
addHtmlHelpers(h);
|
|
278
|
+
const html = h._div({ class: "card" },
|
|
279
|
+
h._h1("Title"),
|
|
280
|
+
h._p({ class: "body" }, "Content"),
|
|
281
|
+
h._img({ src: "/logo.png", alt: "Logo" }), // void tags self-close
|
|
282
|
+
);
|
|
283
|
+
html.toString();
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Void tags (`br`, `hr`, `img`, `input`, `meta`, etc.) render without closing tags. Boolean attributes render as bare names (`disabled` not `disabled="true"`).
|
|
287
|
+
|
|
288
|
+
## Module: Inline Testing (`packages/core/src/testing.ts`)
|
|
289
|
+
|
|
290
|
+
Attach test assertions directly to functions. Tests are registered globally and run with `runAllTests()`. No external test runner needed.
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { tests, assertEqual, assertThrows, assertTrue, assertFalse, runAllTests, resetTests } from "@tina4/core";
|
|
294
|
+
|
|
295
|
+
// Decorate a function with inline tests
|
|
296
|
+
const add = tests(
|
|
297
|
+
assertEqual([5, 3], 8), // add(5, 3) === 8
|
|
298
|
+
assertEqual([0, 0], 0), // add(0, 0) === 0
|
|
299
|
+
assertThrows(Error, [null]), // add(null) throws Error
|
|
300
|
+
)(function add(a: number, b: number | null = null): number {
|
|
301
|
+
if (b === null) throw new Error("b required");
|
|
302
|
+
return a + b;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// The original function works normally
|
|
306
|
+
add(2, 3); // 5
|
|
307
|
+
|
|
308
|
+
// Run all registered tests
|
|
309
|
+
const results = runAllTests({ quiet: false, failfast: false });
|
|
310
|
+
// → { passed: 3, failed: 0, errors: 0, details: [...] }
|
|
311
|
+
|
|
312
|
+
// Additional assertion types
|
|
313
|
+
assertTrue([someArgs]); // result is truthy
|
|
314
|
+
assertFalse([someArgs]); // result is falsy
|
|
315
|
+
|
|
316
|
+
// Reset registry between test runs
|
|
317
|
+
resetTests();
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Module: Seeder / FakeData (`packages/orm/src/seeder.ts`, `packages/orm/src/fakeData.ts`)
|
|
321
|
+
|
|
322
|
+
Database seeding with fake data generation. The ORM `FakeData` extends core `FakeData` (which provides names, emails, addresses, etc.) and adds `forField()` for auto-generating values based on ORM field definitions with column-name heuristics.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { FakeData, seedTable, seedOrm } from "@tina4/orm";
|
|
326
|
+
|
|
327
|
+
// FakeData — deterministic with optional seed
|
|
328
|
+
const fake = new FakeData(42);
|
|
329
|
+
fake.name(); // fullName alias
|
|
330
|
+
fake.email(); // realistic email
|
|
331
|
+
fake.phone(); // phone number
|
|
332
|
+
fake.integer(1, 100);
|
|
333
|
+
fake.numeric(0, 1000, 2); // float alias
|
|
334
|
+
fake.datetime(2020, 2025); // Date object
|
|
335
|
+
fake.boolean();
|
|
336
|
+
fake.uuid();
|
|
337
|
+
fake.address();
|
|
338
|
+
fake.company();
|
|
339
|
+
fake.sentence(5);
|
|
340
|
+
fake.paragraph(3);
|
|
341
|
+
|
|
342
|
+
// forField() — auto-generates based on FieldDefinition + column name heuristics
|
|
343
|
+
fake.forField({ type: "string", maxLength: 50 }, "email"); // generates email
|
|
344
|
+
fake.forField({ type: "integer", min: 0, max: 100 }); // random integer
|
|
345
|
+
fake.forField({ type: "boolean" }); // true/false
|
|
346
|
+
|
|
347
|
+
// seedTable — raw SQL inserts with generator functions
|
|
348
|
+
await seedTable(db, "users", 50, {
|
|
349
|
+
name: () => fake.name(),
|
|
350
|
+
email: () => fake.email(),
|
|
351
|
+
role: "user", // static values also accepted
|
|
352
|
+
}, { active: true }); // overrides applied to every row
|
|
353
|
+
|
|
354
|
+
// seedOrm — auto-seed from model field definitions
|
|
355
|
+
import User from "./src/models/User.js";
|
|
356
|
+
await seedOrm(User, 100, { role: "user" }, 42); // optional seed for determinism
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Column-name heuristics in `forField()`: columns named `email`, `phone`, `name`, `address`, `city`, `country`, `company`, `url`, `uuid`, `ip`, `currency`, etc. get contextually appropriate fake data.
|
|
360
|
+
|
|
361
|
+
## Module: SQL Translation (`packages/orm/src/sqlTranslation.ts`)
|
|
362
|
+
|
|
363
|
+
Cross-engine SQL dialect translator and in-memory query cache. All translator methods are static on `SQLTranslator`. The `QueryCache` provides TTL-based caching with LRU eviction.
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { SQLTranslator, QueryCache } from "@tina4/orm";
|
|
367
|
+
|
|
368
|
+
// Firebird: LIMIT/OFFSET → ROWS X TO Y
|
|
369
|
+
SQLTranslator.limitToRows("SELECT * FROM users LIMIT 10 OFFSET 5");
|
|
370
|
+
// → "SELECT * FROM users ROWS 6 TO 15"
|
|
371
|
+
|
|
372
|
+
// MSSQL: LIMIT → TOP N
|
|
373
|
+
SQLTranslator.limitToTop("SELECT * FROM users LIMIT 10");
|
|
374
|
+
// → "SELECT TOP 10 * FROM users"
|
|
375
|
+
|
|
376
|
+
// MySQL/MSSQL: || concatenation → CONCAT()
|
|
377
|
+
SQLTranslator.concatPipesToFunc("first_name || ' ' || last_name");
|
|
378
|
+
// → "CONCAT(first_name, ' ', last_name)"
|
|
379
|
+
|
|
380
|
+
// Boolean to integer (Firebird)
|
|
381
|
+
SQLTranslator.booleanToInt("WHERE active = TRUE");
|
|
382
|
+
// → "WHERE active = 1"
|
|
383
|
+
|
|
384
|
+
// ILIKE → LOWER() LIKE LOWER()
|
|
385
|
+
SQLTranslator.ilikeToLike("WHERE name ILIKE '%alice%'");
|
|
386
|
+
// → "WHERE LOWER(name) LIKE LOWER('%alice%')"
|
|
387
|
+
|
|
388
|
+
// Auto-increment DDL translation
|
|
389
|
+
SQLTranslator.autoIncrementSyntax(ddl, "postgresql"); // AUTOINCREMENT → SERIAL PRIMARY KEY
|
|
390
|
+
SQLTranslator.autoIncrementSyntax(ddl, "mysql"); // → AUTO_INCREMENT
|
|
391
|
+
SQLTranslator.autoIncrementSyntax(ddl, "mssql"); // → IDENTITY(1,1)
|
|
392
|
+
|
|
393
|
+
// Placeholder style conversion
|
|
394
|
+
SQLTranslator.placeholderStyle("SELECT * FROM t WHERE id = ?", ":"); // → :1
|
|
395
|
+
SQLTranslator.placeholderStyle("SELECT * FROM t WHERE id = ?", "%s"); // → %s
|
|
396
|
+
|
|
397
|
+
// RETURNING clause parsing
|
|
398
|
+
SQLTranslator.parseReturning("INSERT INTO t (x) VALUES (1) RETURNING id, name");
|
|
399
|
+
// → { sql: "INSERT INTO t (x) VALUES (1)", columns: ["id", "name"] }
|
|
400
|
+
|
|
401
|
+
// QueryCache — TTL-based in-memory cache
|
|
402
|
+
const cache = new QueryCache({ defaultTtl: 60, maxSize: 1000 });
|
|
403
|
+
|
|
404
|
+
const key = QueryCache.queryKey("SELECT * FROM users WHERE id = ?", [42]);
|
|
405
|
+
cache.set(key, [{ id: 42, name: "Alice" }], 30); // TTL 30 seconds
|
|
406
|
+
cache.get(key); // → [{ id: 42, name: "Alice" }] or undefined if expired
|
|
407
|
+
cache.has(key); // true/false
|
|
408
|
+
cache.delete(key);
|
|
409
|
+
cache.sweep(); // remove all expired entries
|
|
410
|
+
cache.clear(); // remove everything
|
|
411
|
+
|
|
412
|
+
// Get-or-set pattern
|
|
413
|
+
const rows = cache.remember(key, 60, () => db.execute(sql, params));
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Conventions You Must Follow
|
|
417
|
+
|
|
418
|
+
### Route Files
|
|
419
|
+
- Located in `src/routes/`
|
|
420
|
+
- Filename = HTTP method: `get.ts`, `post.ts`, `put.ts`, `delete.ts`, `patch.ts`
|
|
421
|
+
- Directory path = URL path: `src/routes/api/users/[id]/get.ts` → `GET /api/users/{id}`
|
|
422
|
+
- Dynamic params use bracket notation in filenames: `[id]`, `[...slug]` (converted to `{id}`, `{...slug}` in URL patterns)
|
|
423
|
+
- **Must export** a default async function:
|
|
424
|
+
```typescript
|
|
425
|
+
export default async function (req: Tina4Request, res: Tina4Response) {}
|
|
426
|
+
```
|
|
427
|
+
- **Optionally export** a `meta` object for Swagger:
|
|
428
|
+
```typescript
|
|
429
|
+
export const meta = { summary: "...", tags: ["..."] };
|
|
430
|
+
```
|
|
431
|
+
- **Optionally export** a `template` string to render a Twig template:
|
|
432
|
+
```typescript
|
|
433
|
+
export const template = "page.twig"; // renders src/templates/page.twig
|
|
434
|
+
```
|
|
435
|
+
The route handler provides data; the template renders the HTML. Use `res.template("name.twig", data)` for programmatic template rendering.
|
|
436
|
+
|
|
437
|
+
### Model Files
|
|
438
|
+
- Located in `src/models/`
|
|
439
|
+
- Export a default class with `static tableName` and `static fields`:
|
|
440
|
+
```typescript
|
|
441
|
+
export default class User {
|
|
442
|
+
static tableName = "users";
|
|
443
|
+
static fields = {
|
|
444
|
+
id: { type: "integer" as const, primaryKey: true, autoIncrement: true },
|
|
445
|
+
name: { type: "string" as const, required: true },
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
- Field types: `"string"`, `"text"`, `"integer"`, `"number"`, `"boolean"`, `"datetime"`
|
|
450
|
+
- Field options: `primaryKey`, `autoIncrement`, `required`, `default`, `minLength`, `maxLength`, `min`, `max`, `pattern`
|
|
451
|
+
- Table name should be lowercase plural (e.g., `"users"`, `"products"`)
|
|
452
|
+
|
|
453
|
+
### File-based routes override auto-CRUD
|
|
454
|
+
If both a file route and an auto-CRUD route match, the file route wins.
|
|
455
|
+
|
|
456
|
+
### All packages use barrel exports
|
|
457
|
+
Every package has an `index.ts` that re-exports the public API. Import from the package, not from internal paths.
|
|
458
|
+
|
|
459
|
+
### ESM everywhere
|
|
460
|
+
All code is ESM. Use `.js` extensions in import paths (TypeScript convention for Node16 module resolution):
|
|
461
|
+
```typescript
|
|
462
|
+
import { Router } from "./router.js"; // .js even though the file is .ts
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Architecture Decisions
|
|
466
|
+
|
|
467
|
+
1. **Native `node:http`** — No framework dependency. Zero overhead.
|
|
468
|
+
2. **`tsx` for dev** — No build step needed during development. TypeScript runs directly.
|
|
469
|
+
3. **Convention-based models** — `static fields = {}` over decorators. No special TypeScript config needed.
|
|
470
|
+
4. **CDN for Swagger UI** — Keeps install under 8MB. Single HTML file loads from unpkg.com.
|
|
471
|
+
5. **Process restart for hot-reload** — Simpler and more reliable than HMR with ESM.
|
|
472
|
+
6. **SQLite default** — `better-sqlite3` is synchronous and fast. Full adapters for Postgres, MySQL, MSSQL/SQL Server, and Firebird.
|
|
473
|
+
7. **CLI named `tina4nodejs`** (primary) with `tina4` as alias — So `npx tina4nodejs init` or `npx tina4 init` both work.
|
|
474
|
+
8. **Event system** — Static `Events` class, synchronous dispatch, priority ordering, zero deps.
|
|
475
|
+
9. **Inline testing** — Tests as decorators on functions, no external test runner for unit-level checks.
|
|
476
|
+
10. **SQL translation** — Dialect differences handled at runtime via `SQLTranslator` static methods, not at query-build time.
|
|
477
|
+
11. **Error overlay** — Dev-only rich HTML error page, controlled by `TINA4_DEBUG` env var.
|
|
478
|
+
12. **AI context scaffolding** — Auto-detect and install context files for all major AI coding tools.
|
|
479
|
+
13. **Dev toolbar** — Fixed bottom bar injected into HTML pages in dev mode, showing route info, request ID, version. Admin dashboard at `/_dev/`.
|
|
480
|
+
14. **Default port 7148** — Config priority: explicit config > `PORT` env var > 7148. Default host: `0.0.0.0`.
|
|
481
|
+
|
|
482
|
+
## Database Configuration
|
|
483
|
+
|
|
484
|
+
### Connection string format
|
|
485
|
+
Set `DATABASE_URL` in your `.env` file using `driver://host:port/database` format:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# SQLite (default if nothing configured)
|
|
489
|
+
DATABASE_URL=sqlite:///path/to/db.sqlite
|
|
490
|
+
DATABASE_URL=sqlite://./data/tina4.db
|
|
491
|
+
|
|
492
|
+
# PostgreSQL
|
|
493
|
+
DATABASE_URL=postgres://localhost:5432/mydb
|
|
494
|
+
DATABASE_URL=postgresql://localhost:5432/mydb
|
|
495
|
+
|
|
496
|
+
# MySQL
|
|
497
|
+
DATABASE_URL=mysql://localhost:3306/mydb
|
|
498
|
+
|
|
499
|
+
# MSSQL / SQL Server (both schemes work)
|
|
500
|
+
DATABASE_URL=mssql://localhost:1433/mydb
|
|
501
|
+
DATABASE_URL=sqlserver://localhost:1433/mydb
|
|
502
|
+
|
|
503
|
+
# Firebird
|
|
504
|
+
DATABASE_URL=firebird://localhost:3050/mydb
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Credentials
|
|
508
|
+
Credentials can be embedded in the URL or provided separately:
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
# In the URL
|
|
512
|
+
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
|
|
513
|
+
|
|
514
|
+
# Or as separate env vars (merged when URL has no credentials)
|
|
515
|
+
DATABASE_URL=postgres://localhost:5432/mydb
|
|
516
|
+
DATABASE_USERNAME=myuser
|
|
517
|
+
DATABASE_PASSWORD=mypass
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Credential priority: `config.user` > `config.username` > `DATABASE_USERNAME` env var.
|
|
521
|
+
|
|
522
|
+
### Programmatic configuration
|
|
523
|
+
```typescript
|
|
524
|
+
import { initDatabase } from "@tina4/orm";
|
|
525
|
+
|
|
526
|
+
await initDatabase({ url: "postgres://localhost:5432/mydb" });
|
|
527
|
+
// or
|
|
528
|
+
await initDatabase({ type: "postgres", host: "localhost", port: 5432, database: "mydb", username: "user", password: "pass" });
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Available adapters
|
|
532
|
+
| Adapter | Scheme(s) | Package |
|
|
533
|
+
|---------|-----------|---------|
|
|
534
|
+
| SQLite | `sqlite://` | `better-sqlite3` |
|
|
535
|
+
| PostgreSQL | `postgres://`, `postgresql://` | `pg` |
|
|
536
|
+
| MySQL | `mysql://` | `mysql2` |
|
|
537
|
+
| MSSQL | `mssql://`, `sqlserver://` | `tedious` |
|
|
538
|
+
| Firebird | `firebird://` | `node-firebird` |
|
|
539
|
+
|
|
540
|
+
## Testing
|
|
541
|
+
|
|
542
|
+
Run tests with:
|
|
543
|
+
```bash
|
|
544
|
+
npm test
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
This executes `test/run-all.ts` which runs all 43 test files:
|
|
548
|
+
- `test/integration.ts` — Full integration test (creates a temp project, starts a real server, runs assertions)
|
|
549
|
+
- `test/*.test.ts` — 42 individual test files covering all subsystems (ORM, routing, middleware, database drivers, sessions, queues, WebSocket, GraphQL, i18n, etc.)
|
|
550
|
+
|
|
551
|
+
**Always run tests after making changes.** All tests must pass.
|
|
552
|
+
|
|
553
|
+
When adding new features, add a corresponding `test/<feature>.test.ts` file.
|
|
554
|
+
|
|
555
|
+
## Common Tasks
|
|
556
|
+
|
|
557
|
+
### Adding a new feature to @tina4/core
|
|
558
|
+
1. Create the file in `packages/core/src/`
|
|
559
|
+
2. Export it from `packages/core/src/index.ts`
|
|
560
|
+
3. If it needs integration with server startup, add it to `server.ts`
|
|
561
|
+
4. Add test assertions to `test/integration.ts`
|
|
562
|
+
|
|
563
|
+
### Adding a new database adapter
|
|
564
|
+
1. Create `packages/orm/src/adapters/<name>.ts` implementing `DatabaseAdapter`
|
|
565
|
+
2. Add the case to `initDatabase()` in `packages/orm/src/database.ts`
|
|
566
|
+
3. Add the dependency to `packages/orm/package.json`
|
|
567
|
+
|
|
568
|
+
### Adding a new CLI command
|
|
569
|
+
1. Create `packages/cli/src/commands/<name>.ts`
|
|
570
|
+
2. Add the case to the switch in `packages/cli/src/bin.ts`
|
|
571
|
+
|
|
572
|
+
### Adding a new model field type
|
|
573
|
+
1. Update `FieldType` in `packages/orm/src/types.ts`
|
|
574
|
+
2. Update `fieldTypeToSQLite()` in `packages/orm/src/adapters/sqlite.ts`
|
|
575
|
+
3. Update `fieldToSchemaProperty()` in `packages/swagger/src/generator.ts`
|
|
576
|
+
4. Update validation in `packages/orm/src/validation.ts`
|
|
577
|
+
|
|
578
|
+
## Roadmap (Not Yet Implemented)
|
|
579
|
+
|
|
580
|
+
- Bun runtime compatibility
|
|
581
|
+
|
|
582
|
+
## Don'ts
|
|
583
|
+
|
|
584
|
+
- **Don't add Express, Fastify, or any HTTP framework** — we use native `node:http`
|
|
585
|
+
- **Don't use decorators** — convention-based models with static properties
|
|
586
|
+
- **Don't add CommonJS** — everything is ESM (`"type": "module"`)
|
|
587
|
+
- **Don't bundle `swagger-ui-dist`** — we load Swagger UI from CDN to stay under 8MB
|
|
588
|
+
- **Don't break the 43 test files** — run `npm test` before committing
|
|
589
|
+
- **Don't add unnecessary dependencies** — minimal footprint is a core principle
|
|
590
|
+
- **Don't use `url.parse()`** — use the WHATWG `URL` constructor instead (deprecated in Node 20+)
|
|
591
|
+
|
|
592
|
+
## Tina4 Maintainer Skill
|
|
593
|
+
Always read and follow the instructions in .claude/skills/tina4-maintainer/SKILL.md when working on this codebase. Read its referenced files in .claude/skills/tina4-maintainer/references/ as needed for specific subsystems.
|
|
594
|
+
|
|
595
|
+
## Tina4 Developer Skill
|
|
596
|
+
Always read and follow the instructions in .claude/skills/tina4-developer/SKILL.md when building applications with this framework. Read its referenced files in .claude/skills/tina4-developer/references/ as needed.
|
|
597
|
+
|
|
598
|
+
## Tina4-js Frontend Skill
|
|
599
|
+
Always read and follow the instructions in .claude/skills/tina4-js/SKILL.md when working with tina4-js frontend code. Read its referenced files in .claude/skills/tina4-js/references/ as needed.
|