@server/next 0.42.0 → 0.44.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 +30 -24
- package/index.js +143 -183
- package/package.json +5 -3
package/index.d.ts
CHANGED
|
@@ -96,17 +96,20 @@ type RouteSchema = {
|
|
|
96
96
|
description?: string;
|
|
97
97
|
};
|
|
98
98
|
type RouteOptions = {
|
|
99
|
-
schema?: RouteSchema;
|
|
99
|
+
schema?: RouteSchema | false;
|
|
100
100
|
parser?: BodyMode;
|
|
101
101
|
body?: StandardSchemaV1<any, any>;
|
|
102
102
|
query?: StandardSchemaV1<any, any>;
|
|
103
103
|
params?: StandardSchemaV1<any, any>;
|
|
104
104
|
response?: StandardSchemaV1<any, any>;
|
|
105
105
|
cache?: CacheOption;
|
|
106
|
+
uploads?: string | Bucket | UploadOptions | false;
|
|
106
107
|
};
|
|
107
108
|
type Route = {
|
|
108
109
|
path: string;
|
|
109
|
-
options: RouteOptions
|
|
110
|
+
options: Omit<RouteOptions, "uploads"> & {
|
|
111
|
+
uploads?: Settings["uploads"];
|
|
112
|
+
};
|
|
110
113
|
fns: Middleware[];
|
|
111
114
|
};
|
|
112
115
|
type Cookie = {
|
|
@@ -256,11 +259,15 @@ type Options = {
|
|
|
256
259
|
sessions?: StoreSource;
|
|
257
260
|
cors?: CorsOptions;
|
|
258
261
|
auth?: AuthOption;
|
|
259
|
-
openapi?:
|
|
262
|
+
openapi?: boolean | string | {
|
|
263
|
+
path?: string;
|
|
264
|
+
title?: string;
|
|
265
|
+
description?: string;
|
|
266
|
+
version?: string;
|
|
267
|
+
};
|
|
260
268
|
onError?: OnError;
|
|
261
269
|
onResponse?: OnResponse;
|
|
262
270
|
log?: LogLevel | boolean;
|
|
263
|
-
favicon?: string | BucketFile;
|
|
264
271
|
security?: boolean | SecurityOptions;
|
|
265
272
|
parser?: BodyMode;
|
|
266
273
|
cache?: CacheOption;
|
|
@@ -276,11 +283,15 @@ type Settings = {
|
|
|
276
283
|
sessionsDefault?: boolean;
|
|
277
284
|
cors?: CorsSettings;
|
|
278
285
|
auth?: AuthSettings;
|
|
279
|
-
openapi?:
|
|
286
|
+
openapi?: {
|
|
287
|
+
path: string;
|
|
288
|
+
title?: string;
|
|
289
|
+
description?: string;
|
|
290
|
+
version?: string;
|
|
291
|
+
};
|
|
280
292
|
onError?: OnError;
|
|
281
293
|
onResponse?: OnResponse;
|
|
282
294
|
log: Logger;
|
|
283
|
-
favicon?: string | BucketFile;
|
|
284
295
|
security: SecuritySettings;
|
|
285
296
|
parser: BodyMode;
|
|
286
297
|
cache?: CacheOption;
|
|
@@ -316,9 +327,9 @@ interface ContextExtension {
|
|
|
316
327
|
type Context<C extends ContextTypes = {}> = {
|
|
317
328
|
method: Method;
|
|
318
329
|
ip: string;
|
|
330
|
+
signal: AbortSignal;
|
|
319
331
|
headers: Record<string, string | string[]>;
|
|
320
332
|
cookies: Record<string, string>;
|
|
321
|
-
body?: Field<C, "body", SerializableValue | Buffer | ReadableStream>;
|
|
322
333
|
url: URL & {
|
|
323
334
|
params: Field<C, "params", Record<string, any>>;
|
|
324
335
|
query: Field<C, "query", Record<string, any>>;
|
|
@@ -331,12 +342,12 @@ type Context<C extends ContextTypes = {}> = {
|
|
|
331
342
|
session: Field<C, "session", Record<string, any>>;
|
|
332
343
|
user?: Field<C, "user", Record<string, any>>;
|
|
333
344
|
init: number;
|
|
334
|
-
req?: Request;
|
|
335
|
-
res?: Response & {
|
|
336
|
-
cookies?: Record<string, string>;
|
|
337
|
-
};
|
|
338
345
|
app: Server;
|
|
339
|
-
} &
|
|
346
|
+
} & ("body" extends keyof C ? {
|
|
347
|
+
body: Field<C, "body", never>;
|
|
348
|
+
} : {
|
|
349
|
+
body?: SerializableValue | Buffer | ReadableStream;
|
|
350
|
+
}) & ContextExtension;
|
|
340
351
|
type InlineReply = Response | Reply | BucketFile | {
|
|
341
352
|
body: string;
|
|
342
353
|
headers?: Headers;
|
|
@@ -431,11 +442,6 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
431
442
|
platform: Platform;
|
|
432
443
|
sockets: any[];
|
|
433
444
|
websocket: any;
|
|
434
|
-
faviconCache?: {
|
|
435
|
-
bytes: Buffer;
|
|
436
|
-
type: string;
|
|
437
|
-
etag: string;
|
|
438
|
-
} | null;
|
|
439
445
|
port?: number;
|
|
440
446
|
constructor(options?: Options);
|
|
441
447
|
self(): this;
|
|
@@ -445,6 +451,7 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
445
451
|
test(): {
|
|
446
452
|
get: (path: string, options?: {
|
|
447
453
|
method?: string;
|
|
454
|
+
signal?: AbortSignal | null;
|
|
448
455
|
headers?: HeadersInit;
|
|
449
456
|
cache?: RequestCache;
|
|
450
457
|
redirect?: RequestRedirect;
|
|
@@ -455,11 +462,11 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
455
462
|
priority?: RequestPriority;
|
|
456
463
|
referrer?: string;
|
|
457
464
|
referrerPolicy?: ReferrerPolicy;
|
|
458
|
-
signal?: AbortSignal | null;
|
|
459
465
|
window?: null;
|
|
460
466
|
}) => Promise<Response>;
|
|
461
467
|
head: (path: string, options?: {
|
|
462
468
|
method?: string;
|
|
469
|
+
signal?: AbortSignal | null;
|
|
463
470
|
headers?: HeadersInit;
|
|
464
471
|
cache?: RequestCache;
|
|
465
472
|
redirect?: RequestRedirect;
|
|
@@ -470,13 +477,13 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
470
477
|
priority?: RequestPriority;
|
|
471
478
|
referrer?: string;
|
|
472
479
|
referrerPolicy?: ReferrerPolicy;
|
|
473
|
-
signal?: AbortSignal | null;
|
|
474
480
|
window?: null;
|
|
475
481
|
}) => Promise<Response>;
|
|
476
482
|
post: (path: string, body?: string | number | boolean | ArrayBuffer | {
|
|
477
483
|
[key: string]: SerializableValue;
|
|
478
484
|
} | SerializableValue[] | ReadableStream<any> | Blob | ArrayBufferView<ArrayBuffer> | FormData | URLSearchParams, options?: {
|
|
479
485
|
method?: string;
|
|
486
|
+
signal?: AbortSignal | null;
|
|
480
487
|
headers?: HeadersInit;
|
|
481
488
|
cache?: RequestCache;
|
|
482
489
|
redirect?: RequestRedirect;
|
|
@@ -487,13 +494,13 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
487
494
|
priority?: RequestPriority;
|
|
488
495
|
referrer?: string;
|
|
489
496
|
referrerPolicy?: ReferrerPolicy;
|
|
490
|
-
signal?: AbortSignal | null;
|
|
491
497
|
window?: null;
|
|
492
498
|
}) => Promise<Response>;
|
|
493
499
|
put: (path: string, body?: string | number | boolean | ArrayBuffer | {
|
|
494
500
|
[key: string]: SerializableValue;
|
|
495
501
|
} | SerializableValue[] | ReadableStream<any> | Blob | ArrayBufferView<ArrayBuffer> | FormData | URLSearchParams, options?: {
|
|
496
502
|
method?: string;
|
|
503
|
+
signal?: AbortSignal | null;
|
|
497
504
|
headers?: HeadersInit;
|
|
498
505
|
cache?: RequestCache;
|
|
499
506
|
redirect?: RequestRedirect;
|
|
@@ -504,13 +511,13 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
504
511
|
priority?: RequestPriority;
|
|
505
512
|
referrer?: string;
|
|
506
513
|
referrerPolicy?: ReferrerPolicy;
|
|
507
|
-
signal?: AbortSignal | null;
|
|
508
514
|
window?: null;
|
|
509
515
|
}) => Promise<Response>;
|
|
510
516
|
patch: (path: string, body?: string | number | boolean | ArrayBuffer | {
|
|
511
517
|
[key: string]: SerializableValue;
|
|
512
518
|
} | SerializableValue[] | ReadableStream<any> | Blob | ArrayBufferView<ArrayBuffer> | FormData | URLSearchParams, options?: {
|
|
513
519
|
method?: string;
|
|
520
|
+
signal?: AbortSignal | null;
|
|
514
521
|
headers?: HeadersInit;
|
|
515
522
|
cache?: RequestCache;
|
|
516
523
|
redirect?: RequestRedirect;
|
|
@@ -521,11 +528,11 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
521
528
|
priority?: RequestPriority;
|
|
522
529
|
referrer?: string;
|
|
523
530
|
referrerPolicy?: ReferrerPolicy;
|
|
524
|
-
signal?: AbortSignal | null;
|
|
525
531
|
window?: null;
|
|
526
532
|
}) => Promise<Response>;
|
|
527
533
|
delete: (path: string, options?: {
|
|
528
534
|
method?: string;
|
|
535
|
+
signal?: AbortSignal | null;
|
|
529
536
|
headers?: HeadersInit;
|
|
530
537
|
cache?: RequestCache;
|
|
531
538
|
redirect?: RequestRedirect;
|
|
@@ -536,11 +543,11 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
536
543
|
priority?: RequestPriority;
|
|
537
544
|
referrer?: string;
|
|
538
545
|
referrerPolicy?: ReferrerPolicy;
|
|
539
|
-
signal?: AbortSignal | null;
|
|
540
546
|
window?: null;
|
|
541
547
|
}) => Promise<Response>;
|
|
542
548
|
options: (path: string, options?: {
|
|
543
549
|
method?: string;
|
|
550
|
+
signal?: AbortSignal | null;
|
|
544
551
|
headers?: HeadersInit;
|
|
545
552
|
cache?: RequestCache;
|
|
546
553
|
redirect?: RequestRedirect;
|
|
@@ -551,7 +558,6 @@ declare class Server<C extends ContextTypes = {}> extends Router<C> {
|
|
|
551
558
|
priority?: RequestPriority;
|
|
552
559
|
referrer?: string;
|
|
553
560
|
referrerPolicy?: ReferrerPolicy;
|
|
554
|
-
signal?: AbortSignal | null;
|
|
555
561
|
window?: null;
|
|
556
562
|
}) => Promise<Response>;
|
|
557
563
|
};
|
package/index.js
CHANGED
|
@@ -50,6 +50,7 @@ ServerError_default.extend({
|
|
|
50
50
|
message: "Missing the OAuth 'code' in the request body"
|
|
51
51
|
},
|
|
52
52
|
SESSION_JWT: "The `jwt` strategy is stateless, so there is no `ctx.session` (tried '{key}'). Use the `token` strategy for server-side sessions, or `cookie` for browsers",
|
|
53
|
+
SESSION_GUEST: "No `ctx.session` for this request (tried '{key}'): the `token` strategy carries the session in the Authorization header, and this request has none. Sign in first, or use the `cookie` strategy for guest sessions",
|
|
53
54
|
AUTH_INVALID_HEADER: {
|
|
54
55
|
status: 401,
|
|
55
56
|
message: "Invalid authorization header {type}, must send 'Bearer {TOKEN}' (with space)"
|
|
@@ -104,6 +105,17 @@ var StatusError = class extends Error {
|
|
|
104
105
|
}
|
|
105
106
|
};
|
|
106
107
|
|
|
108
|
+
// src/helpers/bucket.ts
|
|
109
|
+
import FileSystem from "bucket/fs";
|
|
110
|
+
function bucket(root) {
|
|
111
|
+
if (!root) return null;
|
|
112
|
+
if (typeof root === "string") return FileSystem(root);
|
|
113
|
+
if (typeof root.file === "function") return root;
|
|
114
|
+
throw new Error(
|
|
115
|
+
"Invalid bucket: pass a directory path or a `bucket` instance (with .file())"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
107
119
|
// src/helpers/createId.ts
|
|
108
120
|
var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
|
|
109
121
|
var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
|
|
@@ -146,6 +158,16 @@ function createId(source, size = 16) {
|
|
|
146
158
|
}
|
|
147
159
|
|
|
148
160
|
// src/helpers/upload.ts
|
|
161
|
+
function resolveUploads(up) {
|
|
162
|
+
if (!up) return null;
|
|
163
|
+
if (typeof up === "object" && "bucket" in up) {
|
|
164
|
+
const { bucket: bucket2, maxSize, minSize, fileType: fileType2 } = up;
|
|
165
|
+
if (maxSize != null) parseBytes(maxSize);
|
|
166
|
+
if (minSize != null) parseBytes(minSize);
|
|
167
|
+
return { bucket: bucket(bucket2), maxSize, minSize, fileType: fileType2 };
|
|
168
|
+
}
|
|
169
|
+
return { bucket: bucket(up) };
|
|
170
|
+
}
|
|
149
171
|
function parseBytes(value) {
|
|
150
172
|
if (typeof value === "number") return value;
|
|
151
173
|
const units = {
|
|
@@ -164,8 +186,8 @@ function getExt(filename) {
|
|
|
164
186
|
return filename.slice(i).toLowerCase();
|
|
165
187
|
}
|
|
166
188
|
async function saveFileToBucket(originalName, data, bucket2, contentType) {
|
|
167
|
-
const
|
|
168
|
-
const id = `${createId()}${
|
|
189
|
+
const ext = getExt(originalName);
|
|
190
|
+
const id = `${createId()}${ext}`;
|
|
169
191
|
const file2 = bucket2.file(id);
|
|
170
192
|
await file2.write(data, { type: contentType });
|
|
171
193
|
return {
|
|
@@ -188,10 +210,10 @@ function validateFile(originalName, data, contentType, limits) {
|
|
|
188
210
|
);
|
|
189
211
|
}
|
|
190
212
|
if (fileType2 && fileType2.length > 0) {
|
|
191
|
-
const
|
|
213
|
+
const ext = getExt(originalName);
|
|
192
214
|
const mime = contentType.toLowerCase();
|
|
193
215
|
const allowed = fileType2.some(
|
|
194
|
-
(t) => t.toLowerCase() === mime || t.toLowerCase() ===
|
|
216
|
+
(t) => t.toLowerCase() === mime || t.toLowerCase() === ext
|
|
195
217
|
);
|
|
196
218
|
if (!allowed) {
|
|
197
219
|
throw new Error(
|
|
@@ -331,11 +353,11 @@ function isProbablyText(buffer) {
|
|
|
331
353
|
return true;
|
|
332
354
|
}
|
|
333
355
|
var extByMime = {};
|
|
334
|
-
for (const
|
|
356
|
+
for (const ext in mimes_default) extByMime[mimes_default[ext]] = ext;
|
|
335
357
|
function extFromType(type2) {
|
|
336
358
|
const base = (type2 || "").split(";")[0].trim().toLowerCase();
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
359
|
+
const ext = extByMime[base];
|
|
360
|
+
if (ext) return `.${ext}`;
|
|
339
361
|
const sub = base.split("/")[1];
|
|
340
362
|
return sub && /^[a-z0-9]+$/.test(sub) ? `.${sub}` : ".bin";
|
|
341
363
|
}
|
|
@@ -748,20 +770,20 @@ var encodeExt = (name) => encodeURIComponent(name).replace(
|
|
|
748
770
|
);
|
|
749
771
|
function disposition(name) {
|
|
750
772
|
if (!name) return "attachment";
|
|
751
|
-
const
|
|
752
|
-
if (!
|
|
753
|
-
const ascii =
|
|
773
|
+
const clean2 = name.replace(/[\r\n]/g, "").split(/[\\/]/).pop() || "";
|
|
774
|
+
if (!clean2) return "attachment";
|
|
775
|
+
const ascii = clean2.replace(/[^\x20-\x7e]/g, "?");
|
|
754
776
|
const value = `attachment; filename="${ascii.replace(/["\\]/g, "\\$&")}"`;
|
|
755
|
-
if (
|
|
756
|
-
return `${value}; filename*=UTF-8''${encodeExt(
|
|
777
|
+
if (clean2 === ascii) return value;
|
|
778
|
+
return `${value}; filename*=UTF-8''${encodeExt(clean2)}`;
|
|
757
779
|
}
|
|
758
780
|
|
|
759
781
|
// src/helpers/fileType.ts
|
|
760
782
|
function fileType(file2) {
|
|
761
783
|
if (file2.type) return file2.type;
|
|
762
784
|
const name = file2.path || file2.name || "";
|
|
763
|
-
const
|
|
764
|
-
return
|
|
785
|
+
const ext = name.split(".").pop()?.toLowerCase();
|
|
786
|
+
return ext ? mimes_default[ext] : void 0;
|
|
765
787
|
}
|
|
766
788
|
|
|
767
789
|
// src/helpers/isHtml.ts
|
|
@@ -795,8 +817,8 @@ var Reply = class {
|
|
|
795
817
|
return this;
|
|
796
818
|
}
|
|
797
819
|
download(name) {
|
|
798
|
-
const
|
|
799
|
-
if (
|
|
820
|
+
const ext = name?.split(".").pop();
|
|
821
|
+
if (ext && !this.res.headers.get("content-type")) this.type(ext);
|
|
800
822
|
return this.headers("content-disposition", disposition(name));
|
|
801
823
|
}
|
|
802
824
|
headers(key, value) {
|
|
@@ -854,10 +876,10 @@ var Reply = class {
|
|
|
854
876
|
if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(path)) return this.status(404).send();
|
|
855
877
|
try {
|
|
856
878
|
const fs = await import("fs");
|
|
857
|
-
const
|
|
879
|
+
const ext = path.split(".").pop();
|
|
858
880
|
await fs.promises.access(path);
|
|
859
881
|
const stream = fs.createReadStream(path);
|
|
860
|
-
return this.type(
|
|
882
|
+
return this.type(ext).send(stream);
|
|
861
883
|
} catch (error) {
|
|
862
884
|
if (error.code === "ENOENT" || error.code === "EISDIR") {
|
|
863
885
|
return this.status(404).send();
|
|
@@ -1010,8 +1032,8 @@ var validateToken = (authorization) => {
|
|
|
1010
1032
|
return id;
|
|
1011
1033
|
};
|
|
1012
1034
|
function findSessionId(ctx) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1035
|
+
if (ctx.options.auth?.strategy.includes("token")) {
|
|
1036
|
+
if (!ctx.headers.authorization) return;
|
|
1015
1037
|
return validateToken(ctx.headers.authorization);
|
|
1016
1038
|
}
|
|
1017
1039
|
return ctx.cookies.session || void 0;
|
|
@@ -1019,28 +1041,33 @@ function findSessionId(ctx) {
|
|
|
1019
1041
|
|
|
1020
1042
|
// src/middle/session.ts
|
|
1021
1043
|
var loaded = /* @__PURE__ */ new WeakMap();
|
|
1022
|
-
function
|
|
1044
|
+
function noSession(error) {
|
|
1023
1045
|
const target = {};
|
|
1024
1046
|
return new Proxy(target, {
|
|
1025
1047
|
get(target2, key) {
|
|
1026
1048
|
if (typeof key === "symbol" || key === "then") return target2[key];
|
|
1027
|
-
throw
|
|
1049
|
+
throw error(String(key));
|
|
1028
1050
|
},
|
|
1029
1051
|
set(target2, key, value) {
|
|
1030
1052
|
if (typeof key === "symbol") {
|
|
1031
1053
|
target2[key] = value;
|
|
1032
1054
|
return true;
|
|
1033
1055
|
}
|
|
1034
|
-
throw
|
|
1056
|
+
throw error(String(key));
|
|
1035
1057
|
}
|
|
1036
1058
|
});
|
|
1037
1059
|
}
|
|
1038
1060
|
async function session(ctx) {
|
|
1039
|
-
|
|
1040
|
-
|
|
1061
|
+
const strategy = ctx.options.auth?.strategy;
|
|
1062
|
+
if (strategy?.includes("jwt")) {
|
|
1063
|
+
ctx.session = noSession((key) => ServerError_default.SESSION_JWT({ key }));
|
|
1041
1064
|
return;
|
|
1042
1065
|
}
|
|
1043
1066
|
const id = findSessionId(ctx);
|
|
1067
|
+
if (!id && strategy?.includes("token")) {
|
|
1068
|
+
ctx.session = noSession((key) => ServerError_default.SESSION_GUEST({ key }));
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1044
1071
|
ctx.session = id && await ctx.options.sessions.get(id) || {};
|
|
1045
1072
|
loaded.set(ctx, { id, data: JSON.stringify(ctx.session) });
|
|
1046
1073
|
}
|
|
@@ -1050,6 +1077,10 @@ async function finishLogin(ctx, input, opts = {}) {
|
|
|
1050
1077
|
const settings = ctx.options.auth;
|
|
1051
1078
|
const { strategy, onLogin, onUser, onToken } = settings;
|
|
1052
1079
|
const key = String(input.key);
|
|
1080
|
+
if (!strategy.includes("jwt") && !loaded.has(ctx)) {
|
|
1081
|
+
ctx.session = {};
|
|
1082
|
+
loaded.set(ctx, { id: void 0, data: "{}" });
|
|
1083
|
+
}
|
|
1053
1084
|
const auth2 = {
|
|
1054
1085
|
user: key,
|
|
1055
1086
|
provider: input.provider,
|
|
@@ -1613,17 +1644,6 @@ function parseAuthOptions(auth2) {
|
|
|
1613
1644
|
};
|
|
1614
1645
|
}
|
|
1615
1646
|
|
|
1616
|
-
// src/helpers/bucket.ts
|
|
1617
|
-
import FileSystem from "bucket/fs";
|
|
1618
|
-
function bucket(root) {
|
|
1619
|
-
if (!root) return null;
|
|
1620
|
-
if (typeof root === "string") return FileSystem(root);
|
|
1621
|
-
if (typeof root.file === "function") return root;
|
|
1622
|
-
throw new Error(
|
|
1623
|
-
"Invalid bucket: pass a directory path or a `bucket` instance (with .file())"
|
|
1624
|
-
);
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
1647
|
// src/helpers/color.ts
|
|
1628
1648
|
var map = {
|
|
1629
1649
|
reset: 0,
|
|
@@ -1863,19 +1883,7 @@ function config(options = {}) {
|
|
|
1863
1883
|
}
|
|
1864
1884
|
const publicDir = options.public || env2.PUBLIC;
|
|
1865
1885
|
settings.public = publicDir ? bucket(publicDir) : null;
|
|
1866
|
-
|
|
1867
|
-
if (!up) {
|
|
1868
|
-
settings.uploads = null;
|
|
1869
|
-
} else if (typeof up === "object" && "bucket" in up) {
|
|
1870
|
-
const { bucket: bucket2, maxSize, minSize, fileType: fileType2 } = up;
|
|
1871
|
-
if (maxSize != null) parseBytes(maxSize);
|
|
1872
|
-
if (minSize != null) parseBytes(minSize);
|
|
1873
|
-
settings.uploads = { bucket: bucket(bucket2), maxSize, minSize, fileType: fileType2 };
|
|
1874
|
-
} else {
|
|
1875
|
-
settings.uploads = { bucket: bucket(up) };
|
|
1876
|
-
}
|
|
1877
|
-
const favicon2 = options.favicon || env2.FAVICON;
|
|
1878
|
-
if (favicon2) settings.favicon = favicon2;
|
|
1886
|
+
settings.uploads = resolveUploads(options.uploads);
|
|
1879
1887
|
const production = env2.NODE_ENV === "production";
|
|
1880
1888
|
const defaulted = options.sessions == null;
|
|
1881
1889
|
settings.sessionsDefault = defaulted;
|
|
@@ -1886,7 +1894,7 @@ function config(options = {}) {
|
|
|
1886
1894
|
if (!settings.auth.users) {
|
|
1887
1895
|
if (production) {
|
|
1888
1896
|
throw new Error(
|
|
1889
|
-
"Auth in production needs a persistent `users` store, like auth: { ..., users: kv(redis).prefix('
|
|
1897
|
+
"Auth in production needs a persistent `users` store, like auth: { ..., users: kv(redis).prefix('user:') }."
|
|
1890
1898
|
);
|
|
1891
1899
|
}
|
|
1892
1900
|
settings.auth.users = toStore(/* @__PURE__ */ new Map());
|
|
@@ -1903,9 +1911,10 @@ function config(options = {}) {
|
|
|
1903
1911
|
);
|
|
1904
1912
|
}
|
|
1905
1913
|
if (options.openapi) {
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
}
|
|
1914
|
+
const o = options.openapi;
|
|
1915
|
+
if (o === true) settings.openapi = { path: "/openapi.json" };
|
|
1916
|
+
else if (typeof o === "string") settings.openapi = { path: o };
|
|
1917
|
+
else settings.openapi = { path: "/openapi.json", ...o };
|
|
1909
1918
|
}
|
|
1910
1919
|
settings.onError = options.onError || ((error) => {
|
|
1911
1920
|
return new Response(error.message || "Server Error", {
|
|
@@ -1924,9 +1933,8 @@ function config(options = {}) {
|
|
|
1924
1933
|
const origin = settings.cors.origin === true ? "*" : String(settings.cors.origin);
|
|
1925
1934
|
log.message("cors", origin);
|
|
1926
1935
|
}
|
|
1927
|
-
if (settings.favicon) log.message("favicon", loc(settings.favicon));
|
|
1928
1936
|
if (settings.cache !== void 0) log.message("cache", loc(options.cache));
|
|
1929
|
-
if (settings.openapi) log.message("openapi", settings.openapi.path
|
|
1937
|
+
if (settings.openapi) log.message("openapi", settings.openapi.path);
|
|
1930
1938
|
return settings;
|
|
1931
1939
|
}
|
|
1932
1940
|
|
|
@@ -2106,13 +2114,11 @@ async function parseResponse(out, ctx) {
|
|
|
2106
2114
|
out.headers.set("Server-Timing", ctx.time.headers());
|
|
2107
2115
|
}
|
|
2108
2116
|
const prev = loaded.get(ctx);
|
|
2109
|
-
|
|
2110
|
-
const data = jwt ? "{}" : JSON.stringify(ctx.session ?? {});
|
|
2111
|
-
if (!jwt && data !== (prev?.data ?? "{}")) {
|
|
2117
|
+
if (prev && JSON.stringify(ctx.session ?? {}) !== prev.data) {
|
|
2112
2118
|
if (ctx.options.sessionsDefault && ctx.platform.production) {
|
|
2113
2119
|
warnDefault();
|
|
2114
2120
|
}
|
|
2115
|
-
let id = prev
|
|
2121
|
+
let id = prev.id;
|
|
2116
2122
|
if (!id) {
|
|
2117
2123
|
id = createId();
|
|
2118
2124
|
out.headers.append(
|
|
@@ -2128,11 +2134,6 @@ async function parseResponse(out, ctx) {
|
|
|
2128
2134
|
}
|
|
2129
2135
|
ctx.options.sessions.set(id, ctx.session);
|
|
2130
2136
|
}
|
|
2131
|
-
if (ctx?.res?.headers) {
|
|
2132
|
-
for (const key in ctx.res.headers) {
|
|
2133
|
-
out.headers[key] = ctx.res.headers[key];
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
2137
|
return out;
|
|
2137
2138
|
}
|
|
2138
2139
|
|
|
@@ -2465,14 +2466,14 @@ async function getJwtUser(ctx) {
|
|
|
2465
2466
|
return exposed;
|
|
2466
2467
|
}
|
|
2467
2468
|
async function getAuthSession(ctx) {
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
if (!id) return;
|
|
2472
|
-
session2 = await ctx.options.sessions.get(id) ?? void 0;
|
|
2469
|
+
if (loaded.has(ctx)) {
|
|
2470
|
+
const session3 = ctx.session;
|
|
2471
|
+
return session3?.user ? session3 : void 0;
|
|
2473
2472
|
}
|
|
2474
|
-
|
|
2475
|
-
return
|
|
2473
|
+
const id = findSessionId(ctx);
|
|
2474
|
+
if (!id) return;
|
|
2475
|
+
const session2 = await ctx.options.sessions.get(id);
|
|
2476
|
+
return session2?.user ? session2 : void 0;
|
|
2476
2477
|
}
|
|
2477
2478
|
async function getUser(ctx) {
|
|
2478
2479
|
if (!ctx.options.auth) return;
|
|
@@ -2524,31 +2525,32 @@ function auth(app) {
|
|
|
2524
2525
|
app.use(async function middle(ctx) {
|
|
2525
2526
|
ctx.user = await getUser(ctx);
|
|
2526
2527
|
});
|
|
2527
|
-
|
|
2528
|
+
const spec = { schema: { tags: "auth" } };
|
|
2529
|
+
app.post("/auth/logout", spec, logout);
|
|
2528
2530
|
const enabled = app.settings.auth.providers;
|
|
2529
2531
|
for (const name of oauth2) {
|
|
2530
2532
|
if (!enabled.includes(name)) continue;
|
|
2531
2533
|
const key = name.toUpperCase();
|
|
2532
2534
|
if (!env[`${key}_ID`]) throw new Error(`${key}_ID not defined`);
|
|
2533
2535
|
if (!env[`${key}_SECRET`]) throw new Error(`${key}_SECRET not defined`);
|
|
2534
|
-
app.get(`/auth/login/${name}`, providers_default[name].login);
|
|
2535
|
-
app.get(`/auth/callback/${name}`, providers_default[name].callback);
|
|
2536
|
-
app.post(`/auth/verify/${name}`, providers_default[name].verify);
|
|
2536
|
+
app.get(`/auth/login/${name}`, spec, providers_default[name].login);
|
|
2537
|
+
app.get(`/auth/callback/${name}`, spec, providers_default[name].callback);
|
|
2538
|
+
app.post(`/auth/verify/${name}`, spec, providers_default[name].verify);
|
|
2537
2539
|
}
|
|
2538
2540
|
if (enabled.includes("apple")) {
|
|
2539
2541
|
const keys = ["APPLE_ID", "APPLE_TEAM_ID", "APPLE_KEY_ID", "APPLE_PRIVATE_KEY"];
|
|
2540
2542
|
for (const key of keys) {
|
|
2541
2543
|
if (!env[key]) throw new Error(`${key} not defined`);
|
|
2542
2544
|
}
|
|
2543
|
-
app.get("/auth/login/apple", providers_default.apple.login);
|
|
2544
|
-
app.post("/auth/callback/apple", providers_default.apple.callback);
|
|
2545
|
-
app.post("/auth/verify/apple", providers_default.apple.verify);
|
|
2545
|
+
app.get("/auth/login/apple", spec, providers_default.apple.login);
|
|
2546
|
+
app.post("/auth/callback/apple", spec, providers_default.apple.callback);
|
|
2547
|
+
app.post("/auth/verify/apple", spec, providers_default.apple.verify);
|
|
2546
2548
|
}
|
|
2547
2549
|
if (enabled.includes("email")) {
|
|
2548
|
-
app.post("/auth/register/email", providers_default.email.register);
|
|
2549
|
-
app.post("/auth/login/email", providers_default.email.login);
|
|
2550
|
-
app.put("/auth/password/email", providers_default.email.password);
|
|
2551
|
-
app.put("/auth/reset/email", providers_default.email.reset);
|
|
2550
|
+
app.post("/auth/register/email", spec, providers_default.email.register);
|
|
2551
|
+
app.post("/auth/login/email", spec, providers_default.email.login);
|
|
2552
|
+
app.put("/auth/password/email", spec, providers_default.email.password);
|
|
2553
|
+
app.put("/auth/reset/email", spec, providers_default.email.reset);
|
|
2552
2554
|
}
|
|
2553
2555
|
}
|
|
2554
2556
|
|
|
@@ -2587,8 +2589,8 @@ async function assets(ctx) {
|
|
|
2587
2589
|
const info = file2.info?.bind(file2);
|
|
2588
2590
|
const meta = info ? await info() : null;
|
|
2589
2591
|
if (info ? !meta : !await file2.exists()) return;
|
|
2590
|
-
const
|
|
2591
|
-
const ctype =
|
|
2592
|
+
const ext = ctx.url.pathname.split(".").pop()?.toLowerCase();
|
|
2593
|
+
const ctype = ext && mimes_default[ext] || meta?.type || ext;
|
|
2592
2594
|
const headers2 = { "cache-control": CACHE_CONTROL };
|
|
2593
2595
|
let tag;
|
|
2594
2596
|
if (meta) {
|
|
@@ -2623,46 +2625,8 @@ async function assets(ctx) {
|
|
|
2623
2625
|
}
|
|
2624
2626
|
}
|
|
2625
2627
|
|
|
2626
|
-
// src/middle/favicon.ts
|
|
2627
|
-
var CACHE_CONTROL2 = "public, max-age=86400";
|
|
2628
|
-
var ext = (name) => name.split(".").pop() || "ico";
|
|
2629
|
-
async function loadFavicon(fav) {
|
|
2630
|
-
try {
|
|
2631
|
-
const type2 = ext(typeof fav === "string" ? fav : fav?.name);
|
|
2632
|
-
const bytes = typeof fav === "string" ? await (await import("fs/promises")).readFile(fav) : Buffer.from(await fav.bytes());
|
|
2633
|
-
return { bytes, type: type2, etag: etag(bytes) };
|
|
2634
|
-
} catch {
|
|
2635
|
-
return null;
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
async function favicon(ctx) {
|
|
2639
|
-
const fav = ctx.options.favicon;
|
|
2640
|
-
if (!fav) return;
|
|
2641
|
-
if (ctx.app.faviconCache === void 0) {
|
|
2642
|
-
ctx.app.faviconCache = await loadFavicon(fav);
|
|
2643
|
-
}
|
|
2644
|
-
const entry = ctx.app.faviconCache;
|
|
2645
|
-
if (!entry) return 204;
|
|
2646
|
-
const headers2 = { "cache-control": CACHE_CONTROL2, etag: entry.etag };
|
|
2647
|
-
if (ctx.headers["if-none-match"] === entry.etag) {
|
|
2648
|
-
return status(304).headers(headers2).send();
|
|
2649
|
-
}
|
|
2650
|
-
return type(entry.type).headers(headers2).send(entry.bytes);
|
|
2651
|
-
}
|
|
2652
|
-
|
|
2653
2628
|
// src/middle/openapi.ts
|
|
2654
2629
|
import * as fsp from "fs/promises";
|
|
2655
|
-
var entities = {
|
|
2656
|
-
"&": "&",
|
|
2657
|
-
"<": "<",
|
|
2658
|
-
">": ">",
|
|
2659
|
-
'"': """
|
|
2660
|
-
};
|
|
2661
|
-
var encode = (str = "") => {
|
|
2662
|
-
if (typeof str === "number") str = String(str);
|
|
2663
|
-
if (typeof str !== "string") return "";
|
|
2664
|
-
return str.replace(/[&<>"]/g, (tag) => entities[tag]);
|
|
2665
|
-
};
|
|
2666
2630
|
var getConfig = (options = {}) => {
|
|
2667
2631
|
const config2 = { ...options };
|
|
2668
2632
|
if (config2.tags) {
|
|
@@ -2676,6 +2640,25 @@ var getConfig = (options = {}) => {
|
|
|
2676
2640
|
}
|
|
2677
2641
|
return config2;
|
|
2678
2642
|
};
|
|
2643
|
+
var clean = ({ $schema, ...schema }) => schema;
|
|
2644
|
+
async function toJsonSchema(schema) {
|
|
2645
|
+
try {
|
|
2646
|
+
if (typeof schema?.toJsonSchema === "function") {
|
|
2647
|
+
return clean(schema.toJsonSchema());
|
|
2648
|
+
}
|
|
2649
|
+
const vendor = schema?.["~standard"]?.vendor;
|
|
2650
|
+
if (vendor === "zod") {
|
|
2651
|
+
const mod = await import("zod");
|
|
2652
|
+
return clean((mod.toJSONSchema ?? mod.z.toJSONSchema)(schema));
|
|
2653
|
+
}
|
|
2654
|
+
if (vendor === "valibot") {
|
|
2655
|
+
const mod = await import("@valibot/to-json-schema");
|
|
2656
|
+
return clean(mod.toJsonSchema(schema));
|
|
2657
|
+
}
|
|
2658
|
+
} catch {
|
|
2659
|
+
}
|
|
2660
|
+
return zodToSchema(schema);
|
|
2661
|
+
}
|
|
2679
2662
|
function zodToSchema(schema) {
|
|
2680
2663
|
const type2 = schema?.def?.type || "string";
|
|
2681
2664
|
if (type2 === "object") {
|
|
@@ -2698,48 +2681,31 @@ function zodToSchema(schema) {
|
|
|
2698
2681
|
return { type: type2 };
|
|
2699
2682
|
}
|
|
2700
2683
|
var pkgProm = fsp.readFile("package.json", "utf-8").then((data) => JSON.parse(data)).catch(() => ({}));
|
|
2701
|
-
var
|
|
2702
|
-
const found = fn.toString().split("\n").filter((l) => /\s+\/\/\s/.test(l)).map((l) => l.trim().replace("// ", "")).find((l) => l.startsWith(name));
|
|
2703
|
-
if (!found) return "";
|
|
2704
|
-
return encode(found.replace(name, "").trim());
|
|
2705
|
-
};
|
|
2706
|
-
var getDescription = (fn) => getTag("@description", fn) || "";
|
|
2707
|
-
var getReturn = (fn) => getTag("@returns", fn) || "OK";
|
|
2708
|
-
var generateOpenApiPaths = (handlers) => {
|
|
2684
|
+
var generateOpenApiPaths = async (handlers, specPath) => {
|
|
2709
2685
|
const paths = {};
|
|
2710
2686
|
for (const [method, routes] of Object.entries(handlers)) {
|
|
2711
2687
|
for (const route of routes) {
|
|
2712
2688
|
const path = route.path;
|
|
2713
|
-
const fn = route.fns.find((p) => typeof p === "function");
|
|
2714
2689
|
const meta = route.options ?? {};
|
|
2715
2690
|
const config2 = getConfig(route.options?.schema);
|
|
2716
|
-
if (typeof path !== "string" || path === "*" || path ===
|
|
2691
|
+
if (typeof path !== "string" || path === "*" || path === specPath) {
|
|
2717
2692
|
continue;
|
|
2718
2693
|
}
|
|
2694
|
+
if (route.options?.schema === false) continue;
|
|
2719
2695
|
const normalizedPath = path.replace(/\(\w+\)/gi, "").replace(/:([a-zA-Z0-9_]+)/g, "{$1}");
|
|
2720
2696
|
if (!paths[normalizedPath]) {
|
|
2721
2697
|
paths[normalizedPath] = {};
|
|
2722
2698
|
}
|
|
2723
|
-
const getTitle = (fn2) => {
|
|
2724
|
-
if (!fn2.name) return null;
|
|
2725
|
-
const wrongNames = ["default"];
|
|
2726
|
-
if (wrongNames.includes(fn2.name)) return null;
|
|
2727
|
-
if (fn2.name.length <= 3) return null;
|
|
2728
|
-
if (fn2.name.includes("_")) return fn2.name.replace(/_/g, " ");
|
|
2729
|
-
const name = fn2.name.split(/(?=[A-Z])/).join(" ").toLowerCase();
|
|
2730
|
-
return name[0].toUpperCase() + name.slice(1);
|
|
2731
|
-
};
|
|
2732
2699
|
let requestBody;
|
|
2733
2700
|
if (meta?.body) {
|
|
2734
|
-
const schema =
|
|
2701
|
+
const schema = await toJsonSchema(meta.body);
|
|
2735
2702
|
requestBody = { content: { "application/json": { schema } } };
|
|
2736
2703
|
}
|
|
2737
2704
|
let responses;
|
|
2738
2705
|
if (meta?.response) {
|
|
2739
|
-
const schema =
|
|
2740
|
-
const description = getReturn(fn);
|
|
2706
|
+
const schema = await toJsonSchema(meta.response);
|
|
2741
2707
|
responses = {
|
|
2742
|
-
200: { description, content: { "application/json": { schema } } }
|
|
2708
|
+
200: { description: "OK", content: { "application/json": { schema } } }
|
|
2743
2709
|
};
|
|
2744
2710
|
}
|
|
2745
2711
|
const parameters = [];
|
|
@@ -2754,18 +2720,20 @@ var generateOpenApiPaths = (handlers) => {
|
|
|
2754
2720
|
});
|
|
2755
2721
|
});
|
|
2756
2722
|
if (meta?.query) {
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2723
|
+
const schema = await toJsonSchema(meta.query);
|
|
2724
|
+
for (const [name, prop] of Object.entries(schema.properties ?? {})) {
|
|
2725
|
+
parameters.push({
|
|
2726
|
+
name,
|
|
2727
|
+
in: "query",
|
|
2728
|
+
required: schema.required?.includes(name) ?? false,
|
|
2729
|
+
schema: prop
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2764
2732
|
}
|
|
2765
2733
|
paths[normalizedPath][method] = {
|
|
2766
2734
|
tags: config2.tags,
|
|
2767
|
-
summary: config2.title
|
|
2768
|
-
description: config2.description
|
|
2735
|
+
summary: config2.title,
|
|
2736
|
+
description: config2.description,
|
|
2769
2737
|
requestBody,
|
|
2770
2738
|
parameters,
|
|
2771
2739
|
responses
|
|
@@ -2776,34 +2744,21 @@ var generateOpenApiPaths = (handlers) => {
|
|
|
2776
2744
|
};
|
|
2777
2745
|
var openapi_default = async (ctx) => {
|
|
2778
2746
|
const pkg = await pkgProm;
|
|
2747
|
+
const { title, description, version } = ctx.options.openapi ?? {};
|
|
2779
2748
|
const domain = pkg.homepage || ctx.url.origin;
|
|
2780
|
-
|
|
2749
|
+
return {
|
|
2781
2750
|
openapi: "3.0.0",
|
|
2782
2751
|
info: {
|
|
2783
|
-
title: pkg.name || "API Documentation",
|
|
2784
|
-
version: pkg.version || "1.0.0",
|
|
2785
|
-
description: pkg.description || ""
|
|
2752
|
+
title: title || pkg.name || "API Documentation",
|
|
2753
|
+
version: version || pkg.version || "1.0.0",
|
|
2754
|
+
description: description ?? (pkg.description || "")
|
|
2786
2755
|
},
|
|
2787
2756
|
servers: domain ? [{ url: domain }] : [],
|
|
2788
|
-
paths: generateOpenApiPaths(
|
|
2757
|
+
paths: await generateOpenApiPaths(
|
|
2758
|
+
ctx.app.handlers,
|
|
2759
|
+
ctx.options.openapi?.path ?? ""
|
|
2760
|
+
)
|
|
2789
2761
|
};
|
|
2790
|
-
const configuration = ctx.options.openapi?.scalar || {};
|
|
2791
|
-
return `
|
|
2792
|
-
<!doctype html>
|
|
2793
|
-
<html>
|
|
2794
|
-
<head>
|
|
2795
|
-
<title>API Reference</title>
|
|
2796
|
-
<meta charset="utf-8" />
|
|
2797
|
-
<meta
|
|
2798
|
-
name="viewport"
|
|
2799
|
-
content="width=device-width, initial-scale=1" />
|
|
2800
|
-
<style>.open-api-client-button {display: none!important;}</style>
|
|
2801
|
-
</head>
|
|
2802
|
-
<body>
|
|
2803
|
-
<script id="api-reference" type="application/json" data-configuration="${encode(JSON.stringify(configuration))}">${JSON.stringify(openApi, null, 2)}</script>
|
|
2804
|
-
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
2805
|
-
</body>
|
|
2806
|
-
</html> `;
|
|
2807
2762
|
};
|
|
2808
2763
|
|
|
2809
2764
|
// src/middle/preflight.ts
|
|
@@ -3038,7 +2993,7 @@ function isValidMethod(method) {
|
|
|
3038
2993
|
|
|
3039
2994
|
// src/context/node.ts
|
|
3040
2995
|
var chunkArray = (arr) => arr.length > 2 ? [[arr[0], arr[1]], ...chunkArray(arr.slice(2))] : [arr];
|
|
3041
|
-
async function createNode(req, app) {
|
|
2996
|
+
async function createNode(req, app, signal = new AbortController().signal) {
|
|
3042
2997
|
const init = performance.now();
|
|
3043
2998
|
const method = req.method?.toLowerCase() || "get";
|
|
3044
2999
|
if (!isValidMethod(method)) {
|
|
@@ -3072,6 +3027,7 @@ async function createNode(req, app) {
|
|
|
3072
3027
|
body: void 0,
|
|
3073
3028
|
headers: headers2,
|
|
3074
3029
|
cookies: cookies2,
|
|
3030
|
+
signal,
|
|
3075
3031
|
session: {},
|
|
3076
3032
|
init,
|
|
3077
3033
|
app,
|
|
@@ -3112,6 +3068,7 @@ async function createWinter(req, app, server2) {
|
|
|
3112
3068
|
body: void 0,
|
|
3113
3069
|
headers: headers2,
|
|
3114
3070
|
cookies: cookies2,
|
|
3071
|
+
signal: req.signal,
|
|
3115
3072
|
session: {},
|
|
3116
3073
|
init,
|
|
3117
3074
|
app,
|
|
@@ -3149,7 +3106,11 @@ var Node = async (app) => {
|
|
|
3149
3106
|
const http = await import("http");
|
|
3150
3107
|
const server2 = http.createServer(
|
|
3151
3108
|
async (request, response) => {
|
|
3152
|
-
const
|
|
3109
|
+
const controller = new AbortController();
|
|
3110
|
+
response.on("close", () => {
|
|
3111
|
+
if (!response.writableFinished) controller.abort();
|
|
3112
|
+
});
|
|
3113
|
+
const ctx = await createNode(request, app, controller.signal);
|
|
3153
3114
|
if ("error" in ctx) throw ctx.error;
|
|
3154
3115
|
const out = await handleRequest(app, ctx);
|
|
3155
3116
|
response.writeHead(out.status || 200, parseHeaders_default(out.headers));
|
|
@@ -3231,6 +3192,9 @@ var Router = class _Router {
|
|
|
3231
3192
|
options = rest.shift();
|
|
3232
3193
|
}
|
|
3233
3194
|
checkParserConflict(options, this.settings?.parser);
|
|
3195
|
+
if (options.uploads !== void 0) {
|
|
3196
|
+
options.uploads = resolveUploads(options.uploads);
|
|
3197
|
+
}
|
|
3234
3198
|
const base = method === "socket" ? [] : this.middleware;
|
|
3235
3199
|
const fns = [...base, ...rest].filter((fn) => fn != null);
|
|
3236
3200
|
this.handlers[method].push({ path, options, fns });
|
|
@@ -3337,9 +3301,6 @@ var Server = class extends Router {
|
|
|
3337
3301
|
platform;
|
|
3338
3302
|
sockets;
|
|
3339
3303
|
websocket;
|
|
3340
|
-
// Lazily-loaded favicon bytes, cached per server until restart (see favicon
|
|
3341
|
-
// middleware). `undefined` = not loaded yet; `null` = configured but missing.
|
|
3342
|
-
faviconCache;
|
|
3343
3304
|
port;
|
|
3344
3305
|
constructor(options = {}) {
|
|
3345
3306
|
super();
|
|
@@ -3359,13 +3320,12 @@ var Server = class extends Router {
|
|
|
3359
3320
|
app.use(timer);
|
|
3360
3321
|
if (this.settings.cors) app.use(preflight);
|
|
3361
3322
|
app.use(assets);
|
|
3362
|
-
if (this.settings.favicon) app.get("/favicon.ico", favicon);
|
|
3363
3323
|
app.use(session);
|
|
3364
3324
|
if (this.settings.auth) {
|
|
3365
3325
|
auth(app);
|
|
3366
3326
|
}
|
|
3367
3327
|
if (this.settings.openapi) {
|
|
3368
|
-
app.get(this.settings.openapi.path
|
|
3328
|
+
app.get(this.settings.openapi.path, openapi_default);
|
|
3369
3329
|
}
|
|
3370
3330
|
}
|
|
3371
3331
|
self() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@server/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.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",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"server.js"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "bunx tsup src/index.ts --format esm --dts --out-dir . --target node24",
|
|
16
|
+
"build": "bunx tsup src/index.ts --format esm --dts --out-dir . --target node24 --external zod --external @valibot/to-json-schema",
|
|
17
|
+
"prepare": "mkdir -p node_modules/@server && ln -sfn ../.. node_modules/@server/next",
|
|
17
18
|
"start": "bun test --watch",
|
|
18
19
|
"lint": "npx tsc --noEmit && npx @biomejs/biome lint ./src --skip=lint/suspicious/noExplicitAny --skip=lint/style/noParameterAssign --skip=lint/suspicious/noConfusingVoidType --skip=lint/complexity/noBannedTypes",
|
|
19
20
|
"test": "npm run test:bun && tsc --noEmit",
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"Reply": "docs/4. Reply.md",
|
|
55
56
|
"Authentication": "docs/5. Authentication.md",
|
|
56
57
|
"Testing": "docs/6. Testing.md",
|
|
57
|
-
"
|
|
58
|
+
"Concepts": "docs/7. Concepts.md",
|
|
58
59
|
"FAQ": "docs/8. FAQ.md"
|
|
59
60
|
},
|
|
60
61
|
"tutorials": "docs/tutorials"
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
"devDependencies": {
|
|
67
68
|
"@types/bun": "^1.3.0",
|
|
68
69
|
"@types/node": "^24.10.0",
|
|
70
|
+
"@valibot/to-json-schema": "^1.7.1",
|
|
69
71
|
"arktype": "^2.2.3",
|
|
70
72
|
"bun": "^1.3.13",
|
|
71
73
|
"check-dts": "^0.8.2",
|