blaizejs 0.3.1 → 0.3.2
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 +462 -646
- package/dist/{payload-too-large-error-WZMDORKR.js → chunk-3VK325MM.js} +5 -3
- package/dist/{payload-too-large-error-WZMDORKR.js.map → chunk-3VK325MM.js.map} +1 -1
- package/dist/{unsupported-media-type-error-VUXOJ72O.js → chunk-7IM52S7P.js} +5 -3
- package/dist/{unsupported-media-type-error-VUXOJ72O.js.map → chunk-7IM52S7P.js.map} +1 -1
- package/dist/{chunk-ZZEQFU5V.js → chunk-CQKM74J4.js} +4 -3
- package/dist/{chunk-ZZEQFU5V.js.map → chunk-CQKM74J4.js.map} +1 -1
- package/dist/{chunk-3A5J5MKL.js → chunk-HB6MRTGD.js} +4 -3
- package/dist/{chunk-3A5J5MKL.js.map → chunk-HB6MRTGD.js.map} +1 -1
- package/dist/{chunk-SF7ZGOEK.js → chunk-IFP53BNM.js} +3 -2
- package/dist/{chunk-SF7ZGOEK.js.map → chunk-IFP53BNM.js.map} +1 -1
- package/dist/index.cjs +46 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +56 -19
- package/dist/index.js.map +1 -1
- package/dist/{internal-server-error-PKVC3ZEU.js → internal-server-error-PVME2DGN.js} +5 -4
- package/dist/payload-too-large-error-QQG7MKGT.js +17 -0
- package/dist/unsupported-media-type-error-VVHRDTUH.js +17 -0
- package/dist/unsupported-media-type-error-VVHRDTUH.js.map +1 -0
- package/dist/{validation-error-WZFF75S7.js → validation-error-TXMSFWZL.js} +5 -4
- package/dist/validation-error-TXMSFWZL.js.map +1 -0
- package/package.json +4 -4
- /package/dist/{internal-server-error-PKVC3ZEU.js.map → internal-server-error-PVME2DGN.js.map} +0 -0
- /package/dist/{validation-error-WZFF75S7.js.map → payload-too-large-error-QQG7MKGT.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🔥 BlaizeJS Core
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **Type-safe, blazing-fast Node.js framework** with HTTP/2 support, file-based routing, powerful middleware system, and end-to-end type safety for building modern APIs
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/blaizejs)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -11,28 +11,27 @@
|
|
|
11
11
|
- [🌟 Features](#-features)
|
|
12
12
|
- [📦 Installation](#-installation)
|
|
13
13
|
- [🚀 Quick Start](#-quick-start)
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [✅ Testing](#-testing)
|
|
21
|
-
- [🤝 Contributing](#-contributing)
|
|
14
|
+
- [📖 Core Modules](#-core-modules)
|
|
15
|
+
- [🛡️ Error Handling](#️-error-handling)
|
|
16
|
+
- [🎯 API Reference](#-api-reference)
|
|
17
|
+
- [💡 Common Patterns](#-common-patterns)
|
|
18
|
+
- [🧪 Testing](#-testing)
|
|
19
|
+
- [📚 Type System](#-type-system)
|
|
22
20
|
- [🗺️ Roadmap](#️-roadmap)
|
|
21
|
+
- [🤝 Contributing](#-contributing)
|
|
23
22
|
|
|
24
23
|
## 🌟 Features
|
|
25
24
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
25
|
+
- 🚀 **HTTP/2 by Default** - Modern protocol with automatic HTTPS in development
|
|
26
|
+
- 📁 **File-Based Routing** - Routes auto-discovered from file structure *(internal)*
|
|
27
|
+
- 🔧 **Composable Middleware** - Build reusable request/response pipelines
|
|
28
|
+
- 🧩 **Plugin System** - Extend server functionality with lifecycle hooks
|
|
29
|
+
- ✅ **Schema Validation** - Built-in Zod validation for type safety
|
|
30
|
+
- 🛡️ **Semantic Errors** - Rich error classes with automatic formatting
|
|
31
|
+
- 🔗 **Context Management** - AsyncLocalStorage-powered state isolation *(internal)*
|
|
32
|
+
- ⚡ **Zero Configuration** - Works out of the box with sensible defaults
|
|
33
|
+
- 📊 **Type Inference** - Full TypeScript support with automatic types
|
|
34
|
+
- 🔄 **Hot Reloading** - Development mode with automatic route updates
|
|
36
35
|
|
|
37
36
|
## 📦 Installation
|
|
38
37
|
|
|
@@ -47,389 +46,177 @@ npm install blaizejs
|
|
|
47
46
|
yarn add blaizejs
|
|
48
47
|
```
|
|
49
48
|
|
|
50
|
-
### 📋 Requirements
|
|
51
|
-
|
|
52
|
-
- **Node.js**: >= 22.0.0 (LTS recommended)
|
|
53
|
-
- **TypeScript**: >= 5.8.3 (for development)
|
|
54
|
-
- **Package Manager**: pnpm 9.7.0+ (recommended)
|
|
55
|
-
|
|
56
49
|
## 🚀 Quick Start
|
|
57
50
|
|
|
58
|
-
###
|
|
51
|
+
### Creating Your First Server
|
|
59
52
|
|
|
60
53
|
```typescript
|
|
61
|
-
import { createServer } from 'blaizejs';
|
|
54
|
+
import { createServer, createGetRoute, createPostRoute } from 'blaizejs';
|
|
62
55
|
import { fileURLToPath } from 'node:url';
|
|
63
56
|
import path from 'node:path';
|
|
57
|
+
import { z } from 'zod';
|
|
64
58
|
|
|
65
|
-
//
|
|
59
|
+
// ESM path resolution (required for route discovery)
|
|
66
60
|
const __filename = fileURLToPath(import.meta.url);
|
|
67
61
|
const __dirname = path.dirname(__filename);
|
|
68
62
|
|
|
69
|
-
// Create server with
|
|
63
|
+
// Create server with file-based routing
|
|
70
64
|
const server = createServer({
|
|
71
|
-
|
|
65
|
+
port: 3000,
|
|
66
|
+
host: 'localhost',
|
|
67
|
+
routesDir: path.resolve(__dirname, './routes')
|
|
72
68
|
});
|
|
73
69
|
|
|
74
70
|
await server.listen();
|
|
75
|
-
console.log(
|
|
71
|
+
console.log(`🚀 Server running at https://localhost:3000`);
|
|
76
72
|
```
|
|
77
73
|
|
|
78
|
-
###
|
|
74
|
+
### Creating Routes
|
|
75
|
+
|
|
76
|
+
Create route files in your routes directory:
|
|
79
77
|
|
|
80
78
|
```typescript
|
|
81
|
-
// routes/users.ts
|
|
82
|
-
import { createGetRoute,
|
|
79
|
+
// routes/users/[userId].ts
|
|
80
|
+
import { createGetRoute, createPutRoute, NotFoundError } from 'blaizejs';
|
|
83
81
|
import { z } from 'zod';
|
|
84
82
|
|
|
85
|
-
// GET /users
|
|
86
|
-
export const
|
|
83
|
+
// GET /users/:userId
|
|
84
|
+
export const GET = createGetRoute({
|
|
87
85
|
schema: {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
search: z.string().optional(),
|
|
91
|
-
}),
|
|
92
|
-
response: z.object({
|
|
93
|
-
users: z.array(
|
|
94
|
-
z.object({
|
|
95
|
-
id: z.string(),
|
|
96
|
-
name: z.string(),
|
|
97
|
-
email: z.string(),
|
|
98
|
-
})
|
|
99
|
-
),
|
|
100
|
-
total: z.number(),
|
|
101
|
-
}),
|
|
102
|
-
},
|
|
103
|
-
handler: async ctx => {
|
|
104
|
-
// Query params are automatically typed and validated
|
|
105
|
-
const { limit, search } = ctx.request.query;
|
|
106
|
-
|
|
107
|
-
const users = await findUsers({ limit, search });
|
|
108
|
-
return { users, total: users.length };
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// POST /users - Create user with validation
|
|
113
|
-
export const createUser = createPostRoute({
|
|
114
|
-
schema: {
|
|
115
|
-
body: z.object({
|
|
116
|
-
name: z.string().min(1),
|
|
117
|
-
email: z.string().email(),
|
|
86
|
+
params: z.object({
|
|
87
|
+
userId: z.string().uuid()
|
|
118
88
|
}),
|
|
119
89
|
response: z.object({
|
|
120
90
|
id: z.string(),
|
|
121
91
|
name: z.string(),
|
|
122
|
-
email: z.string()
|
|
123
|
-
|
|
124
|
-
}),
|
|
92
|
+
email: z.string()
|
|
93
|
+
})
|
|
125
94
|
},
|
|
126
|
-
handler: async ctx => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
95
|
+
handler: async (ctx, params) => {
|
|
96
|
+
const user = await db.users.findById(params.userId);
|
|
97
|
+
|
|
98
|
+
if (!user) {
|
|
99
|
+
throw new NotFoundError('User not found', {
|
|
100
|
+
resourceType: 'user',
|
|
101
|
+
resourceId: params.userId
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
131
105
|
return user;
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### 🔗 Add Middleware
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
import { createServer, createMiddleware } from 'blaizejs';
|
|
140
|
-
import { fileURLToPath } from 'node:url';
|
|
141
|
-
import path from 'node:path';
|
|
142
|
-
|
|
143
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
144
|
-
const __dirname = path.dirname(__filename);
|
|
145
|
-
|
|
146
|
-
// Create logging middleware
|
|
147
|
-
const logger = createMiddleware({
|
|
148
|
-
name: 'logger',
|
|
149
|
-
handler: async (ctx, next) => {
|
|
150
|
-
const start = Date.now();
|
|
151
|
-
console.log(`→ ${ctx.request.method} ${ctx.request.path}`);
|
|
152
|
-
|
|
153
|
-
await next();
|
|
154
|
-
|
|
155
|
-
const duration = Date.now() - start;
|
|
156
|
-
console.log(`← ${ctx.response.raw.statusCode} (${duration}ms)`);
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Create server with middleware
|
|
161
|
-
const server = createServer({
|
|
162
|
-
routesDir: path.resolve(__dirname, './routes'),
|
|
163
|
-
middleware: [logger],
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
await server.listen();
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## 🏗️ Architecture Overview
|
|
170
|
-
|
|
171
|
-
BlaizeJS is built around five core modules that work together seamlessly:
|
|
172
|
-
|
|
173
|
-
```mermaid
|
|
174
|
-
graph TD
|
|
175
|
-
A[Server] --> B[Router]
|
|
176
|
-
A --> C[Context]
|
|
177
|
-
B --> D[Middleware]
|
|
178
|
-
A --> E[Plugins]
|
|
179
|
-
|
|
180
|
-
B --> F[File-based Routes]
|
|
181
|
-
C --> G[AsyncLocalStorage]
|
|
182
|
-
D --> H[Composable Pipeline]
|
|
183
|
-
E --> I[Lifecycle Management]
|
|
184
|
-
|
|
185
|
-
F --> J[Type-safe Handlers]
|
|
186
|
-
G --> K[State Management]
|
|
187
|
-
H --> L[Request/Response Flow]
|
|
188
|
-
I --> M[Plugin Integration]
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### 🔄 Request Lifecycle
|
|
192
|
-
|
|
193
|
-
1. **Server** receives HTTP request
|
|
194
|
-
2. **Context** creates request/response wrappers with AsyncLocalStorage
|
|
195
|
-
3. **Router** matches request to file-based route
|
|
196
|
-
4. **Middleware** executes in onion-style pattern
|
|
197
|
-
5. **Route Handler** processes request with full type safety
|
|
198
|
-
6. **Context** sends validated response
|
|
199
|
-
|
|
200
|
-
## 📁 Project Structure
|
|
201
|
-
|
|
202
|
-
### 🎯 Recommended Structure
|
|
203
|
-
|
|
204
|
-
```
|
|
205
|
-
my-blaize-app/
|
|
206
|
-
├── src/
|
|
207
|
-
│ ├── server.ts # Server entry point
|
|
208
|
-
│ ├── app-routes.ts # Route registry for blaize client
|
|
209
|
-
│ ├── routes/ # File-based routes
|
|
210
|
-
│ │ ├── index.ts # → /
|
|
211
|
-
│ │ ├── users.ts # → /users
|
|
212
|
-
│ │ ├── users/
|
|
213
|
-
│ │ │ └── [id].ts # → /users/:id
|
|
214
|
-
│ │ └── api/
|
|
215
|
-
│ │ └── v1/
|
|
216
|
-
│ │ └── posts.ts # → /api/v1/posts
|
|
217
|
-
│ ├── middleware/ # Custom middleware
|
|
218
|
-
│ ├── plugins/ # Custom plugins
|
|
219
|
-
│ └── types/ # Shared types
|
|
220
|
-
├── tests/ # Test files
|
|
221
|
-
├── package.json
|
|
222
|
-
└── tsconfig.json
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### 📊 Module Responsibilities
|
|
226
|
-
|
|
227
|
-
| Module | Purpose | Key Features |
|
|
228
|
-
| -------------- | ------------------------- | -------------------------- |
|
|
229
|
-
| **Server** | HTTP server management | HTTP/2, SSL, lifecycle |
|
|
230
|
-
| **Router** | Request routing | File-based, type-safe |
|
|
231
|
-
| **Context** | Request/response handling | AsyncLocalStorage, state |
|
|
232
|
-
| **Middleware** | Request processing | Composable, error handling |
|
|
233
|
-
| **Plugins** | Framework extension | Lifecycle, validation |
|
|
234
|
-
|
|
235
|
-
## 🌐 Production Deployment
|
|
236
|
-
|
|
237
|
-
### ⚠️ HTTP/2 Hosting Limitations
|
|
238
|
-
|
|
239
|
-
BlaizeJS defaults to HTTP/2 for optimal performance, but many hosting providers don't expose SSL certificate access required for HTTP/2:
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
// Production configuration for hosting providers
|
|
243
|
-
const server = createServer({
|
|
244
|
-
routesDir: path.resolve(__dirname, './routes'),
|
|
245
|
-
http2: {
|
|
246
|
-
// Disable HTTP/2 if certificates aren't accessible
|
|
247
|
-
enabled: process.env.HTTP2_ENABLED === 'true',
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### 🔧 Hosting Provider Configurations
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
// Vercel/Netlify (HTTP/1.1 only)
|
|
256
|
-
const server = createServer({
|
|
257
|
-
port: parseInt(process.env.PORT || '3000'),
|
|
258
|
-
routesDir: path.resolve(__dirname, './routes'),
|
|
259
|
-
http2: { enabled: false },
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// VPS/Dedicated (HTTP/2 with Let's Encrypt)
|
|
263
|
-
const server = createServer({
|
|
264
|
-
port: 443,
|
|
265
|
-
host: '0.0.0.0',
|
|
266
|
-
routesDir: path.resolve(__dirname, './routes'),
|
|
267
|
-
http2: {
|
|
268
|
-
enabled: true,
|
|
269
|
-
keyFile: '/etc/letsencrypt/live/yourdomain.com/privkey.pem',
|
|
270
|
-
certFile: '/etc/letsencrypt/live/yourdomain.com/fullchain.pem',
|
|
271
|
-
},
|
|
106
|
+
}
|
|
272
107
|
});
|
|
273
108
|
|
|
274
|
-
//
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
109
|
+
// PUT /users/:userId
|
|
110
|
+
export const PUT = createPutRoute({
|
|
111
|
+
schema: {
|
|
112
|
+
params: z.object({
|
|
113
|
+
userId: z.string().uuid()
|
|
114
|
+
}),
|
|
115
|
+
body: z.object({
|
|
116
|
+
name: z.string().min(1),
|
|
117
|
+
email: z.string().email()
|
|
118
|
+
})
|
|
283
119
|
},
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
### 🚀 Environment Configuration
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
// Environment-aware server setup
|
|
291
|
-
const getServerConfig = () => {
|
|
292
|
-
const env = process.env.NODE_ENV || 'development';
|
|
293
|
-
|
|
294
|
-
switch (env) {
|
|
295
|
-
case 'development':
|
|
296
|
-
return {
|
|
297
|
-
port: 3000,
|
|
298
|
-
routesDir: path.resolve(__dirname, './routes'),
|
|
299
|
-
http2: { enabled: true }, // Auto-generates certs
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
case 'production':
|
|
303
|
-
return {
|
|
304
|
-
port: parseInt(process.env.PORT || '443'),
|
|
305
|
-
host: '0.0.0.0',
|
|
306
|
-
routesDir: path.resolve(__dirname, './dist/routes'),
|
|
307
|
-
http2: {
|
|
308
|
-
enabled: !!process.env.SSL_CERT_PATH,
|
|
309
|
-
keyFile: process.env.SSL_KEY_PATH,
|
|
310
|
-
certFile: process.env.SSL_CERT_PATH,
|
|
311
|
-
},
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
case 'test':
|
|
315
|
-
return {
|
|
316
|
-
port: 0,
|
|
317
|
-
routesDir: path.resolve(__dirname, './test-fixtures/routes'),
|
|
318
|
-
http2: { enabled: false },
|
|
319
|
-
};
|
|
120
|
+
handler: async (ctx, params) => {
|
|
121
|
+
const updatedUser = await db.users.update(params.userId, ctx.body);
|
|
122
|
+
return updatedUser;
|
|
320
123
|
}
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const server = createServer(getServerConfig());
|
|
124
|
+
});
|
|
324
125
|
```
|
|
325
126
|
|
|
326
|
-
|
|
127
|
+
## 📖 Core Modules
|
|
327
128
|
|
|
328
|
-
|
|
129
|
+
BlaizeJS Core consists of several integrated modules. Some are exported for direct use, while others work internally:
|
|
329
130
|
|
|
330
|
-
### 🌐 Server Module
|
|
131
|
+
### 🌐 Server Module *(Exported)*
|
|
331
132
|
|
|
332
|
-
|
|
133
|
+
Create HTTP/2 servers with automatic HTTPS, middleware, and plugins:
|
|
333
134
|
|
|
334
135
|
```typescript
|
|
335
|
-
import { createServer } from 'blaizejs';
|
|
136
|
+
import { createServer, createMiddleware, createPlugin } from 'blaizejs';
|
|
336
137
|
|
|
337
138
|
const server = createServer({
|
|
338
139
|
port: 3000,
|
|
339
140
|
routesDir: './routes',
|
|
340
|
-
http2: { enabled: true },
|
|
141
|
+
http2: { enabled: true }, // Auto-generates dev certificates
|
|
142
|
+
middleware: [loggingMiddleware],
|
|
143
|
+
plugins: [metricsPlugin()]
|
|
341
144
|
});
|
|
342
145
|
|
|
343
|
-
//
|
|
344
|
-
server.
|
|
345
|
-
|
|
146
|
+
// Add middleware after creation
|
|
147
|
+
server.use(corsMiddleware);
|
|
148
|
+
|
|
149
|
+
// Register plugins dynamically
|
|
150
|
+
await server.register(databasePlugin());
|
|
346
151
|
|
|
347
152
|
await server.listen();
|
|
348
153
|
```
|
|
349
154
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
### 🚀 Router Module
|
|
353
|
-
|
|
354
|
-
File-based routing with automatic path generation and type safety.
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
import { createGetRoute } from 'blaizejs';
|
|
358
|
-
import { z } from 'zod';
|
|
359
|
-
|
|
360
|
-
// routes/posts/[id].ts → GET /posts/:id
|
|
361
|
-
export const getPost = createGetRoute({
|
|
362
|
-
schema: {
|
|
363
|
-
params: z.object({ id: z.string().uuid() }),
|
|
364
|
-
response: z.object({
|
|
365
|
-
id: z.string(),
|
|
366
|
-
title: z.string(),
|
|
367
|
-
content: z.string(),
|
|
368
|
-
}),
|
|
369
|
-
},
|
|
370
|
-
handler: async (ctx, params) => {
|
|
371
|
-
return await findPost(params.id);
|
|
372
|
-
},
|
|
373
|
-
});
|
|
374
|
-
```
|
|
155
|
+
### 🚀 Router Module *(Partially Exported)*
|
|
375
156
|
|
|
376
|
-
|
|
157
|
+
**⚠️ Note**: The router itself is internal. Only route creation functions are exported.
|
|
377
158
|
|
|
378
|
-
|
|
159
|
+
#### Available Exports:
|
|
160
|
+
- ✅ `createGetRoute` - Create GET endpoints
|
|
161
|
+
- ✅ `createPostRoute` - Create POST endpoints
|
|
162
|
+
- ✅ `createPutRoute` - Create PUT endpoints
|
|
163
|
+
- ✅ `createPatchRoute` - Create PATCH endpoints
|
|
164
|
+
- ✅ `createDeleteRoute` - Create DELETE endpoints
|
|
165
|
+
- ✅ `createHeadRoute` - Create HEAD endpoints
|
|
166
|
+
- ✅ `createOptionsRoute` - Create OPTIONS endpoints
|
|
379
167
|
|
|
380
|
-
|
|
168
|
+
#### Internal (Not Exported):
|
|
169
|
+
- ❌ `Router` interface - Used internally by server
|
|
170
|
+
- ❌ `Matcher` - Internal route matching
|
|
171
|
+
- ❌ `extractParams` - Internal parameter extraction
|
|
172
|
+
- ❌ Route discovery utilities - Internal file system operations
|
|
381
173
|
|
|
382
174
|
```typescript
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
// Request data
|
|
389
|
-
const userAgent = ctx.request.header('user-agent');
|
|
390
|
-
const body = ctx.request.body;
|
|
391
|
-
|
|
392
|
-
// State management
|
|
393
|
-
setState('userId', '123');
|
|
394
|
-
const userId = getState<string>('userId');
|
|
175
|
+
// ✅ This is how you use routing:
|
|
176
|
+
export const GET = createGetRoute({
|
|
177
|
+
schema: { /* ... */ },
|
|
178
|
+
handler: async (ctx) => { /* ... */ }
|
|
179
|
+
});
|
|
395
180
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
};
|
|
181
|
+
// ❌ You cannot directly access the router:
|
|
182
|
+
// import { Router } from 'blaizejs'; // NOT AVAILABLE
|
|
399
183
|
```
|
|
400
184
|
|
|
401
|
-
|
|
185
|
+
### 🔧 Middleware Module *(Exported)*
|
|
402
186
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
Composable middleware with onion-style execution.
|
|
187
|
+
Build composable request/response pipelines:
|
|
406
188
|
|
|
407
189
|
```typescript
|
|
408
190
|
import { createMiddleware, compose } from 'blaizejs';
|
|
409
191
|
|
|
192
|
+
// Simple middleware
|
|
193
|
+
const logger = createMiddleware(async (ctx, next) => {
|
|
194
|
+
console.log(`→ ${ctx.request.method} ${ctx.request.path}`);
|
|
195
|
+
await next();
|
|
196
|
+
console.log(`← ${ctx.response.statusCode}`);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Middleware with options
|
|
410
200
|
const auth = createMiddleware({
|
|
411
201
|
name: 'auth',
|
|
412
202
|
handler: async (ctx, next) => {
|
|
413
|
-
// Pre-processing
|
|
414
203
|
const token = ctx.request.header('authorization');
|
|
415
|
-
if (!token)
|
|
416
|
-
|
|
204
|
+
if (!token) {
|
|
205
|
+
throw new UnauthorizedError('No token provided');
|
|
206
|
+
}
|
|
207
|
+
ctx.state.user = await verifyToken(token);
|
|
417
208
|
await next();
|
|
418
|
-
|
|
419
|
-
// Post-processing
|
|
420
|
-
ctx.response.header('X-Authenticated', 'true');
|
|
421
209
|
},
|
|
210
|
+
skip: ctx => ctx.request.path.startsWith('/public')
|
|
422
211
|
});
|
|
423
212
|
|
|
424
213
|
// Compose multiple middleware
|
|
425
214
|
const apiMiddleware = compose([cors, auth, rateLimit]);
|
|
426
215
|
```
|
|
427
216
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
### 🧩 Plugins Module
|
|
217
|
+
### 🧩 Plugins Module *(Exported)*
|
|
431
218
|
|
|
432
|
-
|
|
219
|
+
Extend server functionality with lifecycle hooks:
|
|
433
220
|
|
|
434
221
|
```typescript
|
|
435
222
|
import { createPlugin } from 'blaizejs';
|
|
@@ -437,420 +224,449 @@ import { createPlugin } from 'blaizejs';
|
|
|
437
224
|
const databasePlugin = createPlugin(
|
|
438
225
|
'database',
|
|
439
226
|
'1.0.0',
|
|
440
|
-
|
|
441
|
-
let
|
|
442
|
-
|
|
227
|
+
(server) => {
|
|
228
|
+
let connection;
|
|
229
|
+
|
|
443
230
|
return {
|
|
444
231
|
initialize: async () => {
|
|
445
|
-
|
|
446
|
-
|
|
232
|
+
connection = await db.connect();
|
|
233
|
+
console.log('Database connected');
|
|
447
234
|
},
|
|
448
|
-
|
|
449
|
-
await
|
|
235
|
+
onServerStart: async () => {
|
|
236
|
+
await connection.migrate();
|
|
237
|
+
},
|
|
238
|
+
onServerStop: async () => {
|
|
239
|
+
await connection.close();
|
|
450
240
|
},
|
|
241
|
+
terminate: async () => {
|
|
242
|
+
console.log('Database plugin terminated');
|
|
243
|
+
}
|
|
451
244
|
};
|
|
452
|
-
}
|
|
453
|
-
{ connectionString: 'mongodb://localhost:27017/app' }
|
|
245
|
+
}
|
|
454
246
|
);
|
|
455
247
|
|
|
248
|
+
// Use in server
|
|
456
249
|
const server = createServer({
|
|
457
|
-
|
|
458
|
-
plugins: [databasePlugin()],
|
|
250
|
+
plugins: [databasePlugin()]
|
|
459
251
|
});
|
|
460
252
|
```
|
|
461
253
|
|
|
462
|
-
|
|
254
|
+
### 🔗 Context Module *(Internal)*
|
|
463
255
|
|
|
464
|
-
|
|
256
|
+
**⚠️ Note**: Context is automatically managed. You interact with it in handlers.
|
|
465
257
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
| Plugin | Purpose | Status |
|
|
469
|
-
| ----------------------------- | ------------------------------ | -------------- |
|
|
470
|
-
| `@blaizejs/auth-plugin` | Authentication & authorization | 🔄 Coming Soon |
|
|
471
|
-
| `@blaizejs/database-plugin` | Database integration | 🔄 Coming Soon |
|
|
472
|
-
| `@blaizejs/cache-plugin` | Caching strategies | 🔄 Coming Soon |
|
|
473
|
-
| `@blaizejs/validation-plugin` | Enhanced validation | 🔄 Coming Soon |
|
|
474
|
-
| `@blaizejs/monitoring-plugin` | Metrics & observability | 🔄 Coming Soon |
|
|
475
|
-
|
|
476
|
-
### 🛠️ Creating Custom Plugins
|
|
258
|
+
Context is automatically provided to all route handlers and middleware:
|
|
477
259
|
|
|
478
260
|
```typescript
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
// Add routes
|
|
497
|
-
server.router.addRoute('GET', '/plugin-route', {
|
|
498
|
-
handler: () => ({ message: 'From plugin' }),
|
|
499
|
-
});
|
|
500
|
-
},
|
|
501
|
-
{
|
|
502
|
-
/* default options */
|
|
261
|
+
// Context is the first parameter in handlers
|
|
262
|
+
export const GET = createGetRoute({
|
|
263
|
+
handler: async (ctx) => {
|
|
264
|
+
// Request information
|
|
265
|
+
const userId = ctx.request.header('x-user-id');
|
|
266
|
+
const query = ctx.request.query;
|
|
267
|
+
|
|
268
|
+
// State management
|
|
269
|
+
ctx.state.requestStart = Date.now();
|
|
270
|
+
|
|
271
|
+
// Response methods (usually return instead)
|
|
272
|
+
// ctx.response.json({ data });
|
|
273
|
+
// ctx.response.redirect('/login');
|
|
274
|
+
|
|
275
|
+
return { message: 'Hello' };
|
|
503
276
|
}
|
|
504
|
-
);
|
|
277
|
+
});
|
|
505
278
|
```
|
|
506
279
|
|
|
507
|
-
##
|
|
280
|
+
## 🛡️ Error Handling
|
|
508
281
|
|
|
509
|
-
|
|
282
|
+
### Available Error Classes
|
|
510
283
|
|
|
511
|
-
|
|
284
|
+
BlaizeJS exports semantic error classes that automatically format responses:
|
|
512
285
|
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
|
|
286
|
+
```typescript
|
|
287
|
+
import {
|
|
288
|
+
ValidationError, // 400 - Bad Request
|
|
289
|
+
UnauthorizedError, // 401 - Authentication Required
|
|
290
|
+
ForbiddenError, // 403 - Access Denied
|
|
291
|
+
NotFoundError, // 404 - Resource Not Found
|
|
292
|
+
RequestTimeoutError, // 408 - Request Timeout
|
|
293
|
+
ConflictError, // 409 - Resource Conflict
|
|
294
|
+
PayloadTooLargeError, // 413 - Payload Too Large
|
|
295
|
+
UnsupportedMediaTypeError, // 415 - Unsupported Media Type
|
|
296
|
+
UnprocessableEntityError, // 422 - Unprocessable Entity
|
|
297
|
+
RateLimitError, // 429 - Too Many Requests
|
|
298
|
+
InternalServerError // 500 - Server Error
|
|
299
|
+
} from 'blaizejs';
|
|
300
|
+
|
|
301
|
+
// Throw semantic errors
|
|
302
|
+
throw new NotFoundError('User not found', {
|
|
303
|
+
resourceType: 'user',
|
|
304
|
+
resourceId: params.userId,
|
|
305
|
+
suggestion: 'Check the user ID'
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Automatic response format:
|
|
309
|
+
// {
|
|
310
|
+
// "type": "NOT_FOUND",
|
|
311
|
+
// "title": "User not found",
|
|
312
|
+
// "status": 404,
|
|
313
|
+
// "correlationId": "req_abc123",
|
|
314
|
+
// "timestamp": "2024-01-15T10:30:00.000Z",
|
|
315
|
+
// "details": { ... }
|
|
316
|
+
// }
|
|
516
317
|
```
|
|
517
318
|
|
|
518
|
-
###
|
|
319
|
+
### Additional Error Classes
|
|
519
320
|
|
|
520
|
-
|
|
321
|
+
The following error classes are also available for specific scenarios:
|
|
521
322
|
|
|
522
323
|
```typescript
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
handler: async ctx => {
|
|
538
|
-
const { name } = ctx.request.query;
|
|
539
|
-
return {
|
|
540
|
-
message: `Hello ${name || 'World'}!`,
|
|
541
|
-
timestamp: new Date().toISOString(),
|
|
542
|
-
};
|
|
543
|
-
},
|
|
324
|
+
import {
|
|
325
|
+
PayloadTooLargeError, // 413 - Request Entity Too Large
|
|
326
|
+
UnsupportedMediaTypeError, // 415 - Unsupported Media Type
|
|
327
|
+
RequestTimeoutError, // 408 - Request Timeout
|
|
328
|
+
UnprocessableEntityError // 422 - Unprocessable Entity
|
|
329
|
+
} from 'blaizejs';
|
|
330
|
+
|
|
331
|
+
// File size exceeded
|
|
332
|
+
throw new PayloadTooLargeError('File too large', {
|
|
333
|
+
fileCount: 11,
|
|
334
|
+
maxFiles: 10,
|
|
335
|
+
filename: 'huge-video.mp4',
|
|
336
|
+
currentSize: 104857600, // 100MB
|
|
337
|
+
maxSize: 52428800 // 50MB
|
|
544
338
|
});
|
|
545
339
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
response: z.object({
|
|
552
|
-
id: z.string(),
|
|
553
|
-
echo: z.string(),
|
|
554
|
-
}),
|
|
555
|
-
},
|
|
556
|
-
handler: async ctx => {
|
|
557
|
-
const { message } = ctx.request.body;
|
|
558
|
-
return {
|
|
559
|
-
id: crypto.randomUUID(),
|
|
560
|
-
echo: message,
|
|
561
|
-
};
|
|
562
|
-
},
|
|
340
|
+
// Wrong content type
|
|
341
|
+
throw new UnsupportedMediaTypeError('File type not allowed', {
|
|
342
|
+
receivedMimeType: 'application/x-executable',
|
|
343
|
+
allowedMimeTypes: ['image/jpeg', 'image/png', 'application/pdf'],
|
|
344
|
+
filename: 'virus.exe'
|
|
563
345
|
});
|
|
564
|
-
```
|
|
565
346
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
347
|
+
// Request timeout
|
|
348
|
+
throw new RequestTimeoutError('Upload timeout', {
|
|
349
|
+
timeoutMs: 30000,
|
|
350
|
+
elapsedMs: 31000,
|
|
351
|
+
operation: 'file-upload'
|
|
352
|
+
});
|
|
569
353
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
354
|
+
// Business rule violation
|
|
355
|
+
throw new UnprocessableEntityError('Business rule violation', {
|
|
356
|
+
rule: 'minimum_order_amount',
|
|
357
|
+
currentValue: 5.00,
|
|
358
|
+
requiredValue: 10.00,
|
|
359
|
+
message: 'Order total must be at least $10.00'
|
|
360
|
+
});
|
|
574
361
|
```
|
|
575
362
|
|
|
576
|
-
|
|
363
|
+
## 🎯 API Reference
|
|
364
|
+
|
|
365
|
+
### Exported Functions
|
|
366
|
+
|
|
367
|
+
| Function | Description |
|
|
368
|
+
|----------|-------------|
|
|
369
|
+
| **Server** | |
|
|
370
|
+
| `createServer(options?)` | Create HTTP/2 server instance |
|
|
371
|
+
| **Routing** | |
|
|
372
|
+
| `createGetRoute(config)` | Create GET endpoint |
|
|
373
|
+
| `createPostRoute(config)` | Create POST endpoint |
|
|
374
|
+
| `createPutRoute(config)` | Create PUT endpoint |
|
|
375
|
+
| `createPatchRoute(config)` | Create PATCH endpoint |
|
|
376
|
+
| `createDeleteRoute(config)` | Create DELETE endpoint |
|
|
377
|
+
| `createHeadRoute(config)` | Create HEAD endpoint |
|
|
378
|
+
| `createOptionsRoute(config)` | Create OPTIONS endpoint |
|
|
379
|
+
| **Middleware** | |
|
|
380
|
+
| `createMiddleware(handler)` | Create middleware instance |
|
|
381
|
+
| `compose(middleware[])` | Compose multiple middleware |
|
|
382
|
+
| **Plugins** | |
|
|
383
|
+
| `createPlugin(name, version, factory)` | Create server plugin |
|
|
384
|
+
| **Errors** | |
|
|
385
|
+
| `ValidationError` | 400 Bad Request |
|
|
386
|
+
| `UnauthorizedError` | 401 Unauthorized |
|
|
387
|
+
| `ForbiddenError` | 403 Forbidden |
|
|
388
|
+
| `NotFoundError` | 404 Not Found |
|
|
389
|
+
| `RequestTimeoutError` | 408 Request Timeout |
|
|
390
|
+
| `ConflictError` | 409 Conflict |
|
|
391
|
+
| `PayloadTooLargeError` | 413 Payload Too Large |
|
|
392
|
+
| `UnsupportedMediaTypeError` | 415 Unsupported Media Type |
|
|
393
|
+
| `UnprocessableEntityError` | 422 Unprocessable Entity |
|
|
394
|
+
| `RateLimitError` | 429 Too Many Requests |
|
|
395
|
+
| `InternalServerError` | 500 Internal Server Error |
|
|
396
|
+
|
|
397
|
+
### Exported Types
|
|
398
|
+
|
|
399
|
+
All types are re-exported from `@blaize-types`:
|
|
577
400
|
|
|
578
401
|
```typescript
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
402
|
+
import type {
|
|
403
|
+
// Server types
|
|
404
|
+
Server,
|
|
405
|
+
ServerOptionsInput,
|
|
406
|
+
|
|
407
|
+
// Middleware types
|
|
408
|
+
Middleware,
|
|
409
|
+
MiddlewareFunction,
|
|
410
|
+
MiddlewareOptions,
|
|
411
|
+
NextFunction,
|
|
412
|
+
|
|
413
|
+
// Plugin types
|
|
414
|
+
Plugin,
|
|
415
|
+
PluginFactory,
|
|
416
|
+
PluginHooks,
|
|
417
|
+
|
|
418
|
+
// Router types (limited export)
|
|
419
|
+
HttpMethod,
|
|
420
|
+
RouteHandler,
|
|
421
|
+
RouteMethodOptions,
|
|
422
|
+
|
|
423
|
+
// Context types
|
|
424
|
+
Context,
|
|
425
|
+
|
|
426
|
+
// Error types
|
|
427
|
+
BlaizeError,
|
|
428
|
+
ErrorType
|
|
429
|
+
} from 'blaizejs';
|
|
430
|
+
```
|
|
585
431
|
|
|
586
|
-
|
|
587
|
-
const helloData = await client.$get.getHello({
|
|
588
|
-
query: { name: 'TypeScript' }, // Typed and validated
|
|
589
|
-
});
|
|
432
|
+
## 💡 Common Patterns
|
|
590
433
|
|
|
591
|
-
|
|
592
|
-
console.log(helloData.timestamp); // Type: string
|
|
434
|
+
### Protected Routes
|
|
593
435
|
|
|
594
|
-
|
|
595
|
-
const
|
|
596
|
-
|
|
436
|
+
```typescript
|
|
437
|
+
const authMiddleware = createMiddleware({
|
|
438
|
+
name: 'auth',
|
|
439
|
+
handler: async (ctx, next) => {
|
|
440
|
+
const token = ctx.request.header('authorization');
|
|
441
|
+
if (!token) {
|
|
442
|
+
throw new UnauthorizedError('Authentication required');
|
|
443
|
+
}
|
|
444
|
+
ctx.state.user = await verifyToken(token);
|
|
445
|
+
await next();
|
|
446
|
+
}
|
|
597
447
|
});
|
|
598
448
|
|
|
599
|
-
|
|
600
|
-
|
|
449
|
+
export const GET = createGetRoute({
|
|
450
|
+
middleware: [authMiddleware],
|
|
451
|
+
handler: async (ctx) => {
|
|
452
|
+
return { user: ctx.state.user };
|
|
453
|
+
}
|
|
454
|
+
});
|
|
601
455
|
```
|
|
602
456
|
|
|
603
|
-
###
|
|
457
|
+
### Request Validation
|
|
604
458
|
|
|
605
459
|
```typescript
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
Authorization: 'Bearer your-token',
|
|
614
|
-
'User-Agent': 'MyApp/1.0.0',
|
|
460
|
+
export const POST = createPostRoute({
|
|
461
|
+
schema: {
|
|
462
|
+
body: z.object({
|
|
463
|
+
email: z.string().email(),
|
|
464
|
+
password: z.string().min(8),
|
|
465
|
+
age: z.number().int().positive().optional()
|
|
466
|
+
})
|
|
615
467
|
},
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
const data = await client.$get.getHello();
|
|
468
|
+
handler: async (ctx) => {
|
|
469
|
+
// Body is fully validated and typed
|
|
470
|
+
const user = await createUser(ctx.body);
|
|
471
|
+
return user;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
623
474
|
```
|
|
624
475
|
|
|
625
|
-
###
|
|
626
|
-
|
|
627
|
-
The client organizes methods by HTTP verb using the `$method` pattern:
|
|
476
|
+
### Error Handling
|
|
628
477
|
|
|
629
478
|
```typescript
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
479
|
+
export const GET = createGetRoute({
|
|
480
|
+
handler: async (ctx, params) => {
|
|
481
|
+
try {
|
|
482
|
+
const resource = await findResource(params.id);
|
|
483
|
+
|
|
484
|
+
if (!resource) {
|
|
485
|
+
throw new NotFoundError('Resource not found', {
|
|
486
|
+
resourceType: 'item',
|
|
487
|
+
resourceId: params.id
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!hasPermission(ctx.state.user, resource)) {
|
|
492
|
+
throw new ForbiddenError('Access denied', {
|
|
493
|
+
resource: resource.id,
|
|
494
|
+
requiredPermission: 'read'
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return resource;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
// Framework automatically handles error responses
|
|
501
|
+
throw error;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
});
|
|
638
505
|
```
|
|
639
506
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
- 🔒 **Full Type Safety** - Automatically inferred from your route schemas
|
|
643
|
-
- ✅ **Request Validation** - Client-side validation before sending requests
|
|
644
|
-
- 📊 **Response Validation** - Runtime validation of API responses
|
|
645
|
-
- 🎯 **IntelliSense Support** - Complete autocomplete for all routes and parameters
|
|
646
|
-
- 🔄 **Error Handling** - Typed error responses with detailed validation messages
|
|
647
|
-
- ⚡ **Lightweight** - Minimal runtime overhead with proxy-based implementation
|
|
648
|
-
|
|
649
|
-
[📖 Client Package Documentation](https://github.com/jleajones/blaize/tree/main/packages/blaize-client#readme)
|
|
650
|
-
|
|
651
|
-
## ✅ Testing
|
|
652
|
-
|
|
653
|
-
### 🧪 Framework Testing Tools
|
|
507
|
+
## 🧪 Testing
|
|
654
508
|
|
|
655
|
-
|
|
509
|
+
Use `@blaizejs/testing-utils` for testing:
|
|
656
510
|
|
|
657
511
|
```typescript
|
|
658
|
-
import { describe, test, expect } from 'vitest';
|
|
659
512
|
import { createTestContext } from '@blaizejs/testing-utils';
|
|
660
|
-
import {
|
|
513
|
+
import { describe, test, expect } from 'vitest';
|
|
661
514
|
|
|
662
|
-
describe('
|
|
663
|
-
test('
|
|
515
|
+
describe('User Routes', () => {
|
|
516
|
+
test('GET /users/:id returns user', async () => {
|
|
664
517
|
const ctx = createTestContext({
|
|
665
518
|
method: 'GET',
|
|
666
|
-
path: '/users'
|
|
667
|
-
query: { limit: '5', offset: '0' },
|
|
519
|
+
path: '/users/123'
|
|
668
520
|
});
|
|
669
|
-
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
expect.objectContaining({
|
|
675
|
-
id: expect.any(String),
|
|
676
|
-
name: expect.any(String),
|
|
677
|
-
email: expect.any(String),
|
|
678
|
-
}),
|
|
679
|
-
]),
|
|
680
|
-
total: expect.any(Number),
|
|
521
|
+
|
|
522
|
+
const handler = createGetRoute({
|
|
523
|
+
handler: async (ctx, params) => {
|
|
524
|
+
return { id: params.userId, name: 'Test User' };
|
|
525
|
+
}
|
|
681
526
|
});
|
|
527
|
+
|
|
528
|
+
const result = await handler.handler(ctx, { userId: '123' });
|
|
529
|
+
expect(result.id).toBe('123');
|
|
682
530
|
});
|
|
683
531
|
});
|
|
684
532
|
```
|
|
685
533
|
|
|
686
|
-
|
|
534
|
+
## 📚 Type System
|
|
535
|
+
|
|
536
|
+
BlaizeJS provides full type safety through TypeScript:
|
|
687
537
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
538
|
+
### Automatic Type Inference
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
const route = createPostRoute({
|
|
542
|
+
schema: {
|
|
543
|
+
body: z.object({
|
|
544
|
+
name: z.string(),
|
|
545
|
+
age: z.number()
|
|
546
|
+
}),
|
|
547
|
+
response: z.object({
|
|
548
|
+
id: z.string(),
|
|
549
|
+
created: z.boolean()
|
|
550
|
+
})
|
|
551
|
+
},
|
|
552
|
+
handler: async (ctx) => {
|
|
553
|
+
// ctx.body is typed as { name: string; age: number }
|
|
554
|
+
// Return type must match response schema
|
|
555
|
+
return {
|
|
556
|
+
id: '123',
|
|
557
|
+
created: true
|
|
558
|
+
};
|
|
694
559
|
}
|
|
695
|
-
}
|
|
560
|
+
});
|
|
696
561
|
```
|
|
697
562
|
|
|
698
|
-
###
|
|
563
|
+
### Custom Type Extensions
|
|
699
564
|
|
|
700
|
-
```
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
565
|
+
```typescript
|
|
566
|
+
// Extend context state
|
|
567
|
+
declare module 'blaizejs' {
|
|
568
|
+
interface State {
|
|
569
|
+
user?: {
|
|
570
|
+
id: string;
|
|
571
|
+
role: string;
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
```
|
|
706
576
|
|
|
707
|
-
|
|
708
|
-
pnpm test:coverage
|
|
577
|
+
## 🗺️ Roadmap
|
|
709
578
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
579
|
+
### 🚀 Current Beta (v0.3.1)
|
|
580
|
+
|
|
581
|
+
- ✅ Core server with HTTP/2 support
|
|
582
|
+
- ✅ File-based routing (internal)
|
|
583
|
+
- ✅ Middleware system
|
|
584
|
+
- ✅ Plugin architecture
|
|
585
|
+
- ✅ 11 semantic error classes (400-500 status codes)
|
|
586
|
+
- ✅ Schema validation with Zod
|
|
587
|
+
- ✅ Context management (internal)
|
|
588
|
+
- ✅ Type-safe route creation
|
|
589
|
+
|
|
590
|
+
### 🎯 MVP/1.0 Release
|
|
591
|
+
|
|
592
|
+
#### Core Improvements
|
|
593
|
+
- 🔄 **Export Router Utilities** - Parameter extraction, route matching for extensions
|
|
594
|
+
- 🔄 **Custom Error Factory** - Allow user-defined error classes
|
|
595
|
+
- 🔄 **Enhanced Testing Utils** - More comprehensive testing helpers
|
|
596
|
+
- 🔄 **Performance Monitoring** - Built-in metrics and profiling
|
|
597
|
+
- 🔄 **Additional HTTP Status Codes** - 405, 502, 503, 504 error classes
|
|
598
|
+
|
|
599
|
+
#### New Features
|
|
600
|
+
- 🔄 **WebSocket Support** - Real-time communication
|
|
601
|
+
- 🔄 **Response Helpers** - Utility functions for common responses
|
|
602
|
+
- 🔄 **Route Metadata** - Attach custom metadata to routes
|
|
603
|
+
- 🔄 **Built-in Middleware** - CORS, compression, security headers
|
|
604
|
+
- 🔄 **Request Streaming** - Handle large payloads efficiently
|
|
605
|
+
|
|
606
|
+
### 🔮 Post-MVP (v1.1+)
|
|
607
|
+
|
|
608
|
+
- 🔄 **GraphQL Integration** - Built-in GraphQL support
|
|
609
|
+
- 🔄 **gRPC Support** - Protocol buffer services
|
|
610
|
+
- 🔄 **OpenAPI Generation** - Automatic API documentation
|
|
611
|
+
- 🔄 **Distributed Tracing** - OpenTelemetry integration
|
|
612
|
+
- 🔄 **Edge Runtime Support** - Cloudflare Workers, Deno Deploy
|
|
613
|
+
- 🔄 **Bun Compatibility** - Native Bun.serve integration
|
|
715
614
|
|
|
716
615
|
## 🤝 Contributing
|
|
717
616
|
|
|
718
|
-
We welcome contributions
|
|
617
|
+
We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details.
|
|
719
618
|
|
|
720
|
-
###
|
|
619
|
+
### Development Setup
|
|
721
620
|
|
|
722
621
|
```bash
|
|
723
622
|
# Clone the repository
|
|
724
623
|
git clone https://github.com/jleajones/blaize.git
|
|
725
624
|
cd blaize
|
|
726
625
|
|
|
727
|
-
# Install dependencies
|
|
626
|
+
# Install dependencies
|
|
728
627
|
pnpm install
|
|
729
628
|
|
|
730
629
|
# Run tests
|
|
731
630
|
pnpm test
|
|
732
631
|
|
|
733
|
-
#
|
|
734
|
-
pnpm dev
|
|
735
|
-
|
|
736
|
-
# Build all packages
|
|
632
|
+
# Build packages
|
|
737
633
|
pnpm build
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
### 🏗️ Monorepo Structure
|
|
741
634
|
|
|
635
|
+
# Run examples
|
|
636
|
+
pnpm --filter blaizejs dev
|
|
742
637
|
```
|
|
743
|
-
blaize/
|
|
744
|
-
├── packages/
|
|
745
|
-
│ ├── blaizejs/ # Core framework (this package)
|
|
746
|
-
│ ├── client/ # Type-safe client generator
|
|
747
|
-
│ ├── types/ # Shared TypeScript types
|
|
748
|
-
│ ├── testing-utils/ # Testing utilities
|
|
749
|
-
│ └── configs/ # Shared configurations
|
|
750
|
-
├── plugins/ # Official plugins
|
|
751
|
-
├── apps/ # Example applications
|
|
752
|
-
└── docs/ # Documentation
|
|
753
|
-
```
|
|
754
|
-
|
|
755
|
-
### 📝 Code Standards
|
|
756
638
|
|
|
757
|
-
|
|
758
|
-
- ✅ **Testing**: Comprehensive test coverage with Vitest
|
|
759
|
-
- ✅ **Linting**: ESLint with consistent configuration
|
|
760
|
-
- ✅ **Formatting**: Prettier for code formatting
|
|
761
|
-
- ✅ **Commits**: Conventional commits for clear history
|
|
762
|
-
- ✅ **Documentation**: JSDoc comments for public APIs
|
|
639
|
+
### Package Structure
|
|
763
640
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
641
|
+
```
|
|
642
|
+
packages/
|
|
643
|
+
├── blaize-core/ # Main framework (this package)
|
|
644
|
+
│ ├── src/
|
|
645
|
+
│ │ ├── server/ # Server implementation
|
|
646
|
+
│ │ ├── router/ # Router (mostly internal)
|
|
647
|
+
│ │ ├── middleware/ # Middleware system
|
|
648
|
+
│ │ ├── plugins/ # Plugin system
|
|
649
|
+
│ │ ├── context/ # Context (internal)
|
|
650
|
+
│ │ ├── errors/ # Error classes
|
|
651
|
+
│ │ └── index.ts # Main exports
|
|
652
|
+
│ └── package.json
|
|
653
|
+
├── blaize-types/ # Shared TypeScript types
|
|
654
|
+
├── blaize-client/ # Client SDK
|
|
655
|
+
└── blaize-testing-utils/ # Testing utilities
|
|
774
656
|
```
|
|
775
657
|
|
|
776
|
-
###
|
|
777
|
-
|
|
778
|
-
When contributing to the core framework:
|
|
779
|
-
|
|
780
|
-
- ✅ Test all HTTP/2 and HTTP/1.1 compatibility
|
|
781
|
-
- ✅ Test ESM module resolution and path handling
|
|
782
|
-
- ✅ Test AsyncLocalStorage context propagation
|
|
783
|
-
- ✅ Test middleware composition and error handling
|
|
784
|
-
- ✅ Test plugin lifecycle management
|
|
785
|
-
- ✅ Include integration tests with real HTTP requests
|
|
786
|
-
- ✅ Test production deployment scenarios
|
|
787
|
-
- ✅ Test type safety and schema validation
|
|
788
|
-
|
|
789
|
-
### 🎯 Architecture Guidelines
|
|
790
|
-
|
|
791
|
-
Key principles for core framework development:
|
|
792
|
-
|
|
793
|
-
- 🔒 **Type Safety First** - Everything should be typed and validated
|
|
794
|
-
- ⚡ **Performance** - Minimal overhead and optimal execution
|
|
795
|
-
- 🧩 **Modularity** - Clean separation between modules
|
|
796
|
-
- 🔄 **Async/Await** - Modern async patterns throughout
|
|
797
|
-
- 🛡️ **Error Handling** - Comprehensive error management
|
|
798
|
-
- 📖 **Documentation** - Clear examples and API docs
|
|
799
|
-
|
|
800
|
-
## 🗺️ Roadmap
|
|
801
|
-
|
|
802
|
-
### 🚀 Current (v0.1.x)
|
|
803
|
-
|
|
804
|
-
- ✅ **HTTP/2 Server** with HTTP/1.1 fallback and SSL support
|
|
805
|
-
- ✅ **File-Based Routing** with automatic path generation and hot reloading
|
|
806
|
-
- ✅ **Type-Safe Routes** with Zod schema validation and route creators
|
|
807
|
-
- ✅ **Composable Middleware** with onion execution and error handling
|
|
808
|
-
- ✅ **Plugin System** with lifecycle management and validation
|
|
809
|
-
- ✅ **Context Management** with AsyncLocalStorage and state isolation
|
|
810
|
-
- ✅ **Testing Utilities** with comprehensive test helpers
|
|
811
|
-
- ✅ **ESM Support** with proper module resolution
|
|
812
|
-
- ✅ **Client Generation** with full type safety (separate package)
|
|
813
|
-
|
|
814
|
-
### 🎯 Next Release (v0.2.x)
|
|
815
|
-
|
|
816
|
-
- 🔄 **HTTP/2 Hosting Solutions** - Workarounds for hosting provider limitations
|
|
817
|
-
- 🔄 **Performance Optimizations** - Radix tree improvements and caching
|
|
818
|
-
- 🔄 **Advanced Schema Validation** - Enhanced Zod integration and custom validators
|
|
819
|
-
- 🔄 **Built-in Monitoring** - Performance metrics and health checks
|
|
820
|
-
- 🔄 **Route Groups** - Organized routing with shared middleware
|
|
821
|
-
- 🔄 **Plugin Registry** - Centralized plugin discovery and management
|
|
822
|
-
|
|
823
|
-
### 🔮 Future (v0.3.x+)
|
|
824
|
-
|
|
825
|
-
- 🔄 **GraphQL Integration** - File-based GraphQL resolvers
|
|
826
|
-
- 🔄 **WebSocket Support** - Real-time endpoints with type safety
|
|
827
|
-
- 🔄 **Server-Side Streaming** - Streaming responses and SSE
|
|
828
|
-
- 🔄 **Edge Runtime** - Deployment to edge computing platforms
|
|
829
|
-
- 🔄 **Zero-Config Deployment** - One-command deployment to various platforms
|
|
830
|
-
- 🔄 **Advanced Caching** - Multi-layer caching strategies
|
|
831
|
-
|
|
832
|
-
### 🌟 Long-term Vision
|
|
833
|
-
|
|
834
|
-
- 🔄 **Visual Development** - GUI tools for route and middleware management
|
|
835
|
-
- 🔄 **AI-Powered Optimization** - Automatic performance tuning and suggestions
|
|
836
|
-
- 🔄 **Multi-Protocol Support** - gRPC, WebSocket, and HTTP/3 in unified framework
|
|
837
|
-
- 🔄 **Microservices Platform** - Service mesh integration and distributed systems
|
|
838
|
-
- 🔄 **Enterprise Features** - Advanced security, compliance, and governance
|
|
839
|
-
|
|
840
|
-
---
|
|
658
|
+
### Important Notes
|
|
841
659
|
|
|
842
|
-
|
|
660
|
+
When contributing to BlaizeJS Core:
|
|
843
661
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
- 🔗 [Client Package](./src/client/README.md) - Type-safe API client generation
|
|
850
|
-
- 🧪 [Testing Utils](./src/testing-utils/README.md) - Testing utilities and helpers
|
|
662
|
+
1. **Check Exports**: Ensure new features are exported in `src/index.ts`
|
|
663
|
+
2. **Update Types**: Add types to `@blaize-types` package
|
|
664
|
+
3. **Document Internal APIs**: Mark internal-only features clearly
|
|
665
|
+
4. **Add Tests**: Use `@blaizejs/testing-utils` for testing
|
|
666
|
+
5. **Follow Patterns**: Match existing code style and patterns
|
|
851
667
|
|
|
852
668
|
---
|
|
853
669
|
|
|
854
670
|
**Built with ❤️ by the BlaizeJS team**
|
|
855
671
|
|
|
856
|
-
|
|
672
|
+
_For questions or issues, please [open an issue](https://github.com/jleajones/blaize/issues) on GitHub._
|