aetherjs-router 1.0.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/README.md +541 -0
- package/index.js +21 -0
- package/package.json +41 -0
- package/src/aether-adapter.js +98 -0
- package/src/examples/basic-router.js +796 -0
- package/src/path-compiler.js +660 -0
- package/src/route-factory.js +326 -0
- package/src/router-factory.js +840 -0
- package/src/test/benchmark/router-benchmark.test.js +561 -0
package/README.md
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
Aether Router - The Next-Generation High-Performance Node.js Router
|
|
2
|
+
|
|
3
|
+
🚀 What is Aether Router?
|
|
4
|
+
|
|
5
|
+
Aether Router is an advanced, high-performance routing library for Node.js built with modern JavaScript features. It provides a clean, expressive API with powerful capabilities including query parameter support in route patterns, caching, versioning, and grouping for building robust APIs and web applications.
|
|
6
|
+
|
|
7
|
+
📦 Installation
|
|
8
|
+
|
|
9
|
+
Install Aether Router via npm:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install aether-router
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
🚀 Quick Start
|
|
16
|
+
|
|
17
|
+
Basic Usage
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
import { createRouter } from 'aether-router';
|
|
21
|
+
|
|
22
|
+
// Create a router with enhanced features
|
|
23
|
+
const router = createRouter({
|
|
24
|
+
cacheSize: 1000,
|
|
25
|
+
parseQuery: true, // Enable query parameter pattern matching
|
|
26
|
+
autoParseQuery: true, // Automatically parse query parameters
|
|
27
|
+
enableVersioning: true // Enable versioning support
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Basic route
|
|
31
|
+
router.get('/api/health', (ctx) => {
|
|
32
|
+
ctx.setStatus(200);
|
|
33
|
+
ctx.json({
|
|
34
|
+
status: 'ok',
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
version: '1.0.0'
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Route with query parameters
|
|
41
|
+
router.get('/api/search?q=:query&page=:page?&limit=:limit?', (ctx) => {
|
|
42
|
+
const { query, page = '1', limit = '10' } = ctx.params;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
query,
|
|
46
|
+
page: parseInt(page),
|
|
47
|
+
limit: parseInt(limit),
|
|
48
|
+
results: [
|
|
49
|
+
{ id: 1, title: `Result for: ${query}` }
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Route with path and query parameters
|
|
55
|
+
router.get('/api/users/:id?fields=:fields?&expand=:expand?', (ctx) => {
|
|
56
|
+
const { id, fields = 'id,name,email', expand = '' } = ctx.params;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
user: {
|
|
60
|
+
id,
|
|
61
|
+
name: `User ${id}`,
|
|
62
|
+
email: `user${id}@example.com`,
|
|
63
|
+
fields: fields.split(','),
|
|
64
|
+
expand: expand.split(',').filter(Boolean)
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Use with AetherJS or other frameworks
|
|
70
|
+
const middleware = router.middleware();
|
|
71
|
+
|
|
72
|
+
// Export for your server
|
|
73
|
+
export default middleware;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Alternative Import Methods
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
// Method 1: Default import (recommended for most use cases)
|
|
80
|
+
import createRouter from 'aether-router';
|
|
81
|
+
|
|
82
|
+
// Method 2: Named imports for specific components
|
|
83
|
+
import {
|
|
84
|
+
createRouter,
|
|
85
|
+
createRoute,
|
|
86
|
+
createPathCompiler,
|
|
87
|
+
createAetherRouter,
|
|
88
|
+
Router,
|
|
89
|
+
Route,
|
|
90
|
+
PathCompiler
|
|
91
|
+
} from 'aether-router';
|
|
92
|
+
|
|
93
|
+
// Method 3: Using the factory function directly
|
|
94
|
+
import { createAetherRouter } from 'aether-router';
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
⭐ Why Choose Aether Router?
|
|
98
|
+
|
|
99
|
+
Performance Benchmarks
|
|
100
|
+
Based on comprehensive testing, Aether Router delivers outstanding performance:
|
|
101
|
+
|
|
102
|
+
- ~24,000 OPS/SEC - High-volume route matching
|
|
103
|
+
- ~50,000+ OPS/SEC - Cache-hit performance (10x faster than non-cached)
|
|
104
|
+
- <5ms Average Latency - Consistent low-latency response
|
|
105
|
+
- Efficient Memory Usage - ~2000 bytes per route
|
|
106
|
+
- Query Parameter Support - Native query parameter matching in routes
|
|
107
|
+
- Zero Dependencies - Pure JavaScript implementation
|
|
108
|
+
|
|
109
|
+
Key Advantages Over Competitors
|
|
110
|
+
|
|
111
|
+
| Feature | Aether Router | Express.js | Fastify |
|
|
112
|
+
|---------|--------------|------------|---------|
|
|
113
|
+
| Query Parameter Routes | ✅ Native Support | ❌ Middleware Required | ❌ Plugin Required |
|
|
114
|
+
| Built-in Caching | ✅ LRU Cache | ❌ None | ✅ Limited |
|
|
115
|
+
| Memory Efficiency | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
|
|
116
|
+
| Learning Curve | Gentle | Low | Moderate |
|
|
117
|
+
| Query Parsing Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
|
|
118
|
+
| Versioning Support | ✅ Built-in | ❌ Manual | ❌ Manual |
|
|
119
|
+
| Group Routing | ✅ Advanced | ✅ Basic | ✅ Basic |
|
|
120
|
+
|
|
121
|
+
🔧 Core Features
|
|
122
|
+
|
|
123
|
+
1. Query Parameter Routing (Unique Feature!)
|
|
124
|
+
Define routes that match query parameters in the pattern itself:
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// Query parameters become part of the route pattern
|
|
128
|
+
router.get('/products?category=:category&sort=:sort?&minPrice=:minPrice?', (ctx) => {
|
|
129
|
+
const { category, sort = 'name', minPrice = '0' } = ctx.params;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
category,
|
|
133
|
+
filters: { sort, minPrice: parseFloat(minPrice) },
|
|
134
|
+
products: []
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Matches: /api/products?category=electronics&sort=price&minPrice=100
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
2. Advanced Route Groups
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// Versioned API with grouping
|
|
145
|
+
router.group('/api/v1', (v1) => {
|
|
146
|
+
v1.group('/users', (users) => {
|
|
147
|
+
// GET /api/v1/users
|
|
148
|
+
users.get('/', (ctx) => ({ users: [] }));
|
|
149
|
+
|
|
150
|
+
// GET /api/v1/users/:id
|
|
151
|
+
users.get('/:id', (ctx) => ({ user: { id: ctx.params.id } }));
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Admin routes with authentication
|
|
156
|
+
router.group('/admin', (admin) => {
|
|
157
|
+
admin.use((ctx, next) => {
|
|
158
|
+
// Admin authentication middleware
|
|
159
|
+
const token = ctx.headers['x-admin-token'];
|
|
160
|
+
if (!token || token !== 'admin-secret') {
|
|
161
|
+
ctx.setStatus(401);
|
|
162
|
+
return { error: 'Unauthorized' };
|
|
163
|
+
}
|
|
164
|
+
ctx.isAdmin = true;
|
|
165
|
+
return next();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
admin.get('/dashboard?view=:view?', (ctx) => {
|
|
169
|
+
const view = ctx.params.view || 'overview';
|
|
170
|
+
return { view, stats: { users: 1500, revenue: 50000 } };
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
3. Built-in Versioning
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
// Versioned APIs with clean separation
|
|
179
|
+
router.group('/api', (api) => {
|
|
180
|
+
// v1 routes with simpler response
|
|
181
|
+
api.group('/v1', (v1) => {
|
|
182
|
+
v1.get('/users', (ctx) => ({
|
|
183
|
+
users: [
|
|
184
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com' }
|
|
185
|
+
]
|
|
186
|
+
}));
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// v2 routes with enhanced features
|
|
190
|
+
api.group('/v2', (v2) => {
|
|
191
|
+
v2.use((ctx, next) => {
|
|
192
|
+
ctx.apiVersion = 'v2';
|
|
193
|
+
ctx.features = ['enhanced-security', 'caching', 'rate-limiting'];
|
|
194
|
+
return next();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
v2.get('/users/:id?include=:include?&fields=:fields?', (ctx) => {
|
|
198
|
+
const { id, include = '', fields = '' } = ctx.params;
|
|
199
|
+
// Advanced logic with includes and field selection
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
📖 Comprehensive Usage Guide
|
|
206
|
+
|
|
207
|
+
Basic Routing
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
import { createRouter } from 'aether-router';
|
|
211
|
+
|
|
212
|
+
const router = createRouter();
|
|
213
|
+
|
|
214
|
+
// HTTP Methods
|
|
215
|
+
router.get('/users', getUserHandler);
|
|
216
|
+
router.post('/users', createUserHandler);
|
|
217
|
+
router.put('/users/:id', updateUserHandler);
|
|
218
|
+
router.delete('/users/:id', deleteUserHandler);
|
|
219
|
+
router.patch('/users/:id', patchUserHandler);
|
|
220
|
+
|
|
221
|
+
// ALL method (matches all HTTP methods)
|
|
222
|
+
router.all('/status', statusHandler);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Middleware Support
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// Global middleware
|
|
229
|
+
router.use(async (ctx, next) => {
|
|
230
|
+
console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.url}`);
|
|
231
|
+
const start = Date.now();
|
|
232
|
+
await next();
|
|
233
|
+
const duration = Date.now() - start;
|
|
234
|
+
console.log(`Request completed in ${duration}ms`);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Route-specific middleware
|
|
238
|
+
router.get('/admin/data', authMiddleware, adminMiddleware, (ctx) => {
|
|
239
|
+
return { data: 'sensitive' };
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Error handling middleware
|
|
243
|
+
router.use(async (ctx, next) => {
|
|
244
|
+
try {
|
|
245
|
+
await next();
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error('Request error:', error);
|
|
248
|
+
ctx.setStatus(500);
|
|
249
|
+
return {
|
|
250
|
+
error: 'Internal Server Error',
|
|
251
|
+
message: error.message
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Path Patterns
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
// Path parameters
|
|
261
|
+
router.get('/users/:id', (ctx) => {
|
|
262
|
+
const id = ctx.params.id;
|
|
263
|
+
return { user: { id } };
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Optional path parameters
|
|
267
|
+
router.get('/articles/:slug?', (ctx) => {
|
|
268
|
+
const slug = ctx.params.slug || 'latest';
|
|
269
|
+
return { article: { slug } };
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Multiple path parameters
|
|
273
|
+
router.get('/users/:userId/posts/:postId', (ctx) => {
|
|
274
|
+
const { userId, postId } = ctx.params;
|
|
275
|
+
return { userId, postId };
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Wildcard
|
|
279
|
+
router.get('/files/*', (ctx) => {
|
|
280
|
+
const filePath = ctx.params.wildcard;
|
|
281
|
+
return { path: filePath };
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
🔍 Integration Examples
|
|
286
|
+
|
|
287
|
+
With AetherJS
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
import { aether } from 'aetherjs';
|
|
291
|
+
import { createRouter } from 'aether-router';
|
|
292
|
+
|
|
293
|
+
const router = createRouter();
|
|
294
|
+
|
|
295
|
+
// Define routes
|
|
296
|
+
router.get('/api/users', (ctx) => ({
|
|
297
|
+
users: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
|
|
298
|
+
}));
|
|
299
|
+
|
|
300
|
+
router.post('/api/users', async (ctx) => {
|
|
301
|
+
const userData = ctx.body;
|
|
302
|
+
return {
|
|
303
|
+
message: 'User created',
|
|
304
|
+
user: { ...userData, id: Date.now() }
|
|
305
|
+
};
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Create AetherJS app
|
|
309
|
+
const app = aether();
|
|
310
|
+
|
|
311
|
+
// Add router middleware
|
|
312
|
+
app.use(router.middleware());
|
|
313
|
+
|
|
314
|
+
// Start server
|
|
315
|
+
app.start(3000);
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
With Node.js HTTP Module
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
import http from 'http';
|
|
322
|
+
import { createRouter } from 'aether-router';
|
|
323
|
+
|
|
324
|
+
const router = createRouter();
|
|
325
|
+
|
|
326
|
+
// Define routes
|
|
327
|
+
router.get('/hello', (ctx) => {
|
|
328
|
+
ctx.setStatus(200);
|
|
329
|
+
return { message: 'Hello, World!' };
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const middleware = router.middleware();
|
|
333
|
+
|
|
334
|
+
const server = http.createServer(async (req, res) => {
|
|
335
|
+
const ctx = {
|
|
336
|
+
method: req.method,
|
|
337
|
+
url: req.url,
|
|
338
|
+
headers: req.headers,
|
|
339
|
+
body: null,
|
|
340
|
+
setStatus: (code) => { res.statusCode = code; },
|
|
341
|
+
setHeader: (key, value) => { res.setHeader(key, value); },
|
|
342
|
+
json: (data) => {
|
|
343
|
+
res.setHeader('Content-Type', 'application/json');
|
|
344
|
+
res.end(JSON.stringify(data));
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Parse body for POST, PUT, PATCH
|
|
349
|
+
if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
|
|
350
|
+
const body = [];
|
|
351
|
+
req.on('data', (chunk) => body.push(chunk));
|
|
352
|
+
|
|
353
|
+
await new Promise((resolve) => {
|
|
354
|
+
req.on('end', () => {
|
|
355
|
+
if (body.length > 0) {
|
|
356
|
+
ctx.body = JSON.parse(Buffer.concat(body).toString());
|
|
357
|
+
}
|
|
358
|
+
resolve();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
await middleware(ctx, () => {
|
|
365
|
+
// No route matched
|
|
366
|
+
ctx.setStatus(404);
|
|
367
|
+
ctx.json({ error: 'Not Found' });
|
|
368
|
+
});
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error('Router error:', error);
|
|
371
|
+
ctx.setStatus(500);
|
|
372
|
+
ctx.json({ error: 'Internal Server Error' });
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
server.listen(3000, () => {
|
|
377
|
+
console.log('Server running on http://localhost:3000');
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
📊 Performance Comparison
|
|
382
|
+
|
|
383
|
+
Benchmark Results
|
|
384
|
+
Here's how Aether Router compares to other popular routers:
|
|
385
|
+
|
|
386
|
+
| Benchmark | Aether Router | Express.js | Koa.js | Fastify |
|
|
387
|
+
|-----------|---------------|------------|---------|---------|
|
|
388
|
+
| Simple Routing OPS | ~24,000 | ~15,000 | ~18,000 | ~30,000 |
|
|
389
|
+
| With Middleware OPS | ~15,000 | ~8,000 | ~10,000 | ~20,000 |
|
|
390
|
+
| Query Params OPS | ~15,000 | ~6,000 | ~7,000 | ~12,000 |
|
|
391
|
+
| Cached Requests OPS | ~50,000+ | ~15,000 | ~18,000 | ~40,000 |
|
|
392
|
+
| Memory per Route | ~2KB | ~5KB | ~4KB | ~3KB |
|
|
393
|
+
| Query Parsing OPS | ~15,000 | ~5,000 | ~6,000 | ~10,000 |
|
|
394
|
+
|
|
395
|
+
Performance Features
|
|
396
|
+
|
|
397
|
+
```javascript
|
|
398
|
+
// Enable caching for production
|
|
399
|
+
const router = createRouter({
|
|
400
|
+
cacheSize: 10000, // Cache 10,000 routes (optimal for most apps)
|
|
401
|
+
autoParseQuery: true, // Automatic query parsing with caching
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// Production configuration
|
|
405
|
+
const productionRouter = createRouter({
|
|
406
|
+
cacheSize: 50000, // Large cache for high-traffic apps
|
|
407
|
+
parseQuery: true, // Enable query parameter routes
|
|
408
|
+
autoParseQuery: true, // Automatically parse queries
|
|
409
|
+
caseSensitive: false, // Case-insensitive for flexibility
|
|
410
|
+
enableVersioning: true, // Enable API versioning
|
|
411
|
+
});
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
🔧 API Reference
|
|
415
|
+
|
|
416
|
+
Factory Options
|
|
417
|
+
|
|
418
|
+
```javascript
|
|
419
|
+
const options = {
|
|
420
|
+
prefix: '/api', // Global route prefix
|
|
421
|
+
cacheSize: 1000, // Route matching cache size
|
|
422
|
+
parseQuery: true, // Enable query parameter route patterns
|
|
423
|
+
autoParseQuery: true, // Automatically parse query strings
|
|
424
|
+
caseSensitive: false, // Case-insensitive path matching
|
|
425
|
+
strict: false, // Trailing slash flexibility
|
|
426
|
+
enableVersioning: true // Enable version support
|
|
427
|
+
};
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Router Methods
|
|
431
|
+
|
|
432
|
+
```javascript
|
|
433
|
+
// HTTP methods
|
|
434
|
+
router.get(path, handler, ...middleware);
|
|
435
|
+
router.post(path, handler, ...middleware);
|
|
436
|
+
router.put(path, handler, ...middleware);
|
|
437
|
+
router.delete(path, handler, ...middleware);
|
|
438
|
+
router.patch(path, handler, ...middleware);
|
|
439
|
+
router.options(path, handler, ...middleware);
|
|
440
|
+
router.head(path, handler, ...middleware);
|
|
441
|
+
router.all(path, handler, ...middleware);
|
|
442
|
+
|
|
443
|
+
// Grouping and versioning
|
|
444
|
+
router.group(prefix, callback);
|
|
445
|
+
router.version(versions, callback);
|
|
446
|
+
|
|
447
|
+
// RESTful resources
|
|
448
|
+
router.resource(name, handlers, middleware);
|
|
449
|
+
|
|
450
|
+
// Middleware
|
|
451
|
+
router.use(middleware);
|
|
452
|
+
router.use(path, router);
|
|
453
|
+
router.useQueryParser();
|
|
454
|
+
|
|
455
|
+
// Utility
|
|
456
|
+
router.getRoutes();
|
|
457
|
+
router.getStats();
|
|
458
|
+
router.clearCache();
|
|
459
|
+
router.match(method, url);
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Context Object
|
|
463
|
+
|
|
464
|
+
```javascript
|
|
465
|
+
{
|
|
466
|
+
method, // HTTP method
|
|
467
|
+
url, // Request URL
|
|
468
|
+
path, // Path part of URL (without query string)
|
|
469
|
+
params, // Route parameters
|
|
470
|
+
query, // Parsed query parameters
|
|
471
|
+
body, // Request body
|
|
472
|
+
headers, // Request headers
|
|
473
|
+
setStatus, // Function to set response status
|
|
474
|
+
setHeader, // Function to set response header
|
|
475
|
+
json, // Function to send JSON response
|
|
476
|
+
originalUrl // Original URL before processing
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
🏗️ Advanced Configuration
|
|
481
|
+
|
|
482
|
+
Production Setup
|
|
483
|
+
|
|
484
|
+
```javascript
|
|
485
|
+
// config/router.js
|
|
486
|
+
import { createRouter } from 'aether-router';
|
|
487
|
+
|
|
488
|
+
export function createAppRouter() {
|
|
489
|
+
return createRouter({
|
|
490
|
+
prefix: '/api/v1',
|
|
491
|
+
cacheSize: 10000,
|
|
492
|
+
parseQuery: true,
|
|
493
|
+
autoParseQuery: true,
|
|
494
|
+
enableVersioning: true
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// app/routes/index.js
|
|
499
|
+
import { createAppRouter } from '../config/router.js';
|
|
500
|
+
import userRoutes from './users.js';
|
|
501
|
+
import productRoutes from './products.js';
|
|
502
|
+
import adminRoutes from './admin.js';
|
|
503
|
+
|
|
504
|
+
const router = createAppRouter();
|
|
505
|
+
|
|
506
|
+
// Add global middleware
|
|
507
|
+
router.use(loggerMiddleware);
|
|
508
|
+
router.use(authMiddleware);
|
|
509
|
+
router.useError(globalErrorHandler);
|
|
510
|
+
|
|
511
|
+
// Mount route modules
|
|
512
|
+
router.use('/users', userRoutes);
|
|
513
|
+
router.use('/products', productRoutes);
|
|
514
|
+
router.use('/admin', adminRoutes);
|
|
515
|
+
|
|
516
|
+
// 404 handler
|
|
517
|
+
router.use((ctx) => {
|
|
518
|
+
ctx.setStatus(404);
|
|
519
|
+
ctx.json({
|
|
520
|
+
error: 'Not Found',
|
|
521
|
+
message: `Route ${ctx.method} ${ctx.url} not found`,
|
|
522
|
+
timestamp: new Date().toISOString()
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
export const middleware = router.middleware();
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
🤝 Contributing
|
|
530
|
+
|
|
531
|
+
We welcome contributions! Here's how you can help:
|
|
532
|
+
|
|
533
|
+
1. Report Issues: [GitHub Issues](https://github.com/aetherjs/aetherframework-router/issues)
|
|
534
|
+
2. Submit PRs: Follow our contribution guidelines
|
|
535
|
+
3. Improve Documentation: Help us make the docs better
|
|
536
|
+
4. Add Examples: Share your use cases
|
|
537
|
+
|
|
538
|
+
📄 License
|
|
539
|
+
|
|
540
|
+
MIT License - see LICENSE file for details.
|
|
541
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/index.js - Main entry point for AetherJS Router module
|
|
2
|
+
import { createRouter } from './router-factory.js';
|
|
3
|
+
import { createRoute } from './route-factory.js';
|
|
4
|
+
import { createPathCompiler } from './path-compiler.js';
|
|
5
|
+
import { createAetherRouter } from './aether-adapter.js';
|
|
6
|
+
|
|
7
|
+
// Export factory functions for flexible usage
|
|
8
|
+
export {
|
|
9
|
+
createRouter,
|
|
10
|
+
createRoute,
|
|
11
|
+
createPathCompiler,
|
|
12
|
+
createAetherRouter
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Default export for common use case
|
|
16
|
+
export default createRouter;
|
|
17
|
+
|
|
18
|
+
// Export classes for advanced usage
|
|
19
|
+
export { Router } from './router-factory.js';
|
|
20
|
+
export { Route } from './route-factory.js';
|
|
21
|
+
export { PathCompiler } from './path-compiler.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aetherjs-router",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "High-performance router module for AetherJS framework",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
9
|
+
"test:coverage": "npm test -- --coverage",
|
|
10
|
+
"benchmark": "node examples/benchmark.js",
|
|
11
|
+
"lint": "eslint src/ tests/",
|
|
12
|
+
"format": "prettier --write src/ tests/"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"aetherjs",
|
|
16
|
+
"router",
|
|
17
|
+
"high-performance",
|
|
18
|
+
"middleware",
|
|
19
|
+
"http"
|
|
20
|
+
],
|
|
21
|
+
"author": "Aether Framework Team",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/aetherjs/aetherframework-router.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/aetherjs/aetherframework-router/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/aetherjs/aetherframework-router/blob/main/README.md",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@jest/globals": "^30.4.1",
|
|
33
|
+
"autocannon": "^7.12.0",
|
|
34
|
+
"eslint": "^8.56.0",
|
|
35
|
+
"jest": "^29.7.0",
|
|
36
|
+
"prettier": "^3.1.1"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=16.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/aether-adapter.js - Adapter for AetherJS middleware system
|
|
2
|
+
import { createRouter } from './router-factory.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create AetherJS-compatible router middleware
|
|
6
|
+
* @param {Router} router - Router instance
|
|
7
|
+
* @returns {Function} AetherJS middleware function
|
|
8
|
+
*/
|
|
9
|
+
export function createAetherRouter(router) {
|
|
10
|
+
return async function aetherRouterMiddleware(ctx, signal) {
|
|
11
|
+
// Adapter: Convert AetherJS signal to traditional next function
|
|
12
|
+
const next = signal && typeof signal.next === 'function'
|
|
13
|
+
? () => signal.next()
|
|
14
|
+
: (signal && typeof signal === 'function')
|
|
15
|
+
? signal
|
|
16
|
+
: () => {};
|
|
17
|
+
|
|
18
|
+
// Find matching route
|
|
19
|
+
const match = router.match(ctx.method, ctx.url);
|
|
20
|
+
|
|
21
|
+
if (!match) {
|
|
22
|
+
// No route matched, continue to next middleware
|
|
23
|
+
return await next();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Set route parameters on context
|
|
27
|
+
ctx.params = match.params;
|
|
28
|
+
|
|
29
|
+
// Combine router middleware with route-specific middleware
|
|
30
|
+
const middlewareChain = [...router.middleware, ...match.route.middleware];
|
|
31
|
+
|
|
32
|
+
// Execute middleware chain
|
|
33
|
+
let index = -1;
|
|
34
|
+
|
|
35
|
+
async function dispatch(i) {
|
|
36
|
+
if (i <= index) {
|
|
37
|
+
throw new Error('next() called multiple times');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
index = i;
|
|
41
|
+
let fn = middlewareChain[i];
|
|
42
|
+
|
|
43
|
+
if (i === middlewareChain.length) {
|
|
44
|
+
fn = match.route.handler;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!fn) return Promise.resolve();
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
// Adapter: Pass AetherJS context and next function
|
|
51
|
+
return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)));
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return Promise.reject(err);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await dispatch(0);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Factory function for creating AetherJS router with convenience methods
|
|
63
|
+
* @param {Object} options - Router options
|
|
64
|
+
* @returns {Object} Router factory object
|
|
65
|
+
*/
|
|
66
|
+
export function createAetherRouteFactory(options = {}) {
|
|
67
|
+
const router = createRouter(options);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
router,
|
|
71
|
+
|
|
72
|
+
// HTTP method shortcuts
|
|
73
|
+
get: (path, handler, ...middleware) => router.get(path, handler, ...middleware),
|
|
74
|
+
post: (path, handler, ...middleware) => router.post(path, handler, ...middleware),
|
|
75
|
+
put: (path, handler, ...middleware) => router.put(path, handler, ...middleware),
|
|
76
|
+
delete: (path, handler, ...middleware) => router.delete(path, handler, ...middleware),
|
|
77
|
+
patch: (path, handler, ...middleware) => router.patch(path, handler, ...middleware),
|
|
78
|
+
options: (path, handler, ...middleware) => router.options(path, handler, ...middleware),
|
|
79
|
+
head: (path, handler, ...middleware) => router.head(path, handler, ...middleware),
|
|
80
|
+
all: (path, handler, ...middleware) => router.all(path, handler, ...middleware),
|
|
81
|
+
|
|
82
|
+
// Grouping and middleware
|
|
83
|
+
group: (prefix, callback) => router.group(prefix, callback),
|
|
84
|
+
use: (...args) => router.use(...args),
|
|
85
|
+
|
|
86
|
+
// Generate AetherJS middleware
|
|
87
|
+
middleware: () => createAetherRouter(router),
|
|
88
|
+
|
|
89
|
+
// Utility methods
|
|
90
|
+
match: (method, path) => router.match(method, path),
|
|
91
|
+
getRoutes: () => router.getRoutes(),
|
|
92
|
+
getStats: () => router.getStats(),
|
|
93
|
+
clearCache: () => router.clearCache()
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Default export for common use case
|
|
98
|
+
export default createAetherRouteFactory;
|