@techiev2/vajra 1.5.0 → 1.5.1
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 +6 -0
- package/README.md +3 -0
- package/benchmark.sh +17 -12
- package/index.js +5 -4
- package/package.json +1 -1
package/README
CHANGED
|
@@ -22,6 +22,12 @@ Like the Vajra, this server delivers maximum power in minimal form.
|
|
|
22
22
|
|
|
23
23
|
## Changelog
|
|
24
24
|
|
|
25
|
+
### 1.5.1 (2026-01-04)
|
|
26
|
+
- Adds a pre-allocated buffer based body reader for improvement.
|
|
27
|
+
|
|
28
|
+
### 1.5.0 (2026-01-03)
|
|
29
|
+
- Adds support for res.sendFile.
|
|
30
|
+
|
|
25
31
|
### 1.4.3 (2026-01-02)
|
|
26
32
|
- Adds guardrails for unsafe operations with template paths.
|
|
27
33
|
|
package/README.md
CHANGED
package/benchmark.sh
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
set -
|
|
2
|
+
set -eox pipefail
|
|
3
3
|
declare -A portMap=(
|
|
4
4
|
["elysia"]=4000
|
|
5
5
|
["express"]=4001
|
|
@@ -15,23 +15,28 @@ declare -A runnerMap=(
|
|
|
15
15
|
["hono"]="index.js"
|
|
16
16
|
["vajra"]="examples/api.js"
|
|
17
17
|
)
|
|
18
|
-
frameworks=$(ls -d */ | sed 's|/$||' | grep -v auth
|
|
18
|
+
frameworks=$(ls -d */ | sed 's|/$||' | grep -v auth)
|
|
19
|
+
if [ -z "$1" ]; then
|
|
20
|
+
frameworks=$(echo $frameworks | grep "$1")
|
|
21
|
+
fi
|
|
22
|
+
|
|
19
23
|
|
|
20
24
|
LOOPS=5
|
|
21
25
|
|
|
22
26
|
runWrk() {
|
|
23
27
|
port=$1
|
|
24
|
-
sync && echo 3 >
|
|
25
|
-
wrk -t16 -c600 -d10s
|
|
26
|
-
-H "Content-Type: multipart/form-data; boundary=BOUNDARY123456" \
|
|
27
|
-
"http://localhost:${port}/upload" < ../test_upload_2mb.txt
|
|
28
|
+
sync && echo 3 > /proc/sys/vm/drop_caches
|
|
29
|
+
wrk -t16 -c600 -d10s -H "Content-Type: multipart/form-data; boundary=BOUNDARY123456" "http://localhost:${port}/upload" < ../test_upload_2mb.txt
|
|
28
30
|
}
|
|
29
31
|
main() {
|
|
30
|
-
|
|
32
|
+
mkdir -p reports
|
|
33
|
+
runtime=$(date '+%Y-%m-%d_%H:%M:%S')
|
|
34
|
+
reportFile="./reports/report_${runtime}.txt"
|
|
35
|
+
echo "" > $reportFile
|
|
31
36
|
for framework in ${frameworks[@]}; do
|
|
32
37
|
cd $framework
|
|
33
38
|
mkdir -p benchmarks
|
|
34
|
-
echo "" > benchmarks/
|
|
39
|
+
echo "" > benchmarks/report_${runtime}.txt
|
|
35
40
|
port=${portMap[$framework]}
|
|
36
41
|
runner=${runnerMap[$framework]}
|
|
37
42
|
for i in $(seq 1 $LOOPS); do
|
|
@@ -40,18 +45,18 @@ main() {
|
|
|
40
45
|
runningPID=$(lsof | grep -P ":$port" | head -n1 | awk '{print $2}')
|
|
41
46
|
echo "$framework running at $runningPID"
|
|
42
47
|
sleep 2
|
|
43
|
-
runWrk $port >> benchmarks/
|
|
48
|
+
runWrk $port >> benchmarks/report_${runtime}.txt
|
|
44
49
|
sleep 2
|
|
45
50
|
echo "Killing $framework running at $runningPID"
|
|
46
51
|
kill -9 $runningPID
|
|
47
52
|
done
|
|
48
|
-
avg_rps=$(grep "Requests/sec:" benchmarks/
|
|
49
|
-
avg_tps=$(grep "Transfer/sec:" benchmarks/
|
|
53
|
+
avg_rps=$(grep "Requests/sec:" benchmarks/report_${runtime}.txt | awk -F' ' '{print $2}' | awk '{x+=$1; n++} END { print x/n}')
|
|
54
|
+
avg_tps=$(grep "Transfer/sec:" benchmarks/report_${runtime}.txt | awk -F' ' '{print $2}' | awk '{x+=$1; n++} END { print x/n}')
|
|
50
55
|
echo "${framework}
|
|
51
56
|
Average RPS: ${avg_rps}/s
|
|
52
57
|
Average TPS: ${avg_tps}MB/s
|
|
53
58
|
|
|
54
|
-
" >>
|
|
59
|
+
" >> $reportFile
|
|
55
60
|
cd ..
|
|
56
61
|
done
|
|
57
62
|
}
|
package/index.js
CHANGED
|
@@ -62,13 +62,14 @@ export default class Vajra {
|
|
|
62
62
|
if (req.method === 'GET' || req.method === 'HEAD') { return runMiddlwares() }
|
|
63
63
|
async function runMiddlwares() { let idx = 0; const next = async () => { if (idx >= Vajra.#middlewares.length) { return setImmediate(handleRoute) } const fn = Vajra.#middlewares[idx]; idx++; try { await fn(req, res, next); } catch (err) { return default_500({ url: req.url, method: req.method }, res, err); } }; await next(); }
|
|
64
64
|
setImmediate(() => {
|
|
65
|
-
req.body = {}; req.rawData = ''; req.formData = {}; let
|
|
66
|
-
req.on('data', (chunk) => {
|
|
65
|
+
req.body = {}; req.rawData = ''; req.formData = {}; let totalSize = 0; let chunks = []
|
|
66
|
+
req.on('data', (chunk) => { if (totalSize + chunk.length > Vajra.#MAX_FILE_SIZE) { return default_413(res) }; chunks.push(chunk); totalSize += chunk.length; })
|
|
67
67
|
const formDataMatcher = /Content-Disposition: form-data; name=['"](?<name>[^"']+)['"]\s+(?<value>.*?)$/smi;
|
|
68
68
|
let boundaryMatch = (req.headers['Content-Type'] || '').match(/boundary=(.*)/); const boundary = boundaryMatch ? '--' + boundaryMatch[1] : null; const fileDataMatcher = /^Content-Disposition:.*?name=["'](?<field>[^"']+)["'].*?filename=["'](?<fileName>[^"']+)["'].*?Content-Type:\s*(?<contentType>[^\r\n]*)\r?\n\r?\n(?<content>[\s\S]*)$/ims
|
|
69
69
|
req.on('end', async () => {
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
const buffer = Buffer.allocUnsafe(totalSize); let offset = 0;
|
|
71
|
+
for (const chunk of chunks) { chunk.copy(buffer, offset); offset += chunk.length; }
|
|
72
|
+
req.rawData = buffer.toString(); req.files = []; if (boundary) { req.rawData.split(boundary).filter(Boolean).map((line) => {
|
|
72
73
|
let key, value; if (line.includes('filename')) { req.files.push(fileDataMatcher.exec(line)?.groups || {}); return }
|
|
73
74
|
[key, value] = Object.values(line.match(formDataMatcher)?.groups || {}); (key && value) && Object.assign(req.formData, { [key]: value }); return
|
|
74
75
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techiev2/vajra",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Blazing-fast, zero-dependency Node.js server with routing, middleware, multipart uploads, and templating. 111 lines · ~95k req/s · ~52 MB idle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"http-server",
|