@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
package/.env.example ADDED
@@ -0,0 +1,46 @@
1
+ # Nexa
2
+ DB_PATH=./app.db
3
+ APP_URL=http://localhost:3333
4
+
5
+ # PostgreSQL
6
+ # DATABASE_URL=postgresql://user:pass@localhost:5432/nexa
7
+
8
+ # MySQL / MariaDB
9
+ # DATABASE_URL=mysql://user:pass@localhost:3306/nexa
10
+
11
+ PORT=3333
12
+ # REQUIRED in production — use a long random string
13
+ APP_SECRET=change-me-in-production
14
+ APP_LOCALE=en
15
+
16
+ # Optional admin seed (created only if no admin exists)
17
+ # ADMIN_EMAIL=admin@nexa.dev
18
+ # ADMIN_PASSWORD=123456
19
+
20
+ # Mail: log (dev) | smtp
21
+ MAIL_DRIVER=log
22
+ MAIL_FROM=nexa@localhost
23
+
24
+ # Cache: memory | file | redis
25
+ CACHE_DRIVER=memory
26
+
27
+ # Uploads (bytes) — default 5MB
28
+ UPLOAD_MAX_BYTES=5000000
29
+
30
+ # Rate limit: memory (default) | redis (multi-instance; needs CACHE_DRIVER=redis or RATE_LIMIT_DRIVER=redis)
31
+ # RATE_LIMIT_DRIVER=redis
32
+ # REDIS_URL=redis://127.0.0.1:6379
33
+
34
+ # List API cache TTL in seconds (0 = off). Speeds read-heavy apps behind multiple instances when using redis cache.
35
+ # LIST_CACHE_TTL=15
36
+
37
+ # Optional: allow public sign-up (default: on in dev, off when NODE_ENV=production)
38
+ # AUTH_REGISTER=true
39
+
40
+ # Auth brute-force limit (POST /api/auth/* per IP per minute)
41
+ # AUTH_RATE_LIMIT=10
42
+
43
+ # Hot reload (dev only — off in NODE_ENV=production)
44
+ # HOT_RELOAD=false
45
+
46
+ LOG_LEVEL=debug
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nexa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # Nexa
2
+
3
+ Write little → ship a full backend. Goal: **hours, not weeks.**
4
+
5
+ Runs on **Node.js ≥ 20** (Hostinger-friendly). Default UI language is **English**.
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ npx @nexa-stack/framework new my-app
11
+ cd my-app
12
+ npm install
13
+ npm run serve
14
+ ```
15
+
16
+ → http://localhost:3333
17
+ → http://localhost:3333/admin
18
+ → Login: `admin@nexa.dev` / `123456`
19
+
20
+ ## The idea
21
+
22
+ ```ts
23
+ import { resource, string, money, belongsTo, select } from "@nexa-stack/framework";
24
+
25
+ resource("orders", {
26
+ client: belongsTo("clients").required().label("Client"),
27
+ total: money().required().label("Total"),
28
+ status: select(["new", "paid"]).label("Status"),
29
+ }, { label: "Orders" }).admin();
30
+ ```
31
+
32
+ That gives you: database table + REST API + admin panel.
33
+
34
+ Optional power tools:
35
+
36
+ ```ts
37
+ await db.from("orders").where("status", "paid").with("client").get();
38
+ ```
39
+
40
+ ## Commands
41
+
42
+ | Command | What it does |
43
+ |---------|----------------|
44
+ | `nexa serve` | Start server |
45
+ | `nexa new X` | Scaffold a project |
46
+ | `nexa make:resource X` | Add `resources/X.ts` (auto-loaded) |
47
+
48
+ ## Localization
49
+
50
+ - Default: `APP_LOCALE=en`
51
+ - Arabic UI: set `APP_LOCALE=ar` and use `lang/ar.json`
52
+ - Admin has EN / عربي switch either way
53
+
54
+ ## Hostinger / production
55
+
56
+ Prefer MySQL on shared hosting:
57
+
58
+ ```env
59
+ DATABASE_URL=mysql://user:pass@localhost:3306/dbname
60
+ APP_SECRET=long-random-secret-at-least-32-chars
61
+ NODE_ENV=production
62
+ ```
63
+
64
+ SQLite uses `sql.js` (no Visual Studio / node-gyp).
65
+
66
+ ## Docs
67
+
68
+ - [docs/START.md](./docs/START.md) — start here
69
+ - [docs/REFERENCE.md](./docs/REFERENCE.md) — full API reference
70
+ - After `nexa serve`: http://localhost:3333/docs
71
+
72
+ Package: [`@nexa-stack/framework`](https://www.npmjs.com/package/@nexa-stack/framework)
package/bin/nexa.mjs ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Resolve tsx from this package (works with npx / bunx / local install).
4
+ * Avoids bare `--import tsx` which resolves from an empty cwd cache.
5
+ */
6
+ import { spawnSync } from "node:child_process";
7
+ import { createRequire } from "node:module";
8
+ import { dirname, join } from "node:path";
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
+
11
+ const dir = dirname(fileURLToPath(import.meta.url));
12
+ const pkgRoot = join(dir, "..");
13
+ const require = createRequire(join(pkgRoot, "package.json"));
14
+
15
+ let tsxLoader;
16
+ try {
17
+ tsxLoader = require.resolve("tsx/esm");
18
+ } catch {
19
+ try {
20
+ tsxLoader = require.resolve("tsx");
21
+ } catch {
22
+ console.error(
23
+ "[nexa] Missing dependency `tsx`.\n" +
24
+ " Fix: npm install @nexa-stack/framework\n" +
25
+ " Or: cd your-app && npm install"
26
+ );
27
+ process.exit(1);
28
+ }
29
+ }
30
+
31
+ const result = spawnSync(
32
+ process.execPath,
33
+ [
34
+ "--import",
35
+ pathToFileURL(tsxLoader).href,
36
+ join(dir, "nexa.ts"),
37
+ ...process.argv.slice(2),
38
+ ],
39
+ { stdio: "inherit", env: process.env, cwd: process.cwd() }
40
+ );
41
+ process.exit(result.status ?? 1);
package/bin/nexa.ts ADDED
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/env node
2
+ import { mkdirSync, existsSync, cpSync, writeFileSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { moduleDir } from "../packages/core/src/runtime.js";
6
+
7
+ const here = moduleDir(import.meta.url);
8
+ const frameworkRoot = join(here, "..").replace(/\\/g, "/");
9
+ const frameworkPkg = JSON.parse(readFileSync(join(here, "../package.json"), "utf8"));
10
+ const PKG = frameworkPkg.name as string;
11
+ const PKG_VERSION = frameworkPkg.version as string;
12
+
13
+ const [command, ...args] = process.argv.slice(2);
14
+ const arg = args[0];
15
+
16
+ const help = `
17
+ Nexa — ship a full backend in hours
18
+
19
+ nexa serve Start the server
20
+ nexa new my-app Create a new project
21
+ nexa make:resource X New resource file (auto-loaded)
22
+ nexa migrate Sync database tables
23
+ nexa migrate:rollback Roll back last file migration
24
+ nexa db:seed Run seeders
25
+ nexa queue:work Queue worker (scale)
26
+ nexa queue:work once Process one batch
27
+ nexa schedule:work Run scheduled tasks
28
+ nexa test Run tests
29
+ `;
30
+
31
+ const cwd = process.cwd().replace(/\\/g, "/");
32
+ const appUrl = new URL(`file:///${cwd}/app.ts`).href;
33
+
34
+ async function loadApp() {
35
+ await import(appUrl);
36
+ const { loadResources } = await import("../packages/core/src/loader.js");
37
+ await loadResources(cwd);
38
+ }
39
+
40
+ async function withDb() {
41
+ await loadApp();
42
+ const { runMigrations } = await import("../packages/core/src/migrate.js");
43
+ const { loadEnv } = await import("../packages/core/src/config.js");
44
+ const { dbConnection } = await import("../packages/database/src/database.js");
45
+ loadEnv();
46
+ return runMigrations(dbConnection());
47
+ }
48
+
49
+ if (command === "serve" || command === "run") {
50
+ await loadApp();
51
+ const { start } = await import("../packages/core/src/app.js");
52
+ await start();
53
+ } else if (command === "new" && arg) {
54
+ const name = arg.toLowerCase().replace(/[^a-z0-9-_]/g, "-");
55
+ const dir = join(process.cwd(), name);
56
+ if (existsSync(dir)) {
57
+ console.error(`Exists: ${dir}`);
58
+ process.exit(1);
59
+ }
60
+ mkdirSync(join(dir, "public"), { recursive: true });
61
+ mkdirSync(join(dir, "resources"), { recursive: true });
62
+ mkdirSync(join(dir, "lang"), { recursive: true });
63
+ mkdirSync(join(dir, "database", "seeders"), { recursive: true });
64
+ const frameworkPath = frameworkRoot;
65
+ const isPublishedInstall = here.replace(/\\/g, "/").includes("/node_modules/");
66
+ const depVersion = isPublishedInstall ? `^${PKG_VERSION}` : `file:${frameworkPath}`;
67
+ writeFileSync(
68
+ join(dir, "package.json"),
69
+ JSON.stringify(
70
+ {
71
+ name,
72
+ private: true,
73
+ type: "module",
74
+ scripts: {
75
+ serve: "nexa serve",
76
+ start: "nexa serve",
77
+ nexa: "nexa",
78
+ },
79
+ dependencies: { [PKG]: depVersion },
80
+ engines: { node: ">=20" },
81
+ },
82
+ null,
83
+ 2
84
+ )
85
+ );
86
+ writeFileSync(
87
+ join(dir, "app.ts"),
88
+ `import { resource, string, text, boolean } from "${PKG}";
89
+
90
+ // Prefer extra tables in resources/*.ts (auto-loaded)
91
+ resource("posts", {
92
+ title: string().required().label("Title"),
93
+ body: text().label("Body"),
94
+ published: boolean().label("Published"),
95
+ }, { label: "Posts" }).admin();
96
+ `
97
+ );
98
+ writeFileSync(
99
+ join(dir, ".env"),
100
+ `DB_PATH=./app.db
101
+ PORT=3333
102
+ APP_SECRET=change-me-in-dev-only
103
+ APP_LOCALE=en
104
+ `
105
+ );
106
+ writeFileSync(
107
+ join(dir, ".gitignore"),
108
+ `node_modules/
109
+ .env
110
+ *.db
111
+ *.db-shm
112
+ *.db-wal
113
+ storage/
114
+ `
115
+ );
116
+ writeFileSync(
117
+ join(dir, "README.md"),
118
+ `# ${name}
119
+
120
+ Nexa app — quick start.
121
+
122
+ ## Run
123
+
124
+ \`\`\`bash
125
+ npm install
126
+ npm run serve
127
+ \`\`\`
128
+
129
+ | URL | |
130
+ |-----|---|
131
+ | Home | http://localhost:3333 |
132
+ | Admin | http://localhost:3333/admin |
133
+ | Docs | http://localhost:3333/docs |
134
+
135
+ **Admin login:** \`admin@nexa.dev\` / \`123456\`
136
+
137
+ Default UI language is **English**. For Arabic set \`APP_LOCALE=ar\`.
138
+
139
+ ## New table
140
+
141
+ \`\`\`bash
142
+ npx nexa make:resource products
143
+ npx nexa migrate
144
+ \`\`\`
145
+
146
+ Files in \`resources/*.ts\` load automatically (hot reload in development).
147
+
148
+ ## Example resource
149
+
150
+ \`\`\`ts
151
+ import { resource, string, money, belongsTo } from "${PKG}";
152
+
153
+ resource("orders", {
154
+ client: belongsTo("clients").required().label("Client"),
155
+ total: money().required(),
156
+ }, { label: "Orders" }).admin();
157
+ \`\`\`
158
+
159
+ ## Hostinger / production
160
+
161
+ Prefer MySQL on shared hosting:
162
+
163
+ \`\`\`
164
+ DATABASE_URL=mysql://user:pass@localhost:3306/dbname
165
+ APP_SECRET=use-a-long-random-secret
166
+ NODE_ENV=production
167
+ \`\`\`
168
+
169
+ \`.admin()\` enables API + admin UI + auth + soft deletes by default.
170
+
171
+ ## Full API reference
172
+
173
+ - After install: \`node_modules/${PKG}/docs/REFERENCE.md\`
174
+ - Or http://localhost:3333/docs
175
+
176
+ ## Production checklist
177
+
178
+ \`\`\`env
179
+ NODE_ENV=production
180
+ APP_SECRET=random-string-at-least-32-chars
181
+ # AUTH_REGISTER=true # only if you want public sign-up
182
+ \`\`\`
183
+
184
+ ## Commands
185
+
186
+ | Command | |
187
+ |---------|---|
188
+ | \`npm run serve\` | Start server |
189
+ | \`npx nexa make:resource X\` | New resource |
190
+ | \`npx nexa migrate\` | Sync DB |
191
+ | \`npx nexa queue:work\` | Queue worker |
192
+
193
+ ---
194
+ @${PKG} ${PKG_VERSION} · https://www.npmjs.com/package/${PKG}
195
+ `
196
+ );
197
+ writeFileSync(
198
+ join(dir, "lang/en.json"),
199
+ JSON.stringify(
200
+ {
201
+ posts: "Posts",
202
+ ui: {
203
+ title: "Nexa",
204
+ dashboard: "Dashboard",
205
+ login: "Login",
206
+ logout: "Logout",
207
+ add: "Add",
208
+ delete: "Delete",
209
+ empty: "No data",
210
+ confirm_delete: "Delete?",
211
+ },
212
+ },
213
+ null,
214
+ 2
215
+ )
216
+ );
217
+ writeFileSync(
218
+ join(dir, "lang/ar.json"),
219
+ JSON.stringify(
220
+ {
221
+ posts: "المنشورات",
222
+ ui: {
223
+ title: "Nexa",
224
+ dashboard: "لوحة التحكم",
225
+ login: "دخول",
226
+ logout: "خروج",
227
+ add: "إضافة",
228
+ delete: "حذف",
229
+ empty: "لا يوجد بيانات",
230
+ confirm_delete: "حذف؟",
231
+ },
232
+ },
233
+ null,
234
+ 2
235
+ )
236
+ );
237
+ const adminSrc = join(frameworkPath, "public/admin.html");
238
+ if (existsSync(adminSrc)) {
239
+ cpSync(adminSrc, join(dir, "public/admin.html"));
240
+ }
241
+ const indexSrc = join(frameworkPath, "public/index.html");
242
+ if (existsSync(indexSrc)) {
243
+ cpSync(indexSrc, join(dir, "public/index.html"));
244
+ } else {
245
+ writeFileSync(
246
+ join(dir, "public/index.html"),
247
+ `<!DOCTYPE html><meta http-equiv="refresh" content="0;url=/admin"><a href="/admin">Admin</a>`
248
+ );
249
+ }
250
+ console.log(`
251
+ Project ready: ${name}
252
+
253
+ cd ${name}
254
+ npm install
255
+ npm run serve
256
+
257
+ Read README.md for the full quickstart
258
+ Open http://localhost:3333/admin
259
+ Login: admin@nexa.dev / 123456
260
+
261
+ New table:
262
+ npx nexa make:resource products
263
+ `);
264
+ } else if (command === "migrate") {
265
+ await withDb();
266
+ } else if (command === "migrate:rollback") {
267
+ await loadApp();
268
+ const { runMigrations, rollbackMigrations } = await import("../packages/core/src/migrate.js");
269
+ const { loadEnv } = await import("../packages/core/src/config.js");
270
+ const { dbConnection } = await import("../packages/database/src/database.js");
271
+ loadEnv();
272
+ const database = await runMigrations(dbConnection());
273
+ const n = await rollbackMigrations(database, Number(arg) || 1);
274
+ console.log(`Rolled back ${n} migration(s)`);
275
+ } else if (command === "db:seed") {
276
+ const db = await withDb();
277
+ const { runSeeders } = await import("../packages/core/src/migrate.js");
278
+ await runSeeders(db);
279
+ console.log("Seed complete");
280
+ } else if (command === "queue:work") {
281
+ const database = await withDb();
282
+ const { setupQueue, processJobs, workQueue, registerBuiltInJobs } = await import(
283
+ "../packages/queue/src/index.js"
284
+ );
285
+ await setupQueue(database);
286
+ registerBuiltInJobs();
287
+ // `nexa queue:work` = loop · `nexa queue:work once` or a number = single batch
288
+ if (arg === "once" || (arg && /^\d+$/.test(arg))) {
289
+ const n = await processJobs(database, arg === "once" ? 10 : Number(arg));
290
+ console.log(`Processed ${n} job(s)`);
291
+ } else {
292
+ console.log("Queue worker running (Ctrl+C to stop)…");
293
+ await workQueue(database);
294
+ }
295
+ } else if (command === "schedule:work") {
296
+ await loadApp();
297
+ const { loadEnv } = await import("../packages/core/src/config.js");
298
+ const { startScheduler } = await import("../packages/schedule/src/index.js");
299
+ loadEnv();
300
+ try {
301
+ await import(new URL(`file:///${cwd}/schedule.ts`).href);
302
+ } catch {
303
+ console.log("No schedule.ts");
304
+ }
305
+ startScheduler();
306
+ } else if (command === "make:resource" && arg) {
307
+ const name = arg.toLowerCase().replace(/[^a-z0-9_]/g, "");
308
+ mkdirSync("resources", { recursive: true });
309
+ let pkgJson = "";
310
+ try {
311
+ pkgJson = readFileSync("package.json", "utf8");
312
+ } catch {
313
+ /* no package.json */
314
+ }
315
+ const deps = pkgJson ? (JSON.parse(pkgJson).dependencies ?? {}) : {};
316
+ const pkgName = [PKG, "@nexa-stack/framework", "@nexa/framework", "@nexa/core", "nexa-bun", "nexa"].find((n) => n in deps);
317
+ const imp = pkgName ? `"${pkgName}"` : `"../packages/core/src/index.js"`;
318
+ const path = `resources/${name}.ts`;
319
+ writeFileSync(
320
+ path,
321
+ `import { resource, string } from ${imp};
322
+
323
+ resource("${name}", {
324
+ name: string().required().label("Name"),
325
+ }, { label: "${name}" }).admin();
326
+ `
327
+ );
328
+ console.log(`Created ${path} — auto-loaded by nexa serve (no import needed)`);
329
+ } else if (command === "test") {
330
+ const r = spawnSync("npx", ["vitest", "run"], { stdio: "inherit", shell: true });
331
+ process.exit(r.status ?? 1);
332
+ } else {
333
+ console.log(help);
334
+ }
package/docs/AI.md ADDED
@@ -0,0 +1,69 @@
1
+ # AI Instructions — Nexa Framework
2
+
3
+ > **Read this before generating any code for this project.**
4
+
5
+ ## Context
6
+
7
+ This project uses **Nexa** — a Node.js ≥ 20 framework (`@nexa-stack/framework`).
8
+
9
+ NOT Express. NOT NestJS. NOT Laravel.
10
+
11
+ ## No controllers, routes, or page files
12
+
13
+ | Do NOT generate | Nexa equivalent |
14
+ |-----------------|-----------------|
15
+ | Controller.php | `resource()` in app.ts |
16
+ | routes/web.php | auto `/api/{name}` |
17
+ | models/ | `resource()` fields |
18
+ | add.blade.php | `.admin()` auto UI |
19
+ | Manual i18n everywhere | `{ label: "Clients" }` or `labelKey` |
20
+
21
+ ## Import
22
+
23
+ ```ts
24
+ import {
25
+ start, resource, string, number, money, email, text, boolean, belongsTo, getDb,
26
+ } from "@nexa-stack/framework";
27
+ ```
28
+
29
+ ## Template
30
+
31
+ ```ts
32
+ resource("MODULE", {
33
+ field: string().required(),
34
+ price: money().required(),
35
+ parent: belongsTo("OTHER").required(),
36
+ }, { label: "Label" }).admin().auth();
37
+
38
+ await start();
39
+ ```
40
+
41
+ ## Example modules
42
+
43
+ | Resource | Label | Auth |
44
+ |----------|-------|------|
45
+ | clients | Clients | no |
46
+ | suppliers | Suppliers | no |
47
+ | products | Products | no |
48
+ | employees | Employees | yes |
49
+ | orders | Orders | yes |
50
+ | invoices | Invoices | yes |
51
+ | payments | Payments | yes |
52
+
53
+ ## Checklist
54
+
55
+ - [ ] Edit `app.ts` / `resources/*.ts`
56
+ - [ ] Use `resource()` for each module
57
+ - [ ] English labels by default: `{ label: "..." }`
58
+ - [ ] `.admin()` for UI
59
+ - [ ] `.auth()` for protected modules
60
+ - [ ] No manual routes/controllers/pages
61
+ - [ ] Default locale is English (`APP_LOCALE=en`)
62
+
63
+ ## Test
64
+
65
+ ```bash
66
+ npm run serve
67
+ ```
68
+
69
+ http://localhost:3333/admin
@@ -0,0 +1,74 @@
1
+ # Nexa — Architecture
2
+
3
+ ## What Nexa does NOT have
4
+
5
+ Nexa is **not** Laravel / Express. There are no:
6
+
7
+ | Traditional | In Nexa |
8
+ |-------------|---------|
9
+ | Controllers | ❌ — auto-generated from `resource()` |
10
+ | Routes file | ❌ — auto: `/api/{name}` |
11
+ | Models | ❌ — `resource()` defines the table |
12
+ | Migrations | ❌ — tables created on `start()` |
13
+ | Add/Edit pages | ❌ — `.admin()` generates UI |
14
+ | Language files (lang/) | ❌ — labels inline in `resource()` |
15
+
16
+ ---
17
+
18
+ ## What Nexa HAS
19
+
20
+ ```
21
+ app.ts → your entire backend (resources + start)
22
+ packages/ → the framework engine
23
+ public/ → custom HTML pages (optional)
24
+ docs/ → documentation
25
+ ```
26
+
27
+ ---
28
+
29
+ ## How to add a module
30
+
31
+ **One line in `app.ts`:**
32
+
33
+ ```ts
34
+ resource("clients", {
35
+ name: string().required(),
36
+ phone: string().required(),
37
+ }, { label: "Clients" }).admin();
38
+ ```
39
+
40
+ This gives you:
41
+ - Database table `clients`
42
+ - API: GET/POST/PUT/DELETE `/api/clients`
43
+ - Admin page with add form + list
44
+ - Validation
45
+
46
+ **No controller. No route. No add page file.**
47
+
48
+ ---
49
+
50
+ ## How to add a custom page
51
+
52
+ Only when `.admin()` is not enough (e.g. cashier POS):
53
+
54
+ ```
55
+ public/my-page.html → http://localhost:3333/my-page
56
+ ```
57
+
58
+ The page uses `fetch()` to call Nexa API.
59
+
60
+ ---
61
+
62
+ ## Flow
63
+
64
+ ```
65
+ app.ts
66
+ resource("clients", {...}).admin()
67
+
68
+ start()
69
+
70
+ ┌─────────────┬──────────────┬─────────────┐
71
+ │ SQLite DB │ REST API │ Admin UI │
72
+ │ clients │ /api/clients│ /admin │
73
+ └─────────────┴──────────────┴─────────────┘
74
+ ```