@wooksjs/event-http 0.5.25 → 0.6.0

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.
package/dist/index.mjs CHANGED
@@ -1,9 +1,12 @@
1
1
  import { attachHook, createAsyncEventContext, useAsyncEventContext, useEventId, useEventLogger, useRouteParams } from "@wooksjs/event-core";
2
2
  import { Buffer as Buffer$1 } from "buffer";
3
+ import { Readable, pipeline } from "node:stream";
4
+ import { promisify } from "node:util";
5
+ import { createBrotliCompress, createBrotliDecompress, createDeflate, createGunzip, createGzip, createInflate } from "node:zlib";
3
6
  import { URLSearchParams } from "url";
4
7
  import http from "http";
5
8
  import { WooksAdapterBase } from "wooks";
6
- import { Readable } from "stream";
9
+ import { Readable as Readable$1 } from "stream";
7
10
 
8
11
  //#region packages/event-http/src/event-http.ts
9
12
  function createHttpContext(data, options) {
@@ -80,26 +83,417 @@ const cookieAttrFunc = {
80
83
  sameSite: (v) => v ? `SameSite=${typeof v === "string" ? v : "Strict"}` : ""
81
84
  };
82
85
 
86
+ //#endregion
87
+ //#region packages/event-http/src/compressor/body-compressor.ts
88
+ const compressors = { identity: {
89
+ compress: (v) => v,
90
+ uncompress: (v) => v,
91
+ stream: {
92
+ compress: (data) => data,
93
+ uncompress: (data) => data
94
+ }
95
+ } };
96
+ function encodingSupportsStream(encodings) {
97
+ return encodings.every((enc) => compressors[enc]?.stream);
98
+ }
99
+ async function uncompressBody(encodings, compressed) {
100
+ let buf = compressed;
101
+ for (const enc of encodings.slice().reverse()) {
102
+ const c = compressors[enc];
103
+ if (!c) throw new Error(`Unsupported compression type "${enc}".`);
104
+ buf = await c.uncompress(buf);
105
+ }
106
+ return buf;
107
+ }
108
+ async function uncompressBodyStream(encodings, src) {
109
+ if (!encodingSupportsStream(encodings)) throw new Error("Some encodings lack a streaming decompressor");
110
+ let out = src;
111
+ for (const enc of Array.from(encodings).reverse()) out = await compressors[enc].stream.uncompress(out);
112
+ return out;
113
+ }
114
+
115
+ //#endregion
116
+ //#region packages/event-http/src/compressor/zlib-compressors.ts
117
+ const pipeline$1 = pipeline;
118
+ function iterableToReadable(src) {
119
+ return Readable.from(src, { objectMode: false });
120
+ }
121
+ function pump(src, transform) {
122
+ pipeline$1(iterableToReadable(src), transform, (err) => {
123
+ if (err) transform.destroy(err);
124
+ });
125
+ return transform;
126
+ }
127
+ function addStreamCodec(name, createDeflater, createInflater) {
128
+ const c = compressors[name] ?? (compressors[name] = {
129
+ compress: (v) => v,
130
+ uncompress: (v) => v
131
+ });
132
+ c.stream = {
133
+ compress: async (src) => pump(src, createDeflater()),
134
+ uncompress: async (src) => pump(src, createInflater())
135
+ };
136
+ }
137
+ addStreamCodec("gzip", createGzip, createGunzip);
138
+ addStreamCodec("deflate", createDeflate, createInflate);
139
+ addStreamCodec("br", createBrotliCompress, createBrotliDecompress);
140
+ let zp;
141
+ async function zlib() {
142
+ if (!zp) {
143
+ const { gzip, gunzip, deflate, inflate, brotliCompress, brotliDecompress } = await import("node:zlib");
144
+ zp = {
145
+ gzip: promisify(gzip),
146
+ gunzip: promisify(gunzip),
147
+ deflate: promisify(deflate),
148
+ inflate: promisify(inflate),
149
+ brotliCompress: promisify(brotliCompress),
150
+ brotliDecompress: promisify(brotliDecompress)
151
+ };
152
+ }
153
+ return zp;
154
+ }
155
+ compressors.gzip.compress = async (b) => (await zlib()).gzip(b);
156
+ compressors.gzip.uncompress = async (b) => (await zlib()).gunzip(b);
157
+ compressors.deflate.compress = async (b) => (await zlib()).deflate(b);
158
+ compressors.deflate.uncompress = async (b) => (await zlib()).inflate(b);
159
+ compressors.br.compress = async (b) => (await zlib()).brotliCompress(b);
160
+ compressors.br.uncompress = async (b) => (await zlib()).brotliDecompress(b);
161
+
162
+ //#endregion
163
+ //#region packages/event-http/src/response/renderer.ts
164
+ var BaseHttpResponseRenderer = class {
165
+ render(response) {
166
+ if (typeof response.body === "string" || typeof response.body === "boolean" || typeof response.body === "number") {
167
+ if (!response.getContentType()) response.setContentType("text/plain");
168
+ return response.body.toString();
169
+ }
170
+ if (response.body === undefined) return "";
171
+ if (response.body instanceof Uint8Array) return response.body;
172
+ if (typeof response.body === "object") {
173
+ if (!response.getContentType()) response.setContentType("application/json");
174
+ return JSON.stringify(response.body);
175
+ }
176
+ throw new Error(`Unsupported body format "${typeof response.body}"`);
177
+ }
178
+ };
179
+
180
+ //#endregion
181
+ //#region packages/event-http/src/utils/status-codes.ts
182
+ const httpStatusCodes = {
183
+ 100: "Continue",
184
+ 101: "Switching protocols",
185
+ 102: "Processing",
186
+ 103: "Early Hints",
187
+ 200: "OK",
188
+ 201: "Created",
189
+ 202: "Accepted",
190
+ 203: "Non-Authoritative Information",
191
+ 204: "No Content",
192
+ 205: "Reset Content",
193
+ 206: "Partial Content",
194
+ 207: "Multi-Status",
195
+ 208: "Already Reported",
196
+ 226: "IM Used",
197
+ 300: "Multiple Choices",
198
+ 301: "Moved Permanently",
199
+ 302: "Found (Previously \"Moved Temporarily\")",
200
+ 303: "See Other",
201
+ 304: "Not Modified",
202
+ 305: "Use Proxy",
203
+ 306: "Switch Proxy",
204
+ 307: "Temporary Redirect",
205
+ 308: "Permanent Redirect",
206
+ 400: "Bad Request",
207
+ 401: "Unauthorized",
208
+ 402: "Payment Required",
209
+ 403: "Forbidden",
210
+ 404: "Not Found",
211
+ 405: "Method Not Allowed",
212
+ 406: "Not Acceptable",
213
+ 407: "Proxy Authentication Required",
214
+ 408: "Request Timeout",
215
+ 409: "Conflict",
216
+ 410: "Gone",
217
+ 411: "Length Required",
218
+ 412: "Precondition Failed",
219
+ 413: "Payload Too Large",
220
+ 414: "URI Too Long",
221
+ 415: "Unsupported Media Type",
222
+ 416: "Range Not Satisfiable",
223
+ 417: "Expectation Failed",
224
+ 418: "I'm a Teapot",
225
+ 421: "Misdirected Request",
226
+ 422: "Unprocessable Entity",
227
+ 423: "Locked",
228
+ 424: "Failed Dependency",
229
+ 425: "Too Early",
230
+ 426: "Upgrade Required",
231
+ 428: "Precondition Required",
232
+ 429: "Too Many Requests",
233
+ 431: "Request Header Fields Too Large",
234
+ 451: "Unavailable For Legal Reasons",
235
+ 500: "Internal Server Error",
236
+ 501: "Not Implemented",
237
+ 502: "Bad Gateway",
238
+ 503: "Service Unavailable",
239
+ 504: "Gateway Timeout",
240
+ 505: "HTTP Version Not Supported",
241
+ 506: "Variant Also Negotiates",
242
+ 507: "Insufficient Storage",
243
+ 508: "Loop Detected",
244
+ 510: "Not Extended",
245
+ 511: "Network Authentication Required"
246
+ };
247
+ let EHttpStatusCode = function(EHttpStatusCode$1) {
248
+ EHttpStatusCode$1[EHttpStatusCode$1["Continue"] = 100] = "Continue";
249
+ EHttpStatusCode$1[EHttpStatusCode$1["SwitchingProtocols"] = 101] = "SwitchingProtocols";
250
+ EHttpStatusCode$1[EHttpStatusCode$1["Processing"] = 102] = "Processing";
251
+ EHttpStatusCode$1[EHttpStatusCode$1["EarlyHints"] = 103] = "EarlyHints";
252
+ EHttpStatusCode$1[EHttpStatusCode$1["OK"] = 200] = "OK";
253
+ EHttpStatusCode$1[EHttpStatusCode$1["Created"] = 201] = "Created";
254
+ EHttpStatusCode$1[EHttpStatusCode$1["Accepted"] = 202] = "Accepted";
255
+ EHttpStatusCode$1[EHttpStatusCode$1["NonAuthoritativeInformation"] = 203] = "NonAuthoritativeInformation";
256
+ EHttpStatusCode$1[EHttpStatusCode$1["NoContent"] = 204] = "NoContent";
257
+ EHttpStatusCode$1[EHttpStatusCode$1["ResetContent"] = 205] = "ResetContent";
258
+ EHttpStatusCode$1[EHttpStatusCode$1["PartialContent"] = 206] = "PartialContent";
259
+ EHttpStatusCode$1[EHttpStatusCode$1["MultiStatus"] = 207] = "MultiStatus";
260
+ EHttpStatusCode$1[EHttpStatusCode$1["AlreadyReported"] = 208] = "AlreadyReported";
261
+ EHttpStatusCode$1[EHttpStatusCode$1["IMUsed"] = 226] = "IMUsed";
262
+ EHttpStatusCode$1[EHttpStatusCode$1["MultipleChoices"] = 300] = "MultipleChoices";
263
+ EHttpStatusCode$1[EHttpStatusCode$1["MovedPermanently"] = 301] = "MovedPermanently";
264
+ EHttpStatusCode$1[EHttpStatusCode$1["Found"] = 302] = "Found";
265
+ EHttpStatusCode$1[EHttpStatusCode$1["SeeOther"] = 303] = "SeeOther";
266
+ EHttpStatusCode$1[EHttpStatusCode$1["NotModified"] = 304] = "NotModified";
267
+ EHttpStatusCode$1[EHttpStatusCode$1["UseProxy"] = 305] = "UseProxy";
268
+ EHttpStatusCode$1[EHttpStatusCode$1["SwitchProxy"] = 306] = "SwitchProxy";
269
+ EHttpStatusCode$1[EHttpStatusCode$1["TemporaryRedirect"] = 307] = "TemporaryRedirect";
270
+ EHttpStatusCode$1[EHttpStatusCode$1["PermanentRedirect"] = 308] = "PermanentRedirect";
271
+ EHttpStatusCode$1[EHttpStatusCode$1["BadRequest"] = 400] = "BadRequest";
272
+ EHttpStatusCode$1[EHttpStatusCode$1["Unauthorized"] = 401] = "Unauthorized";
273
+ EHttpStatusCode$1[EHttpStatusCode$1["PaymentRequired"] = 402] = "PaymentRequired";
274
+ EHttpStatusCode$1[EHttpStatusCode$1["Forbidden"] = 403] = "Forbidden";
275
+ EHttpStatusCode$1[EHttpStatusCode$1["NotFound"] = 404] = "NotFound";
276
+ EHttpStatusCode$1[EHttpStatusCode$1["MethodNotAllowed"] = 405] = "MethodNotAllowed";
277
+ EHttpStatusCode$1[EHttpStatusCode$1["NotAcceptable"] = 406] = "NotAcceptable";
278
+ EHttpStatusCode$1[EHttpStatusCode$1["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
279
+ EHttpStatusCode$1[EHttpStatusCode$1["RequestTimeout"] = 408] = "RequestTimeout";
280
+ EHttpStatusCode$1[EHttpStatusCode$1["Conflict"] = 409] = "Conflict";
281
+ EHttpStatusCode$1[EHttpStatusCode$1["Gone"] = 410] = "Gone";
282
+ EHttpStatusCode$1[EHttpStatusCode$1["LengthRequired"] = 411] = "LengthRequired";
283
+ EHttpStatusCode$1[EHttpStatusCode$1["PreconditionFailed"] = 412] = "PreconditionFailed";
284
+ EHttpStatusCode$1[EHttpStatusCode$1["PayloadTooLarge"] = 413] = "PayloadTooLarge";
285
+ EHttpStatusCode$1[EHttpStatusCode$1["URITooLong"] = 414] = "URITooLong";
286
+ EHttpStatusCode$1[EHttpStatusCode$1["UnsupportedMediaType"] = 415] = "UnsupportedMediaType";
287
+ EHttpStatusCode$1[EHttpStatusCode$1["RangeNotSatisfiable"] = 416] = "RangeNotSatisfiable";
288
+ EHttpStatusCode$1[EHttpStatusCode$1["ExpectationFailed"] = 417] = "ExpectationFailed";
289
+ EHttpStatusCode$1[EHttpStatusCode$1["ImATeapot"] = 418] = "ImATeapot";
290
+ EHttpStatusCode$1[EHttpStatusCode$1["MisdirectedRequest"] = 421] = "MisdirectedRequest";
291
+ EHttpStatusCode$1[EHttpStatusCode$1["UnprocessableEntity"] = 422] = "UnprocessableEntity";
292
+ EHttpStatusCode$1[EHttpStatusCode$1["Locked"] = 423] = "Locked";
293
+ EHttpStatusCode$1[EHttpStatusCode$1["FailedDependency"] = 424] = "FailedDependency";
294
+ EHttpStatusCode$1[EHttpStatusCode$1["TooEarly"] = 425] = "TooEarly";
295
+ EHttpStatusCode$1[EHttpStatusCode$1["UpgradeRequired"] = 426] = "UpgradeRequired";
296
+ EHttpStatusCode$1[EHttpStatusCode$1["PreconditionRequired"] = 428] = "PreconditionRequired";
297
+ EHttpStatusCode$1[EHttpStatusCode$1["TooManyRequests"] = 429] = "TooManyRequests";
298
+ EHttpStatusCode$1[EHttpStatusCode$1["RequestHeaderFieldsTooLarge"] = 431] = "RequestHeaderFieldsTooLarge";
299
+ EHttpStatusCode$1[EHttpStatusCode$1["UnavailableForLegalReasons"] = 451] = "UnavailableForLegalReasons";
300
+ EHttpStatusCode$1[EHttpStatusCode$1["InternalServerError"] = 500] = "InternalServerError";
301
+ EHttpStatusCode$1[EHttpStatusCode$1["NotImplemented"] = 501] = "NotImplemented";
302
+ EHttpStatusCode$1[EHttpStatusCode$1["BadGateway"] = 502] = "BadGateway";
303
+ EHttpStatusCode$1[EHttpStatusCode$1["ServiceUnavailable"] = 503] = "ServiceUnavailable";
304
+ EHttpStatusCode$1[EHttpStatusCode$1["GatewayTimeout"] = 504] = "GatewayTimeout";
305
+ EHttpStatusCode$1[EHttpStatusCode$1["HTTPVersionNotSupported"] = 505] = "HTTPVersionNotSupported";
306
+ EHttpStatusCode$1[EHttpStatusCode$1["VariantAlsoNegotiates"] = 506] = "VariantAlsoNegotiates";
307
+ EHttpStatusCode$1[EHttpStatusCode$1["InsufficientStorage"] = 507] = "InsufficientStorage";
308
+ EHttpStatusCode$1[EHttpStatusCode$1["LoopDetected"] = 508] = "LoopDetected";
309
+ EHttpStatusCode$1[EHttpStatusCode$1["NotExtended"] = 510] = "NotExtended";
310
+ EHttpStatusCode$1[EHttpStatusCode$1["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired";
311
+ return EHttpStatusCode$1;
312
+ }({});
313
+
314
+ //#endregion
315
+ //#region packages/event-http/src/errors/error-renderer.ts
316
+ const preStyles = "font-family: monospace;width: 100%;max-width: 900px;padding: 10px;margin: 20px auto;border-radius: 8px;background-color: #494949;box-shadow: 0px 0px 3px 2px rgb(255 255 255 / 20%);";
317
+ var HttpErrorRenderer = class extends BaseHttpResponseRenderer {
318
+ renderHtml(response) {
319
+ const data = response.body || {};
320
+ response.setContentType("text/html");
321
+ const keys = Object.keys(data).filter((key) => ![
322
+ "statusCode",
323
+ "error",
324
+ "message"
325
+ ].includes(key));
326
+ return "<html style=\"background-color: #333; color: #bbb;\">" + `<head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head>` + `<body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center>` + `<center><h4>${data.message}</h1></center><hr color="#666">` + `<center style="color: #666;"> Wooks v${"0.5.25"} </center>` + `${keys.length > 0 ? `<pre style="${preStyles}">${JSON.stringify({
327
+ ...data,
328
+ statusCode: undefined,
329
+ message: undefined,
330
+ error: undefined
331
+ }, null, " ")}</pre>` : ""}` + "</body></html>";
332
+ }
333
+ renderText(response) {
334
+ const data = response.body || {};
335
+ response.setContentType("text/plain");
336
+ const keys = Object.keys(data).filter((key) => ![
337
+ "statusCode",
338
+ "error",
339
+ "message"
340
+ ].includes(key));
341
+ return `${data.statusCode} ${httpStatusCodes[data.statusCode]}\n${data.message}` + `\n\n${keys.length > 0 ? `${JSON.stringify({
342
+ ...data,
343
+ statusCode: undefined,
344
+ message: undefined,
345
+ error: undefined
346
+ }, null, " ")}` : ""}`;
347
+ }
348
+ renderJson(response) {
349
+ const data = response.body || {};
350
+ response.setContentType("application/json");
351
+ const keys = Object.keys(data).filter((key) => ![
352
+ "statusCode",
353
+ "error",
354
+ "message"
355
+ ].includes(key));
356
+ return `{"statusCode":${escapeQuotes(data.statusCode)},` + `"error":"${escapeQuotes(data.error)}",` + `"message":"${escapeQuotes(data.message)}"` + `${keys.length > 0 ? `,${keys.map((k) => `"${escapeQuotes(k)}":${JSON.stringify(data[k])}`).join(",")}` : ""}}`;
357
+ }
358
+ render(response) {
359
+ const { acceptsJson, acceptsText, acceptsHtml } = useAccept();
360
+ response.status = response.body?.statusCode || 500;
361
+ if (acceptsJson()) return this.renderJson(response);
362
+ else if (acceptsHtml()) return this.renderHtml(response);
363
+ else if (acceptsText()) return this.renderText(response);
364
+ else return this.renderJson(response);
365
+ }
366
+ };
367
+ function escapeQuotes(s) {
368
+ return (typeof s === "number" ? s : s || "").toString().replace(/"/gu, "\\\"");
369
+ }
370
+
371
+ //#endregion
372
+ //#region packages/event-http/src/errors/http-error.ts
373
+ var HttpError = class extends Error {
374
+ name = "HttpError";
375
+ constructor(code = 500, _body = "") {
376
+ super(typeof _body === "string" ? _body : _body.message);
377
+ this.code = code;
378
+ this._body = _body;
379
+ }
380
+ get body() {
381
+ return typeof this._body === "string" ? {
382
+ statusCode: this.code,
383
+ message: this.message,
384
+ error: httpStatusCodes[this.code]
385
+ } : {
386
+ ...this._body,
387
+ statusCode: this.code,
388
+ message: this.message,
389
+ error: httpStatusCodes[this.code]
390
+ };
391
+ }
392
+ renderer;
393
+ attachRenderer(renderer) {
394
+ this.renderer = renderer;
395
+ }
396
+ getRenderer() {
397
+ return this.renderer;
398
+ }
399
+ };
400
+
83
401
  //#endregion
84
402
  //#region packages/event-http/src/composables/request.ts
85
403
  const xForwardedFor = "x-forwarded-for";
404
+ const DEFAULT_LIMITS = {
405
+ maxCompressed: 1048576,
406
+ maxInflated: 10485760,
407
+ maxRatio: 100,
408
+ readTimeoutMs: 1e4
409
+ };
86
410
  function useRequest() {
87
411
  const { store } = useHttpContext();
88
- const { init } = store("request");
412
+ const { init, get, set } = store("request");
89
413
  const event = store("event");
90
414
  const req = event.get("req");
91
- const rawBody = () => init("rawBody", () => new Promise((resolve, reject) => {
92
- let body = Buffer$1.from("");
93
- req.on("data", (chunk) => {
94
- body = Buffer$1.concat([body, chunk]);
95
- });
96
- req.on("error", (err) => {
97
- reject(err);
98
- });
99
- req.on("end", () => {
100
- resolve(body);
101
- });
102
- }));
415
+ const contentEncoding = req.headers["content-encoding"];
416
+ const contentEncodings = () => init("contentEncodings", () => (contentEncoding || "").split(",").map((p) => p.trim()).filter((p) => !!p));
417
+ const isCompressed = () => init("isCompressed", () => {
418
+ const parts = contentEncodings();
419
+ for (const p of parts) if ([
420
+ "deflate",
421
+ "gzip",
422
+ "br"
423
+ ].includes(p)) return true;
424
+ return false;
425
+ });
426
+ const getMaxCompressed = () => get("maxCompressed") ?? DEFAULT_LIMITS.maxCompressed;
427
+ const setMaxCompressed = (limit) => set("maxCompressed", limit);
428
+ const getReadTimeoutMs = () => get("readTimeoutMs") ?? DEFAULT_LIMITS.readTimeoutMs;
429
+ const setReadTimeoutMs = (limit) => set("readTimeoutMs", limit);
430
+ const getMaxInflated = () => get("maxInflated") ?? DEFAULT_LIMITS.maxInflated;
431
+ const setMaxInflated = (limit) => set("maxInflated", limit);
432
+ const rawBody = () => init("rawBody", async () => {
433
+ const encs = contentEncodings();
434
+ const isZip = isCompressed();
435
+ const streamable = isZip && encodingSupportsStream(encs);
436
+ const maxCompressed = getMaxCompressed();
437
+ const maxInflated = getMaxInflated();
438
+ const timeoutMs = getReadTimeoutMs();
439
+ const cl = Number(req.headers["content-length"] ?? 0);
440
+ const upfrontLimit = isZip ? maxCompressed : maxInflated;
441
+ if (cl && cl > upfrontLimit) throw new HttpError(413, "Payload Too Large");
442
+ for (const enc of encs) if (!compressors[enc]) throw new HttpError(415, `Unsupported Content-Encoding "${enc}"`);
443
+ let timer = null;
444
+ function resetTimer() {
445
+ if (timeoutMs === 0) return;
446
+ clearTimer();
447
+ timer = setTimeout(() => {
448
+ clearTimer();
449
+ req.destroy();
450
+ }, timeoutMs);
451
+ }
452
+ function clearTimer() {
453
+ if (timer) {
454
+ clearTimeout(timer);
455
+ timer = null;
456
+ }
457
+ }
458
+ let rawBytes = 0;
459
+ async function* limitedCompressed() {
460
+ resetTimer();
461
+ try {
462
+ for await (const chunk of req) {
463
+ rawBytes += chunk.length;
464
+ if (rawBytes > upfrontLimit) {
465
+ req.destroy();
466
+ throw new HttpError(413, "Payload Too Large");
467
+ }
468
+ resetTimer();
469
+ yield chunk;
470
+ }
471
+ } finally {
472
+ clearTimer();
473
+ }
474
+ }
475
+ let stream = limitedCompressed();
476
+ if (streamable) stream = await uncompressBodyStream(encs, stream);
477
+ const chunks = [];
478
+ let inflatedBytes = 0;
479
+ try {
480
+ for await (const chunk of stream) {
481
+ inflatedBytes += chunk.length;
482
+ if (inflatedBytes > maxInflated) throw new HttpError(413, "Inflated body too large");
483
+ chunks.push(chunk);
484
+ }
485
+ } catch (error) {
486
+ if (error instanceof HttpError) throw error;
487
+ throw new HttpError(408, "Request body timeout");
488
+ }
489
+ let body = Buffer$1.concat(chunks);
490
+ if (!streamable && isZip) {
491
+ body = await uncompressBody(encs, body);
492
+ inflatedBytes = body.byteLength;
493
+ if (inflatedBytes > maxInflated) throw new HttpError(413, "Inflated body too large");
494
+ }
495
+ return body;
496
+ });
103
497
  const reqId = useEventId().getId;
104
498
  const forwardedIp = () => init("forwardedIp", () => {
105
499
  if (typeof req.headers[xForwardedFor] === "string" && req.headers[xForwardedFor]) return req.headers[xForwardedFor].split(",").shift()?.trim();
@@ -122,7 +516,14 @@ else return remoteIp();
122
516
  rawBody,
123
517
  reqId,
124
518
  getIp,
125
- getIpList
519
+ getIpList,
520
+ isCompressed,
521
+ getMaxCompressed,
522
+ setMaxCompressed,
523
+ getReadTimeoutMs,
524
+ setReadTimeoutMs,
525
+ getMaxInflated,
526
+ setMaxInflated
126
527
  };
127
528
  }
128
529
 
@@ -363,11 +764,15 @@ function useStatus() {
363
764
  //#region packages/event-http/src/utils/url-search-params.ts
364
765
  var WooksURLSearchParams = class extends URLSearchParams {
365
766
  toJson() {
366
- const json = {};
767
+ const json = Object.create(null);
367
768
  for (const [key, value] of this.entries()) if (isArrayParam(key)) {
368
769
  const a = json[key] = json[key] || [];
369
770
  a.push(value);
370
- } else json[key] = value;
771
+ } else {
772
+ if (key === "__proto__") throw new HttpError(400, `Illegal key name "${key}"`);
773
+ if (key in json) throw new HttpError(400, `Duplicate key "${key}"`);
774
+ json[key] = value;
775
+ }
371
776
  return json;
372
777
  }
373
778
  };
@@ -393,245 +798,6 @@ function useSearchParams() {
393
798
  };
394
799
  }
395
800
 
396
- //#endregion
397
- //#region packages/event-http/src/response/renderer.ts
398
- var BaseHttpResponseRenderer = class {
399
- render(response) {
400
- if (typeof response.body === "string" || typeof response.body === "boolean" || typeof response.body === "number") {
401
- if (!response.getContentType()) response.setContentType("text/plain");
402
- return response.body.toString();
403
- }
404
- if (response.body === undefined) return "";
405
- if (response.body instanceof Uint8Array) return response.body;
406
- if (typeof response.body === "object") {
407
- if (!response.getContentType()) response.setContentType("application/json");
408
- return JSON.stringify(response.body);
409
- }
410
- throw new Error(`Unsupported body format "${typeof response.body}"`);
411
- }
412
- };
413
-
414
- //#endregion
415
- //#region packages/event-http/src/utils/status-codes.ts
416
- const httpStatusCodes = {
417
- 100: "Continue",
418
- 101: "Switching protocols",
419
- 102: "Processing",
420
- 103: "Early Hints",
421
- 200: "OK",
422
- 201: "Created",
423
- 202: "Accepted",
424
- 203: "Non-Authoritative Information",
425
- 204: "No Content",
426
- 205: "Reset Content",
427
- 206: "Partial Content",
428
- 207: "Multi-Status",
429
- 208: "Already Reported",
430
- 226: "IM Used",
431
- 300: "Multiple Choices",
432
- 301: "Moved Permanently",
433
- 302: "Found (Previously \"Moved Temporarily\")",
434
- 303: "See Other",
435
- 304: "Not Modified",
436
- 305: "Use Proxy",
437
- 306: "Switch Proxy",
438
- 307: "Temporary Redirect",
439
- 308: "Permanent Redirect",
440
- 400: "Bad Request",
441
- 401: "Unauthorized",
442
- 402: "Payment Required",
443
- 403: "Forbidden",
444
- 404: "Not Found",
445
- 405: "Method Not Allowed",
446
- 406: "Not Acceptable",
447
- 407: "Proxy Authentication Required",
448
- 408: "Request Timeout",
449
- 409: "Conflict",
450
- 410: "Gone",
451
- 411: "Length Required",
452
- 412: "Precondition Failed",
453
- 413: "Payload Too Large",
454
- 414: "URI Too Long",
455
- 415: "Unsupported Media Type",
456
- 416: "Range Not Satisfiable",
457
- 417: "Expectation Failed",
458
- 418: "I'm a Teapot",
459
- 421: "Misdirected Request",
460
- 422: "Unprocessable Entity",
461
- 423: "Locked",
462
- 424: "Failed Dependency",
463
- 425: "Too Early",
464
- 426: "Upgrade Required",
465
- 428: "Precondition Required",
466
- 429: "Too Many Requests",
467
- 431: "Request Header Fields Too Large",
468
- 451: "Unavailable For Legal Reasons",
469
- 500: "Internal Server Error",
470
- 501: "Not Implemented",
471
- 502: "Bad Gateway",
472
- 503: "Service Unavailable",
473
- 504: "Gateway Timeout",
474
- 505: "HTTP Version Not Supported",
475
- 506: "Variant Also Negotiates",
476
- 507: "Insufficient Storage",
477
- 508: "Loop Detected",
478
- 510: "Not Extended",
479
- 511: "Network Authentication Required"
480
- };
481
- let EHttpStatusCode = function(EHttpStatusCode$1) {
482
- EHttpStatusCode$1[EHttpStatusCode$1["Continue"] = 100] = "Continue";
483
- EHttpStatusCode$1[EHttpStatusCode$1["SwitchingProtocols"] = 101] = "SwitchingProtocols";
484
- EHttpStatusCode$1[EHttpStatusCode$1["Processing"] = 102] = "Processing";
485
- EHttpStatusCode$1[EHttpStatusCode$1["EarlyHints"] = 103] = "EarlyHints";
486
- EHttpStatusCode$1[EHttpStatusCode$1["OK"] = 200] = "OK";
487
- EHttpStatusCode$1[EHttpStatusCode$1["Created"] = 201] = "Created";
488
- EHttpStatusCode$1[EHttpStatusCode$1["Accepted"] = 202] = "Accepted";
489
- EHttpStatusCode$1[EHttpStatusCode$1["NonAuthoritativeInformation"] = 203] = "NonAuthoritativeInformation";
490
- EHttpStatusCode$1[EHttpStatusCode$1["NoContent"] = 204] = "NoContent";
491
- EHttpStatusCode$1[EHttpStatusCode$1["ResetContent"] = 205] = "ResetContent";
492
- EHttpStatusCode$1[EHttpStatusCode$1["PartialContent"] = 206] = "PartialContent";
493
- EHttpStatusCode$1[EHttpStatusCode$1["MultiStatus"] = 207] = "MultiStatus";
494
- EHttpStatusCode$1[EHttpStatusCode$1["AlreadyReported"] = 208] = "AlreadyReported";
495
- EHttpStatusCode$1[EHttpStatusCode$1["IMUsed"] = 226] = "IMUsed";
496
- EHttpStatusCode$1[EHttpStatusCode$1["MultipleChoices"] = 300] = "MultipleChoices";
497
- EHttpStatusCode$1[EHttpStatusCode$1["MovedPermanently"] = 301] = "MovedPermanently";
498
- EHttpStatusCode$1[EHttpStatusCode$1["Found"] = 302] = "Found";
499
- EHttpStatusCode$1[EHttpStatusCode$1["SeeOther"] = 303] = "SeeOther";
500
- EHttpStatusCode$1[EHttpStatusCode$1["NotModified"] = 304] = "NotModified";
501
- EHttpStatusCode$1[EHttpStatusCode$1["UseProxy"] = 305] = "UseProxy";
502
- EHttpStatusCode$1[EHttpStatusCode$1["SwitchProxy"] = 306] = "SwitchProxy";
503
- EHttpStatusCode$1[EHttpStatusCode$1["TemporaryRedirect"] = 307] = "TemporaryRedirect";
504
- EHttpStatusCode$1[EHttpStatusCode$1["PermanentRedirect"] = 308] = "PermanentRedirect";
505
- EHttpStatusCode$1[EHttpStatusCode$1["BadRequest"] = 400] = "BadRequest";
506
- EHttpStatusCode$1[EHttpStatusCode$1["Unauthorized"] = 401] = "Unauthorized";
507
- EHttpStatusCode$1[EHttpStatusCode$1["PaymentRequired"] = 402] = "PaymentRequired";
508
- EHttpStatusCode$1[EHttpStatusCode$1["Forbidden"] = 403] = "Forbidden";
509
- EHttpStatusCode$1[EHttpStatusCode$1["NotFound"] = 404] = "NotFound";
510
- EHttpStatusCode$1[EHttpStatusCode$1["MethodNotAllowed"] = 405] = "MethodNotAllowed";
511
- EHttpStatusCode$1[EHttpStatusCode$1["NotAcceptable"] = 406] = "NotAcceptable";
512
- EHttpStatusCode$1[EHttpStatusCode$1["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
513
- EHttpStatusCode$1[EHttpStatusCode$1["RequestTimeout"] = 408] = "RequestTimeout";
514
- EHttpStatusCode$1[EHttpStatusCode$1["Conflict"] = 409] = "Conflict";
515
- EHttpStatusCode$1[EHttpStatusCode$1["Gone"] = 410] = "Gone";
516
- EHttpStatusCode$1[EHttpStatusCode$1["LengthRequired"] = 411] = "LengthRequired";
517
- EHttpStatusCode$1[EHttpStatusCode$1["PreconditionFailed"] = 412] = "PreconditionFailed";
518
- EHttpStatusCode$1[EHttpStatusCode$1["PayloadTooLarge"] = 413] = "PayloadTooLarge";
519
- EHttpStatusCode$1[EHttpStatusCode$1["URITooLong"] = 414] = "URITooLong";
520
- EHttpStatusCode$1[EHttpStatusCode$1["UnsupportedMediaType"] = 415] = "UnsupportedMediaType";
521
- EHttpStatusCode$1[EHttpStatusCode$1["RangeNotSatisfiable"] = 416] = "RangeNotSatisfiable";
522
- EHttpStatusCode$1[EHttpStatusCode$1["ExpectationFailed"] = 417] = "ExpectationFailed";
523
- EHttpStatusCode$1[EHttpStatusCode$1["ImATeapot"] = 418] = "ImATeapot";
524
- EHttpStatusCode$1[EHttpStatusCode$1["MisdirectedRequest"] = 421] = "MisdirectedRequest";
525
- EHttpStatusCode$1[EHttpStatusCode$1["UnprocessableEntity"] = 422] = "UnprocessableEntity";
526
- EHttpStatusCode$1[EHttpStatusCode$1["Locked"] = 423] = "Locked";
527
- EHttpStatusCode$1[EHttpStatusCode$1["FailedDependency"] = 424] = "FailedDependency";
528
- EHttpStatusCode$1[EHttpStatusCode$1["TooEarly"] = 425] = "TooEarly";
529
- EHttpStatusCode$1[EHttpStatusCode$1["UpgradeRequired"] = 426] = "UpgradeRequired";
530
- EHttpStatusCode$1[EHttpStatusCode$1["PreconditionRequired"] = 428] = "PreconditionRequired";
531
- EHttpStatusCode$1[EHttpStatusCode$1["TooManyRequests"] = 429] = "TooManyRequests";
532
- EHttpStatusCode$1[EHttpStatusCode$1["RequestHeaderFieldsTooLarge"] = 431] = "RequestHeaderFieldsTooLarge";
533
- EHttpStatusCode$1[EHttpStatusCode$1["UnavailableForLegalReasons"] = 451] = "UnavailableForLegalReasons";
534
- EHttpStatusCode$1[EHttpStatusCode$1["InternalServerError"] = 500] = "InternalServerError";
535
- EHttpStatusCode$1[EHttpStatusCode$1["NotImplemented"] = 501] = "NotImplemented";
536
- EHttpStatusCode$1[EHttpStatusCode$1["BadGateway"] = 502] = "BadGateway";
537
- EHttpStatusCode$1[EHttpStatusCode$1["ServiceUnavailable"] = 503] = "ServiceUnavailable";
538
- EHttpStatusCode$1[EHttpStatusCode$1["GatewayTimeout"] = 504] = "GatewayTimeout";
539
- EHttpStatusCode$1[EHttpStatusCode$1["HTTPVersionNotSupported"] = 505] = "HTTPVersionNotSupported";
540
- EHttpStatusCode$1[EHttpStatusCode$1["VariantAlsoNegotiates"] = 506] = "VariantAlsoNegotiates";
541
- EHttpStatusCode$1[EHttpStatusCode$1["InsufficientStorage"] = 507] = "InsufficientStorage";
542
- EHttpStatusCode$1[EHttpStatusCode$1["LoopDetected"] = 508] = "LoopDetected";
543
- EHttpStatusCode$1[EHttpStatusCode$1["NotExtended"] = 510] = "NotExtended";
544
- EHttpStatusCode$1[EHttpStatusCode$1["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired";
545
- return EHttpStatusCode$1;
546
- }({});
547
-
548
- //#endregion
549
- //#region packages/event-http/src/errors/error-renderer.ts
550
- const preStyles = "font-family: monospace;width: 100%;max-width: 900px;padding: 10px;margin: 20px auto;border-radius: 8px;background-color: #494949;box-shadow: 0px 0px 3px 2px rgb(255 255 255 / 20%);";
551
- var HttpErrorRenderer = class extends BaseHttpResponseRenderer {
552
- renderHtml(response) {
553
- const data = response.body || {};
554
- response.setContentType("text/html");
555
- const keys = Object.keys(data).filter((key) => ![
556
- "statusCode",
557
- "error",
558
- "message"
559
- ].includes(key));
560
- return "<html style=\"background-color: #333; color: #bbb;\">" + `<head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head>` + `<body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center>` + `<center><h4>${data.message}</h1></center><hr color="#666">` + `<center style="color: #666;"> Wooks v${"0.5.24"} </center>` + `${keys.length > 0 ? `<pre style="${preStyles}">${JSON.stringify({
561
- ...data,
562
- statusCode: undefined,
563
- message: undefined,
564
- error: undefined
565
- }, null, " ")}</pre>` : ""}` + "</body></html>";
566
- }
567
- renderText(response) {
568
- const data = response.body || {};
569
- response.setContentType("text/plain");
570
- const keys = Object.keys(data).filter((key) => ![
571
- "statusCode",
572
- "error",
573
- "message"
574
- ].includes(key));
575
- return `${data.statusCode} ${httpStatusCodes[data.statusCode]}\n${data.message}` + `\n\n${keys.length > 0 ? `${JSON.stringify({
576
- ...data,
577
- statusCode: undefined,
578
- message: undefined,
579
- error: undefined
580
- }, null, " ")}` : ""}`;
581
- }
582
- renderJson(response) {
583
- const data = response.body || {};
584
- response.setContentType("application/json");
585
- const keys = Object.keys(data).filter((key) => ![
586
- "statusCode",
587
- "error",
588
- "message"
589
- ].includes(key));
590
- return `{"statusCode":${escapeQuotes(data.statusCode)},` + `"error":"${escapeQuotes(data.error)}",` + `"message":"${escapeQuotes(data.message)}"` + `${keys.length > 0 ? `,${keys.map((k) => `"${escapeQuotes(k)}":${JSON.stringify(data[k])}`).join(",")}` : ""}}`;
591
- }
592
- render(response) {
593
- const { acceptsJson, acceptsText, acceptsHtml } = useAccept();
594
- response.status = response.body?.statusCode || 500;
595
- if (acceptsJson()) return this.renderJson(response);
596
- else if (acceptsHtml()) return this.renderHtml(response);
597
- else if (acceptsText()) return this.renderText(response);
598
- else return this.renderJson(response);
599
- }
600
- };
601
- function escapeQuotes(s) {
602
- return (typeof s === "number" ? s : s || "").toString().replace(/"/gu, "\\\"");
603
- }
604
-
605
- //#endregion
606
- //#region packages/event-http/src/errors/http-error.ts
607
- var HttpError = class extends Error {
608
- name = "HttpError";
609
- constructor(code = 500, _body = "") {
610
- super(typeof _body === "string" ? _body : _body.message);
611
- this.code = code;
612
- this._body = _body;
613
- }
614
- get body() {
615
- return typeof this._body === "string" ? {
616
- statusCode: this.code,
617
- message: this.message,
618
- error: httpStatusCodes[this.code]
619
- } : {
620
- ...this._body,
621
- statusCode: this.code,
622
- message: this.message,
623
- error: httpStatusCodes[this.code]
624
- };
625
- }
626
- renderer;
627
- attachRenderer(renderer) {
628
- this.renderer = renderer;
629
- }
630
- getRenderer() {
631
- return this.renderer;
632
- }
633
- };
634
-
635
801
  //#endregion
636
802
  //#region packages/event-http/src/response/core.ts
637
803
  const defaultStatus = {
@@ -742,7 +908,7 @@ var BaseHttpResponse = class {
742
908
  if (hasResponded()) this.panic("The response was already sent.", logger);
743
909
  this.mergeHeaders();
744
910
  const res = rawResponse();
745
- if (this.body instanceof Readable) {
911
+ if (this.body instanceof Readable$1) {
746
912
  const stream = this.body;
747
913
  this.mergeStatus("ok");
748
914
  res.writeHead(this.status, { ...this._headers });
@@ -967,4 +1133,4 @@ function createHttpApp(opts, wooks) {
967
1133
  }
968
1134
 
969
1135
  //#endregion
970
- export { BaseHttpResponse, BaseHttpResponseRenderer, EHttpStatusCode, HttpError, HttpErrorRenderer, WooksHttp, WooksURLSearchParams, createHttpApp, createHttpContext, createWooksResponder, httpStatusCodes, renderCacheControl, useAccept, useAuthorization, useCookies, useEventLogger, useHeaders, useHttpContext, useRequest, useResponse, useRouteParams, useSearchParams, useSetCacheControl, useSetCookie, useSetCookies, useSetHeader, useSetHeaders, useStatus };
1136
+ export { BaseHttpResponse, BaseHttpResponseRenderer, DEFAULT_LIMITS, EHttpStatusCode, HttpError, HttpErrorRenderer, WooksHttp, WooksURLSearchParams, createHttpApp, createHttpContext, createWooksResponder, httpStatusCodes, renderCacheControl, useAccept, useAuthorization, useCookies, useEventLogger, useHeaders, useHttpContext, useRequest, useResponse, useRouteParams, useSearchParams, useSetCacheControl, useSetCookie, useSetCookies, useSetHeader, useSetHeaders, useStatus };