owebjs 1.5.8-dev → 1.6.0-dev

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.md CHANGED
@@ -99,6 +99,7 @@ import Oweb from 'owebjs';
99
99
  const app = await new Oweb({
100
100
  uWebSocketsEnabled: true,
101
101
  poweredByHeader: false,
102
+ autoPreflight: true,
102
103
  staticResponseHeaders: {
103
104
  // CORS (set your real origin in production)
104
105
  'access-control-allow-origin': 'https://yourdomain.com',
@@ -126,6 +127,28 @@ await app.loadRoutes({
126
127
  await app.start({ port: 3000, host: '0.0.0.0' });
127
128
  ```
128
129
 
130
+ ## CORS Configuration
131
+
132
+ Use `autoPreflight` to return `204` for preflight requests and set CORS headers through `staticResponseHeaders`.
133
+
134
+ ```js
135
+ import Oweb from 'owebjs';
136
+
137
+ const app = await new Oweb({
138
+ uWebSocketsEnabled: true,
139
+ autoPreflight: true,
140
+ poweredByHeader: false,
141
+ staticResponseHeaders: {
142
+ 'access-control-allow-origin': 'https://yourdomain.com',
143
+ 'access-control-allow-methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
144
+ 'access-control-allow-headers': 'Content-Type, Authorization',
145
+ vary: 'Origin',
146
+ },
147
+ }).setup();
148
+ ```
149
+
150
+ If you need credentials, do not use `*` for `access-control-allow-origin`; set an explicit origin.
151
+
129
152
  ## First App (2 Minutes)
130
153
 
131
154
  Start with a minimal app, then we will add route conventions step by step.
@@ -167,7 +190,7 @@ routes/
167
190
  [id].js
168
191
  auth/
169
192
  login.post.js
170
- matcher/
193
+ posts/
171
194
  [id=integer].js
172
195
  events/
173
196
  sse.js
@@ -236,7 +259,7 @@ export default class LoginPostRoute extends Route {
236
259
 
237
260
  Matcher params add filename-level validation.
238
261
 
239
- `routes/matcher/[id=integer].js` + `matchers/integer.js`
262
+ `routes/posts/[id=integer].js` + `matchers/integer.js`
240
263
 
241
264
  ```js
242
265
  // matchers/integer.js
package/dist/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export { HTTPMethods } from 'fastify/types/utils';
21
21
  interface OwebOptions extends FastifyServerOptions {
22
22
  uWebSocketsEnabled?: boolean;
23
23
  poweredByHeader?: boolean;
24
+ autoPreflight?: boolean;
24
25
  staticResponseHeaders?: Record<string, string>;
25
26
  OWEB_INTERNAL_ERROR_HANDLER?: Function;
26
27
  }
@@ -29,6 +29,7 @@ class Oweb extends _FastifyInstance {
29
29
  this._options = options ?? {};
30
30
  this._options.uWebSocketsEnabled ??= false;
31
31
  this._options.poweredByHeader ??= true;
32
+ this._options.autoPreflight ??= false;
32
33
  this._options.OWEB_INTERNAL_ERROR_HANDLER ??= (_, res, err) => {
33
34
  return res.status(500).send({
34
35
  error: err.message
@@ -62,7 +63,9 @@ class Oweb extends _FastifyInstance {
62
63
  if (this._options.uWebSocketsEnabled) {
63
64
  const serverimp = (await import("../uwebsocket/server.js")).default;
64
65
  const server = await serverimp({
65
- staticResponseHeaders: this._options.staticResponseHeaders
66
+ staticResponseHeaders: this._options.staticResponseHeaders,
67
+ autoPreflight: this._options.autoPreflight,
68
+ poweredByHeader: this._options.poweredByHeader
66
69
  });
67
70
  this.uServer = server;
68
71
  this._options.serverFactory = (handler) => {
@@ -94,6 +97,11 @@ class Oweb extends _FastifyInstance {
94
97
  done();
95
98
  });
96
99
  }
100
+ if (this._options.autoPreflight && !this._options.uWebSocketsEnabled) {
101
+ fastify.options("/*", (_req, res) => {
102
+ return res.status(204).send();
103
+ });
104
+ }
97
105
  const internalKV = this._internalKV;
98
106
  fastify.addHook("onClose", async () => {
99
107
  const watchers = internalKV.get(HMR_WATCHERS_KEY);
@@ -12,8 +12,8 @@ import { readdirSync } from "node:fs";
12
12
  import { WebSocketRoute } from '../structures/WebSocketRoute.js';
13
13
  import { FastifyWebSocketAdapter } from '../structures/FastifyWebSocketAdapter.js';
14
14
  import { formatSSE } from './utils.js';
15
- const websocketRoutes = {};
16
15
  const WS_REGISTRY_KEY = "ws:registered-routes";
16
+ const runtimeStates = /* @__PURE__ */ new WeakMap();
17
17
  const createMethodMap = /* @__PURE__ */ __name(() => ({
18
18
  get: {},
19
19
  post: {},
@@ -22,11 +22,26 @@ const createMethodMap = /* @__PURE__ */ __name(() => ({
22
22
  patch: {},
23
23
  options: {}
24
24
  }), "createMethodMap");
25
- let matcherOverrides = {};
26
- let routeFunctions = createMethodMap();
27
- let temporaryRequests = createMethodMap();
28
- let routesCache = [];
29
- let compiledRoutes = {};
25
+ function createRuntimeState() {
26
+ return {
27
+ matcherOverrides: {},
28
+ routeFunctions: createMethodMap(),
29
+ temporaryRequests: createMethodMap(),
30
+ routesCache: [],
31
+ compiledRoutes: {},
32
+ websocketRoutes: {}
33
+ };
34
+ }
35
+ __name(createRuntimeState, "createRuntimeState");
36
+ function getRuntimeState(oweb) {
37
+ let state = runtimeStates.get(oweb);
38
+ if (!state) {
39
+ state = createRuntimeState();
40
+ runtimeStates.set(oweb, state);
41
+ }
42
+ return state;
43
+ }
44
+ __name(getRuntimeState, "getRuntimeState");
30
45
  function normalizeFsPath(filePath) {
31
46
  return path.resolve(filePath).replaceAll("\\", "/").toLowerCase();
32
47
  }
@@ -47,14 +62,13 @@ async function importFreshModule(filePath, source) {
47
62
  }
48
63
  __name(importFreshModule, "importFreshModule");
49
64
  function resetRuntimeCaches(oweb) {
50
- matcherOverrides = {};
51
- routeFunctions = createMethodMap();
52
- temporaryRequests = createMethodMap();
53
- routesCache = [];
54
- compiledRoutes = {};
55
- for (const key of Object.keys(websocketRoutes)) {
56
- delete websocketRoutes[key];
57
- }
65
+ const state = getRuntimeState(oweb);
66
+ state.matcherOverrides = {};
67
+ state.routeFunctions = createMethodMap();
68
+ state.temporaryRequests = createMethodMap();
69
+ state.routesCache = [];
70
+ state.compiledRoutes = {};
71
+ state.websocketRoutes = {};
58
72
  oweb._internalKV.delete(WS_REGISTRY_KEY);
59
73
  }
60
74
  __name(resetRuntimeCaches, "resetRuntimeCaches");
@@ -66,8 +80,8 @@ function removeExtension(filePath) {
66
80
  return filePath;
67
81
  }
68
82
  __name(removeExtension, "removeExtension");
69
- function createWebSocketProxy(url) {
70
- const getHandler = /* @__PURE__ */ __name(() => websocketRoutes[url], "getHandler");
83
+ function createWebSocketProxy(oweb, url) {
84
+ const getHandler = /* @__PURE__ */ __name(() => getRuntimeState(oweb).websocketRoutes[url], "getHandler");
71
85
  return {
72
86
  compression: getHandler()?._options.compression,
73
87
  maxPayloadLength: getHandler()?._options.maxPayloadLength,
@@ -101,10 +115,11 @@ function createWebSocketProxy(url) {
101
115
  }
102
116
  __name(createWebSocketProxy, "createWebSocketProxy");
103
117
  const applyMatcherHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallbackDir, filePath, content) => {
118
+ const state = getRuntimeState(oweb);
104
119
  let def;
105
120
  const fileName = path.basename(filePath);
106
121
  if (op === "delete-file") {
107
- delete matcherOverrides[removeExtension(fileName)];
122
+ delete state.matcherOverrides[removeExtension(fileName)];
108
123
  success(`Matcher ${filePath} removed in 0ms`, "HMR");
109
124
  return;
110
125
  }
@@ -122,10 +137,11 @@ const applyMatcherHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fall
122
137
  success(`Matcher ${filePath} reloaded in ${end}ms`, "HMR");
123
138
  }
124
139
  if (def) {
125
- matcherOverrides[removeExtension(fileName)] = def;
140
+ state.matcherOverrides[removeExtension(fileName)] = def;
126
141
  }
127
142
  }, "applyMatcherHMR");
128
143
  const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallbackDir, path2, content) => {
144
+ const state = getRuntimeState(oweb);
129
145
  const normalizedChangedPath = normalizeFsPath(path2);
130
146
  if (path2.endsWith("hooks.js") || path2.endsWith("hooks.ts")) {
131
147
  warn(`Hot Module Replacement is not supported for hooks. Restart the server for changes to take effect.`, "HMR");
@@ -133,15 +149,15 @@ const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallba
133
149
  }
134
150
  if (path2.endsWith(".ts")) {
135
151
  const start = Date.now();
136
- compiledRoutes[path2] = content.length ? await generateFunctionFromTypescript(content, path2) : void 0;
152
+ state.compiledRoutes[path2] = content.length ? await generateFunctionFromTypescript(content, path2) : void 0;
137
153
  const end = Date.now() - start;
138
154
  success(`File ${path2} compiled in ${end}ms`, "HMR");
139
155
  }
140
156
  if (op === "new-file") {
141
157
  const start = Date.now();
142
158
  const files = await walk(workingDir, [], fallbackDir);
143
- const routes = await generateRoutes(files, path2);
144
- routesCache = routes;
159
+ const routes = await generateRoutes(files, path2, state.compiledRoutes);
160
+ state.routesCache = routes;
145
161
  const f = routes.find((x) => normalizeFsPath(x.fileInfo.filePath) === normalizedChangedPath);
146
162
  if (!f) {
147
163
  warn(`HMR could not resolve route metadata for file ${path2}`, "HMR");
@@ -161,18 +177,18 @@ const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallba
161
177
  }
162
178
  const method = f.method.toLowerCase();
163
179
  const nextHandler = inner(oweb, f);
164
- if (routeFunctions[method][f.url]) {
165
- routeFunctions[method][f.url] = nextHandler;
180
+ if (state.routeFunctions[method][f.url]) {
181
+ state.routeFunctions[method][f.url] = nextHandler;
166
182
  } else {
167
- temporaryRequests[method][f.url] = nextHandler;
183
+ state.temporaryRequests[method][f.url] = nextHandler;
168
184
  }
169
185
  const end = Date.now() - start;
170
186
  success(`Route ${f.method.toUpperCase()}:${f.url} created in ${end}ms`, "HMR");
171
187
  } else if (op === "modify-file") {
172
188
  const start = Date.now();
173
189
  const files = await walk(workingDir, [], fallbackDir);
174
- const routes = await generateRoutes(files, path2);
175
- routesCache = routes;
190
+ const routes = await generateRoutes(files, path2, state.compiledRoutes);
191
+ state.routesCache = routes;
176
192
  const f = routes.find((x) => normalizeFsPath(x.fileInfo.filePath) === normalizedChangedPath);
177
193
  if (!f) {
178
194
  warn(`HMR could not resolve route metadata for file ${path2}`, "HMR");
@@ -185,19 +201,19 @@ const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallba
185
201
  }
186
202
  }
187
203
  if (f.fn?.prototype instanceof WebSocketRoute) {
188
- websocketRoutes[f.url] = new f.fn();
204
+ state.websocketRoutes[f.url] = new f.fn();
189
205
  const end2 = Date.now() - start;
190
206
  success(`WebSocket Route ${f.url} reloaded in ${end2}ms`, "HMR");
191
207
  return;
192
208
  }
193
209
  const method = f.method.toLowerCase();
194
210
  const nextHandler = inner(oweb, f);
195
- if (routeFunctions[method][f.url]) {
196
- routeFunctions[method][f.url] = nextHandler;
197
- } else if (f.url in temporaryRequests[method]) {
198
- temporaryRequests[method][f.url] = nextHandler;
211
+ if (state.routeFunctions[method][f.url]) {
212
+ state.routeFunctions[method][f.url] = nextHandler;
213
+ } else if (f.url in state.temporaryRequests[method]) {
214
+ state.temporaryRequests[method][f.url] = nextHandler;
199
215
  } else {
200
- routeFunctions[method][f.url] = nextHandler;
216
+ state.routeFunctions[method][f.url] = nextHandler;
201
217
  }
202
218
  const end = Date.now() - start;
203
219
  success(`Route ${f.method.toUpperCase()}:${f.url} reloaded in ${end}ms`, "HMR");
@@ -208,25 +224,25 @@ const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallba
208
224
  if (builded.url.endsWith("/index")) {
209
225
  builded.url = builded.url.slice(0, -"/index".length);
210
226
  }
211
- if (websocketRoutes[builded.url]) {
212
- delete websocketRoutes[builded.url];
227
+ if (state.websocketRoutes[builded.url]) {
228
+ delete state.websocketRoutes[builded.url];
213
229
  const end = Date.now() - start;
214
230
  success(`WebSocket Route ${builded.url} removed (shimmed) in ${end}ms`, "HMR");
215
231
  return;
216
232
  }
217
- const f = routesCache.find((x) => x.method == builded.method && x.url == builded.url);
233
+ const f = state.routesCache.find((x) => x.method == builded.method && x.url == builded.url);
218
234
  if (f) {
219
- if (f.url in temporaryRequests[f.method.toLowerCase()]) {
220
- delete temporaryRequests[f.method.toLowerCase()][f.url];
235
+ if (f.url in state.temporaryRequests[f.method.toLowerCase()]) {
236
+ delete state.temporaryRequests[f.method.toLowerCase()][f.url];
221
237
  } else {
222
- delete routeFunctions[f.method.toLowerCase()][f.url];
238
+ delete state.routeFunctions[f.method.toLowerCase()][f.url];
223
239
  }
224
240
  const end = Date.now() - start;
225
241
  success(`Route ${f.method.toUpperCase()}:${f.url} removed in ${end}ms`, "HMR");
226
242
  }
227
243
  }
228
244
  }, "applyRouteHMR");
229
- const generateRoutes = /* @__PURE__ */ __name(async (files, onlyGenerateFn) => {
245
+ const generateRoutes = /* @__PURE__ */ __name(async (files, onlyGenerateFn, compiledRoutes = {}) => {
230
246
  const routes = [];
231
247
  for (const file of files) {
232
248
  const parsedFile = path.parse(file.rel);
@@ -272,11 +288,12 @@ function inner(oweb, route) {
272
288
  const handleIsAsync = routeFunc.handle.constructor.name === "AsyncFunction";
273
289
  const hasRouteErrorHandler = typeof routeFunc?.handleError === "function";
274
290
  const hmrEnabled = !!oweb._internalKV.get("hmr");
291
+ const state = getRuntimeState(oweb);
275
292
  const checkMatchers = /* @__PURE__ */ __name((req) => {
276
293
  if (!hasMatchers) return true;
277
294
  for (const matcher of matchers) {
278
295
  const param = req.params[matcher.paramName];
279
- const fun = matcherOverrides[matcher.matcherName];
296
+ const fun = state.matcherOverrides[matcher.matcherName];
280
297
  if (fun) {
281
298
  return fun(param);
282
299
  }
@@ -432,7 +449,7 @@ function inner(oweb, route) {
432
449
  if (hmrEnabled && isParametric) {
433
450
  const currentPath = req.raw.url.split("?")[0];
434
451
  const method = req.method.toLowerCase();
435
- const specificHandler = temporaryRequests[method]?.[currentPath];
452
+ const specificHandler = state.temporaryRequests[method]?.[currentPath];
436
453
  if (specificHandler) {
437
454
  return specificHandler(req, res);
438
455
  }
@@ -467,14 +484,15 @@ function getRegisteredWebSocketsForApp(oweb) {
467
484
  __name(getRegisteredWebSocketsForApp, "getRegisteredWebSocketsForApp");
468
485
  function assignSpecificRoute(oweb, route) {
469
486
  if (!route?.fn) return;
487
+ const state = getRuntimeState(oweb);
470
488
  if (route?.fn?.prototype instanceof WebSocketRoute) {
471
489
  const wsInstance = new route.fn();
472
- websocketRoutes[route.url] = wsInstance;
490
+ state.websocketRoutes[route.url] = wsInstance;
473
491
  const registeredWebSockets = getRegisteredWebSocketsForApp(oweb);
474
492
  if (!registeredWebSockets.has(route.url)) {
475
493
  registeredWebSockets.add(route.url);
476
494
  if (oweb._options.uWebSocketsEnabled && oweb.uServer) {
477
- const proxy = createWebSocketProxy(route.url);
495
+ const proxy = createWebSocketProxy(oweb, route.url);
478
496
  oweb.ws(route.url, proxy, route.fileInfo.hooks);
479
497
  } else {
480
498
  oweb.get(route.url, {
@@ -532,7 +550,7 @@ function assignSpecificRoute(oweb, route) {
532
550
  }
533
551
  return;
534
552
  }
535
- const getHandler = /* @__PURE__ */ __name(() => websocketRoutes[route.url], "getHandler");
553
+ const getHandler = /* @__PURE__ */ __name(() => state.websocketRoutes[route.url], "getHandler");
536
554
  socket.on("message", (message, isBinary) => {
537
555
  const h = getHandler();
538
556
  if (h?.message) h.message(adapter, message, isBinary);
@@ -573,12 +591,12 @@ function assignSpecificRoute(oweb, route) {
573
591
  if (!routeHandler) return;
574
592
  const hmrEnabled = !!oweb._internalKV.get("hmr");
575
593
  if (hmrEnabled) {
576
- routeFunctions[route.method][route.url] = routeHandler;
594
+ state.routeFunctions[route.method][route.url] = routeHandler;
577
595
  oweb[route.method](route.url, routeFunc._options || {}, function(req, res) {
578
- if (routeFunctions[route.method][route.url]) {
579
- return routeFunctions[route.method][route.url](req, res);
596
+ if (state.routeFunctions[route.method][route.url]) {
597
+ return state.routeFunctions[route.method][route.url](req, res);
580
598
  } else {
581
- const vals = temporaryRequests[route.method];
599
+ const vals = state.temporaryRequests[route.method];
582
600
  const keys = Object.keys(vals);
583
601
  if (!vals || !keys.length) {
584
602
  return send404(req, res);
@@ -599,7 +617,7 @@ function assignSpecificRoute(oweb, route) {
599
617
  oweb[route.method](route.url, routeFunc._options || {}, routeHandler);
600
618
  }
601
619
  __name(assignSpecificRoute, "assignSpecificRoute");
602
- async function loadMatchers(directoryPath) {
620
+ async function loadMatchers(directoryPath, state) {
603
621
  const files = readdirSync(directoryPath).filter((f) => [
604
622
  ".js",
605
623
  ".ts"
@@ -609,20 +627,21 @@ async function loadMatchers(directoryPath) {
609
627
  const fileName = path.basename(filePath);
610
628
  const packageURL = pathToFileURL(path.resolve(filePath)).href;
611
629
  const def = await import(packageURL);
612
- matcherOverrides[removeExtension(fileName)] = def.default;
630
+ state.matcherOverrides[removeExtension(fileName)] = def.default;
613
631
  }
614
632
  }
615
633
  __name(loadMatchers, "loadMatchers");
616
634
  const assignRoutes = /* @__PURE__ */ __name(async (oweb, directory, matchersDirectory) => {
617
635
  resetRuntimeCaches(oweb);
636
+ const state = getRuntimeState(oweb);
618
637
  if (matchersDirectory) {
619
- await loadMatchers(matchersDirectory);
638
+ await loadMatchers(matchersDirectory, state);
620
639
  }
621
640
  const files = await walk(directory);
622
- const routes = await generateRoutes(files);
623
- routesCache = routes;
641
+ const routes = await generateRoutes(files, void 0, state.compiledRoutes);
642
+ state.routesCache = routes;
624
643
  function fallbackHandle(req, res) {
625
- const vals = temporaryRequests[req.method.toLowerCase()];
644
+ const vals = state.temporaryRequests[req.method.toLowerCase()];
626
645
  const keys = Object.keys(vals);
627
646
  if (!vals || !keys.length) {
628
647
  return send404(req, res);
@@ -6,7 +6,7 @@ const REQUEST_EVENT = "request";
6
6
  import HttpRequest from './request.js';
7
7
  import HttpResponse from './response.js';
8
8
  import http from "node:http";
9
- async function server_default({ cert_file_name, key_file_name, staticResponseHeaders }) {
9
+ async function server_default({ cert_file_name, key_file_name, staticResponseHeaders, autoPreflight, poweredByHeader }) {
10
10
  let uWS;
11
11
  uWS = (await import("uWebSockets.js")).default;
12
12
  let appType = "App";
@@ -37,6 +37,27 @@ async function server_default({ cert_file_name, key_file_name, staticResponseHea
37
37
  const query = req.getQuery();
38
38
  const url = req.getUrl();
39
39
  const requiresBody = method !== "HEAD" && method !== "GET";
40
+ if (autoPreflight && method === "OPTIONS") {
41
+ res.writeStatus("204 No Content");
42
+ let hasPoweredByHeader = false;
43
+ if (normalizedStaticHeaders?.length) {
44
+ for (let i = 0; i < normalizedStaticHeaders.length; i++) {
45
+ const [key, value] = normalizedStaticHeaders[i];
46
+ if (key === "content-length" || key === "transfer-encoding") {
47
+ continue;
48
+ }
49
+ if (key === "x-powered-by") {
50
+ hasPoweredByHeader = true;
51
+ }
52
+ res.writeHeader(key, value);
53
+ }
54
+ }
55
+ if (poweredByHeader && !hasPoweredByHeader) {
56
+ res.writeHeader("x-powered-by", "Oweb");
57
+ }
58
+ res.end();
59
+ return;
60
+ }
40
61
  res.finished = false;
41
62
  res.aborted = false;
42
63
  if (requiresBody) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "owebjs",
3
- "version": "1.5.8-dev",
3
+ "version": "1.6.0-dev",
4
4
  "description": "A flexible and modern web framework built on top of Fastify",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/0http.js DELETED
@@ -1,8 +0,0 @@
1
- import cero from '0http';
2
- const { router, server } = cero();
3
-
4
- router.get('/', (req, res) => {
5
- res.end('Hello, World!');
6
- });
7
-
8
- server.listen(3000);
package/allahbeler2.png DELETED
Binary file
package/allahbeyler.png DELETED
Binary file
package/avatar.png DELETED
Binary file
package/benchmark.txt DELETED
@@ -1,191 +0,0 @@
1
- OWEB UWS:
2
-
3
- ╰─ autocannon -c 100 -d 40 -p 10 localhost:3000
4
- Running 40s test @ http://localhost:3000
5
- 100 connections with 10 pipelining factor
6
-
7
- running [=================== ] 95%
8
- ┌─────────┬──────┬───────┬───────┬───────┬──────────┬──────────┬────────┐
9
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
10
- ├─────────┼──────┼───────┼───────┼───────┼──────────┼──────────┼────────┤
11
- │ Latency │ 5 ms │ 12 ms │ 73 ms │ 98 ms │ 15.81 ms │ 15.73 ms │ 158 ms │
12
- └─────────┴──────┴───────┴───────┴───────┴──────────┴──────────┴────────┘
13
- ┌───────────┬─────────┬─────────┬─────────┬─────────┬───────────┬──────────┬─────────┐
14
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
15
- ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
16
- │ Req/Sec │ 57.087 │ 57.087 │ 64.991 │ 72.191 │ 64.785,27 │ 3.607,71 │ 57.060 │
17
- ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
18
- │ Bytes/Sec │ 9.42 MB │ 9.42 MB │ 10.7 MB │ 11.9 MB │ 10.7 MB │ 596 kB │ 9.41 MB │
19
- └───────────┴─────────┴─────────┴─────────┴─────────┴───────────┴──────────┴─────────┘
20
-
21
- Req/Bytes counts sampled once per second.
22
- # of samples: 38
23
-
24
- 2463k requests in 40.19s, 406 MB read
25
- ╭─ pwsh      40s 459ms⠀     6,13:14 
26
- ╰─ autocannon -c 100 -d 40 -p 10 localhost:3000
27
- Running 40s test @ http://localhost:3000
28
- 100 connections with 10 pipelining factor
29
-
30
-
31
- ┌─────────┬──────┬───────┬───────┬───────┬─────────┬──────────┬────────┐
32
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
33
- ├─────────┼──────┼───────┼───────┼───────┼─────────┼──────────┼────────┤
34
- │ Latency │ 5 ms │ 13 ms │ 72 ms │ 98 ms │ 15.9 ms │ 15.27 ms │ 262 ms │
35
- └─────────┴──────┴───────┴───────┴───────┴─────────┴──────────┴────────┘
36
- ┌───────────┬─────────┬─────────┬─────────┬─────────┬───────────┬──────────┬─────────┐
37
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
38
- ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
39
- │ Req/Sec │ 48.959 │ 48.959 │ 64.447 │ 70.783 │ 63.949,54 │ 4.776,67 │ 48.930 │
40
- ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
41
- │ Bytes/Sec │ 8.08 MB │ 8.08 MB │ 10.6 MB │ 11.7 MB │ 10.6 MB │ 789 kB │ 8.07 MB │
42
- └───────────┴─────────┴─────────┴─────────┴─────────┴───────────┴──────────┴─────────┘
43
-
44
- Req/Bytes counts sampled once per second.
45
- # of samples: 39
46
-
47
- 2495k requests in 40.96s, 412 MB read
48
-
49
-
50
- OWEB FASTIFY:
51
-
52
- ╰─ autocannon -c 100 -d 40 -p 10 localhost:3000
53
- Running 40s test @ http://localhost:3000
54
- 100 connections with 10 pipelining factor
55
-
56
-
57
- ┌─────────┬──────┬───────┬───────┬────────┬──────────┬──────────┬────────┐
58
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
59
- ├─────────┼──────┼───────┼───────┼────────┼──────────┼──────────┼────────┤
60
- │ Latency │ 3 ms │ 21 ms │ 84 ms │ 110 ms │ 26.12 ms │ 20.65 ms │ 200 ms │
61
- └─────────┴──────┴───────┴───────┴────────┴──────────┴──────────┴────────┘
62
- ┌───────────┬─────────┬─────────┬─────────┬────────┬──────────┬──────────┬─────────┐
63
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
64
- ├───────────┼─────────┼─────────┼─────────┼────────┼──────────┼──────────┼─────────┤
65
- │ Req/Sec │ 26.751 │ 26.751 │ 36.287 │ 45.503 │ 37.570,6 │ 4.606,09 │ 26.741 │
66
- ├───────────┼─────────┼─────────┼─────────┼────────┼──────────┼──────────┼─────────┤
67
- │ Bytes/Sec │ 4.76 MB │ 4.76 MB │ 6.46 MB │ 8.1 MB │ 6.69 MB │ 820 kB │ 4.76 MB │
68
- └───────────┴─────────┴─────────┴─────────┴────────┴──────────┴──────────┴─────────┘
69
-
70
- Req/Bytes counts sampled once per second.
71
- # of samples: 40
72
-
73
- 1504k requests in 40.05s, 268 MB read
74
- ╭─ pwsh      40s 334ms⠀     6,13:16 
75
- ╰─ autocannon -c 100 -d 40 -p 10 localhost:3000
76
- Running 40s test @ http://localhost:3000
77
- 100 connections with 10 pipelining factor
78
-
79
-
80
- ┌─────────┬──────┬───────┬───────┬────────┬──────────┬──────────┬────────┐
81
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
82
- ├─────────┼──────┼───────┼───────┼────────┼──────────┼──────────┼────────┤
83
- │ Latency │ 4 ms │ 20 ms │ 93 ms │ 122 ms │ 24.03 ms │ 21.64 ms │ 209 ms │
84
- └─────────┴──────┴───────┴───────┴────────┴──────────┴──────────┴────────┘
85
- ┌───────────┬────────┬────────┬─────────┬─────────┬───────────┬──────────┬────────┐
86
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
87
- ├───────────┼────────┼────────┼─────────┼─────────┼───────────┼──────────┼────────┤
88
- │ Req/Sec │ 29.759 │ 29.759 │ 41.343 │ 44.895 │ 40.765,81 │ 3.004,68 │ 29.750 │
89
- ├───────────┼────────┼────────┼─────────┼─────────┼───────────┼──────────┼────────┤
90
- │ Bytes/Sec │ 5.3 MB │ 5.3 MB │ 7.36 MB │ 7.99 MB │ 7.26 MB │ 535 kB │ 5.3 MB │
91
- └───────────┴────────┴────────┴─────────┴─────────┴───────────┴──────────┴────────┘
92
-
93
- Req/Bytes counts sampled once per second.
94
- # of samples: 40
95
-
96
- 1632k requests in 40.05s, 290 MB read
97
-
98
- DÜZ FASTIFY:
99
-
100
- Running 40s test @ http://localhost:3000
101
- 100 connections with 10 pipelining factor
102
-
103
-
104
- ┌─────────┬──────┬───────┬───────┬───────┬──────────┬──────────┬────────┐
105
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
106
- ├─────────┼──────┼───────┼───────┼───────┼──────────┼──────────┼────────┤
107
- │ Latency │ 3 ms │ 18 ms │ 76 ms │ 97 ms │ 21.09 ms │ 17.82 ms │ 257 ms │
108
- └─────────┴──────┴───────┴───────┴───────┴──────────┴──────────┴────────┘
109
- ┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬──────────┬─────────┐
110
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
111
- ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
112
- │ Req/Sec │ 32.543 │ 32.543 │ 46.783 │ 49.695 │ 46.349,8 │ 2.964,89 │ 32.530 │
113
- ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
114
- │ Bytes/Sec │ 5.79 MB │ 5.79 MB │ 8.33 MB │ 8.85 MB │ 8.25 MB │ 528 kB │ 5.79 MB │
115
- └───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴──────────┴─────────┘
116
-
117
- Req/Bytes counts sampled once per second.
118
- # of samples: 40
119
-
120
- 1855k requests in 40.06s, 330 MB read
121
- ╭─ pwsh      40s 362ms⠀     6,13:18 
122
- ╰─ autocannon -c 100 -d 40 -p 10 localhost:3000
123
- Running 40s test @ http://localhost:3000
124
- 100 connections with 10 pipelining factor
125
-
126
-
127
- ┌─────────┬──────┬───────┬───────┬────────┬──────────┬─────────┬────────┐
128
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
129
- ├─────────┼──────┼───────┼───────┼────────┼──────────┼─────────┼────────┤
130
- │ Latency │ 3 ms │ 18 ms │ 80 ms │ 102 ms │ 21.48 ms │ 18.3 ms │ 188 ms │
131
- └─────────┴──────┴───────┴───────┴────────┴──────────┴─────────┴────────┘
132
- ┌───────────┬─────────┬─────────┬─────────┬────────┬──────────┬──────────┬─────────┐
133
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
134
- ├───────────┼─────────┼─────────┼─────────┼────────┼──────────┼──────────┼─────────┤
135
- │ Req/Sec │ 37.567 │ 37.567 │ 45.535 │ 48.863 │ 45.501,6 │ 2.214,24 │ 37.550 │
136
- ├───────────┼─────────┼─────────┼─────────┼────────┼──────────┼──────────┼─────────┤
137
- │ Bytes/Sec │ 6.68 MB │ 6.68 MB │ 8.11 MB │ 8.7 MB │ 8.1 MB │ 394 kB │ 6.68 MB │
138
- └───────────┴─────────┴─────────┴─────────┴────────┴──────────┴──────────┴─────────┘
139
-
140
- Req/Bytes counts sampled once per second.
141
- # of samples: 40
142
-
143
- 1821k requests in 40.05s, 324 MB read
144
-
145
- DÜZ UWS:
146
-
147
- Running 40s test @ http://localhost:3000
148
- 100 connections with 10 pipelining factor
149
-
150
- running [=================== ] 95%
151
- ┌─────────┬───────┬───────┬───────┬───────┬──────────┬─────────┬────────┐
152
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
153
- ├─────────┼───────┼───────┼───────┼───────┼──────────┼─────────┼────────┤
154
- │ Latency │ 11 ms │ 12 ms │ 19 ms │ 22 ms │ 12.93 ms │ 3.39 ms │ 167 ms │
155
- └─────────┴───────┴───────┴───────┴───────┴──────────┴─────────┴────────┘
156
- ┌───────────┬─────────┬─────────┬─────────┬─────────┬───────────┬─────────┬─────────┐
157
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
158
- ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼─────────┼─────────┤
159
- │ Req/Sec │ 71.743 │ 71.743 │ 80.831 │ 80.959 │ 79.149,48 │ 3.427,1 │ 71.730 │
160
- ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼─────────┼─────────┤
161
- │ Bytes/Sec │ 10.6 MB │ 10.6 MB │ 11.9 MB │ 11.9 MB │ 11.6 MB │ 503 kB │ 10.5 MB │
162
- └───────────┴─────────┴─────────┴─────────┴─────────┴───────────┴─────────┴─────────┘
163
-
164
- Req/Bytes counts sampled once per second.
165
- # of samples: 38
166
-
167
- 3009k requests in 40.43s, 442 MB read
168
- ╭─ pwsh      40s 739ms⠀     6,13:20 
169
- ╰─ autocannon -c 100 -d 40 -p 10 localhost:3000
170
- Running 40s test @ http://localhost:3000
171
- 100 connections with 10 pipelining factor
172
-
173
-
174
- ┌─────────┬───────┬───────┬───────┬───────┬──────────┬─────────┬────────┐
175
- │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
176
- ├─────────┼───────┼───────┼───────┼───────┼──────────┼─────────┼────────┤
177
- │ Latency │ 10 ms │ 12 ms │ 20 ms │ 22 ms │ 12.44 ms │ 3.35 ms │ 181 ms │
178
- └─────────┴───────┴───────┴───────┴───────┴──────────┴─────────┴────────┘
179
- ┌───────────┬────────┬────────┬─────────┬─────────┬───────────┬──────────┬────────┐
180
- │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
181
- ├───────────┼────────┼────────┼─────────┼─────────┼───────────┼──────────┼────────┤
182
- │ Req/Sec │ 68.287 │ 68.287 │ 80.831 │ 89.855 │ 81.377,65 │ 4.922,97 │ 68.284 │
183
- ├───────────┼────────┼────────┼─────────┼─────────┼───────────┼──────────┼────────┤
184
- │ Bytes/Sec │ 10 MB │ 10 MB │ 11.9 MB │ 13.2 MB │ 12 MB │ 724 kB │ 10 MB │
185
- └───────────┴────────┴────────┴─────────┴─────────┴───────────┴──────────┴────────┘
186
-
187
- Req/Bytes counts sampled once per second.
188
- # of samples: 39
189
-
190
- 3175k requests in 41.09s, 467 MB read
191
-
package/express.js DELETED
@@ -1,14 +0,0 @@
1
- import express from 'express';
2
-
3
- const app = express();
4
- const port = 3000;
5
-
6
- // Define a route for the root URL
7
- app.get('/', (req, res) => {
8
- res.send('Hello World from Express!');
9
- });
10
-
11
- // Start the server
12
- app.listen(port, () => {
13
- console.log(`Express app listening at http://localhost:${port}`);
14
- });
package/fasti.js DELETED
@@ -1,14 +0,0 @@
1
- import fastify from 'fastify';
2
-
3
- const app = fastify();
4
-
5
- app.get('/', async (request, reply) => {
6
- return 'Hello, World!';
7
- });
8
-
9
- app.listen({ port: 3000, host: '0.0.0.0' }, (err) => {
10
- if (err) {
11
- console.error(err);
12
- process.exit(1);
13
- }
14
- });
package/purehttp.js DELETED
@@ -1,19 +0,0 @@
1
- import http from 'node:http';
2
-
3
- const hostname = '127.0.0.1';
4
- const port = 3000;
5
-
6
- // Create the server instance
7
- const server = http.createServer((req, res) => {
8
- // Set the response header with HTTP status and Content-Type
9
- res.statusCode = 200;
10
- res.setHeader('Content-Type', 'text/plain');
11
-
12
- // Send the response body
13
- res.end('Hello, World!\n');
14
- });
15
-
16
- // Start listening for requests
17
- server.listen(port, hostname, () => {
18
- console.log(`Server running at http://${hostname}:${port}/`);
19
- });
package/uws.js DELETED
@@ -1,16 +0,0 @@
1
- import uWS from 'uwebsockets.js';
2
-
3
- uWS.App()
4
- .get('/', (res, req) => {
5
- res.writeStatus('200 OK')
6
- .writeHeader('Content-Type', 'text/plain; charset=utf-8')
7
- .end('Hello, World!');
8
- })
9
- .listen('0.0.0.0', 3000, (listenSocket) => {
10
- if (listenSocket) {
11
- console.log('uWebSockets.js listening on http://localhost:3000');
12
- } else {
13
- console.error('Failed to listen on port 3000');
14
- process.exit(1);
15
- }
16
- });