yinzerflow 0.1.18 → 0.2.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/docs/README.md DELETED
@@ -1,327 +0,0 @@
1
- # YinzerFlow Documentation
2
-
3
- Welcome to the YinzerFlow documentation! This directory contains comprehensive documentation for the YinzerFlow framework, a lightweight, modular HTTP server framework for Node.js built with TypeScript.
4
-
5
- ## Contents
6
-
7
- - [Routing System](./routing.md) - Comprehensive guide to the routing system, including route definition, parameters, and groups.
8
- - [Request Lifecycle Hooks](./hooks.md) - In-depth documentation of the hooks system (formerly middleware) for intercepting and modifying requests.
9
- - [Content Type Handling](./content-types.md) - Working with different content types in requests and responses.
10
- - [Error Handling](./error-handling.md) - Guide to handling errors at different levels in your application.
11
- - [Connection Management](#connection-management) - Information about the built-in connection management system.
12
-
13
- ## Getting Started
14
-
15
- If you're new to YinzerFlow, we recommend starting with the examples in the `/example` directory:
16
-
17
- - [TypeScript Example](/example/typescript/README.md) - A type-safe server implementation in TypeScript
18
-
19
- ### Installation
20
-
21
- You can install YinzerFlow using your preferred package manager:
22
-
23
- ```bash
24
- # Using npm
25
- npm install yinzerflow
26
-
27
- # Using Yarn
28
- yarn add yinzerflow
29
-
30
- # Using Bun
31
- bun add yinzerflow
32
- ```
33
-
34
- ### Quick Start
35
-
36
- Here's a minimal example to get a server up and running:
37
-
38
- ```typescript
39
- import { YinzerFlow } from 'yinzerflow';
40
-
41
- // Create a new YinzerFlow instance
42
- const app = new YinzerFlow({ port: 3000 });
43
-
44
- // Add a simple route
45
- app.get('/hello', () => {
46
- return { message: 'Hello, World!' };
47
- });
48
-
49
- // Start the server
50
- await app.listen();
51
- const { port, isListening } = app.getStatus();
52
-
53
- if (isListening) console.log(`Server running on http://localhost:${port}`);
54
- ```
55
-
56
- ## Core Concepts
57
-
58
- ### Routing
59
-
60
- YinzerFlow provides a simple and intuitive routing system:
61
-
62
- ```typescript
63
- // Basic route
64
- app.get('/users', () => {
65
- return { users: [] };
66
- });
67
-
68
- // Route with parameters
69
- app.get('/users/:id', ({ request }) => {
70
- const { id } = request.params;
71
- return { user: { id, name: 'John Doe' } };
72
- });
73
-
74
- // Different HTTP methods
75
- app.post('/users', ({ request }) => {
76
- const newUser = request.body;
77
- // Create user logic
78
- return { success: true, user: newUser };
79
- });
80
-
81
- app.put('/users/:id', ({ request }) => {
82
- const { id } = request.params;
83
- const userData = request.body;
84
- // Update user logic
85
- return { success: true };
86
- });
87
-
88
- app.delete('/users/:id', ({ request }) => {
89
- const { id } = request.params;
90
- // Delete user logic
91
- return { success: true };
92
- });
93
- ```
94
-
95
- For more detailed information about routing, see the [Routing System](./routing.md) documentation.
96
-
97
- ### Request Lifecycle Hooks
98
-
99
- YinzerFlow provides a powerful hooks system (traditionally called middleware in other frameworks) that allows you to intercept and modify requests at various points in the request lifecycle:
100
-
101
- ```typescript
102
- // Global hooks
103
- app.beforeAll(({ request }) => {
104
- console.log(`Request received: ${request.method} ${request.path}`);
105
- });
106
-
107
- // Path-specific hooks
108
- app.beforeAll(
109
- ({ request, response }) => {
110
- const token = request.headers['authorization'];
111
- if (!token) {
112
- response.setStatus(401);
113
- return { error: 'Authentication required' };
114
- }
115
- },
116
- { paths: ['/admin', '/profile'] }
117
- );
118
-
119
- // Excluded paths
120
- app.beforeAll(
121
- authHook,
122
- { paths: 'allButExcluded', excluded: ['/login', '/register'] }
123
- );
124
-
125
- // Method chaining
126
- app
127
- .beforeAll(logRequest)
128
- .beforeAll(checkAuth, { paths: ['/admin/*'] });
129
- ```
130
-
131
- For more detailed information about hooks, see the [Request Lifecycle Hooks](./hooks.md) documentation.
132
-
133
- ### Route Groups
134
-
135
- Group related routes under a common prefix:
136
-
137
- ```typescript
138
- // Define routes
139
- const userRoutes = [
140
- app.get('/profile', userProfileHandler),
141
- app.put('/profile', updateProfileHandler),
142
- app.get('/settings', userSettingsHandler)
143
- ];
144
-
145
- // Apply group
146
- app.group('/user', userRoutes, {
147
- beforeGroup: authHook
148
- });
149
- ```
150
-
151
- ### Content Type Handling
152
-
153
- YinzerFlow automatically parses request bodies based on the Content-Type header:
154
-
155
- ```typescript
156
- // JSON data
157
- app.post('/api/json', ({ request, response }) => {
158
- if (!isJsonData(request.body)) {
159
- response.setStatus(400);
160
- return { error: 'Expected JSON data' };
161
- }
162
-
163
- const { name, email } = request.body;
164
- return { success: true, data: { name, email } };
165
- });
166
-
167
- // File uploads
168
- app.post('/api/upload', ({ request, response }) => {
169
- if (!isMultipartFormData(request.body)) {
170
- response.setStatus(400);
171
- return { error: 'Expected multipart form data' };
172
- }
173
-
174
- const { fields, files } = request.body;
175
- return {
176
- success: true,
177
- message: `Received ${Object.keys(files).length} files`
178
- };
179
- });
180
- ```
181
-
182
- For detailed information about working with different content types, see the [Content Type Handling](./content-types.md) documentation.
183
-
184
- ### Error Handling
185
-
186
- Custom error handling for graceful error responses:
187
-
188
- ```typescript
189
- const app = new YinzerFlow({
190
- port: 3000,
191
- errorHandler: ({ response }, error) => {
192
- console.error('Error:', error);
193
- response.setStatus(500);
194
- return {
195
- success: false,
196
- message: 'An unexpected error occurred',
197
- timestamp: new Date().toISOString()
198
- };
199
- }
200
- });
201
- ```
202
-
203
- ### Event-Based Architecture
204
-
205
- YinzerFlow uses an event-based architecture that allows you to subscribe to framework events:
206
-
207
- ```typescript
208
- import { HookManagerEvent } from 'yinzerflow/constants/hooks';
209
-
210
- // Subscribe to hook execution events
211
- app.hooks.on(HookManagerEvent.BEFORE_ALL_EXECUTED, (context, hook) => {
212
- console.log(`Executed beforeAll hook for ${context.request.path}`);
213
- });
214
-
215
- // Subscribe to hook addition events
216
- app.hooks.on(HookManagerEvent.HOOK_ADDED, (hook) => {
217
- console.log(`Added new hook for paths: ${JSON.stringify(hook.paths)}`);
218
- });
219
-
220
- // Subscribe to route registration events
221
- app.routes.on('route-registered', (route) => {
222
- console.log(`Registered route: ${route.method} ${route.path}`);
223
- });
224
- ```
225
-
226
- ### Connection Management
227
-
228
- YinzerFlow includes built-in connection management that handles tracking, monitoring, and graceful shutdown of server connections. This is managed internally by the framework to ensure reliable operation.
229
-
230
- #### Configuration
231
-
232
- You can configure connection management options when creating a YinzerFlow instance:
233
-
234
- ```typescript
235
- import { YinzerFlow } from 'yinzerflow';
236
-
237
- // Create a YinzerFlow instance with connection management options
238
- const app = new YinzerFlow({
239
- port: 3000,
240
- connectionOptions: {
241
- // Maximum time (in ms) to wait for connections to close during shutdown
242
- gracefulShutdownTimeout: 10000,
243
- // Socket timeout in milliseconds (how long until inactive connections are closed)
244
- socketTimeout: 60000
245
- }
246
- });
247
- ```
248
-
249
- #### Graceful Shutdown
250
-
251
- To implement graceful shutdown in your application:
252
-
253
- ```typescript
254
- // Graceful shutdown example
255
- process.on('SIGTERM', async () => {
256
- console.log('SIGTERM received, shutting down gracefully');
257
-
258
- // This will:
259
- // 1. Stop accepting new connections
260
- // 2. Wait for existing connections to complete (up to the configured timeout)
261
- // 3. Close the server
262
- await app.close();
263
-
264
- console.log('Server shut down gracefully');
265
- process.exit(0);
266
- });
267
-
268
- app.listen();
269
- console.log('Server running on http://localhost:3000');
270
- ```
271
-
272
- The `close()` method handles the entire shutdown process, including:
273
- - Stopping the server from accepting new connections
274
- - Waiting for existing connections to finish (respecting the configured timeout)
275
- - Closing all remaining connections and the server itself
276
-
277
- This ensures that your application can shut down cleanly without abruptly terminating active connections.
278
-
279
- ### Path Matching Patterns
280
-
281
- YinzerFlow supports advanced path matching patterns for hooks:
282
-
283
- ```typescript
284
- // Exact path matching
285
- app.beforeAll(adminHook, { paths: ['/admin'] });
286
-
287
- // Wildcard matching
288
- app.beforeAll(apiHook, { paths: ['/api/*'] });
289
-
290
- // Parameter matching
291
- app.beforeAll(userHook, { paths: ['/users/:id'] });
292
-
293
- // Regular expression matching
294
- app.beforeAll(secureHook, { paths: [/^\/secure\/.+/] });
295
- ```
296
-
297
- ## Contributing to Documentation
298
-
299
- We welcome contributions to improve this documentation! If you find any issues or have suggestions for improvements, please feel free to submit a pull request.
300
-
301
- When contributing to documentation:
302
-
303
- 1. Use clear, concise language
304
- 2. Include code examples where appropriate
305
- 3. Follow Markdown best practices
306
- 4. Test all links to ensure they work correctly
307
-
308
- ## Documentation Structure
309
-
310
- The documentation is organized as follows:
311
-
312
- ```
313
- docs/
314
- ├── README.md # This file - overview and getting started
315
- ├── routing.md # Comprehensive guide to the routing system
316
- ├── hooks.md # In-depth documentation of the hooks system
317
- ├── content-types.md # Working with different content types
318
- ├── error-handling.md # Guide to handling errors at different levels in your application
319
- ```
320
-
321
- Additional documentation files will be added as the framework evolves, including:
322
-
323
- - Error handling and logging
324
- - Performance optimization guides
325
- - Security best practices
326
- - Deployment strategies
327
- - Troubleshooting and FAQs
@@ -1,390 +0,0 @@
1
- # Content Type Handling
2
-
3
- YinzerFlow provides robust support for parsing and handling various content types in HTTP requests and responses. This document explains the core concepts, features, and best practices for working with different content types.
4
-
5
- ## Core Concepts
6
-
7
- In web applications, HTTP requests and responses can contain data in various formats, such as JSON, XML, form data, and more. YinzerFlow automatically detects and parses these formats based on the `Content-Type` header, making it easy to work with different types of data.
8
-
9
- ### Type Safety
10
-
11
- YinzerFlow provides TypeScript interfaces and type guards for each supported content type, allowing you to work with request bodies in a type-safe manner.
12
-
13
- ## Supported Content Types
14
-
15
- YinzerFlow supports the following content types out of the box:
16
-
17
- | Content Type | MIME Type | Type Interface | Type Guard | Description |
18
- |--------------|-----------|----------------|------------|-------------|
19
- | JSON | `application/json` | `TJsonData` | `isJsonData` | JSON data as a generic object |
20
- | XML (Coming Soon) | `application/xml` | `TXmlData` | `isXmlData` | XML data with elements, attributes, and children |
21
- | Multipart Form | `multipart/form-data` | `IMultipartFormData` | `isMultipartFormData` | Form data with fields and file uploads ([See Supported File Parsers](./file-parsers.md)) |
22
-
23
- ## Working with Content Types
24
-
25
- There are two main approaches to working with parsed request bodies in YinzerFlow:
26
-
27
- ### Approach 1: Type Casting (Simple but Less Safe)
28
-
29
- This approach uses TypeScript's type assertion to specify the expected content type:
30
-
31
- ```typescript
32
- import { IMultipartFormData } from 'yinzerflow/types/http/Request';
33
-
34
- app.post('/upload', ({ request }) => {
35
- // Use type assertion - simple but no runtime validation
36
- const body = request.body as IMultipartFormData;
37
-
38
- // Access fields and files directly
39
- const { fields, files } = body;
40
-
41
- // Process the data...
42
- return { success: true };
43
- });
44
- ```
45
-
46
- **When to use this approach:**
47
- - When you're certain about the content type (e.g., an internal API with controlled clients)
48
- - When performance is critical and you want to avoid runtime type checking
49
- - In simple applications where type safety is less important
50
-
51
- ### Approach 2: Type Guards (Safer and Recommended)
52
-
53
- This approach uses runtime type checking for better safety:
54
-
55
- ```typescript
56
- import { isMultipartFormData } from 'yinzerflow/types/http/Request';
57
-
58
- app.post('/upload', ({ request, response }) => {
59
- // Runtime type checking
60
- if (!isMultipartFormData(request.body)) {
61
- response.setStatus(400);
62
- return { error: 'Expected multipart form data' };
63
- }
64
-
65
- // TypeScript knows request.body is IMultipartFormData here
66
- const { fields, files } = request.body;
67
-
68
- // Process the data...
69
- return { success: true };
70
- });
71
- ```
72
-
73
- **When to use this approach:**
74
- - When handling requests from external clients
75
- - When an endpoint might receive different content types
76
- - When you want to provide clear error messages for invalid requests
77
- - In most production applications (recommended approach)
78
-
79
- For more details on handling errors, including content type validation errors, refer to the [Error Handling](./error-handling.md) documentation.
80
-
81
- ## Detailed Content Type Examples
82
-
83
- ### JSON Data
84
-
85
- JSON is one of the most common formats for API requests and responses:
86
-
87
- ```typescript
88
- import { isJsonData } from 'yinzerflow';
89
-
90
- app.post('/api/users', ({ request, response }) => {
91
- if (!isJsonData<{ name: string; email: string; age?: number }>(request.body)) {
92
- response.setStatus(400);
93
- return { error: 'Expected JSON data' };
94
- }
95
-
96
- // Validate required fields
97
- const { name, email, age } = request.body;
98
- if (!name || !email) {
99
- response.setStatus(400);
100
- return { error: 'Name and email are required' };
101
- }
102
-
103
- // Process the data
104
- const newUser = {
105
- id: generateId(),
106
- name,
107
- email,
108
- age: age || null,
109
- createdAt: new Date().toISOString()
110
- };
111
-
112
- // Save the user (example)
113
- saveUser(newUser);
114
-
115
- response.setStatus(201);
116
- return newUser;
117
- });
118
- ```
119
-
120
- #### Type Definition
121
-
122
- ```typescript
123
- // The JSON data type
124
- type TJsonData<T = unknown> = Record<string, unknown> & T;
125
-
126
- // Type guard for JSON data
127
- const isJsonData = <T = unknown>(body: TRequestBody): body is TJsonData<T> => !isMultipartFormData(body) && typeof body === 'object';
128
- ```
129
-
130
- ### Multipart Form Data (File Uploads)
131
-
132
- Multipart form data is used for file uploads and complex forms:
133
-
134
- ```typescript
135
- import { isMultipartFormData } from 'yinzerflow';
136
- import { saveFile } from './file-service';
137
-
138
- app.post('/api/upload', ({ request, response }) => {
139
- if (!isMultipartFormData(request.body)) {
140
- response.setStatus(415);
141
- return { error: 'Expected multipart form data' };
142
- }
143
-
144
- const { fields, files } = request.body;
145
-
146
- // Validate form fields
147
- const userId = fields.userId;
148
- if (!userId) {
149
- response.setStatus(400);
150
- return { error: 'User ID is required' };
151
- }
152
-
153
- // Process uploaded files
154
- const uploadedFiles = [];
155
- for (const file of files) {
156
- // Check file type
157
- if (!file.contentType.startsWith('image/')) {
158
- response.setStatus(400);
159
- return { error: `File ${file.name} is not an image` };
160
- }
161
-
162
- // Check file size (max 5MB)
163
- if (file.size > 5 * 1024 * 1024) {
164
- response.setStatus(400);
165
- return { error: `File ${file.name} exceeds the 5MB limit` };
166
- }
167
-
168
- // Save the file (example)
169
- const savedPath = saveFile(file.path, userId);
170
-
171
- uploadedFiles.push({
172
- originalName: file.name,
173
- size: file.size,
174
- type: file.contentType,
175
- savedPath
176
- });
177
- }
178
-
179
- response.setStatus(201);
180
- return {
181
- success: true,
182
- message: `Uploaded ${uploadedFiles.length} files for user ${userId}`,
183
- files: uploadedFiles
184
- };
185
- });
186
- ```
187
-
188
- #### Type Definition
189
-
190
- ```typescript
191
- // The multipart form data interface
192
- interface IMultipartFormData {
193
- fields: Record<string, string>;
194
- files: Record<string, UploadedFile>;
195
- }
196
-
197
- // The uploaded file interface
198
- interface UploadedFile {
199
- /** Original filename provided by the client */
200
- filename: string;
201
- /** MIME type of the file */
202
- contentType: string;
203
- /** Size of the file in bytes */
204
- size: number;
205
- /** file content */
206
- content: TYamlData | string;
207
- /** Additional metadata about the file */
208
- metadata?: Record<string, string>;
209
- }
210
-
211
- // Type guard for JSON data
212
- const isMultipartFormData = (body: TRequestBody): body is IMultipartFormData =>
213
- body !== null && typeof body === 'object' && 'fields' in body && 'files' in body && Array.isArray(body.files) && typeof (<any>body.fields) === 'object';
214
- ```
215
-
216
- ### XML Data (Coming Soon)
217
-
218
- XML is commonly used in enterprise systems and SOAP APIs:
219
-
220
- ## Best Practices
221
-
222
- ### 1. Always Validate Request Bodies
223
-
224
- Always validate the content type and structure of request bodies, especially for public APIs:
225
-
226
- ```typescript
227
- app.post('/api/data', ({ request, response }) => {
228
- // Validate content type
229
- if (!isJsonData(request.body)) {
230
- response.setStatus(400);
231
- return { error: 'Expected JSON data' };
232
- }
233
-
234
- // Validate required fields
235
- const { name, email } = request.body;
236
- if (!name || !email) {
237
- response.setStatus(400);
238
- return { error: 'Name and email are required' };
239
- }
240
-
241
- // Validate email format
242
- if (!isValidEmail(email)) {
243
- response.setStatus(400);
244
- return { error: 'Invalid email format' };
245
- }
246
-
247
- // Process the data...
248
- });
249
- ```
250
-
251
- ### 2. Use Type Guards for Public APIs
252
-
253
- For public APIs, always use type guards to validate request bodies:
254
-
255
- ```typescript
256
- // Good practice for public APIs
257
- if (!isJsonData(request.body)) {
258
- response.setStatus(400);
259
- return { error: 'Expected JSON data' };
260
- }
261
-
262
- // Avoid type assertions for public APIs
263
- const body = request.body as TJsonData; // Unsafe
264
- ```
265
-
266
- ### 3. Set Appropriate Content-Type Headers for Responses
267
-
268
- Set the appropriate `Content-Type` header for responses:
269
-
270
- ```typescript
271
- app.get('/api/data.csv', ({ response }) => {
272
- const csvContent = generateCsvData();
273
-
274
- response.setStatus(200);
275
- response.modifyHeader('Content-Type', 'text/csv');
276
- response.modifyHeader('Content-Disposition', 'attachment; filename="data.csv"');
277
- return csvContent;
278
- });
279
- ```
280
-
281
- ### 4. Handle Large File Uploads Properly
282
-
283
- For file uploads, implement proper validation and limits:
284
-
285
- ```typescript
286
- app.post('/api/upload', ({ request, response }) => {
287
- if (!isMultipartFormData(request.body)) {
288
- response.setStatus(400);
289
- return { error: 'Expected multipart form data' };
290
- }
291
-
292
- const { files } = request.body;
293
-
294
- // Check file count
295
- if (Object.keys(files).length > 10) {
296
- response.setStatus(400);
297
- return { error: 'Maximum 10 files allowed' };
298
- }
299
-
300
- // Check file sizes
301
- for (const [, file] of Object.entries(files)) {
302
- if (file.size > 10 * 1024 * 1024) { // 10MB
303
- response.setStatus(400);
304
- return { error: `File ${file.name} exceeds the 10MB limit` };
305
- }
306
- }
307
-
308
- // Process files...
309
- });
310
- ```
311
-
312
- ### 5. Use Structured Error Responses
313
-
314
- Provide clear and structured error responses:
315
-
316
- ```typescript
317
- app.post('/api/users', ({ request, response }) => {
318
- if (!isJsonData(request.body)) {
319
- response.setStatus(400);
320
- return {
321
- success: false,
322
- error: 'INVALID_CONTENT_TYPE',
323
- message: 'Expected JSON data'
324
- };
325
- }
326
-
327
- // Validate fields...
328
- const errors = validateUserData(request.body);
329
- if (errors.length > 0) {
330
- response.setStatus(400);
331
- return {
332
- success: false,
333
- error: 'VALIDATION_ERROR',
334
- message: 'Invalid user data',
335
- details: errors
336
- };
337
- }
338
-
339
- // Process the data...
340
- });
341
- ```
342
-
343
- ## Advanced Topics
344
-
345
- ### Custom Content Type Parsers (Future Idea)
346
-
347
- YinzerFlow allows you to register custom content type parsers:
348
-
349
- ### Content Negotiation
350
-
351
- Implement content negotiation to support multiple response formats:
352
-
353
- ```typescript
354
- app.get('/api/users/:id', ({ request, response }) => {
355
- const user = getUserById(request.params.id);
356
- if (!user) {
357
- response.setStatus(404);
358
- return { error: 'User not found' };
359
- }
360
-
361
- // Check Accept header
362
- const acceptHeader = request.headers.accept || 'application/json';
363
-
364
- if (acceptHeader.includes('application/xml')) {
365
- // Return XML response
366
- const xmlData = convertToXml({ user });
367
- response.modifyHeader('Content-Type', 'application/xml');
368
- return xmlData;
369
- }
370
-
371
- if (acceptHeader.includes('text/csv')) {
372
- // Return CSV response
373
- const csvData = convertToCsv([user]);
374
- response.modifyHeader('Content-Type', 'text/csv');
375
- return csvData;
376
- }
377
-
378
- // Default to JSON
379
- return user;
380
- });
381
- ```
382
-
383
- ### Handling Binary Data
384
-
385
- For binary data like images or PDFs we only support uploads using from-data at this time
386
-
387
-
388
- ## Conclusion
389
-
390
- YinzerFlow's content type handling system provides a flexible and type-safe way to work with various data formats in your web applications. By following the best practices outlined in this document, you can build robust APIs that handle different content types correctly and provide a great developer experience.