owebjs 1.5.4-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,10 +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;
39
+ }
40
+ get sent() {
41
+ return this.isClosed();
33
42
  }
34
43
  setHeader(name, value) {
35
44
  this.__headers[toLowerCase(name)] = value;
@@ -50,7 +59,7 @@ class HttpResponse extends Writable {
50
59
  delete this.__headers[toLowerCase(name)];
51
60
  }
52
61
  _flushHeaders() {
53
- if (this.headersSent) return;
62
+ if (this.headersSent || this.isClosed()) return;
54
63
  const message = this.statusMessage || http.STATUS_CODES[this.statusCode] || "Unknown";
55
64
  this.res.writeStatus(`${this.statusCode} ${message}`);
56
65
  const keys = Object.keys(this.__headers);
@@ -72,14 +81,16 @@ class HttpResponse extends Writable {
72
81
  }
73
82
  //@ts-ignore
74
83
  write(data) {
75
- if (this.finished) return;
84
+ if (this.isClosed()) return;
76
85
  this.res.cork(() => {
77
86
  this._flushHeaders();
78
- this.res.write(data);
87
+ if (!this.isClosed()) {
88
+ this.res.write(data);
89
+ }
79
90
  });
80
91
  }
81
92
  writeHead(statusCode) {
82
- if (this.finished) return;
93
+ if (this.isClosed()) return;
83
94
  this.statusCode = statusCode;
84
95
  let headers;
85
96
  if (arguments.length === 2) {
@@ -96,7 +107,7 @@ class HttpResponse extends Writable {
96
107
  }
97
108
  //@ts-ignore
98
109
  end(data) {
99
- if (this.finished) return;
110
+ if (this.isClosed()) return;
100
111
  this.res.cork(() => {
101
112
  this._flushHeaders();
102
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) {
@@ -185,6 +200,9 @@ async function server_default({ cert_file_name, key_file_name }) {
185
200
  statusCode: 200,
186
201
  _headers: {},
187
202
  finished: false,
203
+ get sent() {
204
+ return this.finished;
205
+ },
188
206
  header(key, value) {
189
207
  this._headers[key.toLowerCase()] = value;
190
208
  return this;
@@ -205,40 +223,52 @@ async function server_default({ cert_file_name, key_file_name }) {
205
223
  return this;
206
224
  }
207
225
  };
208
- try {
209
- for (const HookClass of hooks) {
210
- if (aborted || resWrapper.finished) return;
211
- await new Promise((resolve, reject) => {
212
- try {
213
- const hookInstance = typeof HookClass === "function" ? new HookClass() : HookClass;
214
- hookInstance.handle(reqWrapper, resWrapper, (err) => {
215
- if (err) reject(err);
216
- else resolve(true);
217
- });
218
- } catch (err) {
219
- reject(err);
220
- }
221
- });
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;
222
251
  }
223
- } catch (err) {
224
- if (!aborted && !resWrapper.finished) {
225
- console.error("WebSocket Hook Error:", err);
226
- res.writeStatus("500 Internal Server Error");
227
- res.end(JSON.stringify({
228
- error: "Internal Server Error",
229
- message: err.message
230
- }));
252
+ if (aborted || resWrapper.finished) return;
253
+ if (hookIndex >= hooks.length) {
254
+ finishUpgrade();
255
+ return;
231
256
  }
232
- return;
233
- }
234
- if (aborted || resWrapper.finished) return;
235
- const reqData = {
236
- ...reqWrapper,
237
- query
238
- };
239
- res.upgrade({
240
- req: reqData
241
- }, 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();
242
272
  }, "upgrade"),
243
273
  open: /* @__PURE__ */ __name((ws) => {
244
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.4-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
+ });