@server/next 0.34.0 → 0.34.2
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 +28 -12
- package/index.js +417 -252
- package/package.json +2 -1
package/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
|
|
1
3
|
type LimitOptions = {
|
|
2
4
|
maxSize?: number | string;
|
|
3
5
|
minSize?: number | string;
|
|
@@ -6,12 +8,12 @@ type LimitOptions = {
|
|
|
6
8
|
declare class UploadPipeline {
|
|
7
9
|
private _bucket;
|
|
8
10
|
private _limits;
|
|
9
|
-
constructor(bucket?: Bucket | null);
|
|
11
|
+
constructor(bucket?: Bucket | string | null);
|
|
10
12
|
limit(options: LimitOptions): this;
|
|
11
|
-
store(bucket: Bucket): this;
|
|
13
|
+
store(bucket: Bucket | string): this;
|
|
12
14
|
processFile(originalName: string, data: Buffer, contentType: string): Promise<UploadedFile>;
|
|
13
15
|
}
|
|
14
|
-
declare function upload(bucket?: Bucket | null): UploadPipeline;
|
|
16
|
+
declare function upload(bucket?: Bucket | string | null): UploadPipeline;
|
|
15
17
|
|
|
16
18
|
type Method = "get" | "post" | "put" | "patch" | "delete" | "head" | "options" | "socket";
|
|
17
19
|
type ServerConfig<Session = {}, User = {}> = {
|
|
@@ -49,12 +51,21 @@ type Cookie = {
|
|
|
49
51
|
sameSite?: "Strict" | "Lax" | "None";
|
|
50
52
|
};
|
|
51
53
|
type RouterMethod = "*" | Method;
|
|
54
|
+
type BucketFile = {
|
|
55
|
+
readonly path: string;
|
|
56
|
+
readonly id: string;
|
|
57
|
+
readonly name: string;
|
|
58
|
+
exists(): Promise<boolean>;
|
|
59
|
+
write(content: string | Buffer | ReadableStream, options?: {
|
|
60
|
+
type?: string;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
stream(): ReadableStream;
|
|
63
|
+
bytes(): Promise<Uint8Array>;
|
|
64
|
+
remove(): Promise<void>;
|
|
65
|
+
};
|
|
52
66
|
type Bucket = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
write: (path: string, data: string | Buffer | ReadableStream) => Promise<void | string>;
|
|
56
|
-
delete: (path: string) => Promise<boolean>;
|
|
57
|
-
folder?: (prefix: string) => Bucket;
|
|
67
|
+
file(name: string): BucketFile;
|
|
68
|
+
folder?(prefix: string): Bucket;
|
|
58
69
|
};
|
|
59
70
|
type UploadedFile = {
|
|
60
71
|
name: string;
|
|
@@ -160,7 +171,7 @@ type Options = {
|
|
|
160
171
|
openapi?: any;
|
|
161
172
|
onError?: OnError;
|
|
162
173
|
log?: LogLevel | boolean;
|
|
163
|
-
favicon?: string |
|
|
174
|
+
favicon?: string | BucketFile;
|
|
164
175
|
security?: boolean | SecurityOptions;
|
|
165
176
|
body?: BodyMode;
|
|
166
177
|
};
|
|
@@ -179,7 +190,7 @@ type Settings = {
|
|
|
179
190
|
openapi?: any;
|
|
180
191
|
onError?: OnError;
|
|
181
192
|
log: Logger;
|
|
182
|
-
favicon?: string |
|
|
193
|
+
favicon?: string | BucketFile;
|
|
183
194
|
security: SecuritySettings;
|
|
184
195
|
body: BodyMode;
|
|
185
196
|
};
|
|
@@ -340,10 +351,15 @@ declare class Server<O extends ServerConfig = {}> extends Router<O> {
|
|
|
340
351
|
platform: Platform;
|
|
341
352
|
sockets: any[];
|
|
342
353
|
websocket: any;
|
|
354
|
+
faviconCache?: {
|
|
355
|
+
bytes: Buffer;
|
|
356
|
+
type: string;
|
|
357
|
+
etag: string;
|
|
358
|
+
} | null;
|
|
343
359
|
port?: number;
|
|
344
360
|
constructor(options?: Options);
|
|
345
361
|
self(): this;
|
|
346
|
-
node(): Promise<
|
|
362
|
+
node(): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
|
347
363
|
fetch(request: Request, env?: BunEnv): Promise<Response>;
|
|
348
364
|
callback(request: Request, context: unknown): Promise<Response>;
|
|
349
365
|
test(): {
|
|
@@ -462,4 +478,4 @@ declare class Server<O extends ServerConfig = {}> extends Router<O> {
|
|
|
462
478
|
}
|
|
463
479
|
declare function server<Session extends Record<string, any> = {}, User extends Record<string, any> = {}>(options?: Options): Server<ServerConfig<Session, User>>;
|
|
464
480
|
|
|
465
|
-
export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type BodyMode, type Bucket, type BunEnv, type Context, type Cookie, type CorsSettings, type ExtractPathParams, type InferParamType, type InlineReply, type KVStore, type LimitOptions, 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, UploadPipeline, type UploadedFile, cookies, server as default, download, file, headers, json, redirect, router, send, status, type, upload };
|
|
481
|
+
export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type BodyMode, type Bucket, type BucketFile, type BunEnv, type Context, type Cookie, type CorsSettings, type ExtractPathParams, type InferParamType, type InlineReply, type KVStore, type LimitOptions, 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, UploadPipeline, type UploadedFile, cookies, server as default, download, file, headers, json, redirect, router, send, status, type, upload };
|
package/index.js
CHANGED
|
@@ -130,6 +130,163 @@ function createId(source, size = 16) {
|
|
|
130
130
|
return randomId(size);
|
|
131
131
|
}
|
|
132
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
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/helpers/bucket.ts
|
|
216
|
+
import * as fs from "fs";
|
|
217
|
+
import * as fsp from "fs/promises";
|
|
218
|
+
import * as path from "path";
|
|
219
|
+
function localBucket(root) {
|
|
220
|
+
const base = path.resolve(root);
|
|
221
|
+
const resolveKey = (name) => {
|
|
222
|
+
if (!name) throw new Error("File name is required");
|
|
223
|
+
const full = path.resolve(base, name.replace(/^\/+/, ""));
|
|
224
|
+
if (full !== base && !full.startsWith(base + path.sep)) {
|
|
225
|
+
throw new Error(`Path "${name}" escapes the bucket root`);
|
|
226
|
+
}
|
|
227
|
+
return full;
|
|
228
|
+
};
|
|
229
|
+
const file2 = (name) => {
|
|
230
|
+
const full = resolveKey(name);
|
|
231
|
+
return {
|
|
232
|
+
path: full,
|
|
233
|
+
id: name.replace(/^\/+/, ""),
|
|
234
|
+
name: path.basename(name),
|
|
235
|
+
async exists() {
|
|
236
|
+
const stats = await fsp.stat(full).catch(() => null);
|
|
237
|
+
return !!stats?.isFile();
|
|
238
|
+
},
|
|
239
|
+
async write(content) {
|
|
240
|
+
await fsp.mkdir(path.dirname(full), { recursive: true });
|
|
241
|
+
if (content instanceof ReadableStream) {
|
|
242
|
+
const writable = fs.createWriteStream(full);
|
|
243
|
+
for await (const chunk of content) {
|
|
244
|
+
writable.write(chunk);
|
|
245
|
+
}
|
|
246
|
+
await new Promise((resolve2, reject) => {
|
|
247
|
+
writable.on("error", reject);
|
|
248
|
+
writable.end(() => resolve2());
|
|
249
|
+
});
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
await fsp.writeFile(full, content);
|
|
253
|
+
},
|
|
254
|
+
stream() {
|
|
255
|
+
const nodeStream = fs.createReadStream(full);
|
|
256
|
+
return new ReadableStream({
|
|
257
|
+
start(controller) {
|
|
258
|
+
nodeStream.on("data", (chunk) => controller.enqueue(chunk));
|
|
259
|
+
nodeStream.on("end", () => controller.close());
|
|
260
|
+
nodeStream.on("error", (err) => controller.error(err));
|
|
261
|
+
},
|
|
262
|
+
cancel() {
|
|
263
|
+
nodeStream.destroy();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
async bytes() {
|
|
268
|
+
return new Uint8Array(await fsp.readFile(full));
|
|
269
|
+
},
|
|
270
|
+
async remove() {
|
|
271
|
+
await fsp.unlink(full).catch(() => {
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
file: file2,
|
|
278
|
+
folder: (prefix) => localBucket(path.join(base, prefix))
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function bucket(root) {
|
|
282
|
+
if (!root) return null;
|
|
283
|
+
if (typeof root === "string") return localBucket(root);
|
|
284
|
+
if (typeof root.file === "function") return root;
|
|
285
|
+
throw new Error(
|
|
286
|
+
"Invalid bucket: pass a directory path or a `bucket` instance (with .file())"
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
133
290
|
// src/helpers/upload.ts
|
|
134
291
|
function parseBytes(value) {
|
|
135
292
|
if (typeof value === "number") return value;
|
|
@@ -148,24 +305,31 @@ function getExt(filename) {
|
|
|
148
305
|
if (i <= 0) return ".bin";
|
|
149
306
|
return filename.slice(i).toLowerCase();
|
|
150
307
|
}
|
|
151
|
-
async function saveFileToBucket(originalName, data,
|
|
152
|
-
const
|
|
153
|
-
const id = `${createId()}${
|
|
154
|
-
const
|
|
155
|
-
|
|
308
|
+
async function saveFileToBucket(originalName, data, bucket2, contentType) {
|
|
309
|
+
const ext2 = getExt(originalName);
|
|
310
|
+
const id = `${createId()}${ext2}`;
|
|
311
|
+
const file2 = bucket2.file(id);
|
|
312
|
+
await file2.write(data, { type: contentType });
|
|
313
|
+
return {
|
|
314
|
+
name: originalName,
|
|
315
|
+
id,
|
|
316
|
+
path: file2.path,
|
|
317
|
+
type: contentType,
|
|
318
|
+
size: data.length
|
|
319
|
+
};
|
|
156
320
|
}
|
|
157
321
|
var UploadPipeline = class {
|
|
158
322
|
_bucket;
|
|
159
323
|
_limits = {};
|
|
160
|
-
constructor(
|
|
161
|
-
this._bucket = bucket ??
|
|
324
|
+
constructor(bucket2) {
|
|
325
|
+
this._bucket = bucket(bucket2 ?? void 0);
|
|
162
326
|
}
|
|
163
327
|
limit(options) {
|
|
164
328
|
this._limits = { ...this._limits, ...options };
|
|
165
329
|
return this;
|
|
166
330
|
}
|
|
167
|
-
store(
|
|
168
|
-
this._bucket = bucket;
|
|
331
|
+
store(bucket2) {
|
|
332
|
+
this._bucket = bucket(bucket2);
|
|
169
333
|
return this;
|
|
170
334
|
}
|
|
171
335
|
async processFile(originalName, data, contentType) {
|
|
@@ -181,10 +345,10 @@ var UploadPipeline = class {
|
|
|
181
345
|
);
|
|
182
346
|
}
|
|
183
347
|
if (fileType && fileType.length > 0) {
|
|
184
|
-
const
|
|
348
|
+
const ext2 = getExt(originalName);
|
|
185
349
|
const mime = contentType.toLowerCase();
|
|
186
350
|
const allowed = fileType.some(
|
|
187
|
-
(t) => t.toLowerCase() === mime || t.toLowerCase() ===
|
|
351
|
+
(t) => t.toLowerCase() === mime || t.toLowerCase() === ext2
|
|
188
352
|
);
|
|
189
353
|
if (!allowed) {
|
|
190
354
|
throw new Error(
|
|
@@ -200,8 +364,8 @@ var UploadPipeline = class {
|
|
|
200
364
|
return saveFileToBucket(originalName, data, this._bucket, contentType);
|
|
201
365
|
}
|
|
202
366
|
};
|
|
203
|
-
function upload(
|
|
204
|
-
return new UploadPipeline(
|
|
367
|
+
function upload(bucket2) {
|
|
368
|
+
return new UploadPipeline(bucket2);
|
|
205
369
|
}
|
|
206
370
|
|
|
207
371
|
// src/helpers/parseBody.ts
|
|
@@ -231,24 +395,12 @@ function isProbablyText(buffer) {
|
|
|
231
395
|
}
|
|
232
396
|
return true;
|
|
233
397
|
}
|
|
234
|
-
var
|
|
235
|
-
|
|
236
|
-
"application/pdf": ".pdf",
|
|
237
|
-
"application/zip": ".zip",
|
|
238
|
-
"text/plain": ".txt",
|
|
239
|
-
"text/html": ".html",
|
|
240
|
-
"text/csv": ".csv",
|
|
241
|
-
"image/jpeg": ".jpg",
|
|
242
|
-
"image/png": ".png",
|
|
243
|
-
"image/gif": ".gif",
|
|
244
|
-
"image/webp": ".webp",
|
|
245
|
-
"image/svg+xml": ".svg",
|
|
246
|
-
"video/mp4": ".mp4",
|
|
247
|
-
"audio/mpeg": ".mp3"
|
|
248
|
-
};
|
|
398
|
+
var extByMime = {};
|
|
399
|
+
for (const ext2 in mimes_default) extByMime[mimes_default[ext2]] = ext2;
|
|
249
400
|
function extFromType(type2) {
|
|
250
401
|
const base = (type2 || "").split(";")[0].trim().toLowerCase();
|
|
251
|
-
|
|
402
|
+
const ext2 = extByMime[base];
|
|
403
|
+
if (ext2) return `.${ext2}`;
|
|
252
404
|
const sub = base.split("/")[1];
|
|
253
405
|
return sub && /^[a-z0-9]+$/.test(sub) ? `.${sub}` : ".bin";
|
|
254
406
|
}
|
|
@@ -303,6 +455,7 @@ function startPart(headerStr, dest) {
|
|
|
303
455
|
controller = c;
|
|
304
456
|
}
|
|
305
457
|
});
|
|
458
|
+
const file2 = dest.file(id);
|
|
306
459
|
return {
|
|
307
460
|
kind: "file",
|
|
308
461
|
name,
|
|
@@ -310,7 +463,8 @@ function startPart(headerStr, dest) {
|
|
|
310
463
|
type: type2,
|
|
311
464
|
id,
|
|
312
465
|
controller,
|
|
313
|
-
|
|
466
|
+
file: file2,
|
|
467
|
+
write: file2.write(readable, { type: type2 }),
|
|
314
468
|
size: 0
|
|
315
469
|
};
|
|
316
470
|
}
|
|
@@ -333,11 +487,11 @@ async function endPart(part, body) {
|
|
|
333
487
|
addField(body, part.name, ref);
|
|
334
488
|
} else if (part.kind === "file") {
|
|
335
489
|
part.controller.close();
|
|
336
|
-
|
|
490
|
+
await part.write;
|
|
337
491
|
addField(body, part.name, {
|
|
338
492
|
name: part.filename,
|
|
339
493
|
id: part.id,
|
|
340
|
-
path:
|
|
494
|
+
path: part.file.path,
|
|
341
495
|
type: part.type,
|
|
342
496
|
size: part.size
|
|
343
497
|
});
|
|
@@ -401,8 +555,9 @@ async function parseMultipart(stream, boundary, dest) {
|
|
|
401
555
|
if (part) await endPart(part, body);
|
|
402
556
|
return body;
|
|
403
557
|
}
|
|
404
|
-
async function streamToBucket(stream, type2,
|
|
558
|
+
async function streamToBucket(stream, type2, bucket2) {
|
|
405
559
|
const id = `${createId()}${extFromType(type2)}`;
|
|
560
|
+
const file2 = bucket2.file(id);
|
|
406
561
|
let size = 0;
|
|
407
562
|
let controller;
|
|
408
563
|
const readable = new ReadableStream({
|
|
@@ -410,15 +565,15 @@ async function streamToBucket(stream, type2, bucket) {
|
|
|
410
565
|
controller = c;
|
|
411
566
|
}
|
|
412
567
|
});
|
|
413
|
-
const write =
|
|
568
|
+
const write = file2.write(readable, { type: type2 });
|
|
414
569
|
for await (const chunk of asIterable(stream)) {
|
|
415
570
|
controller.enqueue(chunk);
|
|
416
571
|
size += chunk.byteLength;
|
|
417
572
|
}
|
|
418
573
|
controller.close();
|
|
419
|
-
|
|
574
|
+
await write;
|
|
420
575
|
if (!size) return void 0;
|
|
421
|
-
return { name: id, id, path:
|
|
576
|
+
return { name: id, id, path: file2.path, type: type2, size };
|
|
422
577
|
}
|
|
423
578
|
async function parseBody(input, contentType, dest) {
|
|
424
579
|
const type2 = Array.isArray(contentType) ? contentType[0] : contentType;
|
|
@@ -524,13 +679,13 @@ var Reply = class {
|
|
|
524
679
|
}
|
|
525
680
|
type(type2) {
|
|
526
681
|
if (!type2) return this;
|
|
527
|
-
type2 =
|
|
682
|
+
type2 = mimes_default[type2.replace(/^\./, "")] || type2;
|
|
528
683
|
this.res.headers.set("content-type", type2);
|
|
529
684
|
return this;
|
|
530
685
|
}
|
|
531
686
|
download(name) {
|
|
532
|
-
const
|
|
533
|
-
if (
|
|
687
|
+
const ext2 = name?.split(".").pop();
|
|
688
|
+
if (ext2 && !this.res.headers.get("content-type")) this.type(ext2);
|
|
534
689
|
const filename = name ? `; filename="${encodeURIComponent(name)}"` : "";
|
|
535
690
|
return this.headers("content-disposition", `attachment${filename}`);
|
|
536
691
|
}
|
|
@@ -570,9 +725,9 @@ var Reply = class {
|
|
|
570
725
|
async file(path2) {
|
|
571
726
|
try {
|
|
572
727
|
const fs2 = await import("fs");
|
|
573
|
-
const
|
|
728
|
+
const ext2 = path2.split(".").pop();
|
|
574
729
|
const stream = fs2.createReadStream(path2);
|
|
575
|
-
return this.type(
|
|
730
|
+
return this.type(ext2).send(stream);
|
|
576
731
|
} catch (error) {
|
|
577
732
|
if (error.code === "ENOENT") {
|
|
578
733
|
return this.status(404).send();
|
|
@@ -582,6 +737,9 @@ var Reply = class {
|
|
|
582
737
|
}
|
|
583
738
|
send(body = "") {
|
|
584
739
|
const { status: status2 = 200, headers: headers2 } = this.res;
|
|
740
|
+
if (status2 === 101 || status2 === 204 || status2 === 205 || status2 === 304) {
|
|
741
|
+
return new Response(null, { status: status2, headers: headers2 });
|
|
742
|
+
}
|
|
585
743
|
if (typeof body === "string") {
|
|
586
744
|
if (!headers2.get("content-type")) {
|
|
587
745
|
const isHtml = body.trim().startsWith("<");
|
|
@@ -1154,101 +1312,6 @@ function parseAuthOptions(auth2, all) {
|
|
|
1154
1312
|
};
|
|
1155
1313
|
}
|
|
1156
1314
|
|
|
1157
|
-
// src/helpers/bucket.ts
|
|
1158
|
-
import * as fs from "fs";
|
|
1159
|
-
import * as fsp from "fs/promises";
|
|
1160
|
-
import * as path from "path";
|
|
1161
|
-
function thinLocalBucket(root) {
|
|
1162
|
-
const absolute = (name) => {
|
|
1163
|
-
if (!name) throw new Error("File name is required");
|
|
1164
|
-
return path.resolve(path.join(root, name));
|
|
1165
|
-
};
|
|
1166
|
-
return {
|
|
1167
|
-
location: path.resolve(root),
|
|
1168
|
-
read: async (name) => {
|
|
1169
|
-
const fullPath = absolute(name);
|
|
1170
|
-
const stats = await fsp.stat(fullPath).catch(() => null);
|
|
1171
|
-
if (!stats?.isFile()) return null;
|
|
1172
|
-
const nodeStream = fs.createReadStream(fullPath);
|
|
1173
|
-
return new ReadableStream({
|
|
1174
|
-
start(controller) {
|
|
1175
|
-
nodeStream.on("data", (chunk) => controller.enqueue(chunk));
|
|
1176
|
-
nodeStream.on("end", () => controller.close());
|
|
1177
|
-
nodeStream.on("error", (err) => controller.error(err));
|
|
1178
|
-
},
|
|
1179
|
-
cancel() {
|
|
1180
|
-
nodeStream.destroy();
|
|
1181
|
-
}
|
|
1182
|
-
});
|
|
1183
|
-
},
|
|
1184
|
-
write: async (name, value, type2) => {
|
|
1185
|
-
const fullPath = absolute(name);
|
|
1186
|
-
if (!value) return fs.createWriteStream(fullPath);
|
|
1187
|
-
await fsp.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1188
|
-
if (value instanceof ReadableStream) {
|
|
1189
|
-
const writable = fs.createWriteStream(fullPath);
|
|
1190
|
-
for await (const chunk of value) {
|
|
1191
|
-
writable.write(chunk);
|
|
1192
|
-
}
|
|
1193
|
-
await new Promise((resolve2, reject) => {
|
|
1194
|
-
writable.on("error", reject);
|
|
1195
|
-
writable.end(resolve2);
|
|
1196
|
-
});
|
|
1197
|
-
return fullPath;
|
|
1198
|
-
}
|
|
1199
|
-
await fsp.writeFile(fullPath, value, type2);
|
|
1200
|
-
return fullPath;
|
|
1201
|
-
},
|
|
1202
|
-
delete: async (name) => {
|
|
1203
|
-
const fullPath = absolute(name);
|
|
1204
|
-
try {
|
|
1205
|
-
await fsp.unlink(fullPath);
|
|
1206
|
-
return true;
|
|
1207
|
-
} catch {
|
|
1208
|
-
return false;
|
|
1209
|
-
}
|
|
1210
|
-
},
|
|
1211
|
-
folder: (prefix) => thinLocalBucket(path.join(root, prefix))
|
|
1212
|
-
};
|
|
1213
|
-
}
|
|
1214
|
-
function thinBunBucket(s3, prefix = "") {
|
|
1215
|
-
const key = (name) => prefix ? `${prefix}/${name}` : name;
|
|
1216
|
-
return {
|
|
1217
|
-
read: async (name) => {
|
|
1218
|
-
const file2 = s3.file(key(name));
|
|
1219
|
-
if (!await file2.exists()) return null;
|
|
1220
|
-
return await file2.stream();
|
|
1221
|
-
},
|
|
1222
|
-
write: async (name, value) => {
|
|
1223
|
-
const file2 = s3.file(key(name));
|
|
1224
|
-
if (value) {
|
|
1225
|
-
await file2.write(value);
|
|
1226
|
-
return key(name);
|
|
1227
|
-
}
|
|
1228
|
-
return s3.presign(key(name), {
|
|
1229
|
-
expiresIn: 3600,
|
|
1230
|
-
acl: "public-read-write"
|
|
1231
|
-
});
|
|
1232
|
-
},
|
|
1233
|
-
delete: async (name) => {
|
|
1234
|
-
const file2 = s3.file(key(name));
|
|
1235
|
-
if (!await file2.exists()) return null;
|
|
1236
|
-
return await file2.delete();
|
|
1237
|
-
},
|
|
1238
|
-
folder: (sub) => thinBunBucket(s3, key(sub))
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
function bucket_default(root) {
|
|
1242
|
-
if (!root) return null;
|
|
1243
|
-
if (typeof root === "string") {
|
|
1244
|
-
return thinLocalBucket(root);
|
|
1245
|
-
}
|
|
1246
|
-
if (root.file && root.write) {
|
|
1247
|
-
return thinBunBucket(root);
|
|
1248
|
-
}
|
|
1249
|
-
return root;
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
1315
|
// src/helpers/color.ts
|
|
1253
1316
|
var map = {
|
|
1254
1317
|
reset: 0,
|
|
@@ -1452,8 +1515,8 @@ function config(options = {}) {
|
|
|
1452
1515
|
}
|
|
1453
1516
|
settings.cors = cors2;
|
|
1454
1517
|
}
|
|
1455
|
-
settings.public = options.public ?
|
|
1456
|
-
settings.uploads = options.uploads instanceof UploadPipeline ? options.uploads : options.uploads ?
|
|
1518
|
+
settings.public = options.public ? bucket(options.public) : null;
|
|
1519
|
+
settings.uploads = options.uploads instanceof UploadPipeline ? options.uploads : options.uploads ? bucket(options.uploads) : null;
|
|
1457
1520
|
if (options.favicon) settings.favicon = options.favicon;
|
|
1458
1521
|
settings.store = options.store ?? null;
|
|
1459
1522
|
settings.cookies = options.cookies ?? null;
|
|
@@ -1527,6 +1590,16 @@ function applyCors(res, ctx) {
|
|
|
1527
1590
|
}
|
|
1528
1591
|
}
|
|
1529
1592
|
|
|
1593
|
+
// src/helpers/etag.ts
|
|
1594
|
+
function etag(bytes) {
|
|
1595
|
+
let h = 2166136261;
|
|
1596
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1597
|
+
h ^= bytes[i];
|
|
1598
|
+
h = Math.imul(h, 16777619);
|
|
1599
|
+
}
|
|
1600
|
+
return `"${bytes.length.toString(16)}-${(h >>> 0).toString(16)}"`;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1530
1603
|
// src/helpers/createWebsocket.ts
|
|
1531
1604
|
function createWebsocket(sockets, handlers) {
|
|
1532
1605
|
const run = (event, socket, body) => {
|
|
@@ -1916,89 +1989,6 @@ function toWeb(nodeStream) {
|
|
|
1916
1989
|
});
|
|
1917
1990
|
}
|
|
1918
1991
|
|
|
1919
|
-
// src/helpers/types.ts
|
|
1920
|
-
var types = {
|
|
1921
|
-
aac: "audio/aac",
|
|
1922
|
-
abw: "application/x-abiword",
|
|
1923
|
-
arc: "application/x-freearc",
|
|
1924
|
-
avif: "image/avif",
|
|
1925
|
-
avi: "video/x-msvideo",
|
|
1926
|
-
azw: "application/vnd.amazon.ebook",
|
|
1927
|
-
bin: "application/octet-stream",
|
|
1928
|
-
bmp: "image/bmp",
|
|
1929
|
-
bz: "application/x-bzip",
|
|
1930
|
-
bz2: "application/x-bzip2",
|
|
1931
|
-
cda: "application/x-cdf",
|
|
1932
|
-
csh: "application/x-csh",
|
|
1933
|
-
css: "text/css",
|
|
1934
|
-
csv: "text/csv",
|
|
1935
|
-
doc: "application/msword",
|
|
1936
|
-
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1937
|
-
eot: "application/vnd.ms-fontobject",
|
|
1938
|
-
epub: "application/epub+zip",
|
|
1939
|
-
gz: "application/gzip",
|
|
1940
|
-
gif: "image/gif",
|
|
1941
|
-
htm: "text/html",
|
|
1942
|
-
html: "text/html",
|
|
1943
|
-
ico: "image/vnd.microsoft.icon",
|
|
1944
|
-
ics: "text/calendar",
|
|
1945
|
-
jar: "application/java-archive",
|
|
1946
|
-
jpeg: "image/jpeg",
|
|
1947
|
-
jpg: "image/jpeg",
|
|
1948
|
-
js: "text/javascript",
|
|
1949
|
-
json: "application/json",
|
|
1950
|
-
jsonld: "application/ld+json",
|
|
1951
|
-
md: "text/markdown",
|
|
1952
|
-
mid: "audio/midi",
|
|
1953
|
-
midi: "audio/midi",
|
|
1954
|
-
mjs: "text/javascript",
|
|
1955
|
-
mp3: "audio/mpeg",
|
|
1956
|
-
mp4: "video/mp4",
|
|
1957
|
-
mpeg: "video/mpeg",
|
|
1958
|
-
mpkg: "application/vnd.apple.installer+xml",
|
|
1959
|
-
odp: "application/vnd.oasis.opendocument.presentation",
|
|
1960
|
-
ods: "application/vnd.oasis.opendocument.spreadsheet",
|
|
1961
|
-
odt: "application/vnd.oasis.opendocument.text",
|
|
1962
|
-
oga: "audio/ogg",
|
|
1963
|
-
ogv: "video/ogg",
|
|
1964
|
-
ogx: "application/ogg",
|
|
1965
|
-
opus: "audio/opus",
|
|
1966
|
-
otf: "font/otf",
|
|
1967
|
-
png: "image/png",
|
|
1968
|
-
pdf: "application/pdf",
|
|
1969
|
-
php: "application/x-httpd-php",
|
|
1970
|
-
ppt: "application/vnd.ms-powerpoint",
|
|
1971
|
-
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
1972
|
-
rar: "application/vnd.rar",
|
|
1973
|
-
rtf: "application/rtf",
|
|
1974
|
-
sh: "application/x-sh",
|
|
1975
|
-
svg: "image/svg+xml",
|
|
1976
|
-
tar: "application/x-tar",
|
|
1977
|
-
text: "text/plain",
|
|
1978
|
-
tif: "image/tiff",
|
|
1979
|
-
tiff: "image/tiff",
|
|
1980
|
-
ts: "video/mp2t",
|
|
1981
|
-
ttf: "font/ttf",
|
|
1982
|
-
txt: "text/plain",
|
|
1983
|
-
vsd: "application/vnd.visio",
|
|
1984
|
-
wav: "audio/wav",
|
|
1985
|
-
weba: "audio/webm",
|
|
1986
|
-
webm: "video/webm",
|
|
1987
|
-
webp: "image/webp",
|
|
1988
|
-
woff: "font/woff",
|
|
1989
|
-
woff2: "font/woff2",
|
|
1990
|
-
xhtml: "application/xhtml+xml",
|
|
1991
|
-
xls: "application/vnd.ms-excel",
|
|
1992
|
-
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1993
|
-
xml: "application/xml",
|
|
1994
|
-
xul: "application/vnd.mozilla.xul+xml",
|
|
1995
|
-
zip: "application/zip",
|
|
1996
|
-
"3gp": "video/3gpp",
|
|
1997
|
-
"3g2": "video/3gpp2",
|
|
1998
|
-
"7z": "application/x-7z-compressed"
|
|
1999
|
-
};
|
|
2000
|
-
var types_default = types;
|
|
2001
|
-
|
|
2002
1992
|
// src/helpers/verify.ts
|
|
2003
1993
|
import * as crypto3 from "crypto";
|
|
2004
1994
|
function timingSafeEqual(a, b) {
|
|
@@ -2168,28 +2158,38 @@ async function assets(ctx) {
|
|
|
2168
2158
|
if (ctx.method !== "get") return;
|
|
2169
2159
|
if (ctx.url.pathname === "/") return;
|
|
2170
2160
|
try {
|
|
2171
|
-
const asset =
|
|
2172
|
-
if (!asset) return;
|
|
2173
|
-
return type(ctx.url.pathname.split(".").pop()).send(asset);
|
|
2161
|
+
const asset = ctx.options.public.file(ctx.url.pathname);
|
|
2162
|
+
if (!await asset.exists()) return;
|
|
2163
|
+
return type(ctx.url.pathname.split(".").pop()).send(asset.stream());
|
|
2174
2164
|
} catch {
|
|
2175
2165
|
}
|
|
2176
2166
|
}
|
|
2177
2167
|
|
|
2178
2168
|
// src/middle/favicon.ts
|
|
2169
|
+
var CACHE_CONTROL = "public, max-age=86400";
|
|
2170
|
+
var ext = (name) => name.split(".").pop() || "ico";
|
|
2171
|
+
async function loadFavicon(fav) {
|
|
2172
|
+
try {
|
|
2173
|
+
const type2 = ext(typeof fav === "string" ? fav : fav?.name);
|
|
2174
|
+
const bytes = typeof fav === "string" ? await (await import("fs/promises")).readFile(fav) : Buffer.from(await fav.bytes());
|
|
2175
|
+
return { bytes, type: type2, etag: etag(bytes) };
|
|
2176
|
+
} catch {
|
|
2177
|
+
return null;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2179
2180
|
async function favicon(ctx) {
|
|
2180
|
-
if (ctx.method !== "get") return;
|
|
2181
|
-
if (ctx.url.pathname !== "/favicon.ico") return;
|
|
2182
2181
|
const fav = ctx.options.favicon;
|
|
2183
|
-
if (fav)
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
return icon ? type("ico").send(icon) : 204;
|
|
2182
|
+
if (!fav) return;
|
|
2183
|
+
if (ctx.app.faviconCache === void 0) {
|
|
2184
|
+
ctx.app.faviconCache = await loadFavicon(fav);
|
|
2187
2185
|
}
|
|
2188
|
-
const
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
if (
|
|
2192
|
-
|
|
2186
|
+
const entry = ctx.app.faviconCache;
|
|
2187
|
+
if (!entry) return 204;
|
|
2188
|
+
const headers2 = { "cache-control": CACHE_CONTROL, etag: entry.etag };
|
|
2189
|
+
if (ctx.headers["if-none-match"] === entry.etag) {
|
|
2190
|
+
return status(304).headers(headers2).send();
|
|
2191
|
+
}
|
|
2192
|
+
return type(entry.type).headers(headers2).send(entry.bytes);
|
|
2193
2193
|
}
|
|
2194
2194
|
|
|
2195
2195
|
// src/middle/openapi.ts
|
|
@@ -2409,6 +2409,168 @@ function timer(ctx) {
|
|
|
2409
2409
|
ctx.time = createTime();
|
|
2410
2410
|
}
|
|
2411
2411
|
|
|
2412
|
+
// src/helpers/wsNode.ts
|
|
2413
|
+
var GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
2414
|
+
var CONTINUATION = 0;
|
|
2415
|
+
var TEXT = 1;
|
|
2416
|
+
var BINARY = 2;
|
|
2417
|
+
var CLOSE = 8;
|
|
2418
|
+
var PING = 9;
|
|
2419
|
+
var PONG = 10;
|
|
2420
|
+
function encodeFrame(payload, opcode) {
|
|
2421
|
+
const len = payload.length;
|
|
2422
|
+
let header;
|
|
2423
|
+
if (len < 126) {
|
|
2424
|
+
header = Buffer.from([128 | opcode, len]);
|
|
2425
|
+
} else if (len < 65536) {
|
|
2426
|
+
header = Buffer.allocUnsafe(4);
|
|
2427
|
+
header[0] = 128 | opcode;
|
|
2428
|
+
header[1] = 126;
|
|
2429
|
+
header.writeUInt16BE(len, 2);
|
|
2430
|
+
} else {
|
|
2431
|
+
header = Buffer.allocUnsafe(10);
|
|
2432
|
+
header[0] = 128 | opcode;
|
|
2433
|
+
header[1] = 127;
|
|
2434
|
+
header.writeBigUInt64BE(BigInt(len), 2);
|
|
2435
|
+
}
|
|
2436
|
+
return Buffer.concat([header, payload]);
|
|
2437
|
+
}
|
|
2438
|
+
var NodeWebSocket = class {
|
|
2439
|
+
socket;
|
|
2440
|
+
handlers;
|
|
2441
|
+
buffer;
|
|
2442
|
+
fragments;
|
|
2443
|
+
fragmentOpcode;
|
|
2444
|
+
closed;
|
|
2445
|
+
readyState;
|
|
2446
|
+
constructor(socket, handlers) {
|
|
2447
|
+
this.socket = socket;
|
|
2448
|
+
this.handlers = handlers;
|
|
2449
|
+
this.buffer = Buffer.alloc(0);
|
|
2450
|
+
this.fragments = [];
|
|
2451
|
+
this.fragmentOpcode = TEXT;
|
|
2452
|
+
this.closed = false;
|
|
2453
|
+
this.readyState = 1;
|
|
2454
|
+
}
|
|
2455
|
+
send(data) {
|
|
2456
|
+
if (this.closed) return;
|
|
2457
|
+
const isString = typeof data === "string";
|
|
2458
|
+
const payload = isString ? Buffer.from(data) : Buffer.from(data);
|
|
2459
|
+
this.socket.write(encodeFrame(payload, isString ? TEXT : BINARY));
|
|
2460
|
+
}
|
|
2461
|
+
close(code = 1e3, reason = "") {
|
|
2462
|
+
if (this.closed) return;
|
|
2463
|
+
const payload = Buffer.alloc(2 + Buffer.byteLength(reason));
|
|
2464
|
+
payload.writeUInt16BE(code, 0);
|
|
2465
|
+
payload.write(reason, 2);
|
|
2466
|
+
try {
|
|
2467
|
+
this.socket.write(encodeFrame(payload, CLOSE));
|
|
2468
|
+
} catch {
|
|
2469
|
+
}
|
|
2470
|
+
this.shutdown();
|
|
2471
|
+
}
|
|
2472
|
+
// Called once, whether the peer closed, the socket died, or we closed.
|
|
2473
|
+
shutdown() {
|
|
2474
|
+
if (this.closed) return;
|
|
2475
|
+
this.closed = true;
|
|
2476
|
+
this.readyState = 3;
|
|
2477
|
+
try {
|
|
2478
|
+
this.socket.end();
|
|
2479
|
+
} catch {
|
|
2480
|
+
}
|
|
2481
|
+
this.handlers.onClose();
|
|
2482
|
+
}
|
|
2483
|
+
// Feed raw bytes from the TCP socket; parses as many complete frames as it can
|
|
2484
|
+
// and buffers the remainder for the next chunk.
|
|
2485
|
+
receive(chunk) {
|
|
2486
|
+
this.buffer = this.buffer.length ? Buffer.concat([this.buffer, chunk]) : chunk;
|
|
2487
|
+
while (true) {
|
|
2488
|
+
const buf = this.buffer;
|
|
2489
|
+
if (buf.length < 2) return;
|
|
2490
|
+
const fin = (buf[0] & 128) !== 0;
|
|
2491
|
+
const opcode = buf[0] & 15;
|
|
2492
|
+
const masked = (buf[1] & 128) !== 0;
|
|
2493
|
+
let len = buf[1] & 127;
|
|
2494
|
+
let offset = 2;
|
|
2495
|
+
if (len === 126) {
|
|
2496
|
+
if (buf.length < 4) return;
|
|
2497
|
+
len = buf.readUInt16BE(2);
|
|
2498
|
+
offset = 4;
|
|
2499
|
+
} else if (len === 127) {
|
|
2500
|
+
if (buf.length < 10) return;
|
|
2501
|
+
len = Number(buf.readBigUInt64BE(2));
|
|
2502
|
+
offset = 10;
|
|
2503
|
+
}
|
|
2504
|
+
let mask = null;
|
|
2505
|
+
if (masked) {
|
|
2506
|
+
if (buf.length < offset + 4) return;
|
|
2507
|
+
mask = buf.subarray(offset, offset + 4);
|
|
2508
|
+
offset += 4;
|
|
2509
|
+
}
|
|
2510
|
+
if (buf.length < offset + len) return;
|
|
2511
|
+
const payload = Buffer.from(buf.subarray(offset, offset + len));
|
|
2512
|
+
if (mask) {
|
|
2513
|
+
for (let i = 0; i < len; i++) payload[i] ^= mask[i & 3];
|
|
2514
|
+
}
|
|
2515
|
+
this.buffer = buf.subarray(offset + len);
|
|
2516
|
+
this.frame(fin, opcode, payload);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
frame(fin, opcode, payload) {
|
|
2520
|
+
if (opcode === CLOSE) {
|
|
2521
|
+
this.shutdown();
|
|
2522
|
+
return;
|
|
2523
|
+
}
|
|
2524
|
+
if (opcode === PING) {
|
|
2525
|
+
if (!this.closed) this.socket.write(encodeFrame(payload, PONG));
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
if (opcode === PONG) return;
|
|
2529
|
+
if (opcode === CONTINUATION) {
|
|
2530
|
+
this.fragments.push(payload);
|
|
2531
|
+
} else {
|
|
2532
|
+
this.fragments = [payload];
|
|
2533
|
+
this.fragmentOpcode = opcode;
|
|
2534
|
+
}
|
|
2535
|
+
if (!fin) return;
|
|
2536
|
+
const full = this.fragments.length === 1 ? this.fragments[0] : Buffer.concat(this.fragments);
|
|
2537
|
+
this.fragments = [];
|
|
2538
|
+
const body = this.fragmentOpcode === TEXT ? full.toString("utf8") : full;
|
|
2539
|
+
this.handlers.onMessage(body);
|
|
2540
|
+
}
|
|
2541
|
+
};
|
|
2542
|
+
async function attachWebsocket(server2, app) {
|
|
2543
|
+
const { createHash } = await import("crypto");
|
|
2544
|
+
server2.on("upgrade", (req, socket, head) => {
|
|
2545
|
+
const key = req.headers["sec-websocket-key"];
|
|
2546
|
+
const upgrade = String(req.headers.upgrade || "").toLowerCase();
|
|
2547
|
+
if (upgrade !== "websocket" || !key || !app.handlers.socket.length) {
|
|
2548
|
+
socket.destroy();
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
const accept = createHash("sha1").update(key + GUID).digest("base64");
|
|
2552
|
+
socket.write(
|
|
2553
|
+
`HTTP/1.1 101 Switching Protocols\r
|
|
2554
|
+
Upgrade: websocket\r
|
|
2555
|
+
Connection: Upgrade\r
|
|
2556
|
+
Sec-WebSocket-Accept: ${accept}\r
|
|
2557
|
+
\r
|
|
2558
|
+
`
|
|
2559
|
+
);
|
|
2560
|
+
socket.setTimeout(0);
|
|
2561
|
+
socket.setNoDelay(true);
|
|
2562
|
+
const ws = new NodeWebSocket(socket, {
|
|
2563
|
+
onMessage: (body) => app.websocket.message(ws, body),
|
|
2564
|
+
onClose: () => app.websocket.close(ws)
|
|
2565
|
+
});
|
|
2566
|
+
app.websocket.open(ws);
|
|
2567
|
+
if (head?.length) ws.receive(head);
|
|
2568
|
+
socket.on("data", (chunk) => ws.receive(chunk));
|
|
2569
|
+
socket.on("close", () => ws.shutdown());
|
|
2570
|
+
socket.on("error", () => ws.shutdown());
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2412
2574
|
// src/context/node.ts
|
|
2413
2575
|
import { TLSSocket } from "tls";
|
|
2414
2576
|
|
|
@@ -2525,25 +2687,25 @@ var Winter = async (app, request, env2) => {
|
|
|
2525
2687
|
};
|
|
2526
2688
|
var Node = async (app) => {
|
|
2527
2689
|
const http = await import("http");
|
|
2528
|
-
http.createServer(
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2690
|
+
const server2 = http.createServer(
|
|
2691
|
+
async (request, response) => {
|
|
2692
|
+
const ctx = await createNode(request, app);
|
|
2693
|
+
if ("error" in ctx) throw ctx.error;
|
|
2694
|
+
const out = await handleRequest(app, ctx);
|
|
2695
|
+
response.writeHead(out.status || 200, parseHeaders_default(out.headers));
|
|
2696
|
+
if (out.body instanceof ReadableStream) {
|
|
2697
|
+
await iterate(out.body, (chunk) => response.write(chunk));
|
|
2698
|
+
} else {
|
|
2699
|
+
response.write(out.body || "");
|
|
2700
|
+
}
|
|
2701
|
+
response.end();
|
|
2537
2702
|
}
|
|
2538
|
-
|
|
2539
|
-
|
|
2703
|
+
);
|
|
2704
|
+
await attachWebsocket(server2, app);
|
|
2705
|
+
server2.listen(app.settings.port, () => {
|
|
2540
2706
|
app.settings.log.start(`http://localhost:${app.settings.port}/`);
|
|
2541
|
-
if (app.handlers.socket.length) {
|
|
2542
|
-
console.warn(
|
|
2543
|
-
"[server] WebSockets (.socket()) are only supported on Bun, not Node"
|
|
2544
|
-
);
|
|
2545
|
-
}
|
|
2546
2707
|
});
|
|
2708
|
+
return server2;
|
|
2547
2709
|
};
|
|
2548
2710
|
var Netlify = async (app, request, context) => {
|
|
2549
2711
|
request.context = context;
|
|
@@ -2686,6 +2848,9 @@ var Server = class extends Router {
|
|
|
2686
2848
|
platform;
|
|
2687
2849
|
sockets;
|
|
2688
2850
|
websocket;
|
|
2851
|
+
// Lazily-loaded favicon bytes, cached per server until restart (see favicon
|
|
2852
|
+
// middleware). `undefined` = not loaded yet; `null` = configured but missing.
|
|
2853
|
+
faviconCache;
|
|
2689
2854
|
port;
|
|
2690
2855
|
constructor(options = {}) {
|
|
2691
2856
|
super();
|
|
@@ -2704,7 +2869,7 @@ var Server = class extends Router {
|
|
|
2704
2869
|
this.use(timer);
|
|
2705
2870
|
if (this.settings.cors) this.use(preflight);
|
|
2706
2871
|
this.use(assets);
|
|
2707
|
-
this.
|
|
2872
|
+
if (this.settings.favicon) this.get("/favicon.ico", favicon);
|
|
2708
2873
|
this.use(session);
|
|
2709
2874
|
if (this.settings.auth) {
|
|
2710
2875
|
auth(this);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@server/next",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.2",
|
|
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",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"@types/bun": "^1.3.0",
|
|
52
52
|
"@types/jest": "^30.0.0",
|
|
53
53
|
"@types/node": "^24.10.0",
|
|
54
|
+
"bucket": "^0.2.0",
|
|
54
55
|
"bun": "^1.3.13",
|
|
55
56
|
"check-dts": "^0.8.2",
|
|
56
57
|
"jest": "^29.7.0",
|