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/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> •
|
|
14
|
+
<a href="#getting-started">Getting Started</a> •
|
|
15
|
+
<a href="#features">Features</a> •
|
|
16
|
+
<a href="#cli-reference">CLI Reference</a> •
|
|
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
|
+
}
|