@ooneex/middleware 0.0.1 → 0.4.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 CHANGED
@@ -1 +1,413 @@
1
1
  # @ooneex/middleware
2
+
3
+ A middleware framework for TypeScript applications with decorators for request/response pipeline processing. This package provides interfaces and utilities for creating HTTP and WebSocket middleware that intercept and transform requests before they reach controllers.
4
+
5
+ ![Bun](https://img.shields.io/badge/Bun-Compatible-orange?style=flat-square&logo=bun)
6
+ ![Deno](https://img.shields.io/badge/Deno-Compatible-blue?style=flat-square&logo=deno)
7
+ ![Node.js](https://img.shields.io/badge/Node.js-Compatible-green?style=flat-square&logo=node.js)
8
+ ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)
9
+ ![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
10
+
11
+ ## Features
12
+
13
+ ✅ **HTTP Middleware** - Intercept and process HTTP requests and responses
14
+
15
+ ✅ **WebSocket Middleware** - Handle WebSocket connection processing
16
+
17
+ ✅ **Decorator Support** - Register middleware with decorators for clean code
18
+
19
+ ✅ **Context Access** - Full access to request, response, and application context
20
+
21
+ ✅ **Container Integration** - Works seamlessly with dependency injection
22
+
23
+ ✅ **Type-Safe** - Full TypeScript support with proper type definitions
24
+
25
+ ## Installation
26
+
27
+ ### Bun
28
+ ```bash
29
+ bun add @ooneex/middleware
30
+ ```
31
+
32
+ ### pnpm
33
+ ```bash
34
+ pnpm add @ooneex/middleware
35
+ ```
36
+
37
+ ### Yarn
38
+ ```bash
39
+ yarn add @ooneex/middleware
40
+ ```
41
+
42
+ ### npm
43
+ ```bash
44
+ npm install @ooneex/middleware
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ### Basic HTTP Middleware
50
+
51
+ ```typescript
52
+ import { decorator } from '@ooneex/middleware';
53
+ import type { IMiddleware, ContextType } from '@ooneex/middleware';
54
+
55
+ @decorator.middleware()
56
+ class LoggingMiddleware implements IMiddleware {
57
+ public async handle(context: ContextType): Promise<ContextType> {
58
+ console.log(`${context.method} ${context.request.path}`);
59
+
60
+ // Continue to next middleware/controller
61
+ return context;
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### Authentication Middleware
67
+
68
+ ```typescript
69
+ import { decorator } from '@ooneex/middleware';
70
+ import type { IMiddleware, ContextType } from '@ooneex/middleware';
71
+ import { Jwt } from '@ooneex/jwt';
72
+
73
+ @decorator.middleware()
74
+ class AuthMiddleware implements IMiddleware {
75
+ private readonly jwt = new Jwt();
76
+
77
+ public async handle(context: ContextType): Promise<ContextType> {
78
+ const authHeader = context.header.get('Authorization');
79
+
80
+ if (!authHeader?.startsWith('Bearer ')) {
81
+ context.response.exception('Missing authorization header', {
82
+ status: 401
83
+ });
84
+ return context;
85
+ }
86
+
87
+ const token = authHeader.substring(7);
88
+ const isValid = await this.jwt.isValid(token);
89
+
90
+ if (!isValid) {
91
+ context.response.exception('Invalid or expired token', {
92
+ status: 401
93
+ });
94
+ return context;
95
+ }
96
+
97
+ // Set user in context
98
+ const payload = this.jwt.getPayload<{ userId: string; role: string }>(token);
99
+ context.user = { id: payload.userId, role: payload.role };
100
+
101
+ return context;
102
+ }
103
+ }
104
+ ```
105
+
106
+ ### WebSocket Middleware
107
+
108
+ ```typescript
109
+ import { decorator } from '@ooneex/middleware';
110
+ import type { ISocketMiddleware, ContextType as SocketContextType } from '@ooneex/middleware';
111
+
112
+ @decorator.middleware()
113
+ class SocketAuthMiddleware implements ISocketMiddleware {
114
+ public async handle(context: SocketContextType): Promise<SocketContextType> {
115
+ const token = context.queries.token;
116
+
117
+ if (!token) {
118
+ context.response.exception('Missing authentication token', {
119
+ status: 401
120
+ });
121
+ return context;
122
+ }
123
+
124
+ // Validate token and set user
125
+ context.user = await this.validateToken(token);
126
+
127
+ return context;
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### Request Validation Middleware
133
+
134
+ ```typescript
135
+ import { decorator } from '@ooneex/middleware';
136
+ import type { IMiddleware, ContextType } from '@ooneex/middleware';
137
+
138
+ @decorator.middleware()
139
+ class ValidationMiddleware implements IMiddleware {
140
+ public async handle(context: ContextType): Promise<ContextType> {
141
+ // Validate content type for POST/PUT requests
142
+ if (['POST', 'PUT', 'PATCH'].includes(context.method)) {
143
+ const contentType = context.header.get('Content-Type');
144
+
145
+ if (!contentType?.includes('application/json')) {
146
+ context.response.exception('Content-Type must be application/json', {
147
+ status: 415
148
+ });
149
+ return context;
150
+ }
151
+ }
152
+
153
+ return context;
154
+ }
155
+ }
156
+ ```
157
+
158
+ ### Registering Middleware with App
159
+
160
+ ```typescript
161
+ import { App } from '@ooneex/app';
162
+ import { AuthMiddleware, LoggingMiddleware } from './middleware';
163
+
164
+ const app = new App({
165
+ middlewares: [LoggingMiddleware, AuthMiddleware],
166
+ // ... other config
167
+ });
168
+
169
+ await app.run();
170
+ ```
171
+
172
+ ## API Reference
173
+
174
+ ### Interfaces
175
+
176
+ #### `IMiddleware`
177
+
178
+ Interface for HTTP middleware implementations.
179
+
180
+ ```typescript
181
+ interface IMiddleware<T extends ContextConfigType = ContextConfigType> {
182
+ handle: (context: ContextType<T>) => Promise<ContextType<T>> | ContextType<T>;
183
+ }
184
+ ```
185
+
186
+ **Methods:**
187
+
188
+ ##### `handle(context: ContextType): Promise<ContextType> | ContextType`
189
+
190
+ Processes the request context and returns the (potentially modified) context.
191
+
192
+ **Parameters:**
193
+ - `context` - The request context containing request, response, and app data
194
+
195
+ **Returns:** The context object (modified or unchanged)
196
+
197
+ **Example:**
198
+ ```typescript
199
+ public async handle(context: ContextType): Promise<ContextType> {
200
+ // Pre-processing
201
+ console.log('Before controller');
202
+
203
+ // Return context to continue processing
204
+ return context;
205
+ }
206
+ ```
207
+
208
+ ---
209
+
210
+ #### `ISocketMiddleware`
211
+
212
+ Interface for WebSocket middleware implementations.
213
+
214
+ ```typescript
215
+ interface ISocketMiddleware<T extends SocketContextConfigType = SocketContextConfigType> {
216
+ handle: (context: SocketContextType<T>) => Promise<SocketContextType<T>> | SocketContextType<T>;
217
+ }
218
+ ```
219
+
220
+ **Methods:**
221
+
222
+ ##### `handle(context: SocketContextType): Promise<SocketContextType> | SocketContextType`
223
+
224
+ Processes the WebSocket context and returns the (potentially modified) context.
225
+
226
+ **Parameters:**
227
+ - `context` - The WebSocket context containing connection data and channel methods
228
+
229
+ **Returns:** The context object (modified or unchanged)
230
+
231
+ ### Types
232
+
233
+ #### `MiddlewareClassType`
234
+
235
+ ```typescript
236
+ type MiddlewareClassType = new (...args: any[]) => IMiddleware;
237
+ ```
238
+
239
+ #### `SocketMiddlewareClassType`
240
+
241
+ ```typescript
242
+ type SocketMiddlewareClassType = new (...args: any[]) => ISocketMiddleware;
243
+ ```
244
+
245
+ ### Decorators
246
+
247
+ #### `@decorator.middleware()`
248
+
249
+ Registers a class as middleware with the dependency injection container.
250
+
251
+ **Example:**
252
+ ```typescript
253
+ @decorator.middleware()
254
+ class MyMiddleware implements IMiddleware {
255
+ // Implementation
256
+ }
257
+ ```
258
+
259
+ ## Advanced Usage
260
+
261
+ ### CORS Middleware
262
+
263
+ ```typescript
264
+ import { decorator } from '@ooneex/middleware';
265
+ import type { IMiddleware, ContextType } from '@ooneex/middleware';
266
+
267
+ @decorator.middleware()
268
+ class CorsMiddleware implements IMiddleware {
269
+ private readonly allowedOrigins = ['https://example.com', 'https://app.example.com'];
270
+
271
+ public async handle(context: ContextType): Promise<ContextType> {
272
+ const origin = context.header.get('Origin');
273
+
274
+ if (origin && this.allowedOrigins.includes(origin)) {
275
+ context.response.header.set('Access-Control-Allow-Origin', origin);
276
+ context.response.header.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
277
+ context.response.header.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
278
+ }
279
+
280
+ // Handle preflight requests
281
+ if (context.method === 'OPTIONS') {
282
+ context.response.json({}, 204);
283
+ return context;
284
+ }
285
+
286
+ return context;
287
+ }
288
+ }
289
+ ```
290
+
291
+ ### Rate Limiting Middleware
292
+
293
+ ```typescript
294
+ import { decorator } from '@ooneex/middleware';
295
+ import type { IMiddleware, ContextType } from '@ooneex/middleware';
296
+ import { container } from '@ooneex/container';
297
+ import type { IRateLimiter } from '@ooneex/rate-limit';
298
+
299
+ @decorator.middleware()
300
+ class RateLimitMiddleware implements IMiddleware {
301
+ public async handle(context: ContextType): Promise<ContextType> {
302
+ const rateLimiter = container.get<IRateLimiter>('rateLimiter');
303
+ const clientIp = context.ip || 'unknown';
304
+
305
+ const result = await rateLimiter.check(clientIp, 100, 60); // 100 requests per minute
306
+
307
+ context.response.header.set('X-RateLimit-Limit', result.total.toString());
308
+ context.response.header.set('X-RateLimit-Remaining', result.remaining.toString());
309
+ context.response.header.set('X-RateLimit-Reset', result.resetAt.toISOString());
310
+
311
+ if (result.limited) {
312
+ context.response.exception('Too many requests', {
313
+ status: 429,
314
+ data: { retryAfter: result.resetAt }
315
+ });
316
+ }
317
+
318
+ return context;
319
+ }
320
+ }
321
+ ```
322
+
323
+ ### Middleware with Dependencies
324
+
325
+ ```typescript
326
+ import { decorator } from '@ooneex/middleware';
327
+ import { injectable, inject } from '@ooneex/container';
328
+ import type { IMiddleware, ContextType } from '@ooneex/middleware';
329
+ import type { ILogger } from '@ooneex/logger';
330
+
331
+ @decorator.middleware()
332
+ @injectable()
333
+ class LoggingMiddleware implements IMiddleware {
334
+ constructor(
335
+ @inject('logger') private readonly logger: ILogger
336
+ ) {}
337
+
338
+ public async handle(context: ContextType): Promise<ContextType> {
339
+ const start = Date.now();
340
+
341
+ this.logger.info('Request started', {
342
+ method: context.method,
343
+ path: context.request.path,
344
+ ip: context.ip
345
+ });
346
+
347
+ // Store start time for response logging
348
+ context.request.startTime = start;
349
+
350
+ return context;
351
+ }
352
+ }
353
+ ```
354
+
355
+ ### Error Handling Middleware
356
+
357
+ ```typescript
358
+ import { decorator } from '@ooneex/middleware';
359
+ import type { IMiddleware, ContextType } from '@ooneex/middleware';
360
+ import { Exception } from '@ooneex/exception';
361
+
362
+ @decorator.middleware()
363
+ class ErrorMiddleware implements IMiddleware {
364
+ public async handle(context: ContextType): Promise<ContextType> {
365
+ try {
366
+ return context;
367
+ } catch (error) {
368
+ if (error instanceof Exception) {
369
+ context.logger.error(error);
370
+ context.response.exception(error.message, {
371
+ status: error.status,
372
+ data: error.data
373
+ });
374
+ } else {
375
+ context.logger.error('Unexpected error', {
376
+ error: error instanceof Error ? error.message : String(error)
377
+ });
378
+ context.response.exception('Internal server error', {
379
+ status: 500
380
+ });
381
+ }
382
+
383
+ return context;
384
+ }
385
+ }
386
+ }
387
+ ```
388
+
389
+ ## License
390
+
391
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
392
+
393
+ ## Contributing
394
+
395
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
396
+
397
+ ### Development Setup
398
+
399
+ 1. Clone the repository
400
+ 2. Install dependencies: `bun install`
401
+ 3. Run tests: `bun run test`
402
+ 4. Build the project: `bun run build`
403
+
404
+ ### Guidelines
405
+
406
+ - Write tests for new features
407
+ - Follow the existing code style
408
+ - Update documentation for API changes
409
+ - Ensure all tests pass before submitting PR
410
+
411
+ ---
412
+
413
+ Made with ❤️ by the Ooneex team
package/dist/index.d.ts CHANGED
@@ -1,5 +1,15 @@
1
+ import { EContainerScope } from "@ooneex/container";
2
+ import { ContextConfigType, ContextType } from "@ooneex/controller";
3
+ import { ContextConfigType as SocketContextConfigType, ContextType as SocketContextType } from "@ooneex/socket";
1
4
  type MiddlewareClassType = new (...args: any[]) => IMiddleware;
2
- interface IMiddleware {
3
- next: () => Promise<void>;
5
+ type SocketMiddlewareClassType = new (...args: any[]) => ISocketMiddleware;
6
+ interface IMiddleware<T extends ContextConfigType = ContextConfigType> {
7
+ handle: (context: ContextType<T>) => Promise<ContextType<T>> | ContextType<T>;
4
8
  }
5
- export { MiddlewareClassType, IMiddleware };
9
+ interface ISocketMiddleware<T extends SocketContextConfigType = SocketContextConfigType> {
10
+ handle: (context: SocketContextType<T>) => Promise<SocketContextType<T>> | SocketContextType<T>;
11
+ }
12
+ declare const decorator: {
13
+ middleware: (scope?: EContainerScope) => (target: MiddlewareClassType | SocketMiddlewareClassType) => void;
14
+ };
15
+ export { decorator, SocketMiddlewareClassType, MiddlewareClassType, ISocketMiddleware, IMiddleware };
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
+ import{container as o,EContainerScope as a}from"@ooneex/container";var d={middleware:(e=a.Singleton)=>{return(r)=>{o.add(r,e)}}};export{d as decorator};
1
2
 
2
- //# debugId=BC4C8D5165931A0D64756E2164756E21
3
+ //# debugId=99D3A80723DBEBF264756E2164756E21
package/dist/index.js.map CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": [],
3
+ "sources": ["src/decorators.ts"],
4
4
  "sourcesContent": [
5
+ "import { container, EContainerScope } from \"@ooneex/container\";\nimport type { MiddlewareClassType, SocketMiddlewareClassType } from \"./types\";\n\nexport const decorator = {\n middleware: (scope: EContainerScope = EContainerScope.Singleton) => {\n return (target: MiddlewareClassType | SocketMiddlewareClassType): void => {\n container.add(target, scope);\n };\n },\n};\n"
5
6
  ],
6
- "mappings": "",
7
- "debugId": "BC4C8D5165931A0D64756E2164756E21",
7
+ "mappings": "AAAA,oBAAS,qBAAW,0BAGb,IAAM,EAAY,CACvB,WAAY,CAAC,EAAyB,EAAgB,YAAc,CAClE,MAAO,CAAC,IAAkE,CACxE,EAAU,IAAI,EAAQ,CAAK,GAGjC",
8
+ "debugId": "99D3A80723DBEBF264756E2164756E21",
8
9
  "names": []
9
10
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ooneex/middleware",
3
- "description": "",
4
- "version": "0.0.1",
3
+ "description": "Middleware framework with decorators for request/response pipeline processing in HTTP and WebSocket contexts",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -25,10 +25,23 @@
25
25
  "test": "bun test tests",
26
26
  "build": "bunup",
27
27
  "lint": "tsgo --noEmit && bunx biome lint",
28
- "publish:prod": "bun publish --tolerate-republish --access public",
29
- "publish:pack": "bun pm pack --destination ./dist",
30
- "publish:dry": "bun publish --dry-run"
28
+ "npm:publish": "bun publish --tolerate-republish --access public"
31
29
  },
32
- "devDependencies": {},
33
- "peerDependencies": {}
30
+ "dependencies": {
31
+ "@ooneex/container": "0.0.2"
32
+ },
33
+ "devDependencies": {
34
+ "@ooneex/controller": "0.0.1",
35
+ "@ooneex/socket": "0.0.1"
36
+ },
37
+ "keywords": [
38
+ "bun",
39
+ "decorator",
40
+ "handler",
41
+ "http",
42
+ "middleware",
43
+ "ooneex",
44
+ "pipeline",
45
+ "typescript"
46
+ ]
34
47
  }
Binary file