create-nexwire 4.0.0 → 4.1.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.
- package/README.md +5 -4
- package/express/.env +1 -1
- package/express/README.md +13 -7
- package/express/package.json +2 -2
- package/express/src/config/session.ts +1 -1
- package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
- package/express/src/database/migrations/sqlite/meta/_journal.json +1 -1
- package/express/src/database/schema.ts +3 -3
- package/express/src/framework/cache/cache.ts +82 -81
- package/express/src/framework/database/seed.ts +1 -1
- package/express/src/framework/facade.ts +1 -24
- package/express/src/framework/http/app.ts +5 -5
- package/express/src/framework/http/openapi.ts +1 -1
- package/express/src/framework/http/router.ts +3 -18
- package/express/src/framework/http/types.ts +19 -0
- package/express/src/framework/http/validation.ts +18 -18
- package/express/src/framework/maker-cli/middleware/core.mjs +41 -47
- package/express/src/framework/maker-cli/module/core.mjs +920 -684
- package/express/src/framework/maker-cli/module/index.mjs +202 -140
- package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
- package/express/src/framework/maker-cli/stubs/controller/openapi.ts.stub +40 -14
- package/express/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +40 -15
- package/express/src/framework/maker-cli/stubs/controller/plain.ts.stub +42 -16
- package/express/src/framework/maker-cli/stubs/controller/standalone.ts.stub +47 -0
- package/express/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/express/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/express/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/express/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
- package/express/src/framework/maker-cli/stubs/notification/route.api.ts.stub +50 -50
- package/express/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
- package/express/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
- package/express/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
- package/express/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +113 -0
- package/express/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
- package/express/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
- package/express/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
- package/express/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/express/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/express/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/express/src/framework/maker-cli/utils/naming.mjs +56 -13
- package/express/src/framework/modules/discover.ts +57 -54
- package/express/src/framework/notification/index.ts +1 -1
- package/express/src/framework/realtime/broadcast.ts +46 -46
- package/express/src/framework/server.ts +2 -1
- package/express/src/framework/session/session.ts +102 -98
- package/express/src/framework/support/mail.ts +48 -48
- package/express/src/framework/support/status-codes.ts +75 -0
- package/express/src/middlewares/auth-middleware.ts +1 -1
- package/express/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
- package/express/src/modules/auth/__tests__/login.test.ts +171 -0
- package/express/src/modules/auth/__tests__/logout.test.ts +60 -0
- package/express/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
- package/express/src/modules/auth/__tests__/me.test.ts +91 -0
- package/express/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/express/src/modules/auth/__tests__/register.test.ts +291 -0
- package/express/src/modules/auth/__tests__/resetpass.test.ts +206 -0
- package/express/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
- package/express/src/modules/auth/controllers/auth.ts +254 -0
- package/express/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -15
- package/{hono/src/modules/auth/database/models/notifications.ts → express/src/modules/auth/database/models/notification.ts} +1 -1
- package/express/src/modules/auth/database/models/role.ts +1 -1
- package/express/src/modules/auth/database/models/user.ts +1 -1
- package/express/src/modules/auth/facade.ts +12 -0
- package/express/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +3 -4
- package/express/src/modules/auth/jobs/registeruser.ts +23 -23
- package/express/src/modules/auth/routes/{api.ts → index.ts} +150 -150
- package/express/src/modules/auth/routes/role.ts +33 -33
- package/{hono/src/modules/auth/controllers/auth.schema.ts → express/src/modules/auth/schemas/auth.ts} +102 -102
- package/express/src/modules/auth/services/forgetpassword.ts +66 -0
- package/express/src/modules/auth/services/login.ts +63 -0
- package/express/src/modules/auth/services/logout.ts +25 -0
- package/express/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/express/src/modules/auth/services/me.ts +31 -0
- package/express/src/modules/auth/services/refreshtoken.ts +110 -0
- package/express/src/modules/auth/services/register.ts +141 -0
- package/express/src/modules/auth/services/resetpassword.ts +69 -0
- package/express/src/modules/auth/services/verifyemail.ts +77 -0
- package/express/src/modules/auth/types/auth.ts +31 -0
- package/express/src/resources/src/assets/images/logo.png +0 -0
- package/express/src/resources/src/pages/auth/forgetPassword.vue +1 -0
- package/express/src/resources/src/pages/auth/login.vue +1 -1
- package/express/src/storage/database/nexwire.sqlite +0 -0
- package/hono/.env +1 -1
- package/hono/README.md +13 -7
- package/hono/package.json +1 -1
- package/hono/src/config/session.ts +1 -1
- package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
- package/hono/src/database/migrations/sqlite/meta/_journal.json +1 -1
- package/hono/src/database/schema.ts +3 -3
- package/hono/src/framework/cache/cache.ts +82 -81
- package/hono/src/framework/database/seed.ts +1 -1
- package/hono/src/framework/http/app.ts +5 -5
- package/hono/src/framework/http/validation.ts +21 -21
- package/hono/src/framework/maker-cli/middleware/core.mjs +40 -46
- package/hono/src/framework/maker-cli/module/core.mjs +401 -168
- package/hono/src/framework/maker-cli/module/index.mjs +73 -11
- package/hono/src/framework/maker-cli/stubs/controller/openapi.ts.stub +28 -16
- package/hono/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +28 -17
- package/hono/src/framework/maker-cli/stubs/controller/plain.ts.stub +28 -16
- package/hono/src/framework/maker-cli/stubs/controller/standalone.ts.stub +56 -0
- package/hono/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/hono/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/hono/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/hono/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +9 -8
- package/hono/src/framework/maker-cli/stubs/notification/route.api.ts.stub +49 -49
- package/hono/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +11 -11
- package/hono/src/framework/maker-cli/stubs/route/api.ts.stub +93 -79
- package/hono/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -10
- package/hono/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +112 -0
- package/hono/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +37 -0
- package/hono/src/framework/maker-cli/stubs/{controller/schema.plain.ts.stub → schema/name.plain.ts.stub} +13 -13
- package/hono/src/framework/maker-cli/stubs/{controller/schema.ts.stub → schema/name.ts.stub} +32 -32
- package/hono/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/hono/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/hono/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/hono/src/framework/maker-cli/utils/naming.mjs +56 -13
- package/hono/src/framework/modules/discover.ts +57 -54
- package/hono/src/framework/notification/index.ts +1 -1
- package/hono/src/framework/realtime/broadcast.ts +46 -46
- package/hono/src/framework/server.ts +1 -1
- package/hono/src/framework/session/session.ts +121 -116
- package/hono/src/middlewares/auth-middleware.ts +1 -1
- package/hono/src/modules/auth/__tests__/forgetpass.test.ts +170 -0
- package/hono/src/modules/auth/__tests__/login.test.ts +154 -0
- package/hono/src/modules/auth/__tests__/logout.test.ts +53 -0
- package/hono/src/modules/auth/__tests__/logoutalldevices.test.ts +34 -0
- package/hono/src/modules/auth/__tests__/me.test.ts +87 -0
- package/hono/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/hono/src/modules/auth/__tests__/register.test.ts +258 -0
- package/hono/src/modules/auth/__tests__/resetpass.test.ts +214 -0
- package/hono/src/modules/auth/__tests__/verifyemail.test.ts +189 -0
- package/hono/src/modules/auth/controllers/auth.ts +238 -0
- package/hono/src/modules/auth/controllers/{role.controller.ts → role.ts} +25 -25
- package/{express/src/modules/auth/database/models/notifications.ts → hono/src/modules/auth/database/models/notification.ts} +1 -1
- package/hono/src/modules/auth/database/models/role.ts +1 -1
- package/hono/src/modules/auth/database/models/user.ts +1 -1
- package/hono/src/modules/auth/facade.ts +12 -0
- package/hono/src/modules/auth/{controllers/auth.helpers.ts → helpers/auth.ts} +5 -6
- package/hono/src/modules/auth/jobs/registeruser.ts +23 -23
- package/hono/src/modules/auth/routes/{api.ts → index.ts} +150 -150
- package/hono/src/modules/auth/routes/role.ts +33 -33
- package/{express/src/modules/auth/controllers/auth.schema.ts → hono/src/modules/auth/schemas/auth.ts} +102 -102
- package/hono/src/modules/auth/services/forgetpassword.ts +58 -0
- package/hono/src/modules/auth/services/login.ts +61 -0
- package/hono/src/modules/auth/services/logout.ts +24 -0
- package/hono/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/hono/src/modules/auth/services/me.ts +31 -0
- package/hono/src/modules/auth/services/refreshtoken.ts +110 -0
- package/hono/src/modules/auth/services/register.ts +132 -0
- package/hono/src/modules/auth/services/resetpassword.ts +69 -0
- package/hono/src/modules/auth/services/verifyemail.ts +77 -0
- package/hono/src/modules/auth/types/auth.ts +31 -0
- package/hono/src/resources/src/assets/images/logo.png +0 -0
- package/hono/src/storage/database/nexwire.sqlite +0 -0
- package/package.json +1 -1
- package/express/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
- package/express/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -418
- package/express/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
- package/express/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
- package/express/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
- package/express/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
- package/express/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
- package/express/src/modules/auth/controllers/auth.controller.ts +0 -358
- package/hono/src/framework/maker-cli/stubs/example/console.ts.stub +0 -34
- package/hono/src/framework/maker-cli/stubs/example/controller.ts.stub +0 -505
- package/hono/src/framework/maker-cli/stubs/example/job.ts.stub +0 -75
- package/hono/src/framework/maker-cli/stubs/example/route.api.ts.stub +0 -207
- package/hono/src/framework/maker-cli/stubs/example/route.plain.ts.stub +0 -18
- package/hono/src/framework/maker-cli/stubs/example/schema.plain.ts.stub +0 -42
- package/hono/src/framework/maker-cli/stubs/example/schema.ts.stub +0 -42
- package/hono/src/modules/auth/controllers/auth.controller.ts +0 -413
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
<h3 align="center">Stop wiring the stack together. Ship the app.</h3>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
<a href="https://niyamulahsan.github.io/nexwire"><img src="https://img.shields.io/badge/docs-3b8eed" alt="Documentation"></a>
|
|
10
|
+
<a href="https://niyamulahsan.github.io/nexwire"><img src="https://img.shields.io/badge/docs%20%7C%20nexwire-3b8eed" alt="Documentation"></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/create-nexwire"><img src="https://img.shields.io/npm/v/create-nexwire" alt="npm version"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/create-nexwire"><img src="https://img.shields.io/npm/dt/create-nexwire" alt="npm downloads"></a>
|
|
12
13
|
<a href="https://github.com/niyamulahsan/nexwire/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
13
14
|
</p>
|
|
14
15
|
|
|
@@ -48,7 +49,7 @@ Your API is live at `http://localhost:3000`, Scalar docs at `/api-docs`, and the
|
|
|
48
49
|
|
|
49
50
|
The scaffold already ships a pre-migrated, pre-seeded SQLite database, so there is no migration step before `maker dev`. If you point `DATABASE_URL` at MySQL or PostgreSQL, run `npm run maker db:migrate -- --seed` once to build and populate that database instead.
|
|
50
51
|
|
|
51
|
-
Requires **Node.js >= 22
|
|
52
|
+
Requires **Node.js >= 22** or **Bun >= 1.3**.
|
|
52
53
|
|
|
53
54
|
### Current directory
|
|
54
55
|
|
|
@@ -79,7 +80,7 @@ nexwire runs on **Node.js** or **Bun** — pick whichever fits your deployment:
|
|
|
79
80
|
|
|
80
81
|
| Runtime | Minimum version | Notes |
|
|
81
82
|
| ----------- | --------------- | --------------------------------------------------------------------- |
|
|
82
|
-
| **Node.js** | `>= 22
|
|
83
|
+
| **Node.js** | `>= 22` | Default. Uses `node` in Dockerfile. |
|
|
83
84
|
| **Bun** | `>= 1.3` | Pass `--runtime=bun` to `deploy:init`. Uses `oven/bun` in Dockerfile. |
|
|
84
85
|
|
|
85
86
|
## Demo
|
|
@@ -92,7 +93,7 @@ Real recordings from a freshly scaffolded project — no mockups, no staging dat
|
|
|
92
93
|
<img src="https://raw.githubusercontent.com/niyamulahsan/nexwire/main/packages/docs/public/screenshots/dev-mode.gif" alt="nexwire dev mode: API, UI, API docs, queue dashboard and mail UI booting in one command" width="100%">
|
|
93
94
|
</p>
|
|
94
95
|
|
|
95
|
-
**Real deploy** — the same
|
|
96
|
+
**Real deploy** — the same framework running on a live server as a real project, with HTTPS, process supervision, and zero-downtime restarts:
|
|
96
97
|
|
|
97
98
|
<p align="center">
|
|
98
99
|
<img src="https://raw.githubusercontent.com/niyamulahsan/nexwire/main/packages/docs/public/screenshots/real-deploy.gif" alt="nexwire deployed to a real server with HTTPS and process supervision" width="100%">
|
package/express/.env
CHANGED
|
@@ -26,7 +26,7 @@ MAIL_PASSWORD=
|
|
|
26
26
|
|
|
27
27
|
# ── Redis ──────────────────────────────────────────
|
|
28
28
|
# Set true to enable cache, session, queue, events, and Queue Dashboard
|
|
29
|
-
REDIS=
|
|
29
|
+
REDIS=true
|
|
30
30
|
# Shared key prefix for cache, session, queue, events, and dashboards
|
|
31
31
|
REDIS_URL=redis://127.0.0.1:6379
|
|
32
32
|
REDIS_PREFIX=nexwire
|
package/express/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<h3 align="center">Full-stack TypeScript framework for modern web applications</h3>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
<a href="https://niyamulahsan.github.io/nexwire"><img src="https://img.shields.io/badge/docs-3b8eed" alt="Documentation"></a>
|
|
10
|
+
<a href="https://niyamulahsan.github.io/nexwire"><img src="https://img.shields.io/badge/docs%20%7C%20nexwire-3b8eed" alt="Documentation"></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/create-nexwire"><img src="https://img.shields.io/npm/v/create-nexwire" alt="npm version"></a>
|
|
12
12
|
<a href="https://www.npmjs.com/package/create-nexwire"><img src="https://img.shields.io/npm/dt/create-nexwire" alt="npm downloads"></a>
|
|
13
13
|
<a href="https://github.com/niyamulahsan/nexwire/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
@@ -59,8 +59,8 @@ Works with any package manager: `pnpm`, `yarn`, or `bun`.
|
|
|
59
59
|
|
|
60
60
|
All examples use `npm` as the default. nexwire works with any major package manager:
|
|
61
61
|
|
|
62
|
-
| Manager | Create project
|
|
63
|
-
| -------- |
|
|
62
|
+
| Manager | Create project | Run commands |
|
|
63
|
+
| -------- | ----------------------------------- | --------------------- |
|
|
64
64
|
| **npm** | `npm create nexwire@latest my-app` | `npm run maker <cmd>` |
|
|
65
65
|
| **pnpm** | `pnpm create nexwire@latest my-app` | `pnpm maker <cmd>` |
|
|
66
66
|
| **yarn** | `yarn create nexwire@latest my-app` | `yarn maker <cmd>` |
|
|
@@ -73,7 +73,7 @@ nexwire runs on **Node.js** or **Bun** — pick whichever fits your deployment:
|
|
|
73
73
|
|
|
74
74
|
| Runtime | Minimum version | Notes |
|
|
75
75
|
| ----------- | --------------- | --------------------------------------------------------------------- |
|
|
76
|
-
| **Node.js** | `>= 22.12`
|
|
76
|
+
| **Node.js** | `>= 22.12` | Default. Uses `node` in Dockerfile. |
|
|
77
77
|
| **Bun** | `>= 1.3` | Pass `--runtime=bun` to `deploy:init`. Uses `oven/bun` in Dockerfile. |
|
|
78
78
|
|
|
79
79
|
## Features
|
|
@@ -114,14 +114,20 @@ Every feature is a self-contained module under `src/modules/<name>/`:
|
|
|
114
114
|
|
|
115
115
|
```
|
|
116
116
|
src/modules/posts/
|
|
117
|
+
├── __tests__/ # Unit tests
|
|
117
118
|
├── console/ # CLI commands & scheduled tasks
|
|
118
|
-
├── controllers/ # Request handlers
|
|
119
|
+
├── controllers/ # Request handlers
|
|
119
120
|
├── database/
|
|
120
121
|
│ ├── models/ # Drizzle table definitions
|
|
121
|
-
│ └── seeders/ # Test data
|
|
122
|
+
│ └── seeders/ # Test data
|
|
123
|
+
├── helpers/ # Pure functions — no I/O
|
|
122
124
|
├── jobs/ # BullMQ queue handlers
|
|
125
|
+
├── middlewares/ # Module-local middleware
|
|
123
126
|
├── routes/ # HTTP route definitions (auto-discovered)
|
|
124
|
-
|
|
127
|
+
├── schemas/ # Zod validation schemas
|
|
128
|
+
├── services/ # Business logic — DB, files, network
|
|
129
|
+
├── types/ # Shared TypeScript types
|
|
130
|
+
└── facade.ts # Public API for other modules
|
|
125
131
|
```
|
|
126
132
|
|
|
127
133
|
Modules are **auto-discovered** — no manual registration. Create one with:
|
package/express/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from "../modules/auth/database/models/
|
|
2
|
-
export * from "../modules/auth/database/models/role.js";
|
|
3
|
-
export * from "../modules/auth/database/models/user.js";
|
|
1
|
+
export * from "../modules/auth/database/models/notification.js";
|
|
2
|
+
export * from "../modules/auth/database/models/role.js";
|
|
3
|
+
export * from "../modules/auth/database/models/user.js";
|
|
@@ -1,81 +1,82 @@
|
|
|
1
|
-
import { cacheConfig, redisConfig } from "@/config/index.js";
|
|
2
|
-
import { redisClientIfReady } from "@/framework/redis/client.js";
|
|
3
|
-
|
|
4
|
-
function cacheKey(key: string) {
|
|
5
|
-
return `${cacheConfig.keyPrefix}:${key}`;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Why: Provides Redis-backed cache helpers with graceful fallback behavior.
|
|
10
|
-
* When: Controllers/jobs need temporary computed data caching.
|
|
11
|
-
* Where: Application module code via facade.
|
|
12
|
-
* How: Uses namespaced Redis keys and JSON serialization.
|
|
13
|
-
*/
|
|
14
|
-
export const cache = {
|
|
15
|
-
/**
|
|
16
|
-
* Why: Reads cached JSON value by key.
|
|
17
|
-
* When: A feature wants fast lookup before recomputing.
|
|
18
|
-
* Where: Controllers/jobs/services using shared cache.
|
|
19
|
-
* How: Fetches namespaced Redis key and parses JSON.
|
|
20
|
-
*/
|
|
21
|
-
async get<T>(key: string, fallback: T | null = null) {
|
|
22
|
-
const client = redisClientIfReady();
|
|
23
|
-
if (!client) return fallback;
|
|
24
|
-
|
|
25
|
-
const value = await client.get(cacheKey(key));
|
|
26
|
-
return value ? (JSON.parse(value) as T) : fallback;
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
await
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
1
|
+
import { cacheConfig, redisConfig } from "@/config/index.js";
|
|
2
|
+
import { redisClientIfReady } from "@/framework/redis/client.js";
|
|
3
|
+
|
|
4
|
+
function cacheKey(key: string) {
|
|
5
|
+
return `${cacheConfig.keyPrefix}:${key}`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Why: Provides Redis-backed cache helpers with graceful fallback behavior.
|
|
10
|
+
* When: Controllers/jobs need temporary computed data caching.
|
|
11
|
+
* Where: Application module code via facade.
|
|
12
|
+
* How: Uses namespaced Redis keys and JSON serialization.
|
|
13
|
+
*/
|
|
14
|
+
export const cache = {
|
|
15
|
+
/**
|
|
16
|
+
* Why: Reads cached JSON value by key.
|
|
17
|
+
* When: A feature wants fast lookup before recomputing.
|
|
18
|
+
* Where: Controllers/jobs/services using shared cache.
|
|
19
|
+
* How: Fetches namespaced Redis key and parses JSON.
|
|
20
|
+
*/
|
|
21
|
+
async get<T>(key: string, fallback: T | null = null) {
|
|
22
|
+
const client = redisClientIfReady();
|
|
23
|
+
if (!client) return fallback;
|
|
24
|
+
|
|
25
|
+
const value = await client.get(cacheKey(key));
|
|
26
|
+
return value ? (JSON.parse(value) as T) : fallback;
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
async set(key: string, value: unknown, ttl = cacheConfig.ttlSeconds) {
|
|
30
|
+
const client = redisClientIfReady();
|
|
31
|
+
if (!client) return false;
|
|
32
|
+
|
|
33
|
+
await client.set(cacheKey(key), JSON.stringify(value), "EX", ttl);
|
|
34
|
+
return true;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated Use set() instead.
|
|
39
|
+
*/
|
|
40
|
+
async put(key: string, value: unknown, ttl = cacheConfig.ttlSeconds) {
|
|
41
|
+
return this.set(key, value, ttl);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Why: Deletes a cached value by key.
|
|
46
|
+
* When: Cached data becomes stale or must be invalidated.
|
|
47
|
+
* Where: Write/update flows that change source data.
|
|
48
|
+
* How: Removes namespaced Redis cache key.
|
|
49
|
+
*/
|
|
50
|
+
async forget(key: string) {
|
|
51
|
+
const client = redisClientIfReady();
|
|
52
|
+
if (!client) return false;
|
|
53
|
+
|
|
54
|
+
await client.del(cacheKey(key));
|
|
55
|
+
return true;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Why: Returns cached value or computes/stores a fresh one.
|
|
60
|
+
* When: A feature needs cache-aside behavior.
|
|
61
|
+
* Where: Read paths with potentially expensive callbacks.
|
|
62
|
+
* How: Attempts get first, then executes callback and put.
|
|
63
|
+
*/
|
|
64
|
+
async remember<T>(key: string, ttl: number, callback: () => Promise<T>) {
|
|
65
|
+
const cached = await cache.get<T>(key);
|
|
66
|
+
if (cached !== null) return cached;
|
|
67
|
+
|
|
68
|
+
const fresh = await callback();
|
|
69
|
+
await this.put(key, fresh, ttl);
|
|
70
|
+
return fresh;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Why: Indicates whether Redis cache backend is currently usable.
|
|
75
|
+
* When: Runtime checks need to branch on cache availability.
|
|
76
|
+
* Where: Health/status endpoints and conditional flows.
|
|
77
|
+
* How: Confirms REDIS flag and ready Redis client instance.
|
|
78
|
+
*/
|
|
79
|
+
isAvailable() {
|
|
80
|
+
return redisConfig.enabled && redisClientIfReady() !== null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
1
2
|
import { closeDatabase, initDatabase } from "@/framework/database/connection.js";
|
|
2
3
|
import { discoverModuleFiles, importFile } from "@/framework/modules/discover.js";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
4
|
|
|
5
5
|
const NAME_SYMBOL = Symbol.for("drizzle:Name");
|
|
6
6
|
const FK_SYMBOLS = [
|
|
@@ -4,30 +4,7 @@ export { registerOpenApiRoute } from "@/framework/http/openapi.js";
|
|
|
4
4
|
export type { RouteConfig } from "@/framework/http/router.js";
|
|
5
5
|
export { createRoute, createRouter, group, jsonContent } from "@/framework/http/router.js";
|
|
6
6
|
export { validate } from "@/framework/http/validation.js";
|
|
7
|
-
export
|
|
8
|
-
CONTINUE: 100,
|
|
9
|
-
SWITCHING_PROTOCOLS: 101,
|
|
10
|
-
OK: 200,
|
|
11
|
-
CREATED: 201,
|
|
12
|
-
ACCEPTED: 202,
|
|
13
|
-
NO_CONTENT: 204,
|
|
14
|
-
BAD_REQUEST: 400,
|
|
15
|
-
UNAUTHORIZED: 401,
|
|
16
|
-
FORBIDDEN: 403,
|
|
17
|
-
NOT_FOUND: 404,
|
|
18
|
-
METHOD_NOT_ALLOWED: 405,
|
|
19
|
-
CONFLICT: 409,
|
|
20
|
-
GONE: 410,
|
|
21
|
-
PAYLOAD_TOO_LARGE: 413,
|
|
22
|
-
URI_TOO_LONG: 414,
|
|
23
|
-
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
24
|
-
UNPROCESSABLE_ENTITY: 422,
|
|
25
|
-
TOO_MANY_REQUESTS: 429,
|
|
26
|
-
INTERNAL_SERVER_ERROR: 500,
|
|
27
|
-
BAD_GATEWAY: 502,
|
|
28
|
-
SERVICE_UNAVAILABLE: 503,
|
|
29
|
-
GATEWAY_TIMEOUT: 504
|
|
30
|
-
} as const;
|
|
7
|
+
export * as HttpStatusCodes from "@/framework/support/status-codes.js";
|
|
31
8
|
export { cache } from "@/framework/cache/cache.js";
|
|
32
9
|
export { database, db } from "@/framework/database/connection.js";
|
|
33
10
|
export type { PaginatedResult } from "@/framework/database/paginate.js";
|
|
@@ -35,20 +35,20 @@ export function createHttpApp() {
|
|
|
35
35
|
let allOk = true;
|
|
36
36
|
try {
|
|
37
37
|
database();
|
|
38
|
-
checks
|
|
38
|
+
checks.database = "ok";
|
|
39
39
|
} catch {
|
|
40
|
-
checks
|
|
40
|
+
checks.database = "error";
|
|
41
41
|
allOk = false;
|
|
42
42
|
}
|
|
43
43
|
if (redisConfig.enabled) {
|
|
44
44
|
if (redisClientIfReady()) {
|
|
45
|
-
checks
|
|
45
|
+
checks.redis = "ok";
|
|
46
46
|
} else {
|
|
47
|
-
checks
|
|
47
|
+
checks.redis = "error";
|
|
48
48
|
allOk = false;
|
|
49
49
|
}
|
|
50
50
|
} else {
|
|
51
|
-
checks
|
|
51
|
+
checks.redis = "disabled";
|
|
52
52
|
}
|
|
53
53
|
const status = allOk ? 200 : 503;
|
|
54
54
|
res.status(status).json({ status: allOk ? "ready" : "not_ready", checks });
|
|
@@ -5,7 +5,7 @@ import { apiReference } from "@scalar/express-api-reference";
|
|
|
5
5
|
import type { Express } from "express";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { openApiConfig } from "@/config/index.js";
|
|
8
|
-
import type { RouteConfig } from "@/framework/http/
|
|
8
|
+
import type { RouteConfig } from "@/framework/http/types.js";
|
|
9
9
|
|
|
10
10
|
extendZodWithOpenApi(z);
|
|
11
11
|
|
|
@@ -2,25 +2,10 @@ import type { Router as ExpressRouter, RequestHandler } from "express";
|
|
|
2
2
|
import express from "express";
|
|
3
3
|
import type { z } from "zod";
|
|
4
4
|
import { registerOpenApiRoute } from "@/framework/http/openapi.js";
|
|
5
|
+
import type { HttpMethod, RouteConfig } from "@/framework/http/types.js";
|
|
5
6
|
import { validate } from "@/framework/http/validation.js";
|
|
6
7
|
|
|
7
|
-
export type HttpMethod
|
|
8
|
-
|
|
9
|
-
export type RouteConfig = {
|
|
10
|
-
path: string;
|
|
11
|
-
method: HttpMethod;
|
|
12
|
-
tags?: string[];
|
|
13
|
-
summary?: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
request?: {
|
|
16
|
-
params?: z.ZodTypeAny;
|
|
17
|
-
query?: z.ZodTypeAny;
|
|
18
|
-
headers?: z.ZodTypeAny;
|
|
19
|
-
cookies?: z.ZodTypeAny;
|
|
20
|
-
body?: { content: { "application/json": { schema: z.ZodTypeAny } }; description?: string };
|
|
21
|
-
};
|
|
22
|
-
responses: Record<number | string, { description?: string; content?: { "application/json": { schema: z.ZodTypeAny } } }>;
|
|
23
|
-
};
|
|
8
|
+
export type { HttpMethod, RouteConfig };
|
|
24
9
|
|
|
25
10
|
export type NexwireRouter = Omit<ExpressRouter, "route"> & {
|
|
26
11
|
group: (...middlewares: RequestHandler[]) => NexwireRouter;
|
|
@@ -35,7 +20,7 @@ export function createRoute<T extends RouteConfig>(config: T): T {
|
|
|
35
20
|
return config;
|
|
36
21
|
}
|
|
37
22
|
|
|
38
|
-
export const jsonContent = <T extends z.
|
|
23
|
+
export const jsonContent = <T extends z.ZodType>(schema: T, description: string) => ({
|
|
39
24
|
content: {
|
|
40
25
|
"application/json": {
|
|
41
26
|
schema
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export type HttpMethod = "get" | "post" | "put" | "patch" | "delete" | "options" | "head" | "trace";
|
|
4
|
+
|
|
5
|
+
export type RouteConfig = {
|
|
6
|
+
path: string;
|
|
7
|
+
method: HttpMethod;
|
|
8
|
+
tags?: string[];
|
|
9
|
+
summary?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
request?: {
|
|
12
|
+
params?: z.ZodType;
|
|
13
|
+
query?: z.ZodType;
|
|
14
|
+
headers?: z.ZodType;
|
|
15
|
+
cookies?: z.ZodType;
|
|
16
|
+
body?: { content: { "application/json": { schema: z.ZodType } }; description?: string };
|
|
17
|
+
};
|
|
18
|
+
responses: Record<number | string, { description?: string; content?: { "application/json": { schema: z.ZodType } } }>;
|
|
19
|
+
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import createError from "http-errors";
|
|
2
|
-
import type { z } from "zod";
|
|
3
|
-
|
|
4
|
-
export async function validate<T extends z.
|
|
5
|
-
const result = await schema.safeParseAsync(data);
|
|
6
|
-
|
|
7
|
-
if (!result.success) {
|
|
8
|
-
throw createError(422, "Validation failed", {
|
|
9
|
-
success: false,
|
|
10
|
-
error: {
|
|
11
|
-
name: result.error.name,
|
|
12
|
-
issues: result.error.issues
|
|
13
|
-
}
|
|
14
|
-
} as never);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return result.data;
|
|
18
|
-
}
|
|
1
|
+
import createError from "http-errors";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export async function validate<T extends z.ZodType>(schema: T, data: unknown): Promise<z.infer<T>> {
|
|
5
|
+
const result = await schema.safeParseAsync(data);
|
|
6
|
+
|
|
7
|
+
if (!result.success) {
|
|
8
|
+
throw createError(422, "Validation failed", {
|
|
9
|
+
success: false,
|
|
10
|
+
error: {
|
|
11
|
+
name: result.error.name,
|
|
12
|
+
issues: result.error.issues
|
|
13
|
+
}
|
|
14
|
+
} as never);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return result.data;
|
|
18
|
+
}
|
|
@@ -1,47 +1,41 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { hasFlag } from "../utils/flags.mjs";
|
|
4
|
-
import { assertName,
|
|
5
|
-
|
|
6
|
-
const stubsRoot = path.resolve(import.meta.dirname, "../stubs");
|
|
7
|
-
|
|
8
|
-
/** Read a stub file and replace {{key}} placeholders, removing unfilled ones. */
|
|
9
|
-
async function stub(name, values = {}) {
|
|
10
|
-
let content = await fs.readFile(path.join(stubsRoot, name), "utf8");
|
|
11
|
-
for (const [key, value] of Object.entries(values)) {
|
|
12
|
-
content = content.replaceAll(`{{${key}}}`, String(value));
|
|
13
|
-
}
|
|
14
|
-
return content.replace(/\{\{[A-Z0-9_]+\}\}/g, "");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
function
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const content = await stub("middleware/name.ts.stub", { name, camel });
|
|
43
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
44
|
-
await fs.writeFile(filePath, content);
|
|
45
|
-
console.log(`Middleware ready: ${path.relative(process.cwd(), filePath)}`);
|
|
46
|
-
console.log(`Apply it to a route group: group(${camel}Middleware)`);
|
|
47
|
-
}
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { hasFlag } from "../utils/flags.mjs";
|
|
4
|
+
import { assertName, camelCase } from "../utils/naming.mjs";
|
|
5
|
+
|
|
6
|
+
const stubsRoot = path.resolve(import.meta.dirname, "../stubs");
|
|
7
|
+
|
|
8
|
+
/** Read a stub file and replace {{key}} placeholders, removing unfilled ones. */
|
|
9
|
+
async function stub(name, values = {}) {
|
|
10
|
+
let content = await fs.readFile(path.join(stubsRoot, name), "utf8");
|
|
11
|
+
for (const [key, value] of Object.entries(values)) {
|
|
12
|
+
content = content.replaceAll(`{{${key}}}`, String(value));
|
|
13
|
+
}
|
|
14
|
+
return content.replace(/\{\{[A-Z0-9_]+\}\}/g, "");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Generate a middleware file in src/middlewares. */
|
|
18
|
+
export async function makeMiddleware(rawName, flags = []) {
|
|
19
|
+
const name = assertName(rawName, "Middleware name");
|
|
20
|
+
const camel = camelCase(name);
|
|
21
|
+
const dryRun = hasFlag(flags, "--dry-run");
|
|
22
|
+
const force = hasFlag(flags, "--force") || hasFlag(flags, "--yes");
|
|
23
|
+
|
|
24
|
+
const filePath = path.resolve(process.cwd(), "src/middlewares", `${name}-middleware.ts`);
|
|
25
|
+
if (dryRun) return;
|
|
26
|
+
|
|
27
|
+
let exists = false;
|
|
28
|
+
try {
|
|
29
|
+
await fs.access(filePath);
|
|
30
|
+
exists = true;
|
|
31
|
+
} catch {}
|
|
32
|
+
if (exists && !force) {
|
|
33
|
+
throw new Error(`Middleware already exists: ${path.relative(process.cwd(), filePath)}. Re-run with --force to overwrite.`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const content = await stub("middleware/name.ts.stub", { name, camel });
|
|
37
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
38
|
+
await fs.writeFile(filePath, content);
|
|
39
|
+
console.log(`Middleware ready: ${path.relative(process.cwd(), filePath)}`);
|
|
40
|
+
console.log(`Apply it to a route group: group(${camel}Middleware)`);
|
|
41
|
+
}
|