javascript-solid-server 0.0.2
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/.claude/settings.local.json +15 -0
- package/README.md +209 -0
- package/benchmark-report-2025-03-31T14-25-24.234Z.json +44 -0
- package/benchmark.js +286 -0
- package/package.json +21 -0
- package/src/handlers/container.js +110 -0
- package/src/handlers/resource.js +162 -0
- package/src/index.js +8 -0
- package/src/ldp/container.js +43 -0
- package/src/ldp/headers.js +78 -0
- package/src/server.js +68 -0
- package/src/storage/filesystem.js +151 -0
- package/src/utils/url.js +83 -0
- package/visualize-results.js +258 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Simple script to generate an HTML report with charts from benchmark results
|
|
6
|
+
* Usage: node visualize-results.js path/to/benchmark-report.json
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
async function generateVisualization () {
|
|
10
|
+
// Get the report file path from command line args
|
|
11
|
+
const reportPath = process.argv[2];
|
|
12
|
+
|
|
13
|
+
if (!reportPath) {
|
|
14
|
+
console.error('Please provide a path to the benchmark report JSON file.');
|
|
15
|
+
console.error('Usage: node visualize-results.js path/to/benchmark-report.json');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Read and parse the report file
|
|
21
|
+
const reportContent = await fs.readFile(reportPath, 'utf8');
|
|
22
|
+
const report = JSON.parse(reportContent);
|
|
23
|
+
|
|
24
|
+
// Create the HTML template with embedded Chart.js
|
|
25
|
+
const htmlContent = `
|
|
26
|
+
<!DOCTYPE html>
|
|
27
|
+
<html lang="en">
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="UTF-8">
|
|
30
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31
|
+
<title>JavaScriptSolid Server Benchmark Results</title>
|
|
32
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
33
|
+
<style>
|
|
34
|
+
body {
|
|
35
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
36
|
+
margin: 0;
|
|
37
|
+
padding: 20px;
|
|
38
|
+
color: #333;
|
|
39
|
+
background-color: #f5f5f5;
|
|
40
|
+
}
|
|
41
|
+
.container {
|
|
42
|
+
max-width: 1200px;
|
|
43
|
+
margin: 0 auto;
|
|
44
|
+
background-color: white;
|
|
45
|
+
padding: 20px;
|
|
46
|
+
border-radius: 8px;
|
|
47
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
48
|
+
}
|
|
49
|
+
h1, h2 {
|
|
50
|
+
color: #2c3e50;
|
|
51
|
+
}
|
|
52
|
+
.chart-container {
|
|
53
|
+
margin: 30px 0;
|
|
54
|
+
height: 400px;
|
|
55
|
+
}
|
|
56
|
+
.metrics {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-wrap: wrap;
|
|
59
|
+
justify-content: space-between;
|
|
60
|
+
margin: 20px 0;
|
|
61
|
+
}
|
|
62
|
+
.metric-card {
|
|
63
|
+
background-color: #f8f9fa;
|
|
64
|
+
border-radius: 6px;
|
|
65
|
+
padding: 15px;
|
|
66
|
+
width: 18%;
|
|
67
|
+
margin-bottom: 15px;
|
|
68
|
+
box-shadow: 0 1px 5px rgba(0,0,0,0.05);
|
|
69
|
+
}
|
|
70
|
+
.metric-card h3 {
|
|
71
|
+
margin: 0 0 10px 0;
|
|
72
|
+
color: #2c3e50;
|
|
73
|
+
}
|
|
74
|
+
.metric-value {
|
|
75
|
+
font-size: 24px;
|
|
76
|
+
font-weight: bold;
|
|
77
|
+
color: #3498db;
|
|
78
|
+
}
|
|
79
|
+
.info {
|
|
80
|
+
background-color: #e8f4fd;
|
|
81
|
+
padding: 15px;
|
|
82
|
+
border-radius: 6px;
|
|
83
|
+
margin: 20px 0;
|
|
84
|
+
border-left: 4px solid #3498db;
|
|
85
|
+
}
|
|
86
|
+
@media (max-width: 768px) {
|
|
87
|
+
.metric-card {
|
|
88
|
+
width: 45%;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
@media (max-width: 480px) {
|
|
92
|
+
.metric-card {
|
|
93
|
+
width: 100%;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
97
|
+
</head>
|
|
98
|
+
<body>
|
|
99
|
+
<div class="container">
|
|
100
|
+
<h1>JavaScriptSolid Server Benchmark Results</h1>
|
|
101
|
+
<div class="info">
|
|
102
|
+
<strong>Server:</strong> ${report.server}<br>
|
|
103
|
+
<strong>Test Date:</strong> ${new Date(report.timestamp).toLocaleString()}<br>
|
|
104
|
+
<strong>Test Duration:</strong> ${report.testDuration / 1000} seconds per concurrency level
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<h2>Response Time Metrics</h2>
|
|
108
|
+
<div class="metrics">
|
|
109
|
+
<div class="metric-card">
|
|
110
|
+
<h3>Register</h3>
|
|
111
|
+
<div class="metric-value">${report.averageResponseTimes.register.toFixed(2)} ms</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="metric-card">
|
|
114
|
+
<h3>Login</h3>
|
|
115
|
+
<div class="metric-value">${report.averageResponseTimes.login.toFixed(2)} ms</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="metric-card">
|
|
118
|
+
<h3>Read</h3>
|
|
119
|
+
<div class="metric-value">${report.averageResponseTimes.read.toFixed(2)} ms</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="metric-card">
|
|
122
|
+
<h3>Write</h3>
|
|
123
|
+
<div class="metric-value">${report.averageResponseTimes.write.toFixed(2)} ms</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="metric-card">
|
|
126
|
+
<h3>Delete</h3>
|
|
127
|
+
<div class="metric-value">${report.averageResponseTimes.delete.toFixed(2)} ms</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<h2>Response Time Comparison</h2>
|
|
132
|
+
<div class="chart-container">
|
|
133
|
+
<canvas id="responseTimeChart"></canvas>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<h2>Throughput by Concurrent Users</h2>
|
|
137
|
+
<div class="chart-container">
|
|
138
|
+
<canvas id="throughputChart"></canvas>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<script>
|
|
143
|
+
// Data from the benchmark report
|
|
144
|
+
const responseTimeData = {
|
|
145
|
+
labels: ['Register', 'Login', 'Read', 'Write', 'Delete'],
|
|
146
|
+
datasets: [{
|
|
147
|
+
label: 'Average Response Time (ms)',
|
|
148
|
+
data: [
|
|
149
|
+
${report.averageResponseTimes.register.toFixed(2)},
|
|
150
|
+
${report.averageResponseTimes.login.toFixed(2)},
|
|
151
|
+
${report.averageResponseTimes.read.toFixed(2)},
|
|
152
|
+
${report.averageResponseTimes.write.toFixed(2)},
|
|
153
|
+
${report.averageResponseTimes.delete.toFixed(2)}
|
|
154
|
+
],
|
|
155
|
+
backgroundColor: [
|
|
156
|
+
'rgba(52, 152, 219, 0.5)',
|
|
157
|
+
'rgba(46, 204, 113, 0.5)',
|
|
158
|
+
'rgba(155, 89, 182, 0.5)',
|
|
159
|
+
'rgba(230, 126, 34, 0.5)',
|
|
160
|
+
'rgba(231, 76, 60, 0.5)'
|
|
161
|
+
],
|
|
162
|
+
borderColor: [
|
|
163
|
+
'rgba(52, 152, 219, 1)',
|
|
164
|
+
'rgba(46, 204, 113, 1)',
|
|
165
|
+
'rgba(155, 89, 182, 1)',
|
|
166
|
+
'rgba(230, 126, 34, 1)',
|
|
167
|
+
'rgba(231, 76, 60, 1)'
|
|
168
|
+
],
|
|
169
|
+
borderWidth: 1
|
|
170
|
+
}]
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Throughput data from the report
|
|
174
|
+
const throughputData = {
|
|
175
|
+
labels: ${JSON.stringify(report.throughputResults.map(r => r.concurrentUsers + ' Users'))},
|
|
176
|
+
datasets: [{
|
|
177
|
+
label: 'Operations per Second',
|
|
178
|
+
data: ${JSON.stringify(report.throughputResults.map(r => r.throughput.toFixed(2)))},
|
|
179
|
+
backgroundColor: 'rgba(52, 152, 219, 0.5)',
|
|
180
|
+
borderColor: 'rgba(52, 152, 219, 1)',
|
|
181
|
+
borderWidth: 1
|
|
182
|
+
}]
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Create the charts
|
|
186
|
+
window.onload = function() {
|
|
187
|
+
// Response time chart
|
|
188
|
+
const rtCtx = document.getElementById('responseTimeChart').getContext('2d');
|
|
189
|
+
new Chart(rtCtx, {
|
|
190
|
+
type: 'bar',
|
|
191
|
+
data: responseTimeData,
|
|
192
|
+
options: {
|
|
193
|
+
responsive: true,
|
|
194
|
+
maintainAspectRatio: false,
|
|
195
|
+
plugins: {
|
|
196
|
+
title: {
|
|
197
|
+
display: true,
|
|
198
|
+
text: 'Average Response Time by Operation Type'
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
scales: {
|
|
202
|
+
y: {
|
|
203
|
+
beginAtZero: true,
|
|
204
|
+
title: {
|
|
205
|
+
display: true,
|
|
206
|
+
text: 'Time (ms)'
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Throughput chart
|
|
214
|
+
const tpCtx = document.getElementById('throughputChart').getContext('2d');
|
|
215
|
+
new Chart(tpCtx, {
|
|
216
|
+
type: 'line',
|
|
217
|
+
data: throughputData,
|
|
218
|
+
options: {
|
|
219
|
+
responsive: true,
|
|
220
|
+
maintainAspectRatio: false,
|
|
221
|
+
plugins: {
|
|
222
|
+
title: {
|
|
223
|
+
display: true,
|
|
224
|
+
text: 'Throughput by Concurrent Users'
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
scales: {
|
|
228
|
+
y: {
|
|
229
|
+
beginAtZero: true,
|
|
230
|
+
title: {
|
|
231
|
+
display: true,
|
|
232
|
+
text: 'Operations per Second'
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
</script>
|
|
240
|
+
</body>
|
|
241
|
+
</html>
|
|
242
|
+
`;
|
|
243
|
+
|
|
244
|
+
// Write the HTML file
|
|
245
|
+
const htmlFilePath = `benchmark-report-${path.basename(reportPath, '.json')}.html`;
|
|
246
|
+
await fs.writeFile(htmlFilePath, htmlContent);
|
|
247
|
+
|
|
248
|
+
console.log(`Visualization generated successfully: ${htmlFilePath}`);
|
|
249
|
+
console.log(`Open this file in a web browser to view the charts.`);
|
|
250
|
+
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error('Error generating visualization:', error.message);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Run the visualization generator
|
|
258
|
+
generateVisualization();
|