qhttpx 1.8.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/.eslintrc.json +22 -0
- package/.github/workflows/ci.yml +32 -0
- package/.github/workflows/npm-publish.yml +37 -0
- package/.github/workflows/release.yml +21 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +145 -0
- package/LICENSE +21 -0
- package/README.md +343 -0
- package/dist/package.json +61 -0
- package/dist/src/benchmarks/compare-frameworks.js +119 -0
- package/dist/src/benchmarks/quantam-users.js +56 -0
- package/dist/src/benchmarks/simple-json.js +58 -0
- package/dist/src/benchmarks/ultra-mode.js +122 -0
- package/dist/src/cli/index.js +200 -0
- package/dist/src/client/index.js +72 -0
- package/dist/src/core/batch.js +97 -0
- package/dist/src/core/body-parser.js +121 -0
- package/dist/src/core/buffer-pool.js +70 -0
- package/dist/src/core/config.js +50 -0
- package/dist/src/core/fusion.js +183 -0
- package/dist/src/core/logger.js +49 -0
- package/dist/src/core/metrics.js +111 -0
- package/dist/src/core/resources.js +25 -0
- package/dist/src/core/scheduler.js +85 -0
- package/dist/src/core/scope.js +68 -0
- package/dist/src/core/serializer.js +44 -0
- package/dist/src/core/server.js +905 -0
- package/dist/src/core/stream.js +71 -0
- package/dist/src/core/tasks.js +87 -0
- package/dist/src/core/types.js +19 -0
- package/dist/src/core/websocket.js +86 -0
- package/dist/src/core/worker-queue.js +73 -0
- package/dist/src/database/adapters/memory.js +90 -0
- package/dist/src/database/adapters/mongo.js +141 -0
- package/dist/src/database/adapters/postgres.js +111 -0
- package/dist/src/database/adapters/sqlite.js +42 -0
- package/dist/src/database/coalescer.js +134 -0
- package/dist/src/database/manager.js +87 -0
- package/dist/src/database/types.js +2 -0
- package/dist/src/index.js +61 -0
- package/dist/src/middleware/compression.js +133 -0
- package/dist/src/middleware/cors.js +66 -0
- package/dist/src/middleware/presets.js +33 -0
- package/dist/src/middleware/rate-limit.js +77 -0
- package/dist/src/middleware/security.js +69 -0
- package/dist/src/middleware/static.js +191 -0
- package/dist/src/openapi/generator.js +149 -0
- package/dist/src/router/radix-router.js +89 -0
- package/dist/src/router/radix-tree.js +81 -0
- package/dist/src/router/router.js +146 -0
- package/dist/src/testing/index.js +84 -0
- package/dist/src/utils/cookies.js +59 -0
- package/dist/src/utils/logger.js +45 -0
- package/dist/src/utils/signals.js +31 -0
- package/dist/src/utils/sse.js +32 -0
- package/dist/src/validation/index.js +19 -0
- package/dist/src/validation/simple.js +102 -0
- package/dist/src/validation/types.js +12 -0
- package/dist/src/validation/zod.js +18 -0
- package/dist/src/views/index.js +17 -0
- package/dist/src/views/types.js +2 -0
- package/dist/tests/adapters.test.js +106 -0
- package/dist/tests/batch.test.js +117 -0
- package/dist/tests/body-parser.test.js +52 -0
- package/dist/tests/compression-sse.test.js +87 -0
- package/dist/tests/cookies.test.js +63 -0
- package/dist/tests/cors.test.js +55 -0
- package/dist/tests/database.test.js +80 -0
- package/dist/tests/dx.test.js +64 -0
- package/dist/tests/ecosystem.test.js +133 -0
- package/dist/tests/features.test.js +47 -0
- package/dist/tests/fusion.test.js +92 -0
- package/dist/tests/http-basic.test.js +124 -0
- package/dist/tests/logger.test.js +33 -0
- package/dist/tests/middleware.test.js +109 -0
- package/dist/tests/observability.test.js +59 -0
- package/dist/tests/openapi.test.js +64 -0
- package/dist/tests/plugin.test.js +65 -0
- package/dist/tests/plugins.test.js +71 -0
- package/dist/tests/rate-limit.test.js +77 -0
- package/dist/tests/resources.test.js +44 -0
- package/dist/tests/scheduler.test.js +46 -0
- package/dist/tests/schema-routes.test.js +77 -0
- package/dist/tests/security.test.js +83 -0
- package/dist/tests/server-db.test.js +72 -0
- package/dist/tests/smoke.test.js +10 -0
- package/dist/tests/sqlite-fusion.test.js +92 -0
- package/dist/tests/static.test.js +102 -0
- package/dist/tests/stream.test.js +44 -0
- package/dist/tests/task-metrics.test.js +53 -0
- package/dist/tests/tasks.test.js +62 -0
- package/dist/tests/testing.test.js +47 -0
- package/dist/tests/validation.test.js +107 -0
- package/dist/tests/websocket.test.js +146 -0
- package/dist/vitest.config.js +9 -0
- package/docs/AEGIS.md +76 -0
- package/docs/BENCHMARKS.md +36 -0
- package/docs/CAPABILITIES.md +70 -0
- package/docs/CLI.md +43 -0
- package/docs/DATABASE.md +142 -0
- package/docs/ECOSYSTEM.md +146 -0
- package/docs/NEXT_STEPS.md +99 -0
- package/docs/OPENAPI.md +99 -0
- package/docs/PLUGINS.md +59 -0
- package/docs/REAL_WORLD_EXAMPLES.md +109 -0
- package/docs/ROADMAP.md +366 -0
- package/docs/VALIDATION.md +136 -0
- package/eslint.config.cjs +26 -0
- package/examples/api-server.ts +254 -0
- package/package.json +61 -0
- package/src/benchmarks/compare-frameworks.ts +149 -0
- package/src/benchmarks/quantam-users.ts +70 -0
- package/src/benchmarks/simple-json.ts +71 -0
- package/src/benchmarks/ultra-mode.ts +159 -0
- package/src/cli/index.ts +214 -0
- package/src/client/index.ts +93 -0
- package/src/core/batch.ts +110 -0
- package/src/core/body-parser.ts +151 -0
- package/src/core/buffer-pool.ts +96 -0
- package/src/core/config.ts +60 -0
- package/src/core/fusion.ts +210 -0
- package/src/core/logger.ts +70 -0
- package/src/core/metrics.ts +166 -0
- package/src/core/resources.ts +38 -0
- package/src/core/scheduler.ts +126 -0
- package/src/core/scope.ts +87 -0
- package/src/core/serializer.ts +41 -0
- package/src/core/server.ts +1113 -0
- package/src/core/stream.ts +111 -0
- package/src/core/tasks.ts +138 -0
- package/src/core/types.ts +178 -0
- package/src/core/websocket.ts +112 -0
- package/src/core/worker-queue.ts +90 -0
- package/src/database/adapters/memory.ts +99 -0
- package/src/database/adapters/mongo.ts +116 -0
- package/src/database/adapters/postgres.ts +86 -0
- package/src/database/adapters/sqlite.ts +44 -0
- package/src/database/coalescer.ts +153 -0
- package/src/database/manager.ts +97 -0
- package/src/database/types.ts +24 -0
- package/src/index.ts +42 -0
- package/src/middleware/compression.ts +147 -0
- package/src/middleware/cors.ts +98 -0
- package/src/middleware/presets.ts +50 -0
- package/src/middleware/rate-limit.ts +106 -0
- package/src/middleware/security.ts +109 -0
- package/src/middleware/static.ts +216 -0
- package/src/openapi/generator.ts +167 -0
- package/src/router/radix-router.ts +119 -0
- package/src/router/radix-tree.ts +106 -0
- package/src/router/router.ts +190 -0
- package/src/testing/index.ts +104 -0
- package/src/utils/cookies.ts +67 -0
- package/src/utils/logger.ts +59 -0
- package/src/utils/signals.ts +45 -0
- package/src/utils/sse.ts +41 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/simple.ts +93 -0
- package/src/validation/types.ts +38 -0
- package/src/validation/zod.ts +14 -0
- package/src/views/index.ts +1 -0
- package/src/views/types.ts +4 -0
- package/tests/adapters.test.ts +120 -0
- package/tests/batch.test.ts +139 -0
- package/tests/body-parser.test.ts +83 -0
- package/tests/compression-sse.test.ts +98 -0
- package/tests/cookies.test.ts +74 -0
- package/tests/cors.test.ts +79 -0
- package/tests/database.test.ts +90 -0
- package/tests/dx.test.ts +78 -0
- package/tests/ecosystem.test.ts +156 -0
- package/tests/features.test.ts +51 -0
- package/tests/fusion.test.ts +121 -0
- package/tests/http-basic.test.ts +161 -0
- package/tests/logger.test.ts +48 -0
- package/tests/middleware.test.ts +137 -0
- package/tests/observability.test.ts +91 -0
- package/tests/openapi.test.ts +74 -0
- package/tests/plugin.test.ts +85 -0
- package/tests/plugins.test.ts +93 -0
- package/tests/rate-limit.test.ts +97 -0
- package/tests/resources.test.ts +64 -0
- package/tests/scheduler.test.ts +71 -0
- package/tests/schema-routes.test.ts +89 -0
- package/tests/security.test.ts +128 -0
- package/tests/server-db.test.ts +72 -0
- package/tests/smoke.test.ts +9 -0
- package/tests/sqlite-fusion.test.ts +106 -0
- package/tests/static.test.ts +111 -0
- package/tests/stream.test.ts +58 -0
- package/tests/task-metrics.test.ts +78 -0
- package/tests/tasks.test.ts +90 -0
- package/tests/testing.test.ts +53 -0
- package/tests/validation.test.ts +126 -0
- package/tests/websocket.test.ts +132 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +9 -0
package/docs/ROADMAP.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# QHTTPX Roadmap
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
QHTTPX is evolving toward a **lightweight, composable runtime** that prioritizes structural efficiency over microbenchmarks. Each phase targets a specific bottleneck in the HTTP request path, guided by the principle: *lighter core, more powerful abstractions*.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Current State: v0.1
|
|
10
|
+
|
|
11
|
+
### Phase 1 – Hot-path Allocations ✅
|
|
12
|
+
|
|
13
|
+
**Status: Implemented and Preallocated**
|
|
14
|
+
|
|
15
|
+
#### Context Pooling
|
|
16
|
+
- `contextPool: QHTTPXContext[]` preallocates up to `maxConcurrency` contexts at startup
|
|
17
|
+
- `acquireContext()` pulls from pool and resets fields
|
|
18
|
+
- `releaseContext()` cleans and returns to pool
|
|
19
|
+
- **Effect**: No new context allocations under normal load; stable object shapes for hidden-class optimization
|
|
20
|
+
|
|
21
|
+
#### Buffer Reuse
|
|
22
|
+
- Engine supports pluggable `jsonSerializer?: (value: unknown) => string | Buffer`
|
|
23
|
+
- Apps can precompile response buffers (e.g., health checks, metrics)
|
|
24
|
+
- Benchmarks demonstrate zero-allocation JSON on hot paths
|
|
25
|
+
- **Effect**: Users can achieve static buffer responses; mechanism generalizable
|
|
26
|
+
|
|
27
|
+
#### Outcome
|
|
28
|
+
- GC pauses reduced; p99 latencies stabilized
|
|
29
|
+
- Foundation for buffer pools and serialization optimization
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### Phase 2 – Routing (Partial) ⚠️
|
|
34
|
+
|
|
35
|
+
**Status: Functional but Not Optimized**
|
|
36
|
+
|
|
37
|
+
#### Current Implementation
|
|
38
|
+
- Simple `RouteDefinition[]` list
|
|
39
|
+
- Linear scan per HTTP method
|
|
40
|
+
- Per-request normalization: `path.split('/')`, then segment matching
|
|
41
|
+
|
|
42
|
+
#### What's Missing
|
|
43
|
+
- No per-method route buckets (still scans all routes regardless of method)
|
|
44
|
+
- No freeze-on-boot design (routes can be added/modified at any time)
|
|
45
|
+
- No compiled/flattened structure (no radix tree, no array indices)
|
|
46
|
+
- Per-request overhead: string splits, object allocations in match logic
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### Phase 3 – Zero-cost Middleware ✅
|
|
51
|
+
|
|
52
|
+
**Status: Implemented (Lighter Pipeline)**
|
|
53
|
+
|
|
54
|
+
#### Pipeline Flattening
|
|
55
|
+
- Removed per-request stack copy combining middlewares + route handler
|
|
56
|
+
- Route handler no longer wrapped as extra middleware
|
|
57
|
+
- Simple Koa-compatible dispatch loop: `middlewares[i](ctx, next)`
|
|
58
|
+
- **Effect**: Fewer allocations; maintains familiar middleware API
|
|
59
|
+
|
|
60
|
+
#### What's Not Yet Done
|
|
61
|
+
- No precompiled runner (still dynamically dispatches through middleware chain)
|
|
62
|
+
- Could evolve to single-loop execution with inlined middleware logic
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### Phase 4 – Serialization (Partial) ✅
|
|
67
|
+
|
|
68
|
+
**Status: Pluggable Hook Available**
|
|
69
|
+
|
|
70
|
+
- Engine supports custom `jsonSerializer` at options level
|
|
71
|
+
- Hot paths can use prebuilt Buffers
|
|
72
|
+
- Mechanism ready for future schema compilers (e.g., fast-json-stringify style)
|
|
73
|
+
|
|
74
|
+
#### What's Not Yet Done
|
|
75
|
+
- No built-in schema compiler integration
|
|
76
|
+
- No automatic path detection (e.g., `/health` → static buffer)
|
|
77
|
+
- General buffer pools (small/medium/large) not yet part of runtime
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### Phase 5+ – Scheduler & Runtime
|
|
82
|
+
|
|
83
|
+
**Status: Skeleton Present**
|
|
84
|
+
|
|
85
|
+
- `Scheduler` class with concurrency model
|
|
86
|
+
- `TaskEngine` for background tasks
|
|
87
|
+
- **Not yet**: Per-worker queues, work-stealing, lock-free primitives
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## v0.2: Focus on Router & Consolidation
|
|
92
|
+
|
|
93
|
+
### Goals
|
|
94
|
+
1. **Make routing lazy-safe and indexable**: Support per-method buckets and freeze-on-boot design
|
|
95
|
+
2. **Improve measurability**: Track what v0.1 achieved; establish baselines for v0.2 improvements
|
|
96
|
+
3. **Maintain backward compatibility**: Existing API surface unchanged
|
|
97
|
+
|
|
98
|
+
### Scope
|
|
99
|
+
|
|
100
|
+
#### 1. Router Optimization – Frozen, Per-Method Buckets
|
|
101
|
+
**Deliverable**: `router.ts` refactor
|
|
102
|
+
|
|
103
|
+
- **Step 1**: Reorganize routes internally into per-method buckets
|
|
104
|
+
- `routes: { GET: RouteDefinition[], POST: RouteDefinition[], ... }`
|
|
105
|
+
- Reduce scan loop overhead by method-specific lookups
|
|
106
|
+
|
|
107
|
+
- **Step 2**: Introduce freeze-on-boot pattern
|
|
108
|
+
- After `listen()` is called, warn (or optionally error) on new route registrations
|
|
109
|
+
- Document that routes must be registered before server start
|
|
110
|
+
|
|
111
|
+
- **Step 3**: Prebuild derived structures
|
|
112
|
+
- Static routes table (path → handler, no params)
|
|
113
|
+
- Parameterized routes list (faster fallback for `/foo/:id` patterns)
|
|
114
|
+
- Prepare for radix compilation in v0.3
|
|
115
|
+
|
|
116
|
+
**Why v0.2**: Routing is a bottleneck on every request. Fixing it early unlocks measurable gains and clearer architecture for v0.3's radix tree.
|
|
117
|
+
|
|
118
|
+
**Risk**: Low. Public API unchanged; internals reorganized.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
#### 2. Buffer Pools (Optional, If Time)
|
|
123
|
+
**Deliverable**: Internal buffer pool API
|
|
124
|
+
|
|
125
|
+
- Small/medium/large pools for response bodies
|
|
126
|
+
- Available to engine and future plugins
|
|
127
|
+
- Keep mechanism simple: leak-proof, no external memory pressure
|
|
128
|
+
|
|
129
|
+
**Why optional**: Nice-to-have for future buffer reuse. Can ship in v0.2 if implementation is lightweight; defer to v0.3 if it adds complexity.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
#### 3. Documentation & Baselines
|
|
134
|
+
**Deliverable**: Updated `docs/`
|
|
135
|
+
|
|
136
|
+
- Clarify v0.1 achievements (context pooling, pipeline flattening, pluggable JSON)
|
|
137
|
+
- Benchmark baseline for v0.2 router changes
|
|
138
|
+
- Explain freezing pattern and its implications for startup/shutdown
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### Out of Scope for v0.2
|
|
143
|
+
- Radix tree implementation (defer to v0.3)
|
|
144
|
+
- Precompiled middleware runner (v0.3+)
|
|
145
|
+
- Scheduler work-stealing (v0.5+)
|
|
146
|
+
- Built-in schema compiler (v0.3+)
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## v0.3: Radix Router & Middleware Compilation
|
|
151
|
+
|
|
152
|
+
### Goals
|
|
153
|
+
1. **Flatten routing further**: Build radix tree at boot, match in tight loop
|
|
154
|
+
2. **Precompile middleware chains**: Remove per-request dispatch overhead
|
|
155
|
+
3. **Introduce buffer pools formally**: General-purpose pooling for response bodies
|
|
156
|
+
|
|
157
|
+
### Scope
|
|
158
|
+
|
|
159
|
+
#### 1. Radix Router
|
|
160
|
+
- Build tree structure at `listen()` time
|
|
161
|
+
- Flatten into arrays: `{ charCode, childIndex, handlerIndex, paramKeyIndex }`
|
|
162
|
+
- Match becomes a tight loop with zero allocations
|
|
163
|
+
- Support param extraction and wildcard routes
|
|
164
|
+
|
|
165
|
+
#### 2. Middleware Precompilation
|
|
166
|
+
- When `use()` or routes are registered, compile a single runner function
|
|
167
|
+
- Runner is array of middleware + final handler, executed in simple loop
|
|
168
|
+
- Maintains Koa semantics, removes dispatch overhead
|
|
169
|
+
|
|
170
|
+
#### 3. Buffer Pools Formalized
|
|
171
|
+
- Integrate into `server.ts` as standard pools (S/M/L)
|
|
172
|
+
- Make available to route handlers via context
|
|
173
|
+
- Document pool semantics (request-scoped resets)
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## v0.5: Scheduler & Lock-Free Queues
|
|
178
|
+
|
|
179
|
+
### Goals
|
|
180
|
+
1. **Per-worker concurrency**: Ring buffers, per-core task queues
|
|
181
|
+
2. **Work-stealing**: Tasks move to idle cores
|
|
182
|
+
3. **Unified HTTP + Task scheduling**: Same primitives, different priorities
|
|
183
|
+
|
|
184
|
+
### Scope
|
|
185
|
+
- Refactor `scheduler.ts` and `tasks.ts`
|
|
186
|
+
- Introduce lock-free work queues
|
|
187
|
+
- Integrate with existing concurrency model
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Key Principles (All Phases)
|
|
192
|
+
|
|
193
|
+
1. **Lighter, not faster**: Optimize structure, not just throughput
|
|
194
|
+
2. **Backward compatible**: Public API stays stable across minor versions
|
|
195
|
+
3. **Measurable**: Baseline before, measure after, document gains
|
|
196
|
+
4. **Composable**: Each phase builds on previous; no regressions
|
|
197
|
+
5. **No benchmark chasing**: Focus on real bottlenecks (allocation, dispatch, lookup)
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Success Criteria
|
|
202
|
+
|
|
203
|
+
### v0.2
|
|
204
|
+
- [ ] Router per-method buckets implemented
|
|
205
|
+
- [ ] Freeze-on-boot pattern enforced
|
|
206
|
+
- [ ] Derived structures (static/param routes) prebuild correctly
|
|
207
|
+
- [ ] Tests pass; backward compatibility verified
|
|
208
|
+
- [ ] Baseline benchmarks established (context pooling, pipeline, router latency)
|
|
209
|
+
|
|
210
|
+
### v0.3
|
|
211
|
+
- [ ] Radix tree built at boot; match is tight loop
|
|
212
|
+
- [ ] Middleware chain precompiled; no per-request dispatch
|
|
213
|
+
- [ ] Buffer pools integrated into `server.ts`
|
|
214
|
+
- [ ] P50/P99 latencies improve measurably vs v0.2
|
|
215
|
+
- [ ] Public API unchanged
|
|
216
|
+
|
|
217
|
+
### v0.5
|
|
218
|
+
- [ ] Scheduler supports per-worker queues
|
|
219
|
+
- [ ] Work-stealing implemented and tested
|
|
220
|
+
- [ ] HTTP and task scheduling unified under same primitives
|
|
221
|
+
- [ ] System handles burst loads better (work moves to idle cores)
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Next Steps
|
|
226
|
+
|
|
227
|
+
1. **Immediate**: Start v0.2 router refactor (per-method buckets, freeze pattern)
|
|
228
|
+
2. **Parallel**: Update benchmarks to track v0.2 improvements
|
|
229
|
+
3. **Document**: Clarify startup/shutdown semantics with frozen routes
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Framework & Production Roadmap
|
|
234
|
+
|
|
235
|
+
This section focuses on framework-level features: production readiness, observability, safety, and capabilities that differentiate QHTTPX from frameworks like Express and Fastify.
|
|
236
|
+
|
|
237
|
+
### v0.4 – Production Framework Essentials ✅
|
|
238
|
+
|
|
239
|
+
**Status: Implemented**
|
|
240
|
+
|
|
241
|
+
**Goals**
|
|
242
|
+
|
|
243
|
+
- Make behavior predictable in real services (errors, shutdown, config).
|
|
244
|
+
- Provide first-class observability and safety, not just hot-path speed.
|
|
245
|
+
- Keep the core small and composable.
|
|
246
|
+
|
|
247
|
+
**Scope**
|
|
248
|
+
|
|
249
|
+
1. **Error Handling & HTTP Error Model**
|
|
250
|
+
- Introduce an `HttpError` type with `status`, `code`, `details`.
|
|
251
|
+
- Add an opt-in global error middleware:
|
|
252
|
+
- Catches thrown errors from handlers/middlewares.
|
|
253
|
+
- Maps `HttpError` to consistent JSON or text responses.
|
|
254
|
+
- Hides internal errors behind a generic 500 except in development.
|
|
255
|
+
- Provide helpers to register custom 404 and 405 handlers instead of hardcoded text responses.
|
|
256
|
+
|
|
257
|
+
2. **Configuration & Environment Handling**
|
|
258
|
+
- Clarify how `QHTTPXOptions` is used for runtime configuration.
|
|
259
|
+
- Provide a recommended pattern for constructing `QHTTPX` instances from environment variables.
|
|
260
|
+
- Optionally expose a small `loadConfig({ env, defaults })` helper to centralize configuration parsing and validation.
|
|
261
|
+
|
|
262
|
+
3. **Graceful Shutdown & Lifecycle Hooks**
|
|
263
|
+
- Add lifecycle hooks on the `QHTTPX` instance, such as `onStart`, `onShutdown`, `onBeforeShutdown`.
|
|
264
|
+
- Implement graceful shutdown:
|
|
265
|
+
- Stop accepting new connections.
|
|
266
|
+
- Let in-flight requests complete up to a configurable timeout.
|
|
267
|
+
- Flush metrics and background tasks before process exit.
|
|
268
|
+
- Provide a helper to wire process signals, e.g. `attachSignalHandlers(app, { signals: ['SIGINT', 'SIGTERM'] })`.
|
|
269
|
+
|
|
270
|
+
4. **Observability & Request Identity**
|
|
271
|
+
- Extend metrics and logger support with optional request ID correlation:
|
|
272
|
+
- Generate a `x-request-id` when missing.
|
|
273
|
+
- Expose the request ID on the context and in log entries/metrics.
|
|
274
|
+
- Add a minimal interface for integrating external tracers (e.g., OpenTelemetry) without coupling QHTTPX to specific libraries.
|
|
275
|
+
- Promote a structured logging example (JSON sink) as the recommended production default.
|
|
276
|
+
|
|
277
|
+
5. **Security Baseline Pack**
|
|
278
|
+
- Combine CORS and security headers middlewares into a recommended preset:
|
|
279
|
+
- `createSecureDefaults({ cors, csp, hsts, ... })` returning a set of middlewares.
|
|
280
|
+
- Add a simple rate-limiting middleware (token bucket per IP or identifier) with pluggable storage, starting with in-memory.
|
|
281
|
+
- Extend `readBody` handling to enforce a `maxBodyBytes` option and return 413 on oversized requests.
|
|
282
|
+
|
|
283
|
+
6. **Testing Utilities**
|
|
284
|
+
- Provide a lightweight testing helper:
|
|
285
|
+
- `createTestClient(app: QHTTPX)` to start the server on a random port.
|
|
286
|
+
- `client.request({ method, path, body, headers })` for integration tests.
|
|
287
|
+
- Ensure it works smoothly with Vitest and document recommended test patterns.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### v0.6 – Differentiating Features vs Fastify/Express
|
|
292
|
+
|
|
293
|
+
**Goals**
|
|
294
|
+
|
|
295
|
+
- Turn QHTTPX into a resource-aware HTTP runtime, not just a router.
|
|
296
|
+
- Offer built-in quality-of-service and safety mechanisms.
|
|
297
|
+
- Leverage existing scheduler, task engine, and metrics as first-class features.
|
|
298
|
+
|
|
299
|
+
**Scope**
|
|
300
|
+
|
|
301
|
+
1. **Unified HTTP + Background Task Runtime**
|
|
302
|
+
- Elevate the existing `TaskEngine` and `Scheduler` into a core story:
|
|
303
|
+
- Clear API to register tasks tied to the application lifecycle.
|
|
304
|
+
- Expose task metrics (queue depths, retries, failures) via `/__qhttpx/metrics`.
|
|
305
|
+
- Introduce simple task priority classes (e.g., high vs low priority queues).
|
|
306
|
+
|
|
307
|
+
2. **Resource-Aware Admission Control**
|
|
308
|
+
- Extend resource checks beyond RSS to include:
|
|
309
|
+
- In-flight requests.
|
|
310
|
+
- Scheduler queue sizes.
|
|
311
|
+
- Latency metrics (e.g., p99).
|
|
312
|
+
- Introduce a load shedding mechanism:
|
|
313
|
+
- Optionally classify routes as "critical" or "best-effort".
|
|
314
|
+
- Shed lower-priority requests under high load (returning 503).
|
|
315
|
+
|
|
316
|
+
3. **Latency Budgets & SLO-Aware Routing**
|
|
317
|
+
- Allow per-route or per-group latency budgets (e.g., `100ms`).
|
|
318
|
+
- Track budget violations and optionally:
|
|
319
|
+
- Reject or degrade noncritical routes.
|
|
320
|
+
- Provide simple SLO classes such as `critical`, `standard`, `background`.
|
|
321
|
+
|
|
322
|
+
4. **Concurrency & Priority Classes for Requests**
|
|
323
|
+
- Build on `SchedulerOptions` and per-worker queues:
|
|
324
|
+
- Annotate routes with priority and map them to worker queues.
|
|
325
|
+
- Route latency-sensitive paths to high-priority queues and batch/admin endpoints to lower-priority queues.
|
|
326
|
+
|
|
327
|
+
5. **First-Class Streaming & SSE Patterns**
|
|
328
|
+
- Promote existing SSE and streaming helpers into a "live services" story:
|
|
329
|
+
- Provide patterns for broadcasting events to many SSE clients.
|
|
330
|
+
- Ensure backpressure-aware file and data streaming.
|
|
331
|
+
- Integrate connection counts and streaming durations into metrics.
|
|
332
|
+
|
|
333
|
+
6. **Introspectable Runtime**
|
|
334
|
+
- Extend `/__qhttpx/metrics` and related endpoints to expose:
|
|
335
|
+
- Per-worker queue statistics.
|
|
336
|
+
- Task engine statistics (registered tasks, pending retries).
|
|
337
|
+
- Router statistics (route counts, frozen state).
|
|
338
|
+
- Optionally add `/__qhttpx/runtime` for a JSON summary of runtime state (excluding secrets).
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### v0.8 – Developer Experience & Ergonomics
|
|
343
|
+
|
|
344
|
+
**Goals**
|
|
345
|
+
|
|
346
|
+
- Improve ergonomics without bloating the core engine.
|
|
347
|
+
- Provide opinionated presets that are still decomposable into primitives.
|
|
348
|
+
|
|
349
|
+
**Scope**
|
|
350
|
+
|
|
351
|
+
1. **Routing Ergonomics**
|
|
352
|
+
- Add route builders such as `app.route('/users/:id')` to group methods.
|
|
353
|
+
- Provide a typed helper layer for TypeScript users:
|
|
354
|
+
- Wrap `QHTTPXHandler` with generics for params, query, body, and response types.
|
|
355
|
+
|
|
356
|
+
2. **Preset Stacks**
|
|
357
|
+
- "API preset":
|
|
358
|
+
- JSON responses, CORS, logger, metrics, error handler, security headers, and size limits.
|
|
359
|
+
- "Static site + API preset":
|
|
360
|
+
- Adds static middleware with recommended settings on top of the API preset.
|
|
361
|
+
- Implement presets as thin helpers that call `app.use(...)` with existing middlewares.
|
|
362
|
+
|
|
363
|
+
3. **CLI (Optional)**
|
|
364
|
+
- Introduce a minimal CLI to scaffold starter projects:
|
|
365
|
+
- Example app with `/health`, `/metrics`, SSE example, a background task, and basic tests.
|
|
366
|
+
- Keep the CLI small and optional so QHTTPX remains primarily a runtime and library.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Input Validation & Coercion
|
|
2
|
+
|
|
3
|
+
QHTTPX v1.2.0 introduces a powerful, zero-dependency validation system. It ensures that incoming data (Body, Query Parameters, and URL Params) matches your expectations before your handler ever runs.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Zero Dependency**: Built-in `SimpleValidator` handles common use cases without bloating your `node_modules`.
|
|
8
|
+
- **Automatic Coercion**: Automatically converts query parameters (which are always strings) into numbers or booleans based on your schema.
|
|
9
|
+
- **Type Safety**: Ensures `ctx.body`, `ctx.query`, and `ctx.params` contain valid data.
|
|
10
|
+
- **Extensible**: You can plug in your own validator (like Zod, Joi, or TypeBox) if you need more power.
|
|
11
|
+
|
|
12
|
+
## Basic Usage
|
|
13
|
+
|
|
14
|
+
Define a `schema` object in your route options. The schema can have `body`, `query`, `params`, and `response` sections.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { createHttpApp } from 'qhttpx';
|
|
18
|
+
|
|
19
|
+
const app = createHttpApp();
|
|
20
|
+
|
|
21
|
+
app.post('/users', {
|
|
22
|
+
schema: {
|
|
23
|
+
// Validate Request Body
|
|
24
|
+
body: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
username: { type: 'string', min: 3, max: 20 },
|
|
28
|
+
age: { type: 'number', min: 18 },
|
|
29
|
+
email: { type: 'string', pattern: '^\\S+@\\S+\\.\\S+$' },
|
|
30
|
+
tags: { type: 'array', items: { type: 'string' } }
|
|
31
|
+
},
|
|
32
|
+
required: true // default is true
|
|
33
|
+
},
|
|
34
|
+
// Validate Query Parameters
|
|
35
|
+
query: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
ref: { type: 'string' }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
handler: (ctx) => {
|
|
43
|
+
// TypeScript knows these are valid if you cast them,
|
|
44
|
+
// or trust the runtime validation.
|
|
45
|
+
const { username, age } = ctx.body as any;
|
|
46
|
+
|
|
47
|
+
ctx.json({
|
|
48
|
+
status: 'created',
|
|
49
|
+
user: { username, age }
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Schema Reference
|
|
56
|
+
|
|
57
|
+
The built-in `SimpleValidator` supports the following types:
|
|
58
|
+
|
|
59
|
+
### `string`
|
|
60
|
+
- `min`: Minimum length
|
|
61
|
+
- `max`: Maximum length
|
|
62
|
+
- `pattern`: Regex pattern (string)
|
|
63
|
+
- `enum`: Array of allowed values
|
|
64
|
+
|
|
65
|
+
### `number`
|
|
66
|
+
- `min`: Minimum value
|
|
67
|
+
- `max`: Maximum value
|
|
68
|
+
- `enum`: Array of allowed values
|
|
69
|
+
|
|
70
|
+
### `boolean`
|
|
71
|
+
- Coerces `"true"`, `"false"` strings to booleans automatically.
|
|
72
|
+
|
|
73
|
+
### `array`
|
|
74
|
+
- `min`: Minimum items
|
|
75
|
+
- `max`: Maximum items
|
|
76
|
+
- `items`: Schema for array items
|
|
77
|
+
|
|
78
|
+
### `object`
|
|
79
|
+
- `properties`: Map of field names to schemas.
|
|
80
|
+
|
|
81
|
+
## Automatic Coercion
|
|
82
|
+
|
|
83
|
+
Query parameters are inherently strings. The validator automatically converts them for you.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// URL: /items?page=5&active=true
|
|
87
|
+
|
|
88
|
+
app.get('/items', {
|
|
89
|
+
schema: {
|
|
90
|
+
query: {
|
|
91
|
+
type: 'object',
|
|
92
|
+
properties: {
|
|
93
|
+
page: { type: 'number' }, // "5" -> 5
|
|
94
|
+
active: { type: 'boolean' } // "true" -> true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
handler: (ctx) => {
|
|
99
|
+
const page = ctx.query.page; // This is now a number!
|
|
100
|
+
// ...
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Custom Validators (Zod, etc.)
|
|
106
|
+
|
|
107
|
+
If you prefer a library like Zod, you can implement the `Validator` interface and pass it to the app options.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { z } from 'zod';
|
|
111
|
+
import { Validator, ValidationResult } from 'qhttpx';
|
|
112
|
+
|
|
113
|
+
class ZodValidator implements Validator {
|
|
114
|
+
validate(schema: any, data: any): ValidationResult {
|
|
115
|
+
const result = schema.safeParse(data);
|
|
116
|
+
if (result.success) {
|
|
117
|
+
return { success: true, data: result.data };
|
|
118
|
+
}
|
|
119
|
+
return { success: false, error: result.error };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const app = createHttpApp({
|
|
124
|
+
validator: new ZodValidator()
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Now use Zod schemas in your routes!
|
|
128
|
+
app.post('/zod', {
|
|
129
|
+
schema: {
|
|
130
|
+
body: z.object({
|
|
131
|
+
name: z.string().min(3)
|
|
132
|
+
})
|
|
133
|
+
},
|
|
134
|
+
handler: (ctx) => { ... }
|
|
135
|
+
});
|
|
136
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const tsParser = require("@typescript-eslint/parser");
|
|
2
|
+
const tsPlugin = require("@typescript-eslint/eslint-plugin");
|
|
3
|
+
const prettierPlugin = require("eslint-plugin-prettier");
|
|
4
|
+
|
|
5
|
+
module.exports = [
|
|
6
|
+
{
|
|
7
|
+
files: ["**/*.ts"],
|
|
8
|
+
ignores: ["dist/**", "node_modules/**"],
|
|
9
|
+
languageOptions: {
|
|
10
|
+
parser: tsParser,
|
|
11
|
+
parserOptions: {
|
|
12
|
+
project: "./tsconfig.json",
|
|
13
|
+
tsconfigRootDir: __dirname,
|
|
14
|
+
sourceType: "module",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
plugins: {
|
|
18
|
+
"@typescript-eslint": tsPlugin,
|
|
19
|
+
prettier: prettierPlugin,
|
|
20
|
+
},
|
|
21
|
+
rules: {
|
|
22
|
+
...tsPlugin.configs.recommended.rules,
|
|
23
|
+
"prettier/prettier": "off",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
];
|