hoa 0.3.3 → 0.3.5

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.
@@ -1,546 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
- var request_exports = {};
19
- __export(request_exports, {
20
- default: () => HoaRequest
21
- });
22
- module.exports = __toCommonJS(request_exports);
23
- var import_utils = require("./lib/utils.js");
24
- class HoaRequest {
25
- /**
26
- * Parsed URL object of the incoming request.
27
- * This property provides access to all URL components and is lazily initialized.
28
- *
29
- * @returns {URL} The parsed URL object with all URL components
30
- * @public
31
- */
32
- get url() {
33
- return this._url || (this._url = new URL(this.ctx.request.url));
34
- }
35
- /**
36
- * Overwrite the request URL using a string or URL instance.
37
- * This allows for URL manipulation and rewriting within middleware.
38
- *
39
- * @param {string|URL} val - The new URL as a string or URL object
40
- * @public
41
- */
42
- set url(val) {
43
- if (typeof val === "string") {
44
- val = new URL(val);
45
- }
46
- this._url = val;
47
- }
48
- /**
49
- * Full request URL string including protocol, host, path, query, and hash.
50
- *
51
- * @returns {string} The complete URL as a string
52
- * @public
53
- */
54
- get href() {
55
- return this.url.href;
56
- }
57
- /**
58
- * Replace the full href (origin + path + search + hash).
59
- * This is a convenience method for completely changing the URL.
60
- *
61
- * @param {string} val - The new complete URL string
62
- * @public
63
- */
64
- set href(val) {
65
- this.url.href = val;
66
- }
67
- /**
68
- * Origin portion of the URL (scheme + host + port).
69
- *
70
- * @returns {string} The origin (e.g., 'https://example.com:8443')
71
- * @public
72
- */
73
- get origin() {
74
- return this.url.origin;
75
- }
76
- /**
77
- * Replace origin while keeping path, search, and hash components.
78
- * Useful for proxying requests to different hosts.
79
- *
80
- * @param {string} val - The new origin (protocol + host + port)
81
- * @public
82
- */
83
- set origin(val) {
84
- const { pathname, search, hash } = this.url;
85
- this.url = `${val}${pathname}${search}${hash}`;
86
- }
87
- /**
88
- * URL protocol including the trailing colon (e.g., 'https:').
89
- *
90
- * @returns {string} The protocol with colon
91
- * @public
92
- */
93
- get protocol() {
94
- return this.url.protocol;
95
- }
96
- /**
97
- * Set the URL protocol.
98
- *
99
- * @param {string} val - The new protocol (should include colon)
100
- * @public
101
- */
102
- set protocol(val) {
103
- this.url.protocol = val;
104
- }
105
- /**
106
- * Host with port (if present), e.g., 'example.com:8443'.
107
- *
108
- * @returns {string} The host including port if non-standard
109
- * @public
110
- */
111
- get host() {
112
- return this.url.host;
113
- }
114
- /**
115
- * Set host (may include port).
116
- *
117
- * @param {string} val - The new host, optionally with port
118
- * @public
119
- */
120
- set host(val) {
121
- this.url.host = val;
122
- }
123
- /**
124
- * Hostname without port, e.g., 'example.com'.
125
- *
126
- * @returns {string} The hostname without port
127
- * @public
128
- */
129
- get hostname() {
130
- const hostname = this.url.hostname;
131
- if (hostname && hostname.startsWith("[") && hostname.endsWith("]")) {
132
- return hostname.slice(1, -1);
133
- }
134
- return hostname;
135
- }
136
- /**
137
- * Set hostname (without port).
138
- *
139
- * @param {string} val - The new hostname
140
- * @public
141
- */
142
- set hostname(val) {
143
- this.url.hostname = val;
144
- }
145
- /**
146
- * Port string, e.g., '443'. Empty string if using default port.
147
- *
148
- * @returns {string} The port number as a string, or empty string for default ports
149
- * @public
150
- */
151
- get port() {
152
- return this.url.port;
153
- }
154
- /**
155
- * Set port string.
156
- *
157
- * @param {string} val - The new port number as a string
158
- * @public
159
- */
160
- set port(val) {
161
- this.url.port = val;
162
- }
163
- /**
164
- * Pathname starting with '/', e.g., '/users/1'.
165
- *
166
- * @returns {string} The URL pathname
167
- * @public
168
- */
169
- get pathname() {
170
- return this.url.pathname;
171
- }
172
- /**
173
- * Set pathname.
174
- *
175
- * @param {string} val - The new pathname (should start with '/')
176
- * @public
177
- */
178
- set pathname(val) {
179
- this.url.pathname = val;
180
- }
181
- /**
182
- * Raw search string including '?', or empty string if no query parameters.
183
- *
184
- * @returns {string} The search string with leading '?' or empty string
185
- * @public
186
- */
187
- get search() {
188
- return this.url.search;
189
- }
190
- /**
191
- * Set raw search string (should begin with '?').
192
- * This invalidates the cached query object.
193
- *
194
- * @param {string} val - The new search string
195
- * @public
196
- */
197
- set search(val) {
198
- this.url.search = val.startsWith("?") ? val : val ? `?${val}` : "";
199
- this._query = null;
200
- }
201
- /**
202
- * Hash fragment including '#', or empty string if no hash.
203
- *
204
- * @returns {string} The hash fragment with leading '#' or empty string
205
- * @public
206
- */
207
- get hash() {
208
- return this.url.hash;
209
- }
210
- /**
211
- * Set hash fragment including '#'.
212
- *
213
- * @param {string} val - The new hash fragment
214
- * @public
215
- */
216
- set hash(val) {
217
- this.url.hash = val || "";
218
- }
219
- /**
220
- * HTTP method (typically upper-case in most runtimes).
221
- *
222
- * @returns {string} The HTTP method (GET, POST, PUT, DELETE, etc.)
223
- * @public
224
- */
225
- get method() {
226
- return this._method || (this._method = this.ctx.request.method);
227
- }
228
- /**
229
- * Override request method (useful for method override middlewares).
230
- * This allows changing the perceived HTTP method for routing purposes.
231
- *
232
- * @param {string} val - The new HTTP method
233
- * @public
234
- */
235
- set method(val) {
236
- this._method = val;
237
- }
238
- /**
239
- * Parsed query object where duplicate keys become arrays.
240
- * This provides a convenient way to access URL query parameters.
241
- *
242
- * @returns {Record<string, string|string[]>} Object with query parameters
243
- * @public
244
- */
245
- get query() {
246
- return this._query || (this._query = (0, import_utils.parseSearchParamsToQuery)(this.url.searchParams));
247
- }
248
- /**
249
- * Replace query parameters with a plain object.
250
- * Arrays are serialized with repeated keys.
251
- *
252
- * @param {Record<string, string|string[]>} val - New query parameters
253
- * @public
254
- */
255
- set query(val) {
256
- this.search = (0, import_utils.stringifyQueryToString)(val);
257
- this._query = null;
258
- }
259
- /**
260
- * Expose a plain object snapshot of request headers.
261
- * Uses the Web Standard Headers API internally for proper header handling.
262
- * Returns a plain object representation where header names are normalized.
263
- *
264
- * @returns {Record<string, string>} Object with all request headers
265
- * @public
266
- */
267
- get headers() {
268
- this._headers = this._headers || new Headers(this.ctx.request.headers);
269
- return Object.fromEntries(this._headers.entries());
270
- }
271
- /**
272
- * Set the request headers.
273
- * Accepts either a Headers object or a plain object/array of header entries.
274
- * This replaces all existing headers with the new ones.
275
- *
276
- * @param {Headers|Record<string, string>|Array<[string, string]>} val - Headers to set
277
- * @public
278
- */
279
- set headers(val) {
280
- if (val instanceof Headers) {
281
- this._headers = val;
282
- } else {
283
- this._headers = new Headers(val);
284
- }
285
- }
286
- /**
287
- * Get the request body stream.
288
- * Returns the underlying ReadableStream from the Web Standard Request.
289
- * This provides direct access to the request body for custom processing.
290
- *
291
- * @returns {ReadableStream|null} The request body stream, or null if not available
292
- * @public
293
- */
294
- get body() {
295
- return this._body || (this._body = this.ctx.request.body);
296
- }
297
- /**
298
- * Set a custom request body stream.
299
- * This allows middleware to replace the request body with a custom stream.
300
- *
301
- * @param {any} val - The new request body
302
- * @public
303
- */
304
- set body(val) {
305
- this._body = val;
306
- }
307
- /**
308
- * Get a request header value by name (case-insensitive).
309
- * Uses the Web Standard Headers API for proper header retrieval.
310
- * Special-cases "referer/referrer" for compatibility.
311
- *
312
- * @param {string} field - The header name
313
- * @returns {string|null} The header value or null if not found
314
- * @public
315
- */
316
- get(field) {
317
- if (!field) return null;
318
- this._headers = this._headers || new Headers(this.ctx.request.headers);
319
- switch (field = field.toLowerCase()) {
320
- case "referer":
321
- case "referrer":
322
- return this._headers.get("referrer") ?? this._headers.get("referer");
323
- default:
324
- return this._headers.get(field);
325
- }
326
- }
327
- /**
328
- * Get all Set-Cookie header values as an array.
329
- * Returns all Set-Cookie headers from the request (useful for proxying).
330
- *
331
- * @returns {string[]} Array of Set-Cookie header values
332
- * @public
333
- */
334
- getSetCookie() {
335
- this._headers = this._headers || new Headers(this.ctx.request.headers);
336
- return this._headers.getSetCookie();
337
- }
338
- /**
339
- * Check if a request header is present.
340
- * Uses the Web Standard Headers API for case-insensitive header checking.
341
- *
342
- * @param {string} field - The header name to check
343
- * @returns {boolean} True if the header exists
344
- * @public
345
- */
346
- has(field) {
347
- if (!field) return false;
348
- this._headers = this._headers || new Headers(this.ctx.request.headers);
349
- return this._headers.has(field);
350
- }
351
- /**
352
- * Set request header(s) using the Web Standard Headers API.
353
- * Accepts various input formats:
354
- * - Single key/value pair
355
- * - Plain object with multiple headers
356
- * Replaces existing header values.
357
- *
358
- * @param {string|Record<string,string>} field - Header name or headers object
359
- * @param {string} [val] - Header value when field is a string
360
- * @public
361
- */
362
- set(field, val) {
363
- if (!field) return;
364
- this._headers = this._headers || new Headers(this.ctx.request.headers);
365
- if (typeof field === "string") {
366
- this._headers.set(field, val);
367
- } else {
368
- Object.keys(field).forEach((header) => this._headers.set(header, field[header]));
369
- }
370
- }
371
- /**
372
- * Append a request header value using the Web Standard Headers API.
373
- * Does not replace existing values, but appends to them.
374
- * This is useful for headers that can have multiple values.
375
- *
376
- * @param {string|Record<string,string>} field - The header name or headers object
377
- * @param {string} [val] - The value to append when field is a string
378
- * @public
379
- */
380
- append(field, val) {
381
- if (!field) return;
382
- this._headers = this._headers || new Headers(this.ctx.request.headers);
383
- if (typeof field === "string") {
384
- this._headers.append(field, val);
385
- } else {
386
- Object.keys(field).forEach((header) => this._headers.append(header, field[header]));
387
- }
388
- }
389
- /**
390
- * Delete a request header by name using the Web Standard Headers API.
391
- * Header deletion is case-insensitive.
392
- *
393
- * @param {string} field - The header name to delete
394
- * @public
395
- */
396
- delete(field) {
397
- if (!field) return;
398
- this._headers = this._headers || new Headers(this.ctx.request.headers);
399
- this._headers.delete(field);
400
- }
401
- /**
402
- * Get the client IP addresses from the X-Forwarded-For header.
403
- * Returns an array of IP addresses in order from client to proxy.
404
- * If X-Forwarded-For is not available, returns array with single IP from get ip().
405
- *
406
- * @returns {string[]} Array of IP addresses, or array with single IP if X-Forwarded-For not found
407
- * @public
408
- */
409
- get ips() {
410
- const xForwardedFor = this.get("x-forwarded-for");
411
- if (!xForwardedFor) {
412
- const singleIp = this.ip;
413
- return singleIp ? [singleIp] : [];
414
- }
415
- return xForwardedFor.split(",").map((ip) => ip.trim()).filter((ip) => ip);
416
- }
417
- /**
418
- * Get the client IP address by checking various headers in order of precedence.
419
- * Based on the request-ip library implementation logic.
420
- *
421
- * @returns {string} The client IP address, or empty string if none found
422
- * @public
423
- */
424
- get ip() {
425
- const headers = [
426
- "x-client-ip",
427
- "x-forwarded-for",
428
- "cf-connecting-ip",
429
- "do-connecting-ip",
430
- "fastly-client-ip",
431
- "true-client-ip",
432
- "x-real-ip",
433
- "x-cluster-client-ip",
434
- "x-forwarded",
435
- "forwarded-for",
436
- "forwarded",
437
- "x-appengine-user-ip",
438
- "cf-pseudo-ipv4"
439
- ];
440
- for (const header of headers) {
441
- const value = this.get(header);
442
- if (value) {
443
- if (header === "x-forwarded-for" || header === "forwarded-for") {
444
- const firstIp = value.split(",")[0]?.trim();
445
- if (firstIp) {
446
- return firstIp;
447
- }
448
- } else {
449
- return value;
450
- }
451
- }
452
- }
453
- return "";
454
- }
455
- /**
456
- * Get the request content length from the Content-Length header.
457
- * Returns undefined if the header is missing, empty, or not a valid number.
458
- *
459
- * @returns {number|null} The content length in bytes, or null if not available
460
- * @public
461
- */
462
- get length() {
463
- const len = this.get("Content-Length");
464
- if (len == null) return null;
465
- return ~~len;
466
- }
467
- /**
468
- * Get the request content type from the Content-Type header.
469
- * Returns only the media type without parameters (e.g., 'application/json').
470
- *
471
- * @returns {string|null} The content type, or null if not set
472
- * @public
473
- */
474
- get type() {
475
- const type = this.get("Content-Type");
476
- if (!type) return null;
477
- return type.split(";", 1)[0].trim().toLowerCase();
478
- }
479
- /**
480
- * Read request body as a Blob.
481
- * This method can only be called once per request.
482
- *
483
- * @returns {Promise<Blob>} The request body as a Blob
484
- * @public
485
- */
486
- async blob() {
487
- return this.ctx.request.blob();
488
- }
489
- /**
490
- * Read request body as an ArrayBuffer.
491
- * This method can only be called once per request.
492
- *
493
- * @returns {Promise<ArrayBuffer>} The request body as an ArrayBuffer
494
- * @public
495
- */
496
- async arrayBuffer() {
497
- return this.ctx.request.arrayBuffer();
498
- }
499
- /**
500
- * Read request body as text.
501
- * This reads the Web Standard Request body stream. Platforms typically allow
502
- * reading the underlying stream only once; calling multiple different readers
503
- * (e.g., text() then blob()) is invalid.
504
- *
505
- * @returns {Promise<string>} The request body as a string
506
- * @public
507
- */
508
- async text() {
509
- return this.ctx.request.text();
510
- }
511
- /**
512
- * Read request body as JSON.
513
- * This method can only be called once per request.
514
- *
515
- * @returns {Promise<any>} The parsed JSON data
516
- * @throws {SyntaxError}
517
- * @public
518
- */
519
- async json() {
520
- const text = await this.text();
521
- return JSON.parse(text);
522
- }
523
- /**
524
- * Read request body as FormData.
525
- * This method can only be called once per request.
526
- *
527
- * @returns {Promise<FormData>} The request body as FormData
528
- * @public
529
- */
530
- async formData() {
531
- return this.ctx.request.formData();
532
- }
533
- /**
534
- * Return JSON representation of the request.
535
- *
536
- * @returns {ReqJSON} JSON representation of request
537
- * @public
538
- */
539
- toJSON() {
540
- return {
541
- method: this.method,
542
- url: this.href,
543
- headers: this.headers
544
- };
545
- }
546
- }