@nexa-stack/framework 1.0.0

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 (81) hide show
  1. package/.env.example +46 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -0
  4. package/bin/nexa.mjs +41 -0
  5. package/bin/nexa.ts +334 -0
  6. package/docs/AI.md +69 -0
  7. package/docs/ARCHITECTURE.md +74 -0
  8. package/docs/EXAMPLES.md +114 -0
  9. package/docs/FRAMEWORK.md +226 -0
  10. package/docs/LANGUAGE.md +39 -0
  11. package/docs/README.md +7 -0
  12. package/docs/READY.md +51 -0
  13. package/docs/REFERENCE.md +255 -0
  14. package/docs/START.md +98 -0
  15. package/docs/advanced.md +97 -0
  16. package/docs/authentication.md +54 -0
  17. package/docs/cli.md +15 -0
  18. package/docs/compare.md +51 -0
  19. package/docs/configuration.md +65 -0
  20. package/docs/database.md +396 -0
  21. package/docs/installation.md +57 -0
  22. package/docs/localization.md +47 -0
  23. package/docs/resources.md +75 -0
  24. package/docs/routing.md +59 -0
  25. package/docs/seeding.md +33 -0
  26. package/docs/services.md +146 -0
  27. package/package.json +77 -0
  28. package/packages/auth/src/auth.test.ts +23 -0
  29. package/packages/auth/src/auth.ts +287 -0
  30. package/packages/auth/src/index.ts +17 -0
  31. package/packages/cache/src/index.ts +203 -0
  32. package/packages/client/src/index.ts +49 -0
  33. package/packages/core/src/app.ts +97 -0
  34. package/packages/core/src/config.ts +55 -0
  35. package/packages/core/src/dev.ts +104 -0
  36. package/packages/core/src/fields.test.ts +81 -0
  37. package/packages/core/src/fields.ts +309 -0
  38. package/packages/core/src/index.ts +60 -0
  39. package/packages/core/src/lang.ts +79 -0
  40. package/packages/core/src/loader.ts +42 -0
  41. package/packages/core/src/migrate.ts +91 -0
  42. package/packages/core/src/policy.ts +36 -0
  43. package/packages/core/src/registry.ts +17 -0
  44. package/packages/core/src/reload.ts +92 -0
  45. package/packages/core/src/resource.test.ts +32 -0
  46. package/packages/core/src/resource.ts +87 -0
  47. package/packages/core/src/routes.ts +22 -0
  48. package/packages/core/src/runtime.ts +11 -0
  49. package/packages/database/src/builder.ts +266 -0
  50. package/packages/database/src/database.ts +252 -0
  51. package/packages/database/src/dialect.ts +186 -0
  52. package/packages/database/src/index.ts +6 -0
  53. package/packages/database/src/mysql.ts +114 -0
  54. package/packages/database/src/postgres.ts +117 -0
  55. package/packages/database/src/query.ts +115 -0
  56. package/packages/database/src/sqlite.ts +216 -0
  57. package/packages/database/src/types.ts +104 -0
  58. package/packages/events/src/index.ts +17 -0
  59. package/packages/export/src/index.ts +36 -0
  60. package/packages/log/src/index.ts +38 -0
  61. package/packages/mail/src/index.ts +130 -0
  62. package/packages/notifications/src/index.ts +84 -0
  63. package/packages/plugins/src/index.ts +39 -0
  64. package/packages/queue/src/index.ts +185 -0
  65. package/packages/queue/src/jobs.ts +9 -0
  66. package/packages/schedule/src/index.ts +64 -0
  67. package/packages/server/src/index.ts +1 -0
  68. package/packages/server/src/middleware.ts +143 -0
  69. package/packages/server/src/query.ts +40 -0
  70. package/packages/server/src/router.ts +813 -0
  71. package/packages/sms/src/index.ts +33 -0
  72. package/packages/storage/src/upload.ts +36 -0
  73. package/packages/testing/src/index.ts +67 -0
  74. package/packages/validation/src/index.ts +1 -0
  75. package/packages/validation/src/validate.test.ts +35 -0
  76. package/packages/validation/src/validate.ts +112 -0
  77. package/public/admin.html +369 -0
  78. package/public/compare.html +66 -0
  79. package/public/dev-bar.js +213 -0
  80. package/public/docs.html +315 -0
  81. package/public/index.html +66 -0
@@ -0,0 +1,114 @@
1
+ # Examples
2
+
3
+ ## ERP (full)
4
+
5
+ See current `app.ts` in project root — 7 modules with relations and seed data.
6
+
7
+ ```bash
8
+ bun run dev
9
+ # → http://localhost:3333/admin
10
+ ```
11
+
12
+ ---
13
+
14
+ ## School (minimal)
15
+
16
+ ```ts
17
+ import { start, resource, string, number, email, belongsTo } from "./packages/core/src/index.js";
18
+
19
+ resource("students", {
20
+ name: string().required(),
21
+ email: email().required(),
22
+ age: number().required(),
23
+ }, { label: "Students" }).admin();
24
+
25
+ resource("courses", {
26
+ title: string().required(),
27
+ price: number().required(),
28
+ }, { label: "Courses" }).admin();
29
+
30
+ resource("enrollments", {
31
+ student: belongsTo("students").required(),
32
+ course: belongsTo("courses").required(),
33
+ }, { label: "Enrollments" }).admin().auth();
34
+
35
+ await start("./school.db");
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Shop (minimal)
41
+
42
+ ```ts
43
+ import { start, resource, string, money, getDb } from "./packages/core/src/index.js";
44
+
45
+ resource("products", {
46
+ name: string().required(),
47
+ price: money().required(),
48
+ }, { label: "Products" }).admin();
49
+
50
+ resource("sales", {
51
+ total: money().required(),
52
+ items: string().required(),
53
+ }, { label: "Sales" }).auth();
54
+
55
+ await start("./shop.db");
56
+
57
+ const db = getDb()!;
58
+ if (db.findAll("products").length === 0) {
59
+ db.insert("products", { name: "Coffee", price: 5 });
60
+ db.insert("products", { name: "Tea", price: 3 });
61
+ }
62
+ ```
63
+
64
+ Cashier page: http://localhost:3333/cashier
65
+
66
+ ---
67
+
68
+ ## Warehouse (minimal)
69
+
70
+ ```ts
71
+ import { start, resource, string, number, belongsTo } from "./packages/core/src/index.js";
72
+
73
+ resource("items", {
74
+ name: string().required(),
75
+ sku: string().required(),
76
+ qty: number().required(),
77
+ }, { label: "Items" }).admin();
78
+
79
+ resource("movements", {
80
+ item: belongsTo("items").required(),
81
+ type: string().required(),
82
+ qty: number().required(),
83
+ }, { label: "Movements" }).admin().auth();
84
+
85
+ await start("./warehouse.db");
86
+ ```
87
+
88
+ ---
89
+
90
+ ## CRM (minimal)
91
+
92
+ ```ts
93
+ import { start, resource, string, email, money, belongsTo } from "./packages/core/src/index.js";
94
+
95
+ resource("leads", {
96
+ name: string().required(),
97
+ email: email().required(),
98
+ phone: string().required(),
99
+ }, { label: "Leads" }).admin();
100
+
101
+ resource("deals", {
102
+ lead: belongsTo("leads").required(),
103
+ amount: money().required(),
104
+ stage: string().required(),
105
+ }, { label: "Deals" }).admin().auth();
106
+
107
+ resource("tasks", {
108
+ deal: belongsTo("deals").required(),
109
+ note: string().required(),
110
+ done: boolean(),
111
+ }, { label: "Tasks" }).admin().auth();
112
+
113
+ await start("./crm.db");
114
+ ```
@@ -0,0 +1,226 @@
1
+ # Nexa Framework Reference
2
+
3
+ ## Import
4
+
5
+ ```ts
6
+ import {
7
+ start,
8
+ resource,
9
+ string,
10
+ number,
11
+ boolean,
12
+ text,
13
+ email,
14
+ money,
15
+ belongsTo,
16
+ hasMany,
17
+ file,
18
+ image,
19
+ on,
20
+ dispatch,
21
+ getDb,
22
+ } from "./packages/core/src/index.js";
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Commands (Vocabulary)
28
+
29
+ ### `resource(name, fields, options?)`
30
+
31
+ Defines a database table + REST API.
32
+
33
+ ```ts
34
+ resource("clients", {
35
+ name: string().required(),
36
+ phone: string().required(),
37
+ email: email(),
38
+ }, { label: "Clients" });
39
+ ```
40
+
41
+ | Parameter | Type | Description |
42
+ |-----------|------|-------------|
43
+ | `name` | string | Table name (plural, lowercase). API: `/api/clients` |
44
+ | `fields` | object | Field builders (see below) |
45
+ | `options.label` | string | Display name in admin UI |
46
+
47
+ **Auto-generated:**
48
+ - `GET /api/{name}` — list
49
+ - `GET /api/{name}/:id` — get one
50
+ - `POST /api/{name}` — create
51
+ - `PUT /api/{name}/:id` — update
52
+ - `DELETE /api/{name}/:id` — delete
53
+
54
+ ---
55
+
56
+ ### Field types
57
+
58
+ | Function | Type | SQL | Example |
59
+ |----------|------|-----|---------|
60
+ | `string()` | text | TEXT | name, phone, status |
61
+ | `number()` | number | REAL | age, qty, stock |
62
+ | `money()` | money | REAL | price, total, salary |
63
+ | `email()` | email | TEXT | email (validated) |
64
+ | `text()` | long text | TEXT | notes, description |
65
+ | `boolean()` | boolean | INTEGER | active, paid |
66
+ | `belongsTo("table")` | relation | INTEGER | foreign key → `{name}_id` |
67
+ | `hasMany("table")` | hasMany | — | loads related records (not stored) |
68
+ | `file()` | file | TEXT | stores upload URL |
69
+ | `image()` | image | TEXT | stores image upload URL |
70
+
71
+ ### Field modifiers
72
+
73
+ | Modifier | Description |
74
+ |----------|-------------|
75
+ | `.required()` | Field is mandatory |
76
+ | `.min(n)` | Minimum value (number) or length (string) |
77
+ | `.max(n)` | Maximum value (number) or length (string) |
78
+
79
+ ```ts
80
+ age: number().required().min(1).max(100)
81
+ name: string().required().min(2)
82
+ price: money().required()
83
+ client: belongsTo("clients").required()
84
+ ```
85
+
86
+ ---
87
+
88
+ ### Chain methods
89
+
90
+ | Method | Description |
91
+ |--------|-------------|
92
+ | `.admin()` | Show in admin UI at `/admin` |
93
+ | `.auth(role?)` | Require login. Default role: `"admin"`. Admin role bypasses all checks |
94
+ | `.policy(rules)` | Per-action authorization: `view`, `create`, `update`, `delete` |
95
+
96
+ ```ts
97
+ resource("clients", { ... }).admin(); // public CRUD + admin page
98
+ resource("orders", { ... }).admin().auth(); // login required
99
+ resource("employees", { ... }).admin().auth("admin"); // admin only
100
+ ```
101
+
102
+ ---
103
+
104
+ ### `await start(dbPath, port?)`
105
+
106
+ Starts everything:
107
+ 1. Connects to database (SQLite, PostgreSQL, or MySQL/MariaDB)
108
+ 2. Creates all tables
109
+ 3. Sets up auth (login/register) + job queue
110
+ 4. Seeds admin user
111
+ 5. Starts HTTP server
112
+ 6. Serves admin UI from `public/`
113
+
114
+ ```ts
115
+ await start("./app.db"); // default port 3333
116
+ await start("./app.db", 8080); // custom port
117
+ ```
118
+
119
+ **Default admin:** `admin@nexa.dev` / `123456`
120
+
121
+ ---
122
+
123
+ ## API Endpoints
124
+
125
+ ### Auth
126
+
127
+ | Method | URL | Body | Description |
128
+ |--------|-----|------|-------------|
129
+ | POST | `/api/auth/login` | `{ email, password }` | Returns `{ user, token }` |
130
+ | POST | `/api/auth/register` | `{ email, password }` | Create user |
131
+
132
+ Use token: `Authorization: Bearer {token}`
133
+
134
+ ### Resources
135
+
136
+ All resources follow REST pattern at `/api/{name}`.
137
+
138
+ Response format:
139
+ ```json
140
+ { "data": [...], "meta": { "page": 1, "limit": 20 } }
141
+ ```
142
+
143
+ Error format:
144
+ ```json
145
+ { "error": "message", "errors": [{ "field": "name", "message": "..." }] }
146
+ ```
147
+
148
+ ### Framework
149
+
150
+ | Method | URL | Description |
151
+ |--------|-----|------|-------------|
152
+ | GET | `/api` | List all endpoints |
153
+ | GET | `/api/schema` | Resource schemas (for admin UI) |
154
+ | GET | `/api/dashboard` | Stats + revenue |
155
+
156
+ ### Pages
157
+
158
+ | URL | Description |
159
+ |-----|-------------|
160
+ | `/admin` | Auto-generated admin panel |
161
+ | `/cashier` | Cashier POS (if exists) |
162
+ | `/` | Redirects to admin |
163
+
164
+ ---
165
+
166
+ ## Terminal
167
+
168
+ ```bash
169
+ bun run dev # start app.ts
170
+ bun run our make clients # create resource file template
171
+ bun run our test # run tests
172
+ bun run our migrate # create tables only
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Relations
178
+
179
+ ```ts
180
+ resource("clients", { name: string().required() }, { label: "Clients" }).admin();
181
+
182
+ resource("orders", {
183
+ client: belongsTo("clients").required(),
184
+ product: belongsTo("products").required(),
185
+ qty: number().required(),
186
+ total: money().required(),
187
+ }, { label: "Orders" }).admin().auth();
188
+ ```
189
+
190
+ - Creates column `client_id` referencing `clients.id`
191
+ - API accepts `{ client: 1 }` or `{ client_id: 1 }`
192
+ - GET responses include nested object: `{ client: { id: 1, name: "..." } }`
193
+ - Admin UI shows dropdown for relation fields
194
+
195
+ ---
196
+
197
+ ## Seed data (optional)
198
+
199
+ ```ts
200
+ await start("./app.db");
201
+
202
+ const db = getDb()!;
203
+ if (db.findAll("clients").length === 0) {
204
+ db.insert("clients", { name: "Al Noor Co", phone: "0501234567" });
205
+ }
206
+ ```
207
+
208
+ ---
209
+
210
+ ## What NOT to do
211
+
212
+ - Do NOT write Express/Fastify/NestJS routes
213
+ - Do NOT write SQL manually
214
+ - Do NOT write controllers or models
215
+ - Do NOT install Laravel or other frameworks
216
+ - Do NOT edit files in `packages/` unless extending the framework
217
+
218
+ ---
219
+
220
+ ## What TO do
221
+
222
+ 1. Edit `app.ts`
223
+ 2. Define `resource()` for each module
224
+ 3. Chain `.admin()` and `.auth()` as needed
225
+ 4. Call `await start("./my.db")`
226
+ 5. Open `/admin`
@@ -0,0 +1,39 @@
1
+ # Localization (English + Arabic)
2
+
3
+ Default language is **English**.
4
+
5
+ ```
6
+ lang/
7
+ en.json ← default
8
+ ar.json ← Arabic (optional)
9
+ ```
10
+
11
+ ## Labels
12
+
13
+ Prefer `labelKey` for shared translations, or `.label("…")` for a fixed string:
14
+
15
+ ```ts
16
+ resource("clients", {
17
+ name: string().required().label("Name"),
18
+ }, { label: "Clients", labelKey: "clients" }).admin();
19
+ ```
20
+
21
+ ```json
22
+ // lang/en.json
23
+ { "clients": "Clients", "ui": { "login": "Login" } }
24
+
25
+ // lang/ar.json
26
+ { "clients": "العملاء", "ui": { "login": "دخول" } }
27
+ ```
28
+
29
+ ## Switching language
30
+
31
+ Admin includes **EN / عربي** buttons.
32
+ API: `GET /api/schema?lang=en` or `?lang=ar`
33
+
34
+ ## Config
35
+
36
+ ```env
37
+ APP_LOCALE=en
38
+ # APP_LOCALE=ar
39
+ ```
package/docs/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Nexa Docs
2
+
3
+ 1. **[START.md](./START.md)** — start here
4
+ 2. **[REFERENCE.md](./REFERENCE.md)** — full API reference (developers & AI)
5
+ 3. Everything else is optional when you need it
6
+
7
+ Default language: **English**. Arabic UI: `APP_LOCALE=ar`.
package/docs/READY.md ADDED
@@ -0,0 +1,51 @@
1
+ # Nexa — Ready for review
2
+
3
+ **Version:** 1.0.0
4
+ **Status:** Backend-first · Node.js · English default · publishable (`@nexa-stack/framework`)
5
+ **Tests:** `npm test`
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm add @nexa-stack/framework
11
+ ```
12
+
13
+ ```ts
14
+ import { resource, string, start, db, createClient, route } from "@nexa-stack/framework";
15
+ ```
16
+
17
+ In this repo: `npm run serve`
18
+
19
+ ## Feature map
20
+
21
+ | Area | Status | API |
22
+ |------|--------|-----|
23
+ | Resources | ✅ | `resource()` + `db.from()` |
24
+ | Relations | ✅ | `belongsTo` / `hasMany` / `belongsToMany` |
25
+ | Transactions | ✅ | `db.transaction()` |
26
+ | Migrations | ✅ | auto ADD COLUMN + `migrate:rollback` |
27
+ | REST API | ✅ | CRUD + `route()` |
28
+ | Auth | ✅ | JWT · roles · reset · verify |
29
+ | Soft deletes | ✅ | `.admin()` default |
30
+ | Admin UI | ✅ | `/admin` (English default; Arabic optional) |
31
+ | SQLite / PG / MySQL | ✅ | `.env` |
32
+ | Queue | ✅ | retries · claim |
33
+ | Notifications | ✅ | `notify` + `POST /api/notifications` |
34
+ | Frontend bridge | ✅ thin | `createClient()` |
35
+ | Dev Bar | ✅ | dev only |
36
+ | npm | ✅ | `@nexa-stack/framework@1.0.0` |
37
+
38
+ ## Intentionally later
39
+
40
+ - Full Nexa frontend UI framework
41
+ - whereHas / scopes / morphs
42
+ - Multi-tenancy, GraphQL, WebSockets
43
+ - Community growth
44
+
45
+ ## Review
46
+
47
+ 1. `npm test`
48
+ 2. `npm run serve` → `/admin`
49
+ 3. Docs: [START](./START.md) · [REFERENCE](./REFERENCE.md)
50
+
51
+ Login: `admin@nexa.dev` / `123456`
@@ -0,0 +1,255 @@
1
+ # Nexa — API reference
2
+
3
+ Production backend framework for **Node.js ≥ 20**.
4
+ Package: `@nexa-stack/framework` · CLI: `nexa` · Default locale: **English**
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ npx @nexa-stack/framework new my-app
10
+ cd my-app && npm install && npm run serve
11
+ ```
12
+
13
+ - **CLI:** `nexa` (serve, new, make:resource, migrate, queue:work, …)
14
+ - **Import:** `from "@nexa-stack/framework"`
15
+ - **Requires:** Node.js ≥ 20
16
+
17
+ ---
18
+
19
+ ## Core idea
20
+
21
+ ```ts
22
+ import { resource, string, money, belongsTo } from "@nexa-stack/framework";
23
+
24
+ resource("orders", {
25
+ client: belongsTo("clients").required().label("Client"),
26
+ total: money().required().label("Total"),
27
+ }, { label: "Orders" }).admin();
28
+ ```
29
+
30
+ One `resource` → table + REST API + admin UI.
31
+
32
+ | Feature | Status |
33
+ |---------|--------|
34
+ | REST CRUD | ✅ |
35
+ | Auth (JWT) | ✅ |
36
+ | Soft delete (admin default) | ✅ |
37
+ | Relations | belongsTo / hasMany / belongsToMany |
38
+ | Query builder | `db.from().where().with()` |
39
+ | Transactions | ✅ |
40
+ | Smart migrations | ADD COLUMN auto |
41
+ | Queue + retries | ✅ |
42
+ | Notifications | `notify` + HTTP API |
43
+ | Custom routes | `route()` |
44
+ | Dev Bar | dev only |
45
+ | i18n | `en` default · `ar` optional |
46
+
47
+ ---
48
+
49
+ ## Fields
50
+
51
+ | Helper | DB | Notes |
52
+ |--------|-----|--------|
53
+ | `string()` | TEXT | |
54
+ | `text()` | TEXT | long text |
55
+ | `number()` | REAL/INT | |
56
+ | `boolean()` | INT | API returns **true/false** |
57
+ | `email()` | TEXT | validated |
58
+ | `money()` | REAL | |
59
+ | `date()` / `datetime()` | TEXT | |
60
+ | `select([...])` | TEXT | |
61
+ | `belongsTo("x")` | `x_id` | name the field `teacher`, not `teacher_id` |
62
+ | `hasMany("x")` | — | |
63
+ | `belongsToMany("x")` | pivot table | |
64
+ | `file()` / `image()` | TEXT | URL path |
65
+
66
+ Chain: `.required()` `.label("…")` `.min()` `.max()`
67
+
68
+ SQL reserved names (`order`, `group`, …) throw a clear error — rename (e.g. `lesson_order`).
69
+
70
+ ---
71
+
72
+ ## REST API
73
+
74
+ For `resource("products")`:
75
+
76
+ | Method | URL |
77
+ |--------|-----|
78
+ | GET | `/api/products` |
79
+ | GET | `/api/products/:id` |
80
+ | POST | `/api/products` |
81
+ | PUT | `/api/products/:id` |
82
+ | DELETE | `/api/products/:id` |
83
+ | POST | `/api/products/:id/restore` |
84
+ | GET | `/api/products/export?format=csv` |
85
+
86
+ List query: `?search=&sort=&order=&page=&limit=&trashed=only|with`
87
+
88
+ ### Notifications
89
+
90
+ | Method | Path | Auth |
91
+ |--------|------|------|
92
+ | GET | `/api/notifications` | ✅ |
93
+ | POST | `/api/notifications` | ✅ create (calls `notify`) |
94
+ | POST | `/api/notifications/:id/read` | ✅ |
95
+ | POST | `/api/notifications/read-all` | ✅ |
96
+
97
+ Only **admin** may set `user_id` to notify another user.
98
+
99
+ ### Custom routes
100
+
101
+ ```ts
102
+ import { route, start } from "@nexa-stack/framework";
103
+
104
+ route("POST", "/api/checkout", async (req, user) => {
105
+ if (!user) return Response.json({ error: "Unauthorized" }, { status: 401 });
106
+ return Response.json({ data: { ok: true } });
107
+ });
108
+
109
+ await start();
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Query builder
115
+
116
+ ```ts
117
+ import { db } from "@nexa-stack/framework";
118
+
119
+ await db.from("orders").where("status", "paid").with("client").get();
120
+ await db.transaction(async (tx) => { /* ... */ });
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Auth
126
+
127
+ | Endpoint | Notes |
128
+ |----------|--------|
129
+ | POST `/api/auth/login` | → `{ data: { user, token } }` |
130
+ | POST `/api/auth/register` | off in production unless `AUTH_REGISTER=true` |
131
+ | POST `/api/auth/forgot-password` | |
132
+ | POST `/api/auth/reset-password` | |
133
+ | POST `/api/auth/verify-email` | |
134
+
135
+ `.admin()` implies auth. Use `.policy({ … })` for roles.
136
+
137
+ ---
138
+
139
+ ## Config (`.env`)
140
+
141
+ ```env
142
+ PORT=3333
143
+ APP_SECRET=change-me-in-production # ≥32 chars in production
144
+ APP_LOCALE=en # or ar
145
+ DB_PATH=./app.db
146
+ # DATABASE_URL=mysql://user:pass@localhost:3306/nexa
147
+ # DATABASE_URL=postgresql://user:pass@localhost:5432/nexa
148
+ AUTH_RATE_LIMIT=10
149
+ RATE_LIMIT=120
150
+ HOT_RELOAD=true # off in production
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Localization
156
+
157
+ Default UI language is **English**.
158
+
159
+ ```
160
+ lang/en.json
161
+ lang/ar.json # optional Arabic
162
+ ```
163
+
164
+ Admin: EN / عربي buttons. Schema: `/api/schema?lang=en`
165
+
166
+ ```env
167
+ APP_LOCALE=en
168
+ ```
169
+
170
+ ---
171
+
172
+ ## CLI
173
+
174
+ | Command | |
175
+ |---------|---|
176
+ | `nexa serve` | Start (hot reload in dev) |
177
+ | `nexa new app` | New project |
178
+ | `nexa make:resource X` | File under `resources/` |
179
+ | `nexa migrate` | Sync tables |
180
+ | `nexa migrate:rollback` | |
181
+ | `nexa db:seed` | |
182
+ | `nexa queue:work` | |
183
+ | `nexa schedule:work` | |
184
+
185
+ ---
186
+
187
+ ## Hot reload (dev)
188
+
189
+ Watches `app.ts` and `resources/*.ts`. Disabled when `NODE_ENV=production`.
190
+
191
+ ---
192
+
193
+ ## Dev Bar (dev only)
194
+
195
+ Bottom toolbar on `/admin`, `/docs`, `/` — not shown in production.
196
+
197
+ | Endpoint | |
198
+ |----------|---|
199
+ | `GET /api/dev/status` | port, resources, recent requests |
200
+ | `GET /api/dev/routes` | route manifest |
201
+ | `GET /dev-bar.js` | toolbar script |
202
+
203
+ ---
204
+
205
+ ## Admin / health
206
+
207
+ | Path | |
208
+ |------|---|
209
+ | `/admin` | Admin UI |
210
+ | `/docs` | Docs |
211
+ | `/api/schema?lang=en` | Schema + UI strings |
212
+ | `/health` | `{ ok: true }` |
213
+ | `/ready` | DB check |
214
+
215
+ ---
216
+
217
+ ## Security (v0.5+)
218
+
219
+ - Signed JWT (HMAC-SHA256)
220
+ - Mass assignment ignored for unknown fields
221
+ - `.admin()` requires auth
222
+ - Register closed in production by default
223
+ - Auth rate limit
224
+ - Strong `APP_SECRET` required in production
225
+
226
+ ---
227
+
228
+ ## Project layout
229
+
230
+ ```
231
+ app.ts
232
+ resources/*.ts
233
+ .env
234
+ lang/en.json
235
+ lang/ar.json
236
+ public/
237
+ database/migrations/
238
+ database/seeders/
239
+ schedule.ts # optional
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Checklist for testers (1.0.0)
245
+
246
+ 1. `npx @nexa-stack/framework new test-app && cd test-app && npm install`
247
+ 2. Read project `README.md`
248
+ 3. `npm run serve` → `/admin` (EN default) + `/docs`
249
+ 4. `npx nexa make:resource products` → `/api/products` without restart
250
+ 5. DELETE on `.admin()` resource → soft delete
251
+ 6. `boolean()` JSON is `true`/`false`
252
+ 7. Failed logins → 429 after threshold
253
+ 8. Port in use → auto next port
254
+ 9. Production without strong `APP_SECRET` → refuses to start
255
+ 10. `package.json` has `"@nexa-stack/framework": "^1.0.x"`