@razvan11/paladin 1.1.3 → 1.1.5
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 +211 -7
- package/dist/app/app.d.ts +0 -1
- package/dist/functions/controller.d.ts +1 -1
- package/dist/functions/render.d.ts +0 -4
- package/dist/index.js +4 -8
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,15 +1,219 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @razvan11/paladin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A modern, decorator-based backend framework for Bun with dependency injection, controller registration, and WebSocket support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Decorator-based routing** - Clean, expressive route definitions
|
|
8
|
+
- **Dependency Injection** - Powered by Inversify
|
|
9
|
+
- **Built for Bun** - Leverages Bun's performance
|
|
10
|
+
- **WebSocket support** - First-class WebSocket handling
|
|
11
|
+
- **React SSR** - Server-side rendering utilities
|
|
12
|
+
- **Middleware support** - Per-route middleware functions
|
|
13
|
+
- **Static file serving** - Built-in asset handling
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
4
16
|
|
|
5
17
|
```bash
|
|
6
|
-
bun
|
|
18
|
+
bun add @razvan11/paladin
|
|
7
19
|
```
|
|
8
20
|
|
|
9
|
-
|
|
21
|
+
## Quick Start
|
|
10
22
|
|
|
11
|
-
```
|
|
12
|
-
|
|
23
|
+
```typescript
|
|
24
|
+
import { App, controller, get, post, service, inject } from '@razvan11/paladin';
|
|
25
|
+
|
|
26
|
+
@service()
|
|
27
|
+
class UserService {
|
|
28
|
+
getUsers() {
|
|
29
|
+
return [{ id: 1, name: 'John' }];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@controller('/users')
|
|
34
|
+
class UserController {
|
|
35
|
+
constructor(@inject(UserService) private userService: UserService) {}
|
|
36
|
+
|
|
37
|
+
@get('/')
|
|
38
|
+
async list(c: Context) {
|
|
39
|
+
return c.json(this.userService.getUsers());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@post('/')
|
|
43
|
+
async create(c: Context) {
|
|
44
|
+
const body = await c.req.json();
|
|
45
|
+
return c.json({ created: body });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const app = new App({ name: 'MyApp' });
|
|
50
|
+
app.registerController(UserController);
|
|
51
|
+
app.run();
|
|
13
52
|
```
|
|
14
53
|
|
|
15
|
-
|
|
54
|
+
## Decorators
|
|
55
|
+
|
|
56
|
+
### Controller Decorators
|
|
57
|
+
|
|
58
|
+
| Decorator | Description |
|
|
59
|
+
|-----------|-------------|
|
|
60
|
+
| `@controller(prefix)` | Marks a class as a controller with a route prefix |
|
|
61
|
+
| `@get(path, ...middlewares)` | Handles GET requests |
|
|
62
|
+
| `@post(path, ...middlewares)` | Handles POST requests |
|
|
63
|
+
| `@put(path, ...middlewares)` | Handles PUT requests |
|
|
64
|
+
| `@patch(path, ...middlewares)` | Handles PATCH requests |
|
|
65
|
+
| `@del(path, ...middlewares)` | Handles DELETE requests |
|
|
66
|
+
| `@options(path, ...middlewares)` | Handles OPTIONS requests |
|
|
67
|
+
| `@all(path, ...middlewares)` | Handles all HTTP methods |
|
|
68
|
+
|
|
69
|
+
### Service Decorators
|
|
70
|
+
|
|
71
|
+
| Decorator | Description |
|
|
72
|
+
|-----------|-------------|
|
|
73
|
+
| `@service()` | Marks a class as a service (singleton) |
|
|
74
|
+
| `@repository()` | Marks a class as a repository (singleton) |
|
|
75
|
+
| `@database(options)` | Marks a class as a database connection |
|
|
76
|
+
|
|
77
|
+
### WebSocket Decorators
|
|
78
|
+
|
|
79
|
+
| Decorator | Description |
|
|
80
|
+
|-----------|-------------|
|
|
81
|
+
| `@websocket(path)` | Marks a class as a WebSocket handler |
|
|
82
|
+
| `@onMessage()` | Handles incoming messages |
|
|
83
|
+
| `@onOpen()` | Handles connection open |
|
|
84
|
+
| `@onClose()` | Handles connection close |
|
|
85
|
+
| `@onDrain()` | Handles backpressure drain |
|
|
86
|
+
|
|
87
|
+
## Dependency Injection
|
|
88
|
+
|
|
89
|
+
Use the `@inject()` decorator to inject dependencies into your controllers and services:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
@controller('/api')
|
|
93
|
+
class ApiController {
|
|
94
|
+
constructor(
|
|
95
|
+
@inject(UserService) private userService: UserService,
|
|
96
|
+
@inject(AuthService) private authService: AuthService,
|
|
97
|
+
) {}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Middleware
|
|
102
|
+
|
|
103
|
+
Add middleware to individual routes:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const authMiddleware = async (c: Context, next: Next) => {
|
|
107
|
+
const token = c.req.header('Authorization');
|
|
108
|
+
if (!token) return c.json({ error: 'Unauthorized' }, 401);
|
|
109
|
+
await next();
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
@controller('/protected')
|
|
113
|
+
class ProtectedController {
|
|
114
|
+
@get('/', authMiddleware)
|
|
115
|
+
async secure(c: Context) {
|
|
116
|
+
return c.json({ message: 'Secret data' });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## WebSockets
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { websocket, onMessage, onOpen, onClose } from '@razvan11/paladin';
|
|
125
|
+
|
|
126
|
+
@websocket('/chat')
|
|
127
|
+
class ChatHandler {
|
|
128
|
+
@onOpen()
|
|
129
|
+
handleOpen(ws: ServerWebSocket) {
|
|
130
|
+
console.log('Client connected');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@onMessage()
|
|
134
|
+
handleMessage(ws: ServerWebSocket, message: string | Buffer) {
|
|
135
|
+
ws.send(`Echo: ${message}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@onClose()
|
|
139
|
+
handleClose(ws: ServerWebSocket) {
|
|
140
|
+
console.log('Client disconnected');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const app = new App({ name: 'ChatApp' });
|
|
145
|
+
app.registerWebSocket(ChatHandler);
|
|
146
|
+
app.run();
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Static Files
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const app = new App({ name: 'MyApp' });
|
|
153
|
+
|
|
154
|
+
app.serveStatic({
|
|
155
|
+
path: '/static',
|
|
156
|
+
root: './public'
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Use the `asset()` helper to generate URLs:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { asset } from '@razvan11/paladin';
|
|
164
|
+
|
|
165
|
+
asset('dist', 'app.js'); // Returns '/static/dist/app.js'
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## React SSR
|
|
169
|
+
|
|
170
|
+
Render React components on the server:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { render, LayoutView } from '@razvan11/paladin';
|
|
174
|
+
|
|
175
|
+
@controller('/')
|
|
176
|
+
class IndexController {
|
|
177
|
+
@get('/')
|
|
178
|
+
index(c: Context) {
|
|
179
|
+
return render(c, LayoutView, {
|
|
180
|
+
title: 'My App',
|
|
181
|
+
scripts: [asset('dist', 'app.js')],
|
|
182
|
+
styles: [asset('dist', 'app.css')],
|
|
183
|
+
children: <div id="root" />,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Configuration
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const app = new App({
|
|
193
|
+
name: 'MyApp',
|
|
194
|
+
cors: ['https://example.com'], // CORS origins (default: ['*'])
|
|
195
|
+
validators: myValidators, // Optional validators
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Environment variables:
|
|
200
|
+
- `PORT` - Server port (default: 3000)
|
|
201
|
+
- `APP_ENV` - Environment mode (`local`, `development`, `staging`, `production`)
|
|
202
|
+
|
|
203
|
+
## API Reference
|
|
204
|
+
|
|
205
|
+
### App
|
|
206
|
+
|
|
207
|
+
| Method | Description |
|
|
208
|
+
|--------|-------------|
|
|
209
|
+
| `registerController(Controller)` | Register a controller class |
|
|
210
|
+
| `registerControllers(...Controllers)` | Register multiple controllers |
|
|
211
|
+
| `registerWebSocket(WSHandler)` | Register a WebSocket handler |
|
|
212
|
+
| `registerWebSockets(...WSHandlers)` | Register multiple WebSocket handlers |
|
|
213
|
+
| `serveStatic(options)` | Serve static files |
|
|
214
|
+
| `getAppInstance()` | Get the underlying Hono instance |
|
|
215
|
+
| `run()` | Start the server |
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT © Razvan
|
package/dist/app/app.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface RouteDefinition {
|
|
|
7
7
|
handlerName: string | symbol;
|
|
8
8
|
middlewares: MiddlewareHandler[];
|
|
9
9
|
}
|
|
10
|
-
export type MiddlewareHandler = (c: Context, next: Next) => Promise<Response | void> | Response | void;
|
|
10
|
+
export type MiddlewareHandler = (c: Context, next: Next) => Promise<Response> | Promise<void> | Response | void;
|
|
11
11
|
/**
|
|
12
12
|
* @controller decorator
|
|
13
13
|
* Marks a class as a controller and registers it with the DI container
|
|
@@ -18,10 +18,6 @@ export declare function render<P extends object>(c: Context, Component: FC<P>, p
|
|
|
18
18
|
* @returns HTML response
|
|
19
19
|
*/
|
|
20
20
|
export declare function renderElement(c: Context, element: ReactElement): Response;
|
|
21
|
-
/**
|
|
22
|
-
* Create a render function bound to a context
|
|
23
|
-
* Useful for cleaner controller code
|
|
24
|
-
*/
|
|
25
21
|
export declare function createRenderer(c: Context): {
|
|
26
22
|
render: <P extends object>(Component: FC<P>, props?: P) => Response;
|
|
27
23
|
renderElement: (element: ReactElement) => Response;
|
package/dist/index.js
CHANGED
|
@@ -19887,7 +19887,6 @@ class App {
|
|
|
19887
19887
|
cors;
|
|
19888
19888
|
logger;
|
|
19889
19889
|
wsHandlers = new Map;
|
|
19890
|
-
server = null;
|
|
19891
19890
|
constructor(options2) {
|
|
19892
19891
|
this.app = new Hono2;
|
|
19893
19892
|
this.name = options2.name;
|
|
@@ -19925,8 +19924,6 @@ class App {
|
|
|
19925
19924
|
} else {
|
|
19926
19925
|
app[route.method](fullPath, handler);
|
|
19927
19926
|
}
|
|
19928
|
-
if (Bun.env.APP_ENV === "local")
|
|
19929
|
-
this.logger.info(` ${route.method.toUpperCase().padEnd(7)} ${fullPath}`);
|
|
19930
19927
|
}
|
|
19931
19928
|
return this;
|
|
19932
19929
|
}
|
|
@@ -19976,9 +19973,9 @@ class App {
|
|
|
19976
19973
|
});
|
|
19977
19974
|
try {
|
|
19978
19975
|
const server = Bun.serve({
|
|
19979
|
-
port: 3000,
|
|
19980
|
-
hostname: "
|
|
19981
|
-
development:
|
|
19976
|
+
port: Bun.env.PORT ? parseInt(Bun.env.PORT, 10) : 3000,
|
|
19977
|
+
hostname: "0.0.0.0",
|
|
19978
|
+
development: Bun.env.APP_ENV === "local",
|
|
19982
19979
|
fetch(req, server2) {
|
|
19983
19980
|
const url = new URL(req.url);
|
|
19984
19981
|
if (wsPaths.has(url.pathname)) {
|
|
@@ -20015,7 +20012,6 @@ class App {
|
|
|
20015
20012
|
}
|
|
20016
20013
|
}
|
|
20017
20014
|
});
|
|
20018
|
-
this.server = server;
|
|
20019
20015
|
this.logger.success(`Server started on port ${server.port}`);
|
|
20020
20016
|
return server;
|
|
20021
20017
|
} catch (error) {
|
|
@@ -20076,7 +20072,7 @@ function getDataSourceForCLI(DatabaseClass, url) {
|
|
|
20076
20072
|
const source = instance.getSource();
|
|
20077
20073
|
if (options2?.migrations) {
|
|
20078
20074
|
source.setOptions({
|
|
20079
|
-
migrations: [options2.migrations
|
|
20075
|
+
migrations: [`${options2.migrations}/*.ts`],
|
|
20080
20076
|
migrationsTableName: options2.migrationsTable || "migrations"
|
|
20081
20077
|
});
|
|
20082
20078
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@razvan11/paladin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "A Bun-based backend framework with decorators, dependency injection, and controller registration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -64,4 +64,5 @@
|
|
|
64
64
|
"reflect-metadata": "^0.2.2",
|
|
65
65
|
"zod": "^4.2.1"
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
}
|
|
68
|
+
|