vector-framework 0.9.7 → 0.9.9

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,8 +1,11 @@
1
1
  # Vector Framework
2
2
 
3
- **The speed of Bun. The developer experience you love.**
3
+ **Blazing fast, secure, and developer-friendly API framework for Bun**
4
4
 
5
- Vector brings the blazing performance of Bun to developers who appreciate the simplicity and elegance of frameworks like Encore. Build production-ready APIs with a familiar, declarative syntax while leveraging Bun's incredible speed.
5
+ - **70,000+ requests/second** - Optimized for extreme performance
6
+ - **Single dependency** - Only itty-router, minimizing security risks
7
+ - **Zero build step** - Native TypeScript execution with Bun
8
+ - **Encore-like DX** - Declarative, type-safe APIs you'll love
6
9
 
7
10
  ## Quick Start
8
11
 
@@ -12,9 +15,9 @@ Vector brings the blazing performance of Bun to developers who appreciate the si
12
15
  bun add vector-framework
13
16
  ```
14
17
 
15
- ### Configuration
18
+ ### 1. Configure Your App
16
19
 
17
- Create a `vector.config.ts` file in your project root:
20
+ Create `vector.config.ts` in your project root:
18
21
 
19
22
  ```typescript
20
23
  // vector.config.ts
@@ -22,507 +25,674 @@ import type { VectorConfigSchema } from "vector-framework";
22
25
 
23
26
  const config: VectorConfigSchema = {
24
27
  // Server configuration
25
- port: 3000, // Server port (default: 3000)
26
- hostname: "localhost", // Server hostname (default: localhost)
27
- routesDir: "./routes", // Routes directory (default: ./routes)
28
- development: process.env.NODE_ENV !== "production", // Development mode
29
- reusePort: true, // Reuse port (default: true)
30
- idleTimeout: 60, // Idle timeout in seconds (default: 60)
28
+ port: 3000,
29
+ hostname: "localhost",
30
+ development: process.env.NODE_ENV !== "production",
31
+ routesDir: "./routes", // Auto-discovers routes here
31
32
 
32
33
  // CORS configuration
33
34
  cors: {
34
- origin: "*", // String, array, or function
35
- credentials: true, // Allow credentials
35
+ origin: "*",
36
+ credentials: true,
36
37
  allowHeaders: ["Content-Type", "Authorization"],
37
38
  allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
38
- exposeHeaders: ["X-Total-Count"],
39
- maxAge: 86400, // Preflight cache duration in seconds
40
39
  },
40
+
41
+ // Authentication handler
42
+ auth: async (request) => {
43
+ const token = request.headers.get("Authorization")?.replace("Bearer ", "");
44
+ if (token === "valid-token") {
45
+ return { id: "user-123", email: "user@example.com" };
46
+ }
47
+ throw new Error("Invalid token");
48
+ },
49
+
50
+ // Optional: Cache handler (Redis example)
51
+ cache: async (key, factory, ttl) => {
52
+ // Your caching logic here
53
+ const cached = await redis.get(key);
54
+ if (cached) return JSON.parse(cached);
55
+
56
+ const value = await factory();
57
+ await redis.setex(key, ttl, JSON.stringify(value));
58
+ return value;
59
+ },
60
+
61
+ // Optional: Middleware
62
+ before: [
63
+ async (request) => {
64
+ console.log(`${request.method} ${request.url}`);
65
+ return request;
66
+ }
67
+ ],
68
+ after: [
69
+ async (response, request) => {
70
+ const duration = Date.now() - (request.startTime || Date.now());
71
+ response.headers.set("X-Response-Time", `${duration}ms`);
72
+ return response;
73
+ }
74
+ ],
41
75
  };
42
76
 
43
77
  export default config;
44
78
  ```
45
79
 
46
- ### Your First API (Encore-style)
80
+ ### 2. Create Your First Route
47
81
 
48
82
  ```typescript
49
83
  // routes/hello.ts
50
84
  import { route } from "vector-framework";
51
85
 
52
- // Public endpoint - clean and declarative
86
+ // Simple public endpoint
53
87
  export const hello = route(
54
88
  {
55
89
  method: "GET",
56
90
  path: "/hello/:name",
91
+ expose: true, // Public endpoint (default: true)
57
92
  },
58
93
  async (req) => {
59
- const { name } = req.params!;
60
- return { message: `Hello ${name}!` };
94
+ return { message: `Hello ${req.params.name}!` };
61
95
  }
62
96
  );
63
97
 
64
- // Protected endpoint - auth built-in
98
+ // Protected endpoint - uses auth from config
65
99
  export const getProfile = route(
66
100
  {
67
101
  method: "GET",
68
102
  path: "/profile",
69
- auth: true, // That's it! Auth handled.
103
+ auth: true, // Requires authentication
104
+ expose: true,
70
105
  },
71
106
  async (req) => {
72
107
  return {
73
- user: req.authUser,
74
- lastLogin: new Date(),
108
+ user: req.authUser, // Typed from your auth handler
109
+ timestamp: new Date(),
75
110
  };
76
111
  }
77
112
  );
78
- ```
79
113
 
80
- ### Start Your Server
81
-
82
- ```typescript
83
- // server.ts
84
- import vector from "vector-framework";
85
-
86
- // Set up auth (once, globally)
87
- vector.protected = async (request) => {
88
- const token = request.headers.get("Authorization")?.replace("Bearer ", "");
89
- // Your auth logic here
90
- if (token === "valid-token") {
91
- return { id: "user-123", email: "user@example.com" };
114
+ // Cached endpoint
115
+ export const getUsers = route(
116
+ {
117
+ method: "GET",
118
+ path: "/users",
119
+ cache: 300, // Cache for 5 minutes
120
+ expose: true,
121
+ },
122
+ async () => {
123
+ // Expensive operation, will be cached
124
+ const users = await db.users.findMany();
125
+ return { users };
92
126
  }
93
- throw new Error("Invalid token");
94
- };
95
-
96
- // Start server - routes auto-discovered from ./routes
97
- vector.serve({ port: 3000 });
127
+ );
98
128
  ```
99
129
 
100
- ### CLI Commands
130
+ ### 3. Start Your Server
101
131
 
102
132
  ```bash
103
- # Run development server
133
+ # Development mode with hot reload
104
134
  bun vector dev
105
135
 
106
- # Run production server
136
+ # Production mode
107
137
  bun vector start
108
138
 
109
- # Build for production
110
- bun vector build
111
-
112
- # Run with custom options
113
- bun vector dev --port 3000 --routes ./api
139
+ # With custom options
140
+ bun vector dev --port 4000 --routes ./api
114
141
  ```
115
142
 
116
- ## Why Vector?
117
-
118
- If you've been looking for Encore-like developer experience with Bun's performance, Vector is your answer. Define your routes declaratively, enjoy automatic type safety, and ship faster than ever.
143
+ That's it! Your API is running at `http://localhost:3000` 🎉
119
144
 
120
- ## Features
145
+ ## TypeScript Type Safety
121
146
 
122
- - **Fast & Lightweight** - Built on Bun and itty-router for maximum performance
123
- - **Type-Safe** - Full TypeScript support with excellent type inference
124
- - **Auto Route Discovery** - Automatically discovers and loads routes from your filesystem
125
- - **Middleware System** - Flexible pre/post request middleware pipeline
126
- - **Built-in Authentication** - Simple but powerful authentication system
127
- - **Response Caching** - Automatic response caching with configurable TTL
128
- - **CORS Support** - Configurable CORS with sensible defaults
129
- - **Developer Experience** - Auto route discovery and CLI tools
147
+ Vector provides full type safety with customizable types. Define your types in the config and use them in routes:
130
148
 
131
- ## Familiar Patterns, Modern Performance
149
+ ```typescript
150
+ // vector.config.ts
151
+ import type { VectorConfigSchema, VectorTypes } from "vector-framework";
132
152
 
133
- ### Real-World Example: User Service
153
+ // Define your custom user type
154
+ interface MyUser {
155
+ id: string;
156
+ email: string;
157
+ role: "admin" | "user";
158
+ permissions: string[];
159
+ }
134
160
 
135
- ```typescript
136
- // routes/users.ts
137
- import { route } from "vector-framework";
138
- import { db } from "../db";
161
+ // Extend Vector types
162
+ interface MyAppTypes extends VectorTypes {
163
+ auth: MyUser;
164
+ }
139
165
 
140
- // Public endpoint with caching
141
- export const listUsers = route(
142
- {
143
- method: "GET",
144
- path: "/users",
145
- cache: 300, // Cache for 5 minutes
146
- },
147
- async () => {
148
- const users = await db.user.findMany();
149
- return { users };
150
- }
151
- );
166
+ // Use in config with type parameter
167
+ const config: VectorConfigSchema<MyAppTypes> = {
168
+ port: 3000,
152
169
 
153
- // Protected endpoint with automatic body parsing
154
- export const createUser = route(
155
- {
156
- method: "POST",
157
- path: "/users",
158
- auth: true, // Auth required
170
+ // Auth handler returns your custom type
171
+ auth: async (request): Promise<MyUser> => {
172
+ // Your auth logic
173
+ return {
174
+ id: "user-123",
175
+ email: "user@example.com",
176
+ role: "admin",
177
+ permissions: ["read", "write"],
178
+ };
159
179
  },
160
- async (req) => {
161
- const { name, email } = req.content; // Type-safe, auto-parsed
180
+ };
162
181
 
163
- const user = await db.user.create({
164
- data: { name, email },
165
- });
182
+ export default config;
183
+ export type { MyAppTypes }; // Export for use in routes
184
+ ```
166
185
 
167
- return { user };
168
- }
169
- );
186
+ ```typescript
187
+ // routes/admin.ts
188
+ import { route, APIError } from "vector-framework";
189
+ import type { MyAppTypes } from "../vector.config";
170
190
 
171
- // Parameter extraction made simple
172
- export const getUser = route(
191
+ // Use type parameter to get fully typed request
192
+ export const adminOnly = route<MyAppTypes>(
173
193
  {
174
194
  method: "GET",
175
- path: "/users/:id",
195
+ path: "/admin/data",
196
+ auth: true,
197
+ expose: true,
176
198
  },
177
199
  async (req) => {
178
- const { id } = req.params!;
179
- const user = await db.user.findUnique({ where: { id } });
180
-
181
- if (!user) {
182
- throw new APIError("User not found", 404);
200
+ // req.authUser is now typed as MyUser
201
+ if (req.authUser?.role !== "admin") {
202
+ throw APIError.forbidden("Admin access required");
183
203
  }
184
204
 
185
- return { user };
205
+ // TypeScript knows these properties exist
206
+ return {
207
+ user: req.authUser.email,
208
+ permissions: req.authUser.permissions,
209
+ };
186
210
  }
187
211
  );
188
212
  ```
189
213
 
190
- ## Why Choose Vector?
191
-
192
- ### 🚀 Bun-Powered Performance
214
+ ## Core Features
193
215
 
194
- - **Native TypeScript** execution without transpilation overhead
195
- - **Significantly faster** startup times and request handling
196
- - **Minimal memory footprint** thanks to Bun's efficient runtime
216
+ ### Route Options
197
217
 
198
- ### 💡 Developer Experience
199
-
200
- - **Encore-inspired API** - If you know Encore, you already know Vector
201
- - **Auto route discovery** - Just write your routes, we'll find them
202
- - **Type safety everywhere** - Full TypeScript support
203
- - **Built-in essentials** - Auth, caching, CORS, middleware - all included
218
+ ```typescript
219
+ interface RouteOptions {
220
+ method: string; // HTTP method (GET, POST, etc.)
221
+ path: string; // Route path with params (/users/:id)
222
+ expose?: boolean; // Make route accessible (default: true)
223
+ auth?: boolean; // Require authentication
224
+ cache?: number | { // Cache configuration
225
+ ttl: number; // Time to live in seconds
226
+ key?: string; // Custom cache key
227
+ };
228
+ rawRequest?: boolean; // Skip body parsing
229
+ rawResponse?: boolean; // Return raw response
230
+ responseContentType?: string; // Response content type
231
+ }
232
+ ```
204
233
 
205
- ### 🔄 Easy Migration
234
+ ### Request Object
206
235
 
207
- Moving from Express, Fastify, or Encore? Vector makes it simple:
236
+ Every route handler receives a typed request object:
208
237
 
209
238
  ```typescript
210
- // Encore-style (what you know)
211
- export const get = api(
212
- { expose: true, method: "GET", path: "/hello/:name" },
213
- async ({ name }: { name: string }): Promise<Response> => {
214
- const msg = `Hello ${name}!`;
215
- return { message: msg };
216
- }
217
- );
218
-
219
- // Vector-style (what you write)
220
- export const hello = route(
221
- { expose: true, method: "GET", path: "/hello/:name" },
239
+ export const example = route(
240
+ { method: "POST", path: "/example/:id" },
222
241
  async (req) => {
223
- return { message: `Hello ${req.params.name}!` };
242
+ // All available request properties:
243
+ req.params.id; // URL parameters
244
+ req.query.search; // Query parameters
245
+ req.headers; // Request headers
246
+ req.cookies; // Parsed cookies
247
+ req.content; // Parsed body (JSON/form data)
248
+ req.authUser; // Authenticated user (when auth: true)
249
+ req.context; // Request context
250
+ req.metadata; // Route metadata
224
251
  }
225
252
  );
226
253
  ```
227
254
 
228
- ## For Encore Users
255
+ ### Error Handling
229
256
 
230
- Switching from Encore? You'll feel right at home. Vector provides the same declarative, type-safe API design with the performance benefits of Bun:
231
-
232
- | Encore | Vector |
233
- | ---------------------- | -------------------------------- |
234
- | `api.requireAuth()` | `{ auth: true }` in route config |
235
- | Auto-generated clients | Not yet available |
236
- | Built-in tracing | Middleware support |
237
- | Cloud deployment | Deploy anywhere Bun runs |
238
-
239
- **The key difference:** Vector runs on Bun, giving you significantly better performance and lower resource usage while maintaining the developer experience you love.
240
-
241
- ## Route Options
257
+ Vector provides comprehensive error responses:
242
258
 
243
259
  ```typescript
244
- interface RouteOptions {
245
- method: string; // HTTP method (GET, POST, etc.)
246
- path: string; // Route path with params (/users/:id)
247
- expose?: boolean; // Make route accessible (default: true)
248
- auth?: boolean; // Require authentication (default: false)
249
- cache?:
250
- | number
251
- | {
252
- // Cache configuration
253
- ttl: number; // Time to live in seconds
254
- key?: string; // Custom cache key
255
- };
256
- rawRequest?: boolean; // Skip body parsing (default: false)
257
- rawResponse?: boolean; // Return raw response (default: false)
258
- responseContentType?: string; // Response content type
259
- }
260
- ```
260
+ import { APIError } from "vector-framework";
261
261
 
262
- ## Middleware
262
+ export const example = route(
263
+ { method: "GET", path: "/data/:id" },
264
+ async (req) => {
265
+ // Client errors (4xx)
266
+ if (!req.params.id) {
267
+ throw APIError.badRequest("ID is required");
268
+ }
263
269
 
264
- ### Before Middleware (Pre-handlers)
270
+ const data = await findData(req.params.id);
271
+ if (!data) {
272
+ throw APIError.notFound("Data not found");
273
+ }
265
274
 
266
- ```typescript
267
- const authMiddleware = async (request) => {
268
- // Modify request or return Response to short-circuit
269
- request.customData = "value";
270
- return request;
271
- };
275
+ if (!canAccess(req.authUser, data)) {
276
+ throw APIError.forbidden("Access denied");
277
+ }
278
+
279
+ // Rate limiting
280
+ if (await isRateLimited(req)) {
281
+ throw APIError.tooManyRequests("Please wait before trying again");
282
+ }
272
283
 
273
- const rateLimitMiddleware = async (request) => {
274
- // Return Response to stop processing
275
- if (tooManyRequests) {
276
- return new Response("Too Many Requests", { status: 429 });
284
+ // Server errors (5xx)
285
+ try {
286
+ return await processData(data);
287
+ } catch (error) {
288
+ throw APIError.internalServerError("Processing failed");
289
+ }
277
290
  }
278
- return request;
279
- };
291
+ );
280
292
  ```
281
293
 
282
- ### Finally Middleware (Post-handlers)
294
+ ## Configuration Reference
295
+
296
+ ### VectorConfigSchema
283
297
 
284
298
  ```typescript
285
- const corsMiddleware = async (response, request) => {
286
- // Modify response headers
287
- response.headers.set("X-Custom-Header", "value");
288
- return response;
289
- };
299
+ interface VectorConfigSchema {
300
+ // Server
301
+ port?: number; // Server port (default: 3000)
302
+ hostname?: string; // Server hostname (default: localhost)
303
+ reusePort?: boolean; // Reuse port (default: true)
304
+ development?: boolean; // Development mode
305
+ routesDir?: string; // Routes directory (default: ./routes)
306
+ routeExcludePatterns?: string[]; // Patterns to exclude from route scanning
307
+ idleTimeout?: number; // Idle timeout in seconds
308
+
309
+ // CORS
310
+ cors?: CorsOptions | boolean;
311
+
312
+ // Handlers
313
+ auth?: ProtectedHandler; // Authentication handler
314
+ cache?: CacheHandler; // Cache handler
315
+
316
+ // Middleware
317
+ before?: BeforeMiddleware[]; // Pre-request middleware
318
+ after?: AfterMiddleware[]; // Post-response middleware
319
+ }
290
320
  ```
291
321
 
292
- ## Authentication
293
-
294
- Implement your authentication logic:
322
+ ### Example: Full Configuration
295
323
 
296
324
  ```typescript
297
- vector.protected = async (request) => {
298
- const authHeader = request.headers.get("Authorization");
299
-
300
- if (!authHeader?.startsWith("Bearer ")) {
301
- throw new Error("Invalid authorization header");
302
- }
325
+ // vector.config.ts
326
+ import type { VectorConfigSchema } from "vector-framework";
327
+ import { verifyJWT } from "./lib/auth";
328
+ import { redis } from "./lib/redis";
303
329
 
304
- const token = authHeader.substring(7);
305
- const user = await validateToken(token);
330
+ const config: VectorConfigSchema = {
331
+ port: process.env.PORT || 3000,
332
+ hostname: "0.0.0.0",
333
+ development: process.env.NODE_ENV !== "production",
334
+ routesDir: "./api/routes",
335
+ routeExcludePatterns: ["*.test.ts", "*.spec.ts"], // Optional: custom exclusions
336
+ idleTimeout: 60,
306
337
 
307
- if (!user) {
308
- throw new Error("Invalid token");
309
- }
338
+ cors: {
339
+ origin: ["https://example.com", "https://app.example.com"],
340
+ credentials: true,
341
+ allowHeaders: ["Content-Type", "Authorization", "X-Request-ID"],
342
+ allowMethods: ["GET", "POST", "PUT", "DELETE"],
343
+ maxAge: 86400,
344
+ },
310
345
 
311
- return user; // This will be available as req.authUser
312
- };
313
- ```
346
+ auth: async (request) => {
347
+ const token = request.headers.get("Authorization")?.replace("Bearer ", "");
348
+ if (!token) throw new Error("No token provided");
314
349
 
315
- ## Caching
350
+ const user = await verifyJWT(token);
351
+ if (!user) throw new Error("Invalid token");
316
352
 
317
- Implement your caching strategy:
353
+ return user;
354
+ },
318
355
 
319
- ```typescript
320
- vector.cache = async (key, factory, ttl) => {
321
- // Example with Redis
322
- const cached = await redis.get(key);
323
- if (cached) return JSON.parse(cached);
356
+ cache: async (key, factory, ttl) => {
357
+ const cached = await redis.get(key);
358
+ if (cached) return JSON.parse(cached);
324
359
 
325
- const value = await factory();
326
- await redis.setex(key, ttl, JSON.stringify(value));
360
+ const value = await factory();
361
+ await redis.setex(key, ttl, JSON.stringify(value));
362
+ return value;
363
+ },
327
364
 
328
- return value;
365
+ before: [
366
+ // Logging middleware
367
+ async (request) => {
368
+ request.startTime = Date.now();
369
+ console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`);
370
+ return request;
371
+ },
372
+
373
+ // Request ID middleware
374
+ async (request) => {
375
+ request.id = crypto.randomUUID();
376
+ return request;
377
+ },
378
+ ],
379
+
380
+ after: [
381
+ // Response time header
382
+ async (response, request) => {
383
+ const duration = Date.now() - (request.startTime || Date.now());
384
+ response.headers.set("X-Response-Time", `${duration}ms`);
385
+ return response;
386
+ },
387
+
388
+ // Security headers
389
+ async (response) => {
390
+ response.headers.set("X-Content-Type-Options", "nosniff");
391
+ response.headers.set("X-Frame-Options", "DENY");
392
+ return response;
393
+ },
394
+ ],
329
395
  };
396
+
397
+ export default config;
330
398
  ```
331
399
 
332
- ## Configuration
400
+ ## CLI Commands
333
401
 
334
- ```typescript
335
- interface VectorConfig {
336
- port?: number; // Server port (default: 3000)
337
- hostname?: string; // Server hostname (default: localhost)
338
- reusePort?: boolean; // Reuse port (default: true)
339
- development?: boolean; // Development mode
340
- routesDir?: string; // Routes directory (default: ./routes)
341
- autoDiscover?: boolean; // Auto-discover routes (default: true)
342
- idleTimeout?: number; // Idle timeout in seconds (default: 60)
343
- cors?: CorsOptions; // CORS configuration
344
- before?: BeforeMiddlewareHandler[]; // Pre-request middleware
345
- finally?: AfterMiddlewareHandler[]; // Post-response middleware
346
- }
402
+ ```bash
403
+ # Development server with hot reload
404
+ bun vector dev
347
405
 
348
- interface CorsOptions {
349
- origin?: string | string[] | ((origin: string) => boolean);
350
- credentials?: boolean;
351
- allowHeaders?: string | string[];
352
- allowMethods?: string | string[];
353
- exposeHeaders?: string | string[];
354
- maxAge?: number;
355
- }
356
- ```
406
+ # Production server
407
+ bun vector start
357
408
 
358
- ### Middleware Configuration
409
+ # Build for production
410
+ bun vector build
359
411
 
360
- ```typescript
361
- // Add before middleware (runs before routes)
362
- vector.before(async (request) => {
363
- console.log(`${request.method} ${request.url}`);
364
- return request;
365
- });
366
-
367
- // Add finally middleware (runs after routes)
368
- vector.finally(async (response, request) => {
369
- response.headers.set("X-Response-Time", Date.now() - request.startTime);
370
- return response;
371
- });
412
+ # Command options
413
+ bun vector dev --port 4000 # Custom port
414
+ bun vector dev --host 0.0.0.0 # Custom host
415
+ bun vector dev --routes ./api # Custom routes directory
416
+ bun vector dev --config ./custom.config.ts # Custom config file
372
417
  ```
373
418
 
374
419
  ## Project Structure
375
420
 
376
421
  ```
377
422
  my-app/
378
- ├── routes/ # Auto-discovered routes
379
- ├── users.ts
380
- │ ├── posts.ts
381
- └── health.ts
382
- ├── middleware/ # Custom middleware
423
+ ├── vector.config.ts # Framework configuration
424
+ ├── routes/ # Auto-discovered routes
425
+ │ ├── users.ts # /users endpoints
426
+ ├── posts.ts # /posts endpoints
427
+ ├── users.test.ts # Test file (automatically excluded)
428
+ │ └── admin/ # Nested routes
429
+ │ └── stats.ts # /admin/stats endpoints
430
+ ├── lib/ # Your libraries
383
431
  │ ├── auth.ts
384
- └── logging.ts
385
- ├── server.ts # Main server file
432
+ ├── db.ts
433
+ │ └── redis.ts
386
434
  └── package.json
387
435
  ```
388
436
 
389
- ## TypeScript Support
437
+ ## Route Discovery
390
438
 
391
- Vector is written in TypeScript and provides full type safety with customizable types:
439
+ Vector automatically discovers and loads route files from your `routesDir` (default: `./routes`). By default, the following file patterns are excluded from route scanning:
440
+
441
+ - `*.test.ts`, `*.test.js`, `*.test.tsx`, `*.test.jsx` - Test files
442
+ - `*.spec.ts`, `*.spec.js`, `*.spec.tsx`, `*.spec.jsx` - Spec files
443
+ - `*.tests.ts`, `*.tests.js` - Test suite files
444
+ - `**/__tests__/**` - Test directories
445
+ - `*.interface.ts`, `*.type.ts` - Type definition files
446
+ - `*.d.ts` - TypeScript declaration files
447
+
448
+ You can customize the exclusion patterns using the `routeExcludePatterns` configuration option:
392
449
 
393
450
  ```typescript
394
- import { createVector, route, APIError } from "vector-framework";
395
- import type { VectorRequest, VectorTypes } from "vector-framework";
451
+ // vector.config.ts
452
+ const config: VectorConfigSchema = {
453
+ routesDir: "./routes",
454
+ // Custom exclusion patterns (overrides defaults)
455
+ routeExcludePatterns: [
456
+ "*.test.ts",
457
+ "*.spec.ts",
458
+ "*.mock.ts",
459
+ "_*.ts", // Exclude files starting with underscore
460
+ ],
461
+ };
462
+ ```
396
463
 
397
- // Define your custom user type
398
- interface MyUser {
399
- id: string;
400
- email: string;
401
- role: "admin" | "user";
402
- permissions: string[];
403
- }
464
+ ## Performance
404
465
 
405
- // Extend Vector types
406
- interface MyAppTypes extends VectorTypes {
407
- auth: MyUser;
408
- }
466
+ Vector achieves exceptional performance through:
409
467
 
410
- // Create typed Vector instance
411
- const vector = createVector<MyAppTypes>();
412
-
413
- // Configure authentication with your custom type
414
- vector.protected = async (request): Promise<MyUser> => {
415
- // Your auth logic here
416
- return {
417
- id: "user-123",
418
- email: "user@example.com",
419
- role: "admin",
420
- permissions: ["read", "write"],
421
- };
422
- };
468
+ - **Bun Runtime**: Native TypeScript execution without transpilation
469
+ - **Minimal Dependencies**: Only itty-router (3KB) as dependency
470
+ - **Optimized Routing**: Efficient regex-based route matching
471
+ - **Smart Caching**: Built-in response caching with configurable TTL
423
472
 
424
- // Routes automatically have typed authUser
425
- vector.route(
426
- { method: "GET", path: "/admin", expose: true, auth: true },
427
- async (request) => {
428
- // request.authUser is typed as MyUser
429
- if (request.authUser?.role !== "admin") {
430
- throw APIError.forbidden("Admin access required");
431
- }
432
- return { adminData: "..." };
433
- }
434
- );
435
- ```
473
+ Benchmarks show Vector handling **70,000+ requests/second** on standard hardware.
474
+
475
+ ## Why Vector?
436
476
 
437
- ## Error Handling
477
+ ### For Encore Users
478
+ Love Encore's declarative API design but need more flexibility? Vector provides the same developer experience with the freedom to deploy anywhere Bun runs.
438
479
 
439
- Vector provides comprehensive built-in error responses for all HTTP status codes:
480
+ ### For Express/Fastify Users
481
+ Tired of middleware chains and verbose configurations? Vector's declarative approach makes APIs cleaner and more maintainable.
440
482
 
441
- ### Common Client Errors (4xx)
483
+ ### For New Projects
484
+ Starting fresh? Vector gives you production-ready features from day one with minimal configuration.
485
+
486
+ ## Error Reference
487
+
488
+ Vector provides comprehensive error responses for all HTTP status codes. All errors return a consistent format:
489
+
490
+ ```json
491
+ {
492
+ "error": true,
493
+ "message": "Error message",
494
+ "statusCode": 400,
495
+ "timestamp": "2025-01-01T00:00:00.000Z"
496
+ }
497
+ ```
498
+
499
+ ### Client Errors (4xx)
442
500
 
443
501
  ```typescript
444
502
  import { APIError } from "vector-framework";
445
503
 
446
- // Basic errors
447
- APIError.badRequest("Invalid input"); // 400
448
- APIError.unauthorized("Please login"); // 401
449
- APIError.forbidden("Access denied"); // 403
450
- APIError.notFound("Resource not found"); // 404
451
- APIError.conflict("Resource already exists"); // 409
452
-
453
- // Validation and input errors
454
- APIError.unprocessableEntity("Invalid data"); // 422
455
- APIError.invalidArgument("Field required"); // 422 (alias)
456
- APIError.payloadTooLarge("File too large"); // 413
457
- APIError.unsupportedMediaType("Invalid type"); // 415
458
-
459
- // Rate limiting and timeouts
460
- APIError.tooManyRequests("Rate limit exceeded"); // 429
461
- APIError.rateLimitExceeded("Try again later"); // 429 (alias)
462
- APIError.requestTimeout("Request took too long"); // 408
463
-
464
- // Method and protocol errors
465
- APIError.methodNotAllowed("POST not allowed"); // 405
466
- APIError.notAcceptable("Cannot produce response"); // 406
467
- APIError.preconditionFailed("ETag mismatch"); // 412
504
+ // 400 Bad Request
505
+ APIError.badRequest("Invalid input data");
506
+
507
+ // 401 Unauthorized
508
+ APIError.unauthorized("Authentication required");
509
+
510
+ // 402 Payment Required
511
+ APIError.paymentRequired("Subscription expired");
512
+
513
+ // 403 Forbidden
514
+ APIError.forbidden("Access denied");
515
+
516
+ // 404 Not Found
517
+ APIError.notFound("Resource not found");
518
+
519
+ // 405 Method Not Allowed
520
+ APIError.methodNotAllowed("POST not allowed on this endpoint");
521
+
522
+ // 406 Not Acceptable
523
+ APIError.notAcceptable("Cannot produce requested content type");
524
+
525
+ // 408 Request Timeout
526
+ APIError.requestTimeout("Request took too long");
527
+
528
+ // 409 Conflict
529
+ APIError.conflict("Resource already exists");
530
+
531
+ // 410 Gone
532
+ APIError.gone("Resource permanently deleted");
533
+
534
+ // 411 Length Required
535
+ APIError.lengthRequired("Content-Length header required");
536
+
537
+ // 412 Precondition Failed
538
+ APIError.preconditionFailed("ETag mismatch");
539
+
540
+ // 413 Payload Too Large
541
+ APIError.payloadTooLarge("Request body exceeds limit");
542
+
543
+ // 414 URI Too Long
544
+ APIError.uriTooLong("URL exceeds maximum length");
545
+
546
+ // 415 Unsupported Media Type
547
+ APIError.unsupportedMediaType("Content-Type not supported");
548
+
549
+ // 416 Range Not Satisfiable
550
+ APIError.rangeNotSatisfiable("Requested range cannot be satisfied");
551
+
552
+ // 417 Expectation Failed
553
+ APIError.expectationFailed("Expect header requirements not met");
554
+
555
+ // 418 I'm a Teapot
556
+ APIError.imATeapot("I refuse to brew coffee");
557
+
558
+ // 421 Misdirected Request
559
+ APIError.misdirectedRequest("Request sent to wrong server");
560
+
561
+ // 422 Unprocessable Entity
562
+ APIError.unprocessableEntity("Validation failed");
563
+
564
+ // 423 Locked
565
+ APIError.locked("Resource is locked");
566
+
567
+ // 424 Failed Dependency
568
+ APIError.failedDependency("Dependent request failed");
569
+
570
+ // 425 Too Early
571
+ APIError.tooEarly("Request is too early");
572
+
573
+ // 426 Upgrade Required
574
+ APIError.upgradeRequired("Protocol upgrade required");
575
+
576
+ // 428 Precondition Required
577
+ APIError.preconditionRequired("Precondition headers required");
578
+
579
+ // 429 Too Many Requests
580
+ APIError.tooManyRequests("Rate limit exceeded");
581
+
582
+ // 431 Request Header Fields Too Large
583
+ APIError.requestHeaderFieldsTooLarge("Headers too large");
584
+
585
+ // 451 Unavailable For Legal Reasons
586
+ APIError.unavailableForLegalReasons("Content blocked for legal reasons");
468
587
  ```
469
588
 
470
589
  ### Server Errors (5xx)
471
590
 
472
591
  ```typescript
473
- // Server errors
474
- APIError.internalServerError("Something went wrong"); // 500
475
- APIError.notImplemented("Feature coming soon"); // 501
476
- APIError.badGateway("Upstream server error"); // 502
477
- APIError.serviceUnavailable("Service down"); // 503
478
- APIError.maintenance("Under maintenance"); // 503 (alias)
479
- APIError.gatewayTimeout("Upstream timeout"); // 504
592
+ // 500 Internal Server Error
593
+ APIError.internalServerError("Something went wrong");
594
+
595
+ // 501 Not Implemented
596
+ APIError.notImplemented("Feature not yet available");
597
+
598
+ // 502 Bad Gateway
599
+ APIError.badGateway("Upstream server error");
600
+
601
+ // 503 Service Unavailable
602
+ APIError.serviceUnavailable("Service temporarily down");
603
+
604
+ // 504 Gateway Timeout
605
+ APIError.gatewayTimeout("Upstream server timeout");
606
+
607
+ // 505 HTTP Version Not Supported
608
+ APIError.httpVersionNotSupported("HTTP/3 not supported");
609
+
610
+ // 506 Variant Also Negotiates
611
+ APIError.variantAlsoNegotiates("Content negotiation error");
612
+
613
+ // 507 Insufficient Storage
614
+ APIError.insufficientStorage("Server storage full");
615
+
616
+ // 508 Loop Detected
617
+ APIError.loopDetected("Infinite loop detected");
618
+
619
+ // 510 Not Extended
620
+ APIError.notExtended("Extension required");
621
+
622
+ // 511 Network Authentication Required
623
+ APIError.networkAuthenticationRequired("Network login required");
624
+ ```
625
+
626
+ ### Convenience Aliases
627
+
628
+ ```typescript
629
+ // Alias for 422 Unprocessable Entity
630
+ APIError.invalidArgument("Field 'email' is required");
631
+
632
+ // Alias for 429 Too Many Requests
633
+ APIError.rateLimitExceeded("Try again in 60 seconds");
634
+
635
+ // Alias for 503 Service Unavailable
636
+ APIError.maintenance("Scheduled maintenance in progress");
480
637
  ```
481
638
 
482
639
  ### Custom Errors
483
640
 
484
641
  ```typescript
485
- // Create custom error with any status code
486
- APIError.custom(456, 'Custom error message');
642
+ // Create error with any status code
643
+ APIError.custom(456, "Custom error message");
487
644
 
488
- // All errors include additional metadata
489
- // Response format:
490
- {
491
- "error": true,
492
- "message": "Error message",
493
- "statusCode": 400,
494
- "timestamp": "2025-01-01T00:00:00.000Z"
495
- }
645
+ // With custom content type
646
+ APIError.custom(400, "Invalid XML", "application/xml");
496
647
  ```
497
648
 
498
649
  ### Usage in Routes
499
650
 
500
651
  ```typescript
501
- vector.route(
502
- { method: "GET", path: "/api/data/:id", expose: true },
652
+ export const example = route(
653
+ { method: "POST", path: "/api/users" },
503
654
  async (req) => {
504
- // Validation
505
- if (!req.params?.id) {
506
- throw APIError.badRequest("ID is required");
655
+ // Validation errors
656
+ if (!req.content?.email) {
657
+ throw APIError.badRequest("Email is required");
507
658
  }
508
659
 
509
- // Check rate limits
510
- if (await isRateLimited(req)) {
511
- throw APIError.tooManyRequests("Please wait before trying again");
660
+ if (!isValidEmail(req.content.email)) {
661
+ throw APIError.unprocessableEntity("Invalid email format");
512
662
  }
513
663
 
514
- // Fetch data
515
- const data = await fetchData(req.params.id);
516
- if (!data) {
517
- throw APIError.notFound("Data not found");
664
+ // Authentication errors
665
+ if (!req.authUser) {
666
+ throw APIError.unauthorized("Please login first");
518
667
  }
519
668
 
520
- // Check permissions
521
- if (!canAccess(req.authUser, data)) {
522
- throw APIError.forbidden("You cannot access this resource");
669
+ if (req.authUser.role !== "admin") {
670
+ throw APIError.forbidden("Admin access required");
671
+ }
672
+
673
+ // Resource errors
674
+ const existingUser = await findUserByEmail(req.content.email);
675
+ if (existingUser) {
676
+ throw APIError.conflict("Email already registered");
523
677
  }
524
678
 
525
- return data;
679
+ // Rate limiting
680
+ if (await checkRateLimit(req.authUser.id)) {
681
+ throw APIError.tooManyRequests("Maximum 5 users per hour");
682
+ }
683
+
684
+ try {
685
+ const user = await createUser(req.content);
686
+ return { user };
687
+ } catch (error) {
688
+ // Database errors
689
+ if (error.code === "STORAGE_FULL") {
690
+ throw APIError.insufficientStorage("Database full");
691
+ }
692
+
693
+ // Generic server error
694
+ throw APIError.internalServerError("Failed to create user");
695
+ }
526
696
  }
527
697
  );
528
698
  ```