owebjs 1.5.5-dev → 1.5.7-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.
@@ -19,20 +19,32 @@ function watchDirectory(dir, ignoreInitial = true, onUpdate) {
19
19
  ".js",
20
20
  ".ts"
21
21
  ];
22
- watcher.on("add", async (filePath) => {
22
+ let operationQueue = Promise.resolve();
23
+ const enqueueUpdate = /* @__PURE__ */ __name((op, filePath) => {
23
24
  if (!supportedExtensions.includes(extname(filePath))) return;
24
- const content = readFileSync(filePath, "utf-8");
25
- onUpdate("new-file", filePath, content);
25
+ operationQueue = operationQueue.then(() => {
26
+ let content = "";
27
+ if (op !== "delete-file") {
28
+ try {
29
+ content = readFileSync(filePath, "utf-8");
30
+ } catch {
31
+ return;
32
+ }
33
+ }
34
+ return onUpdate(op, filePath, content);
35
+ }).catch(() => {
36
+ });
37
+ }, "enqueueUpdate");
38
+ watcher.on("add", (filePath) => {
39
+ enqueueUpdate("new-file", filePath);
26
40
  });
27
- watcher.on("change", async (filePath) => {
28
- if (!supportedExtensions.includes(extname(filePath))) return;
29
- const content = readFileSync(filePath, "utf-8");
30
- onUpdate("modify-file", filePath, content);
41
+ watcher.on("change", (filePath) => {
42
+ enqueueUpdate("modify-file", filePath);
31
43
  });
32
44
  watcher.on("unlink", (filePath) => {
33
- if (!supportedExtensions.includes(extname(filePath))) return;
34
- onUpdate("delete-file", filePath, "");
45
+ enqueueUpdate("delete-file", filePath);
35
46
  });
47
+ return watcher;
36
48
  }
37
49
  __name(watchDirectory, "watchDirectory");
38
50
  export {
@@ -3,6 +3,14 @@ import {
3
3
  } from "../chunk-SHUYVCID.js";
4
4
  import { Readable } from "stream";
5
5
  import { forEach } from './utils/object.js';
6
+ const NOOP_SOCKET = Object.freeze({
7
+ destroy: /* @__PURE__ */ __name(() => {
8
+ }, "destroy"),
9
+ on: /* @__PURE__ */ __name(() => {
10
+ }, "on"),
11
+ removeListener: /* @__PURE__ */ __name(() => {
12
+ }, "removeListener")
13
+ });
6
14
  class HttpRequest extends Readable {
7
15
  static {
8
16
  __name(this, "HttpRequest");
@@ -15,34 +23,55 @@ class HttpRequest extends Readable {
15
23
  statusCode;
16
24
  statusMessage;
17
25
  body;
18
- headers;
19
- socket;
20
26
  // https://nodejs.org/api/http.html#class-httpincomingmessage
21
27
  complete = false;
22
28
  connection;
23
- constructor(uRequest, uResponse) {
29
+ resumeScheduled = false;
30
+ _headers = null;
31
+ _socket = NOOP_SOCKET;
32
+ _socketFactory;
33
+ get socket() {
34
+ if (this._socketFactory && this._socket === NOOP_SOCKET) {
35
+ this._socket = this._socketFactory();
36
+ this.connection = this._socket;
37
+ }
38
+ return this._socket;
39
+ }
40
+ set socket(value) {
41
+ this._socket = value || NOOP_SOCKET;
42
+ this.connection = this._socket;
43
+ this._socketFactory = void 0;
44
+ }
45
+ bindSocketFactory(factory) {
46
+ this._socketFactory = factory;
47
+ this._socket = NOOP_SOCKET;
48
+ this.connection = this._socket;
49
+ }
50
+ get headers() {
51
+ if (!this._headers) {
52
+ const headers = {};
53
+ this.req.forEach((header, value) => {
54
+ headers[header.toLowerCase()] = value;
55
+ });
56
+ this._headers = headers;
57
+ }
58
+ return this._headers;
59
+ }
60
+ set headers(value) {
61
+ this._headers = value;
62
+ }
63
+ constructor(uRequest, uResponse, prefetched) {
24
64
  super({
25
65
  highWaterMark: 64 * 1024
26
66
  });
27
67
  this.uResponse = uResponse;
28
68
  this.req = uRequest;
29
- const q = uRequest.getQuery();
30
- this.url = uRequest.getUrl() + (q ? "?" + q : "");
31
- this.method = uRequest.getMethod().toUpperCase();
69
+ const query = prefetched?.query ?? uRequest.getQuery();
70
+ const url = prefetched?.url ?? uRequest.getUrl();
71
+ this.url = url + (query ? "?" + query : "");
72
+ this.method = prefetched?.method ?? uRequest.getMethod().toUpperCase();
32
73
  this.body = {};
33
- this.headers = {};
34
- this.socket = {
35
- destroy: /* @__PURE__ */ __name(() => {
36
- }, "destroy"),
37
- on: /* @__PURE__ */ __name(() => {
38
- }, "on"),
39
- removeListener: /* @__PURE__ */ __name(() => {
40
- }, "removeListener")
41
- };
42
- this.connection = this.socket;
43
- uRequest.forEach((header, value) => {
44
- this.headers[header.toLowerCase()] = value;
45
- });
74
+ this.connection = this._socket;
46
75
  }
47
76
  getRawHeaders() {
48
77
  const raw = [];
@@ -55,13 +84,17 @@ class HttpRequest extends Readable {
55
84
  return this.req;
56
85
  }
57
86
  _read(_) {
58
- if (this.uResponse) {
59
- setImmediate(() => {
60
- if (this.uResponse && !this.uResponse.aborted) {
61
- this.uResponse.resume();
62
- }
63
- });
64
- }
87
+ const uRes = this.uResponse;
88
+ if (!uRes || uRes.aborted || uRes.finished || !uRes.isPaused) return;
89
+ if (this.resumeScheduled) return;
90
+ this.resumeScheduled = true;
91
+ setImmediate(() => {
92
+ this.resumeScheduled = false;
93
+ const res = this.uResponse;
94
+ if (res && !res.aborted && !res.finished && res.isPaused) {
95
+ res.resume();
96
+ }
97
+ });
65
98
  }
66
99
  }
67
100
  export {
@@ -16,8 +16,8 @@ class HttpResponse extends Writable {
16
16
  statusMessage;
17
17
  __headers;
18
18
  headersSent;
19
- socket;
20
19
  finished;
20
+ _socket = null;
21
21
  constructor(uResponse, uServer) {
22
22
  super();
23
23
  this.res = uResponse;
@@ -26,13 +26,19 @@ class HttpResponse extends Writable {
26
26
  this.statusMessage = null;
27
27
  this.__headers = {};
28
28
  this.headersSent = false;
29
- this.socket = new HttpResponseSocket(uResponse);
30
- this.res.onAborted(() => {
31
- this.finished = this.res.finished = true;
32
- });
29
+ this.finished = false;
30
+ }
31
+ get socket() {
32
+ if (!this._socket) {
33
+ this._socket = new HttpResponseSocket(this.res);
34
+ }
35
+ return this._socket;
36
+ }
37
+ isClosed() {
38
+ return this.finished || this.res.aborted || this.res.finished;
33
39
  }
34
40
  get sent() {
35
- return this.finished;
41
+ return this.isClosed();
36
42
  }
37
43
  setHeader(name, value) {
38
44
  this.__headers[toLowerCase(name)] = value;
@@ -53,7 +59,7 @@ class HttpResponse extends Writable {
53
59
  delete this.__headers[toLowerCase(name)];
54
60
  }
55
61
  _flushHeaders() {
56
- if (this.headersSent) return;
62
+ if (this.headersSent || this.isClosed()) return;
57
63
  const message = this.statusMessage || http.STATUS_CODES[this.statusCode] || "Unknown";
58
64
  this.res.writeStatus(`${this.statusCode} ${message}`);
59
65
  const keys = Object.keys(this.__headers);
@@ -75,14 +81,16 @@ class HttpResponse extends Writable {
75
81
  }
76
82
  //@ts-ignore
77
83
  write(data) {
78
- if (this.finished) return;
84
+ if (this.isClosed()) return;
79
85
  this.res.cork(() => {
80
86
  this._flushHeaders();
81
- this.res.write(data);
87
+ if (!this.isClosed()) {
88
+ this.res.write(data);
89
+ }
82
90
  });
83
91
  }
84
92
  writeHead(statusCode) {
85
- if (this.finished) return;
93
+ if (this.isClosed()) return;
86
94
  this.statusCode = statusCode;
87
95
  let headers;
88
96
  if (arguments.length === 2) {
@@ -99,7 +107,7 @@ class HttpResponse extends Writable {
99
107
  }
100
108
  //@ts-ignore
101
109
  end(data) {
102
- if (this.finished) return;
110
+ if (this.isClosed()) return;
103
111
  this.res.cork(() => {
104
112
  this._flushHeaders();
105
113
  this.finished = true;
@@ -6,11 +6,15 @@ class HttpResponseSocket {
6
6
  __name(this, "HttpResponseSocket");
7
7
  }
8
8
  uResponse;
9
+ _remoteAddress;
9
10
  constructor(uResponse) {
10
11
  this.uResponse = uResponse;
11
12
  }
12
13
  get remoteAddress() {
13
- return Buffer.from(this.uResponse.getRemoteAddressAsText()).toString();
14
+ if (this._remoteAddress === void 0) {
15
+ this._remoteAddress = Buffer.from(this.uResponse.getRemoteAddressAsText()).toString();
16
+ }
17
+ return this._remoteAddress;
14
18
  }
15
19
  destroy() {
16
20
  return this.uResponse.end();
@@ -22,41 +22,56 @@ async function server_default({ cert_file_name, key_file_name }) {
22
22
  cert_file_name,
23
23
  key_file_name
24
24
  };
25
+ const copyArrayBufferToBuffer = /* @__PURE__ */ __name((bytes) => {
26
+ const src = new Uint8Array(bytes);
27
+ const out = Buffer.allocUnsafe(src.byteLength);
28
+ out.set(src);
29
+ return out;
30
+ }, "copyArrayBufferToBuffer");
25
31
  const uServer = uWS[appType](config).any("/*", (res, req) => {
32
+ const method = req.getMethod().toUpperCase();
33
+ const query = req.getQuery();
34
+ const url = req.getUrl();
35
+ const requiresBody = method !== "HEAD" && method !== "GET";
26
36
  res.finished = false;
27
37
  res.aborted = false;
28
- res.isPaused = false;
38
+ if (requiresBody) {
39
+ res.isPaused = false;
40
+ }
29
41
  res.onAborted(() => {
30
42
  res.aborted = true;
31
43
  res.finished = true;
32
44
  });
33
- const reqWrapper = new HttpRequest(req, res);
45
+ const reqWrapper = new HttpRequest(req, res, {
46
+ method,
47
+ query,
48
+ url
49
+ });
34
50
  const resWrapper = new HttpResponse(res, uServer);
35
51
  reqWrapper.res = resWrapper;
36
52
  resWrapper.req = reqWrapper;
37
- reqWrapper.socket = resWrapper.socket;
38
- const originalResume = res.resume;
39
- res.resume = function() {
40
- if (res.isPaused && !res.finished && !res.aborted) {
41
- res.isPaused = false;
42
- originalResume.call(res);
43
- }
44
- };
53
+ reqWrapper.bindSocketFactory(() => resWrapper.socket);
45
54
  handler(reqWrapper, resWrapper);
46
- const method = reqWrapper.method;
47
- if (method !== "HEAD" && method !== "GET" && !resWrapper.finished) {
55
+ if (requiresBody && !resWrapper.finished) {
56
+ const originalResume = res.resume;
57
+ res.resume = function() {
58
+ if (res.isPaused && !res.finished && !res.aborted) {
59
+ res.isPaused = false;
60
+ originalResume.call(res);
61
+ }
62
+ };
48
63
  res.onData((bytes, isLast) => {
49
- if (res.finished || res.aborted) return;
50
- const chunk = Buffer.from(bytes.slice(0));
64
+ if (res.finished || res.aborted || reqWrapper.destroyed) return;
65
+ const chunk = copyArrayBufferToBuffer(bytes);
51
66
  const streamReady = reqWrapper.push(chunk);
52
67
  if (isLast) {
53
68
  reqWrapper.complete = true;
54
69
  reqWrapper.push(null);
55
- } else if (!streamReady) {
56
- if (!res.isPaused) {
57
- res.isPaused = true;
58
- res.pause();
59
- }
70
+ return;
71
+ }
72
+ if (!streamReady && !res.isPaused) {
73
+ res.isPaused = true;
74
+ res.pause();
60
75
  }
61
76
  });
62
77
  } else if (!resWrapper.finished) {
@@ -208,40 +223,52 @@ async function server_default({ cert_file_name, key_file_name }) {
208
223
  return this;
209
224
  }
210
225
  };
211
- try {
212
- for (const HookClass of hooks) {
213
- if (aborted || resWrapper.finished) return;
214
- await new Promise((resolve, reject) => {
215
- try {
216
- const hookInstance = typeof HookClass === "function" ? new HookClass() : HookClass;
217
- hookInstance.handle(reqWrapper, resWrapper, (err) => {
218
- if (err) reject(err);
219
- else resolve(true);
220
- });
221
- } catch (err) {
222
- reject(err);
223
- }
224
- });
226
+ const sendUpgradeError = /* @__PURE__ */ __name((hookError) => {
227
+ if (aborted || resWrapper.finished) return;
228
+ const normalizedError = hookError instanceof Error ? hookError : new Error(String(hookError));
229
+ console.error("WebSocket Hook Error:", normalizedError);
230
+ res.writeStatus("500 Internal Server Error");
231
+ res.end(JSON.stringify({
232
+ error: "Internal Server Error",
233
+ message: normalizedError.message
234
+ }));
235
+ }, "sendUpgradeError");
236
+ const finishUpgrade = /* @__PURE__ */ __name(() => {
237
+ if (aborted || resWrapper.finished) return;
238
+ const reqData = {
239
+ ...reqWrapper,
240
+ query
241
+ };
242
+ res.upgrade({
243
+ req: reqData
244
+ }, secKey, secProtocol, secExtensions, context);
245
+ }, "finishUpgrade");
246
+ let hookIndex = 0;
247
+ const runNextHook = /* @__PURE__ */ __name((hookError) => {
248
+ if (hookError) {
249
+ sendUpgradeError(hookError);
250
+ return;
225
251
  }
226
- } catch (err) {
227
- if (!aborted && !resWrapper.finished) {
228
- console.error("WebSocket Hook Error:", err);
229
- res.writeStatus("500 Internal Server Error");
230
- res.end(JSON.stringify({
231
- error: "Internal Server Error",
232
- message: err.message
233
- }));
252
+ if (aborted || resWrapper.finished) return;
253
+ if (hookIndex >= hooks.length) {
254
+ finishUpgrade();
255
+ return;
234
256
  }
235
- return;
236
- }
237
- if (aborted || resWrapper.finished) return;
238
- const reqData = {
239
- ...reqWrapper,
240
- query
241
- };
242
- res.upgrade({
243
- req: reqData
244
- }, secKey, secProtocol, secExtensions, context);
257
+ const HookClass = hooks[hookIndex++];
258
+ const hookInstance = typeof HookClass === "function" ? new HookClass() : HookClass;
259
+ let doneCalled = false;
260
+ const done = /* @__PURE__ */ __name((doneError) => {
261
+ if (doneCalled) return;
262
+ doneCalled = true;
263
+ runNextHook(doneError);
264
+ }, "done");
265
+ try {
266
+ hookInstance.handle(reqWrapper, resWrapper, done);
267
+ } catch (err) {
268
+ done(err);
269
+ }
270
+ }, "runNextHook");
271
+ runNextHook();
245
272
  }, "upgrade"),
246
273
  open: /* @__PURE__ */ __name((ws) => {
247
274
  if (behaviors.open) {
package/express.js ADDED
@@ -0,0 +1,14 @@
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 ADDED
@@ -0,0 +1,14 @@
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "owebjs",
3
- "version": "1.5.5-dev",
3
+ "version": "1.5.7-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",
@@ -22,8 +22,9 @@
22
22
  "scripts": {
23
23
  "start": "node .",
24
24
  "build": "tsup",
25
- "dev": "tsup && node .",
26
- "test": "tsup && node test/index.js",
25
+ "dev": "tsup && node test/index.js",
26
+ "test": "tsup && vitest run --config test/vitest.config.mts",
27
+ "test:watch": "tsup && vitest --config test/vitest.config.mts",
27
28
  "format": "prettier --write . --ignore-path .gitignore"
28
29
  },
29
30
  "homepage": "https://github.com/owebjs/oweb",
@@ -59,7 +60,8 @@
59
60
  "prettier": "^3.0.3",
60
61
  "tslib": "^2.6.2",
61
62
  "tsup": "^8.5.0",
62
- "typescript": "^5.2.2"
63
+ "typescript": "^5.2.2",
64
+ "vitest": "^3.2.4"
63
65
  },
64
66
  "type": "module",
65
67
  "pnpm": {
package/purehttp.js ADDED
@@ -0,0 +1,19 @@
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 ADDED
@@ -0,0 +1,16 @@
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
+ });