yinzerflow 0.1.9 β†’ 0.1.11

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,375 +1,296 @@
1
- # YinzerJS Documentation
2
-
3
- ## Table of Contents
4
-
5
- 1. [Overview](#overview)
6
- 2. [Installation](#installation)
7
- - [Using npm](#using-npm)
8
- - [Using Bun](#using-bun)
9
- - [Using Yarn](#using-yarn)
10
- - [Using pnpm](#using-pnpm)
11
- 3. [Getting Started](#getting-started)
12
- - [Importing YinzerFlow](#importing-yinzerflow)
13
- - [Creating an Application Instance](#creating-an-application-instance)
14
- - [Defining Routes](#defining-routes)
15
- 4. [Route Groups](#route-groups)
16
- 5. [Response Handling](#response-handling)
17
- 6. [Middleware](#middleware)
18
- - [Global Middleware](#global-middleware)
19
- - [Route-specific Middleware](#route-specific-middleware)
20
- 7. [Starting the Server](#starting-the-server)
21
- 8. [Request Handling Flow](#request-handling-flow)
22
- 9. [Error Handling](#error-handling)
23
- 10. [Examples](#examples)
24
- 11. [Contribution](#contribution)
25
- 12. [Conclusion](#conclusion)
26
-
27
- ## Overview
28
-
29
- YinzerFlow is a lightweight HTTP server framework built for Node.js, designed with TypeScript in mind. It leverages TypeScript's powerful type system to provide enhanced type safety and autocompletion, making it easier for developers to build robust web applications. With YinzerFlow, you can enjoy a flexible routing system, middleware support, and a straightforward interface to handle incoming requests and responsesβ€”all while benefiting from TypeScript's clear typing and error-checking capabilities. This combination allows for a smoother development experience, reducing runtime errors and improving code maintainability.
30
-
31
- ```typescript
32
- import { YinzerFlow } from 'yinzerflow';
33
-
34
- export const app = new YinzerFlow({
35
- port: 5000,
36
- });
37
-
38
- app.post('/example-route', (ctx) => {
39
- const { body } = ctx.request;
40
- return { message: 'Hello, world!' };
41
- });
42
-
43
- app.listen();
44
- ```
1
+ # YinzerFlow
2
+
3
+ <div align="center">
4
+ <h3>A lightweight, modular HTTP server framework for Node.js</h3>
5
+ <p>Built with TypeScript. Zero dependencies. Blazing fast.</p>
6
+ </div>
7
+
8
+ ## Features
9
+
10
+ - πŸš€ **Lightweight & Fast**: Built from scratch with performance in mind
11
+ - 🧩 **Modular Architecture**: Easily extensible with a clean component structure
12
+ - πŸ”’ **Type-Safe**: Full TypeScript support with comprehensive type definitions
13
+ - πŸ§ͺ **Well-Tested**: Extensive test coverage for reliability
14
+ - πŸ“¦ **Zero Dependencies**: No bloated node_modules folder
15
+ - πŸͺ **Request Lifecycle Hooks**: Powerful hooks system for request processing (formerly middleware)
16
+ - πŸ›£οΈ **Route Groups**: Organize routes with prefixes and shared hooks
17
+ - πŸ”„ **Event-Based Architecture**: Subscribe to framework events for advanced customization
18
+ - 🌐 **Content Type Handling**: Built-in support for JSON, XML, multipart forms, and more
19
+ - πŸ”Œ **Connection Management**: Robust connection tracking with statistics and graceful shutdown
45
20
 
46
21
  ## Installation
47
22
 
48
- To get started with YinzerFlow, simply install it using your preferred package manager. YinzerFlow is available via npm, as well as other popular package managers such as Bun, Yarn, and pnpm. Follow the instructions below based on the package manager you are using:
49
-
50
- ### Using npm
51
-
52
- If you are using npm, run the following command in your terminal:
53
-
54
23
  ```bash
55
24
  npm install yinzerflow
56
- ```
57
-
58
- ### Using Bun
59
-
60
- For those who prefer Bun, you can add YinzerFlow to your project with the following command:
61
-
62
- ```bash
25
+ # or
26
+ yarn add yinzerflow
27
+ # or
63
28
  bun add yinzerflow
64
29
  ```
65
30
 
66
- ### Using Yarn
31
+ ## Quick Start
67
32
 
68
- If you're a Yarn user, you can easily install YinzerFlow by running:
33
+ ### JavaScript
69
34
 
70
- ```bash
71
- yarn add yinzerflow
72
- ```
35
+ ```javascript
36
+ const { YinzerFlow } = require('yinzerflow');
73
37
 
74
- ### Using pnpm
38
+ const app = new YinzerFlow({ port: 3000 });
75
39
 
76
- For developers who utilize pnpm, you can install YinzerFlow with:
40
+ app.get('/hello', ({ request }) => {
41
+ return { message: 'Hello, World!' };
42
+ });
77
43
 
78
- ```bash
79
- pnpm add yinzerflow
44
+ app.listen();
45
+ console.log('Server running on http://localhost:3000');
80
46
  ```
81
47
 
82
- ## Getting Started
83
-
84
- ### Importing YinzerFlow
48
+ ### TypeScript
85
49
 
86
50
  ```typescript
87
51
  import { YinzerFlow } from 'yinzerflow';
88
- ```
89
52
 
90
- ### Creating an Application Instance
91
-
92
- ```typescript
93
- const app = new YinzerFlow({ port: 5000 });
94
- ```
53
+ const app = new YinzerFlow({ port: 3000 });
95
54
 
96
- ### Defining Routes
97
-
98
- You can define routes using the `route` method. Each route can have a path, a handler, and optional before and after handlers.
55
+ app.get('/hello', ({ request }) => {
56
+ return { message: 'Hello, World!' };
57
+ });
99
58
 
100
- ```typescript
101
- app.get(
102
- '/example',
103
- ({ request, response }) => {
104
- return { message: 'Hello, world!' };
105
- },
106
- {
107
- beforeHandler: ({ request, response }) => {
108
- // Logic before the main handler
109
- },
110
- afterHandler: ({ request, response }) => {
111
- // Logic after the main handler
112
- },
113
- },
114
- );
59
+ app.listen();
60
+ console.log('Server running on http://localhost:3000');
115
61
  ```
116
62
 
117
- **Before handler uses:**
118
- The before handler is a good place to perform operations such as authentication, validation, or any pre-processing logic before the main handler is executed.
63
+ ## Core Concepts
119
64
 
120
- **After handler uses:**
121
- The after handler can be used for post-processing tasks like logging, modifying the response, or cleaning up resources after the main handler has completed.
122
-
123
- ### Additional Route Methods and Parameters
124
-
125
- YinzerFlow supports various HTTP methods such as `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. You can use these methods to define routes for specific HTTP requests.
65
+ ### Routing
126
66
 
127
67
  ```typescript
128
- app.get('/example-route', ({ request, response }) => {
129
- // Route handler logic
130
- });
131
- app.patch('/example-route', ({ request, response }) => {
132
- // Route handler logic
133
- });
134
- app.post('/example-route', ({ request, response }) => {
135
- // Route handler logic
136
- });
137
- app.delete('/example-route', ({ request, response }) => {
138
- // Route handler logic
68
+ // Basic routes
69
+ app.get('/users', getAllUsersHandler);
70
+ app.post('/users', createUserHandler);
71
+ app.get('/users/:id', getUserByIdHandler);
72
+ app.put('/users/:id', updateUserHandler);
73
+ app.delete('/users/:id', deleteUserHandler);
74
+
75
+ // Route groups
76
+ app.group('/api/v1', [
77
+ app.get('/products', getProductsHandler),
78
+ app.post('/products', createProductHandler)
79
+ ], {
80
+ beforeGroup: authenticationHook
139
81
  });
140
82
  ```
141
83
 
142
- You can also specify route parameters using the `:param` syntax:
84
+ ### Request Lifecycle Hooks
143
85
 
144
86
  ```typescript
145
- app.get('/users/:id', ({ request }) => {
146
- const { params } = request;
147
- const { id } = params;
148
- // Route handler logic
87
+ // Global hook for all requests
88
+ app.beforeAll(({ request }) => {
89
+ console.log(`Request received: ${request.method} ${request.path}`);
149
90
  });
150
- ```
151
91
 
152
- You can also access query parameters from the request:
92
+ // Path-specific hooks
93
+ app.beforeAll(
94
+ ({ request, response }) => {
95
+ const token = request.headers['authorization'];
96
+ if (!token) {
97
+ response.setStatus(401);
98
+ return { error: 'Authentication required' };
99
+ }
100
+ },
101
+ { paths: ['/admin/*', '/profile/*'] }
102
+ );
153
103
 
154
- ```typescript
155
- app.get('/search?location=pittsburgh', ({ request, response }) => {
156
- const { query } = request;
157
- const { location } = query;
158
- // Route handler logic
159
- });
104
+ // Exclude specific paths
105
+ app.beforeAll(
106
+ authHook,
107
+ { paths: 'allButExcluded', excluded: ['/login', '/register'] }
108
+ );
160
109
  ```
161
110
 
162
- ## Route Groups
163
-
164
- You can group routes using the `group` method. This is useful for applying middleware to multiple routes at once or for organizing related routes.
111
+ ### Content Type Handling
165
112
 
166
113
  ```typescript
167
- app.group('/api', () => {
168
- app.get('/users', ({ request, response }) => {
169
- // Route handler logic
170
- });
171
- app.post('/users', ({ request, response }) => {
172
- // Route handler logic
173
- });
114
+ // Automatically parses JSON requests
115
+ app.post('/api/json', ({ request, response }) => {
116
+ if (!isJsonData(request.body)) {
117
+ response.setStatus(400);
118
+ return { error: 'Expected JSON data' };
119
+ }
120
+
121
+ const { name, email } = request.body;
122
+ return { success: true, data: { name, email } };
174
123
  });
175
- ```
176
124
 
177
- If needed, you can also apply before handlers to the entire group. This logic will run after the global middleware but before the individual route before handlers
178
-
179
- ```typescript
180
- app.group(
181
- '/api',
182
- {
183
- beforeHandler: ({ request, response }) => {
184
- // Logic before the group
185
- },
186
- },
187
- () => {
188
- app.get('/users', ({ request, response }) => {
189
- // Route handler logic
190
- });
191
- },
192
- );
125
+ // Handle file uploads
126
+ app.post('/api/upload', ({ request, response }) => {
127
+ if (!isMultipartFormData(request.body)) {
128
+ response.setStatus(400);
129
+ return { error: 'Expected multipart form data' };
130
+ }
131
+
132
+ const { fields, files } = request.body;
133
+ return {
134
+ success: true,
135
+ message: `Received ${Object.keys(files).length} files`
136
+ };
137
+ });
193
138
  ```
194
139
 
195
- ## Response Handling
140
+ ### Connection Management
196
141
 
197
- The route handlers, before handlers, and middleware functions can return a response in the form of an object or a string. The headers and status code can be set using the `response` object as well. The return headers and status code will be assumed if they are not specified.
142
+ YinzerFlow provides a robust connection management system that allows you to track, monitor, and gracefully handle server connections:
198
143
 
199
144
  ```typescript
200
- app.get('/example-route', () => {
201
- return {
202
- success: true,
203
- message: 'Hello, world!',
204
- };
205
- });
206
- ```
145
+ import { YinzerFlow } from 'yinzerflow';
146
+ import { ConnectionEvent } from 'yinzerflow/constants/connection';
207
147
 
208
- In this example, the response status is set to `200`, and the response body is an object meaning the response headers will be set to `application/json` by default.
148
+ const app = new YinzerFlow({ port: 3000 });
209
149
 
210
- ```typescript
211
- app.post('/example-route', ({ response }) => {
212
- response.setStatus(201);
213
- return 'Hello, world!';
150
+ // Subscribe to connection events
151
+ app.connectionManager.on(ConnectionEvent.CONNECTION_ADDED, (socket) => {
152
+ console.log('New connection established');
214
153
  });
215
- ```
216
154
 
217
- For more strict control over the response, you can use the `TResponseBody` type parameter to specify the response body type:
155
+ app.connectionManager.on(ConnectionEvent.CONNECTION_ERROR, (socket, error) => {
156
+ console.error('Connection error:', error);
157
+ });
218
158
 
219
- ```typescript
220
- app.get('/example-route', (): TResponseBody<{ success: boolean; message: string }> => {
159
+ // Get connection statistics
160
+ app.get('/admin/stats', ({ request }) => {
161
+ const stats = app.connectionManager.getStats();
221
162
  return {
222
- success: true,
223
- message: 'Hello, world!',
163
+ activeConnections: stats.activeConnections,
164
+ totalConnections: stats.totalConnections,
165
+ connectionErrors: stats.connectionErrors,
166
+ uptime: stats.uptime
224
167
  };
225
168
  });
226
- ```
227
-
228
- In this example, the response status is set to `201`, and the response body is a string. The response headers are set to `text/plain` by default in this case.
229
-
230
- ### Changing or Adding Headers
231
-
232
- You can also add or remove headers using the `response` object:
233
169
 
234
- ```typescript
235
- app.get('/example-route', ({ response }) => {
236
- response.addHeaders(['Content-Type: application/json']);
237
- response.removeHeaders(['X-Auth-Token']);
238
- return { success: true, message: 'Hello, world!' };
170
+ // Graceful shutdown example
171
+ process.on('SIGTERM', async () => {
172
+ console.log('SIGTERM received, shutting down gracefully');
173
+
174
+ // Give connections 5 seconds to finish before force closing
175
+ await app.connectionManager.closeAllConnections(5000);
176
+ await app.close();
177
+
178
+ console.log('Server shut down gracefully');
179
+ process.exit(0);
239
180
  });
240
- ```
241
-
242
- ## Route Validation (Coming Soon)
243
-
244
- You can validate routes using the `validate` method. This method takes a schema object and validates the request body, query parameters, or route parameters against it.
245
181
 
246
- ```typescript
247
- app.post(
248
- '/example-route',
249
- ({ request }) => {
250
- const { body } = request;
251
- const { name, age } = body;
252
- // Route handler logic
253
- },
254
- {
255
- validate: {
256
- body: {
257
- email: { type: 'email', required: true },
258
- name: { type: 'string', required: true },
259
- dateOfBirth: { type: 'date' },
260
- age: { type: 'number', min: 18 },
261
- },
262
- },
263
- },
264
- );
182
+ app.listen();
183
+ console.log('Server running on http://localhost:3000');
265
184
  ```
266
185
 
267
- ## Middleware
268
-
269
- YinzerFlow supports middleware that can be applied globally or to specific routes. Middleware can manipulate the request and also return a response. This is useful for tasks such as authentication and throttling.
270
-
271
- ### Global Middleware
272
-
273
- To apply middleware before all routes:
274
-
275
- ```typescript
276
- app.beforeAll(({ request, response }) => {
277
- // middleware logic
278
- });
279
- ```
186
+ #### ConnectionManager API
280
187
 
281
- To apply middleware to specific routes:
188
+ The `ConnectionManager` class provides the following methods and properties:
282
189
 
283
- ```typescript
284
- app.beforeAll(
285
- ({ request, response }) => {
286
- // middleware logic
287
- },
288
- {
289
- paths: ['/user/:id'],
290
- excluded: [],
291
- },
292
- );
293
- ```
190
+ - **Methods**:
191
+ - `setServer(server: Server)`: Associates the connection manager with a server instance
192
+ - `addConnection(socket: Socket)`: Registers a new socket connection for tracking
193
+ - `removeConnection(socket: Socket)`: Removes a socket from tracking when it closes
194
+ - `getStats()`: Returns statistics about connections (`IConnectionStats`)
195
+ - `closeAllConnections(gracePeriod?: number)`: Gracefully closes all active connections
196
+ - `on(event: ConnectionEvent, listener: Function)`: Subscribes to connection events
197
+ - `off(event: ConnectionEvent, listener: Function)`: Unsubscribes from connection events
294
198
 
295
- To apply middleware to all routes except specific ones:
199
+ - **Events**:
200
+ - `CONNECTION_ADDED`: Fired when a new connection is established
201
+ - `CONNECTION_REMOVED`: Fired when a connection is closed
202
+ - `CONNECTION_ERROR`: Fired when a connection encounters an error
203
+ - `ALL_CONNECTIONS_CLOSED`: Fired when all connections have been closed
204
+ - `SERVER_LISTENING`: Fired when the server starts listening
205
+ - `SERVER_CLOSED`: Fired when the server is closed
296
206
 
297
- ```typescript
298
- app.beforeAll(
299
- ({ request, response }) => {
300
- // middleware logic
301
- },
302
- {
303
- paths: 'allButExcluded',
304
- excluded: ['/login', '/register'],
305
- },
306
- );
307
- ```
207
+ - **Statistics**:
208
+ - `activeConnections`: Number of currently active connections
209
+ - `totalConnections`: Total number of connections since server start
210
+ - `connectionErrors`: Number of connection errors encountered
211
+ - `uptime`: Server uptime in milliseconds
308
212
 
309
- ### Route-specific Middleware
213
+ #### Graceful Shutdown Pattern
310
214
 
311
- To apply middleware to specific routes:
215
+ For production applications, implementing a graceful shutdown pattern is recommended:
312
216
 
313
217
  ```typescript
314
- app.use({ paths: ['/route1', '/route2'] }, (ctx) => {
315
- // Middleware logic
316
- });
218
+ // Graceful shutdown handler
219
+ const gracefulShutdown = async (signal: string) => {
220
+ console.log(`${signal} received, starting graceful shutdown`);
221
+
222
+ // Step 1: Stop accepting new connections (optional)
223
+ server.close();
224
+
225
+ // Step 2: Allow existing connections to finish (with timeout)
226
+ console.log('Closing remaining connections...');
227
+ await app.connectionManager.closeAllConnections(10000); // 10 second grace period
228
+
229
+ // Step 3: Close the server completely
230
+ console.log('Shutting down server...');
231
+ await app.close();
232
+
233
+ console.log('Shutdown complete');
234
+ process.exit(0);
235
+ };
236
+
237
+ // Register shutdown handlers
238
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
239
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
317
240
  ```
318
241
 
319
- ## Starting the Server
242
+ This pattern ensures that your server can handle restarts and deployments without dropping active connections.
320
243
 
321
- To start the server, call the `listen` method:
322
-
323
- ```typescript
324
- app.listen();
325
- ```
244
+ ## Examples
326
245
 
327
- ## Request Handling Flow
246
+ Check out the [examples](/example) directory for more detailed usage examples:
328
247
 
329
- The request handling process follows this sequence:
248
+ - [JavaScript Example](/example/javascript) - Basic server implementation in JavaScript
249
+ - [TypeScript Example](/example/typescript) - Type-safe server implementation in TypeScript
330
250
 
331
- 1. Incoming request
332
- 2. Route validation
333
- 3. Global middleware
334
- 4. Before group middleware
335
- 5. Before route middleware
336
- 6. Route handler
337
- 7. After route middleware
338
- 8. Response sent
251
+ ## Documentation
339
252
 
340
- ## Error Handling
253
+ For detailed documentation, see the [docs](/docs) directory:
341
254
 
342
- ### Why Use Built-in Error Handling?
255
+ - [Getting Started](/docs/README.md) - Overview and quick start guide
256
+ - [Routing System](/docs/routing.md) - Comprehensive guide to the routing system
257
+ - [Request Lifecycle Hooks](/docs/hooks.md) - In-depth documentation of the hooks system
258
+ - [Content Type Handling](/docs/content-types.md) - Working with different content types
259
+ - [Error Handling](/docs/error-handling.md) - Guide to handling errors at different levels
260
+ - [Testing Guide](/TESTING.md) - Comprehensive guide to testing the framework
343
261
 
344
- 1. **Enhanced User Experience**: Custom error handlers provide meaningful and consistent error messages to users.
345
- 2. **Simplified Debugging**: Built-in logging capabilities make identifying and fixing errors easier.
346
- 3. **Improved Reliability**: Effectively catching and managing errors prevents unexpected crashes, ensuring smoother operation.
262
+ ## Project Structure
347
263
 
348
- ### How to Set Up a Custom Error Handler
264
+ ```
265
+ yinzerflow/
266
+ β”œβ”€β”€ app/ # Source code
267
+ β”œβ”€β”€ docs/ # Documentation
268
+ β”œβ”€β”€ example/ # Usage examples
269
+ β”‚ β”œβ”€β”€ javascript/ # JavaScript example
270
+ β”‚ └── typescript/ # TypeScript example
271
+ β”œβ”€β”€ package.json # Package configuration
272
+ β”œβ”€β”€ TESTING.md # Testing documentation
273
+ └── README.md # This file
274
+ ```
349
275
 
350
- Set up a custom error handler by defining it in the `errorHandler` method when creating a new instance of YinzerFlow.
276
+ ## Contributing
351
277
 
352
- #### Example Usage
278
+ Contributions are welcome! Please feel free to submit a Pull Request.
353
279
 
354
- ```typescript
355
- const app = new YinzerFlow({
356
- port: 5000,
357
- errorHandler: ({ response }, error): TResponseBody<unknown> => {
358
- console.error('Server error: \n', error);
359
- response.setStatus(<THttpStatusCode>HttpStatusCode.TOO_MANY_REQUESTS);
360
- return { success: false, message: 'Internal server error' };
361
- },
362
- });
363
- ```
280
+ 1. Fork the repository
281
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
282
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
283
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
284
+ 5. Open a Pull Request
364
285
 
365
- ## Examples
286
+ ## License
366
287
 
367
- Refer to the examples folder in the YinzerFlow repository for more detailed usage examples.
288
+ This project is licensed under the MIT License - see the LICENSE file for details.
368
289
 
369
- ## Contribution
290
+ ## Why "YinzerFlow"?
370
291
 
371
- Guidelines coming soon. For now, feel free to open an issue or submit a pull request.
292
+ "Yinzer" is a term for a native or inhabitant of the city of Pittsburgh, Pennsylvania. The name combines the local Pittsburgh dialect with "flow" to represent the smooth flow of HTTP requests through the framework.
372
293
 
373
- ## Conclusion
294
+ ---
374
295
 
375
- YinzerFlow provides a straightforward way to build HTTP servers in Node.js, with support for routing and middleware. For more advanced features, consider extending the framework or integrating with other libraries.
296
+ Built with ❀️ in Pittsburgh