@server/next 0.36.2 → 0.37.1
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 +11 -6
- package/index.js +148 -88
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -10,10 +10,8 @@ declare class UploadPipeline {
|
|
|
10
10
|
private _limits;
|
|
11
11
|
constructor(bucket?: Bucket | string | null);
|
|
12
12
|
limit(options: LimitOptions): this;
|
|
13
|
-
store(bucket: Bucket | string): this;
|
|
14
13
|
processFile(originalName: string, data: Buffer, contentType: string): Promise<UploadedFile>;
|
|
15
14
|
}
|
|
16
|
-
declare function upload(bucket?: Bucket | string | null): UploadPipeline;
|
|
17
15
|
|
|
18
16
|
type CookieOptions = string | string[] | Cookie | Cookie[] | null;
|
|
19
17
|
interface ResponseData {
|
|
@@ -31,7 +29,7 @@ declare class Reply$1 {
|
|
|
31
29
|
cookies(key: string | Record<string, CookieOptions>, value?: CookieOptions): this;
|
|
32
30
|
json(body: unknown): Response;
|
|
33
31
|
redirect(path: string): Response;
|
|
34
|
-
file(path: string): Promise<Response>;
|
|
32
|
+
file(path: string | BucketFile): Promise<Response>;
|
|
35
33
|
send(body?: string | Buffer | ReadableStream | any): Response;
|
|
36
34
|
}
|
|
37
35
|
type Params<K extends keyof Reply$1> = Reply$1[K] extends (...args: infer A) => any ? A : never;
|
|
@@ -99,6 +97,7 @@ type BucketFile = {
|
|
|
99
97
|
readonly path: string;
|
|
100
98
|
readonly id: string;
|
|
101
99
|
readonly name: string;
|
|
100
|
+
readonly type?: string;
|
|
102
101
|
exists(): Promise<boolean>;
|
|
103
102
|
info?(): Promise<FileInfo>;
|
|
104
103
|
write(content: string | Buffer | ReadableStream, options?: {
|
|
@@ -120,6 +119,9 @@ type UploadedFile = {
|
|
|
120
119
|
type: string;
|
|
121
120
|
size: number;
|
|
122
121
|
};
|
|
122
|
+
type UploadOptions = LimitOptions & {
|
|
123
|
+
bucket: string | Bucket;
|
|
124
|
+
};
|
|
123
125
|
type CorsSettings = {
|
|
124
126
|
origin: string | boolean;
|
|
125
127
|
methods: string;
|
|
@@ -204,11 +206,12 @@ type SecuritySettings = {
|
|
|
204
206
|
hsts: string | null;
|
|
205
207
|
};
|
|
206
208
|
type OnError = (error: Error, ctx: Context) => Response | Promise<Response>;
|
|
209
|
+
type OnResponse = (response: Response, ctx: Context) => Response | void | Promise<Response | void>;
|
|
207
210
|
type Options = {
|
|
208
211
|
port?: number;
|
|
209
212
|
secret?: string;
|
|
210
213
|
public?: string | Bucket;
|
|
211
|
-
uploads?: string | Bucket |
|
|
214
|
+
uploads?: string | Bucket | UploadOptions;
|
|
212
215
|
store?: KVStore;
|
|
213
216
|
cookies?: KVStore;
|
|
214
217
|
session?: KVStore | {
|
|
@@ -218,6 +221,7 @@ type Options = {
|
|
|
218
221
|
auth?: AuthOption;
|
|
219
222
|
openapi?: any;
|
|
220
223
|
onError?: OnError;
|
|
224
|
+
onResponse?: OnResponse;
|
|
221
225
|
log?: LogLevel | boolean;
|
|
222
226
|
favicon?: string | BucketFile;
|
|
223
227
|
security?: boolean | SecurityOptions;
|
|
@@ -238,6 +242,7 @@ type Settings = {
|
|
|
238
242
|
auth?: AuthSettings;
|
|
239
243
|
openapi?: any;
|
|
240
244
|
onError?: OnError;
|
|
245
|
+
onResponse?: OnResponse;
|
|
241
246
|
log: Logger;
|
|
242
247
|
favicon?: string | BucketFile;
|
|
243
248
|
security: SecuritySettings;
|
|
@@ -298,7 +303,7 @@ type Context<Params extends Record<string, string | undefined> = Record<string,
|
|
|
298
303
|
};
|
|
299
304
|
app: Server;
|
|
300
305
|
};
|
|
301
|
-
type InlineReply = Response | Reply | {
|
|
306
|
+
type InlineReply = Response | Reply | BucketFile | {
|
|
302
307
|
body: string;
|
|
303
308
|
headers?: Headers;
|
|
304
309
|
} | SerializableValue | JSX.Element | Buffer | ReadableStream;
|
|
@@ -501,4 +506,4 @@ declare class Server<O extends ServerConfig = {}> extends Router<O> {
|
|
|
501
506
|
}
|
|
502
507
|
declare function server<Session extends Record<string, any> = {}, User extends Record<string, any> = {}>(options?: Options): Server<ServerConfig<Session, User>>;
|
|
503
508
|
|
|
504
|
-
export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type BodyMode, type BodyOption, type Bucket, type BucketFile, type BunEnv, type CacheOption, type Context, type Cookie, type CorsSettings, type ExtractPathParams, type FileInfo, type InferParamType, type InlineReply, type KVStore, type
|
|
509
|
+
export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type BodyMode, type BodyOption, type Bucket, type BucketFile, type BunEnv, type CacheOption, type Context, type Cookie, type CorsSettings, type ExtractPathParams, type FileInfo, type InferParamType, type InlineReply, type KVStore, type LogLevel, type Logger, type Method, type Middleware, type Options, type ParamTypeMap, type ParamsToObject, type PathToParams, type Platform, type Provider, type Route, type RouteOptions, type RouterMethod, type SecurityOptions, type SecuritySettings, type SerializableValue, Server, type ServerConfig, TypedServerError as ServerError, type Settings, type Strategy, type Time, type UploadOptions, type UploadedFile, cache, cookies, server as default, download, file, headers, json, redirect, router, send, status, type };
|
package/index.js
CHANGED
|
@@ -89,46 +89,19 @@ 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
99
|
};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
100
|
+
|
|
101
|
+
// src/helpers/bucket.ts
|
|
102
|
+
import * as fs from "fs";
|
|
103
|
+
import * as fsp from "fs/promises";
|
|
104
|
+
import * as path from "path";
|
|
132
105
|
|
|
133
106
|
// src/helpers/mimes.ts
|
|
134
107
|
var mimes_default = {
|
|
@@ -213,9 +186,6 @@ var mimes_default = {
|
|
|
213
186
|
};
|
|
214
187
|
|
|
215
188
|
// src/helpers/bucket.ts
|
|
216
|
-
import * as fs from "fs";
|
|
217
|
-
import * as fsp from "fs/promises";
|
|
218
|
-
import * as path from "path";
|
|
219
189
|
function localBucket(root) {
|
|
220
190
|
const base = path.resolve(root);
|
|
221
191
|
const resolveKey = (name) => {
|
|
@@ -228,6 +198,7 @@ function localBucket(root) {
|
|
|
228
198
|
};
|
|
229
199
|
const file2 = (name, win) => {
|
|
230
200
|
const full = resolveKey(name);
|
|
201
|
+
const type2 = mimes_default[path.extname(name).slice(1).toLowerCase()];
|
|
231
202
|
const read = () => {
|
|
232
203
|
let opts;
|
|
233
204
|
if (win) {
|
|
@@ -250,6 +221,7 @@ function localBucket(root) {
|
|
|
250
221
|
path: full,
|
|
251
222
|
id: name.replace(/^\/+/, ""),
|
|
252
223
|
name: path.basename(name),
|
|
224
|
+
type: type2,
|
|
253
225
|
async exists() {
|
|
254
226
|
const stats = await fsp.stat(full).catch(() => null);
|
|
255
227
|
return !!stats?.isFile();
|
|
@@ -259,7 +231,7 @@ function localBucket(root) {
|
|
|
259
231
|
const exists = !!stats?.isFile();
|
|
260
232
|
const total = stats?.size ?? 0;
|
|
261
233
|
const size = win ? Math.max(0, Math.min(win.end, total) - win.start) : total;
|
|
262
|
-
return { exists, size, date: stats?.mtime ?? null };
|
|
234
|
+
return { exists, size, date: stats?.mtime ?? null, type: type2 };
|
|
263
235
|
},
|
|
264
236
|
// Read-only view of [start, end), composed relative to the current window.
|
|
265
237
|
slice(start, end) {
|
|
@@ -311,6 +283,47 @@ function bucket(root) {
|
|
|
311
283
|
);
|
|
312
284
|
}
|
|
313
285
|
|
|
286
|
+
// src/helpers/createId.ts
|
|
287
|
+
var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
|
|
288
|
+
var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
|
|
289
|
+
var cyrb53 = (str, seed = 0) => {
|
|
290
|
+
if (typeof str !== "string") str = String(str);
|
|
291
|
+
let h1 = 3735928559 ^ seed;
|
|
292
|
+
let h2 = 1103547991 ^ seed;
|
|
293
|
+
for (let i = 0, ch; i < str.length; i++) {
|
|
294
|
+
ch = str.charCodeAt(i);
|
|
295
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
296
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
297
|
+
}
|
|
298
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
299
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
300
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
301
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
302
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
303
|
+
};
|
|
304
|
+
var hash = (str, size) => {
|
|
305
|
+
let chars = "";
|
|
306
|
+
let num = cyrb53(str);
|
|
307
|
+
for (let i = 0; i < size; i++) {
|
|
308
|
+
if (num < alphabet.length) num = cyrb53(str, i);
|
|
309
|
+
chars += alphabet[num % alphabet.length];
|
|
310
|
+
num = Math.floor(num / alphabet.length);
|
|
311
|
+
}
|
|
312
|
+
return chars;
|
|
313
|
+
};
|
|
314
|
+
var randomId = (size = 16) => {
|
|
315
|
+
let id = "";
|
|
316
|
+
const bytes = random(size);
|
|
317
|
+
while (size--) {
|
|
318
|
+
id += alphabet[bytes[size] & 61];
|
|
319
|
+
}
|
|
320
|
+
return id;
|
|
321
|
+
};
|
|
322
|
+
function createId(source, size = 16) {
|
|
323
|
+
if (source) return hash(source, size);
|
|
324
|
+
return randomId(size);
|
|
325
|
+
}
|
|
326
|
+
|
|
314
327
|
// src/helpers/upload.ts
|
|
315
328
|
function parseBytes(value) {
|
|
316
329
|
if (typeof value === "number") return value;
|
|
@@ -352,10 +365,6 @@ var UploadPipeline = class {
|
|
|
352
365
|
this._limits = { ...this._limits, ...options };
|
|
353
366
|
return this;
|
|
354
367
|
}
|
|
355
|
-
store(bucket2) {
|
|
356
|
-
this._bucket = bucket(bucket2);
|
|
357
|
-
return this;
|
|
358
|
-
}
|
|
359
368
|
async processFile(originalName, data, contentType) {
|
|
360
369
|
const { maxSize, minSize, fileType } = this._limits;
|
|
361
370
|
if (maxSize !== void 0 && data.length > parseBytes(maxSize)) {
|
|
@@ -381,16 +390,31 @@ var UploadPipeline = class {
|
|
|
381
390
|
}
|
|
382
391
|
}
|
|
383
392
|
if (!this._bucket) {
|
|
384
|
-
throw new Error(
|
|
385
|
-
`No destination configured. Pass a bucket to upload() or call .store()`
|
|
386
|
-
);
|
|
393
|
+
throw new Error(`No upload destination configured (missing bucket)`);
|
|
387
394
|
}
|
|
388
395
|
return saveFileToBucket(originalName, data, this._bucket, contentType);
|
|
389
396
|
}
|
|
390
397
|
};
|
|
391
|
-
|
|
392
|
-
|
|
398
|
+
|
|
399
|
+
// src/helpers/bodyLimit.ts
|
|
400
|
+
var INF = Number.POSITIVE_INFINITY;
|
|
401
|
+
var DEFAULT_MAX = "1mb";
|
|
402
|
+
var resolveMax = (max) => max === false ? INF : parseBytes(max == null ? DEFAULT_MAX : max);
|
|
403
|
+
var UNITS = ["b", "kb", "mb", "gb", "tb"];
|
|
404
|
+
function human(bytes) {
|
|
405
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return `${bytes}`;
|
|
406
|
+
const i = Math.min(
|
|
407
|
+
Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
408
|
+
UNITS.length - 1
|
|
409
|
+
);
|
|
410
|
+
const value = bytes / 1024 ** i;
|
|
411
|
+
const rounded = i === 0 ? Math.round(value) : Math.round(value * 10) / 10;
|
|
412
|
+
return `${rounded}${UNITS[i]}`;
|
|
393
413
|
}
|
|
414
|
+
var tooLarge = (max) => new StatusError(
|
|
415
|
+
`Request body exceeds the ${human(max)} limit. Raise it with body: { max: '10mb' } on the route or server, or set max: false to disable.`,
|
|
416
|
+
413
|
|
417
|
+
);
|
|
394
418
|
|
|
395
419
|
// src/helpers/parseBody.ts
|
|
396
420
|
function getBoundary(header) {
|
|
@@ -438,10 +462,18 @@ function toStream(input) {
|
|
|
438
462
|
}
|
|
439
463
|
});
|
|
440
464
|
}
|
|
441
|
-
async function toBuffer(input) {
|
|
442
|
-
if (!(input instanceof ReadableStream))
|
|
465
|
+
async function toBuffer(input, max = INF) {
|
|
466
|
+
if (!(input instanceof ReadableStream)) {
|
|
467
|
+
if (input.length > max) throw tooLarge(max);
|
|
468
|
+
return input;
|
|
469
|
+
}
|
|
443
470
|
const chunks = [];
|
|
444
|
-
|
|
471
|
+
let total = 0;
|
|
472
|
+
for await (const chunk of asIterable(input)) {
|
|
473
|
+
total += chunk.byteLength;
|
|
474
|
+
if (total > max) throw tooLarge(max);
|
|
475
|
+
chunks.push(Buffer.from(chunk));
|
|
476
|
+
}
|
|
445
477
|
return Buffer.concat(chunks);
|
|
446
478
|
}
|
|
447
479
|
function parseUrlEncoded(text) {
|
|
@@ -522,13 +554,21 @@ async function endPart(part, body) {
|
|
|
522
554
|
}
|
|
523
555
|
}
|
|
524
556
|
var BREAK = Buffer.from("\r\n\r\n");
|
|
525
|
-
async function parseMultipart(stream, boundary, dest) {
|
|
557
|
+
async function parseMultipart(stream, boundary, dest, max = INF) {
|
|
526
558
|
const delim = Buffer.from(`\r
|
|
527
559
|
--${boundary}`);
|
|
528
560
|
const body = {};
|
|
529
561
|
let buf = Buffer.from("\r\n");
|
|
530
562
|
let state = "boundary";
|
|
531
563
|
let part = null;
|
|
564
|
+
let textBytes = 0;
|
|
565
|
+
const feed = (p, data) => {
|
|
566
|
+
if (p.kind === "text") {
|
|
567
|
+
textBytes += data.length;
|
|
568
|
+
if (textBytes > max) throw tooLarge(max);
|
|
569
|
+
}
|
|
570
|
+
feedPart(p, data);
|
|
571
|
+
};
|
|
532
572
|
for await (const chunk of asIterable(stream)) {
|
|
533
573
|
buf = Buffer.concat([buf, Buffer.from(chunk)]);
|
|
534
574
|
let advanced = true;
|
|
@@ -560,13 +600,13 @@ async function parseMultipart(stream, boundary, dest) {
|
|
|
560
600
|
if (i === -1) {
|
|
561
601
|
const safe = buf.length - (delim.length - 1);
|
|
562
602
|
if (safe > 0 && part) {
|
|
563
|
-
|
|
603
|
+
feed(part, buf.subarray(0, safe));
|
|
564
604
|
buf = buf.subarray(safe);
|
|
565
605
|
}
|
|
566
606
|
break;
|
|
567
607
|
}
|
|
568
608
|
if (part) {
|
|
569
|
-
|
|
609
|
+
feed(part, buf.subarray(0, i));
|
|
570
610
|
await endPart(part, body);
|
|
571
611
|
part = null;
|
|
572
612
|
}
|
|
@@ -599,24 +639,24 @@ async function streamToBucket(stream, type2, bucket2) {
|
|
|
599
639
|
if (!size) return void 0;
|
|
600
640
|
return { name: id, id, path: file2.path, type: type2, size };
|
|
601
641
|
}
|
|
602
|
-
async function parseBody(input, contentType, dest) {
|
|
642
|
+
async function parseBody(input, contentType, dest, max = INF) {
|
|
603
643
|
const type2 = Array.isArray(contentType) ? contentType[0] : contentType;
|
|
604
644
|
const boundary = type2 && /multipart\/form-data/i.test(type2) ? getBoundary(type2) : null;
|
|
605
|
-
if (boundary) return parseMultipart(toStream(input), boundary, dest);
|
|
645
|
+
if (boundary) return parseMultipart(toStream(input), boundary, dest, max);
|
|
606
646
|
if (!type2 || /^text\//i.test(type2)) {
|
|
607
|
-
const buf = await toBuffer(input);
|
|
647
|
+
const buf = await toBuffer(input, max);
|
|
608
648
|
return buf.length ? buf.toString("utf-8") : void 0;
|
|
609
649
|
}
|
|
610
650
|
if (/application\/json/i.test(type2)) {
|
|
611
|
-
const buf = await toBuffer(input);
|
|
651
|
+
const buf = await toBuffer(input, max);
|
|
612
652
|
return buf.length ? JSON.parse(buf.toString("utf-8")) : void 0;
|
|
613
653
|
}
|
|
614
654
|
if (/application\/x-www-form-urlencoded/i.test(type2)) {
|
|
615
|
-
const buf = await toBuffer(input);
|
|
655
|
+
const buf = await toBuffer(input, max);
|
|
616
656
|
return buf.length ? parseUrlEncoded(buf.toString("utf-8")) : void 0;
|
|
617
657
|
}
|
|
618
658
|
if (!dest) {
|
|
619
|
-
const buf = await toBuffer(input);
|
|
659
|
+
const buf = await toBuffer(input, max);
|
|
620
660
|
return buf.length ? buf : void 0;
|
|
621
661
|
}
|
|
622
662
|
if (dest instanceof UploadPipeline) {
|
|
@@ -626,19 +666,7 @@ async function parseBody(input, contentType, dest) {
|
|
|
626
666
|
return streamToBucket(toStream(input), type2, dest);
|
|
627
667
|
}
|
|
628
668
|
|
|
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
669
|
// 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
670
|
var sources = /* @__PURE__ */ new WeakMap();
|
|
643
671
|
function setBodySource(ctx, source) {
|
|
644
672
|
sources.set(ctx, source);
|
|
@@ -648,8 +676,11 @@ async function resolveBody(ctx, body) {
|
|
|
648
676
|
if (!source) return void 0;
|
|
649
677
|
const mode = typeof body === "string" ? body : body?.mode ?? "parse";
|
|
650
678
|
const max = resolveMax(typeof body === "object" ? body?.max : void 0);
|
|
679
|
+
const contentType = String(ctx.headers["content-type"] || "");
|
|
680
|
+
const isMultipart = /multipart\/form-data/i.test(contentType);
|
|
651
681
|
const declared = Number(ctx.headers["content-length"]);
|
|
652
|
-
|
|
682
|
+
const trustDeclared = !isMultipart && !ctx.options.uploads;
|
|
683
|
+
if (max !== INF && trustDeclared && declared > max) throw tooLarge(max);
|
|
653
684
|
if (mode === "stream") return source.getStream();
|
|
654
685
|
if (mode === "raw") {
|
|
655
686
|
const raw = await source.getBuffer();
|
|
@@ -667,7 +698,6 @@ async function resolveBody(ctx, body) {
|
|
|
667
698
|
new TransformStream({
|
|
668
699
|
transform(chunk, controller) {
|
|
669
700
|
size += chunk.byteLength;
|
|
670
|
-
if (size > max) return controller.error(tooLarge(max));
|
|
671
701
|
controller.enqueue(chunk);
|
|
672
702
|
}
|
|
673
703
|
})
|
|
@@ -675,7 +705,8 @@ async function resolveBody(ctx, body) {
|
|
|
675
705
|
const parsed = await parseBody(
|
|
676
706
|
counted,
|
|
677
707
|
ctx.headers["content-type"],
|
|
678
|
-
ctx.options.uploads
|
|
708
|
+
ctx.options.uploads,
|
|
709
|
+
max
|
|
679
710
|
);
|
|
680
711
|
if (size && !ctx.headers["content-length"]) {
|
|
681
712
|
ctx.headers["content-length"] = String(size);
|
|
@@ -861,6 +892,10 @@ var Reply = class {
|
|
|
861
892
|
return this.headers("location", path2).status(302).send();
|
|
862
893
|
}
|
|
863
894
|
async file(path2) {
|
|
895
|
+
if (typeof path2 !== "string") {
|
|
896
|
+
if (!await path2.exists()) return this.status(404).send();
|
|
897
|
+
return this.type(path2.type).send(path2.stream());
|
|
898
|
+
}
|
|
864
899
|
try {
|
|
865
900
|
const fs2 = await import("fs");
|
|
866
901
|
const ext2 = path2.split(".").pop();
|
|
@@ -889,7 +924,7 @@ var Reply = class {
|
|
|
889
924
|
return new Response(body, { status: status2, headers: headers2 });
|
|
890
925
|
}
|
|
891
926
|
const name = body?.constructor?.name;
|
|
892
|
-
if (
|
|
927
|
+
if (body instanceof Uint8Array) {
|
|
893
928
|
if (!headers2.has("content-length")) {
|
|
894
929
|
headers2.set("content-length", String(body.length));
|
|
895
930
|
}
|
|
@@ -1543,16 +1578,16 @@ var STATUS_TEXT = {
|
|
|
1543
1578
|
502: "Bad Gateway",
|
|
1544
1579
|
503: "Service Unavailable"
|
|
1545
1580
|
};
|
|
1546
|
-
var
|
|
1581
|
+
var UNITS2 = ["b", "kb", "mb", "gb", "tb"];
|
|
1547
1582
|
function formatBytes(bytes) {
|
|
1548
1583
|
if (!bytes || bytes < 0) return "0b";
|
|
1549
1584
|
const i = Math.min(
|
|
1550
1585
|
Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
1551
|
-
|
|
1586
|
+
UNITS2.length - 1
|
|
1552
1587
|
);
|
|
1553
1588
|
const value = bytes / 1024 ** i;
|
|
1554
1589
|
const rounded = i === 0 ? Math.round(value) : Math.round(value * 10) / 10;
|
|
1555
|
-
return `${rounded}${
|
|
1590
|
+
return `${rounded}${UNITS2[i]}`;
|
|
1556
1591
|
}
|
|
1557
1592
|
var SCOPE_COLORS = {
|
|
1558
1593
|
start: "green",
|
|
@@ -1685,9 +1720,20 @@ function config(options = {}) {
|
|
|
1685
1720
|
}
|
|
1686
1721
|
settings.cors = cors2;
|
|
1687
1722
|
}
|
|
1688
|
-
|
|
1689
|
-
settings.
|
|
1690
|
-
|
|
1723
|
+
const publicDir = options.public || env2.PUBLIC;
|
|
1724
|
+
settings.public = publicDir ? bucket(publicDir) : null;
|
|
1725
|
+
const up = options.uploads;
|
|
1726
|
+
if (!up) {
|
|
1727
|
+
settings.uploads = null;
|
|
1728
|
+
} else if (typeof up === "object" && "bucket" in up) {
|
|
1729
|
+
const { bucket: bucket2, maxSize, minSize, fileType } = up;
|
|
1730
|
+
const hasLimits = maxSize != null || minSize != null || fileType != null;
|
|
1731
|
+
settings.uploads = hasLimits ? new UploadPipeline(bucket2).limit({ maxSize, minSize, fileType }) : bucket(bucket2);
|
|
1732
|
+
} else {
|
|
1733
|
+
settings.uploads = bucket(up);
|
|
1734
|
+
}
|
|
1735
|
+
const favicon2 = options.favicon || env2.FAVICON;
|
|
1736
|
+
if (favicon2) settings.favicon = favicon2;
|
|
1691
1737
|
settings.store = options.store ?? null;
|
|
1692
1738
|
settings.cookies = options.cookies ?? null;
|
|
1693
1739
|
if (options.session) {
|
|
@@ -1714,6 +1760,7 @@ function config(options = {}) {
|
|
|
1714
1760
|
status: error.status || 500
|
|
1715
1761
|
});
|
|
1716
1762
|
});
|
|
1763
|
+
settings.onResponse = options.onResponse;
|
|
1717
1764
|
const loc = (v) => typeof v === "string" ? v : "enabled";
|
|
1718
1765
|
if (settings.auth) {
|
|
1719
1766
|
log.message("auth", `${settings.auth.providers.join(", ")} auth enabled`);
|
|
@@ -1842,6 +1889,16 @@ async function parseResponse(out, ctx) {
|
|
|
1842
1889
|
if (out instanceof Blob) {
|
|
1843
1890
|
out = new Response(out, { headers: { "Content-Type": out.type } });
|
|
1844
1891
|
}
|
|
1892
|
+
if (out && typeof out.stream === "function" && typeof out.bytes === "function" && typeof out.exists === "function" && typeof out.name === "string") {
|
|
1893
|
+
if (!await out.exists()) {
|
|
1894
|
+
out = new Response(null, { status: 404 });
|
|
1895
|
+
} else {
|
|
1896
|
+
out = new Response(
|
|
1897
|
+
out.stream(),
|
|
1898
|
+
out.type ? { headers: { "content-type": out.type } } : void 0
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1845
1902
|
if (out instanceof ReadableStream) {
|
|
1846
1903
|
out = new Response(out);
|
|
1847
1904
|
}
|
|
@@ -1999,7 +2056,11 @@ function validate(ctx, schema) {
|
|
|
1999
2056
|
|
|
2000
2057
|
// src/helpers/handleRequest.ts
|
|
2001
2058
|
async function handleRequest(app, ctx) {
|
|
2002
|
-
|
|
2059
|
+
let res = await getResponse(app, ctx);
|
|
2060
|
+
if (res && ctx.options.onResponse) {
|
|
2061
|
+
const replaced = await ctx.options.onResponse(res, ctx);
|
|
2062
|
+
if (replaced) res = replaced;
|
|
2063
|
+
}
|
|
2003
2064
|
if (res) ctx.options.log.request(ctx, res);
|
|
2004
2065
|
return res;
|
|
2005
2066
|
}
|
|
@@ -3223,6 +3284,5 @@ export {
|
|
|
3223
3284
|
router,
|
|
3224
3285
|
send,
|
|
3225
3286
|
status,
|
|
3226
|
-
type
|
|
3227
|
-
upload
|
|
3287
|
+
type
|
|
3228
3288
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@server/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.1",
|
|
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",
|