vibe-gx 1.0.2 → 1.0.4

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.
Files changed (3) hide show
  1. package/README.md +109 -1
  2. package/package.json +1 -1
  3. package/vibe.d.ts +88 -8
package/README.md CHANGED
@@ -120,9 +120,40 @@ app.get("/users", async (req, res) => {
120
120
  });
121
121
  ```
122
122
 
123
- ### Streaming Uploads
123
+ ### File Uploads
124
+
125
+ Vibe supports multipart file uploads with built-in validation.
126
+
127
+ #### Basic File Upload
128
+
129
+ ```javascript
130
+ app.post("/upload", { media: { dest: "uploads" } }, (req, res) => {
131
+ return { files: req.files, body: req.body };
132
+ });
133
+ ```
134
+
135
+ #### Media Options
124
136
 
125
137
  ```javascript
138
+ app.post(
139
+ "/upload",
140
+ {
141
+ media: {
142
+ dest: "uploads", // Folder to save files
143
+ public: true, // Save inside public folder (default: true)
144
+ maxSize: 5 * 1024 * 1024, // Max file size: 5MB
145
+ allowedTypes: ["image/jpeg", "image/png", "application/pdf"],
146
+ },
147
+ },
148
+ handler,
149
+ );
150
+ ```
151
+
152
+ #### Streaming Uploads (Large Files)
153
+
154
+ ```javascript
155
+ import fs from "fs";
156
+
126
157
  app.post("/upload", { media: { streaming: true } }, (req, res) => {
127
158
  req.on("file", (name, stream, info) => {
128
159
  stream.pipe(fs.createWriteStream(`/uploads/${info.filename}`));
@@ -131,8 +162,85 @@ app.post("/upload", { media: { streaming: true } }, (req, res) => {
131
162
  });
132
163
  ```
133
164
 
165
+ #### Uploaded File Object
166
+
167
+ ```javascript
168
+ // req.files contains:
169
+ [
170
+ {
171
+ filename: "image-a7x92b.png", // Saved filename
172
+ originalName: "photo.png", // Original filename
173
+ type: "image/png", // MIME type
174
+ filePath: "/uploads/image.png", // Full path
175
+ size: 102400, // Size in bytes
176
+ },
177
+ ];
178
+ ```
179
+
134
180
  ---
135
181
 
182
+ ### Interceptors (Middleware)
183
+
184
+ Interceptors run before your handler. Return `false` to stop execution.
185
+
186
+ #### Single Interceptor
187
+
188
+ ```javascript
189
+ const authCheck = (req, res) => {
190
+ if (!req.headers.authorization) {
191
+ res.unauthorized("Token required");
192
+ return false;
193
+ }
194
+ req.user = { id: 1 };
195
+ return true;
196
+ };
197
+
198
+ app.get("/protected", { intercept: authCheck }, (req) => {
199
+ return { user: req.user };
200
+ });
201
+ ```
202
+
203
+ #### Multiple Interceptors
204
+
205
+ ```javascript
206
+ app.get(
207
+ "/admin",
208
+ {
209
+ intercept: [authCheck, adminCheck, rateLimiter],
210
+ },
211
+ handler,
212
+ );
213
+ ```
214
+
215
+ #### Global Interceptors
216
+
217
+ ```javascript
218
+ // Applies to ALL routes
219
+ app.plugin((req, res) => {
220
+ console.log(`${req.method} ${req.url}`);
221
+ });
222
+ ```
223
+
224
+ ---
225
+
226
+ ### Route Options
227
+
228
+ ```javascript
229
+ app.post(
230
+ "/path",
231
+ {
232
+ intercept: authMiddleware, // Middleware function(s)
233
+ media: {
234
+ // File upload config
235
+ dest: "uploads",
236
+ maxSize: 10 * 1024 * 1024,
237
+ allowedTypes: ["image/*"],
238
+ },
239
+ },
240
+ handler,
241
+ );
242
+ ```
243
+
136
244
  ## 🛠️ API Reference
137
245
 
138
246
  ### Application
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-gx",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "A lightweight, regex-based Node.js web framework built for speed and simplicity.",
5
5
  "type": "module",
6
6
  "main": "vibe.js",
package/vibe.d.ts CHANGED
@@ -26,25 +26,67 @@ export interface UploadedFile {
26
26
 
27
27
  /**
28
28
  * Configuration for file uploads on a specific route.
29
+ *
30
+ * @example
31
+ * {
32
+ * dest: "uploads",
33
+ * maxSize: 5 * 1024 * 1024, // 5MB
34
+ * allowedTypes: ["image/jpeg", "image/png"],
35
+ * public: true
36
+ * }
29
37
  */
30
38
  export interface MediaOptions {
31
39
  /** Save file inside the configured public folder. Default: true */
32
40
  public?: boolean;
33
41
  /** Subfolder destination for uploads (e.g., "uploads/avatars") */
34
42
  dest?: string;
35
- /** Maximum allowed file size in bytes. Default: 10 MB */
43
+ /** Maximum allowed file size in bytes. Default: 10 MB (10485760) */
36
44
  maxSize?: number;
37
- /** Allowed MIME types (e.g., ["image/png", "image/jpeg"]) */
45
+ /**
46
+ * Allowed MIME types. Supports wildcards like "image/*"
47
+ * @example ["image/jpeg", "image/png", "application/pdf"]
48
+ */
38
49
  allowedTypes?: string[];
50
+ /** Enable streaming mode for large files. Use req.on('file', ...) */
51
+ streaming?: boolean;
39
52
  }
40
53
 
41
54
  /**
42
55
  * Options for registering a route.
56
+ *
57
+ * @example
58
+ * // With interceptor only
59
+ * { intercept: authMiddleware }
60
+ *
61
+ * @example
62
+ * // With file upload
63
+ * {
64
+ * intercept: authMiddleware,
65
+ * media: {
66
+ * dest: "uploads",
67
+ * maxSize: 10 * 1024 * 1024,
68
+ * allowedTypes: ["image/*"]
69
+ * }
70
+ * }
43
71
  */
44
72
  export interface RouteOptions {
45
- /** Middleware(s) to run before the main handler */
73
+ /**
74
+ * Middleware function(s) to run before the handler.
75
+ * Return false to stop execution.
76
+ * @example
77
+ * intercept: (req, res) => {
78
+ * if (!req.headers.authorization) {
79
+ * res.unauthorized();
80
+ * return false;
81
+ * }
82
+ * return true;
83
+ * }
84
+ */
46
85
  intercept?: Interceptor | Interceptor[];
47
- /** Configuration for file uploads */
86
+ /**
87
+ * Configuration for file uploads (multipart/form-data).
88
+ * Files will be available in req.files array.
89
+ */
48
90
  media?: MediaOptions;
49
91
  }
50
92
 
@@ -89,7 +131,13 @@ export interface VibeResponse extends ServerResponse {
89
131
  json: (data: any) => void;
90
132
  send: (data: string | number | boolean | object) => void;
91
133
  status: (code: number) => VibeResponse;
134
+ /** Send a file from the public folder */
92
135
  sendFile: (filePath: string) => void;
136
+ /** Send any file by absolute path */
137
+ sendAbsoluteFile: (
138
+ absolutePath: string,
139
+ opts?: { download?: boolean; filename?: string },
140
+ ) => void;
93
141
  sendHtml: (filename: string) => void;
94
142
  redirect: (url: string, code?: number) => void;
95
143
 
@@ -167,13 +215,45 @@ export const color: Record<ColorName, (text: string) => string>;
167
215
 
168
216
  /**
169
217
  * Route registration function.
170
- * Supports two signatures:
171
- * 1. (path, handler)
172
- * 2. (path, options, handler)
218
+ *
219
+ * @example
220
+ * // Simple handler
221
+ * app.get("/path", (req, res) => { ... });
222
+ *
223
+ * @example
224
+ * // Static response
225
+ * app.get("/", "Hello World");
226
+ *
227
+ * @example
228
+ * // With options (interceptor + file upload)
229
+ * app.post("/upload", {
230
+ * intercept: authMiddleware,
231
+ * media: {
232
+ * dest: "uploads",
233
+ * maxSize: 10 * 1024 * 1024,
234
+ * allowedTypes: ["image/*"]
235
+ * }
236
+ * }, handler);
173
237
  */
174
238
  export interface RouteRegistrar {
239
+ /**
240
+ * Register a route with a handler or static response.
241
+ * @param path - Route path (e.g., "/users/:id")
242
+ * @param handler - Handler function, string, number, or object
243
+ */
175
244
  (path: string, handler: Handler | string | number | object): void;
176
- (path: string, options: RouteOptions, handler: Handler): void;
245
+
246
+ /**
247
+ * Register a route with options and handler.
248
+ * @param path - Route path (e.g., "/upload")
249
+ * @param options - Route options (intercept, media)
250
+ * @param handler - Handler function
251
+ */
252
+ (
253
+ path: string,
254
+ options: RouteOptions,
255
+ handler: Handler | string | number | object,
256
+ ): void;
177
257
  }
178
258
 
179
259
  /** Sub-router or prefixed router instance */