@abejarano/ts-express-server 1.6.0 → 1.7.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/abstract/ServerTypes.d.ts +9 -0
- package/dist/adapters/BunAdapter.d.ts +11 -1
- package/dist/adapters/BunAdapter.js +359 -36
- package/dist/decorators/Controller.d.ts +1 -1
- package/dist/decorators/Controller.js +4 -1
- package/dist/decorators/DecoratorGuards.d.ts +1 -0
- package/dist/decorators/DecoratorGuards.js +14 -0
- package/dist/decorators/Handlers.d.ts +5 -5
- package/dist/decorators/Handlers.js +5 -1
- package/dist/decorators/Use.d.ts +1 -1
- package/dist/decorators/Use.js +5 -1
- package/package.json +1 -1
|
@@ -24,6 +24,15 @@ export interface ServerResponse {
|
|
|
24
24
|
send(body: unknown): void | Promise<void>;
|
|
25
25
|
set(name: string, value: string): this;
|
|
26
26
|
header(name: string, value: string): this;
|
|
27
|
+
cookie?(name: string, value: string, options?: {
|
|
28
|
+
maxAge?: number;
|
|
29
|
+
domain?: string;
|
|
30
|
+
path?: string;
|
|
31
|
+
expires?: Date;
|
|
32
|
+
httpOnly?: boolean;
|
|
33
|
+
secure?: boolean;
|
|
34
|
+
sameSite?: "lax" | "strict" | "none";
|
|
35
|
+
}): this;
|
|
27
36
|
end(body?: unknown): void | Promise<void>;
|
|
28
37
|
}
|
|
29
38
|
export type ServerHandlerInput = ServerHandler | ServerHandler[] | ServerRouter;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ServerAdapter, ServerApp, ServerInstance, ServerRouter, ServerRuntime } from "../abstract/ServerTypes";
|
|
1
|
+
import { ServerAdapter, ServerApp, ServerInstance, ServerRequest, ServerRouter, ServerRuntime } from "../abstract/ServerTypes";
|
|
2
2
|
export declare class BunAdapter implements ServerAdapter {
|
|
3
3
|
runtime: ServerRuntime;
|
|
4
4
|
createApp(): ServerApp;
|
|
@@ -6,3 +6,13 @@ export declare class BunAdapter implements ServerAdapter {
|
|
|
6
6
|
configure(app: ServerApp, _port: number): void;
|
|
7
7
|
listen(app: ServerApp, port: number, onListen: () => void): ServerInstance;
|
|
8
8
|
}
|
|
9
|
+
type MultipartFile = {
|
|
10
|
+
name: string;
|
|
11
|
+
size: number;
|
|
12
|
+
type: string;
|
|
13
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
14
|
+
lastModified?: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function getFiles(req: ServerRequest, field: string): MultipartFile[];
|
|
17
|
+
export declare function getFile(req: ServerRequest, field: string): MultipartFile | undefined;
|
|
18
|
+
export {};
|
|
@@ -1,28 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BunAdapter = void 0;
|
|
4
|
+
exports.getFiles = getFiles;
|
|
5
|
+
exports.getFile = getFile;
|
|
4
6
|
const ServerTypes_1 = require("../abstract/ServerTypes");
|
|
5
7
|
class BunResponse {
|
|
6
|
-
constructor() {
|
|
8
|
+
constructor(cookieJar, handlerTimeoutMs) {
|
|
7
9
|
this.statusCode = 200;
|
|
10
|
+
this.statusExplicitlySet = false;
|
|
8
11
|
this.headers = new Headers();
|
|
12
|
+
this.setCookies = [];
|
|
9
13
|
this.body = null;
|
|
10
14
|
this.ended = false;
|
|
15
|
+
this.cookieJar = cookieJar;
|
|
16
|
+
this.handlerTimeoutMs = handlerTimeoutMs;
|
|
11
17
|
this.endPromise = new Promise((resolve) => {
|
|
12
18
|
this.resolveEnd = resolve;
|
|
13
19
|
});
|
|
14
20
|
}
|
|
15
21
|
status(code) {
|
|
22
|
+
this.statusExplicitlySet = true;
|
|
16
23
|
this.statusCode = code;
|
|
17
24
|
return this;
|
|
18
25
|
}
|
|
19
26
|
set(name, value) {
|
|
27
|
+
if (name.toLowerCase() === "set-cookie") {
|
|
28
|
+
this.setCookies.push(value);
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
20
31
|
this.headers.set(name, value);
|
|
21
32
|
return this;
|
|
22
33
|
}
|
|
23
34
|
header(name, value) {
|
|
24
35
|
return this.set(name, value);
|
|
25
36
|
}
|
|
37
|
+
cookie(name, value, options = {}) {
|
|
38
|
+
if (this.cookieJar && typeof this.cookieJar.set === "function") {
|
|
39
|
+
this.cookieJar.set(name, value, toCookieJarOptions(options));
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
this.setCookies.push(serializeCookie(name, value, options));
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
26
45
|
json(body) {
|
|
27
46
|
if (!this.headers.has("content-type")) {
|
|
28
47
|
this.headers.set("content-type", "application/json");
|
|
@@ -71,13 +90,47 @@ class BunResponse {
|
|
|
71
90
|
waitForEnd() {
|
|
72
91
|
return this.endPromise;
|
|
73
92
|
}
|
|
93
|
+
getHandlerTimeoutMs() {
|
|
94
|
+
return this.handlerTimeoutMs;
|
|
95
|
+
}
|
|
74
96
|
toResponse() {
|
|
75
97
|
if (this.rawResponse) {
|
|
76
|
-
|
|
98
|
+
const headerMap = new Map();
|
|
99
|
+
const setCookies = readSetCookieHeaders(this.rawResponse.headers);
|
|
100
|
+
this.rawResponse.headers.forEach((value, key) => {
|
|
101
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
headerMap.set(key.toLowerCase(), value);
|
|
105
|
+
});
|
|
106
|
+
this.headers.forEach((value, key) => {
|
|
107
|
+
headerMap.set(key.toLowerCase(), value);
|
|
108
|
+
});
|
|
109
|
+
setCookies.push(...this.setCookies);
|
|
110
|
+
const headersInit = [];
|
|
111
|
+
headerMap.forEach((value, key) => {
|
|
112
|
+
headersInit.push([key, value]);
|
|
113
|
+
});
|
|
114
|
+
for (const cookie of setCookies) {
|
|
115
|
+
headersInit.push(["set-cookie", cookie]);
|
|
116
|
+
}
|
|
117
|
+
return new Response(this.rawResponse.body, {
|
|
118
|
+
status: this.statusExplicitlySet
|
|
119
|
+
? this.statusCode
|
|
120
|
+
: this.rawResponse.status,
|
|
121
|
+
headers: headersInit,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const headersInit = [];
|
|
125
|
+
this.headers.forEach((value, key) => {
|
|
126
|
+
headersInit.push([key, value]);
|
|
127
|
+
});
|
|
128
|
+
for (const cookie of this.setCookies) {
|
|
129
|
+
headersInit.push(["set-cookie", cookie]);
|
|
77
130
|
}
|
|
78
131
|
return new Response(this.body, {
|
|
79
132
|
status: this.statusCode,
|
|
80
|
-
headers:
|
|
133
|
+
headers: headersInit,
|
|
81
134
|
});
|
|
82
135
|
}
|
|
83
136
|
}
|
|
@@ -128,14 +181,41 @@ class BunRouter {
|
|
|
128
181
|
...(routeMatch ? routeMatch.route.handlers : []),
|
|
129
182
|
];
|
|
130
183
|
let chainCompleted = false;
|
|
184
|
+
let timeoutId;
|
|
185
|
+
let timedOut = false;
|
|
186
|
+
const timeoutMs = res.getHandlerTimeoutMs();
|
|
187
|
+
const timeoutPromise = typeof timeoutMs === "number" && timeoutMs > 0
|
|
188
|
+
? new Promise((resolve) => {
|
|
189
|
+
timeoutId = setTimeout(() => {
|
|
190
|
+
timedOut = true;
|
|
191
|
+
if (!res.isEnded()) {
|
|
192
|
+
res.status(504).json({ message: "Handler timeout" });
|
|
193
|
+
}
|
|
194
|
+
resolve(false);
|
|
195
|
+
}, timeoutMs);
|
|
196
|
+
})
|
|
197
|
+
: null;
|
|
131
198
|
try {
|
|
132
|
-
chainCompleted =
|
|
199
|
+
chainCompleted = timeoutPromise
|
|
200
|
+
? await Promise.race([runHandlers(handlers, req, res), timeoutPromise])
|
|
201
|
+
: await runHandlers(handlers, req, res);
|
|
133
202
|
}
|
|
134
203
|
catch (error) {
|
|
135
|
-
res.
|
|
204
|
+
if (!res.isEnded()) {
|
|
205
|
+
res.status(500).json({ message: "Internal server error" });
|
|
206
|
+
}
|
|
136
207
|
done(error);
|
|
137
208
|
return;
|
|
138
209
|
}
|
|
210
|
+
finally {
|
|
211
|
+
if (timeoutId) {
|
|
212
|
+
clearTimeout(timeoutId);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (timedOut) {
|
|
216
|
+
done();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
139
219
|
if (!routeMatch) {
|
|
140
220
|
if (chainCompleted && !res.isEnded() && !suppressNotFound) {
|
|
141
221
|
res.status(404).json({ message: "Not found" });
|
|
@@ -195,11 +275,25 @@ class BunRouter {
|
|
|
195
275
|
}
|
|
196
276
|
}
|
|
197
277
|
class BunApp extends BunRouter {
|
|
278
|
+
constructor() {
|
|
279
|
+
super(...arguments);
|
|
280
|
+
this.settings = new Map();
|
|
281
|
+
}
|
|
282
|
+
set(key, value) {
|
|
283
|
+
this.settings.set(key, value);
|
|
284
|
+
}
|
|
285
|
+
get(key) {
|
|
286
|
+
return this.settings.get(key);
|
|
287
|
+
}
|
|
198
288
|
createFetchHandler() {
|
|
199
289
|
return async (request, server) => {
|
|
200
290
|
const client = server?.requestIP?.(request);
|
|
201
|
-
const
|
|
202
|
-
const
|
|
291
|
+
const cookieJar = request.cookies;
|
|
292
|
+
const handlerTimeoutMs = this.get("handlerTimeoutMs");
|
|
293
|
+
const trustProxy = this.get("trustProxy") === true;
|
|
294
|
+
const ipOverride = trustProxy ? undefined : client?.address;
|
|
295
|
+
const req = createRequest(request, ipOverride);
|
|
296
|
+
const res = new BunResponse(cookieJar, typeof handlerTimeoutMs === "number" ? handlerTimeoutMs : undefined);
|
|
203
297
|
await this.handle(req, res, () => undefined);
|
|
204
298
|
if (!res.isEnded()) {
|
|
205
299
|
res.status(204).end();
|
|
@@ -220,8 +314,9 @@ class BunAdapter {
|
|
|
220
314
|
}
|
|
221
315
|
configure(app, _port) {
|
|
222
316
|
const bunApp = app;
|
|
223
|
-
bunApp.use(
|
|
224
|
-
bunApp.use(
|
|
317
|
+
bunApp.use(createMultipartBodyParser(bunApp));
|
|
318
|
+
bunApp.use(createJsonBodyParser(bunApp));
|
|
319
|
+
bunApp.use(createUrlEncodedBodyParser(bunApp));
|
|
225
320
|
}
|
|
226
321
|
listen(app, port, onListen) {
|
|
227
322
|
const bunApp = app;
|
|
@@ -239,33 +334,121 @@ class BunAdapter {
|
|
|
239
334
|
}
|
|
240
335
|
}
|
|
241
336
|
exports.BunAdapter = BunAdapter;
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
337
|
+
const createJsonBodyParser = (app) => {
|
|
338
|
+
return async (req, res, next) => {
|
|
339
|
+
if (!req.raw || req.body !== undefined) {
|
|
340
|
+
return next();
|
|
341
|
+
}
|
|
342
|
+
const contentType = String(req.headers["content-type"] || "");
|
|
343
|
+
if (!contentType.includes("application/json")) {
|
|
344
|
+
return next();
|
|
345
|
+
}
|
|
346
|
+
const limit = getBodyLimit(app);
|
|
347
|
+
const contentLength = parseContentLength(req.headers["content-length"]);
|
|
348
|
+
if (contentLength !== undefined && contentLength > limit) {
|
|
349
|
+
res.status(413).json({ message: "Payload too large" });
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
req.body = await req.raw.json();
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
res.status(400).json({ message: "Invalid JSON" });
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
next();
|
|
360
|
+
};
|
|
257
361
|
};
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
362
|
+
const createUrlEncodedBodyParser = (app) => {
|
|
363
|
+
return async (req, res, next) => {
|
|
364
|
+
if (!req.raw || req.body !== undefined) {
|
|
365
|
+
return next();
|
|
366
|
+
}
|
|
367
|
+
const contentType = String(req.headers["content-type"] || "");
|
|
368
|
+
if (!contentType.includes("application/x-www-form-urlencoded")) {
|
|
369
|
+
return next();
|
|
370
|
+
}
|
|
371
|
+
const limit = getBodyLimit(app);
|
|
372
|
+
const contentLength = parseContentLength(req.headers["content-length"]);
|
|
373
|
+
if (contentLength !== undefined && contentLength > limit) {
|
|
374
|
+
res.status(413).json({ message: "Payload too large" });
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const text = await req.raw.text();
|
|
378
|
+
req.body = Object.fromEntries(new URLSearchParams(text));
|
|
379
|
+
next();
|
|
380
|
+
};
|
|
381
|
+
};
|
|
382
|
+
const createMultipartBodyParser = (app) => {
|
|
383
|
+
return async (req, res, next) => {
|
|
384
|
+
if (!req.raw || req.body !== undefined) {
|
|
385
|
+
return next();
|
|
386
|
+
}
|
|
387
|
+
const contentType = String(req.headers["content-type"] || "");
|
|
388
|
+
if (!contentType.includes("multipart/form-data")) {
|
|
389
|
+
return next();
|
|
390
|
+
}
|
|
391
|
+
const options = normalizeMultipartOptions(app.get("multipart"));
|
|
392
|
+
const lengthHeader = req.headers["content-length"];
|
|
393
|
+
const contentLength = parseContentLength(lengthHeader);
|
|
394
|
+
if (contentLength !== undefined && contentLength > options.maxBodyBytes) {
|
|
395
|
+
res.status(413).json({ message: "Payload too large" });
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
const formData = await req.raw.formData();
|
|
400
|
+
const fields = {};
|
|
401
|
+
const files = {};
|
|
402
|
+
let fileCount = 0;
|
|
403
|
+
for (const [key, value] of formData.entries()) {
|
|
404
|
+
if (isFile(value)) {
|
|
405
|
+
if (value.size > options.maxFileBytes) {
|
|
406
|
+
res.status(413).json({ message: "Payload too large" });
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (!isMimeAllowed(value.type, options.allowedMimeTypes)) {
|
|
410
|
+
res.status(415).json({ message: "Unsupported media type" });
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
fileCount += 1;
|
|
414
|
+
if (fileCount > options.maxFiles) {
|
|
415
|
+
res.status(413).json({ message: "Payload too large" });
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const bucket = files[key];
|
|
419
|
+
if (bucket) {
|
|
420
|
+
bucket.push(value);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
files[key] = [value];
|
|
424
|
+
}
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
const existing = fields[key];
|
|
428
|
+
const textValue = String(value);
|
|
429
|
+
if (existing === undefined) {
|
|
430
|
+
fields[key] = textValue;
|
|
431
|
+
}
|
|
432
|
+
else if (Array.isArray(existing)) {
|
|
433
|
+
existing.push(textValue);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
fields[key] = [existing, textValue];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (Object.keys(fields).length > 0) {
|
|
440
|
+
req.body = fields;
|
|
441
|
+
}
|
|
442
|
+
if (Object.keys(files).length > 0) {
|
|
443
|
+
req.files = files;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
res.status(400).json({ message: "Invalid multipart form data" });
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
next();
|
|
451
|
+
};
|
|
269
452
|
};
|
|
270
453
|
function createRequest(request, ipOverride) {
|
|
271
454
|
const url = new URL(request.url);
|
|
@@ -306,6 +489,109 @@ function toQueryRecord(search) {
|
|
|
306
489
|
}
|
|
307
490
|
return record;
|
|
308
491
|
}
|
|
492
|
+
const DEFAULT_MULTIPART_OPTIONS = {
|
|
493
|
+
maxBodyBytes: 10 * 1024 * 1024,
|
|
494
|
+
maxFileBytes: 10 * 1024 * 1024,
|
|
495
|
+
maxFiles: 10,
|
|
496
|
+
};
|
|
497
|
+
function serializeCookie(name, value, options) {
|
|
498
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
499
|
+
if (options.maxAge !== undefined) {
|
|
500
|
+
const maxAgeSeconds = Math.floor(options.maxAge / 1000);
|
|
501
|
+
parts.push(`Max-Age=${maxAgeSeconds}`);
|
|
502
|
+
if (!options.expires) {
|
|
503
|
+
parts.push(`Expires=${new Date(Date.now() + options.maxAge).toUTCString()}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (options.domain) {
|
|
507
|
+
parts.push(`Domain=${options.domain}`);
|
|
508
|
+
}
|
|
509
|
+
if (options.path) {
|
|
510
|
+
parts.push(`Path=${options.path}`);
|
|
511
|
+
}
|
|
512
|
+
if (options.expires) {
|
|
513
|
+
parts.push(`Expires=${options.expires.toUTCString()}`);
|
|
514
|
+
}
|
|
515
|
+
if (options.httpOnly) {
|
|
516
|
+
parts.push("HttpOnly");
|
|
517
|
+
}
|
|
518
|
+
if (options.secure || options.sameSite === "none") {
|
|
519
|
+
parts.push("Secure");
|
|
520
|
+
}
|
|
521
|
+
if (options.sameSite) {
|
|
522
|
+
const normalized = options.sameSite === "none"
|
|
523
|
+
? "None"
|
|
524
|
+
: options.sameSite === "strict"
|
|
525
|
+
? "Strict"
|
|
526
|
+
: "Lax";
|
|
527
|
+
parts.push(`SameSite=${normalized}`);
|
|
528
|
+
}
|
|
529
|
+
return parts.join("; ");
|
|
530
|
+
}
|
|
531
|
+
function toCookieJarOptions(options) {
|
|
532
|
+
const sameSite = options.sameSite === "none"
|
|
533
|
+
? "None"
|
|
534
|
+
: options.sameSite === "strict"
|
|
535
|
+
? "Strict"
|
|
536
|
+
: options.sameSite === "lax"
|
|
537
|
+
? "Lax"
|
|
538
|
+
: undefined;
|
|
539
|
+
const maxAge = options.maxAge === undefined
|
|
540
|
+
? undefined
|
|
541
|
+
: Math.floor(options.maxAge / 1000);
|
|
542
|
+
return {
|
|
543
|
+
maxAge,
|
|
544
|
+
domain: options.domain,
|
|
545
|
+
path: options.path,
|
|
546
|
+
expires: options.expires,
|
|
547
|
+
httpOnly: options.httpOnly,
|
|
548
|
+
secure: options.secure || options.sameSite === "none",
|
|
549
|
+
sameSite,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function getBodyLimit(app) {
|
|
553
|
+
return normalizeMultipartOptions(app.get("multipart")).maxBodyBytes;
|
|
554
|
+
}
|
|
555
|
+
function normalizeMultipartOptions(input) {
|
|
556
|
+
if (!input || typeof input !== "object") {
|
|
557
|
+
return { ...DEFAULT_MULTIPART_OPTIONS };
|
|
558
|
+
}
|
|
559
|
+
const value = input;
|
|
560
|
+
return {
|
|
561
|
+
maxBodyBytes: value.maxBodyBytes ?? DEFAULT_MULTIPART_OPTIONS.maxBodyBytes,
|
|
562
|
+
maxFileBytes: value.maxFileBytes ?? DEFAULT_MULTIPART_OPTIONS.maxFileBytes,
|
|
563
|
+
maxFiles: value.maxFiles ?? DEFAULT_MULTIPART_OPTIONS.maxFiles,
|
|
564
|
+
allowedMimeTypes: value.allowedMimeTypes,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function isMimeAllowed(type, allowed) {
|
|
568
|
+
if (!allowed || allowed.length === 0) {
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
const normalized = type.toLowerCase();
|
|
572
|
+
for (const entry of allowed) {
|
|
573
|
+
const rule = entry.toLowerCase();
|
|
574
|
+
if (rule.endsWith("/*")) {
|
|
575
|
+
const prefix = rule.slice(0, -1);
|
|
576
|
+
if (normalized.startsWith(prefix)) {
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (normalized === rule) {
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
function isFile(value) {
|
|
588
|
+
if (!value || typeof value !== "object") {
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
return (typeof value.arrayBuffer === "function" &&
|
|
592
|
+
typeof value.name === "string" &&
|
|
593
|
+
typeof value.size === "number");
|
|
594
|
+
}
|
|
309
595
|
function parseCookies(cookieHeader) {
|
|
310
596
|
if (!cookieHeader) {
|
|
311
597
|
return undefined;
|
|
@@ -316,10 +602,47 @@ function parseCookies(cookieHeader) {
|
|
|
316
602
|
if (!name) {
|
|
317
603
|
return;
|
|
318
604
|
}
|
|
319
|
-
|
|
605
|
+
try {
|
|
606
|
+
cookies[name] = decodeURIComponent(rest.join("="));
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
cookies[name] = rest.join("=");
|
|
610
|
+
}
|
|
320
611
|
});
|
|
321
612
|
return cookies;
|
|
322
613
|
}
|
|
614
|
+
function parseContentLength(header) {
|
|
615
|
+
if (header === undefined) {
|
|
616
|
+
return undefined;
|
|
617
|
+
}
|
|
618
|
+
const value = Array.isArray(header) ? header[0] : header;
|
|
619
|
+
if (!value) {
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
const parsed = Number.parseInt(value, 10);
|
|
623
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
624
|
+
}
|
|
625
|
+
function readSetCookieHeaders(headers) {
|
|
626
|
+
const bunHeaders = headers;
|
|
627
|
+
const setCookieFromApi = bunHeaders.getSetCookie?.() ?? bunHeaders.getAll?.("Set-Cookie");
|
|
628
|
+
if (setCookieFromApi && setCookieFromApi.length > 0) {
|
|
629
|
+
return setCookieFromApi;
|
|
630
|
+
}
|
|
631
|
+
const setCookies = [];
|
|
632
|
+
headers.forEach((value, key) => {
|
|
633
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
634
|
+
setCookies.push(value);
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
return setCookies;
|
|
638
|
+
}
|
|
639
|
+
function getFiles(req, field) {
|
|
640
|
+
const map = (req.files ?? {});
|
|
641
|
+
return map[field] ?? [];
|
|
642
|
+
}
|
|
643
|
+
function getFile(req, field) {
|
|
644
|
+
return getFiles(req, field)[0];
|
|
645
|
+
}
|
|
323
646
|
function extractIp(headers) {
|
|
324
647
|
const forwarded = headers["x-forwarded-for"];
|
|
325
648
|
if (forwarded) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
export declare function Controller(basePath: string): (
|
|
2
|
+
export declare function Controller(basePath: string): (...args: any[]) => void;
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Controller = Controller;
|
|
4
4
|
const MetadataKeys_1 = require("./MetadataKeys");
|
|
5
|
+
const DecoratorGuards_1 = require("./DecoratorGuards");
|
|
5
6
|
require("reflect-metadata");
|
|
6
7
|
function Controller(basePath) {
|
|
7
|
-
return function (
|
|
8
|
+
return function (...args) {
|
|
9
|
+
(0, DecoratorGuards_1.assertLegacyDecorator)(args, "@Controller");
|
|
10
|
+
const target = args[0];
|
|
8
11
|
Reflect.defineMetadata(MetadataKeys_1.MetadataKeys.BASE_PATH, basePath, target);
|
|
9
12
|
};
|
|
10
13
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function assertLegacyDecorator(args: any[], decoratorName: string): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertLegacyDecorator = assertLegacyDecorator;
|
|
4
|
+
function assertLegacyDecorator(args, decoratorName) {
|
|
5
|
+
const context = args[1];
|
|
6
|
+
if (args.length === 2 &&
|
|
7
|
+
context &&
|
|
8
|
+
typeof context === "object" &&
|
|
9
|
+
"kind" in context) {
|
|
10
|
+
throw new Error(`[ts-express-server] ${decoratorName} is not compatible with ` +
|
|
11
|
+
`standard TypeScript decorators. Enable "experimentalDecorators": true ` +
|
|
12
|
+
`in your tsconfig and ensure your files are included by that config.`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -11,8 +11,8 @@ export interface IRouter {
|
|
|
11
11
|
path: string;
|
|
12
12
|
handlerName: string | symbol;
|
|
13
13
|
}
|
|
14
|
-
export declare const Get: (path: string) => (
|
|
15
|
-
export declare const Post: (path: string) => (
|
|
16
|
-
export declare const Put: (path: string) => (
|
|
17
|
-
export declare const Delete: (path: string) => (
|
|
18
|
-
export declare const Patch: (path: string) => (
|
|
14
|
+
export declare const Get: (path: string) => (...args: any[]) => void;
|
|
15
|
+
export declare const Post: (path: string) => (...args: any[]) => void;
|
|
16
|
+
export declare const Put: (path: string) => (...args: any[]) => void;
|
|
17
|
+
export declare const Delete: (path: string) => (...args: any[]) => void;
|
|
18
|
+
export declare const Patch: (path: string) => (...args: any[]) => void;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = exports.Methods = void 0;
|
|
4
4
|
const MetadataKeys_1 = require("./MetadataKeys");
|
|
5
|
+
const DecoratorGuards_1 = require("./DecoratorGuards");
|
|
5
6
|
require("reflect-metadata");
|
|
6
7
|
var Methods;
|
|
7
8
|
(function (Methods) {
|
|
@@ -13,7 +14,10 @@ var Methods;
|
|
|
13
14
|
})(Methods || (exports.Methods = Methods = {}));
|
|
14
15
|
function methodDecorator(method) {
|
|
15
16
|
return function (path) {
|
|
16
|
-
return function (
|
|
17
|
+
return function (...args) {
|
|
18
|
+
(0, DecoratorGuards_1.assertLegacyDecorator)(args, `@${method.toUpperCase()}`);
|
|
19
|
+
const target = args[0];
|
|
20
|
+
const key = args[1];
|
|
17
21
|
const routers = Reflect.getMetadata(MetadataKeys_1.MetadataKeys.ROUTERS, target) || [];
|
|
18
22
|
routers.push({
|
|
19
23
|
method,
|
package/dist/decorators/Use.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ServerHandler } from "../abstract";
|
|
2
2
|
import "reflect-metadata";
|
|
3
|
-
export declare function Use(middleware: ServerHandler | ServerHandler[]): (
|
|
3
|
+
export declare function Use(middleware: ServerHandler | ServerHandler[]): (...args: any[]) => void;
|
package/dist/decorators/Use.js
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Use = Use;
|
|
4
4
|
const MetadataKeys_1 = require("./MetadataKeys");
|
|
5
|
+
const DecoratorGuards_1 = require("./DecoratorGuards");
|
|
5
6
|
require("reflect-metadata");
|
|
6
7
|
function Use(middleware) {
|
|
7
|
-
return function (
|
|
8
|
+
return function (...args) {
|
|
9
|
+
(0, DecoratorGuards_1.assertLegacyDecorator)(args, "@Use");
|
|
10
|
+
const target = args[0];
|
|
11
|
+
const key = args[1];
|
|
8
12
|
const newMiddlewares = Array.isArray(middleware)
|
|
9
13
|
? middleware
|
|
10
14
|
: [middleware];
|