@engjts/nexus 0.1.6 → 0.1.8
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/BENCHMARK_REPORT.md +343 -0
- package/dist/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
- package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
- package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
- package/dist/advanced/playground/playground.d.ts +19 -0
- package/dist/advanced/playground/playground.d.ts.map +1 -1
- package/dist/advanced/playground/playground.js +70 -0
- package/dist/advanced/playground/playground.js.map +1 -1
- package/dist/advanced/playground/types.d.ts +20 -0
- package/dist/advanced/playground/types.d.ts.map +1 -1
- package/dist/cli/templates/generators.d.ts.map +1 -1
- package/dist/cli/templates/generators.js +16 -13
- package/dist/cli/templates/generators.js.map +1 -1
- package/dist/cli/templates/index.js +25 -25
- package/dist/core/application.d.ts +14 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/application.js +173 -71
- package/dist/core/application.js.map +1 -1
- package/dist/core/context-pool.d.ts +2 -13
- package/dist/core/context-pool.d.ts.map +1 -1
- package/dist/core/context-pool.js +7 -45
- package/dist/core/context-pool.js.map +1 -1
- package/dist/core/context.d.ts +108 -5
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +449 -53
- package/dist/core/context.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/middleware.d.ts +6 -0
- package/dist/core/middleware.d.ts.map +1 -1
- package/dist/core/middleware.js +83 -84
- package/dist/core/middleware.js.map +1 -1
- package/dist/core/performance/fast-json.d.ts +149 -0
- package/dist/core/performance/fast-json.d.ts.map +1 -0
- package/dist/core/performance/fast-json.js +473 -0
- package/dist/core/performance/fast-json.js.map +1 -0
- package/dist/core/router/file-router.d.ts +20 -7
- package/dist/core/router/file-router.d.ts.map +1 -1
- package/dist/core/router/file-router.js +41 -13
- package/dist/core/router/file-router.js.map +1 -1
- package/dist/core/router/index.d.ts +6 -0
- package/dist/core/router/index.d.ts.map +1 -1
- package/dist/core/router/index.js +33 -6
- package/dist/core/router/index.js.map +1 -1
- package/dist/core/router/radix-tree.d.ts +4 -1
- package/dist/core/router/radix-tree.d.ts.map +1 -1
- package/dist/core/router/radix-tree.js +7 -3
- package/dist/core/router/radix-tree.js.map +1 -1
- package/dist/core/serializer.d.ts +251 -0
- package/dist/core/serializer.d.ts.map +1 -0
- package/dist/core/serializer.js +290 -0
- package/dist/core/serializer.js.map +1 -0
- package/dist/core/types.d.ts +39 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/advanced/playground/generatePlaygroundHTML.ts +107 -0
- package/src/advanced/playground/playground.ts +225 -145
- package/src/advanced/playground/types.ts +29 -0
- package/src/cli/templates/generators.ts +16 -13
- package/src/cli/templates/index.ts +25 -25
- package/src/core/application.ts +202 -84
- package/src/core/context-pool.ts +8 -56
- package/src/core/context.ts +497 -53
- package/src/core/index.ts +14 -0
- package/src/core/middleware.ts +99 -89
- package/src/core/router/file-router.ts +41 -12
- package/src/core/router/index.ts +213 -180
- package/src/core/router/radix-tree.ts +20 -4
- package/src/core/serializer.ts +397 -0
- package/src/core/types.ts +43 -1
- package/src/index.ts +17 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# 📊 Benchmark Report: Nexus vs Express vs Fastify
|
|
2
|
+
|
|
3
|
+
**Tanggal Test:** 7 Desember 2025
|
|
4
|
+
**Environment:** macOS
|
|
5
|
+
**Node.js Runtime:** tsx (TypeScript execution)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ⚙️ Konfigurasi Benchmark
|
|
10
|
+
|
|
11
|
+
| Parameter | Value |
|
|
12
|
+
|-----------|-------|
|
|
13
|
+
| Duration | 10 detik per test |
|
|
14
|
+
| Connections | 100 concurrent |
|
|
15
|
+
| Pipelining | 10 requests per connection |
|
|
16
|
+
| Tool | autocannon v7.15.0 |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🏆 Hasil Overall
|
|
21
|
+
|
|
22
|
+
| Rank | Server | Avg Req/sec | Avg Latency | Total Errors |
|
|
23
|
+
|------|--------|-------------|-------------|--------------|
|
|
24
|
+
| 🥇 | **Fastify** | 71,578 | 16.5 ms | 0 |
|
|
25
|
+
| 🥈 | **Nexus** | 49,313 | 19.2 ms | 910 |
|
|
26
|
+
| 🥉 | **Express** | 20,277 | 51.6 ms | 0 |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 📌 Hasil Per Endpoint
|
|
31
|
+
|
|
32
|
+
### 1. Simple JSON Response (`GET /json`)
|
|
33
|
+
|
|
34
|
+
Response: `{ "message": "Hello, World!" }`
|
|
35
|
+
|
|
36
|
+
| Server | Req/sec | Latency (avg) | Latency (p99) | Throughput |
|
|
37
|
+
|--------|---------|---------------|---------------|------------|
|
|
38
|
+
| **Fastify** | 98,004 | 9.69 ms | 16 ms | 18.51 MB/s |
|
|
39
|
+
| **Nexus** | 96,131 | 9.99 ms | 15 ms | 16.69 MB/s |
|
|
40
|
+
| Express | 27,071 | 36.42 ms | 74 ms | 6.76 MB/s |
|
|
41
|
+
|
|
42
|
+
**🏆 Winner:** Fastify (+1.9% vs Nexus)
|
|
43
|
+
|
|
44
|
+
> ✅ **Nexus sangat kompetitif!** Hanya 1.9% lebih lambat dari Fastify untuk simple JSON.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### 2. Echo with Query Parameter (`GET /echo?name=Benchmark`)
|
|
49
|
+
|
|
50
|
+
Response: `{ "echo": "Hello, Benchmark!" }`
|
|
51
|
+
|
|
52
|
+
| Server | Req/sec | Latency (avg) | Latency (p99) | Throughput |
|
|
53
|
+
|--------|---------|---------------|---------------|------------|
|
|
54
|
+
| **Fastify** | 92,023 | 10.44 ms | 19 ms | 17.47 MB/s |
|
|
55
|
+
| **Nexus** | 71,066 | 13.70 ms | 23 ms | 12.40 MB/s |
|
|
56
|
+
| Express | 22,747 | 43.39 ms | 87 ms | 5.71 MB/s |
|
|
57
|
+
|
|
58
|
+
**🏆 Winner:** Fastify (+29.5% vs Nexus)
|
|
59
|
+
|
|
60
|
+
> ⚠️ **Gap mulai terlihat** saat ada query parameter parsing.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### 3. Large Array Response (`GET /users`)
|
|
65
|
+
|
|
66
|
+
Response: Array of 100 user objects
|
|
67
|
+
|
|
68
|
+
| Server | Req/sec | Latency (avg) | Latency (p99) | Throughput |
|
|
69
|
+
|--------|---------|---------------|---------------|------------|
|
|
70
|
+
| **Fastify** | 31,896 | 30.83 ms | 54 ms | 218.99 MB/s |
|
|
71
|
+
| **Nexus** | 30,047 | 32.75 ms | 68 ms | 205.84 MB/s |
|
|
72
|
+
| Express | 15,349 | 64.47 ms | 127 ms | 106.37 MB/s |
|
|
73
|
+
|
|
74
|
+
**🏆 Winner:** Fastify (+6.2% vs Nexus)
|
|
75
|
+
|
|
76
|
+
> ✅ **Nexus performs well** untuk payload besar, hanya 6% lebih lambat.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### 4. POST with JSON Body (`POST /data`)
|
|
81
|
+
|
|
82
|
+
Request Body: `{ "test": "data", "numbers": [1,2,3,4,5] }`
|
|
83
|
+
|
|
84
|
+
| Server | Req/sec | Latency (avg) | Latency (p99) | Errors |
|
|
85
|
+
|--------|---------|---------------|---------------|--------|
|
|
86
|
+
| **Fastify** | 64,388 | 15.03 ms | 28 ms | 0 |
|
|
87
|
+
| Express | 15,941 | 62.10 ms | 103 ms | 0 |
|
|
88
|
+
| Nexus | 8 | 20.44 ms | 27 ms | **910** |
|
|
89
|
+
|
|
90
|
+
**🏆 Winner:** Fastify
|
|
91
|
+
|
|
92
|
+
> 🚨 **CRITICAL ISSUE:** Nexus mengalami 910 timeout errors dan hanya mampu handle 8 req/sec!
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 🐛 Issue Analysis: Nexus POST Body Parsing
|
|
97
|
+
|
|
98
|
+
### Problem yang Ditemukan
|
|
99
|
+
|
|
100
|
+
Saat benchmark POST endpoint, Nexus mengalami:
|
|
101
|
+
- **910 timeout errors**
|
|
102
|
+
- **0 successful 2xx responses** (82 non-2xx responses)
|
|
103
|
+
- Throughput drop dari ~70k req/s ke **8 req/s**
|
|
104
|
+
|
|
105
|
+
### Root Cause Analysis
|
|
106
|
+
|
|
107
|
+
Berdasarkan kode server Nexus yang kita gunakan:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
app.post('/data', async (ctx) => {
|
|
111
|
+
const body = await ctx.body(); // ← Potential bottleneck
|
|
112
|
+
return ctx.json({ received: body, processed: true });
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Kemungkinan penyebab:**
|
|
117
|
+
|
|
118
|
+
1. **Async Body Parsing Bottleneck**
|
|
119
|
+
- `ctx.body()` mungkin tidak di-optimize untuk high concurrency
|
|
120
|
+
- Setiap request harus wait untuk body stream selesai di-parse
|
|
121
|
+
- Dengan 100 connections × 10 pipelining = 1000 concurrent requests, ini bisa cause blocking
|
|
122
|
+
|
|
123
|
+
2. **Stream Handling Issue**
|
|
124
|
+
- Body parsing mungkin tidak properly buffer incoming data
|
|
125
|
+
- Potential memory pressure saat banyak request bersamaan
|
|
126
|
+
|
|
127
|
+
3. **Missing Content-Type Handling**
|
|
128
|
+
- Mungkin perlu explicit JSON parser middleware
|
|
129
|
+
|
|
130
|
+
4. **Connection Pool Exhaustion**
|
|
131
|
+
- Async operations yang pending bisa exhaust available resources
|
|
132
|
+
|
|
133
|
+
### Rekomendasi untuk Nexus
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Option 1: Pre-parse body dengan middleware
|
|
137
|
+
app.use(async (ctx, next) => {
|
|
138
|
+
if (ctx.method === 'POST' || ctx.method === 'PUT') {
|
|
139
|
+
ctx.parsedBody = await ctx.body();
|
|
140
|
+
}
|
|
141
|
+
return next();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Option 2: Gunakan built-in JSON middleware jika ada
|
|
145
|
+
app.use(nexus.json()); // seperti express.json()
|
|
146
|
+
|
|
147
|
+
// Option 3: Batasi concurrent body parsing
|
|
148
|
+
import { Semaphore } from 'async-mutex';
|
|
149
|
+
const bodySemaphore = new Semaphore(50);
|
|
150
|
+
|
|
151
|
+
app.post('/data', async (ctx) => {
|
|
152
|
+
const [, release] = await bodySemaphore.acquire();
|
|
153
|
+
try {
|
|
154
|
+
const body = await ctx.body();
|
|
155
|
+
return ctx.json({ received: body, processed: true });
|
|
156
|
+
} finally {
|
|
157
|
+
release();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 📈 Performance Gap Analysis: Mengapa Nexus Kalah dari Fastify?
|
|
165
|
+
|
|
166
|
+
### 1. JSON Serialization
|
|
167
|
+
|
|
168
|
+
**Fastify** menggunakan `fast-json-stringify` yang pre-compile JSON schema:
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
// Fastify internal - schema-based serialization
|
|
172
|
+
const stringify = fastJson({
|
|
173
|
+
type: 'object',
|
|
174
|
+
properties: {
|
|
175
|
+
message: { type: 'string' }
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
// Output langsung tanpa JSON.stringify overhead
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Improvement untuk Nexus:**
|
|
182
|
+
- Implement schema-based JSON serialization
|
|
183
|
+
- Cache stringify functions per route
|
|
184
|
+
- Gunakan library seperti `fast-json-stringify` atau `@msgpack/msgpack`
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### 2. Router Performance
|
|
189
|
+
|
|
190
|
+
**Fastify** menggunakan `find-my-way`, radix tree-based router yang O(log n):
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
/users/:id → Radix tree lookup (sangat cepat)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Improvement untuk Nexus:**
|
|
197
|
+
- Evaluate current router implementation
|
|
198
|
+
- Consider menggunakan `find-my-way` atau `trek-router`
|
|
199
|
+
- Implement route caching untuk static routes
|
|
200
|
+
- Pre-compile regex patterns saat startup
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### 3. Request/Response Object Overhead
|
|
205
|
+
|
|
206
|
+
**Fastify** minimize object creation dan property access:
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// Fastify - minimal wrapper
|
|
210
|
+
reply.send({ data }); // Direct write ke socket
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Improvement untuk Nexus:**
|
|
214
|
+
- Reduce Context object properties
|
|
215
|
+
- Lazy-load properties yang jarang dipakai
|
|
216
|
+
- Pool dan reuse context objects
|
|
217
|
+
- Avoid spread operators di hot paths
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### 4. Body Parsing Strategy
|
|
222
|
+
|
|
223
|
+
**Fastify** parse body secara lazy dan streaming:
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
// Hanya parse jika diperlukan
|
|
227
|
+
fastify.addContentTypeParser('application/json',
|
|
228
|
+
{ parseAs: 'buffer' },
|
|
229
|
+
(req, body, done) => {
|
|
230
|
+
// Efficient buffer-based parsing
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Improvement untuk Nexus:**
|
|
236
|
+
- Implement streaming body parser
|
|
237
|
+
- Add body size limits untuk prevent DoS
|
|
238
|
+
- Cache parsed bodies
|
|
239
|
+
- Support different content types efficiently
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### 5. Middleware Chain Optimization
|
|
244
|
+
|
|
245
|
+
**Improvement untuk Nexus:**
|
|
246
|
+
```typescript
|
|
247
|
+
// Instead of array-based middleware
|
|
248
|
+
// Use compiled function chain
|
|
249
|
+
const compiledHandler = compileMiddleware([
|
|
250
|
+
middleware1,
|
|
251
|
+
middleware2,
|
|
252
|
+
routeHandler
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
function compileMiddleware(middlewares) {
|
|
256
|
+
return middlewares.reduceRight(
|
|
257
|
+
(next, mw) => (ctx) => mw(ctx, next),
|
|
258
|
+
(ctx) => ctx
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
### 6. HTTP Header Handling
|
|
266
|
+
|
|
267
|
+
**Improvement untuk Nexus:**
|
|
268
|
+
- Pre-allocate common headers
|
|
269
|
+
- Use header caching
|
|
270
|
+
- Minimize header parsing overhead
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// Cache static headers
|
|
274
|
+
const COMMON_HEADERS = {
|
|
275
|
+
'content-type': 'application/json; charset=utf-8',
|
|
276
|
+
'x-powered-by': 'Nexus'
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Apply once, not per-request
|
|
280
|
+
response.writeHead(200, COMMON_HEADERS);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 🎯 Priority Improvements untuk Nexus
|
|
286
|
+
|
|
287
|
+
### High Priority (Impact Besar)
|
|
288
|
+
|
|
289
|
+
| # | Improvement | Expected Gain | Effort |
|
|
290
|
+
|---|-------------|---------------|--------|
|
|
291
|
+
| 1 | Fix POST body parsing | +10,000% untuk POST | Medium |
|
|
292
|
+
| 2 | Schema-based JSON stringify | +15-25% | Medium |
|
|
293
|
+
| 3 | Optimize router | +10-20% | High |
|
|
294
|
+
|
|
295
|
+
### Medium Priority
|
|
296
|
+
|
|
297
|
+
| # | Improvement | Expected Gain | Effort |
|
|
298
|
+
|---|-------------|---------------|--------|
|
|
299
|
+
| 4 | Query string parser optimization | +5-15% | Low |
|
|
300
|
+
| 5 | Context object pooling | +5-10% | Medium |
|
|
301
|
+
| 6 | Header caching | +3-5% | Low |
|
|
302
|
+
|
|
303
|
+
### Low Priority (Nice to Have)
|
|
304
|
+
|
|
305
|
+
| # | Improvement | Expected Gain | Effort |
|
|
306
|
+
|---|-------------|---------------|--------|
|
|
307
|
+
| 7 | HTTP/2 support | Varies | High |
|
|
308
|
+
| 8 | Cluster mode built-in | Linear scaling | Medium |
|
|
309
|
+
| 9 | Native addon for hot paths | +20-30% | Very High |
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 📋 Summary
|
|
314
|
+
|
|
315
|
+
### Kelebihan Nexus Saat Ini
|
|
316
|
+
- ✅ Performa GET request sangat bagus (hampir setara Fastify)
|
|
317
|
+
- ✅ Simple JSON response hanya 1.9% lebih lambat dari Fastify
|
|
318
|
+
- ✅ Large payload handling bagus (6% gap)
|
|
319
|
+
- ✅ Developer Experience yang baik
|
|
320
|
+
|
|
321
|
+
### Area yang Perlu Diperbaiki
|
|
322
|
+
- 🔴 **CRITICAL:** POST body parsing broken under load
|
|
323
|
+
- 🟡 Query parameter parsing bisa lebih cepat (+29% gap)
|
|
324
|
+
- 🟡 JSON serialization bisa di-optimize
|
|
325
|
+
|
|
326
|
+
### Rekomendasi Utama
|
|
327
|
+
1. **Immediate:** Fix body parsing untuk POST/PUT requests
|
|
328
|
+
2. **Short-term:** Implement schema-based JSON serialization
|
|
329
|
+
3. **Medium-term:** Optimize router dengan radix tree
|
|
330
|
+
4. **Long-term:** Consider V8 fast-path optimizations
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 🔗 Resources
|
|
335
|
+
|
|
336
|
+
- [Fastify Benchmarks](https://fastify.dev/benchmarks/)
|
|
337
|
+
- [fast-json-stringify](https://github.com/fastify/fast-json-stringify)
|
|
338
|
+
- [find-my-way Router](https://github.com/delvedor/find-my-way)
|
|
339
|
+
- [Node.js Performance Best Practices](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/)
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
*Report generated by benchmark suite on December 7, 2025*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generatePlaygroundHTML.d.ts","sourceRoot":"","sources":["../../../src/advanced/playground/generatePlaygroundHTML.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAI3C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"generatePlaygroundHTML.d.ts","sourceRoot":"","sources":["../../../src/advanced/playground/generatePlaygroundHTML.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAI3C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CA+5DxF"}
|
|
@@ -515,6 +515,39 @@ function generatePlaygroundHTML(config, baseUrl) {
|
|
|
515
515
|
line-height: 1.5;
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
.info-curl-section {
|
|
519
|
+
margin-bottom: 16px;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.info-curl-btn {
|
|
523
|
+
width: 100%;
|
|
524
|
+
padding: 10px 16px;
|
|
525
|
+
background: var(--bg-tertiary);
|
|
526
|
+
color: var(--text-primary);
|
|
527
|
+
border: 1px solid var(--border-color);
|
|
528
|
+
border-radius: 6px;
|
|
529
|
+
font-size: 12px;
|
|
530
|
+
font-weight: 500;
|
|
531
|
+
cursor: pointer;
|
|
532
|
+
transition: all 0.2s;
|
|
533
|
+
display: flex;
|
|
534
|
+
align-items: center;
|
|
535
|
+
justify-content: center;
|
|
536
|
+
gap: 8px;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.info-curl-btn:hover {
|
|
540
|
+
background: var(--accent-color);
|
|
541
|
+
border-color: var(--accent-color);
|
|
542
|
+
color: #fff;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.info-curl-btn.copied {
|
|
546
|
+
background: var(--success-color);
|
|
547
|
+
border-color: var(--success-color);
|
|
548
|
+
color: #fff;
|
|
549
|
+
}
|
|
550
|
+
|
|
518
551
|
/* Resizer */
|
|
519
552
|
.resizer {
|
|
520
553
|
height: 6px;
|
|
@@ -1400,6 +1433,13 @@ function generatePlaygroundHTML(config, baseUrl) {
|
|
|
1400
1433
|
html += '<span class="path">' + ep.path + '</span>';
|
|
1401
1434
|
html += '</div>';
|
|
1402
1435
|
|
|
1436
|
+
// Copy as CURL button
|
|
1437
|
+
html += '<div class="info-curl-section">';
|
|
1438
|
+
html += '<button class="info-curl-btn" onclick="copyAsCurl()" id="curlCopyBtn">';
|
|
1439
|
+
html += '<span>📋</span> Copy as CURL';
|
|
1440
|
+
html += '</button>';
|
|
1441
|
+
html += '</div>';
|
|
1442
|
+
|
|
1403
1443
|
// Deprecated warning
|
|
1404
1444
|
if (ep.deprecated) {
|
|
1405
1445
|
html += '<div class="info-section">';
|
|
@@ -1549,6 +1589,73 @@ function generatePlaygroundHTML(config, baseUrl) {
|
|
|
1549
1589
|
saveCurrentTabState();
|
|
1550
1590
|
}
|
|
1551
1591
|
|
|
1592
|
+
function copyAsCurl() {
|
|
1593
|
+
const method = document.getElementById('methodSelect').value;
|
|
1594
|
+
const url = document.getElementById('urlInput').value;
|
|
1595
|
+
const body = requestEditor ? requestEditor.getValue() : '{}';
|
|
1596
|
+
|
|
1597
|
+
// Build CURL command
|
|
1598
|
+
let curl = 'curl';
|
|
1599
|
+
|
|
1600
|
+
// Add method
|
|
1601
|
+
if (method !== 'GET') {
|
|
1602
|
+
curl += ' -X ' + method;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
// Add URL (with query params)
|
|
1606
|
+
let fullUrl = url;
|
|
1607
|
+
const enabledParams = params.filter(p => p.enabled && p.key);
|
|
1608
|
+
if (enabledParams.length > 0) {
|
|
1609
|
+
const queryString = enabledParams
|
|
1610
|
+
.map(p => encodeURIComponent(p.key) + '=' + encodeURIComponent(p.value))
|
|
1611
|
+
.join('&');
|
|
1612
|
+
fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryString;
|
|
1613
|
+
}
|
|
1614
|
+
curl += " '" + fullUrl + "'";
|
|
1615
|
+
|
|
1616
|
+
// Add headers
|
|
1617
|
+
headers.filter(h => h.enabled && h.key).forEach(h => {
|
|
1618
|
+
curl += " -H '" + h.key + ": " + h.value + "'";
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
// Add body for non-GET requests
|
|
1622
|
+
if (method !== 'GET' && method !== 'DELETE') {
|
|
1623
|
+
try {
|
|
1624
|
+
// Check if body is valid JSON and not empty
|
|
1625
|
+
const parsedBody = JSON.parse(body);
|
|
1626
|
+
if (Object.keys(parsedBody).length > 0) {
|
|
1627
|
+
// Escape single quotes in body for shell
|
|
1628
|
+
const escapedBody = body.replace(/'/g, "'\\''");
|
|
1629
|
+
curl += " -d '" + escapedBody + "'";
|
|
1630
|
+
}
|
|
1631
|
+
} catch (e) {
|
|
1632
|
+
// If not valid JSON but has content, still include it
|
|
1633
|
+
if (body && body.trim() !== '' && body.trim() !== '{}') {
|
|
1634
|
+
const escapedBody = body.replace(/'/g, "'\\''");
|
|
1635
|
+
curl += " -d '" + escapedBody + "'";
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// Copy to clipboard
|
|
1641
|
+
navigator.clipboard.writeText(curl).then(() => {
|
|
1642
|
+
// Visual feedback
|
|
1643
|
+
const btn = document.getElementById('curlCopyBtn');
|
|
1644
|
+
if (btn) {
|
|
1645
|
+
const originalText = btn.innerHTML;
|
|
1646
|
+
btn.innerHTML = '<span>✓</span> Copied!';
|
|
1647
|
+
btn.classList.add('copied');
|
|
1648
|
+
setTimeout(() => {
|
|
1649
|
+
btn.innerHTML = originalText;
|
|
1650
|
+
btn.classList.remove('copied');
|
|
1651
|
+
}, 2000);
|
|
1652
|
+
}
|
|
1653
|
+
}).catch(err => {
|
|
1654
|
+
console.error('Failed to copy CURL:', err);
|
|
1655
|
+
alert('Failed to copy CURL to clipboard');
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1552
1659
|
function switchTab(tab) {
|
|
1553
1660
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
1554
1661
|
document.querySelectorAll('.editor-wrapper').forEach(w => w.classList.remove('active'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generatePlaygroundHTML.js","sourceRoot":"","sources":["../../../src/advanced/playground/generatePlaygroundHTML.ts"],"names":[],"mappings":";;AAIA,
|
|
1
|
+
{"version":3,"file":"generatePlaygroundHTML.js","sourceRoot":"","sources":["../../../src/advanced/playground/generatePlaygroundHTML.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { Plugin } from '../../core/types';
|
|
2
2
|
import { PlaygroundConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* API Playground Plugin
|
|
5
|
+
* Provides an interactive API explorer with authentication and development-mode security
|
|
6
|
+
*
|
|
7
|
+
* @param config - Configuration options for the playground
|
|
8
|
+
* @returns Plugin instance
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* app.use(playground({
|
|
13
|
+
* path: '/playground',
|
|
14
|
+
* developmentOnly: true,
|
|
15
|
+
* auth: {
|
|
16
|
+
* username: 'admin',
|
|
17
|
+
* password: 'secret123'
|
|
18
|
+
* }
|
|
19
|
+
* }));
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
3
22
|
export declare function playground(config?: PlaygroundConfig): Plugin;
|
|
4
23
|
//# sourceMappingURL=playground.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../../src/advanced/playground/playground.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoC,MAAM,EAA2B,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AAGxD,wBAAgB,UAAU,CAAC,MAAM,GAAE,gBAAqB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../../src/advanced/playground/playground.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoC,MAAM,EAA2B,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AAGxD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,MAAM,GAAE,gBAAqB,GAAG,MAAM,CA0KhE"}
|
|
@@ -7,6 +7,25 @@ const zodToExample_1 = require("./zodToExample");
|
|
|
7
7
|
const getTagFromPath_1 = require("./getTagFromPath");
|
|
8
8
|
const generateSummary_1 = require("./generateSummary");
|
|
9
9
|
const generatePlaygroundHTML_1 = require("./generatePlaygroundHTML");
|
|
10
|
+
/**
|
|
11
|
+
* API Playground Plugin
|
|
12
|
+
* Provides an interactive API explorer with authentication and development-mode security
|
|
13
|
+
*
|
|
14
|
+
* @param config - Configuration options for the playground
|
|
15
|
+
* @returns Plugin instance
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* app.use(playground({
|
|
20
|
+
* path: '/playground',
|
|
21
|
+
* developmentOnly: true,
|
|
22
|
+
* auth: {
|
|
23
|
+
* username: 'admin',
|
|
24
|
+
* password: 'secret123'
|
|
25
|
+
* }
|
|
26
|
+
* }));
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
10
29
|
function playground(config = {}) {
|
|
11
30
|
const resolvedConfig = {
|
|
12
31
|
path: '/playground',
|
|
@@ -15,6 +34,7 @@ function playground(config = {}) {
|
|
|
15
34
|
enableHistory: true,
|
|
16
35
|
maxHistory: 50,
|
|
17
36
|
enableVariables: true,
|
|
37
|
+
developmentOnly: true,
|
|
18
38
|
defaultHeaders: { 'Content-Type': 'application/json' },
|
|
19
39
|
variables: { baseUrl: 'http://localhost:3000', token: '' },
|
|
20
40
|
...config
|
|
@@ -26,6 +46,11 @@ function playground(config = {}) {
|
|
|
26
46
|
name: 'playground',
|
|
27
47
|
version: '1.0.0',
|
|
28
48
|
install(app) {
|
|
49
|
+
// Check if playground should be disabled in production
|
|
50
|
+
if (resolvedConfig.developmentOnly && process.env.NODE_ENV === 'production') {
|
|
51
|
+
console.warn('⚠️ API Playground is disabled in production mode');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
29
54
|
appInstance = app;
|
|
30
55
|
const originalRoute = app.route.bind(app);
|
|
31
56
|
const originalGet = app.get.bind(app);
|
|
@@ -71,7 +96,48 @@ function playground(config = {}) {
|
|
|
71
96
|
app.delete = wrapMethod('DELETE', originalDelete);
|
|
72
97
|
app.patch = wrapMethod('PATCH', originalPatch);
|
|
73
98
|
const basePath = resolvedConfig.path;
|
|
99
|
+
/**
|
|
100
|
+
* Authentication middleware for playground routes
|
|
101
|
+
* Implements HTTP Basic Authentication when auth config is provided
|
|
102
|
+
*/
|
|
103
|
+
const authMiddleware = (ctx) => {
|
|
104
|
+
if (!resolvedConfig.auth) {
|
|
105
|
+
return true; // No auth required
|
|
106
|
+
}
|
|
107
|
+
const authHeader = ctx.raw?.req?.headers?.authorization || ctx.headers?.get?.('authorization');
|
|
108
|
+
if (!authHeader || !authHeader.startsWith('Basic ')) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const base64Credentials = authHeader.substring(6);
|
|
113
|
+
const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8');
|
|
114
|
+
const [username, password] = credentials.split(':');
|
|
115
|
+
return (username === resolvedConfig.auth.username &&
|
|
116
|
+
password === resolvedConfig.auth.password);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Returns 401 Unauthorized response with WWW-Authenticate header
|
|
124
|
+
* This triggers the browser's native authentication dialog
|
|
125
|
+
*/
|
|
126
|
+
const unauthorizedResponse = () => {
|
|
127
|
+
return {
|
|
128
|
+
statusCode: 401,
|
|
129
|
+
headers: {
|
|
130
|
+
'WWW-Authenticate': 'Basic realm="API Playground"',
|
|
131
|
+
'Content-Type': 'text/plain'
|
|
132
|
+
},
|
|
133
|
+
body: 'Unauthorized'
|
|
134
|
+
};
|
|
135
|
+
};
|
|
74
136
|
originalGet(basePath, async (ctx) => {
|
|
137
|
+
// Check authentication
|
|
138
|
+
if (!authMiddleware(ctx)) {
|
|
139
|
+
return unauthorizedResponse();
|
|
140
|
+
}
|
|
75
141
|
if (!detectedBaseUrl && ctx.raw?.req) {
|
|
76
142
|
const req = ctx.raw.req;
|
|
77
143
|
const protocol = req.headers['x-forwarded-proto'] || 'http';
|
|
@@ -85,6 +151,10 @@ function playground(config = {}) {
|
|
|
85
151
|
};
|
|
86
152
|
});
|
|
87
153
|
originalGet(basePath + '/api/routes', async (ctx) => {
|
|
154
|
+
// Check authentication
|
|
155
|
+
if (!authMiddleware(ctx)) {
|
|
156
|
+
return unauthorizedResponse();
|
|
157
|
+
}
|
|
88
158
|
// Combine intercepted routes with routes from router (for file-based routing)
|
|
89
159
|
const allRoutes = getAllRoutes(routes, appInstance, basePath);
|
|
90
160
|
const routesData = allRoutes.map(r => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground.js","sourceRoot":"","sources":["../../../src/advanced/playground/playground.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"playground.js","sourceRoot":"","sources":["../../../src/advanced/playground/playground.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { HTTPMethod, RouteMeta, SchemaConfig } from "../../core/types";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for the API Playground
|
|
4
|
+
*/
|
|
2
5
|
export interface PlaygroundConfig {
|
|
6
|
+
/** URL path where the playground will be accessible (default: '/playground') */
|
|
3
7
|
path?: string;
|
|
8
|
+
/** Title displayed in the playground UI (default: 'API Playground') */
|
|
4
9
|
title?: string;
|
|
10
|
+
/** Color theme for the playground interface (default: 'dark') */
|
|
5
11
|
theme?: 'dark' | 'light';
|
|
12
|
+
/** Default HTTP headers to include in API requests */
|
|
6
13
|
defaultHeaders?: Record<string, string>;
|
|
14
|
+
/** Enable/disable request history tracking (default: true) */
|
|
7
15
|
enableHistory?: boolean;
|
|
16
|
+
/** Maximum number of requests to keep in history (default: 50) */
|
|
8
17
|
maxHistory?: number;
|
|
18
|
+
/** Enable/disable variable substitution in requests (default: true) */
|
|
9
19
|
enableVariables?: boolean;
|
|
20
|
+
/** Predefined variables for use in requests (e.g., {{baseUrl}}, {{token}}) */
|
|
10
21
|
variables?: Record<string, string>;
|
|
22
|
+
/** Enable only in development mode (default: true) */
|
|
23
|
+
developmentOnly?: boolean;
|
|
24
|
+
/** Basic authentication configuration to protect playground access */
|
|
25
|
+
auth?: {
|
|
26
|
+
/** Username for basic authentication */
|
|
27
|
+
username: string;
|
|
28
|
+
/** Password for basic authentication */
|
|
29
|
+
password: string;
|
|
30
|
+
};
|
|
11
31
|
}
|
|
12
32
|
export interface StoredRoute {
|
|
13
33
|
method: HTTPMethod;
|