@server/next 0.36.1 → 0.37.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/index.d.ts +36 -32
- package/index.js +195 -151
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -15,6 +15,38 @@ declare class UploadPipeline {
|
|
|
15
15
|
}
|
|
16
16
|
declare function upload(bucket?: Bucket | string | null): UploadPipeline;
|
|
17
17
|
|
|
18
|
+
type CookieOptions = string | string[] | Cookie | Cookie[] | null;
|
|
19
|
+
interface ResponseData {
|
|
20
|
+
headers: Headers;
|
|
21
|
+
status?: number;
|
|
22
|
+
}
|
|
23
|
+
declare class Reply$1 {
|
|
24
|
+
res: ResponseData;
|
|
25
|
+
constructor();
|
|
26
|
+
status(status: number): this;
|
|
27
|
+
type(type?: string): this;
|
|
28
|
+
download(name?: string): this;
|
|
29
|
+
headers(key: string | Record<string, string>, value?: string): this;
|
|
30
|
+
cache(value: CacheOption): this;
|
|
31
|
+
cookies(key: string | Record<string, CookieOptions>, value?: CookieOptions): this;
|
|
32
|
+
json(body: unknown): Response;
|
|
33
|
+
redirect(path: string): Response;
|
|
34
|
+
file(path: string): Promise<Response>;
|
|
35
|
+
send(body?: string | Buffer | ReadableStream | any): Response;
|
|
36
|
+
}
|
|
37
|
+
type Params<K extends keyof Reply$1> = Reply$1[K] extends (...args: infer A) => any ? A : never;
|
|
38
|
+
declare const status: (...args: Params<"status">) => Reply$1;
|
|
39
|
+
declare const headers: (...args: Params<"headers">) => Reply$1;
|
|
40
|
+
declare const type: (...args: Params<"type">) => Reply$1;
|
|
41
|
+
declare const cache: (...args: Params<"cache">) => Reply$1;
|
|
42
|
+
declare const download: (...args: Params<"download">) => Reply$1;
|
|
43
|
+
declare const cookies: (...args: Params<"cookies">) => Reply$1;
|
|
44
|
+
declare const send: (...args: Params<"send">) => Response;
|
|
45
|
+
declare const json: (...args: Params<"json">) => Response;
|
|
46
|
+
declare const file: (...args: Params<"file">) => Promise<Response>;
|
|
47
|
+
declare const redirect: (...args: Params<"redirect">) => Response;
|
|
48
|
+
|
|
49
|
+
type Reply = ReturnType<typeof status>;
|
|
18
50
|
type Method = "get" | "post" | "put" | "patch" | "delete" | "head" | "options" | "socket";
|
|
19
51
|
type ServerConfig<Session = {}, User = {}> = {
|
|
20
52
|
Session?: Session;
|
|
@@ -172,6 +204,7 @@ type SecuritySettings = {
|
|
|
172
204
|
hsts: string | null;
|
|
173
205
|
};
|
|
174
206
|
type OnError = (error: Error, ctx: Context) => Response | Promise<Response>;
|
|
207
|
+
type OnResponse = (response: Response, ctx: Context) => Response | void | Promise<Response | void>;
|
|
175
208
|
type Options = {
|
|
176
209
|
port?: number;
|
|
177
210
|
secret?: string;
|
|
@@ -186,6 +219,7 @@ type Options = {
|
|
|
186
219
|
auth?: AuthOption;
|
|
187
220
|
openapi?: any;
|
|
188
221
|
onError?: OnError;
|
|
222
|
+
onResponse?: OnResponse;
|
|
189
223
|
log?: LogLevel | boolean;
|
|
190
224
|
favicon?: string | BucketFile;
|
|
191
225
|
security?: boolean | SecurityOptions;
|
|
@@ -206,6 +240,7 @@ type Settings = {
|
|
|
206
240
|
auth?: AuthSettings;
|
|
207
241
|
openapi?: any;
|
|
208
242
|
onError?: OnError;
|
|
243
|
+
onResponse?: OnResponse;
|
|
209
244
|
log: Logger;
|
|
210
245
|
favicon?: string | BucketFile;
|
|
211
246
|
security: SecuritySettings;
|
|
@@ -266,7 +301,7 @@ type Context<Params extends Record<string, string | undefined> = Record<string,
|
|
|
266
301
|
};
|
|
267
302
|
app: Server;
|
|
268
303
|
};
|
|
269
|
-
type InlineReply = Response | {
|
|
304
|
+
type InlineReply = Response | Reply | {
|
|
270
305
|
body: string;
|
|
271
306
|
headers?: Headers;
|
|
272
307
|
} | SerializableValue | JSX.Element | Buffer | ReadableStream;
|
|
@@ -337,37 +372,6 @@ declare class Router<O extends ServerConfig = object> {
|
|
|
337
372
|
}
|
|
338
373
|
declare function router(): Router;
|
|
339
374
|
|
|
340
|
-
type CookieOptions = string | string[] | Cookie | Cookie[] | null;
|
|
341
|
-
interface ResponseData {
|
|
342
|
-
headers: Headers;
|
|
343
|
-
status?: number;
|
|
344
|
-
}
|
|
345
|
-
declare class Reply {
|
|
346
|
-
res: ResponseData;
|
|
347
|
-
constructor();
|
|
348
|
-
status(status: number): this;
|
|
349
|
-
type(type?: string): this;
|
|
350
|
-
download(name?: string): this;
|
|
351
|
-
headers(key: string | Record<string, string>, value?: string): this;
|
|
352
|
-
cache(value: CacheOption): this;
|
|
353
|
-
cookies(key: string | Record<string, CookieOptions>, value?: CookieOptions): this;
|
|
354
|
-
json(body: unknown): Response;
|
|
355
|
-
redirect(path: string): Response;
|
|
356
|
-
file(path: string): Promise<Response>;
|
|
357
|
-
send(body?: string | Buffer | ReadableStream | any): Response;
|
|
358
|
-
}
|
|
359
|
-
type Params<K extends keyof Reply> = Reply[K] extends (...args: infer A) => any ? A : never;
|
|
360
|
-
declare const status: (...args: Params<"status">) => Reply;
|
|
361
|
-
declare const headers: (...args: Params<"headers">) => Reply;
|
|
362
|
-
declare const type: (...args: Params<"type">) => Reply;
|
|
363
|
-
declare const cache: (...args: Params<"cache">) => Reply;
|
|
364
|
-
declare const download: (...args: Params<"download">) => Reply;
|
|
365
|
-
declare const cookies: (...args: Params<"cookies">) => Reply;
|
|
366
|
-
declare const send: (...args: Params<"send">) => Response;
|
|
367
|
-
declare const json: (...args: Params<"json">) => Response;
|
|
368
|
-
declare const file: (...args: Params<"file">) => Promise<Response>;
|
|
369
|
-
declare const redirect: (...args: Params<"redirect">) => Response;
|
|
370
|
-
|
|
371
375
|
declare class Server<O extends ServerConfig = {}> extends Router<O> {
|
|
372
376
|
settings: Settings;
|
|
373
377
|
platform: Platform;
|
package/index.js
CHANGED
|
@@ -89,127 +89,13 @@ if (typeof process !== "undefined") {
|
|
|
89
89
|
Object.assign(globalThis.env, process.env);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// src/helpers/
|
|
93
|
-
var
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
let h2 = 1103547991 ^ seed;
|
|
99
|
-
for (let i = 0, ch; i < str.length; i++) {
|
|
100
|
-
ch = str.charCodeAt(i);
|
|
101
|
-
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
102
|
-
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
103
|
-
}
|
|
104
|
-
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
105
|
-
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
106
|
-
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
107
|
-
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
108
|
-
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
109
|
-
};
|
|
110
|
-
var hash = (str, size) => {
|
|
111
|
-
let chars = "";
|
|
112
|
-
let num = cyrb53(str);
|
|
113
|
-
for (let i = 0; i < size; i++) {
|
|
114
|
-
if (num < alphabet.length) num = cyrb53(str, i);
|
|
115
|
-
chars += alphabet[num % alphabet.length];
|
|
116
|
-
num = Math.floor(num / alphabet.length);
|
|
117
|
-
}
|
|
118
|
-
return chars;
|
|
119
|
-
};
|
|
120
|
-
var randomId = (size = 16) => {
|
|
121
|
-
let id = "";
|
|
122
|
-
const bytes = random(size);
|
|
123
|
-
while (size--) {
|
|
124
|
-
id += alphabet[bytes[size] & 61];
|
|
92
|
+
// src/helpers/StatusError.ts
|
|
93
|
+
var StatusError = class extends Error {
|
|
94
|
+
status;
|
|
95
|
+
constructor(msg, status2 = 500) {
|
|
96
|
+
super(msg);
|
|
97
|
+
this.status = status2;
|
|
125
98
|
}
|
|
126
|
-
return id;
|
|
127
|
-
};
|
|
128
|
-
function createId(source, size = 16) {
|
|
129
|
-
if (source) return hash(source, size);
|
|
130
|
-
return randomId(size);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// src/helpers/mimes.ts
|
|
134
|
-
var mimes_default = {
|
|
135
|
-
aac: "audio/aac",
|
|
136
|
-
abw: "application/x-abiword",
|
|
137
|
-
arc: "application/x-freearc",
|
|
138
|
-
avif: "image/avif",
|
|
139
|
-
avi: "video/x-msvideo",
|
|
140
|
-
azw: "application/vnd.amazon.ebook",
|
|
141
|
-
bin: "application/octet-stream",
|
|
142
|
-
bmp: "image/bmp",
|
|
143
|
-
bz: "application/x-bzip",
|
|
144
|
-
bz2: "application/x-bzip2",
|
|
145
|
-
cda: "application/x-cdf",
|
|
146
|
-
csh: "application/x-csh",
|
|
147
|
-
css: "text/css",
|
|
148
|
-
csv: "text/csv",
|
|
149
|
-
doc: "application/msword",
|
|
150
|
-
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
151
|
-
eot: "application/vnd.ms-fontobject",
|
|
152
|
-
epub: "application/epub+zip",
|
|
153
|
-
gz: "application/gzip",
|
|
154
|
-
gif: "image/gif",
|
|
155
|
-
htm: "text/html",
|
|
156
|
-
html: "text/html",
|
|
157
|
-
ico: "image/vnd.microsoft.icon",
|
|
158
|
-
ics: "text/calendar",
|
|
159
|
-
jar: "application/java-archive",
|
|
160
|
-
jpeg: "image/jpeg",
|
|
161
|
-
jpg: "image/jpeg",
|
|
162
|
-
js: "text/javascript",
|
|
163
|
-
json: "application/json",
|
|
164
|
-
jsonld: "application/ld+json",
|
|
165
|
-
md: "text/markdown",
|
|
166
|
-
mid: "audio/midi",
|
|
167
|
-
midi: "audio/midi",
|
|
168
|
-
mjs: "text/javascript",
|
|
169
|
-
mp3: "audio/mpeg",
|
|
170
|
-
mp4: "video/mp4",
|
|
171
|
-
mpeg: "video/mpeg",
|
|
172
|
-
mpkg: "application/vnd.apple.installer+xml",
|
|
173
|
-
odp: "application/vnd.oasis.opendocument.presentation",
|
|
174
|
-
ods: "application/vnd.oasis.opendocument.spreadsheet",
|
|
175
|
-
odt: "application/vnd.oasis.opendocument.text",
|
|
176
|
-
oga: "audio/ogg",
|
|
177
|
-
ogv: "video/ogg",
|
|
178
|
-
ogx: "application/ogg",
|
|
179
|
-
opus: "audio/opus",
|
|
180
|
-
otf: "font/otf",
|
|
181
|
-
png: "image/png",
|
|
182
|
-
pdf: "application/pdf",
|
|
183
|
-
php: "application/x-httpd-php",
|
|
184
|
-
ppt: "application/vnd.ms-powerpoint",
|
|
185
|
-
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
186
|
-
rar: "application/vnd.rar",
|
|
187
|
-
rtf: "application/rtf",
|
|
188
|
-
sh: "application/x-sh",
|
|
189
|
-
svg: "image/svg+xml",
|
|
190
|
-
tar: "application/x-tar",
|
|
191
|
-
text: "text/plain",
|
|
192
|
-
tif: "image/tiff",
|
|
193
|
-
tiff: "image/tiff",
|
|
194
|
-
ts: "video/mp2t",
|
|
195
|
-
ttf: "font/ttf",
|
|
196
|
-
txt: "text/plain",
|
|
197
|
-
vsd: "application/vnd.visio",
|
|
198
|
-
wav: "audio/wav",
|
|
199
|
-
weba: "audio/webm",
|
|
200
|
-
webm: "video/webm",
|
|
201
|
-
webp: "image/webp",
|
|
202
|
-
woff: "font/woff",
|
|
203
|
-
woff2: "font/woff2",
|
|
204
|
-
xhtml: "application/xhtml+xml",
|
|
205
|
-
xls: "application/vnd.ms-excel",
|
|
206
|
-
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
207
|
-
xml: "application/xml",
|
|
208
|
-
xul: "application/vnd.mozilla.xul+xml",
|
|
209
|
-
zip: "application/zip",
|
|
210
|
-
"3gp": "video/3gpp",
|
|
211
|
-
"3g2": "video/3gpp2",
|
|
212
|
-
"7z": "application/x-7z-compressed"
|
|
213
99
|
};
|
|
214
100
|
|
|
215
101
|
// src/helpers/bucket.ts
|
|
@@ -311,6 +197,47 @@ function bucket(root) {
|
|
|
311
197
|
);
|
|
312
198
|
}
|
|
313
199
|
|
|
200
|
+
// src/helpers/createId.ts
|
|
201
|
+
var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
|
|
202
|
+
var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
|
|
203
|
+
var cyrb53 = (str, seed = 0) => {
|
|
204
|
+
if (typeof str !== "string") str = String(str);
|
|
205
|
+
let h1 = 3735928559 ^ seed;
|
|
206
|
+
let h2 = 1103547991 ^ seed;
|
|
207
|
+
for (let i = 0, ch; i < str.length; i++) {
|
|
208
|
+
ch = str.charCodeAt(i);
|
|
209
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
210
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
211
|
+
}
|
|
212
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
213
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
214
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
215
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
216
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
217
|
+
};
|
|
218
|
+
var hash = (str, size) => {
|
|
219
|
+
let chars = "";
|
|
220
|
+
let num = cyrb53(str);
|
|
221
|
+
for (let i = 0; i < size; i++) {
|
|
222
|
+
if (num < alphabet.length) num = cyrb53(str, i);
|
|
223
|
+
chars += alphabet[num % alphabet.length];
|
|
224
|
+
num = Math.floor(num / alphabet.length);
|
|
225
|
+
}
|
|
226
|
+
return chars;
|
|
227
|
+
};
|
|
228
|
+
var randomId = (size = 16) => {
|
|
229
|
+
let id = "";
|
|
230
|
+
const bytes = random(size);
|
|
231
|
+
while (size--) {
|
|
232
|
+
id += alphabet[bytes[size] & 61];
|
|
233
|
+
}
|
|
234
|
+
return id;
|
|
235
|
+
};
|
|
236
|
+
function createId(source, size = 16) {
|
|
237
|
+
if (source) return hash(source, size);
|
|
238
|
+
return randomId(size);
|
|
239
|
+
}
|
|
240
|
+
|
|
314
241
|
// src/helpers/upload.ts
|
|
315
242
|
function parseBytes(value) {
|
|
316
243
|
if (typeof value === "number") return value;
|
|
@@ -392,6 +319,108 @@ function upload(bucket2) {
|
|
|
392
319
|
return new UploadPipeline(bucket2);
|
|
393
320
|
}
|
|
394
321
|
|
|
322
|
+
// src/helpers/bodyLimit.ts
|
|
323
|
+
var INF = Number.POSITIVE_INFINITY;
|
|
324
|
+
var DEFAULT_MAX = "1mb";
|
|
325
|
+
var resolveMax = (max) => max === false ? INF : parseBytes(max == null ? DEFAULT_MAX : max);
|
|
326
|
+
var UNITS = ["b", "kb", "mb", "gb", "tb"];
|
|
327
|
+
function human(bytes) {
|
|
328
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return `${bytes}`;
|
|
329
|
+
const i = Math.min(
|
|
330
|
+
Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
331
|
+
UNITS.length - 1
|
|
332
|
+
);
|
|
333
|
+
const value = bytes / 1024 ** i;
|
|
334
|
+
const rounded = i === 0 ? Math.round(value) : Math.round(value * 10) / 10;
|
|
335
|
+
return `${rounded}${UNITS[i]}`;
|
|
336
|
+
}
|
|
337
|
+
var tooLarge = (max) => new StatusError(
|
|
338
|
+
`Request body exceeds the ${human(max)} limit. Raise it with body: { max: '10mb' } on the route or server, or set max: false to disable.`,
|
|
339
|
+
413
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// src/helpers/mimes.ts
|
|
343
|
+
var mimes_default = {
|
|
344
|
+
aac: "audio/aac",
|
|
345
|
+
abw: "application/x-abiword",
|
|
346
|
+
arc: "application/x-freearc",
|
|
347
|
+
avif: "image/avif",
|
|
348
|
+
avi: "video/x-msvideo",
|
|
349
|
+
azw: "application/vnd.amazon.ebook",
|
|
350
|
+
bin: "application/octet-stream",
|
|
351
|
+
bmp: "image/bmp",
|
|
352
|
+
bz: "application/x-bzip",
|
|
353
|
+
bz2: "application/x-bzip2",
|
|
354
|
+
cda: "application/x-cdf",
|
|
355
|
+
csh: "application/x-csh",
|
|
356
|
+
css: "text/css",
|
|
357
|
+
csv: "text/csv",
|
|
358
|
+
doc: "application/msword",
|
|
359
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
360
|
+
eot: "application/vnd.ms-fontobject",
|
|
361
|
+
epub: "application/epub+zip",
|
|
362
|
+
gz: "application/gzip",
|
|
363
|
+
gif: "image/gif",
|
|
364
|
+
htm: "text/html",
|
|
365
|
+
html: "text/html",
|
|
366
|
+
ico: "image/vnd.microsoft.icon",
|
|
367
|
+
ics: "text/calendar",
|
|
368
|
+
jar: "application/java-archive",
|
|
369
|
+
jpeg: "image/jpeg",
|
|
370
|
+
jpg: "image/jpeg",
|
|
371
|
+
js: "text/javascript",
|
|
372
|
+
json: "application/json",
|
|
373
|
+
jsonld: "application/ld+json",
|
|
374
|
+
md: "text/markdown",
|
|
375
|
+
mid: "audio/midi",
|
|
376
|
+
midi: "audio/midi",
|
|
377
|
+
mjs: "text/javascript",
|
|
378
|
+
mp3: "audio/mpeg",
|
|
379
|
+
mp4: "video/mp4",
|
|
380
|
+
mpeg: "video/mpeg",
|
|
381
|
+
mpkg: "application/vnd.apple.installer+xml",
|
|
382
|
+
odp: "application/vnd.oasis.opendocument.presentation",
|
|
383
|
+
ods: "application/vnd.oasis.opendocument.spreadsheet",
|
|
384
|
+
odt: "application/vnd.oasis.opendocument.text",
|
|
385
|
+
oga: "audio/ogg",
|
|
386
|
+
ogv: "video/ogg",
|
|
387
|
+
ogx: "application/ogg",
|
|
388
|
+
opus: "audio/opus",
|
|
389
|
+
otf: "font/otf",
|
|
390
|
+
png: "image/png",
|
|
391
|
+
pdf: "application/pdf",
|
|
392
|
+
php: "application/x-httpd-php",
|
|
393
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
394
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
395
|
+
rar: "application/vnd.rar",
|
|
396
|
+
rtf: "application/rtf",
|
|
397
|
+
sh: "application/x-sh",
|
|
398
|
+
svg: "image/svg+xml",
|
|
399
|
+
tar: "application/x-tar",
|
|
400
|
+
text: "text/plain",
|
|
401
|
+
tif: "image/tiff",
|
|
402
|
+
tiff: "image/tiff",
|
|
403
|
+
ts: "video/mp2t",
|
|
404
|
+
ttf: "font/ttf",
|
|
405
|
+
txt: "text/plain",
|
|
406
|
+
vsd: "application/vnd.visio",
|
|
407
|
+
wav: "audio/wav",
|
|
408
|
+
weba: "audio/webm",
|
|
409
|
+
webm: "video/webm",
|
|
410
|
+
webp: "image/webp",
|
|
411
|
+
woff: "font/woff",
|
|
412
|
+
woff2: "font/woff2",
|
|
413
|
+
xhtml: "application/xhtml+xml",
|
|
414
|
+
xls: "application/vnd.ms-excel",
|
|
415
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
416
|
+
xml: "application/xml",
|
|
417
|
+
xul: "application/vnd.mozilla.xul+xml",
|
|
418
|
+
zip: "application/zip",
|
|
419
|
+
"3gp": "video/3gpp",
|
|
420
|
+
"3g2": "video/3gpp2",
|
|
421
|
+
"7z": "application/x-7z-compressed"
|
|
422
|
+
};
|
|
423
|
+
|
|
395
424
|
// src/helpers/parseBody.ts
|
|
396
425
|
function getBoundary(header) {
|
|
397
426
|
if (!header) return null;
|
|
@@ -438,10 +467,18 @@ function toStream(input) {
|
|
|
438
467
|
}
|
|
439
468
|
});
|
|
440
469
|
}
|
|
441
|
-
async function toBuffer(input) {
|
|
442
|
-
if (!(input instanceof ReadableStream))
|
|
470
|
+
async function toBuffer(input, max = INF) {
|
|
471
|
+
if (!(input instanceof ReadableStream)) {
|
|
472
|
+
if (input.length > max) throw tooLarge(max);
|
|
473
|
+
return input;
|
|
474
|
+
}
|
|
443
475
|
const chunks = [];
|
|
444
|
-
|
|
476
|
+
let total = 0;
|
|
477
|
+
for await (const chunk of asIterable(input)) {
|
|
478
|
+
total += chunk.byteLength;
|
|
479
|
+
if (total > max) throw tooLarge(max);
|
|
480
|
+
chunks.push(Buffer.from(chunk));
|
|
481
|
+
}
|
|
445
482
|
return Buffer.concat(chunks);
|
|
446
483
|
}
|
|
447
484
|
function parseUrlEncoded(text) {
|
|
@@ -522,13 +559,21 @@ async function endPart(part, body) {
|
|
|
522
559
|
}
|
|
523
560
|
}
|
|
524
561
|
var BREAK = Buffer.from("\r\n\r\n");
|
|
525
|
-
async function parseMultipart(stream, boundary, dest) {
|
|
562
|
+
async function parseMultipart(stream, boundary, dest, max = INF) {
|
|
526
563
|
const delim = Buffer.from(`\r
|
|
527
564
|
--${boundary}`);
|
|
528
565
|
const body = {};
|
|
529
566
|
let buf = Buffer.from("\r\n");
|
|
530
567
|
let state = "boundary";
|
|
531
568
|
let part = null;
|
|
569
|
+
let textBytes = 0;
|
|
570
|
+
const feed = (p, data) => {
|
|
571
|
+
if (p.kind === "text") {
|
|
572
|
+
textBytes += data.length;
|
|
573
|
+
if (textBytes > max) throw tooLarge(max);
|
|
574
|
+
}
|
|
575
|
+
feedPart(p, data);
|
|
576
|
+
};
|
|
532
577
|
for await (const chunk of asIterable(stream)) {
|
|
533
578
|
buf = Buffer.concat([buf, Buffer.from(chunk)]);
|
|
534
579
|
let advanced = true;
|
|
@@ -560,13 +605,13 @@ async function parseMultipart(stream, boundary, dest) {
|
|
|
560
605
|
if (i === -1) {
|
|
561
606
|
const safe = buf.length - (delim.length - 1);
|
|
562
607
|
if (safe > 0 && part) {
|
|
563
|
-
|
|
608
|
+
feed(part, buf.subarray(0, safe));
|
|
564
609
|
buf = buf.subarray(safe);
|
|
565
610
|
}
|
|
566
611
|
break;
|
|
567
612
|
}
|
|
568
613
|
if (part) {
|
|
569
|
-
|
|
614
|
+
feed(part, buf.subarray(0, i));
|
|
570
615
|
await endPart(part, body);
|
|
571
616
|
part = null;
|
|
572
617
|
}
|
|
@@ -599,24 +644,24 @@ async function streamToBucket(stream, type2, bucket2) {
|
|
|
599
644
|
if (!size) return void 0;
|
|
600
645
|
return { name: id, id, path: file2.path, type: type2, size };
|
|
601
646
|
}
|
|
602
|
-
async function parseBody(input, contentType, dest) {
|
|
647
|
+
async function parseBody(input, contentType, dest, max = INF) {
|
|
603
648
|
const type2 = Array.isArray(contentType) ? contentType[0] : contentType;
|
|
604
649
|
const boundary = type2 && /multipart\/form-data/i.test(type2) ? getBoundary(type2) : null;
|
|
605
|
-
if (boundary) return parseMultipart(toStream(input), boundary, dest);
|
|
650
|
+
if (boundary) return parseMultipart(toStream(input), boundary, dest, max);
|
|
606
651
|
if (!type2 || /^text\//i.test(type2)) {
|
|
607
|
-
const buf = await toBuffer(input);
|
|
652
|
+
const buf = await toBuffer(input, max);
|
|
608
653
|
return buf.length ? buf.toString("utf-8") : void 0;
|
|
609
654
|
}
|
|
610
655
|
if (/application\/json/i.test(type2)) {
|
|
611
|
-
const buf = await toBuffer(input);
|
|
656
|
+
const buf = await toBuffer(input, max);
|
|
612
657
|
return buf.length ? JSON.parse(buf.toString("utf-8")) : void 0;
|
|
613
658
|
}
|
|
614
659
|
if (/application\/x-www-form-urlencoded/i.test(type2)) {
|
|
615
|
-
const buf = await toBuffer(input);
|
|
660
|
+
const buf = await toBuffer(input, max);
|
|
616
661
|
return buf.length ? parseUrlEncoded(buf.toString("utf-8")) : void 0;
|
|
617
662
|
}
|
|
618
663
|
if (!dest) {
|
|
619
|
-
const buf = await toBuffer(input);
|
|
664
|
+
const buf = await toBuffer(input, max);
|
|
620
665
|
return buf.length ? buf : void 0;
|
|
621
666
|
}
|
|
622
667
|
if (dest instanceof UploadPipeline) {
|
|
@@ -626,19 +671,7 @@ async function parseBody(input, contentType, dest) {
|
|
|
626
671
|
return streamToBucket(toStream(input), type2, dest);
|
|
627
672
|
}
|
|
628
673
|
|
|
629
|
-
// src/helpers/StatusError.ts
|
|
630
|
-
var StatusError = class extends Error {
|
|
631
|
-
status;
|
|
632
|
-
constructor(msg, status2 = 500) {
|
|
633
|
-
super(msg);
|
|
634
|
-
this.status = status2;
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
|
|
638
674
|
// src/helpers/body.ts
|
|
639
|
-
var INF = Number.POSITIVE_INFINITY;
|
|
640
|
-
var resolveMax = (max) => max === false || max == null ? INF : parseBytes(max);
|
|
641
|
-
var tooLarge = (max) => new StatusError(`Request body exceeds the ${max}-byte limit`, 413);
|
|
642
675
|
var sources = /* @__PURE__ */ new WeakMap();
|
|
643
676
|
function setBodySource(ctx, source) {
|
|
644
677
|
sources.set(ctx, source);
|
|
@@ -648,8 +681,11 @@ async function resolveBody(ctx, body) {
|
|
|
648
681
|
if (!source) return void 0;
|
|
649
682
|
const mode = typeof body === "string" ? body : body?.mode ?? "parse";
|
|
650
683
|
const max = resolveMax(typeof body === "object" ? body?.max : void 0);
|
|
684
|
+
const contentType = String(ctx.headers["content-type"] || "");
|
|
685
|
+
const isMultipart = /multipart\/form-data/i.test(contentType);
|
|
651
686
|
const declared = Number(ctx.headers["content-length"]);
|
|
652
|
-
|
|
687
|
+
const trustDeclared = !isMultipart && !ctx.options.uploads;
|
|
688
|
+
if (max !== INF && trustDeclared && declared > max) throw tooLarge(max);
|
|
653
689
|
if (mode === "stream") return source.getStream();
|
|
654
690
|
if (mode === "raw") {
|
|
655
691
|
const raw = await source.getBuffer();
|
|
@@ -667,7 +703,6 @@ async function resolveBody(ctx, body) {
|
|
|
667
703
|
new TransformStream({
|
|
668
704
|
transform(chunk, controller) {
|
|
669
705
|
size += chunk.byteLength;
|
|
670
|
-
if (size > max) return controller.error(tooLarge(max));
|
|
671
706
|
controller.enqueue(chunk);
|
|
672
707
|
}
|
|
673
708
|
})
|
|
@@ -675,7 +710,8 @@ async function resolveBody(ctx, body) {
|
|
|
675
710
|
const parsed = await parseBody(
|
|
676
711
|
counted,
|
|
677
712
|
ctx.headers["content-type"],
|
|
678
|
-
ctx.options.uploads
|
|
713
|
+
ctx.options.uploads,
|
|
714
|
+
max
|
|
679
715
|
);
|
|
680
716
|
if (size && !ctx.headers["content-length"]) {
|
|
681
717
|
ctx.headers["content-length"] = String(size);
|
|
@@ -1543,16 +1579,16 @@ var STATUS_TEXT = {
|
|
|
1543
1579
|
502: "Bad Gateway",
|
|
1544
1580
|
503: "Service Unavailable"
|
|
1545
1581
|
};
|
|
1546
|
-
var
|
|
1582
|
+
var UNITS2 = ["b", "kb", "mb", "gb", "tb"];
|
|
1547
1583
|
function formatBytes(bytes) {
|
|
1548
1584
|
if (!bytes || bytes < 0) return "0b";
|
|
1549
1585
|
const i = Math.min(
|
|
1550
1586
|
Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
1551
|
-
|
|
1587
|
+
UNITS2.length - 1
|
|
1552
1588
|
);
|
|
1553
1589
|
const value = bytes / 1024 ** i;
|
|
1554
1590
|
const rounded = i === 0 ? Math.round(value) : Math.round(value * 10) / 10;
|
|
1555
|
-
return `${rounded}${
|
|
1591
|
+
return `${rounded}${UNITS2[i]}`;
|
|
1556
1592
|
}
|
|
1557
1593
|
var SCOPE_COLORS = {
|
|
1558
1594
|
start: "green",
|
|
@@ -1714,6 +1750,7 @@ function config(options = {}) {
|
|
|
1714
1750
|
status: error.status || 500
|
|
1715
1751
|
});
|
|
1716
1752
|
});
|
|
1753
|
+
settings.onResponse = options.onResponse;
|
|
1717
1754
|
const loc = (v) => typeof v === "string" ? v : "enabled";
|
|
1718
1755
|
if (settings.auth) {
|
|
1719
1756
|
log.message("auth", `${settings.auth.providers.join(", ")} auth enabled`);
|
|
@@ -1836,6 +1873,9 @@ async function parseResponse(out, ctx) {
|
|
|
1836
1873
|
if (typeof out === "function") {
|
|
1837
1874
|
out = await out(ctx);
|
|
1838
1875
|
}
|
|
1876
|
+
if (out && typeof out.send === "function" && out.res?.headers instanceof Headers) {
|
|
1877
|
+
out = out.send();
|
|
1878
|
+
}
|
|
1839
1879
|
if (out instanceof Blob) {
|
|
1840
1880
|
out = new Response(out, { headers: { "Content-Type": out.type } });
|
|
1841
1881
|
}
|
|
@@ -1996,7 +2036,11 @@ function validate(ctx, schema) {
|
|
|
1996
2036
|
|
|
1997
2037
|
// src/helpers/handleRequest.ts
|
|
1998
2038
|
async function handleRequest(app, ctx) {
|
|
1999
|
-
|
|
2039
|
+
let res = await getResponse(app, ctx);
|
|
2040
|
+
if (res && ctx.options.onResponse) {
|
|
2041
|
+
const replaced = await ctx.options.onResponse(res, ctx);
|
|
2042
|
+
if (replaced) res = replaced;
|
|
2043
|
+
}
|
|
2000
2044
|
if (res) ctx.options.log.request(ctx, res);
|
|
2001
2045
|
return res;
|
|
2002
2046
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@server/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
|
|
5
5
|
"homepage": "https://server-js.com/",
|
|
6
6
|
"repository": "github:franciscop/server-next",
|