@meridianjs/framework 0.1.11 → 0.1.13
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 +154 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.js +6 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# @meridianjs/framework
|
|
2
|
+
|
|
3
|
+
The MeridianJS application runtime. Handles bootstrapping, the Awilix DI container, Express server setup, file-based route / subscriber / job / link loading, plugin registration, rate limiting, input validation, and SSE.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @meridianjs/framework
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// src/main.ts
|
|
15
|
+
import { bootstrap } from "@meridianjs/framework"
|
|
16
|
+
import path from "node:path"
|
|
17
|
+
import { fileURLToPath } from "node:url"
|
|
18
|
+
|
|
19
|
+
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
20
|
+
const app = await bootstrap({ rootDir })
|
|
21
|
+
await app.start()
|
|
22
|
+
|
|
23
|
+
process.on("SIGTERM", async () => { await app.stop(); process.exit(0) })
|
|
24
|
+
process.on("SIGINT", async () => { await app.stop(); process.exit(0) })
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`bootstrap()` reads `meridian.config.ts` from `rootDir`, loads all modules and plugins, registers file-based routes and subscribers, and returns a `MeridianApp` handle.
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
Define your config in `meridian.config.ts` at the project root:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { defineConfig } from "@meridianjs/framework"
|
|
35
|
+
import dotenv from "dotenv"
|
|
36
|
+
dotenv.config()
|
|
37
|
+
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
projectConfig: {
|
|
40
|
+
databaseUrl: process.env.DATABASE_URL!,
|
|
41
|
+
jwtSecret: process.env.JWT_SECRET!,
|
|
42
|
+
httpPort: Number(process.env.PORT) || 9000,
|
|
43
|
+
cors: {
|
|
44
|
+
origin: process.env.CORS_ORIGIN ?? "http://localhost:5174",
|
|
45
|
+
credentials: true,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
modules: [
|
|
49
|
+
{ resolve: "@meridianjs/event-bus-local" },
|
|
50
|
+
{ resolve: "@meridianjs/job-queue-local" },
|
|
51
|
+
],
|
|
52
|
+
plugins: [
|
|
53
|
+
{ resolve: "@meridianjs/meridian" },
|
|
54
|
+
],
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## File-based Routes
|
|
59
|
+
|
|
60
|
+
Create route files under `src/api/` and export named HTTP method handlers:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
src/api/admin/projects/route.ts → GET /admin/projects, POST /admin/projects
|
|
64
|
+
src/api/admin/projects/[id]/route.ts → GET /admin/projects/:id, PUT, DELETE
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// src/api/admin/projects/route.ts
|
|
69
|
+
import type { Request, Response } from "express"
|
|
70
|
+
|
|
71
|
+
export const GET = async (req: Request, res: Response) => {
|
|
72
|
+
const svc = req.scope.resolve("projectModuleService") as any
|
|
73
|
+
const projects = await svc.listProjects()
|
|
74
|
+
res.json({ projects })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const POST = async (req: Request, res: Response) => {
|
|
78
|
+
const svc = req.scope.resolve("projectModuleService") as any
|
|
79
|
+
const project = await svc.createProject(req.body)
|
|
80
|
+
res.status(201).json({ project })
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Middleware
|
|
85
|
+
|
|
86
|
+
Define route-level middleware in `src/api/middlewares.ts`:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { authenticateJWT } from "@meridianjs/auth"
|
|
90
|
+
import { apiRateLimit, authRateLimit } from "@meridianjs/framework"
|
|
91
|
+
|
|
92
|
+
export default {
|
|
93
|
+
routes: [
|
|
94
|
+
{ matcher: "/auth", middlewares: [authRateLimit] },
|
|
95
|
+
{ matcher: "/admin", middlewares: [apiRateLimit, authenticateJWT] },
|
|
96
|
+
],
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Rate Limiting
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { authRateLimit, oauthRateLimit, apiRateLimit } from "@meridianjs/framework"
|
|
104
|
+
|
|
105
|
+
// authRateLimit — 20 req / 15 min (for /auth endpoints)
|
|
106
|
+
// oauthRateLimit — 30 req / 15 min (for OAuth flows)
|
|
107
|
+
// apiRateLimit — 200 req / 15 min (for /admin endpoints)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Input Validation
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { validate } from "@meridianjs/framework"
|
|
114
|
+
import { z } from "zod"
|
|
115
|
+
|
|
116
|
+
const schema = z.object({ name: z.string().min(1), color: z.string() })
|
|
117
|
+
|
|
118
|
+
export const POST = async (req: Request, res: Response) => {
|
|
119
|
+
const body = validate(req.body, schema) // throws 400 with field errors on failure
|
|
120
|
+
// body is typed as z.infer<typeof schema>
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## SSE — Server-Sent Events
|
|
125
|
+
|
|
126
|
+
Push real-time updates to connected dashboard clients:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { sseManager } from "@meridianjs/framework"
|
|
130
|
+
|
|
131
|
+
// In a subscriber or route handler:
|
|
132
|
+
sseManager.broadcast({ type: "issue.updated", payload: { id: issue.id } })
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Clients connect via `GET /admin/events`. The dashboard uses this to invalidate TanStack Query caches without polling.
|
|
136
|
+
|
|
137
|
+
## Exports
|
|
138
|
+
|
|
139
|
+
| Export | Description |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `bootstrap(options)` | Boot the application, return `MeridianApp` |
|
|
142
|
+
| `defineConfig(config)` | Type-safe config helper |
|
|
143
|
+
| `defineMiddlewares(config)` | Type-safe middleware config helper |
|
|
144
|
+
| `createMeridianContainer()` | Create a raw Awilix container |
|
|
145
|
+
| `ConsoleLogger` | Default logger implementation |
|
|
146
|
+
| `validate(data, schema)` | Zod validation with auto 400 response |
|
|
147
|
+
| `authRateLimit` | Rate limiter for auth routes |
|
|
148
|
+
| `apiRateLimit` | Rate limiter for API routes |
|
|
149
|
+
| `sseManager` | SSE broadcast singleton |
|
|
150
|
+
| `loadRoutes`, `loadModules`, `loadSubscribers`, `loadJobs`, `loadLinks`, `loadPlugins` | Low-level loaders (plugin authors) |
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -173,8 +173,20 @@ declare class ConsoleLogger implements ILogger {
|
|
|
173
173
|
|
|
174
174
|
declare function createServer(container: MeridianContainer, config: MeridianConfig): Express;
|
|
175
175
|
|
|
176
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* Strict limiter for password-based auth endpoints (login, register).
|
|
178
|
+
* 10 requests per minute per IP — guards against brute-force attacks.
|
|
179
|
+
*/
|
|
177
180
|
declare const authRateLimit: express_rate_limit.RateLimitRequestHandler;
|
|
181
|
+
/**
|
|
182
|
+
* Loose limiter for OAuth redirect/callback endpoints.
|
|
183
|
+
* 30 requests per minute per IP — a complete OAuth flow (initiate → callback →
|
|
184
|
+
* exchange) consumes 3 requests, so 30/min allows ~10 flows per minute.
|
|
185
|
+
* OAuth routes are not brute-forceable at the application level because they
|
|
186
|
+
* require a real interaction with the external identity provider and a
|
|
187
|
+
* cryptographic CSRF nonce.
|
|
188
|
+
*/
|
|
189
|
+
declare const oauthRateLimit: express_rate_limit.RateLimitRequestHandler;
|
|
178
190
|
/** General API limiter: 300 requests per minute per IP. */
|
|
179
191
|
declare const apiRateLimit: express_rate_limit.RateLimitRequestHandler;
|
|
180
192
|
|
|
@@ -211,4 +223,4 @@ declare class SseManager {
|
|
|
211
223
|
/** Singleton shared across all routes and subscribers. */
|
|
212
224
|
declare const sseManager: SseManager;
|
|
213
225
|
|
|
214
|
-
export { type BootstrapOptions, ConsoleLogger, type MeridianApp, type MiddlewareRoute, type MiddlewaresConfig, SseManager, apiRateLimit, authRateLimit, bootstrap, createMeridianContainer, createServer, defineConfig, defineMiddlewares, loadConfig, loadJobs, loadLinks, loadModules, loadPlugins, loadRoutes, loadSubscribers, resolveModuleDefinition, sseManager, validate };
|
|
226
|
+
export { type BootstrapOptions, ConsoleLogger, type MeridianApp, type MiddlewareRoute, type MiddlewaresConfig, SseManager, apiRateLimit, authRateLimit, bootstrap, createMeridianContainer, createServer, defineConfig, defineMiddlewares, loadConfig, loadJobs, loadLinks, loadModules, loadPlugins, loadRoutes, loadSubscribers, oauthRateLimit, resolveModuleDefinition, sseManager, validate };
|
package/dist/index.js
CHANGED
|
@@ -730,6 +730,11 @@ var authRateLimit = rateLimit({
|
|
|
730
730
|
max: 10,
|
|
731
731
|
...sharedOpts
|
|
732
732
|
});
|
|
733
|
+
var oauthRateLimit = rateLimit({
|
|
734
|
+
windowMs: 6e4,
|
|
735
|
+
max: 30,
|
|
736
|
+
...sharedOpts
|
|
737
|
+
});
|
|
733
738
|
var apiRateLimit = rateLimit({
|
|
734
739
|
windowMs: 6e4,
|
|
735
740
|
max: 300,
|
|
@@ -820,6 +825,7 @@ export {
|
|
|
820
825
|
loadPlugins,
|
|
821
826
|
loadRoutes,
|
|
822
827
|
loadSubscribers,
|
|
828
|
+
oauthRateLimit,
|
|
823
829
|
resolveModuleDefinition,
|
|
824
830
|
sseManager,
|
|
825
831
|
validate
|
package/package.json
CHANGED