@adonisjs/http-server 8.0.0-next.12 → 8.0.0-next.13
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/build/define_config-DpDqx7A4.js +1953 -0
- package/build/factories/http_context.d.ts +4 -4
- package/build/factories/main.d.ts +2 -2
- package/build/factories/main.js +162 -346
- package/build/factories/request.d.ts +3 -3
- package/build/factories/response.d.ts +3 -3
- package/build/helpers-C_2HouOe.js +52 -0
- package/build/helpers-Dt-25M21.js +660 -0
- package/build/index.d.ts +2 -2
- package/build/index.js +157 -375
- package/build/src/client/url_builder.js +3 -12
- package/build/src/exception_handler.d.ts +7 -0
- package/build/src/helpers.js +3 -26
- package/build/src/http_context/main.d.ts +7 -7
- package/build/src/redirect.d.ts +2 -2
- package/build/src/request.d.ts +1 -1
- package/build/src/response.d.ts +2 -2
- package/build/src/server/main.d.ts +5 -5
- package/build/src/types/main.js +1 -0
- package/build/src/types/response.d.ts +6 -1
- package/build/url_builder-piNQy-CF.js +114 -0
- package/package.json +19 -16
- package/build/chunk-2QM3D5BN.js +0 -87
- package/build/chunk-77CSRFCU.js +0 -131
- package/build/chunk-L2UOVWDK.js +0 -4441
- package/build/chunk-QDK57QGB.js +0 -1176
|
@@ -0,0 +1,1953 @@
|
|
|
1
|
+
import { T as __export, _ as Route, b as httpRequest, d as safeDecodeURI, f as toRoutesJSON, g as BriskRoute, h as RouteResource, l as serializeCookie, m as RouteGroup, n as createSignedURL, o as mime, p as trustProxy, r as encodeUrl, s as parseRoute, v as httpExceptionHandler, w as debug_default, x as httpResponseSerializer, y as httpMiddleware } from "./helpers-Dt-25M21.js";
|
|
2
|
+
import { n as findRoute, t as createURL } from "./helpers-C_2HouOe.js";
|
|
3
|
+
import { t as createUrlBuilder } from "./url_builder-piNQy-CF.js";
|
|
4
|
+
import { parse, stringify } from "qs";
|
|
5
|
+
import { Exception, RuntimeException, createError } from "@poppinss/utils/exception";
|
|
6
|
+
import { inspect } from "node:util";
|
|
7
|
+
import { parse as parse$1 } from "cookie-es";
|
|
8
|
+
import matchit from "@poppinss/matchit";
|
|
9
|
+
import string from "@poppinss/utils/string";
|
|
10
|
+
import { moduleCaller, moduleImporter } from "@adonisjs/fold";
|
|
11
|
+
import Macroable from "@poppinss/macroable";
|
|
12
|
+
import is from "@sindresorhus/is";
|
|
13
|
+
import Middleware from "@poppinss/middleware";
|
|
14
|
+
import onFinished from "on-finished";
|
|
15
|
+
import fresh from "fresh";
|
|
16
|
+
import typeIs from "type-is";
|
|
17
|
+
import accepts from "accepts";
|
|
18
|
+
import { isIP } from "node:net";
|
|
19
|
+
import proxyAddr from "proxy-addr";
|
|
20
|
+
import { MessageBuilder, safeEqual } from "@poppinss/utils";
|
|
21
|
+
import lodash from "@poppinss/utils/lodash";
|
|
22
|
+
import base64 from "@poppinss/utils/base64";
|
|
23
|
+
import etag from "etag";
|
|
24
|
+
import vary from "vary";
|
|
25
|
+
import destroy from "destroy";
|
|
26
|
+
import { extname } from "node:path";
|
|
27
|
+
import { Buffer } from "node:buffer";
|
|
28
|
+
import { stat } from "node:fs/promises";
|
|
29
|
+
import { createReadStream } from "node:fs";
|
|
30
|
+
import contentDisposition from "content-disposition";
|
|
31
|
+
import { Readable } from "node:stream";
|
|
32
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
33
|
+
import { safeStringify } from "@poppinss/utils/json";
|
|
34
|
+
var Qs = class {
|
|
35
|
+
#config;
|
|
36
|
+
constructor(config) {
|
|
37
|
+
this.#config = config;
|
|
38
|
+
}
|
|
39
|
+
parse = (value) => {
|
|
40
|
+
return parse(value, this.#config.parse);
|
|
41
|
+
};
|
|
42
|
+
stringify = (value) => {
|
|
43
|
+
return stringify(value, this.#config.stringify);
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
var errors_exports = /* @__PURE__ */ __export({
|
|
47
|
+
E_CANNOT_LOOKUP_ROUTE: () => E_CANNOT_LOOKUP_ROUTE,
|
|
48
|
+
E_HTTP_EXCEPTION: () => E_HTTP_EXCEPTION,
|
|
49
|
+
E_HTTP_REQUEST_ABORTED: () => E_HTTP_REQUEST_ABORTED,
|
|
50
|
+
E_ROUTE_NOT_FOUND: () => E_ROUTE_NOT_FOUND
|
|
51
|
+
});
|
|
52
|
+
const E_ROUTE_NOT_FOUND = createError("Cannot %s:%s", "E_ROUTE_NOT_FOUND", 404);
|
|
53
|
+
const E_CANNOT_LOOKUP_ROUTE = createError("Cannot lookup route \"%s\"", "E_CANNOT_LOOKUP_ROUTE", 500);
|
|
54
|
+
const E_HTTP_EXCEPTION = class HttpException extends Exception {
|
|
55
|
+
body;
|
|
56
|
+
static code = "E_HTTP_EXCEPTION";
|
|
57
|
+
static invoke(body, status, code = "E_HTTP_EXCEPTION") {
|
|
58
|
+
if (body === null || body === void 0) {
|
|
59
|
+
const error$1 = new this("HTTP Exception", {
|
|
60
|
+
status,
|
|
61
|
+
code
|
|
62
|
+
});
|
|
63
|
+
error$1.body = "Internal server error";
|
|
64
|
+
return error$1;
|
|
65
|
+
}
|
|
66
|
+
if (typeof body === "object") {
|
|
67
|
+
const error$1 = new this(body.message || "HTTP Exception", {
|
|
68
|
+
status,
|
|
69
|
+
code
|
|
70
|
+
});
|
|
71
|
+
error$1.body = body;
|
|
72
|
+
return error$1;
|
|
73
|
+
}
|
|
74
|
+
const error = new this(body, {
|
|
75
|
+
status,
|
|
76
|
+
code
|
|
77
|
+
});
|
|
78
|
+
error.body = body;
|
|
79
|
+
return error;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const E_HTTP_REQUEST_ABORTED = class AbortException extends E_HTTP_EXCEPTION {
|
|
83
|
+
handle(error, ctx) {
|
|
84
|
+
ctx.response.status(error.status).send(error.body);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var Redirect = class {
|
|
88
|
+
#forwardQueryString = false;
|
|
89
|
+
#statusCode = 302;
|
|
90
|
+
#queryString = {};
|
|
91
|
+
#request;
|
|
92
|
+
#response;
|
|
93
|
+
#router;
|
|
94
|
+
#qs;
|
|
95
|
+
constructor(request, response, router, qs) {
|
|
96
|
+
this.#request = request;
|
|
97
|
+
this.#response = response;
|
|
98
|
+
this.#router = router;
|
|
99
|
+
this.#qs = qs;
|
|
100
|
+
}
|
|
101
|
+
#sendResponse(url, query) {
|
|
102
|
+
const stringified = this.#qs.stringify(query);
|
|
103
|
+
url = stringified ? `${url}?${stringified}` : url;
|
|
104
|
+
debug_default("redirecting to url \"%s\"", url);
|
|
105
|
+
this.#response.location(encodeUrl(url));
|
|
106
|
+
this.#response.safeStatus(this.#statusCode);
|
|
107
|
+
this.#response.type("text/plain; charset=utf-8");
|
|
108
|
+
this.#response.send(`Redirecting to ${url}`);
|
|
109
|
+
}
|
|
110
|
+
#getReferrerUrl() {
|
|
111
|
+
let url = this.#request.headers["referer"] || this.#request.headers["referrer"] || "/";
|
|
112
|
+
return Array.isArray(url) ? url[0] : url;
|
|
113
|
+
}
|
|
114
|
+
status(statusCode) {
|
|
115
|
+
this.#statusCode = statusCode;
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
clearQs() {
|
|
119
|
+
this.#forwardQueryString = false;
|
|
120
|
+
this.#queryString = {};
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
withQs(name, value) {
|
|
124
|
+
if (typeof name === "undefined") {
|
|
125
|
+
this.#forwardQueryString = true;
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
if (typeof name === "string") {
|
|
129
|
+
this.#queryString[name] = value;
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
Object.assign(this.#queryString, name);
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
back() {
|
|
136
|
+
let query = {};
|
|
137
|
+
const referrerUrl = this.#getReferrerUrl();
|
|
138
|
+
const url = safeDecodeURI(referrerUrl, false);
|
|
139
|
+
debug_default("referrer url \"%s\"", referrerUrl);
|
|
140
|
+
debug_default("referrer base url \"%s\"", url.pathname);
|
|
141
|
+
if (this.#forwardQueryString) query = this.#qs.parse(url.query || "");
|
|
142
|
+
Object.assign(query, this.#queryString);
|
|
143
|
+
this.#sendResponse(url.pathname || "", query);
|
|
144
|
+
}
|
|
145
|
+
toRoute(...args) {
|
|
146
|
+
const [identifier, params, options] = args;
|
|
147
|
+
if (options && options.qs) {
|
|
148
|
+
this.withQs(options.qs);
|
|
149
|
+
options.qs = void 0;
|
|
150
|
+
}
|
|
151
|
+
const url = this.#router.urlBuilder.urlFor(identifier, params, options);
|
|
152
|
+
return this.toPath(url);
|
|
153
|
+
}
|
|
154
|
+
toPath(url) {
|
|
155
|
+
let query = {};
|
|
156
|
+
if (this.#forwardQueryString) query = this.#qs.parse(safeDecodeURI(this.#request.url, false).query || "");
|
|
157
|
+
Object.assign(query, this.#queryString);
|
|
158
|
+
this.#sendResponse(url, query);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
function pack$2(value) {
|
|
162
|
+
if (value === void 0 || value === null) return null;
|
|
163
|
+
return base64.urlEncode(new MessageBuilder().build(value));
|
|
164
|
+
}
|
|
165
|
+
function canUnpack$2(encodedValue) {
|
|
166
|
+
return typeof encodedValue === "string";
|
|
167
|
+
}
|
|
168
|
+
function unpack$2(encodedValue) {
|
|
169
|
+
return new MessageBuilder().verify(base64.urlDecode(encodedValue, "utf-8", false));
|
|
170
|
+
}
|
|
171
|
+
function pack$1(key, value, encryption) {
|
|
172
|
+
if (value === void 0 || value === null) return null;
|
|
173
|
+
return `s:${encryption.verifier.sign(value, void 0, key)}`;
|
|
174
|
+
}
|
|
175
|
+
function canUnpack$1(signedValue) {
|
|
176
|
+
return typeof signedValue === "string" && signedValue.substring(0, 2) === "s:";
|
|
177
|
+
}
|
|
178
|
+
function unpack$1(key, signedValue, encryption) {
|
|
179
|
+
const value = signedValue.slice(2);
|
|
180
|
+
if (!value) return null;
|
|
181
|
+
return encryption.verifier.unsign(value, key);
|
|
182
|
+
}
|
|
183
|
+
function pack(key, value, encryption) {
|
|
184
|
+
if (value === void 0 || value === null) return null;
|
|
185
|
+
return `e:${encryption.encrypt(value, void 0, key)}`;
|
|
186
|
+
}
|
|
187
|
+
function canUnpack(encryptedValue) {
|
|
188
|
+
return typeof encryptedValue === "string" && encryptedValue.substring(0, 2) === "e:";
|
|
189
|
+
}
|
|
190
|
+
function unpack(key, encryptedValue, encryption) {
|
|
191
|
+
const value = encryptedValue.slice(2);
|
|
192
|
+
if (!value) return null;
|
|
193
|
+
return encryption.decrypt(value, key);
|
|
194
|
+
}
|
|
195
|
+
var CookieClient = class {
|
|
196
|
+
#encryption;
|
|
197
|
+
constructor(encryption) {
|
|
198
|
+
this.#encryption = encryption;
|
|
199
|
+
}
|
|
200
|
+
encrypt(key, value) {
|
|
201
|
+
return pack(key, value, this.#encryption);
|
|
202
|
+
}
|
|
203
|
+
sign(key, value) {
|
|
204
|
+
return pack$1(key, value, this.#encryption);
|
|
205
|
+
}
|
|
206
|
+
encode(_, value, stringify$1 = true) {
|
|
207
|
+
return stringify$1 ? pack$2(value) : value;
|
|
208
|
+
}
|
|
209
|
+
unsign(key, value) {
|
|
210
|
+
return canUnpack$1(value) ? unpack$1(key, value, this.#encryption) : null;
|
|
211
|
+
}
|
|
212
|
+
decrypt(key, value) {
|
|
213
|
+
return canUnpack(value) ? unpack(key, value, this.#encryption) : null;
|
|
214
|
+
}
|
|
215
|
+
decode(_, value, stringified = true) {
|
|
216
|
+
if (!stringified) return value;
|
|
217
|
+
return canUnpack$2(value) ? unpack$2(value) : null;
|
|
218
|
+
}
|
|
219
|
+
parse(key, value) {
|
|
220
|
+
if (canUnpack$1(value)) return unpack$1(key, value, this.#encryption);
|
|
221
|
+
if (canUnpack(value)) return unpack(key, value, this.#encryption);
|
|
222
|
+
if (canUnpack$2(value)) return unpack$2(value);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
var CookieParser = class {
|
|
226
|
+
#client;
|
|
227
|
+
#cachedCookies = {
|
|
228
|
+
signedCookies: {},
|
|
229
|
+
plainCookies: {},
|
|
230
|
+
encryptedCookies: {}
|
|
231
|
+
};
|
|
232
|
+
#cookies;
|
|
233
|
+
constructor(cookieHeader, encryption) {
|
|
234
|
+
this.#client = new CookieClient(encryption);
|
|
235
|
+
this.#cookies = this.#parse(cookieHeader);
|
|
236
|
+
}
|
|
237
|
+
#parse(cookieHeader) {
|
|
238
|
+
if (!cookieHeader) return {};
|
|
239
|
+
return parse$1(cookieHeader);
|
|
240
|
+
}
|
|
241
|
+
decode(key, stringified = true) {
|
|
242
|
+
const value = this.#cookies[key];
|
|
243
|
+
if (value === null || value === void 0) return null;
|
|
244
|
+
const cache = this.#cachedCookies.plainCookies;
|
|
245
|
+
if (cache[key] !== void 0) return cache[key];
|
|
246
|
+
const parsed = this.#client.decode(key, value, stringified);
|
|
247
|
+
if (parsed !== null) cache[key] = parsed;
|
|
248
|
+
return parsed;
|
|
249
|
+
}
|
|
250
|
+
unsign(key) {
|
|
251
|
+
const value = this.#cookies[key];
|
|
252
|
+
if (value === null || value === void 0) return null;
|
|
253
|
+
const cache = this.#cachedCookies.signedCookies;
|
|
254
|
+
if (cache[key] !== void 0) return cache[key];
|
|
255
|
+
const parsed = this.#client.unsign(key, value);
|
|
256
|
+
if (parsed !== null) cache[key] = parsed;
|
|
257
|
+
return parsed;
|
|
258
|
+
}
|
|
259
|
+
decrypt(key) {
|
|
260
|
+
const value = this.#cookies[key];
|
|
261
|
+
if (value === null || value === void 0) return null;
|
|
262
|
+
const cache = this.#cachedCookies.encryptedCookies;
|
|
263
|
+
if (cache[key] !== void 0) return cache[key];
|
|
264
|
+
const parsed = this.#client.decrypt(key, value);
|
|
265
|
+
if (parsed !== null) cache[key] = parsed;
|
|
266
|
+
return parsed;
|
|
267
|
+
}
|
|
268
|
+
list() {
|
|
269
|
+
return this.#cookies;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
var HttpRequest = class extends Macroable {
|
|
273
|
+
#qsParser;
|
|
274
|
+
#encryption;
|
|
275
|
+
#config;
|
|
276
|
+
#requestBody = {};
|
|
277
|
+
#requestData = {};
|
|
278
|
+
#originalRequestData = {};
|
|
279
|
+
#requestQs = {};
|
|
280
|
+
#rawRequestBody;
|
|
281
|
+
#lazyAccepts;
|
|
282
|
+
#cookieParser;
|
|
283
|
+
parsedUrl;
|
|
284
|
+
ctx;
|
|
285
|
+
constructor(request, response, encryption, config, qsParser) {
|
|
286
|
+
super();
|
|
287
|
+
this.request = request;
|
|
288
|
+
this.response = response;
|
|
289
|
+
this.#qsParser = qsParser;
|
|
290
|
+
this.#config = config;
|
|
291
|
+
this.#encryption = encryption;
|
|
292
|
+
this.parsedUrl = safeDecodeURI(request.url, false);
|
|
293
|
+
this.#parseQueryString();
|
|
294
|
+
}
|
|
295
|
+
#parseQueryString() {
|
|
296
|
+
if (this.parsedUrl.query) {
|
|
297
|
+
this.updateQs(this.#qsParser.parse(this.parsedUrl.query));
|
|
298
|
+
this.#originalRequestData = { ...this.#requestData };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
#initiateCookieParser() {
|
|
302
|
+
if (!this.#cookieParser) this.#cookieParser = new CookieParser(this.header("cookie"), this.#encryption);
|
|
303
|
+
}
|
|
304
|
+
#initiateAccepts() {
|
|
305
|
+
this.#lazyAccepts = this.#lazyAccepts || accepts(this.request);
|
|
306
|
+
}
|
|
307
|
+
id() {
|
|
308
|
+
let requestId = this.header("x-request-id");
|
|
309
|
+
if (!requestId && this.#config.generateRequestId) {
|
|
310
|
+
requestId = this.#config.createRequestId();
|
|
311
|
+
this.request.headers["x-request-id"] = requestId;
|
|
312
|
+
}
|
|
313
|
+
return requestId;
|
|
314
|
+
}
|
|
315
|
+
setInitialBody(body) {
|
|
316
|
+
if (this.#originalRequestData && Object.isFrozen(this.#originalRequestData)) throw new Error("Cannot re-set initial body. Use \"request.updateBody\" instead");
|
|
317
|
+
this.updateBody(body);
|
|
318
|
+
this.#originalRequestData = Object.freeze(lodash.cloneDeep(this.#requestData));
|
|
319
|
+
}
|
|
320
|
+
updateBody(body) {
|
|
321
|
+
this.#requestBody = body;
|
|
322
|
+
this.#requestData = {
|
|
323
|
+
...this.#requestBody,
|
|
324
|
+
...this.#requestQs
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
updateRawBody(rawBody) {
|
|
328
|
+
this.#rawRequestBody = rawBody;
|
|
329
|
+
}
|
|
330
|
+
updateQs(data) {
|
|
331
|
+
this.#requestQs = data;
|
|
332
|
+
this.#requestData = {
|
|
333
|
+
...this.#requestBody,
|
|
334
|
+
...this.#requestQs
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
params() {
|
|
338
|
+
return this.ctx?.params || {};
|
|
339
|
+
}
|
|
340
|
+
qs() {
|
|
341
|
+
return this.#requestQs;
|
|
342
|
+
}
|
|
343
|
+
body() {
|
|
344
|
+
return this.#requestBody;
|
|
345
|
+
}
|
|
346
|
+
all() {
|
|
347
|
+
return this.#requestData;
|
|
348
|
+
}
|
|
349
|
+
original() {
|
|
350
|
+
return this.#originalRequestData;
|
|
351
|
+
}
|
|
352
|
+
raw() {
|
|
353
|
+
return this.#rawRequestBody || null;
|
|
354
|
+
}
|
|
355
|
+
input(key, defaultValue) {
|
|
356
|
+
return lodash.get(this.#requestData, key, defaultValue);
|
|
357
|
+
}
|
|
358
|
+
param(key, defaultValue) {
|
|
359
|
+
return lodash.get(this.params(), key, defaultValue);
|
|
360
|
+
}
|
|
361
|
+
except(keys) {
|
|
362
|
+
return lodash.omit(this.#requestData, keys);
|
|
363
|
+
}
|
|
364
|
+
only(keys) {
|
|
365
|
+
return lodash.pick(this.#requestData, keys);
|
|
366
|
+
}
|
|
367
|
+
intended() {
|
|
368
|
+
return this.request.method;
|
|
369
|
+
}
|
|
370
|
+
method() {
|
|
371
|
+
if (this.#config.allowMethodSpoofing && this.intended() === "POST") return this.input("_method", this.intended()).toUpperCase();
|
|
372
|
+
return this.intended();
|
|
373
|
+
}
|
|
374
|
+
headers() {
|
|
375
|
+
return this.request.headers;
|
|
376
|
+
}
|
|
377
|
+
header(key, defaultValue) {
|
|
378
|
+
key = key.toLowerCase();
|
|
379
|
+
const headers = this.headers();
|
|
380
|
+
switch (key) {
|
|
381
|
+
case "referer":
|
|
382
|
+
case "referrer": return headers.referrer || headers.referer || defaultValue;
|
|
383
|
+
default: return headers[key] || defaultValue;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
ip() {
|
|
387
|
+
const ipFn = this.#config.getIp;
|
|
388
|
+
if (typeof ipFn === "function") return ipFn(this);
|
|
389
|
+
return proxyAddr(this.request, this.#config.trustProxy);
|
|
390
|
+
}
|
|
391
|
+
ips() {
|
|
392
|
+
return proxyAddr.all(this.request, this.#config.trustProxy);
|
|
393
|
+
}
|
|
394
|
+
protocol() {
|
|
395
|
+
if ("encrypted" in this.request.socket) return "https";
|
|
396
|
+
if (trustProxy(this.request.socket.remoteAddress, this.#config.trustProxy)) {
|
|
397
|
+
const forwardedProtocol = this.header("X-Forwarded-Proto");
|
|
398
|
+
return forwardedProtocol ? forwardedProtocol.split(/\s*,\s*/)[0] : "http";
|
|
399
|
+
}
|
|
400
|
+
return "http";
|
|
401
|
+
}
|
|
402
|
+
secure() {
|
|
403
|
+
return this.protocol() === "https";
|
|
404
|
+
}
|
|
405
|
+
host() {
|
|
406
|
+
let host = this.header("host");
|
|
407
|
+
if (trustProxy(this.request.socket.remoteAddress, this.#config.trustProxy)) host = this.header("X-Forwarded-Host") || host;
|
|
408
|
+
if (!host) return null;
|
|
409
|
+
return host;
|
|
410
|
+
}
|
|
411
|
+
hostname() {
|
|
412
|
+
const host = this.host();
|
|
413
|
+
if (!host) return null;
|
|
414
|
+
const offset = host[0] === "[" ? host.indexOf("]") + 1 : 0;
|
|
415
|
+
const index = host.indexOf(":", offset);
|
|
416
|
+
return index !== -1 ? host.substring(0, index) : host;
|
|
417
|
+
}
|
|
418
|
+
subdomains() {
|
|
419
|
+
const hostname = this.hostname();
|
|
420
|
+
if (!hostname || isIP(hostname)) return [];
|
|
421
|
+
const offset = this.#config.subdomainOffset;
|
|
422
|
+
const subdomains = hostname.split(".").reverse().slice(offset);
|
|
423
|
+
if (subdomains[subdomains.length - 1] === "www") subdomains.splice(subdomains.length - 1, 1);
|
|
424
|
+
return subdomains;
|
|
425
|
+
}
|
|
426
|
+
ajax() {
|
|
427
|
+
return this.header("X-Requested-With", "").toLowerCase() === "xmlhttprequest";
|
|
428
|
+
}
|
|
429
|
+
pjax() {
|
|
430
|
+
return !!this.header("X-Pjax");
|
|
431
|
+
}
|
|
432
|
+
url(includeQueryString) {
|
|
433
|
+
const pathname = this.parsedUrl.pathname;
|
|
434
|
+
return includeQueryString && this.parsedUrl.query ? `${pathname}?${this.parsedUrl.query}` : pathname;
|
|
435
|
+
}
|
|
436
|
+
completeUrl(includeQueryString) {
|
|
437
|
+
return `${this.protocol()}://${this.host()}${this.url(includeQueryString)}`;
|
|
438
|
+
}
|
|
439
|
+
matchesRoute(routeIdentifier) {
|
|
440
|
+
if (!this.ctx || !this.ctx.route) return false;
|
|
441
|
+
const route = this.ctx.route;
|
|
442
|
+
return !!(Array.isArray(routeIdentifier) ? routeIdentifier : [routeIdentifier]).find((identifier) => {
|
|
443
|
+
if (route.pattern === identifier || route.name === identifier) return true;
|
|
444
|
+
if (typeof route.handler === "function") return false;
|
|
445
|
+
return route.handler.reference === identifier;
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
is(types) {
|
|
449
|
+
return typeIs(this.request, types) || null;
|
|
450
|
+
}
|
|
451
|
+
accepts(types) {
|
|
452
|
+
this.#initiateAccepts();
|
|
453
|
+
return this.#lazyAccepts.type(types) || null;
|
|
454
|
+
}
|
|
455
|
+
types() {
|
|
456
|
+
this.#initiateAccepts();
|
|
457
|
+
return this.#lazyAccepts.types();
|
|
458
|
+
}
|
|
459
|
+
language(languages) {
|
|
460
|
+
this.#initiateAccepts();
|
|
461
|
+
return this.#lazyAccepts.language(languages) || null;
|
|
462
|
+
}
|
|
463
|
+
languages() {
|
|
464
|
+
this.#initiateAccepts();
|
|
465
|
+
return this.#lazyAccepts.languages();
|
|
466
|
+
}
|
|
467
|
+
charset(charsets) {
|
|
468
|
+
this.#initiateAccepts();
|
|
469
|
+
return this.#lazyAccepts.charset(charsets) || null;
|
|
470
|
+
}
|
|
471
|
+
charsets() {
|
|
472
|
+
this.#initiateAccepts();
|
|
473
|
+
return this.#lazyAccepts.charsets();
|
|
474
|
+
}
|
|
475
|
+
encoding(encodings) {
|
|
476
|
+
this.#initiateAccepts();
|
|
477
|
+
return this.#lazyAccepts.encoding(encodings) || null;
|
|
478
|
+
}
|
|
479
|
+
encodings() {
|
|
480
|
+
this.#initiateAccepts();
|
|
481
|
+
return this.#lazyAccepts.encodings();
|
|
482
|
+
}
|
|
483
|
+
hasBody() {
|
|
484
|
+
return typeIs.hasBody(this.request);
|
|
485
|
+
}
|
|
486
|
+
fresh() {
|
|
487
|
+
if (["GET", "HEAD"].indexOf(this.intended()) === -1) return false;
|
|
488
|
+
const status = this.response.statusCode;
|
|
489
|
+
if (status >= 200 && status < 300 || status === 304) return fresh(this.headers(), this.response.getHeaders());
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
stale() {
|
|
493
|
+
return !this.fresh();
|
|
494
|
+
}
|
|
495
|
+
cookiesList() {
|
|
496
|
+
this.#initiateCookieParser();
|
|
497
|
+
return this.#cookieParser.list();
|
|
498
|
+
}
|
|
499
|
+
cookie(key, defaultValue) {
|
|
500
|
+
this.#initiateCookieParser();
|
|
501
|
+
return this.#cookieParser.unsign(key) || defaultValue;
|
|
502
|
+
}
|
|
503
|
+
encryptedCookie(key, defaultValue) {
|
|
504
|
+
this.#initiateCookieParser();
|
|
505
|
+
return this.#cookieParser.decrypt(key) || defaultValue;
|
|
506
|
+
}
|
|
507
|
+
plainCookie(key, defaultValueOrOptions, encoded) {
|
|
508
|
+
this.#initiateCookieParser();
|
|
509
|
+
if (is.object(defaultValueOrOptions)) return this.#cookieParser.decode(key, defaultValueOrOptions?.encoded) || defaultValueOrOptions.defaultValue;
|
|
510
|
+
return this.#cookieParser.decode(key, encoded) || defaultValueOrOptions;
|
|
511
|
+
}
|
|
512
|
+
hasValidSignature(purpose) {
|
|
513
|
+
const { signature, ...rest } = this.qs();
|
|
514
|
+
if (!signature) return false;
|
|
515
|
+
const signedUrl = this.#encryption.verifier.unsign(signature, purpose);
|
|
516
|
+
if (!signedUrl) return false;
|
|
517
|
+
const queryString = this.#qsParser.stringify(rest);
|
|
518
|
+
return queryString ? safeEqual(signedUrl, `${this.url()}?${queryString}`) : safeEqual(signedUrl, this.url());
|
|
519
|
+
}
|
|
520
|
+
serialize() {
|
|
521
|
+
return {
|
|
522
|
+
id: this.id(),
|
|
523
|
+
url: this.url(),
|
|
524
|
+
query: this.parsedUrl.query,
|
|
525
|
+
body: this.all(),
|
|
526
|
+
params: this.params(),
|
|
527
|
+
headers: this.headers(),
|
|
528
|
+
method: this.method(),
|
|
529
|
+
protocol: this.protocol(),
|
|
530
|
+
cookies: this.cookiesList(),
|
|
531
|
+
hostname: this.hostname(),
|
|
532
|
+
ip: this.ip(),
|
|
533
|
+
subdomains: this.ctx?.subdomains || {}
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
toJSON() {
|
|
537
|
+
return this.serialize();
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
var RoutesStore = class {
|
|
541
|
+
usingDomains = false;
|
|
542
|
+
tree = {
|
|
543
|
+
tokens: [],
|
|
544
|
+
domains: {}
|
|
545
|
+
};
|
|
546
|
+
#getDomainNode(domain) {
|
|
547
|
+
if (!this.tree.domains[domain]) {
|
|
548
|
+
this.tree.tokens.push(parseRoute(domain));
|
|
549
|
+
this.tree.domains[domain] = {};
|
|
550
|
+
}
|
|
551
|
+
return this.tree.domains[domain];
|
|
552
|
+
}
|
|
553
|
+
#getMethodNode(domain, method) {
|
|
554
|
+
const domainNode = this.#getDomainNode(domain);
|
|
555
|
+
if (!domainNode[method]) domainNode[method] = {
|
|
556
|
+
tokens: [],
|
|
557
|
+
routes: {},
|
|
558
|
+
routeKeys: {}
|
|
559
|
+
};
|
|
560
|
+
return domainNode[method];
|
|
561
|
+
}
|
|
562
|
+
#collectRouteParams(route, tokens) {
|
|
563
|
+
const collectedParams = /* @__PURE__ */ new Set();
|
|
564
|
+
for (let token of tokens) if ([1, 3].includes(token.type)) if (collectedParams.has(token.val)) throw new RuntimeException(`Duplicate param "${token.val}" found in "${route.pattern}"`);
|
|
565
|
+
else collectedParams.add(token.val);
|
|
566
|
+
const params = [...collectedParams];
|
|
567
|
+
collectedParams.clear();
|
|
568
|
+
return params;
|
|
569
|
+
}
|
|
570
|
+
#registerRoute(domain, method, tokens, route) {
|
|
571
|
+
const methodRoutes = this.#getMethodNode(domain, method);
|
|
572
|
+
if (methodRoutes.routes[route.pattern]) throw new RuntimeException(`Duplicate route found. "${method}: ${route.pattern}" route already exists`);
|
|
573
|
+
if (debug_default.enabled) {
|
|
574
|
+
debug_default("registering route to the store %O", route);
|
|
575
|
+
debug_default("route middleware %O", route.middleware.all().entries());
|
|
576
|
+
}
|
|
577
|
+
methodRoutes.tokens.push(tokens);
|
|
578
|
+
methodRoutes.routes[route.pattern] = route;
|
|
579
|
+
methodRoutes.routeKeys[route.pattern] = domain !== "root" ? `${domain}-${method}-${route.pattern}` : `${method}-${route.pattern}`;
|
|
580
|
+
}
|
|
581
|
+
add(route) {
|
|
582
|
+
if (route.domain !== "root") this.usingDomains = true;
|
|
583
|
+
const routeNode = { ...route };
|
|
584
|
+
routeNode.meta.params = this.#collectRouteParams(routeNode, route.tokens);
|
|
585
|
+
route.methods.forEach((method) => {
|
|
586
|
+
this.#registerRoute(route.domain, method, route.tokens, routeNode);
|
|
587
|
+
});
|
|
588
|
+
return this;
|
|
589
|
+
}
|
|
590
|
+
match(url, method, shouldDecodeParam, domain) {
|
|
591
|
+
const domainName = domain?.tokens[0]?.old || "root";
|
|
592
|
+
if (!this.tree.domains[domainName]) return null;
|
|
593
|
+
const matchedMethod = this.tree.domains[domainName][method];
|
|
594
|
+
if (!matchedMethod) return null;
|
|
595
|
+
const matchedRoute = matchit.match(url, matchedMethod.tokens);
|
|
596
|
+
if (!matchedRoute.length) return null;
|
|
597
|
+
const route = matchedMethod.routes[matchedRoute[0].old];
|
|
598
|
+
return {
|
|
599
|
+
route,
|
|
600
|
+
routeKey: matchedMethod.routeKeys[route.pattern],
|
|
601
|
+
params: matchit.exec(url, matchedRoute, shouldDecodeParam),
|
|
602
|
+
subdomains: domain?.hostname ? matchit.exec(domain.hostname, domain.tokens) : {}
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
matchDomain(hostname) {
|
|
606
|
+
if (!hostname || !this.usingDomains) return [];
|
|
607
|
+
return matchit.match(hostname, this.tree.tokens);
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
var UrlBuilder = class {
|
|
611
|
+
#params = {};
|
|
612
|
+
#qs = {};
|
|
613
|
+
#shouldPerformLookup = true;
|
|
614
|
+
#baseUrl;
|
|
615
|
+
#router;
|
|
616
|
+
#domain;
|
|
617
|
+
constructor(router, domain) {
|
|
618
|
+
this.#router = router;
|
|
619
|
+
this.#domain = domain;
|
|
620
|
+
}
|
|
621
|
+
prefixUrl(url) {
|
|
622
|
+
this.#baseUrl = url;
|
|
623
|
+
return this;
|
|
624
|
+
}
|
|
625
|
+
disableRouteLookup() {
|
|
626
|
+
this.#shouldPerformLookup = false;
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
629
|
+
qs(queryString) {
|
|
630
|
+
if (!queryString) return this;
|
|
631
|
+
this.#qs = queryString;
|
|
632
|
+
return this;
|
|
633
|
+
}
|
|
634
|
+
params(params) {
|
|
635
|
+
if (!params) return this;
|
|
636
|
+
this.#params = params;
|
|
637
|
+
return this;
|
|
638
|
+
}
|
|
639
|
+
make(identifier) {
|
|
640
|
+
return this.#router.makeUrl(identifier, this.#params, {
|
|
641
|
+
prefixUrl: this.#baseUrl,
|
|
642
|
+
disableRouteLookup: !this.#shouldPerformLookup,
|
|
643
|
+
domain: this.#domain,
|
|
644
|
+
qs: this.#qs
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
makeSigned(identifier, options) {
|
|
648
|
+
return this.#router.makeSignedUrl(identifier, this.#params, {
|
|
649
|
+
prefixUrl: this.#baseUrl,
|
|
650
|
+
disableRouteLookup: !this.#shouldPerformLookup,
|
|
651
|
+
domain: this.#domain,
|
|
652
|
+
qs: this.#qs,
|
|
653
|
+
...options
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
var RouteMatchers = class extends Macroable {
|
|
658
|
+
number() {
|
|
659
|
+
return {
|
|
660
|
+
match: /^[0-9]+$/,
|
|
661
|
+
cast: (value) => Number(value)
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
uuid() {
|
|
665
|
+
return {
|
|
666
|
+
match: /^[0-9a-zA-F]{8}-[0-9a-zA-F]{4}-[0-9a-zA-F]{4}-[0-9a-zA-F]{4}-[0-9a-zA-F]{12}$/,
|
|
667
|
+
cast: (value) => value.toLowerCase()
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
slug() {
|
|
671
|
+
return { match: /^[^\s-_](?!.*?[-_]{2,})([a-z0-9-\\]{1,})[^\s]*[^-_\s]$/ };
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
function middlewareReferenceBuilder(name, middleware) {
|
|
675
|
+
const handler = moduleImporter(middleware, "handle").toHandleMethod();
|
|
676
|
+
return function(...args) {
|
|
677
|
+
return {
|
|
678
|
+
...handler,
|
|
679
|
+
name,
|
|
680
|
+
reference: middleware,
|
|
681
|
+
args: args[0]
|
|
682
|
+
};
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function defineNamedMiddleware(collection) {
|
|
686
|
+
return Object.keys(collection).reduce((result, key) => {
|
|
687
|
+
result[key] = middlewareReferenceBuilder(key, collection[key]);
|
|
688
|
+
return result;
|
|
689
|
+
}, {});
|
|
690
|
+
}
|
|
691
|
+
function createSignedUrlBuilder(router, encryption, searchParamsStringifier) {
|
|
692
|
+
let domainsList;
|
|
693
|
+
function createSignedUrlForRoute(identifier, params, options, method) {
|
|
694
|
+
if (!domainsList) domainsList = Object.keys(router.toJSON()).filter((domain$1) => domain$1 !== "root");
|
|
695
|
+
const domain = domainsList.find((name) => identifier.startsWith(`${name}@`));
|
|
696
|
+
const routeIdentifier = domain ? identifier.replace(/* @__PURE__ */ new RegExp(`^${domain}@`), "") : identifier;
|
|
697
|
+
const route = router.findOrFail(routeIdentifier, domain, method, true);
|
|
698
|
+
return createSignedURL(route.name ?? route.pattern, route.tokens, searchParamsStringifier, encryption, params, options);
|
|
699
|
+
}
|
|
700
|
+
const signedRoute = function route(...[identifier, params, options]) {
|
|
701
|
+
return createSignedUrlForRoute(identifier, params, options);
|
|
702
|
+
};
|
|
703
|
+
signedRoute.get = function routeGet(...[identifier, params, options]) {
|
|
704
|
+
const method = "GET";
|
|
705
|
+
const url = createSignedUrlForRoute(identifier, params, options, method);
|
|
706
|
+
return {
|
|
707
|
+
url,
|
|
708
|
+
method,
|
|
709
|
+
toString() {
|
|
710
|
+
return url;
|
|
711
|
+
},
|
|
712
|
+
form: {
|
|
713
|
+
action: url,
|
|
714
|
+
method
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
};
|
|
718
|
+
signedRoute.post = function routePost(...[identifier, params, options]) {
|
|
719
|
+
const method = "POST";
|
|
720
|
+
const url = createSignedUrlForRoute(identifier, params, options, method);
|
|
721
|
+
return {
|
|
722
|
+
url,
|
|
723
|
+
method,
|
|
724
|
+
toString() {
|
|
725
|
+
return url;
|
|
726
|
+
},
|
|
727
|
+
form: {
|
|
728
|
+
action: url,
|
|
729
|
+
method
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
};
|
|
733
|
+
signedRoute.put = function routePut(...[identifier, params, options]) {
|
|
734
|
+
const method = "PUT";
|
|
735
|
+
const url = createSignedUrlForRoute(identifier, params, options, method);
|
|
736
|
+
return {
|
|
737
|
+
url,
|
|
738
|
+
method,
|
|
739
|
+
toString() {
|
|
740
|
+
return url;
|
|
741
|
+
},
|
|
742
|
+
form: {
|
|
743
|
+
action: url,
|
|
744
|
+
method
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
};
|
|
748
|
+
signedRoute.patch = function routePatch(...[identifier, params, options]) {
|
|
749
|
+
const method = "PATCH";
|
|
750
|
+
const url = createSignedUrlForRoute(identifier, params, options, method);
|
|
751
|
+
return {
|
|
752
|
+
url,
|
|
753
|
+
method,
|
|
754
|
+
toString() {
|
|
755
|
+
return url;
|
|
756
|
+
},
|
|
757
|
+
form: {
|
|
758
|
+
action: url,
|
|
759
|
+
method
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
};
|
|
763
|
+
signedRoute.delete = function routeDelete(...[identifier, params, options]) {
|
|
764
|
+
const method = "DELETE";
|
|
765
|
+
const url = createSignedUrlForRoute(identifier, params, options, method);
|
|
766
|
+
return {
|
|
767
|
+
url,
|
|
768
|
+
method,
|
|
769
|
+
toString() {
|
|
770
|
+
return url;
|
|
771
|
+
},
|
|
772
|
+
form: {
|
|
773
|
+
action: url,
|
|
774
|
+
method
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
};
|
|
778
|
+
signedRoute.method = function routeGet(method, ...[identifier, params, options]) {
|
|
779
|
+
const url = createSignedUrlForRoute(identifier, params, options, method);
|
|
780
|
+
return {
|
|
781
|
+
url,
|
|
782
|
+
method,
|
|
783
|
+
toString() {
|
|
784
|
+
return url;
|
|
785
|
+
},
|
|
786
|
+
form: {
|
|
787
|
+
action: url,
|
|
788
|
+
method
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
};
|
|
792
|
+
return signedRoute;
|
|
793
|
+
}
|
|
794
|
+
var Router = class {
|
|
795
|
+
#commited = false;
|
|
796
|
+
#app;
|
|
797
|
+
#store = new RoutesStore();
|
|
798
|
+
#encryption;
|
|
799
|
+
#globalMatchers = {};
|
|
800
|
+
#middleware = [];
|
|
801
|
+
#openedGroups = [];
|
|
802
|
+
#routesToBeCommitted = [];
|
|
803
|
+
usingDomains = false;
|
|
804
|
+
matchers = new RouteMatchers();
|
|
805
|
+
get commited() {
|
|
806
|
+
return this.#commited;
|
|
807
|
+
}
|
|
808
|
+
qs;
|
|
809
|
+
urlBuilder;
|
|
810
|
+
routes = {};
|
|
811
|
+
constructor(app, encryption, qsParser) {
|
|
812
|
+
this.#app = app;
|
|
813
|
+
this.#encryption = encryption;
|
|
814
|
+
this.qs = qsParser;
|
|
815
|
+
this.urlBuilder = {
|
|
816
|
+
urlFor: createUrlBuilder(() => this.toJSON(), this.qs.stringify),
|
|
817
|
+
signedUrlFor: createSignedUrlBuilder(this, this.#encryption, this.qs.stringify)
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
register(route) {
|
|
821
|
+
this.routes[route.domain] = this.routes[route.domain] || [];
|
|
822
|
+
this.routes[route.domain].push(route);
|
|
823
|
+
}
|
|
824
|
+
#pushToRoutes(entity) {
|
|
825
|
+
const openedGroup = this.#openedGroups[this.#openedGroups.length - 1];
|
|
826
|
+
if (openedGroup) {
|
|
827
|
+
openedGroup.routes.push(entity);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
this.#routesToBeCommitted.push(entity);
|
|
831
|
+
}
|
|
832
|
+
parsePattern(pattern, matchers) {
|
|
833
|
+
return parseRoute(pattern, matchers);
|
|
834
|
+
}
|
|
835
|
+
use(middleware) {
|
|
836
|
+
middleware.forEach((one) => this.#middleware.push({
|
|
837
|
+
reference: one,
|
|
838
|
+
...moduleImporter(one, "handle").toHandleMethod()
|
|
839
|
+
}));
|
|
840
|
+
return this;
|
|
841
|
+
}
|
|
842
|
+
named(collection) {
|
|
843
|
+
return defineNamedMiddleware(collection);
|
|
844
|
+
}
|
|
845
|
+
route(pattern, methods, handler) {
|
|
846
|
+
const route = new Route(this.#app, this.#middleware, {
|
|
847
|
+
pattern,
|
|
848
|
+
methods,
|
|
849
|
+
handler,
|
|
850
|
+
globalMatchers: this.#globalMatchers
|
|
851
|
+
});
|
|
852
|
+
this.#pushToRoutes(route);
|
|
853
|
+
return route;
|
|
854
|
+
}
|
|
855
|
+
any(pattern, handler) {
|
|
856
|
+
return this.route(pattern, [
|
|
857
|
+
"HEAD",
|
|
858
|
+
"OPTIONS",
|
|
859
|
+
"GET",
|
|
860
|
+
"POST",
|
|
861
|
+
"PUT",
|
|
862
|
+
"PATCH",
|
|
863
|
+
"DELETE"
|
|
864
|
+
], handler);
|
|
865
|
+
}
|
|
866
|
+
get(pattern, handler) {
|
|
867
|
+
return this.route(pattern, ["GET", "HEAD"], handler);
|
|
868
|
+
}
|
|
869
|
+
post(pattern, handler) {
|
|
870
|
+
return this.route(pattern, ["POST"], handler);
|
|
871
|
+
}
|
|
872
|
+
put(pattern, handler) {
|
|
873
|
+
return this.route(pattern, ["PUT"], handler);
|
|
874
|
+
}
|
|
875
|
+
patch(pattern, handler) {
|
|
876
|
+
return this.route(pattern, ["PATCH"], handler);
|
|
877
|
+
}
|
|
878
|
+
delete(pattern, handler) {
|
|
879
|
+
return this.route(pattern, ["DELETE"], handler);
|
|
880
|
+
}
|
|
881
|
+
group(callback) {
|
|
882
|
+
const group = new RouteGroup([]);
|
|
883
|
+
this.#pushToRoutes(group);
|
|
884
|
+
this.#openedGroups.push(group);
|
|
885
|
+
callback();
|
|
886
|
+
this.#openedGroups.pop();
|
|
887
|
+
return group;
|
|
888
|
+
}
|
|
889
|
+
resource(resource, controller) {
|
|
890
|
+
const resourceInstance = new RouteResource(this.#app, this.#middleware, {
|
|
891
|
+
resource,
|
|
892
|
+
controller,
|
|
893
|
+
shallow: false,
|
|
894
|
+
globalMatchers: this.#globalMatchers
|
|
895
|
+
});
|
|
896
|
+
this.#pushToRoutes(resourceInstance);
|
|
897
|
+
return resourceInstance;
|
|
898
|
+
}
|
|
899
|
+
shallowResource(resource, controller) {
|
|
900
|
+
const resourceInstance = new RouteResource(this.#app, this.#middleware, {
|
|
901
|
+
resource,
|
|
902
|
+
controller,
|
|
903
|
+
shallow: true,
|
|
904
|
+
globalMatchers: this.#globalMatchers
|
|
905
|
+
});
|
|
906
|
+
this.#pushToRoutes(resourceInstance);
|
|
907
|
+
return resourceInstance;
|
|
908
|
+
}
|
|
909
|
+
on(pattern) {
|
|
910
|
+
const briskRoute = new BriskRoute(this.#app, this.#middleware, {
|
|
911
|
+
pattern,
|
|
912
|
+
globalMatchers: this.#globalMatchers
|
|
913
|
+
});
|
|
914
|
+
this.#pushToRoutes(briskRoute);
|
|
915
|
+
return briskRoute;
|
|
916
|
+
}
|
|
917
|
+
where(param, matcher) {
|
|
918
|
+
if (typeof matcher === "string") this.#globalMatchers[param] = { match: new RegExp(matcher) };
|
|
919
|
+
else if (is.regExp(matcher)) this.#globalMatchers[param] = { match: matcher };
|
|
920
|
+
else this.#globalMatchers[param] = matcher;
|
|
921
|
+
return this;
|
|
922
|
+
}
|
|
923
|
+
commit() {
|
|
924
|
+
if (this.#commited) return;
|
|
925
|
+
debug_default("Committing routes to the routes store");
|
|
926
|
+
const routeNamesByDomain = /* @__PURE__ */ new Map();
|
|
927
|
+
toRoutesJSON(this.#routesToBeCommitted).forEach((route) => {
|
|
928
|
+
if (!routeNamesByDomain.has(route.domain)) routeNamesByDomain.set(route.domain, /* @__PURE__ */ new Set());
|
|
929
|
+
const routeNames = routeNamesByDomain.get(route.domain);
|
|
930
|
+
if (route.name && routeNames.has(route.name)) throw new RuntimeException(`A route with name "${route.name}" already exists. It may happen when two routes use the same controller, so make sure to give explicit names to these routes`);
|
|
931
|
+
if (route.name) routeNames.add(route.name);
|
|
932
|
+
this.register(route);
|
|
933
|
+
this.#store.add(route);
|
|
934
|
+
});
|
|
935
|
+
routeNamesByDomain.clear();
|
|
936
|
+
this.usingDomains = this.#store.usingDomains;
|
|
937
|
+
this.#routesToBeCommitted = [];
|
|
938
|
+
this.#globalMatchers = {};
|
|
939
|
+
this.#middleware = [];
|
|
940
|
+
this.#openedGroups = [];
|
|
941
|
+
this.#commited = true;
|
|
942
|
+
}
|
|
943
|
+
find(routeIdentifier, domain, method, disableLegacyLookup) {
|
|
944
|
+
return findRoute(this.routes, routeIdentifier, domain, method, disableLegacyLookup);
|
|
945
|
+
}
|
|
946
|
+
findOrFail(routeIdentifier, domain, method, disableLegacyLookup) {
|
|
947
|
+
const route = this.find(routeIdentifier, domain, method, disableLegacyLookup);
|
|
948
|
+
if (!route) {
|
|
949
|
+
if (method) throw new Error(`Cannot lookup route "${routeIdentifier}" for method "${method}"`);
|
|
950
|
+
throw new Error(`Cannot lookup route "${routeIdentifier}"`);
|
|
951
|
+
}
|
|
952
|
+
return route;
|
|
953
|
+
}
|
|
954
|
+
has(routeIdentifier, domain, method, followLookupStrategy) {
|
|
955
|
+
return !!this.find(routeIdentifier, domain, method, followLookupStrategy);
|
|
956
|
+
}
|
|
957
|
+
toJSON() {
|
|
958
|
+
return this.routes;
|
|
959
|
+
}
|
|
960
|
+
generateTypes(indentation = 0) {
|
|
961
|
+
const routesList = {};
|
|
962
|
+
function trackRoute(route, domain) {
|
|
963
|
+
let params = [];
|
|
964
|
+
let paramsTuple = [];
|
|
965
|
+
let hasRequiredParams = false;
|
|
966
|
+
for (let token of route.tokens) if (token.type === 1) {
|
|
967
|
+
hasRequiredParams = true;
|
|
968
|
+
params.push(`'${token.val}': ParamValue`);
|
|
969
|
+
paramsTuple.push("ParamValue");
|
|
970
|
+
} else if (token.type === 3) {
|
|
971
|
+
params.push(`'${token.val}'?: ParamValue`);
|
|
972
|
+
paramsTuple.push("ParamValue?");
|
|
973
|
+
} else if (token.type === 2) {
|
|
974
|
+
hasRequiredParams = true;
|
|
975
|
+
params.push(`'*': ParamValue[]`);
|
|
976
|
+
paramsTuple.push("...ParamValue[]");
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
979
|
+
route.methods.forEach((method) => {
|
|
980
|
+
routesList["ALL"] = routesList["ALL"] ?? {};
|
|
981
|
+
routesList[method] = routesList[method] ?? {};
|
|
982
|
+
const identifiers = [];
|
|
983
|
+
if (route.name) identifiers.push(domain && routesList[method][route.name] ? `${domain}@${route.name}` : route.name);
|
|
984
|
+
identifiers.forEach((identifier) => {
|
|
985
|
+
routesList["ALL"][identifier] = {
|
|
986
|
+
params,
|
|
987
|
+
paramsTuple,
|
|
988
|
+
hasRequiredParams
|
|
989
|
+
};
|
|
990
|
+
routesList[method][identifier] = {
|
|
991
|
+
params,
|
|
992
|
+
paramsTuple,
|
|
993
|
+
hasRequiredParams
|
|
994
|
+
};
|
|
995
|
+
});
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
const domains = Object.keys(this.routes).filter((domain) => domain !== "root");
|
|
999
|
+
this.routes["root"]?.forEach((route) => trackRoute.bind(this)(route));
|
|
1000
|
+
domains.forEach((domain) => this.routes[domain].forEach((route) => trackRoute.bind(this)(route, domain)));
|
|
1001
|
+
return {
|
|
1002
|
+
imports: [],
|
|
1003
|
+
types: ["type ParamValue = string | number | bigint | boolean"],
|
|
1004
|
+
routes: Object.keys(routesList).reduce((result, method) => {
|
|
1005
|
+
result.push(`${" ".repeat(indentation)}${method}: {`);
|
|
1006
|
+
Object.keys(routesList[method]).forEach((identifier) => {
|
|
1007
|
+
const key = `'${identifier}'`;
|
|
1008
|
+
const { paramsTuple, hasRequiredParams, params } = routesList[method][identifier];
|
|
1009
|
+
const dictName = hasRequiredParams ? "params" : "params?";
|
|
1010
|
+
const tupleName = hasRequiredParams ? "paramsTuple" : "paramsTuple?";
|
|
1011
|
+
const dictValue = `{${params.join(",")}}`;
|
|
1012
|
+
const value = `{ ${tupleName}: ${`[${paramsTuple?.join(",")}]`}; ${dictName}: ${dictValue} }`;
|
|
1013
|
+
result.push(`${" ".repeat(indentation + 2)}${key}: ${value}`);
|
|
1014
|
+
});
|
|
1015
|
+
result.push(`${" ".repeat(indentation)}}`);
|
|
1016
|
+
return result;
|
|
1017
|
+
}, []).join("\n")
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
match(uri, method, shouldDecodeParam, hostname) {
|
|
1021
|
+
const matchingDomain = this.#store.matchDomain(hostname);
|
|
1022
|
+
return matchingDomain.length ? this.#store.match(uri, method, shouldDecodeParam, {
|
|
1023
|
+
tokens: matchingDomain,
|
|
1024
|
+
hostname
|
|
1025
|
+
}) : this.#store.match(uri, method, shouldDecodeParam);
|
|
1026
|
+
}
|
|
1027
|
+
builder() {
|
|
1028
|
+
return new UrlBuilder(this);
|
|
1029
|
+
}
|
|
1030
|
+
builderForDomain(domain) {
|
|
1031
|
+
return new UrlBuilder(this, domain);
|
|
1032
|
+
}
|
|
1033
|
+
makeUrl(routeIdentifier, params, options) {
|
|
1034
|
+
const normalizedOptions = Object.assign({}, options);
|
|
1035
|
+
if (options?.disableRouteLookup) return createURL(routeIdentifier, parseRoute(routeIdentifier), this.qs.stringify, params, options);
|
|
1036
|
+
const route = this.findOrFail(routeIdentifier, normalizedOptions.domain);
|
|
1037
|
+
return createURL(route.name ?? route.pattern, route.tokens, this.qs.stringify, params, options);
|
|
1038
|
+
}
|
|
1039
|
+
makeSignedUrl(routeIdentifier, params, options) {
|
|
1040
|
+
const normalizedOptions = Object.assign({}, options);
|
|
1041
|
+
if (options?.disableRouteLookup) return createSignedURL(routeIdentifier, parseRoute(routeIdentifier), this.qs.stringify, this.#encryption, params, options);
|
|
1042
|
+
const route = this.findOrFail(routeIdentifier, normalizedOptions.domain);
|
|
1043
|
+
return createSignedURL(route.name ?? route.pattern, route.tokens, this.qs.stringify, this.#encryption, params, options);
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
const ResponseStatus = {
|
|
1047
|
+
Continue: 100,
|
|
1048
|
+
SwitchingProtocols: 101,
|
|
1049
|
+
Processing: 102,
|
|
1050
|
+
EarlyHints: 103,
|
|
1051
|
+
Ok: 200,
|
|
1052
|
+
Created: 201,
|
|
1053
|
+
Accepted: 202,
|
|
1054
|
+
NonAuthoritativeInformation: 203,
|
|
1055
|
+
NoContent: 204,
|
|
1056
|
+
ResetContent: 205,
|
|
1057
|
+
PartialContent: 206,
|
|
1058
|
+
MultiStatus: 207,
|
|
1059
|
+
AlreadyReported: 208,
|
|
1060
|
+
IMUsed: 226,
|
|
1061
|
+
MultipleChoices: 300,
|
|
1062
|
+
MovedPermanently: 301,
|
|
1063
|
+
Found: 302,
|
|
1064
|
+
SeeOther: 303,
|
|
1065
|
+
NotModified: 304,
|
|
1066
|
+
UseProxy: 305,
|
|
1067
|
+
TemporaryRedirect: 307,
|
|
1068
|
+
PermanentRedirect: 308,
|
|
1069
|
+
BadRequest: 400,
|
|
1070
|
+
Unauthorized: 401,
|
|
1071
|
+
PaymentRequired: 402,
|
|
1072
|
+
Forbidden: 403,
|
|
1073
|
+
NotFound: 404,
|
|
1074
|
+
MethodNotAllowed: 405,
|
|
1075
|
+
NotAcceptable: 406,
|
|
1076
|
+
ProxyAuthenticationRequired: 407,
|
|
1077
|
+
RequestTimeout: 408,
|
|
1078
|
+
Conflict: 409,
|
|
1079
|
+
Gone: 410,
|
|
1080
|
+
LengthRequired: 411,
|
|
1081
|
+
PreconditionFailed: 412,
|
|
1082
|
+
PayloadTooLarge: 413,
|
|
1083
|
+
URITooLong: 414,
|
|
1084
|
+
UnsupportedMediaType: 415,
|
|
1085
|
+
RangeNotSatisfiable: 416,
|
|
1086
|
+
ExpectationFailed: 417,
|
|
1087
|
+
ImATeapot: 418,
|
|
1088
|
+
MisdirectedRequest: 421,
|
|
1089
|
+
UnprocessableEntity: 422,
|
|
1090
|
+
Locked: 423,
|
|
1091
|
+
FailedDependency: 424,
|
|
1092
|
+
TooEarly: 425,
|
|
1093
|
+
UpgradeRequired: 426,
|
|
1094
|
+
PreconditionRequired: 428,
|
|
1095
|
+
TooManyRequests: 429,
|
|
1096
|
+
RequestHeaderFieldsTooLarge: 431,
|
|
1097
|
+
UnavailableForLegalReasons: 451,
|
|
1098
|
+
InternalServerError: 500,
|
|
1099
|
+
NotImplemented: 501,
|
|
1100
|
+
BadGateway: 502,
|
|
1101
|
+
ServiceUnavailable: 503,
|
|
1102
|
+
GatewayTimeout: 504,
|
|
1103
|
+
HTTPVersionNotSupported: 505,
|
|
1104
|
+
VariantAlsoNegotiates: 506,
|
|
1105
|
+
InsufficientStorage: 507,
|
|
1106
|
+
LoopDetected: 508,
|
|
1107
|
+
NotExtended: 510,
|
|
1108
|
+
NetworkAuthenticationRequired: 511
|
|
1109
|
+
};
|
|
1110
|
+
var CookieSerializer = class {
|
|
1111
|
+
#client;
|
|
1112
|
+
constructor(encryption) {
|
|
1113
|
+
this.#client = new CookieClient(encryption);
|
|
1114
|
+
}
|
|
1115
|
+
encode(key, value, options) {
|
|
1116
|
+
const stringify$1 = options?.stringify ?? options?.encode;
|
|
1117
|
+
const packedValue = this.#client.encode(key, value, stringify$1);
|
|
1118
|
+
if (packedValue === null || packedValue === void 0) return null;
|
|
1119
|
+
return serializeCookie(key, packedValue, options);
|
|
1120
|
+
}
|
|
1121
|
+
sign(key, value, options) {
|
|
1122
|
+
const packedValue = this.#client.sign(key, value);
|
|
1123
|
+
if (packedValue === null) return null;
|
|
1124
|
+
return serializeCookie(key, packedValue, options);
|
|
1125
|
+
}
|
|
1126
|
+
encrypt(key, value, options) {
|
|
1127
|
+
const packedValue = this.#client.encrypt(key, value);
|
|
1128
|
+
if (packedValue === null) return null;
|
|
1129
|
+
return serializeCookie(key, packedValue, options);
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
const CACHEABLE_HTTP_METHODS = ["GET", "HEAD"];
|
|
1133
|
+
const DATA_TYPES = {
|
|
1134
|
+
buffer: "buffer",
|
|
1135
|
+
regexp: "regexp",
|
|
1136
|
+
date: "date",
|
|
1137
|
+
object: "object",
|
|
1138
|
+
number: "number",
|
|
1139
|
+
boolean: "boolean",
|
|
1140
|
+
string: "string",
|
|
1141
|
+
bigint: "bigint"
|
|
1142
|
+
};
|
|
1143
|
+
const CONTENT_TYPES = {
|
|
1144
|
+
text: "text/plain; charset=utf-8",
|
|
1145
|
+
html: "text/html; charset=utf-8",
|
|
1146
|
+
script: "application/octet-stream; charset=utf-8",
|
|
1147
|
+
json: "application/json; charset=utf-8"
|
|
1148
|
+
};
|
|
1149
|
+
var HttpResponse = class extends Macroable {
|
|
1150
|
+
#qs;
|
|
1151
|
+
#headers = {};
|
|
1152
|
+
#hasExplicitStatus = false;
|
|
1153
|
+
#cookieSerializer;
|
|
1154
|
+
#router;
|
|
1155
|
+
#config;
|
|
1156
|
+
get hasLazyBody() {
|
|
1157
|
+
return !!(this.lazyBody.content || this.lazyBody.fileToStream || this.lazyBody.stream);
|
|
1158
|
+
}
|
|
1159
|
+
get hasContent() {
|
|
1160
|
+
return !!this.lazyBody.content;
|
|
1161
|
+
}
|
|
1162
|
+
get hasStream() {
|
|
1163
|
+
return !!this.lazyBody.stream;
|
|
1164
|
+
}
|
|
1165
|
+
get hasFileToStream() {
|
|
1166
|
+
return !!this.lazyBody.fileToStream;
|
|
1167
|
+
}
|
|
1168
|
+
get content() {
|
|
1169
|
+
return this.lazyBody.content;
|
|
1170
|
+
}
|
|
1171
|
+
get outgoingStream() {
|
|
1172
|
+
return this.lazyBody.stream?.[0];
|
|
1173
|
+
}
|
|
1174
|
+
get fileToStream() {
|
|
1175
|
+
return this.lazyBody.fileToStream ? {
|
|
1176
|
+
path: this.lazyBody.fileToStream[0],
|
|
1177
|
+
generateEtag: this.lazyBody.fileToStream[1]
|
|
1178
|
+
} : void 0;
|
|
1179
|
+
}
|
|
1180
|
+
lazyBody = {};
|
|
1181
|
+
ctx;
|
|
1182
|
+
constructor(request, response, encryption, config, router, qs) {
|
|
1183
|
+
super();
|
|
1184
|
+
this.request = request;
|
|
1185
|
+
this.response = response;
|
|
1186
|
+
this.#qs = qs;
|
|
1187
|
+
this.#config = config;
|
|
1188
|
+
this.#router = router;
|
|
1189
|
+
this.#cookieSerializer = new CookieSerializer(encryption);
|
|
1190
|
+
}
|
|
1191
|
+
get finished() {
|
|
1192
|
+
return this.response.writableFinished;
|
|
1193
|
+
}
|
|
1194
|
+
get headersSent() {
|
|
1195
|
+
return this.response.headersSent;
|
|
1196
|
+
}
|
|
1197
|
+
get isPending() {
|
|
1198
|
+
return !this.headersSent && !this.finished;
|
|
1199
|
+
}
|
|
1200
|
+
#castHeaderValue(value) {
|
|
1201
|
+
return Array.isArray(value) ? value.map(String) : String(value);
|
|
1202
|
+
}
|
|
1203
|
+
#endResponse(body, statusCode) {
|
|
1204
|
+
this.writeHead(statusCode);
|
|
1205
|
+
this.response.end(body, null, null);
|
|
1206
|
+
}
|
|
1207
|
+
writeBody(content, generateEtag, jsonpCallbackName) {
|
|
1208
|
+
const hasEmptyBody = content === null || content === void 0 || content === "";
|
|
1209
|
+
if (hasEmptyBody) this.safeStatus(204);
|
|
1210
|
+
const statusCode = this.response.statusCode;
|
|
1211
|
+
if (statusCode && (statusCode < ResponseStatus.Ok || statusCode === ResponseStatus.NoContent || statusCode === ResponseStatus.NotModified)) {
|
|
1212
|
+
this.removeHeader("Content-Type");
|
|
1213
|
+
this.removeHeader("Content-Length");
|
|
1214
|
+
this.removeHeader("Transfer-Encoding");
|
|
1215
|
+
this.#endResponse();
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
if (hasEmptyBody) {
|
|
1219
|
+
this.removeHeader("Content-Length");
|
|
1220
|
+
this.#endResponse();
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
let contentType;
|
|
1224
|
+
const dataType = typeof content;
|
|
1225
|
+
if (dataType === DATA_TYPES.string) contentType = content.trimStart().startsWith("<") ? CONTENT_TYPES.html : CONTENT_TYPES.text;
|
|
1226
|
+
else if (content instanceof Uint8Array) contentType = CONTENT_TYPES.script;
|
|
1227
|
+
else if (content instanceof RegExp) {
|
|
1228
|
+
content = String(content);
|
|
1229
|
+
contentType = CONTENT_TYPES.text;
|
|
1230
|
+
} else if (content instanceof Date) {
|
|
1231
|
+
content = content.toISOString();
|
|
1232
|
+
contentType = CONTENT_TYPES.text;
|
|
1233
|
+
} else if (dataType === "object") {
|
|
1234
|
+
content = this.#config.serializeJSON(content);
|
|
1235
|
+
contentType = CONTENT_TYPES.json;
|
|
1236
|
+
} else if (dataType === DATA_TYPES.number || dataType === DATA_TYPES.boolean || dataType === DATA_TYPES.bigint) {
|
|
1237
|
+
content = String(content);
|
|
1238
|
+
contentType = CONTENT_TYPES.text;
|
|
1239
|
+
} else throw new RuntimeException(`Cannot serialize "${dataType}" to HTTP response`);
|
|
1240
|
+
if (jsonpCallbackName) {
|
|
1241
|
+
content = content.replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1242
|
+
content = `/**/ typeof ${jsonpCallbackName} === 'function' && ${jsonpCallbackName}(${content});`;
|
|
1243
|
+
}
|
|
1244
|
+
if (generateEtag) this.setEtag(content);
|
|
1245
|
+
if (generateEtag && this.fresh()) {
|
|
1246
|
+
this.removeHeader("Content-Type");
|
|
1247
|
+
this.removeHeader("Content-Length");
|
|
1248
|
+
this.removeHeader("Transfer-Encoding");
|
|
1249
|
+
this.#endResponse(null, ResponseStatus.NotModified);
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
this.header("Content-Length", Buffer.byteLength(content));
|
|
1253
|
+
if (jsonpCallbackName) {
|
|
1254
|
+
this.header("X-Content-Type-Options", "nosniff");
|
|
1255
|
+
this.safeHeader("Content-Type", "text/javascript; charset=utf-8");
|
|
1256
|
+
} else this.safeHeader("Content-type", contentType);
|
|
1257
|
+
this.#endResponse(content);
|
|
1258
|
+
}
|
|
1259
|
+
streamBody(body, errorCallback) {
|
|
1260
|
+
const readable = body instanceof ReadableStream ? Readable.fromWeb(body) : body;
|
|
1261
|
+
return new Promise((resolve) => {
|
|
1262
|
+
let finished = false;
|
|
1263
|
+
readable.on("error", (error) => {
|
|
1264
|
+
/* c8 ignore next 3 */
|
|
1265
|
+
if (finished) return;
|
|
1266
|
+
finished = true;
|
|
1267
|
+
destroy(readable);
|
|
1268
|
+
this.type("text");
|
|
1269
|
+
if (!this.headersSent) if (typeof errorCallback === "function") this.#endResponse(...errorCallback(error));
|
|
1270
|
+
else this.#endResponse(error.code === "ENOENT" ? "File not found" : "Cannot process file", error.code === "ENOENT" ? ResponseStatus.NotFound : ResponseStatus.InternalServerError);
|
|
1271
|
+
else this.response.destroy();
|
|
1272
|
+
resolve();
|
|
1273
|
+
});
|
|
1274
|
+
readable.on("end", () => {
|
|
1275
|
+
if (!this.headersSent) this.#endResponse();
|
|
1276
|
+
resolve();
|
|
1277
|
+
});
|
|
1278
|
+
onFinished(this.response, () => {
|
|
1279
|
+
finished = true;
|
|
1280
|
+
destroy(readable);
|
|
1281
|
+
});
|
|
1282
|
+
this.relayHeaders();
|
|
1283
|
+
readable.pipe(this.response);
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
async streamFileForDownload(filePath, generateEtag, errorCallback) {
|
|
1287
|
+
try {
|
|
1288
|
+
const stats = await stat(filePath);
|
|
1289
|
+
if (!stats || !stats.isFile()) throw new TypeError("response.download only accepts path to a file");
|
|
1290
|
+
this.header("Last-Modified", stats.mtime.toUTCString());
|
|
1291
|
+
this.type(extname(filePath));
|
|
1292
|
+
if (generateEtag) this.setEtag(stats, true);
|
|
1293
|
+
if (this.request.method === "HEAD") {
|
|
1294
|
+
this.#endResponse(null, generateEtag && this.fresh() ? ResponseStatus.NotModified : ResponseStatus.Ok);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
if (generateEtag && this.fresh()) {
|
|
1298
|
+
this.#endResponse(null, ResponseStatus.NotModified);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
this.header("Content-length", stats.size);
|
|
1302
|
+
return this.streamBody(createReadStream(filePath), errorCallback);
|
|
1303
|
+
} catch (error) {
|
|
1304
|
+
this.type("text");
|
|
1305
|
+
this.removeHeader("Etag");
|
|
1306
|
+
if (typeof errorCallback === "function") this.#endResponse(...errorCallback(error));
|
|
1307
|
+
else this.#endResponse(error.code === "ENOENT" ? "File not found" : "Cannot process file", error.code === "ENOENT" ? ResponseStatus.NotFound : ResponseStatus.InternalServerError);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
onFinish(callback) {
|
|
1311
|
+
onFinished(this.response, callback);
|
|
1312
|
+
}
|
|
1313
|
+
relayHeaders() {
|
|
1314
|
+
if (!this.headersSent) for (let key in this.#headers) {
|
|
1315
|
+
const value = this.#headers[key];
|
|
1316
|
+
if (value) this.response.setHeader(key, value);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
writeHead(statusCode) {
|
|
1320
|
+
this.response.writeHead(statusCode || this.response.statusCode, this.#headers);
|
|
1321
|
+
return this;
|
|
1322
|
+
}
|
|
1323
|
+
getHeader(key) {
|
|
1324
|
+
const value = this.#headers[key.toLowerCase()];
|
|
1325
|
+
return value === void 0 ? this.response.getHeader(key) : value;
|
|
1326
|
+
}
|
|
1327
|
+
getHeaders() {
|
|
1328
|
+
return {
|
|
1329
|
+
...this.response.getHeaders(),
|
|
1330
|
+
...this.#headers
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
header(key, value) {
|
|
1334
|
+
if (value === null || value === void 0) return this;
|
|
1335
|
+
this.#headers[key.toLowerCase()] = this.#castHeaderValue(value);
|
|
1336
|
+
return this;
|
|
1337
|
+
}
|
|
1338
|
+
append(key, value) {
|
|
1339
|
+
if (value === null || value === void 0) return this;
|
|
1340
|
+
key = key.toLowerCase();
|
|
1341
|
+
let existingHeader = this.getHeader(key);
|
|
1342
|
+
let casted = this.#castHeaderValue(value);
|
|
1343
|
+
if (!existingHeader) {
|
|
1344
|
+
this.#headers[key] = casted;
|
|
1345
|
+
return this;
|
|
1346
|
+
}
|
|
1347
|
+
existingHeader = this.#castHeaderValue(existingHeader);
|
|
1348
|
+
casted = Array.isArray(existingHeader) ? existingHeader.concat(casted) : [existingHeader].concat(casted);
|
|
1349
|
+
this.#headers[key] = casted;
|
|
1350
|
+
return this;
|
|
1351
|
+
}
|
|
1352
|
+
safeHeader(key, value) {
|
|
1353
|
+
if (!this.getHeader(key)) this.header(key, value);
|
|
1354
|
+
return this;
|
|
1355
|
+
}
|
|
1356
|
+
removeHeader(key) {
|
|
1357
|
+
key = key.toLowerCase();
|
|
1358
|
+
this.response.removeHeader(key);
|
|
1359
|
+
if (this.#headers[key]) delete this.#headers[key.toLowerCase()];
|
|
1360
|
+
return this;
|
|
1361
|
+
}
|
|
1362
|
+
getStatus() {
|
|
1363
|
+
return this.response.statusCode;
|
|
1364
|
+
}
|
|
1365
|
+
status(code) {
|
|
1366
|
+
this.#hasExplicitStatus = true;
|
|
1367
|
+
this.response.statusCode = code;
|
|
1368
|
+
return this;
|
|
1369
|
+
}
|
|
1370
|
+
safeStatus(code) {
|
|
1371
|
+
if (this.#hasExplicitStatus) return this;
|
|
1372
|
+
this.status(code);
|
|
1373
|
+
return this;
|
|
1374
|
+
}
|
|
1375
|
+
type(type, charset) {
|
|
1376
|
+
type = charset ? `${type}; charset=${charset}` : type;
|
|
1377
|
+
this.header("Content-Type", mime.contentType(type));
|
|
1378
|
+
return this;
|
|
1379
|
+
}
|
|
1380
|
+
vary(field) {
|
|
1381
|
+
vary(this.response, field);
|
|
1382
|
+
return this;
|
|
1383
|
+
}
|
|
1384
|
+
setEtag(body, weak = false) {
|
|
1385
|
+
this.header("Etag", etag(body, { weak }));
|
|
1386
|
+
return this;
|
|
1387
|
+
}
|
|
1388
|
+
setRequestId() {
|
|
1389
|
+
const requestId = this.request.headers["x-request-id"];
|
|
1390
|
+
if (requestId) this.header("X-Request-Id", requestId);
|
|
1391
|
+
return this;
|
|
1392
|
+
}
|
|
1393
|
+
fresh() {
|
|
1394
|
+
if (this.request.method && !CACHEABLE_HTTP_METHODS.includes(this.request.method)) return false;
|
|
1395
|
+
const status = this.response.statusCode;
|
|
1396
|
+
if (status >= ResponseStatus.Ok && status < ResponseStatus.MultipleChoices || status === ResponseStatus.NotModified) return fresh(this.request.headers, this.#headers);
|
|
1397
|
+
return false;
|
|
1398
|
+
}
|
|
1399
|
+
getBody() {
|
|
1400
|
+
if (this.lazyBody.content) return this.lazyBody.content[0];
|
|
1401
|
+
return null;
|
|
1402
|
+
}
|
|
1403
|
+
send(body, generateEtag = this.#config.etag) {
|
|
1404
|
+
if (body instanceof Response) {
|
|
1405
|
+
body.headers.forEach((value, key) => this.header(key, value));
|
|
1406
|
+
this.safeStatus(body.status);
|
|
1407
|
+
if (body.body) this.stream(body.body);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
this.lazyBody.content = [body, generateEtag];
|
|
1411
|
+
}
|
|
1412
|
+
json(body, generateEtag = this.#config.etag) {
|
|
1413
|
+
return this.send(body, generateEtag);
|
|
1414
|
+
}
|
|
1415
|
+
jsonp(body, callbackName = this.#config.jsonpCallbackName, generateEtag = this.#config.etag) {
|
|
1416
|
+
this.lazyBody.content = [
|
|
1417
|
+
body,
|
|
1418
|
+
generateEtag,
|
|
1419
|
+
callbackName
|
|
1420
|
+
];
|
|
1421
|
+
}
|
|
1422
|
+
stream(body, errorCallback) {
|
|
1423
|
+
if (body instanceof ReadableStream === false && (typeof body.pipe !== "function" || !body.readable || typeof body.read !== "function")) throw new TypeError("response.stream accepts a readable stream only");
|
|
1424
|
+
this.lazyBody.stream = [body, errorCallback];
|
|
1425
|
+
}
|
|
1426
|
+
download(filePath, generateEtag = this.#config.etag, errorCallback) {
|
|
1427
|
+
this.lazyBody.fileToStream = [
|
|
1428
|
+
filePath,
|
|
1429
|
+
generateEtag,
|
|
1430
|
+
errorCallback
|
|
1431
|
+
];
|
|
1432
|
+
}
|
|
1433
|
+
attachment(filePath, name, disposition, generateEtag, errorCallback) {
|
|
1434
|
+
name = name || filePath;
|
|
1435
|
+
this.header("Content-Disposition", contentDisposition(name, { type: disposition }));
|
|
1436
|
+
return this.download(filePath, generateEtag, errorCallback);
|
|
1437
|
+
}
|
|
1438
|
+
location(url) {
|
|
1439
|
+
this.header("Location", url);
|
|
1440
|
+
return this;
|
|
1441
|
+
}
|
|
1442
|
+
redirect(path, forwardQueryString = false, statusCode = ResponseStatus.Found) {
|
|
1443
|
+
const handler = new Redirect(this.request, this, this.#router, this.#qs);
|
|
1444
|
+
if (forwardQueryString) handler.withQs();
|
|
1445
|
+
if (path === "back") return handler.status(statusCode).back();
|
|
1446
|
+
if (path) return handler.status(statusCode).toPath(path);
|
|
1447
|
+
return handler;
|
|
1448
|
+
}
|
|
1449
|
+
abort(body, status) {
|
|
1450
|
+
throw E_HTTP_REQUEST_ABORTED.invoke(body, status || ResponseStatus.BadRequest);
|
|
1451
|
+
}
|
|
1452
|
+
abortIf(condition, body, status) {
|
|
1453
|
+
if (condition) this.abort(body, status);
|
|
1454
|
+
}
|
|
1455
|
+
abortUnless(condition, body, status) {
|
|
1456
|
+
if (!condition) this.abort(body, status);
|
|
1457
|
+
}
|
|
1458
|
+
cookie(key, value, options) {
|
|
1459
|
+
options = Object.assign({}, this.#config.cookie, options);
|
|
1460
|
+
const serialized = this.#cookieSerializer.sign(key, value, options);
|
|
1461
|
+
if (!serialized) return this;
|
|
1462
|
+
this.append("set-cookie", serialized);
|
|
1463
|
+
return this;
|
|
1464
|
+
}
|
|
1465
|
+
encryptedCookie(key, value, options) {
|
|
1466
|
+
options = Object.assign({}, this.#config.cookie, options);
|
|
1467
|
+
const serialized = this.#cookieSerializer.encrypt(key, value, options);
|
|
1468
|
+
if (!serialized) return this;
|
|
1469
|
+
this.append("set-cookie", serialized);
|
|
1470
|
+
return this;
|
|
1471
|
+
}
|
|
1472
|
+
plainCookie(key, value, options) {
|
|
1473
|
+
options = Object.assign({}, this.#config.cookie, options);
|
|
1474
|
+
const serialized = this.#cookieSerializer.encode(key, value, options);
|
|
1475
|
+
if (!serialized) return this;
|
|
1476
|
+
this.append("set-cookie", serialized);
|
|
1477
|
+
return this;
|
|
1478
|
+
}
|
|
1479
|
+
clearCookie(key, options) {
|
|
1480
|
+
options = Object.assign({}, this.#config.cookie, options);
|
|
1481
|
+
options.expires = /* @__PURE__ */ new Date(1);
|
|
1482
|
+
options.maxAge = -1;
|
|
1483
|
+
const serialized = this.#cookieSerializer.encode(key, "", {
|
|
1484
|
+
...options,
|
|
1485
|
+
encode: false
|
|
1486
|
+
});
|
|
1487
|
+
this.append("set-cookie", serialized);
|
|
1488
|
+
return this;
|
|
1489
|
+
}
|
|
1490
|
+
finish() {
|
|
1491
|
+
if (!this.isPending) return;
|
|
1492
|
+
this.setRequestId();
|
|
1493
|
+
if (this.content) {
|
|
1494
|
+
httpResponseSerializer.traceSync(this.writeBody, void 0, this, ...this.content);
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
if (this.lazyBody.stream) {
|
|
1498
|
+
this.streamBody(...this.lazyBody.stream);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
if (this.lazyBody.fileToStream) {
|
|
1502
|
+
this.streamFileForDownload(...this.lazyBody.fileToStream);
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
this.#endResponse();
|
|
1506
|
+
}
|
|
1507
|
+
continue() {
|
|
1508
|
+
this.status(ResponseStatus.Continue);
|
|
1509
|
+
return this.send(null, false);
|
|
1510
|
+
}
|
|
1511
|
+
switchingProtocols() {
|
|
1512
|
+
this.status(ResponseStatus.SwitchingProtocols);
|
|
1513
|
+
return this.send(null, false);
|
|
1514
|
+
}
|
|
1515
|
+
ok(body, generateEtag) {
|
|
1516
|
+
this.status(ResponseStatus.Ok);
|
|
1517
|
+
return this.send(body, generateEtag);
|
|
1518
|
+
}
|
|
1519
|
+
created(body, generateEtag) {
|
|
1520
|
+
this.status(ResponseStatus.Created);
|
|
1521
|
+
return this.send(body, generateEtag);
|
|
1522
|
+
}
|
|
1523
|
+
accepted(body, generateEtag) {
|
|
1524
|
+
this.status(ResponseStatus.Accepted);
|
|
1525
|
+
return this.send(body, generateEtag);
|
|
1526
|
+
}
|
|
1527
|
+
nonAuthoritativeInformation(body, generateEtag) {
|
|
1528
|
+
this.status(ResponseStatus.NonAuthoritativeInformation);
|
|
1529
|
+
return this.send(body, generateEtag);
|
|
1530
|
+
}
|
|
1531
|
+
noContent() {
|
|
1532
|
+
this.status(ResponseStatus.NoContent);
|
|
1533
|
+
return this.send(null, false);
|
|
1534
|
+
}
|
|
1535
|
+
resetContent() {
|
|
1536
|
+
this.status(ResponseStatus.ResetContent);
|
|
1537
|
+
return this.send(null, false);
|
|
1538
|
+
}
|
|
1539
|
+
partialContent(body, generateEtag) {
|
|
1540
|
+
this.status(ResponseStatus.PartialContent);
|
|
1541
|
+
return this.send(body, generateEtag);
|
|
1542
|
+
}
|
|
1543
|
+
multipleChoices(body, generateEtag) {
|
|
1544
|
+
this.status(ResponseStatus.MultipleChoices);
|
|
1545
|
+
return this.send(body, generateEtag);
|
|
1546
|
+
}
|
|
1547
|
+
movedPermanently(body, generateEtag) {
|
|
1548
|
+
this.status(ResponseStatus.MovedPermanently);
|
|
1549
|
+
return this.send(body, generateEtag);
|
|
1550
|
+
}
|
|
1551
|
+
movedTemporarily(body, generateEtag) {
|
|
1552
|
+
this.status(ResponseStatus.Found);
|
|
1553
|
+
return this.send(body, generateEtag);
|
|
1554
|
+
}
|
|
1555
|
+
seeOther(body, generateEtag) {
|
|
1556
|
+
this.status(ResponseStatus.SeeOther);
|
|
1557
|
+
return this.send(body, generateEtag);
|
|
1558
|
+
}
|
|
1559
|
+
notModified(body, generateEtag) {
|
|
1560
|
+
this.status(ResponseStatus.NotModified);
|
|
1561
|
+
return this.send(body, generateEtag);
|
|
1562
|
+
}
|
|
1563
|
+
useProxy(body, generateEtag) {
|
|
1564
|
+
this.status(ResponseStatus.UseProxy);
|
|
1565
|
+
return this.send(body, generateEtag);
|
|
1566
|
+
}
|
|
1567
|
+
temporaryRedirect(body, generateEtag) {
|
|
1568
|
+
this.status(ResponseStatus.TemporaryRedirect);
|
|
1569
|
+
return this.send(body, generateEtag);
|
|
1570
|
+
}
|
|
1571
|
+
badRequest(body, generateEtag) {
|
|
1572
|
+
this.status(ResponseStatus.BadRequest);
|
|
1573
|
+
return this.send(body, generateEtag);
|
|
1574
|
+
}
|
|
1575
|
+
unauthorized(body, generateEtag) {
|
|
1576
|
+
this.status(ResponseStatus.Unauthorized);
|
|
1577
|
+
return this.send(body, generateEtag);
|
|
1578
|
+
}
|
|
1579
|
+
paymentRequired(body, generateEtag) {
|
|
1580
|
+
this.status(ResponseStatus.PaymentRequired);
|
|
1581
|
+
return this.send(body, generateEtag);
|
|
1582
|
+
}
|
|
1583
|
+
forbidden(body, generateEtag) {
|
|
1584
|
+
this.status(ResponseStatus.Forbidden);
|
|
1585
|
+
return this.send(body, generateEtag);
|
|
1586
|
+
}
|
|
1587
|
+
notFound(body, generateEtag) {
|
|
1588
|
+
this.status(ResponseStatus.NotFound);
|
|
1589
|
+
return this.send(body, generateEtag);
|
|
1590
|
+
}
|
|
1591
|
+
methodNotAllowed(body, generateEtag) {
|
|
1592
|
+
this.status(ResponseStatus.MethodNotAllowed);
|
|
1593
|
+
return this.send(body, generateEtag);
|
|
1594
|
+
}
|
|
1595
|
+
notAcceptable(body, generateEtag) {
|
|
1596
|
+
this.status(ResponseStatus.NotAcceptable);
|
|
1597
|
+
return this.send(body, generateEtag);
|
|
1598
|
+
}
|
|
1599
|
+
proxyAuthenticationRequired(body, generateEtag) {
|
|
1600
|
+
this.status(ResponseStatus.ProxyAuthenticationRequired);
|
|
1601
|
+
return this.send(body, generateEtag);
|
|
1602
|
+
}
|
|
1603
|
+
requestTimeout(body, generateEtag) {
|
|
1604
|
+
this.status(ResponseStatus.RequestTimeout);
|
|
1605
|
+
return this.send(body, generateEtag);
|
|
1606
|
+
}
|
|
1607
|
+
conflict(body, generateEtag) {
|
|
1608
|
+
this.status(ResponseStatus.Conflict);
|
|
1609
|
+
return this.send(body, generateEtag);
|
|
1610
|
+
}
|
|
1611
|
+
gone(body, generateEtag) {
|
|
1612
|
+
this.status(ResponseStatus.Gone);
|
|
1613
|
+
return this.send(body, generateEtag);
|
|
1614
|
+
}
|
|
1615
|
+
lengthRequired(body, generateEtag) {
|
|
1616
|
+
this.status(ResponseStatus.LengthRequired);
|
|
1617
|
+
return this.send(body, generateEtag);
|
|
1618
|
+
}
|
|
1619
|
+
preconditionFailed(body, generateEtag) {
|
|
1620
|
+
this.status(ResponseStatus.PreconditionFailed);
|
|
1621
|
+
return this.send(body, generateEtag);
|
|
1622
|
+
}
|
|
1623
|
+
requestEntityTooLarge(body, generateEtag) {
|
|
1624
|
+
this.status(ResponseStatus.PayloadTooLarge);
|
|
1625
|
+
return this.send(body, generateEtag);
|
|
1626
|
+
}
|
|
1627
|
+
requestUriTooLong(body, generateEtag) {
|
|
1628
|
+
this.status(ResponseStatus.URITooLong);
|
|
1629
|
+
return this.send(body, generateEtag);
|
|
1630
|
+
}
|
|
1631
|
+
unsupportedMediaType(body, generateEtag) {
|
|
1632
|
+
this.status(ResponseStatus.UnsupportedMediaType);
|
|
1633
|
+
return this.send(body, generateEtag);
|
|
1634
|
+
}
|
|
1635
|
+
requestedRangeNotSatisfiable(body, generateEtag) {
|
|
1636
|
+
this.status(ResponseStatus.RangeNotSatisfiable);
|
|
1637
|
+
return this.send(body, generateEtag);
|
|
1638
|
+
}
|
|
1639
|
+
expectationFailed(body, generateEtag) {
|
|
1640
|
+
this.status(ResponseStatus.ExpectationFailed);
|
|
1641
|
+
return this.send(body, generateEtag);
|
|
1642
|
+
}
|
|
1643
|
+
unprocessableEntity(body, generateEtag) {
|
|
1644
|
+
this.status(ResponseStatus.UnprocessableEntity);
|
|
1645
|
+
return this.send(body, generateEtag);
|
|
1646
|
+
}
|
|
1647
|
+
tooManyRequests(body, generateEtag) {
|
|
1648
|
+
this.status(ResponseStatus.TooManyRequests);
|
|
1649
|
+
return this.send(body, generateEtag);
|
|
1650
|
+
}
|
|
1651
|
+
internalServerError(body, generateEtag) {
|
|
1652
|
+
this.status(ResponseStatus.InternalServerError);
|
|
1653
|
+
return this.send(body, generateEtag);
|
|
1654
|
+
}
|
|
1655
|
+
notImplemented(body, generateEtag) {
|
|
1656
|
+
this.status(ResponseStatus.NotImplemented);
|
|
1657
|
+
return this.send(body, generateEtag);
|
|
1658
|
+
}
|
|
1659
|
+
badGateway(body, generateEtag) {
|
|
1660
|
+
this.status(ResponseStatus.BadGateway);
|
|
1661
|
+
return this.send(body, generateEtag);
|
|
1662
|
+
}
|
|
1663
|
+
serviceUnavailable(body, generateEtag) {
|
|
1664
|
+
this.status(ResponseStatus.ServiceUnavailable);
|
|
1665
|
+
return this.send(body, generateEtag);
|
|
1666
|
+
}
|
|
1667
|
+
gatewayTimeout(body, generateEtag) {
|
|
1668
|
+
this.status(ResponseStatus.GatewayTimeout);
|
|
1669
|
+
return this.send(body, generateEtag);
|
|
1670
|
+
}
|
|
1671
|
+
httpVersionNotSupported(body, generateEtag) {
|
|
1672
|
+
this.status(ResponseStatus.HTTPVersionNotSupported);
|
|
1673
|
+
return this.send(body, generateEtag);
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
const asyncLocalStorage = {
|
|
1677
|
+
isEnabled: false,
|
|
1678
|
+
storage: null,
|
|
1679
|
+
create() {
|
|
1680
|
+
this.isEnabled = true;
|
|
1681
|
+
this.storage = new AsyncLocalStorage();
|
|
1682
|
+
return this.storage;
|
|
1683
|
+
},
|
|
1684
|
+
destroy() {
|
|
1685
|
+
this.isEnabled = false;
|
|
1686
|
+
this.storage = null;
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
var HttpContext = class extends Macroable {
|
|
1690
|
+
static get usingAsyncLocalStorage() {
|
|
1691
|
+
return asyncLocalStorage.isEnabled;
|
|
1692
|
+
}
|
|
1693
|
+
static get() {
|
|
1694
|
+
if (!this.usingAsyncLocalStorage || !asyncLocalStorage.storage) return null;
|
|
1695
|
+
return asyncLocalStorage.storage.getStore() || null;
|
|
1696
|
+
}
|
|
1697
|
+
static getOrFail() {
|
|
1698
|
+
if (!this.usingAsyncLocalStorage || !asyncLocalStorage.storage) throw new RuntimeException("HTTP context is not available. Enable \"useAsyncLocalStorage\" inside \"config/app.ts\" file");
|
|
1699
|
+
const store = this.get();
|
|
1700
|
+
if (!store) throw new RuntimeException("Http context is not available outside of an HTTP request");
|
|
1701
|
+
return store;
|
|
1702
|
+
}
|
|
1703
|
+
static runOutsideContext(callback, ...args) {
|
|
1704
|
+
if (!asyncLocalStorage.storage) return callback(...args);
|
|
1705
|
+
return asyncLocalStorage.storage.exit(callback, ...args);
|
|
1706
|
+
}
|
|
1707
|
+
route;
|
|
1708
|
+
routeKey;
|
|
1709
|
+
params = {};
|
|
1710
|
+
subdomains = {};
|
|
1711
|
+
constructor(request, response, logger, containerResolver) {
|
|
1712
|
+
super();
|
|
1713
|
+
this.request = request;
|
|
1714
|
+
this.response = response;
|
|
1715
|
+
this.logger = logger;
|
|
1716
|
+
this.containerResolver = containerResolver;
|
|
1717
|
+
this.request.ctx = this;
|
|
1718
|
+
this.response.ctx = this;
|
|
1719
|
+
}
|
|
1720
|
+
/* c8 ignore next 3 */
|
|
1721
|
+
inspect() {
|
|
1722
|
+
return inspect(this, false, 1, true);
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
function routeFinder(router, resolver, ctx, errorResponder) {
|
|
1726
|
+
return function() {
|
|
1727
|
+
const url = ctx.request.url();
|
|
1728
|
+
const method = ctx.request.method();
|
|
1729
|
+
const hostname = router.usingDomains ? ctx.request.hostname() : void 0;
|
|
1730
|
+
const route = router.match(url, method, ctx.request.parsedUrl.shouldDecodeParam, hostname);
|
|
1731
|
+
if (route) {
|
|
1732
|
+
ctx.params = route.params;
|
|
1733
|
+
ctx.subdomains = route.subdomains;
|
|
1734
|
+
ctx.route = route.route;
|
|
1735
|
+
ctx.routeKey = route.routeKey;
|
|
1736
|
+
return route.route.execute(route.route, resolver, ctx, errorResponder);
|
|
1737
|
+
}
|
|
1738
|
+
return Promise.reject(new E_ROUTE_NOT_FOUND([method, url]));
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
function writeResponse(ctx) {
|
|
1742
|
+
return function() {
|
|
1743
|
+
try {
|
|
1744
|
+
ctx.response.finish();
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
ctx.logger.fatal({ err: error }, "Response serialization failed");
|
|
1747
|
+
ctx.response.internalServerError(error.message);
|
|
1748
|
+
ctx.response.finish();
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
function middlewareHandler(resolver, ctx) {
|
|
1753
|
+
return function(fn, next) {
|
|
1754
|
+
debug_default("executing middleware %s", fn.name);
|
|
1755
|
+
return httpMiddleware.tracePromise(fn.handle, httpMiddleware.hasSubscribers ? { middleware: fn } : void 0, void 0, resolver, ctx, next);
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
var Server = class {
|
|
1759
|
+
#booted = false;
|
|
1760
|
+
#defaultErrorHandler = {
|
|
1761
|
+
report() {},
|
|
1762
|
+
handle(error, ctx) {
|
|
1763
|
+
ctx.response.status(error.status || 500).send(error.message || "Internal server error");
|
|
1764
|
+
}
|
|
1765
|
+
};
|
|
1766
|
+
#logger;
|
|
1767
|
+
#errorHandler;
|
|
1768
|
+
#resolvedErrorHandler = this.#defaultErrorHandler;
|
|
1769
|
+
#emitter;
|
|
1770
|
+
#app;
|
|
1771
|
+
#encryption;
|
|
1772
|
+
#config;
|
|
1773
|
+
#qsParser;
|
|
1774
|
+
#serverMiddlewareStack;
|
|
1775
|
+
#router;
|
|
1776
|
+
#nodeHttpServer;
|
|
1777
|
+
#middleware = [];
|
|
1778
|
+
#requestErrorResponder = (error, ctx) => {
|
|
1779
|
+
this.#resolvedErrorHandler.report(error, ctx);
|
|
1780
|
+
return httpExceptionHandler.tracePromise(this.#resolvedErrorHandler.handle, void 0, this.#resolvedErrorHandler, error, ctx);
|
|
1781
|
+
};
|
|
1782
|
+
get booted() {
|
|
1783
|
+
return this.#booted;
|
|
1784
|
+
}
|
|
1785
|
+
get usingAsyncLocalStorage() {
|
|
1786
|
+
return asyncLocalStorage.isEnabled;
|
|
1787
|
+
}
|
|
1788
|
+
constructor(app, encryption, emitter, logger, config) {
|
|
1789
|
+
this.#app = app;
|
|
1790
|
+
this.#emitter = emitter;
|
|
1791
|
+
this.#config = config;
|
|
1792
|
+
this.#logger = logger;
|
|
1793
|
+
this.#encryption = encryption;
|
|
1794
|
+
this.#qsParser = new Qs(this.#config.qs);
|
|
1795
|
+
this.#router = new Router(this.#app, this.#encryption, this.#qsParser);
|
|
1796
|
+
this.#createAsyncLocalStore();
|
|
1797
|
+
debug_default("server config: %O", this.#config);
|
|
1798
|
+
}
|
|
1799
|
+
#createAsyncLocalStore() {
|
|
1800
|
+
if (this.#config.useAsyncLocalStorage) {
|
|
1801
|
+
debug_default("creating ALS store for HTTP context");
|
|
1802
|
+
asyncLocalStorage.create();
|
|
1803
|
+
} else asyncLocalStorage.destroy();
|
|
1804
|
+
}
|
|
1805
|
+
#createServerMiddlewareStack() {
|
|
1806
|
+
this.#serverMiddlewareStack = new Middleware();
|
|
1807
|
+
this.#middleware.forEach((middleware) => this.#serverMiddlewareStack.add(middleware));
|
|
1808
|
+
this.#serverMiddlewareStack.freeze();
|
|
1809
|
+
this.#middleware = [];
|
|
1810
|
+
}
|
|
1811
|
+
#handleRequest(ctx, resolver) {
|
|
1812
|
+
return this.#serverMiddlewareStack.runner().errorHandler((error) => this.#requestErrorResponder(error, ctx)).finalHandler(routeFinder(this.#router, resolver, ctx, this.#requestErrorResponder)).run(middlewareHandler(resolver, ctx)).catch((error) => {
|
|
1813
|
+
ctx.logger.fatal({ err: error }, "Exception raised by error handler");
|
|
1814
|
+
return this.#defaultErrorHandler.handle(error, ctx);
|
|
1815
|
+
}).finally(writeResponse(ctx));
|
|
1816
|
+
}
|
|
1817
|
+
pipeline(middleware) {
|
|
1818
|
+
const middlewareStack = new Middleware();
|
|
1819
|
+
middleware.forEach((one) => {
|
|
1820
|
+
middlewareStack.add({
|
|
1821
|
+
reference: one,
|
|
1822
|
+
...moduleCaller(one, "handle").toHandleMethod()
|
|
1823
|
+
});
|
|
1824
|
+
});
|
|
1825
|
+
middlewareStack.freeze();
|
|
1826
|
+
const stackRunner = middlewareStack.runner();
|
|
1827
|
+
return {
|
|
1828
|
+
finalHandler(handler) {
|
|
1829
|
+
stackRunner.finalHandler(handler);
|
|
1830
|
+
return this;
|
|
1831
|
+
},
|
|
1832
|
+
errorHandler(handler) {
|
|
1833
|
+
stackRunner.errorHandler(handler);
|
|
1834
|
+
return this;
|
|
1835
|
+
},
|
|
1836
|
+
run(ctx) {
|
|
1837
|
+
return stackRunner.run((handler, next) => {
|
|
1838
|
+
return handler.handle(ctx.containerResolver, ctx, next);
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
use(middleware) {
|
|
1844
|
+
middleware.forEach((one) => this.#middleware.push({
|
|
1845
|
+
reference: one,
|
|
1846
|
+
...moduleImporter(one, "handle").toHandleMethod()
|
|
1847
|
+
}));
|
|
1848
|
+
return this;
|
|
1849
|
+
}
|
|
1850
|
+
errorHandler(handler) {
|
|
1851
|
+
this.#errorHandler = handler;
|
|
1852
|
+
return this;
|
|
1853
|
+
}
|
|
1854
|
+
async boot() {
|
|
1855
|
+
if (this.#booted) return;
|
|
1856
|
+
debug_default("booting HTTP server");
|
|
1857
|
+
this.#createServerMiddlewareStack();
|
|
1858
|
+
this.#router.commit();
|
|
1859
|
+
if (this.#errorHandler) {
|
|
1860
|
+
if (debug_default.enabled) debug_default("using custom error handler \"%s\"", this.#errorHandler);
|
|
1861
|
+
const moduleExports = await this.#errorHandler();
|
|
1862
|
+
this.#resolvedErrorHandler = await this.#app.container.make(moduleExports.default);
|
|
1863
|
+
}
|
|
1864
|
+
this.#booted = true;
|
|
1865
|
+
}
|
|
1866
|
+
setNodeServer(server) {
|
|
1867
|
+
server.timeout = this.#config.timeout ?? server.timeout;
|
|
1868
|
+
server.keepAliveTimeout = this.#config.keepAliveTimeout ?? server.keepAliveTimeout;
|
|
1869
|
+
server.headersTimeout = this.#config.headersTimeout ?? server.headersTimeout;
|
|
1870
|
+
server.requestTimeout = this.#config.requestTimeout ?? server.requestTimeout;
|
|
1871
|
+
this.#nodeHttpServer = server;
|
|
1872
|
+
}
|
|
1873
|
+
getNodeServer() {
|
|
1874
|
+
return this.#nodeHttpServer;
|
|
1875
|
+
}
|
|
1876
|
+
getRouter() {
|
|
1877
|
+
return this.#router;
|
|
1878
|
+
}
|
|
1879
|
+
createRequest(req, res) {
|
|
1880
|
+
return new HttpRequest(req, res, this.#encryption, this.#config, this.#qsParser);
|
|
1881
|
+
}
|
|
1882
|
+
createResponse(req, res) {
|
|
1883
|
+
return new HttpResponse(req, res, this.#encryption, this.#config, this.#router, this.#qsParser);
|
|
1884
|
+
}
|
|
1885
|
+
createHttpContext(request, response, resolver) {
|
|
1886
|
+
return new HttpContext(request, response, this.#logger.child({ request_id: request.id() }), resolver);
|
|
1887
|
+
}
|
|
1888
|
+
getMiddlewareList() {
|
|
1889
|
+
return this.#serverMiddlewareStack ? Array.from(this.#serverMiddlewareStack.all()) : [...this.#middleware];
|
|
1890
|
+
}
|
|
1891
|
+
handle(req, res) {
|
|
1892
|
+
const startTime = this.#emitter.hasListeners("http:request_completed") ? process.hrtime() : null;
|
|
1893
|
+
const resolver = this.#app.container.createResolver();
|
|
1894
|
+
const ctx = this.createHttpContext(this.createRequest(req, res), this.createResponse(req, res), resolver);
|
|
1895
|
+
if (startTime) onFinished(res, () => {
|
|
1896
|
+
this.#emitter.emit("http:request_completed", {
|
|
1897
|
+
ctx,
|
|
1898
|
+
duration: process.hrtime(startTime)
|
|
1899
|
+
});
|
|
1900
|
+
});
|
|
1901
|
+
if (this.usingAsyncLocalStorage) return asyncLocalStorage.storage.run(ctx, () => httpRequest.tracePromise(this.#handleRequest, httpRequest.hasSubscribers ? { ctx } : void 0, this, ctx, resolver));
|
|
1902
|
+
return httpRequest.tracePromise(this.#handleRequest, httpRequest.hasSubscribers ? { ctx } : void 0, this, ctx, resolver);
|
|
1903
|
+
}
|
|
1904
|
+
};
|
|
1905
|
+
function defineConfig(config) {
|
|
1906
|
+
const { trustProxy: trustProxy$1, ...rest } = config;
|
|
1907
|
+
const defaults = {
|
|
1908
|
+
allowMethodSpoofing: false,
|
|
1909
|
+
trustProxy: proxyAddr.compile("loopback"),
|
|
1910
|
+
subdomainOffset: 2,
|
|
1911
|
+
generateRequestId: !!config.createRequestId,
|
|
1912
|
+
createRequestId() {
|
|
1913
|
+
return crypto.randomUUID();
|
|
1914
|
+
},
|
|
1915
|
+
serializeJSON: safeStringify,
|
|
1916
|
+
useAsyncLocalStorage: false,
|
|
1917
|
+
etag: false,
|
|
1918
|
+
jsonpCallbackName: "callback",
|
|
1919
|
+
cookie: {
|
|
1920
|
+
maxAge: "2h",
|
|
1921
|
+
path: "/",
|
|
1922
|
+
httpOnly: true,
|
|
1923
|
+
secure: true,
|
|
1924
|
+
sameSite: "lax"
|
|
1925
|
+
},
|
|
1926
|
+
qs: {
|
|
1927
|
+
parse: {
|
|
1928
|
+
depth: 5,
|
|
1929
|
+
parameterLimit: 1e3,
|
|
1930
|
+
allowSparse: false,
|
|
1931
|
+
arrayLimit: 20,
|
|
1932
|
+
comma: true
|
|
1933
|
+
},
|
|
1934
|
+
stringify: {
|
|
1935
|
+
encode: true,
|
|
1936
|
+
encodeValuesOnly: false,
|
|
1937
|
+
arrayFormat: "indices",
|
|
1938
|
+
skipNulls: false
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
const normalizedConfig = lodash.merge({}, defaults, rest);
|
|
1943
|
+
if (normalizedConfig.cookie.maxAge) normalizedConfig.cookie.maxAge = string.seconds.parse(normalizedConfig.cookie.maxAge);
|
|
1944
|
+
if (typeof trustProxy$1 === "boolean") {
|
|
1945
|
+
const tpValue = trustProxy$1;
|
|
1946
|
+
normalizedConfig.trustProxy = (_, __) => tpValue;
|
|
1947
|
+
} else if (typeof trustProxy$1 === "string") {
|
|
1948
|
+
const tpValue = trustProxy$1;
|
|
1949
|
+
normalizedConfig.trustProxy = proxyAddr.compile(tpValue);
|
|
1950
|
+
} else if (trustProxy$1) normalizedConfig.trustProxy = trustProxy$1;
|
|
1951
|
+
return normalizedConfig;
|
|
1952
|
+
}
|
|
1953
|
+
export { Qs as _, CookieSerializer as a, HttpRequest as c, Redirect as d, E_CANNOT_LOOKUP_ROUTE as f, errors_exports as g, E_ROUTE_NOT_FOUND as h, HttpResponse as i, CookieParser as l, E_HTTP_REQUEST_ABORTED as m, Server as n, ResponseStatus as o, E_HTTP_EXCEPTION as p, HttpContext as r, Router as s, defineConfig as t, CookieClient as u };
|