@mila.solutions/express 5.2.1-mila.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/LICENSE +25 -0
- package/Readme.md +212 -0
- package/index.js +11 -0
- package/lib/application.js +694 -0
- package/lib/express.js +81 -0
- package/lib/request.js +527 -0
- package/lib/response.js +1186 -0
- package/lib/schema-serializer.js +102 -0
- package/lib/utils.js +271 -0
- package/lib/view.js +205 -0
- package/package.json +99 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
(The MIT License)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
|
|
4
|
+
Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
|
|
5
|
+
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
|
6
|
+
Copyright (c) 2026 Mila (performance fork)
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
9
|
+
a copy of this software and associated documentation files (the
|
|
10
|
+
'Software'), to deal in the Software without restriction, including
|
|
11
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
12
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
13
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
14
|
+
the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be
|
|
17
|
+
included in all copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
20
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
21
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
22
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
23
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
24
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
25
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/Readme.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# @mila.solutions/express
|
|
2
|
+
|
|
3
|
+
Performance-focused fork of [Express](https://expressjs.com/) 5.2.1 with integrated [`fast-json-stringify`](https://github.com/fastify/fast-json-stringify) and internal optimizations.
|
|
4
|
+
|
|
5
|
+
**100% compatible with Express 5** — all 1,342 tests pass. Drop-in replacement.
|
|
6
|
+
|
|
7
|
+
## What's different
|
|
8
|
+
|
|
9
|
+
| Feature | Express 5.2.1 | @mila.solutions/express |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| JSON serialization | `JSON.stringify` | `fast-json-stringify` (pre-compiled via JSON Schema) |
|
|
12
|
+
| Route-level schemas | No | Yes — per-route, per-status-code |
|
|
13
|
+
| `res.locals` | Allocated every request | Lazy — only allocated on first access |
|
|
14
|
+
| Error handler binding | New closure per request | Pre-bound at init |
|
|
15
|
+
| `req.fresh` check | Always runs | Short-circuited for non-conditional requests |
|
|
16
|
+
| ETag on JSON | Always | Configurable via `json etag` setting |
|
|
17
|
+
|
|
18
|
+
### Benchmark results (10K requests, 20 concurrent)
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Express 5.2.1 (original) ███████████████████████████████████ 6,647 rps
|
|
22
|
+
@mila (no schema) ████████████████████████████████████████ 7,577 rps (+14%)
|
|
23
|
+
@mila (schema) ███████████████████████████████████████ 7,377 rps (+11%)
|
|
24
|
+
@mila (schema + no etag) ███████████████████████████████████████ 7,414 rps (+12%)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**+10-19% faster** depending on configuration. Improvement comes from reduced allocations per request and pre-compiled JSON serialization.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @mila.solutions/express
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
Works exactly like Express:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const express = require('@mila.solutions/express')
|
|
41
|
+
const app = express()
|
|
42
|
+
|
|
43
|
+
app.get('/users', (req, res) => {
|
|
44
|
+
res.json({ id: 1, name: 'John' })
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
app.listen(3000)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Schema-based JSON serialization
|
|
51
|
+
|
|
52
|
+
Define a JSON Schema on your route to enable `fast-json-stringify`. The schema is compiled once at startup and reused for every request.
|
|
53
|
+
|
|
54
|
+
### Basic usage
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
const express = require('@mila.solutions/express')
|
|
58
|
+
const app = express()
|
|
59
|
+
|
|
60
|
+
app.get('/user', {
|
|
61
|
+
schema: {
|
|
62
|
+
response: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
id: { type: 'integer' },
|
|
66
|
+
name: { type: 'string' },
|
|
67
|
+
email: { type: 'string' }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}, (req, res) => {
|
|
72
|
+
res.json({ id: 1, name: 'John', email: 'john@example.com' })
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Per-status-code schemas
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
app.post('/users', {
|
|
80
|
+
schema: {
|
|
81
|
+
response: {
|
|
82
|
+
201: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
id: { type: 'integer' },
|
|
86
|
+
name: { type: 'string' }
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
400: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
error: { type: 'string' },
|
|
93
|
+
details: { type: 'array', items: { type: 'string' } }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}, (req, res) => {
|
|
99
|
+
// Schema for 201 is used automatically
|
|
100
|
+
res.status(201).json({ id: 1, name: 'John' })
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Generic status code groups
|
|
105
|
+
|
|
106
|
+
Use `2xx`, `3xx`, `4xx`, `5xx` to match any status code in a range:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
app.get('/data', {
|
|
110
|
+
schema: {
|
|
111
|
+
response: {
|
|
112
|
+
'2xx': {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
data: { type: 'string' }
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
'4xx': {
|
|
119
|
+
type: 'object',
|
|
120
|
+
properties: {
|
|
121
|
+
error: { type: 'string' }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}, handler)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Resolution order
|
|
130
|
+
|
|
131
|
+
When `res.json()` is called, the serializer is resolved in this order:
|
|
132
|
+
|
|
133
|
+
1. **Exact match** — status code matches exactly (e.g. `200`)
|
|
134
|
+
2. **Generic match** — status code group matches (e.g. `2xx`)
|
|
135
|
+
3. **Default** — a plain schema object (no status code keys)
|
|
136
|
+
4. **Fallback** — standard `JSON.stringify`
|
|
137
|
+
|
|
138
|
+
### App-level schemas
|
|
139
|
+
|
|
140
|
+
Set a default schema for all routes:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
app.set('json schema', {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
data: {},
|
|
147
|
+
meta: {}
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Route-level schemas always take priority over app-level.
|
|
153
|
+
|
|
154
|
+
## Additional settings
|
|
155
|
+
|
|
156
|
+
### `json etag`
|
|
157
|
+
|
|
158
|
+
Disable ETag generation for JSON responses:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
app.set('json etag', false)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
This skips the `etag` computation on every `res.json()` call, which saves CPU when your clients don't use conditional requests (`If-None-Match`).
|
|
165
|
+
|
|
166
|
+
## How it works
|
|
167
|
+
|
|
168
|
+
### fast-json-stringify
|
|
169
|
+
|
|
170
|
+
Standard `JSON.stringify` inspects every value at runtime to determine its type. `fast-json-stringify` uses JSON Schema to generate a specialized serializer function at startup that knows the exact shape of your data. This avoids type checking per-value and produces strings ~1.5x faster for typical API payloads.
|
|
171
|
+
|
|
172
|
+
Schemas are compiled once and cached via `WeakMap`, so the same schema object always returns the same pre-compiled serializer.
|
|
173
|
+
|
|
174
|
+
### Internal optimizations
|
|
175
|
+
|
|
176
|
+
These apply to all requests, even without schemas:
|
|
177
|
+
|
|
178
|
+
- **Pre-bound error handler** — `logerror` is bound once at `app.init()` instead of creating a new closure per request in `app.handle()`
|
|
179
|
+
- **Lazy `res.locals`** — Uses a self-replacing getter. The object is only created when your code actually accesses `res.locals`, saving an allocation on routes that don't use it
|
|
180
|
+
- **`req.fresh` short-circuit** — Skips the full freshness check when the request has no `If-None-Match` or `If-Modified-Since` headers
|
|
181
|
+
- **Buffer fast-path in `_sendJson()`** — Pre-computes Content-Length from the buffer byte length instead of going through the full `res.send()` path
|
|
182
|
+
|
|
183
|
+
## Compatibility
|
|
184
|
+
|
|
185
|
+
- Based on Express **5.2.1**
|
|
186
|
+
- Requires Node.js **>= 18**
|
|
187
|
+
- All **1,342** Express tests pass
|
|
188
|
+
- Drop-in replacement — no API changes for existing code
|
|
189
|
+
- Schemas are opt-in; without them, it behaves identically to Express
|
|
190
|
+
|
|
191
|
+
## Running tests
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm install
|
|
195
|
+
npm test
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Running benchmarks
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# A/B comparison (Express original vs @mila)
|
|
202
|
+
node benchmarks/compare.js --requests 10000
|
|
203
|
+
|
|
204
|
+
# Scaling test
|
|
205
|
+
node benchmarks/scale.js
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
[MIT](LICENSE)
|
|
211
|
+
|
|
212
|
+
Based on [Express](https://github.com/expressjs/express) by the Express.js community.
|
package/index.js
ADDED