tezx 4.0.2 → 4.0.4

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.
@@ -31,7 +31,7 @@ class Context {
31
31
  return this;
32
32
  const _key = key.toLowerCase();
33
33
  const append = options?.append || _key === "set-cookie";
34
- const target = this.res?.headers ?? (this.#headers ??= new Headers());
34
+ const target = this.headers;
35
35
  if (append) {
36
36
  target.append(_key, value);
37
37
  }
@@ -47,10 +47,11 @@ class Context {
47
47
  this.#status = status;
48
48
  return this;
49
49
  }
50
- #newResponse(body, type, init = {}) {
51
- let headers = (0, response_js_1.mergeHeaders)(this.#headers, init.headers);
52
- if (!headers.has("Content-Type"))
50
+ createResponse(body, type, init = {}) {
51
+ const headers = (0, response_js_1.mergeHeaders)(this.#headers, init.headers);
52
+ if (type) {
53
53
  headers.set("Content-Type", type);
54
+ }
54
55
  return new Response(body, {
55
56
  status: init.status ?? this.#status,
56
57
  statusText: init.statusText,
@@ -58,50 +59,16 @@ class Context {
58
59
  });
59
60
  }
60
61
  newResponse(body, init = {}) {
61
- let headers = (0, response_js_1.mergeHeaders)(this.#headers, init.headers);
62
- return new Response(body, {
63
- status: init.status ?? this.#status,
64
- statusText: init.statusText,
65
- headers,
66
- });
62
+ return this.createResponse(body, null, init);
67
63
  }
68
64
  text(content, init) {
69
- return this.#newResponse(content, "text/plain; charset=utf-8", init);
65
+ return this.createResponse(content, "text/plain; charset=utf-8", init);
70
66
  }
71
67
  html(strings, init) {
72
- return this.#newResponse(strings, "text/html; charset=utf-8", init);
68
+ return this.createResponse(strings, "text/html; charset=utf-8", init);
73
69
  }
74
70
  json(json, init) {
75
- return this.#newResponse(JSON.stringify(json), "application/json; charset=utf-8", init);
76
- }
77
- send(body, init) {
78
- let _body;
79
- let type;
80
- if (body === null || body === undefined) {
81
- _body = "";
82
- type = "text/plain";
83
- }
84
- else if (typeof body === "string" || typeof body === "number") {
85
- _body = body;
86
- type = "text/plain";
87
- }
88
- else if (body instanceof Uint8Array || body instanceof ArrayBuffer || (typeof ReadableStream !== "undefined" && body instanceof ReadableStream)) {
89
- _body = body;
90
- type = "application/octet-stream";
91
- }
92
- else if (body instanceof Blob || (typeof File !== "undefined" && body instanceof File)) {
93
- _body = body;
94
- type = body.type || "application/octet-stream";
95
- }
96
- else if (typeof body === "object") {
97
- _body = JSON.stringify(body);
98
- type = "application/json";
99
- }
100
- else {
101
- _body = String(body);
102
- type = "text/plain";
103
- }
104
- return this.#newResponse(_body, type, init);
71
+ return this.createResponse(JSON.stringify(json), "application/json; charset=utf-8", init);
105
72
  }
106
73
  redirect(url, status = 302) {
107
74
  const headers = new Headers(this.#headers);
@@ -122,18 +89,15 @@ class Context {
122
89
  if (init?.filename) {
123
90
  headers["Content-Disposition"] = `attachment; filename="${init?.filename}"`;
124
91
  }
92
+ let contentType = null;
125
93
  if (init?.download || init?.filename) {
126
- headers["Content-Type"] = "application/octet-stream";
127
- return this.newResponse(stream, {
128
- status: init?.status ?? this.#status,
129
- statusText: init?.statusText,
130
- headers,
131
- });
94
+ contentType = "application/octet-stream";
95
+ }
96
+ else {
97
+ const ext = (0, utils_js_1.extensionExtract)(filePath);
98
+ contentType = mimeTypes_js_1.mimeTypes[ext] ?? mimeTypes_js_1.defaultMimeType;
132
99
  }
133
- const ext = (0, utils_js_1.extensionExtract)(filePath);
134
- const mimeType = mimeTypes_js_1.mimeTypes[ext] ?? mimeTypes_js_1.defaultMimeType;
135
- headers["Content-Type"] = mimeType;
136
- return this.newResponse(stream, {
100
+ return this.createResponse(stream, contentType, {
137
101
  status: init?.status ?? this.#status,
138
102
  statusText: init?.statusText,
139
103
  headers,
@@ -8,7 +8,6 @@ class TezXRequest {
8
8
  pathname;
9
9
  #rawRequest;
10
10
  params = {};
11
- remoteAddress = {};
12
11
  #bodyConsumed = false;
13
12
  #rawBodyArrayBuffer;
14
13
  #cachedText;
@@ -7,10 +7,9 @@ const url_js_1 = require("../utils/url.js");
7
7
  const context_js_1 = require("./context.js");
8
8
  const router_js_1 = require("./router.js");
9
9
  class TezX extends router_js_1.Router {
10
- #pathResolver;
11
10
  #notFound = response_js_1.notFoundResponse;
12
11
  #errorHandler = response_js_1.handleErrorResponse;
13
- constructor({ basePath = "/", routeRegistry = new RadixRouter_js_1.RadixRouter(), } = {}) {
12
+ constructor({ basePath = "/", routeRegistry = new RadixRouter_js_1.RadixRouter() } = {}) {
14
13
  super({ basePath });
15
14
  if (!routeRegistry) {
16
15
  throw new Error("routeRegistry is required for TezX initialization");
@@ -30,24 +29,27 @@ class TezX extends router_js_1.Router {
30
29
  let index = -1;
31
30
  const dispatch = (i) => {
32
31
  if (i <= index)
33
- throw new Error("next() called multiple times");
32
+ return Promise.reject(new Error("next() called multiple times"));
34
33
  index = i;
35
34
  const fn = stack[i];
36
35
  if (!fn)
37
36
  return ctx.res;
38
- const result = fn(ctx, () => dispatch(i + 1));
39
- if (!(result instanceof Promise)) {
40
- if (result instanceof Response)
41
- ctx.res = result;
42
- return ctx.res;
37
+ try {
38
+ const result = fn(ctx, () => dispatch(i + 1));
39
+ if (!(result instanceof Promise)) {
40
+ if (result instanceof Response)
41
+ ctx.res = result;
42
+ return result ?? ctx.res;
43
+ }
44
+ return result.then((res) => {
45
+ if (res instanceof Response)
46
+ ctx.res = res;
47
+ return res ?? ctx.res;
48
+ }, (err) => Promise.reject(err));
49
+ }
50
+ catch (err) {
51
+ return Promise.reject(err);
43
52
  }
44
- return result.then((res) => {
45
- if (res instanceof Response)
46
- ctx.res = res;
47
- return ctx.res;
48
- }, (err) => {
49
- throw err;
50
- });
51
53
  };
52
54
  return dispatch(0);
53
55
  }
@@ -66,9 +68,13 @@ class TezX extends router_js_1.Router {
66
68
  if (mLen === 0)
67
69
  return this.#notFound(ctx);
68
70
  ctx.params = params;
69
- if (mLen === 1)
70
- return await middlewares[0](ctx) ?? this.#notFound(ctx);
71
- return await this.#composeChain(ctx, middlewares) ?? this.#notFound(ctx);
71
+ if (mLen === 1) {
72
+ ctx.res = await middlewares[0](ctx);
73
+ }
74
+ else {
75
+ ctx.res = await this.#composeChain(ctx, middlewares);
76
+ }
77
+ return ctx.res ?? this.#notFound(ctx);
72
78
  }
73
79
  catch (err) {
74
80
  let error = err instanceof Error ? err : new Error(String(err));
@@ -76,10 +82,9 @@ class TezX extends router_js_1.Router {
76
82
  }
77
83
  }
78
84
  async serve(req, server) {
79
- const method = req.method || "GET";
85
+ const method = req.method;
80
86
  if (method === "HEAD") {
81
- const headReq = new Request(req, { method: "GET" });
82
- const res = await this.#handleRequest(headReq, "GET", server);
87
+ const res = await this.#handleRequest(req, "GET", server);
83
88
  return new Response(null, {
84
89
  status: res.status,
85
90
  statusText: res.statusText,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateUUID = exports.generateRandomBase64 = exports.generateID = exports.useFormData = void 0;
4
+ exports.getConnInfo = getConnInfo;
4
5
  const formData_js_1 = require("./formData.js");
5
6
  Object.defineProperty(exports, "useFormData", { enumerable: true, get: function () { return formData_js_1.useFormData; } });
6
7
  const generateID_js_1 = require("./generateID.js");
@@ -11,3 +12,6 @@ exports.default = {
11
12
  useFormData: formData_js_1.useFormData,
12
13
  generateID: generateID_js_1.generateID, generateRandomBase64: generateID_js_1.generateRandomBase64, generateUUID: generateID_js_1.generateUUID
13
14
  };
15
+ function getConnInfo(ctx) {
16
+ return ctx?.server?.requestIP?.(ctx.rawRequest);
17
+ }
package/cjs/index.js CHANGED
@@ -5,7 +5,7 @@ const router_js_1 = require("./core/router.js");
5
5
  Object.defineProperty(exports, "Router", { enumerable: true, get: function () { return router_js_1.Router; } });
6
6
  const server_js_1 = require("./core/server.js");
7
7
  Object.defineProperty(exports, "TezX", { enumerable: true, get: function () { return server_js_1.TezX; } });
8
- exports.version = "4.0.2";
8
+ exports.version = "4.0.4";
9
9
  exports.default = {
10
10
  Router: router_js_1.Router,
11
11
  TezX: server_js_1.TezX,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.detectBot = void 0;
4
+ const index_js_1 = require("../helper/index.js");
4
5
  const rateLimit_js_1 = require("../utils/rateLimit.js");
5
6
  const detectBot = (opts = {}) => {
6
7
  const botUAs = opts.botUserAgents || ["bot", "spider", "crawl", "slurp"];
@@ -8,7 +9,7 @@ const detectBot = (opts = {}) => {
8
9
  const botRegex = new RegExp(botUAs.join("|"), "i");
9
10
  checkBot = (ua) => botRegex.test(ua);
10
11
  const keyGenerator = opts.keyGenerator ?? ((ctx) => {
11
- const addr = ctx.req.remoteAddress;
12
+ const addr = (0, index_js_1.getConnInfo)(ctx);
12
13
  return addr ? `${addr.address}:${addr.port}` : "unknown";
13
14
  });
14
15
  const maxReq = opts.maxRequests || 30;
@@ -20,7 +20,6 @@ __exportStar(require("./cache-control.js"), exports);
20
20
  __exportStar(require("./cors.js"), exports);
21
21
  __exportStar(require("./detect-bot.js"), exports);
22
22
  __exportStar(require("./etag.js"), exports);
23
- __exportStar(require("./getConnInfo.js"), exports);
24
23
  __exportStar(require("./i18n.js"), exports);
25
24
  __exportStar(require("./logger.js"), exports);
26
25
  __exportStar(require("./pagination.js"), exports);
@@ -42,8 +42,9 @@ const paginationHandler = (options = {}) => {
42
42
  pagination,
43
43
  };
44
44
  ctx.body = body;
45
- if (next) {
46
- return await next();
45
+ let res = await next();
46
+ if (res) {
47
+ return res;
47
48
  }
48
49
  return ctx.json(body);
49
50
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = exports.rateLimiter = void 0;
3
+ exports.rateLimiter = exports.default = void 0;
4
+ const index_js_1 = require("../helper/index.js");
4
5
  const rateLimit_js_1 = require("../utils/rateLimit.js");
5
6
  const rateLimiter = (options) => {
6
7
  const { maxRequests, windowMs, keyGenerator = (ctx) => {
@@ -12,9 +13,8 @@ const rateLimiter = (options) => {
12
13
  const clientIp = ctx.req.header("client-ip");
13
14
  if (clientIp)
14
15
  return clientIp;
15
- const addr = ctx.req.remoteAddress?.address || "unknown";
16
- const port = ctx.req.remoteAddress?.port || "0";
17
- return `${addr}:${port}`;
16
+ const { port, address } = (0, index_js_1.getConnInfo)(ctx) ?? {};
17
+ return `${address}:${port}`;
18
18
  }, storage = (0, rateLimit_js_1.createRateLimitDefaultStorage)(), onError = (ctx, retryAfter, error) => {
19
19
  ctx.status(429);
20
20
  throw new Error(`Rate limit exceeded. Try again in ${retryAfter} seconds.`);
@@ -33,5 +33,5 @@ const rateLimiter = (options) => {
33
33
  return await next();
34
34
  };
35
35
  };
36
- exports.rateLimiter = rateLimiter;
37
36
  exports.default = rateLimiter;
37
+ exports.rateLimiter = rateLimiter;
@@ -15,6 +15,10 @@ exports.mimeTypes = {
15
15
  tsv: "text/tab-separated-values",
16
16
  rtf: "application/rtf",
17
17
  markdown: "text/markdown",
18
+ jsx: "text/javascript",
19
+ ts: "text/typescript",
20
+ tsx: "text/typescript",
21
+ jsonld: "application/ld+json",
18
22
  png: "image/png",
19
23
  jpg: "image/jpeg",
20
24
  jpeg: "image/jpeg",
@@ -25,6 +29,13 @@ exports.mimeTypes = {
25
29
  bmp: "image/bmp",
26
30
  tiff: "image/tiff",
27
31
  psd: "image/vnd.adobe.photoshop",
32
+ tif: "image/tiff",
33
+ avif: "image/avif",
34
+ woff: "font/woff",
35
+ woff2: "font/woff2",
36
+ ttf: "font/ttf",
37
+ otf: "font/otf",
38
+ eot: "application/vnd.ms-fontobject",
28
39
  mp4: "video/mp4",
29
40
  webm: "video/webm",
30
41
  ogg: "video/ogg",
@@ -33,6 +44,9 @@ exports.mimeTypes = {
33
44
  wmv: "video/x-ms-wmv",
34
45
  flv: "video/x-flv",
35
46
  "3gp": "video/3gpp",
47
+ mkv: "video/x-matroska",
48
+ mpeg: "video/mpeg",
49
+ mpg: "video/mpeg",
36
50
  mp3: "audio/mpeg",
37
51
  wav: "audio/wav",
38
52
  aac: "audio/aac",
@@ -40,11 +54,7 @@ exports.mimeTypes = {
40
54
  m4a: "audio/mp4",
41
55
  mid: "audio/midi",
42
56
  midi: "audio/midi",
43
- woff: "font/woff",
44
- woff2: "font/woff2",
45
- ttf: "font/ttf",
46
- otf: "font/otf",
47
- eot: "application/vnd.ms-fontobject",
57
+ weba: "audio/webm",
48
58
  pdf: "application/pdf",
49
59
  odp: "application/vnd.oasis.opendocument.presentation",
50
60
  zip: "application/zip",
@@ -83,6 +93,8 @@ exports.mimeTypes = {
83
93
  deb: "application/x-debian-package",
84
94
  rpm: "application/x-redhat-package-manager",
85
95
  apk: "application/vnd.android.package-archive",
96
+ bin: "application/octet-stream",
97
+ iso: "application/octet-stream",
86
98
  webmanifest: "application/manifest+json",
87
99
  ics: "text/calendar",
88
100
  vcf: "text/vcard",
package/cookie/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Context } from "../core/context.js";
1
+ import { Context } from "../index.js";
2
2
  import { CookieOptions } from "../types/index.js";
3
3
  /**
4
4
  * Get the value of a specific cookie by name from the request context.
package/core/context.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ExtractParamsFromPath, HttpBaseResponse, ResHeaderKey, ResponseInit, WebSocketEvent } from "../types/index.js";
2
+ import { ContentType } from "../utils/mimeTypes.js";
2
3
  import { TezXRequest } from "./request.js";
3
4
  export declare class Context<TPath extends string = any> {
4
5
  #private;
@@ -112,6 +113,35 @@ export declare class Context<TPath extends string = any> {
112
113
  * ctx.status(404).text("Not found");
113
114
  */
114
115
  status(status: number): this;
116
+ /**
117
+ * Creates a native Response object with optional body, Content-Type, and headers.
118
+ *
119
+ * This function always overwrites the `Content-Type` header if `type` is provided.
120
+ * It merges any existing headers from the context (`this.#headers`) with headers
121
+ * provided in `init.headers`.
122
+ *
123
+ * @param {BodyInit | null} body - The response body. Can be string, Blob, ArrayBuffer, Uint8Array, or null.
124
+ * @param {string | null} type - The MIME type to set in the `Content-Type` header. If null, no Content-Type is set.
125
+ * @param {ResponseInit} [init={}] - Optional response initialization object (status, statusText, headers).
126
+ * @param {number} [init.status] - HTTP status code (default is `this.#status`).
127
+ * @param {string} [init.statusText] - Optional status text for the response.
128
+ * @param {HeadersInit} [init.headers] - Additional headers to merge with context headers.
129
+ *
130
+ * @returns {Response} A native Fetch API Response object.
131
+ *
132
+ * @example
133
+ * // Simple text response
134
+ * const res = createResponse("Hello World", "text/plain");
135
+ *
136
+ * @example
137
+ * // JSON response
138
+ * const res = createResponse(JSON.stringify({ ok: true }), "application/json", { status: 200 });
139
+ *
140
+ * @example
141
+ * // Custom headers
142
+ * const res = createResponse("Hello", "text/plain", { headers: { "X-Custom": "test" } });
143
+ */
144
+ createResponse(body: BodyInit | null, type: ContentType | null, init?: ResponseInit): Response;
115
145
  /**
116
146
  * Protected helper method to create a Response or PlainResponse
117
147
  * based on runtime environment (Node.js or Web).
@@ -157,38 +187,6 @@ export declare class Context<TPath extends string = any> {
157
187
  * ctx.json({ success: true });
158
188
  */
159
189
  json(json: object, init?: ResponseInit): HttpBaseResponse;
160
- /**
161
- * Send a response with automatic content type detection.
162
- *
163
- * This method determines the proper `Content-Type` header based on the type of `body`.
164
- * If a `Content-Type` is provided in `init.headers`, it will be used instead.
165
- *
166
- * Supported types:
167
- * - `string` / `number` → "text/plain"
168
- * - `object` → JSON serialized, "application/json"
169
- * - `Uint8Array` / `ArrayBuffer` / `ReadableStream` → "application/octet-stream"
170
- * - `Blob` / `File` → uses `body.type` or falls back to "application/octet-stream"
171
- * - `null` / `undefined` → empty string, "text/plain"
172
- * - any other → `String(body)`, "text/plain"
173
- *
174
- * @param {any} body - Response body of any type.
175
- * @param {ResponseInit} [init] - Optional response init object, headers, status, etc.
176
- * @returns {HttpBaseResponse} - A Bun-compatible response object.
177
- *
178
- * @example
179
- * // Send a string
180
- * ctx.send("Hello World");
181
- *
182
- * // Send JSON
183
- * ctx.send({ user: "Alice", id: 123 });
184
- *
185
- * // Send binary
186
- * ctx.send(new Uint8Array([1,2,3]));
187
- *
188
- * // Custom content-type
189
- * ctx.send("Hello", { headers: { "Content-Type": "text/html" } });
190
- */
191
- send(body: any, init?: ResponseInit): HttpBaseResponse;
192
190
  /**
193
191
  * Sends an HTTP redirect response to the specified URL.
194
192
  *
package/core/context.js CHANGED
@@ -28,7 +28,7 @@ export class Context {
28
28
  return this;
29
29
  const _key = key.toLowerCase();
30
30
  const append = options?.append || _key === "set-cookie";
31
- const target = this.res?.headers ?? (this.#headers ??= new Headers());
31
+ const target = this.headers;
32
32
  if (append) {
33
33
  target.append(_key, value);
34
34
  }
@@ -44,10 +44,11 @@ export class Context {
44
44
  this.#status = status;
45
45
  return this;
46
46
  }
47
- #newResponse(body, type, init = {}) {
48
- let headers = mergeHeaders(this.#headers, init.headers);
49
- if (!headers.has("Content-Type"))
47
+ createResponse(body, type, init = {}) {
48
+ const headers = mergeHeaders(this.#headers, init.headers);
49
+ if (type) {
50
50
  headers.set("Content-Type", type);
51
+ }
51
52
  return new Response(body, {
52
53
  status: init.status ?? this.#status,
53
54
  statusText: init.statusText,
@@ -55,50 +56,16 @@ export class Context {
55
56
  });
56
57
  }
57
58
  newResponse(body, init = {}) {
58
- let headers = mergeHeaders(this.#headers, init.headers);
59
- return new Response(body, {
60
- status: init.status ?? this.#status,
61
- statusText: init.statusText,
62
- headers,
63
- });
59
+ return this.createResponse(body, null, init);
64
60
  }
65
61
  text(content, init) {
66
- return this.#newResponse(content, "text/plain; charset=utf-8", init);
62
+ return this.createResponse(content, "text/plain; charset=utf-8", init);
67
63
  }
68
64
  html(strings, init) {
69
- return this.#newResponse(strings, "text/html; charset=utf-8", init);
65
+ return this.createResponse(strings, "text/html; charset=utf-8", init);
70
66
  }
71
67
  json(json, init) {
72
- return this.#newResponse(JSON.stringify(json), "application/json; charset=utf-8", init);
73
- }
74
- send(body, init) {
75
- let _body;
76
- let type;
77
- if (body === null || body === undefined) {
78
- _body = "";
79
- type = "text/plain";
80
- }
81
- else if (typeof body === "string" || typeof body === "number") {
82
- _body = body;
83
- type = "text/plain";
84
- }
85
- else if (body instanceof Uint8Array || body instanceof ArrayBuffer || (typeof ReadableStream !== "undefined" && body instanceof ReadableStream)) {
86
- _body = body;
87
- type = "application/octet-stream";
88
- }
89
- else if (body instanceof Blob || (typeof File !== "undefined" && body instanceof File)) {
90
- _body = body;
91
- type = body.type || "application/octet-stream";
92
- }
93
- else if (typeof body === "object") {
94
- _body = JSON.stringify(body);
95
- type = "application/json";
96
- }
97
- else {
98
- _body = String(body);
99
- type = "text/plain";
100
- }
101
- return this.#newResponse(_body, type, init);
68
+ return this.createResponse(JSON.stringify(json), "application/json; charset=utf-8", init);
102
69
  }
103
70
  redirect(url, status = 302) {
104
71
  const headers = new Headers(this.#headers);
@@ -119,18 +86,15 @@ export class Context {
119
86
  if (init?.filename) {
120
87
  headers["Content-Disposition"] = `attachment; filename="${init?.filename}"`;
121
88
  }
89
+ let contentType = null;
122
90
  if (init?.download || init?.filename) {
123
- headers["Content-Type"] = "application/octet-stream";
124
- return this.newResponse(stream, {
125
- status: init?.status ?? this.#status,
126
- statusText: init?.statusText,
127
- headers,
128
- });
91
+ contentType = "application/octet-stream";
92
+ }
93
+ else {
94
+ const ext = extensionExtract(filePath);
95
+ contentType = mimeTypes[ext] ?? defaultMimeType;
129
96
  }
130
- const ext = extensionExtract(filePath);
131
- const mimeType = mimeTypes[ext] ?? defaultMimeType;
132
- headers["Content-Type"] = mimeType;
133
- return this.newResponse(stream, {
97
+ return this.createResponse(stream, contentType, {
134
98
  status: init?.status ?? this.#status,
135
99
  statusText: init?.statusText,
136
100
  headers,
package/core/request.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ExtractParamsFromPath, HTTPMethod, NetAddr, ReqHeaderKey, RequestHeaders } from "../types/index.js";
1
+ import { ExtractParamsFromPath, HTTPMethod, ReqHeaderKey, RequestHeaders } from "../types/index.js";
2
2
  /**
3
3
  * A wrapper around the raw HTTP request that provides convenient access to URL, headers, body parsing, and route parameters.
4
4
  *
@@ -26,18 +26,6 @@ export declare class TezXRequest<Path extends string = any> {
26
26
  * @type {ExtractParamsFromPath<Path>}
27
27
  */
28
28
  readonly params: ExtractParamsFromPath<Path>;
29
- /**
30
- * Remote address details of the connected client.
31
- * @requires injectRemoteAddress middleware.
32
- * @typedef {Object} NetAddr
33
- * @property {string} [transport] - Transport protocol (e.g., "tcp", "udp").
34
- * @property {"IPv4" | "IPv6" | "Unix"} [family] - Address family.
35
- * @property {string} [hostname] - Hostname or IP address.
36
- * @property {number} [port] - Port number.
37
- * @type {NetAddr}
38
- * @default {}
39
- */
40
- remoteAddress: NetAddr;
41
29
  /**
42
30
  * Creates an instance of TezXRequest.
43
31
  *
package/core/request.js CHANGED
@@ -5,7 +5,6 @@ export class TezXRequest {
5
5
  pathname;
6
6
  #rawRequest;
7
7
  params = {};
8
- remoteAddress = {};
9
8
  #bodyConsumed = false;
10
9
  #rawBodyArrayBuffer;
11
10
  #cachedText;
package/core/server.d.ts CHANGED
@@ -25,7 +25,7 @@ export declare class TezX<T extends Record<string, any> = {}> extends Router<T>
25
25
  #private;
26
26
  /** Internal route registry to hold all routes */
27
27
  protected router?: RouteRegistry;
28
- constructor({ basePath, routeRegistry, }?: TezXConfig);
28
+ constructor({ basePath, routeRegistry }?: TezXConfig);
29
29
  /**
30
30
  * Register a custom 404 (not found) handler.
31
31
  *
package/core/server.js CHANGED
@@ -4,10 +4,9 @@ import { getPathname } from "../utils/url.js";
4
4
  import { Context } from "./context.js";
5
5
  import { Router } from "./router.js";
6
6
  export class TezX extends Router {
7
- #pathResolver;
8
7
  #notFound = notFoundResponse;
9
8
  #errorHandler = handleErrorResponse;
10
- constructor({ basePath = "/", routeRegistry = new RadixRouter(), } = {}) {
9
+ constructor({ basePath = "/", routeRegistry = new RadixRouter() } = {}) {
11
10
  super({ basePath });
12
11
  if (!routeRegistry) {
13
12
  throw new Error("routeRegistry is required for TezX initialization");
@@ -27,24 +26,27 @@ export class TezX extends Router {
27
26
  let index = -1;
28
27
  const dispatch = (i) => {
29
28
  if (i <= index)
30
- throw new Error("next() called multiple times");
29
+ return Promise.reject(new Error("next() called multiple times"));
31
30
  index = i;
32
31
  const fn = stack[i];
33
32
  if (!fn)
34
33
  return ctx.res;
35
- const result = fn(ctx, () => dispatch(i + 1));
36
- if (!(result instanceof Promise)) {
37
- if (result instanceof Response)
38
- ctx.res = result;
39
- return ctx.res;
34
+ try {
35
+ const result = fn(ctx, () => dispatch(i + 1));
36
+ if (!(result instanceof Promise)) {
37
+ if (result instanceof Response)
38
+ ctx.res = result;
39
+ return result ?? ctx.res;
40
+ }
41
+ return result.then((res) => {
42
+ if (res instanceof Response)
43
+ ctx.res = res;
44
+ return res ?? ctx.res;
45
+ }, (err) => Promise.reject(err));
46
+ }
47
+ catch (err) {
48
+ return Promise.reject(err);
40
49
  }
41
- return result.then((res) => {
42
- if (res instanceof Response)
43
- ctx.res = res;
44
- return ctx.res;
45
- }, (err) => {
46
- throw err;
47
- });
48
50
  };
49
51
  return dispatch(0);
50
52
  }
@@ -63,9 +65,13 @@ export class TezX extends Router {
63
65
  if (mLen === 0)
64
66
  return this.#notFound(ctx);
65
67
  ctx.params = params;
66
- if (mLen === 1)
67
- return await middlewares[0](ctx) ?? this.#notFound(ctx);
68
- return await this.#composeChain(ctx, middlewares) ?? this.#notFound(ctx);
68
+ if (mLen === 1) {
69
+ ctx.res = await middlewares[0](ctx);
70
+ }
71
+ else {
72
+ ctx.res = await this.#composeChain(ctx, middlewares);
73
+ }
74
+ return ctx.res ?? this.#notFound(ctx);
69
75
  }
70
76
  catch (err) {
71
77
  let error = err instanceof Error ? err : new Error(String(err));
@@ -73,10 +79,9 @@ export class TezX extends Router {
73
79
  }
74
80
  }
75
81
  async serve(req, server) {
76
- const method = req.method || "GET";
82
+ const method = req.method;
77
83
  if (method === "HEAD") {
78
- const headReq = new Request(req, { method: "GET" });
79
- const res = await this.#handleRequest(headReq, "GET", server);
84
+ const res = await this.#handleRequest(req, "GET", server);
80
85
  return new Response(null, {
81
86
  status: res.status,
82
87
  statusText: res.statusText,