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.
Files changed (119) hide show
  1. package/BENCHMARK_REPORT.md +96 -0
  2. package/CARBONAH.md +140 -0
  3. package/CLAUDE.md +599 -0
  4. package/COMPARISON.md +194 -0
  5. package/README.md +595 -0
  6. package/package.json +59 -0
  7. package/packages/cli/src/bin.ts +110 -0
  8. package/packages/cli/src/commands/init.ts +194 -0
  9. package/packages/cli/src/commands/migrate.ts +96 -0
  10. package/packages/cli/src/commands/migrateCreate.ts +59 -0
  11. package/packages/cli/src/commands/routes.ts +61 -0
  12. package/packages/cli/src/commands/serve.ts +58 -0
  13. package/packages/cli/src/commands/test.ts +83 -0
  14. package/packages/core/gallery/auth/meta.json +1 -0
  15. package/packages/core/gallery/auth/src/routes/api/gallery/auth/login/post.ts +22 -0
  16. package/packages/core/gallery/auth/src/routes/api/gallery/auth/verify/get.ts +16 -0
  17. package/packages/core/gallery/auth/src/routes/gallery/auth/get.ts +97 -0
  18. package/packages/core/gallery/database/meta.json +1 -0
  19. package/packages/core/gallery/database/src/routes/api/gallery/db/notes/get.ts +13 -0
  20. package/packages/core/gallery/database/src/routes/api/gallery/db/notes/post.ts +17 -0
  21. package/packages/core/gallery/database/src/routes/api/gallery/db/tables/get.ts +23 -0
  22. package/packages/core/gallery/error-overlay/meta.json +1 -0
  23. package/packages/core/gallery/error-overlay/src/routes/api/gallery/crash/get.ts +17 -0
  24. package/packages/core/gallery/orm/meta.json +1 -0
  25. package/packages/core/gallery/orm/src/routes/api/gallery/products/get.ts +12 -0
  26. package/packages/core/gallery/orm/src/routes/api/gallery/products/post.ts +7 -0
  27. package/packages/core/gallery/queue/meta.json +1 -0
  28. package/packages/core/gallery/queue/src/routes/api/gallery/queue/produce/post.ts +16 -0
  29. package/packages/core/gallery/queue/src/routes/api/gallery/queue/status/get.ts +10 -0
  30. package/packages/core/gallery/rest-api/meta.json +1 -0
  31. package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/get.ts +6 -0
  32. package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/post.ts +7 -0
  33. package/packages/core/gallery/templates/meta.json +1 -0
  34. package/packages/core/gallery/templates/src/routes/gallery/page/get.ts +15 -0
  35. package/packages/core/gallery/templates/src/templates/gallery_page.twig +257 -0
  36. package/packages/core/public/css/tina4.css +2463 -0
  37. package/packages/core/public/css/tina4.min.css +1 -0
  38. package/packages/core/public/favicon.ico +0 -0
  39. package/packages/core/public/images/logo.svg +5 -0
  40. package/packages/core/public/images/tina4-logo-icon.webp +0 -0
  41. package/packages/core/public/js/frond.min.js +420 -0
  42. package/packages/core/public/js/tina4-dev-admin.min.js +327 -0
  43. package/packages/core/public/js/tina4.min.js +93 -0
  44. package/packages/core/public/swagger/index.html +90 -0
  45. package/packages/core/public/swagger/oauth2-redirect.html +63 -0
  46. package/packages/core/src/ai.ts +359 -0
  47. package/packages/core/src/api.ts +248 -0
  48. package/packages/core/src/auth.ts +287 -0
  49. package/packages/core/src/cache.ts +121 -0
  50. package/packages/core/src/constants.ts +48 -0
  51. package/packages/core/src/container.ts +90 -0
  52. package/packages/core/src/devAdmin.ts +2024 -0
  53. package/packages/core/src/devMailbox.ts +316 -0
  54. package/packages/core/src/dotenv.ts +172 -0
  55. package/packages/core/src/errorOverlay.test.ts +122 -0
  56. package/packages/core/src/errorOverlay.ts +278 -0
  57. package/packages/core/src/events.ts +112 -0
  58. package/packages/core/src/fakeData.ts +309 -0
  59. package/packages/core/src/graphql.ts +812 -0
  60. package/packages/core/src/health.ts +31 -0
  61. package/packages/core/src/htmlElement.ts +172 -0
  62. package/packages/core/src/i18n.ts +136 -0
  63. package/packages/core/src/index.ts +88 -0
  64. package/packages/core/src/logger.ts +226 -0
  65. package/packages/core/src/messenger.ts +822 -0
  66. package/packages/core/src/middleware.ts +138 -0
  67. package/packages/core/src/queue.ts +481 -0
  68. package/packages/core/src/queueBackends/kafkaBackend.ts +348 -0
  69. package/packages/core/src/queueBackends/rabbitmqBackend.ts +479 -0
  70. package/packages/core/src/rateLimiter.ts +107 -0
  71. package/packages/core/src/request.ts +189 -0
  72. package/packages/core/src/response.ts +146 -0
  73. package/packages/core/src/routeDiscovery.ts +87 -0
  74. package/packages/core/src/router.ts +398 -0
  75. package/packages/core/src/scss.ts +366 -0
  76. package/packages/core/src/server.ts +610 -0
  77. package/packages/core/src/service.ts +380 -0
  78. package/packages/core/src/session.ts +480 -0
  79. package/packages/core/src/sessionHandlers/mongoHandler.ts +286 -0
  80. package/packages/core/src/sessionHandlers/valkeyHandler.ts +184 -0
  81. package/packages/core/src/static.ts +58 -0
  82. package/packages/core/src/testing.ts +233 -0
  83. package/packages/core/src/types.ts +98 -0
  84. package/packages/core/src/watcher.ts +37 -0
  85. package/packages/core/src/websocket.ts +408 -0
  86. package/packages/core/src/wsdl.ts +546 -0
  87. package/packages/core/templates/errors/302.twig +14 -0
  88. package/packages/core/templates/errors/401.twig +9 -0
  89. package/packages/core/templates/errors/403.twig +29 -0
  90. package/packages/core/templates/errors/404.twig +29 -0
  91. package/packages/core/templates/errors/500.twig +38 -0
  92. package/packages/core/templates/errors/502.twig +9 -0
  93. package/packages/core/templates/errors/503.twig +12 -0
  94. package/packages/core/templates/errors/base.twig +37 -0
  95. package/packages/frond/src/engine.ts +1475 -0
  96. package/packages/frond/src/index.ts +2 -0
  97. package/packages/orm/src/adapters/firebird.ts +455 -0
  98. package/packages/orm/src/adapters/mssql.ts +440 -0
  99. package/packages/orm/src/adapters/mysql.ts +355 -0
  100. package/packages/orm/src/adapters/postgres.ts +362 -0
  101. package/packages/orm/src/adapters/sqlite.ts +270 -0
  102. package/packages/orm/src/autoCrud.ts +231 -0
  103. package/packages/orm/src/baseModel.ts +536 -0
  104. package/packages/orm/src/database.ts +321 -0
  105. package/packages/orm/src/fakeData.ts +118 -0
  106. package/packages/orm/src/index.ts +49 -0
  107. package/packages/orm/src/migration.ts +392 -0
  108. package/packages/orm/src/model.ts +56 -0
  109. package/packages/orm/src/query.ts +113 -0
  110. package/packages/orm/src/seeder.ts +120 -0
  111. package/packages/orm/src/sqlTranslation.ts +272 -0
  112. package/packages/orm/src/types.ts +110 -0
  113. package/packages/orm/src/validation.ts +93 -0
  114. package/packages/swagger/src/generator.ts +189 -0
  115. package/packages/swagger/src/index.ts +2 -0
  116. package/packages/swagger/src/ui.ts +48 -0
  117. package/skills/tina4-developer.skill +0 -0
  118. package/skills/tina4-js.skill +0 -0
  119. package/skills/tina4-maintainer.skill +0 -0
package/README.md ADDED
@@ -0,0 +1,595 @@
1
+ <p align="center">
2
+ <img src="https://tina4.com/logo.svg" alt="Tina4" width="200">
3
+ </p>
4
+
5
+ <h1 align="center">Tina4 Node.js</h1>
6
+ <h3 align="center">This is not a framework</h3>
7
+
8
+ <p align="center">
9
+ Laravel joy. TypeScript speed. 10x less code. Zero third-party dependencies.
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://tina4.com">Documentation</a> &bull;
14
+ <a href="#getting-started">Getting Started</a> &bull;
15
+ <a href="#features">Features</a> &bull;
16
+ <a href="#cli-reference">CLI Reference</a> &bull;
17
+ <a href="https://tina4.com">tina4.com</a>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <img src="https://img.shields.io/badge/tests-1247%20passing-brightgreen" alt="Tests">
22
+ <img src="https://img.shields.io/badge/carbonah-A%2B%20rated-00cc44" alt="Carbonah A+">
23
+ <img src="https://img.shields.io/badge/zero--dep-core-blue" alt="Zero Dependencies">
24
+ <img src="https://img.shields.io/badge/node-20%2B-blue" alt="Node 20+">
25
+ <img src="https://img.shields.io/badge/license-MIT-lightgrey" alt="MIT License">
26
+ </p>
27
+
28
+ ---
29
+
30
+ ## Quickstart
31
+
32
+ ```bash
33
+ npm install tina4-nodejs
34
+ npx tina4nodejs init my-app
35
+ cd my-app
36
+ npx tina4nodejs serve
37
+ # -> http://localhost:7148
38
+ ```
39
+
40
+ That's it. Zero configuration, zero classes, zero boilerplate.
41
+
42
+ ---
43
+
44
+ ## What's Included
45
+
46
+ Every feature is built from scratch -- no npm install, no node_modules bloat, no third-party runtime dependencies in core.
47
+
48
+ | Category | Features |
49
+ |----------|----------|
50
+ | **HTTP** | Native `node:http` server, file-based + programmatic routing, path params (`{id}`, `[...slug]`), middleware pipeline, CORS, rate limiting, graceful shutdown |
51
+ | **Templates** | Frond engine (Twig-compatible), inheritance, partials, 53+ filters, macros, fragment caching, sandboxing |
52
+ | **ORM** | Active Record, typed fields with validation, soft delete, relationships (`hasOne`/`hasMany`/`belongsTo`), scopes, result caching, auto-CRUD |
53
+ | **Database** | SQLite, PostgreSQL, MySQL, MSSQL/SQL Server, Firebird -- unified adapter interface, `driver://host:port/database` connection strings |
54
+ | **Auth** | Zero-dep JWT (HS256 + RS256), sessions (file backend), PBKDF2 password hashing, form tokens |
55
+ | **API** | Swagger/OpenAPI auto-generation, GraphQL with schema builder and GraphiQL IDE |
56
+ | **Background** | File-backed queue with priority, delayed jobs, retry, batch processing |
57
+ | **Real-time** | Native WebSocket (RFC 6455), per-path routing, connection manager, broadcast |
58
+ | **Frontend** | tina4-css (~24 KB), frond.js helper, SCSS compiler, live reload, CSS hot-reload |
59
+ | **DX** | Dev admin dashboard, error overlay, request inspector, hot-reload, Carbonah green benchmarks |
60
+ | **Data** | Migrations with rollback, 26+ fake data generators, ORM and table seeders |
61
+ | **Other** | Service runner, localization (i18n), in-memory cache (TTL/tags/LRU), HTTP constants, health check, configurable error pages |
62
+
63
+ **580 tests across all modules. All Carbonah benchmarks rated A+.**
64
+
65
+ For full documentation visit **[tina4.com](https://tina4.com)**.
66
+
67
+ ---
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ npm install tina4-nodejs
73
+ ```
74
+
75
+ Or scaffold a new project directly:
76
+
77
+ ```bash
78
+ npx tina4nodejs init my-app
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Getting Started
84
+
85
+ ### 1. Create a project
86
+
87
+ ```bash
88
+ npx tina4nodejs init my-app
89
+ cd my-app
90
+ ```
91
+
92
+ This creates:
93
+
94
+ ```
95
+ my-app/
96
+ ├── package.json # Entry point
97
+ ├── tsconfig.json # TypeScript config
98
+ ├── .env # Configuration
99
+ ├── src/
100
+ │ ├── routes/ # API + page routes (auto-discovered)
101
+ │ ├── models/ # Database models (auto-CRUD)
102
+ │ ├── templates/ # Frond/Twig templates
103
+ │ ├── seeds/ # Database seeders
104
+ │ ├── scss/ # SCSS (auto-compiled to public/css/)
105
+ │ └── public/ # Static assets served at /
106
+ ├── migrations/ # SQL migration files
107
+ ├── data/ # SQLite database (auto-created)
108
+ └── test/ # Tests
109
+ ```
110
+
111
+ ### 2. Create a route
112
+
113
+ **File-based routing** -- the directory path becomes the URL:
114
+
115
+ ```
116
+ src/routes/api/hello/get.ts -> GET /api/hello
117
+ ```
118
+
119
+ ```typescript
120
+ // src/routes/api/hello/get.ts
121
+ import type { Tina4Request, Tina4Response } from "tina4-nodejs";
122
+
123
+ export default async function (request: Tina4Request, response: Tina4Response) {
124
+ response({message: "Hello from Tina4!"}, HTTP_OK);
125
+ }
126
+ ```
127
+
128
+ **Programmatic routing** -- decorator-style in a single file:
129
+
130
+ ```typescript
131
+ // src/routes/hello.ts
132
+ import { get } from "tina4-nodejs";
133
+
134
+ get("/api/hello/{name}", async (request, response) => {
135
+ response({message: `Hello, ${request.params.name}!`}, HTTP_OK);
136
+ });
137
+ ```
138
+
139
+ Visit `http://localhost:7148/api/hello` -- routes are auto-discovered, no imports needed.
140
+
141
+ ### 3. Add a database
142
+
143
+ Edit `.env`:
144
+
145
+ ```bash
146
+ DATABASE_URL=sqlite:///data/app.db
147
+ ```
148
+
149
+ Create and run a migration:
150
+
151
+ ```bash
152
+ npx tina4nodejs migrate:create "create users table"
153
+ ```
154
+
155
+ Edit the generated SQL:
156
+
157
+ ```sql
158
+ CREATE TABLE IF NOT EXISTS users (
159
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
160
+ name TEXT NOT NULL,
161
+ email TEXT NOT NULL,
162
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
163
+ );
164
+ ```
165
+
166
+ ```bash
167
+ npx tina4nodejs migrate
168
+ ```
169
+
170
+ ### 4. Create an ORM model
171
+
172
+ Create `src/models/User.ts`:
173
+
174
+ ```typescript
175
+ export default class User {
176
+ static tableName = "users";
177
+
178
+ static fields = {
179
+ id: { type: "integer" as const, primaryKey: true, autoIncrement: true },
180
+ name: { type: "string" as const, required: true, maxLength: 100 },
181
+ email: { type: "string" as const, required: true, pattern: "^[^@]+@[^@]+\\.[^@]+$" },
182
+ createdAt: { type: "datetime" as const, default: "now" },
183
+ };
184
+ }
185
+ ```
186
+
187
+ ### 5. Build a REST API
188
+
189
+ **File-based** -- create `src/routes/api/users/get.ts`:
190
+
191
+ ```typescript
192
+ import type { Tina4Request, Tina4Response } from "tina4-nodejs";
193
+ import { getAdapter } from "tina4-nodejs";
194
+
195
+ export default async function (request: Tina4Request, response: Tina4Response) {
196
+ const db = getAdapter();
197
+ const users = db.query("SELECT * FROM users LIMIT 100");
198
+ response(users, HTTP_OK);
199
+ }
200
+ ```
201
+
202
+ Create `src/routes/api/users/[id]/get.ts`:
203
+
204
+ ```typescript
205
+ export default async function (request: Tina4Request, response: Tina4Response) {
206
+ const db = getAdapter();
207
+ const user = db.query("SELECT * FROM users WHERE id = ?", [request.params.id]);
208
+ if (user.length) {
209
+ response(user[0], HTTP_OK);
210
+ } else {
211
+ response({error: "Not found"}, HTTP_NOT_FOUND);
212
+ }
213
+ }
214
+ ```
215
+
216
+ Create `src/routes/api/users/post.ts`:
217
+
218
+ ```typescript
219
+ export default async function (request: Tina4Request, response: Tina4Response) {
220
+ const db = getAdapter();
221
+ db.execute("INSERT INTO users (name, email) VALUES (?, ?)",
222
+ [request.body.name, request.body.email]);
223
+ response({success: true}, HTTP_CREATED);
224
+ }
225
+ ```
226
+
227
+ > **Auto-CRUD alternative**: Simply define the model in `src/models/User.ts` and Tina4 auto-generates all CRUD endpoints. File routes override auto-CRUD when both exist.
228
+
229
+ ### 6. Add a template
230
+
231
+ Create `src/templates/base.twig`:
232
+
233
+ ```twig
234
+ <!DOCTYPE html>
235
+ <html>
236
+ <head>
237
+ <title>{% block title %}My App{% endblock %}</title>
238
+ <link rel="stylesheet" href="/css/tina4.min.css">
239
+ {% block stylesheets %}{% endblock %}
240
+ </head>
241
+ <body>
242
+ {% block content %}{% endblock %}
243
+ <script src="/js/frond.js"></script>
244
+ {% block javascripts %}{% endblock %}
245
+ </body>
246
+ </html>
247
+ ```
248
+
249
+ Create `src/templates/pages/home.twig`:
250
+
251
+ ```twig
252
+ {% extends "base.twig" %}
253
+ {% block content %}
254
+ <div class="container mt-4">
255
+ <h1>{{ title }}</h1>
256
+ <ul>
257
+ {% for user in users %}
258
+ <li>{{ user.name }} -- {{ user.email }}</li>
259
+ {% endfor %}
260
+ </ul>
261
+ </div>
262
+ {% endblock %}
263
+ ```
264
+
265
+ Render it from a route:
266
+
267
+ ```typescript
268
+ // src/routes/page/home/get.ts
269
+ export default async function (request: Tina4Request, response: Tina4Response) {
270
+ const db = getAdapter();
271
+ const users = db.query("SELECT * FROM users LIMIT 20");
272
+ await response.render("pages/home.twig", {title: "Users", users});
273
+ }
274
+ ```
275
+
276
+ ### 7. Seed, test, deploy
277
+
278
+ ```bash
279
+ npx tina4nodejs seed # Run seeders from src/seeds/
280
+ npx tina4nodejs test # Run test suite
281
+ npx tina4nodejs build # Build distributable
282
+ ```
283
+
284
+ For the complete step-by-step guide, visit **[tina4.com](https://tina4.com)**.
285
+
286
+ ---
287
+
288
+ ## Features
289
+
290
+ ### Routing
291
+
292
+ Tina4 supports both **file-based** and **programmatic** routing:
293
+
294
+ ```typescript
295
+ // File-based: src/routes/api/items/get.ts
296
+ export default async function (request: Tina4Request, response: Tina4Response) {
297
+ response({items: []}, HTTP_OK);
298
+ }
299
+
300
+ // Programmatic: src/routes/webhooks.ts
301
+ import { get, post, noauth, secured, middleware } from "tina4-nodejs";
302
+
303
+ get("/api/items", async (request, response) => {
304
+ response({items: []}, HTTP_OK);
305
+ });
306
+
307
+ noauth(
308
+ post("/api/webhook", async (request, response) => {
309
+ response({ok: true}, HTTP_OK);
310
+ })
311
+ );
312
+
313
+ secured(
314
+ get("/api/admin/stats", async (request, response) => {
315
+ response({secret: true}, HTTP_OK);
316
+ })
317
+ );
318
+ ```
319
+
320
+ Path parameter types: `{id}` (string), `[id]` (file-based), `[...slug]` (catch-all).
321
+
322
+ ### ORM
323
+
324
+ Active Record with typed fields, validation, soft delete, relationships, and auto-CRUD:
325
+
326
+ ```typescript
327
+ // src/models/User.ts
328
+ export default class User {
329
+ static tableName = "users";
330
+
331
+ static fields = {
332
+ id: { type: "integer" as const, primaryKey: true, autoIncrement: true },
333
+ name: { type: "string" as const, required: true, maxLength: 100 },
334
+ email: { type: "string" as const, required: true, pattern: "^[^@]+@[^@]+$" },
335
+ role: { type: "string" as const, default: "user" },
336
+ age: { type: "integer" as const, min: 0, max: 150 },
337
+ };
338
+ }
339
+
340
+ // Auto-generates: GET/POST /api/users, GET/PUT/DELETE /api/users/:id
341
+ // With filtering, sorting, pagination, and validation built in
342
+ ```
343
+
344
+ ### Database
345
+
346
+ Unified interface across multiple engines:
347
+
348
+ ```typescript
349
+ import { getAdapter, initDatabase } from "tina4-nodejs";
350
+
351
+ const db = initDatabase("sqlite:///data/app.db");
352
+
353
+ const result = db.query("SELECT * FROM users WHERE age > ?", [18]);
354
+ const row = db.query("SELECT * FROM users WHERE id = ?", [1]);
355
+ db.execute("INSERT INTO users (name, email) VALUES (?, ?)", ["Alice", "alice@test.com"]);
356
+ ```
357
+
358
+ ### Middleware
359
+
360
+ ```typescript
361
+ import { middleware } from "tina4-nodejs";
362
+
363
+ const authCheck = async (request: Tina4Request, response: Tina4Response, next: Function) => {
364
+ if (!request.headers.authorization) {
365
+ return response({error: "Unauthorized"}, HTTP_UNAUTHORIZED);
366
+ }
367
+ return next();
368
+ };
369
+
370
+ middleware(authCheck,
371
+ get("/protected", async (request, response) => {
372
+ response({secret: true}, HTTP_OK);
373
+ })
374
+ );
375
+ ```
376
+
377
+ ### JWT Authentication
378
+
379
+ ```typescript
380
+ import { Auth } from "tina4-nodejs";
381
+
382
+ const auth = new Auth({secret: "your-secret"});
383
+ const token = auth.createToken({userId: 42});
384
+ const payload = auth.validateToken(token);
385
+ ```
386
+
387
+ POST/PUT/PATCH/DELETE routes require `Authorization: Bearer <token>` by default. Use `noauth()` to make public, `secured()` to protect GET routes.
388
+
389
+ ### Sessions
390
+
391
+ ```typescript
392
+ request.session.set("userId", 42);
393
+ const userId = request.session.get("userId");
394
+ ```
395
+
396
+ Backend: file (default). Set via `TINA4_SESSION_HANDLER` in `.env`.
397
+
398
+ ### Queues
399
+
400
+ ```typescript
401
+ import { Queue, Producer, Consumer } from "tina4-nodejs";
402
+
403
+ new Producer(new Queue({topic: "emails"})).produce({to: "alice@example.com"});
404
+
405
+ new Consumer(new Queue({topic: "emails"})).onMessage((msg) => {
406
+ sendEmail(msg.data);
407
+ });
408
+ ```
409
+
410
+ ### GraphQL
411
+
412
+ ```typescript
413
+ import { GraphQL } from "tina4-nodejs";
414
+
415
+ const gql = new GraphQL();
416
+ gql.schema.fromModels();
417
+ gql.registerRoute("/graphql"); // GET = GraphiQL IDE, POST = queries
418
+ ```
419
+
420
+ ### WebSocket
421
+
422
+ ```typescript
423
+ import { WebSocketManager } from "tina4-nodejs";
424
+
425
+ const ws = new WebSocketManager();
426
+
427
+ ws.route("/ws/chat", async (connection, message) => {
428
+ await ws.broadcast("/ws/chat", `User said: ${message}`);
429
+ });
430
+ ```
431
+
432
+ ### Swagger / OpenAPI
433
+
434
+ Auto-generated at `/swagger`:
435
+
436
+ ```typescript
437
+ // src/routes/api/users/get.ts
438
+ export const meta = {
439
+ summary: "Get all users",
440
+ tags: ["Users"],
441
+ };
442
+
443
+ export default async function (request: Tina4Request, response: Tina4Response) {
444
+ const db = getAdapter();
445
+ response(db.query("SELECT * FROM users"), HTTP_OK);
446
+ }
447
+ ```
448
+
449
+ ### Service Runner
450
+
451
+ ```typescript
452
+ import { ServiceRunner } from "tina4-nodejs";
453
+
454
+ const runner = new ServiceRunner();
455
+
456
+ runner.register("cleanup", "0 */6 * * *", async () => {
457
+ // Runs every 6 hours
458
+ await cleanupExpiredSessions();
459
+ });
460
+ ```
461
+
462
+ ### Template Engine (Frond)
463
+
464
+ Twig-compatible, 53+ filters, macros, inheritance, fragment caching, sandboxing:
465
+
466
+ ```twig
467
+ {% extends "base.twig" %}
468
+ {% block content %}
469
+ <h1>{{ title | upper }}</h1>
470
+ {% for item in items %}
471
+ <p>{{ item.name }} -- {{ item.price | number_format(2) }}</p>
472
+ {% endfor %}
473
+
474
+ {% cache "sidebar" 300 %}
475
+ {% include "partials/sidebar.twig" %}
476
+ {% endcache %}
477
+ {% endblock %}
478
+ ```
479
+
480
+ ### REST Client
481
+
482
+ ```typescript
483
+ import { Api } from "tina4-nodejs";
484
+
485
+ const api = new Api("https://api.example.com", {authHeader: "Bearer xyz"});
486
+ const result = await api.sendRequest("/users/42");
487
+ ```
488
+
489
+ ### Data Seeder
490
+
491
+ ```typescript
492
+ import { Fake, seedModel } from "tina4-nodejs";
493
+
494
+ const fake = new Fake();
495
+ fake.name(); // "Alice Johnson"
496
+ fake.email(); // "alice.johnson@example.com"
497
+
498
+ seedModel(User, {count: 50});
499
+ ```
500
+
501
+ ### Response Cache
502
+
503
+ ```typescript
504
+ import { cached } from "tina4-nodejs";
505
+
506
+ cached(60,
507
+ get("/api/stats", async (request, response) => {
508
+ response(computeExpensiveStats(), HTTP_OK);
509
+ })
510
+ );
511
+ ```
512
+
513
+ ### SCSS, Localization
514
+
515
+ - **SCSS**: Drop `.scss` in `src/scss/` -- auto-compiled to CSS. Variables, nesting, mixins, `@import`, `@extend`.
516
+ - **i18n**: JSON translation files, parameter substitution.
517
+
518
+ ---
519
+
520
+ ## Dev Mode
521
+
522
+ Set `TINA4_DEBUG_LEVEL=DEBUG` in `.env` to enable:
523
+
524
+ - **Live reload** -- browser auto-refreshes on code changes
525
+ - **CSS hot-reload** -- SCSS changes apply without page refresh
526
+ - **Error overlay** -- rich error display in the browser
527
+ - **Dev admin** with routes, queue, requests, errors, system tabs
528
+
529
+ ---
530
+
531
+ ## CLI Reference
532
+
533
+ ```bash
534
+ npx tina4nodejs init [dir] # Scaffold a new project
535
+ npx tina4nodejs serve [--port 7148] # Start dev server (default: 7148)
536
+ npx tina4nodejs migrate # Run pending migrations
537
+ npx tina4nodejs migrate:create <desc> # Create a migration file
538
+ npx tina4nodejs migrate:rollback # Rollback last batch
539
+ npx tina4nodejs seed # Run seeders from src/seeds/
540
+ npx tina4nodejs routes # List all registered routes
541
+ npx tina4nodejs test # Run test suite
542
+ npx tina4nodejs build # Build distributable package
543
+ npx tina4nodejs ai [--all] # Detect AI tools and install context
544
+ ```
545
+
546
+ ## Environment
547
+
548
+ ```bash
549
+ SECRET=your-jwt-secret
550
+ DATABASE_URL=sqlite:///data/app.db
551
+ DATABASE_USERNAME=admin # Separate credentials for networked databases
552
+ DATABASE_PASSWORD=secret
553
+ TINA4_DEBUG_LEVEL=DEBUG # DEBUG, INFO, WARNING, ERROR, ALL
554
+ TINA4_LANGUAGE=en # en, fr, af, zh, ja, es
555
+ TINA4_SESSION_HANDLER=SessionFileHandler
556
+ SWAGGER_TITLE=My API
557
+ ```
558
+
559
+ ## Carbonah Green Benchmarks
560
+
561
+ All benchmarks rated **A+** (South Africa grid, 1000 iterations each):
562
+
563
+ | Metric | Value |
564
+ |--------|-------|
565
+ | Startup time | 38ms |
566
+ | Memory usage | 104.2MB |
567
+ | SCI score | 0.00552 gCO2eq |
568
+ | Grade | A+ |
569
+
570
+ Run locally: `npx tina4nodejs benchmark`
571
+
572
+ ---
573
+
574
+ ## Documentation
575
+
576
+ Full guides, API reference, and examples at **[tina4.com](https://tina4.com)**.
577
+
578
+ ## License
579
+
580
+ MIT (c) 2007-2025 Tina4 Stack
581
+ https://opensource.org/licenses/MIT
582
+
583
+ ---
584
+
585
+ <p align="center"><b>Tina4</b> -- The framework that keeps out of the way of your coding.</p>
586
+
587
+ ---
588
+
589
+ ## Our Sponsors
590
+
591
+ **Sponsored with 🩵 by Code Infinity**
592
+
593
+ [<img src="https://codeinfinity.co.za/wp-content/uploads/2025/09/c8e-logo-github.png" alt="Code Infinity" width="100">](https://codeinfinity.co.za/about-open-source-policy?utm_source=github&utm_medium=website&utm_campaign=opensource_campaign&utm_id=opensource)
594
+
595
+ *Supporting open source communities <span style="color: #1DC7DE;">•</span> Innovate <span style="color: #1DC7DE;">•</span> Code <span style="color: #1DC7DE;">•</span> Empower*
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "tina4-nodejs",
3
+ "version": "3.0.0-rc.2",
4
+ "type": "module",
5
+ "description": "This is not a framework. Tina4 for Node.js/TypeScript — zero deps, 38 built-in features.",
6
+ "keywords": ["tina4", "framework", "web", "api", "orm", "graphql", "websocket", "typescript"],
7
+ "homepage": "https://tina4.com/nodejs",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/tina4stack/tina4-nodejs.git"
11
+ },
12
+ "license": "MIT",
13
+ "author": "Tina4 Team <info@tina4.com>",
14
+ "workspaces": [
15
+ "packages/*"
16
+ ],
17
+ "main": "packages/core/src/index.ts",
18
+ "exports": {
19
+ ".": "./packages/core/src/index.ts",
20
+ "./orm": "./packages/orm/src/index.ts",
21
+ "./swagger": "./packages/swagger/src/index.ts",
22
+ "./frond": "./packages/frond/src/engine.ts"
23
+ },
24
+ "files": [
25
+ "packages/core/src/**/*",
26
+ "packages/core/public/**/*",
27
+ "packages/core/templates/**/*",
28
+ "packages/core/gallery/**/*",
29
+ "packages/orm/src/**/*",
30
+ "packages/swagger/src/**/*",
31
+ "packages/frond/src/**/*",
32
+ "packages/cli/src/**/*",
33
+ "skills/**/*",
34
+ "CLAUDE.md",
35
+ "COMPARISON.md",
36
+ "BENCHMARK_REPORT.md",
37
+ "CARBONAH.md",
38
+ "README.md"
39
+ ],
40
+ "bin": {
41
+ "tina4nodejs": "packages/cli/src/bin.ts"
42
+ },
43
+ "scripts": {
44
+ "build": "npm run build --workspaces",
45
+ "clean": "rm -rf packages/*/dist",
46
+ "test": "tsx test/run-all.ts"
47
+ },
48
+ "engines": {
49
+ "node": ">=20.0.0"
50
+ },
51
+ "dependencies": {
52
+ "better-sqlite3": "^11.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "typescript": "^5.7.0",
56
+ "tsx": "^4.19.0",
57
+ "esbuild": "^0.24.0"
58
+ }
59
+ }