owebjs 1.5.5-dev → 1.5.8-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,9 +16,10 @@ class HttpResponse extends Writable {
16
16
  statusMessage;
17
17
  __headers;
18
18
  headersSent;
19
- socket;
20
19
  finished;
21
- constructor(uResponse, uServer) {
20
+ staticHeaders;
21
+ _socket = null;
22
+ constructor(uResponse, uServer, staticHeaders) {
22
23
  super();
23
24
  this.res = uResponse;
24
25
  this.server = uServer;
@@ -26,13 +27,20 @@ class HttpResponse extends Writable {
26
27
  this.statusMessage = null;
27
28
  this.__headers = {};
28
29
  this.headersSent = false;
29
- this.socket = new HttpResponseSocket(uResponse);
30
- this.res.onAborted(() => {
31
- this.finished = this.res.finished = true;
32
- });
30
+ this.finished = false;
31
+ this.staticHeaders = staticHeaders;
32
+ }
33
+ get socket() {
34
+ if (!this._socket) {
35
+ this._socket = new HttpResponseSocket(this.res);
36
+ }
37
+ return this._socket;
38
+ }
39
+ isClosed() {
40
+ return this.finished || this.res.aborted || this.res.finished;
33
41
  }
34
42
  get sent() {
35
- return this.finished;
43
+ return this.isClosed();
36
44
  }
37
45
  setHeader(name, value) {
38
46
  this.__headers[toLowerCase(name)] = value;
@@ -53,9 +61,21 @@ class HttpResponse extends Writable {
53
61
  delete this.__headers[toLowerCase(name)];
54
62
  }
55
63
  _flushHeaders() {
56
- if (this.headersSent) return;
64
+ if (this.headersSent || this.isClosed()) return;
57
65
  const message = this.statusMessage || http.STATUS_CODES[this.statusCode] || "Unknown";
58
66
  this.res.writeStatus(`${this.statusCode} ${message}`);
67
+ if (this.staticHeaders?.length) {
68
+ for (let i = 0; i < this.staticHeaders.length; i++) {
69
+ const [key, value] = this.staticHeaders[i];
70
+ if (key === "content-length" || key === "transfer-encoding") {
71
+ continue;
72
+ }
73
+ if (this.__headers[key] !== void 0) {
74
+ continue;
75
+ }
76
+ this.res.writeHeader(key, value);
77
+ }
78
+ }
59
79
  const keys = Object.keys(this.__headers);
60
80
  for (let i = 0; i < keys.length; i++) {
61
81
  const key = keys[i];
@@ -75,14 +95,16 @@ class HttpResponse extends Writable {
75
95
  }
76
96
  //@ts-ignore
77
97
  write(data) {
78
- if (this.finished) return;
98
+ if (this.isClosed()) return;
79
99
  this.res.cork(() => {
80
100
  this._flushHeaders();
81
- this.res.write(data);
101
+ if (!this.isClosed()) {
102
+ this.res.write(data);
103
+ }
82
104
  });
83
105
  }
84
106
  writeHead(statusCode) {
85
- if (this.finished) return;
107
+ if (this.isClosed()) return;
86
108
  this.statusCode = statusCode;
87
109
  let headers;
88
110
  if (arguments.length === 2) {
@@ -99,7 +121,7 @@ class HttpResponse extends Writable {
99
121
  }
100
122
  //@ts-ignore
101
123
  end(data) {
102
- if (this.finished) return;
124
+ if (this.isClosed()) return;
103
125
  this.res.cork(() => {
104
126
  this._flushHeaders();
105
127
  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();
@@ -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 }) {
9
+ async function server_default({ cert_file_name, key_file_name, staticResponseHeaders }) {
10
10
  let uWS;
11
11
  uWS = (await import("uWebSockets.js")).default;
12
12
  let appType = "App";
@@ -22,41 +22,60 @@ async function server_default({ cert_file_name, key_file_name }) {
22
22
  cert_file_name,
23
23
  key_file_name
24
24
  };
25
+ const normalizedStaticHeaders = staticResponseHeaders ? Object.entries(staticResponseHeaders).map(([k, v]) => [
26
+ k.toLowerCase(),
27
+ String(v)
28
+ ]) : void 0;
29
+ const copyArrayBufferToBuffer = /* @__PURE__ */ __name((bytes) => {
30
+ const src = new Uint8Array(bytes);
31
+ const out = Buffer.allocUnsafe(src.byteLength);
32
+ out.set(src);
33
+ return out;
34
+ }, "copyArrayBufferToBuffer");
25
35
  const uServer = uWS[appType](config).any("/*", (res, req) => {
36
+ const method = req.getMethod().toUpperCase();
37
+ const query = req.getQuery();
38
+ const url = req.getUrl();
39
+ const requiresBody = method !== "HEAD" && method !== "GET";
26
40
  res.finished = false;
27
41
  res.aborted = false;
28
- res.isPaused = false;
42
+ if (requiresBody) {
43
+ res.isPaused = false;
44
+ }
29
45
  res.onAborted(() => {
30
46
  res.aborted = true;
31
47
  res.finished = true;
32
48
  });
33
- const reqWrapper = new HttpRequest(req, res);
34
- const resWrapper = new HttpResponse(res, uServer);
49
+ const reqWrapper = new HttpRequest(req, res, {
50
+ method,
51
+ query,
52
+ url
53
+ });
54
+ const resWrapper = new HttpResponse(res, uServer, normalizedStaticHeaders);
35
55
  reqWrapper.res = resWrapper;
36
56
  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
- };
57
+ reqWrapper.bindSocketFactory(() => resWrapper.socket);
45
58
  handler(reqWrapper, resWrapper);
46
- const method = reqWrapper.method;
47
- if (method !== "HEAD" && method !== "GET" && !resWrapper.finished) {
59
+ if (requiresBody && !resWrapper.finished) {
60
+ const originalResume = res.resume;
61
+ res.resume = function() {
62
+ if (res.isPaused && !res.finished && !res.aborted) {
63
+ res.isPaused = false;
64
+ originalResume.call(res);
65
+ }
66
+ };
48
67
  res.onData((bytes, isLast) => {
49
- if (res.finished || res.aborted) return;
50
- const chunk = Buffer.from(bytes.slice(0));
68
+ if (res.finished || res.aborted || reqWrapper.destroyed) return;
69
+ const chunk = copyArrayBufferToBuffer(bytes);
51
70
  const streamReady = reqWrapper.push(chunk);
52
71
  if (isLast) {
53
72
  reqWrapper.complete = true;
54
73
  reqWrapper.push(null);
55
- } else if (!streamReady) {
56
- if (!res.isPaused) {
57
- res.isPaused = true;
58
- res.pause();
59
- }
74
+ return;
75
+ }
76
+ if (!streamReady && !res.isPaused) {
77
+ res.isPaused = true;
78
+ res.pause();
60
79
  }
61
80
  });
62
81
  } else if (!resWrapper.finished) {
@@ -208,40 +227,52 @@ async function server_default({ cert_file_name, key_file_name }) {
208
227
  return this;
209
228
  }
210
229
  };
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
- });
230
+ const sendUpgradeError = /* @__PURE__ */ __name((hookError) => {
231
+ if (aborted || resWrapper.finished) return;
232
+ const normalizedError = hookError instanceof Error ? hookError : new Error(String(hookError));
233
+ console.error("WebSocket Hook Error:", normalizedError);
234
+ res.writeStatus("500 Internal Server Error");
235
+ res.end(JSON.stringify({
236
+ error: "Internal Server Error",
237
+ message: normalizedError.message
238
+ }));
239
+ }, "sendUpgradeError");
240
+ const finishUpgrade = /* @__PURE__ */ __name(() => {
241
+ if (aborted || resWrapper.finished) return;
242
+ const reqData = {
243
+ ...reqWrapper,
244
+ query
245
+ };
246
+ res.upgrade({
247
+ req: reqData
248
+ }, secKey, secProtocol, secExtensions, context);
249
+ }, "finishUpgrade");
250
+ let hookIndex = 0;
251
+ const runNextHook = /* @__PURE__ */ __name((hookError) => {
252
+ if (hookError) {
253
+ sendUpgradeError(hookError);
254
+ return;
225
255
  }
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
- }));
256
+ if (aborted || resWrapper.finished) return;
257
+ if (hookIndex >= hooks.length) {
258
+ finishUpgrade();
259
+ return;
234
260
  }
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);
261
+ const HookClass = hooks[hookIndex++];
262
+ const hookInstance = typeof HookClass === "function" ? new HookClass() : HookClass;
263
+ let doneCalled = false;
264
+ const done = /* @__PURE__ */ __name((doneError) => {
265
+ if (doneCalled) return;
266
+ doneCalled = true;
267
+ runNextHook(doneError);
268
+ }, "done");
269
+ try {
270
+ hookInstance.handle(reqWrapper, resWrapper, done);
271
+ } catch (err) {
272
+ done(err);
273
+ }
274
+ }, "runNextHook");
275
+ runNextHook();
245
276
  }, "upgrade"),
246
277
  open: /* @__PURE__ */ __name((ws) => {
247
278
  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.8-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
+ });