@server/next 0.27.0 → 0.27.3

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.js CHANGED
@@ -1,15 +1,3 @@
1
- // src/polyfill.ts
2
- globalThis.env = {};
3
- if (typeof globalThis.Netlify !== "undefined") {
4
- Object.assign(
5
- globalThis.env,
6
- globalThis.Netlify.env.toObject()
7
- );
8
- }
9
- if (typeof process !== "undefined") {
10
- Object.assign(globalThis.env, process.env);
11
- }
12
-
13
1
  // src/ServerError.ts
14
2
  var ServerError = class _ServerError extends Error {
15
3
  code;
@@ -34,7 +22,6 @@ var ServerError = class _ServerError extends Error {
34
22
  this.message = messageStr;
35
23
  this.status = status2;
36
24
  }
37
- // Add error codes dynamically to the global object
38
25
  static extend(errors) {
39
26
  for (const code in errors) {
40
27
  const error = errors[code];
@@ -46,31 +33,9 @@ var ServerError = class _ServerError extends Error {
46
33
  }
47
34
  return errors;
48
35
  }
49
- // Dynamically added error methods from errors/index.ts
50
- static NO_STORE;
51
- static NO_STORE_WRITE;
52
- static NO_STORE_READ;
53
- static AUTH_ARGON_NEEDED;
54
- static AUTH_INVALID_TYPE;
55
- static AUTH_INVALID_TOKEN;
56
- static AUTH_INVALID_COOKIE;
57
- static AUTH_NO_PROVIDER;
58
- static AUTH_INVALID_PROVIDER;
59
- static AUTH_NO_SESSION;
60
- static AUTH_NO_USER;
61
- static LOGIN_NO_EMAIL;
62
- static LOGIN_INVALID_EMAIL;
63
- static LOGIN_NO_PASSWORD;
64
- static LOGIN_INVALID_PASSWORD;
65
- static LOGIN_WRONG_EMAIL;
66
- static LOGIN_WRONG_PASSWORD;
67
- static REGISTER_NO_EMAIL;
68
- static REGISTER_INVALID_EMAIL;
69
- static REGISTER_NO_PASSWORD;
70
- static REGISTER_INVALID_PASSWORD;
71
- static REGISTER_EMAIL_EXISTS;
72
36
  };
73
- var ServerError_default = ServerError;
37
+ var TypedServerError = ServerError;
38
+ var ServerError_default = TypedServerError;
74
39
 
75
40
  // src/errors/index.ts
76
41
  ServerError_default.extend({
@@ -78,9 +43,10 @@ ServerError_default.extend({
78
43
  NO_STORE_WRITE: "You need a 'store' to write 'ctx.session.{key}'",
79
44
  NO_STORE_READ: "You need a 'store' to read 'ctx.session.{key}'",
80
45
  AUTH_ARGON_NEEDED: "Argon2 is needed for the auth module, please install it with 'npm i argon2'",
81
- AUTH_INVALID_TYPE: "Invalid Authorization type, '{type}'",
82
46
  AUTH_INVALID_TOKEN: "Invalid Authorization token",
83
47
  AUTH_INVALID_COOKIE: "Invalid Authorization cookie",
48
+ AUTH_INVALID_HEADER: "Invalid authorization header {type}, must send 'Bearer {TOKEN}' (with space)",
49
+ AUTH_INVALID_STRATEGY: "Invalid Authorization type '{strategy}', valid one is '{valid}'",
84
50
  AUTH_NO_PROVIDER: "No provider passed to the option 'auth.provider'",
85
51
  AUTH_INVALID_PROVIDER: "Invalid provider '{provider}', valid ones are: '{valid}'",
86
52
  AUTH_NO_SESSION: { status: 401, message: "Invalid session" },
@@ -101,143 +67,119 @@ ServerError_default.extend({
101
67
  REGISTER_EMAIL_EXISTS: "Email is already registered"
102
68
  });
103
69
 
104
- // src/helpers/createCookies.ts
105
- function createCookies(cookies2) {
106
- if (!cookies2 || !Object.keys(cookies2).length) return [];
107
- return Object.entries(cookies2).map(([key, val]) => {
108
- if (!val) {
109
- val = { value: "", expires: (/* @__PURE__ */ new Date(0)).toUTCString() };
110
- }
111
- if (typeof val === "string") {
112
- val = { value: val };
113
- }
114
- const { value, path: path2, expires } = val;
115
- const pathPart = `;Path=${path2 || "/"}`;
116
- const expiresPart = expires ? `;Expires=${expires}` : "";
117
- return `${key}=${value || ""}${pathPart}${expiresPart}`;
118
- });
70
+ // src/polyfill.ts
71
+ globalThis.env = {};
72
+ if (typeof globalThis.Netlify !== "undefined") {
73
+ Object.assign(
74
+ globalThis.env,
75
+ globalThis.Netlify.env.toObject()
76
+ );
77
+ }
78
+ if (typeof process !== "undefined") {
79
+ Object.assign(globalThis.env, process.env);
119
80
  }
120
81
 
121
- // src/helpers/createId.ts
122
- var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
123
- var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
124
- var cyrb53 = (str, seed = 0) => {
125
- if (typeof str !== "string") str = String(str);
126
- let h1 = 3735928559 ^ seed;
127
- let h2 = 1103547991 ^ seed;
128
- for (let i = 0, ch; i < str.length; i++) {
129
- ch = str.charCodeAt(i);
130
- h1 = Math.imul(h1 ^ ch, 2654435761);
131
- h2 = Math.imul(h2 ^ ch, 1597334677);
132
- }
133
- h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
134
- h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
135
- h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
136
- h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
137
- return 4294967296 * (2097151 & h2) + (h1 >>> 0);
138
- };
139
- var hash = (str, size) => {
140
- let chars = "";
141
- let num = cyrb53(str);
142
- for (let i = 0; i < size; i++) {
143
- if (num < alphabet.length) num = cyrb53(str, i);
144
- chars += alphabet[num % alphabet.length];
145
- num = Math.floor(num / alphabet.length);
146
- }
147
- return chars;
148
- };
149
- var randomId = (size = 16) => {
150
- let id = "";
151
- const bytes = random(size);
152
- while (size--) {
153
- id += alphabet[bytes[size] & 61];
82
+ // src/auth/updateUser.ts
83
+ async function updateUser(user, auth2, store) {
84
+ if (auth2.provider === "email") {
85
+ return await store.set(auth2.email, user);
154
86
  }
155
- return id;
156
- };
157
- function createId(source, size = 16) {
158
- if (source) return hash(source, size);
159
- return randomId(size);
160
87
  }
161
88
 
162
- // src/helpers/createWebsocket.ts
163
- function createWebsocket(sockets, handlers) {
164
- return {
165
- message: async (socket, body) => {
166
- handlers.socket?.filter((s) => s[1] === "message")?.map((s) => s[2]({ socket, sockets, body }));
167
- },
168
- open: (socket) => {
169
- sockets.push(socket);
170
- handlers.socket?.filter((s) => s[1] === "open")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
171
- },
172
- close: (socket) => {
173
- sockets.splice(sockets.indexOf(socket), 1);
174
- handlers.socket?.filter((s) => s[1] === "close")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
175
- }
89
+ // src/auth/providers/email.ts
90
+ var createSession = async (user, ctx) => {
91
+ const { strategy, session: session2, cleanUser, redirect: redirect2 = "/user" } = ctx.options.auth;
92
+ user = await cleanUser(user);
93
+ const id = createId();
94
+ const provider = "email";
95
+ ctx.user = {
96
+ id,
97
+ strategy,
98
+ provider,
99
+ email: user.email
176
100
  };
177
- }
178
-
179
- // src/auth/auth.ts
180
- var validateToken = (authorization) => {
181
- const [type2, id] = authorization.trim().split(" ");
182
- if (type2.toLowerCase() !== "bearer") {
183
- throw ServerError_default.AUTH_INVALID_TYPE({ type: type2 });
184
- }
185
- if (id.length !== 16) {
186
- throw ServerError_default.AUTH_INVALID_TOKEN();
101
+ await session2.set(
102
+ id,
103
+ { id, strategy, provider, user: user.email },
104
+ { expires: "1w" }
105
+ );
106
+ if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
107
+ if (strategy.includes("token")) {
108
+ return status(201).json({ ...user, token: id });
187
109
  }
188
- return id;
189
- };
190
- var validateCookie = (authorization) => {
191
- if (authorization.length !== 16) {
192
- throw ServerError_default.AUTH_INVALID_COOKIE();
110
+ if (strategy.includes("cookie")) {
111
+ return status(302).cookies({ authentication: id }).redirect(redirect2);
193
112
  }
194
- return authorization;
195
- };
196
- var findSessionId = (ctx) => {
197
- const type2 = ctx.options.auth.type;
198
- if (type2.includes("token")) {
199
- if (!ctx.headers.authorization) return;
200
- return validateToken(ctx.headers.authorization);
113
+ if (strategy.includes("jwt")) {
114
+ throw new Error("JWT auth not supported yet");
201
115
  }
202
- if (type2.includes("cookie")) {
203
- if (!ctx.cookies.authentication) return;
204
- return validateCookie(ctx.cookies.authentication);
116
+ if (strategy.includes("key")) {
117
+ throw new Error("Key auth not supported yet");
205
118
  }
206
- throw new Error(`Invalid auth type "${type2}"`);
119
+ throw new Error("Unknown auth type");
207
120
  };
208
- async function auth(ctx) {
209
- if (!ctx.options.auth) return;
210
- const options = ctx.options.auth;
211
- const sessionId = findSessionId(ctx);
212
- if (!sessionId) return;
213
- const auth3 = await options.session.get(sessionId);
214
- if (!auth3) return;
215
- if (!auth3.provider) throw ServerError_default.AUTH_NO_PROVIDER();
216
- if (!options.provider.includes(auth3.provider)) {
217
- throw ServerError_default.AUTH_INVALID_PROVIDER({
218
- provider: auth3.provider,
219
- valid: options.provider
220
- });
221
- }
222
- return auth3;
121
+ async function emailLogin(ctx) {
122
+ const { email, password } = ctx.body;
123
+ if (!email) throw ServerError_default.LOGIN_NO_EMAIL();
124
+ if (!/@/.test(email)) throw ServerError_default.LOGIN_INVALID_EMAIL();
125
+ if (!password) throw ServerError_default.LOGIN_NO_PASSWORD();
126
+ if (password.length < 8) throw ServerError_default.LOGIN_INVALID_PASSWORD();
127
+ const store = ctx.options.auth.store;
128
+ if (!await store.has(email)) throw ServerError_default.LOGIN_WRONG_EMAIL();
129
+ const user = await store.get(email);
130
+ const isValid = await verify(password, user.password);
131
+ if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
132
+ return createSession(user, ctx);
133
+ }
134
+ async function emailRegister(ctx) {
135
+ const { email, password, ...data } = ctx.body;
136
+ if (!email) throw ServerError_default.REGISTER_NO_EMAIL();
137
+ if (!/@/.test(email)) throw ServerError_default.REGISTER_INVALID_EMAIL();
138
+ if (!password) throw ServerError_default.REGISTER_NO_PASSWORD();
139
+ if (password.length < 8) throw ServerError_default.REGISTER_INVALID_PASSWORD();
140
+ const store = ctx.options.auth.store;
141
+ if (await store.has(email)) throw ServerError_default.REGISTER_EMAIL_EXISTS();
142
+ const time = (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "");
143
+ const user = {
144
+ id: createId(email),
145
+ strategy: ctx.options.auth.strategy,
146
+ provider: "email",
147
+ email,
148
+ password: await hash(password),
149
+ time,
150
+ ...data
151
+ };
152
+ await store.set(email, user);
153
+ return createSession(user, ctx);
154
+ }
155
+ async function emailResetPassword() {
156
+ }
157
+ async function emailUpdatePassword(ctx) {
158
+ const passwords = ctx.body;
159
+ const fullUser = await ctx.options.auth.store.get(ctx.user.email);
160
+ if (!fullUser) throw ServerError_default.AUTH_NO_USER();
161
+ const isValid = await verify(passwords.previous, fullUser.password);
162
+ if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
163
+ fullUser.password = await hash(passwords.updated);
164
+ await updateUser(fullUser, ctx.user, ctx.options.auth.store);
165
+ return 200;
223
166
  }
167
+ var email_default = {
168
+ login: emailLogin,
169
+ register: emailRegister,
170
+ reset: emailResetPassword,
171
+ password: emailUpdatePassword
172
+ };
224
173
 
225
174
  // src/reply.ts
175
+ var EXPIRED = (/* @__PURE__ */ new Date(0)).toUTCString();
226
176
  var Reply = class {
227
177
  res;
228
178
  constructor() {
229
179
  this.res = {
230
- headers: {},
231
- cookies: {}
180
+ headers: new Headers()
232
181
  };
233
182
  }
234
- generateHeaders() {
235
- const headers2 = new Headers(this.res.headers);
236
- for (const cookie of createCookies(this.res.cookies)) {
237
- headers2.append("set-cookie", cookie);
238
- }
239
- return headers2;
240
- }
241
183
  status(status2) {
242
184
  this.res.status = status2;
243
185
  return this;
@@ -245,46 +187,55 @@ var Reply = class {
245
187
  type(type2) {
246
188
  if (!type2) return this;
247
189
  type2 = types_default[type2.replace(/^\./, "")] || type2;
248
- return this.headers({ "content-type": type2 });
190
+ this.res.headers.set("content-type", type2);
191
+ return this;
249
192
  }
250
- download(name, type2) {
251
- if (name && !type2) type2 = name.split(".").pop();
252
- if (type2) this.type(type2);
253
- const filename = name ? `; filename="${name}"` : "";
254
- return this.headers({ "content-disposition": `attachment${filename}` });
193
+ download(name) {
194
+ const ext = name?.split(".").pop();
195
+ if (type && ext && !this.res.headers.get("content-type")) this.type(ext);
196
+ const filename = name ? `; filename="${encodeURIComponent(name)}"` : "";
197
+ return this.headers("content-disposition", `attachment${filename}`);
255
198
  }
256
- headers(headers2) {
257
- if (!headers2 || typeof headers2 !== "object") return this;
258
- for (const key in headers2) {
259
- this.res.headers[key] = headers2[key];
199
+ headers(key, value) {
200
+ if (typeof key !== "string") {
201
+ Object.entries(key).map(([key2, value2]) => this.headers(key2, value2));
202
+ return this;
260
203
  }
204
+ if (Array.isArray(value)) {
205
+ Object.values(value).map((val) => this.headers(key, val));
206
+ return this;
207
+ }
208
+ this.res.headers.append(key, value);
261
209
  return this;
262
210
  }
263
- cookies(cookies2) {
264
- if (!cookies2 || typeof cookies2 !== "object") return this;
265
- for (const key in cookies2) {
266
- if (typeof cookies2[key] === "string") {
267
- this.res.cookies[key] = { value: cookies2[key] };
268
- } else {
269
- this.res.cookies[key] = cookies2[key];
270
- }
211
+ cookies(key, value) {
212
+ if (typeof key === "object") {
213
+ Object.entries(key).map(([key2, value2]) => this.cookies(key2, value2));
214
+ return this;
271
215
  }
272
- return this;
216
+ if (Array.isArray(value)) {
217
+ Object.values(value).map((val) => this.cookies(key, val));
218
+ return this;
219
+ }
220
+ console.log(key, value);
221
+ if (value === null) return this.cookies(key, { expires: EXPIRED });
222
+ if (typeof value !== "object") return this.cookies(key, { value });
223
+ return this.headers("set-cookie", createCookies(key, value));
273
224
  }
274
225
  json(body) {
275
- return this.headers({
276
- "content-type": "application/json"
277
- }).send(JSON.stringify(body));
226
+ return this.headers("content-type", "application/json").send(
227
+ JSON.stringify(body)
228
+ );
278
229
  }
279
- redirect(Location) {
280
- return this.headers({ Location }).status(302).send();
230
+ redirect(path2) {
231
+ return this.headers("location", path2).status(302).send();
281
232
  }
282
- async file(path2, renderer = async (data) => data) {
233
+ async file(path2) {
283
234
  try {
284
- const fs2 = await import("fs/promises");
285
- const data = await fs2.readFile(path2);
235
+ const fs2 = await import("fs");
286
236
  const ext = path2.split(".").pop();
287
- return this.type(ext).send(await renderer(data));
237
+ const stream = fs2.createReadStream(path2);
238
+ return this.type(ext).send(stream);
288
239
  } catch (error) {
289
240
  if (error.code === "ENOENT") {
290
241
  return this.status(404).send();
@@ -292,156 +243,39 @@ var Reply = class {
292
243
  throw error;
293
244
  }
294
245
  }
295
- async view(path2, renderer = async (data) => data, ctx) {
296
- if (!ctx?.options.views) {
297
- throw new Error("Views not enabled");
298
- }
299
- const data = await ctx.options.views.read(path2);
300
- if (!data) return this.status(404).send();
301
- return this.type(path2.split(".").pop()).send(await renderer(data));
302
- }
303
246
  send(body = "") {
304
- const { status: status2 = 200 } = this.res;
247
+ const { status: status2 = 200, headers: headers2 } = this.res;
305
248
  if (typeof body === "string") {
306
- if (!this.res.headers["content-type"]) {
307
- const isHtml = body.startsWith("<");
308
- this.res.headers["content-type"] = isHtml ? "text/html" : "text/plain";
249
+ if (!headers2.get("content-type")) {
250
+ const isHtml = body.trim().startsWith("<");
251
+ headers2.set("content-type", isHtml ? "text/html" : "text/plain");
309
252
  }
310
- const headers2 = this.generateHeaders();
311
253
  return new Response(body, { status: status2, headers: headers2 });
312
254
  }
313
255
  const name = body?.constructor?.name;
314
256
  if (name === "Buffer") {
315
- const headers2 = this.generateHeaders();
316
257
  return new Response(body, { status: status2, headers: headers2 });
317
258
  }
318
- if (name === "ReadableStream") {
319
- const headers2 = this.generateHeaders();
259
+ if (typeof body?.getReader === "function") {
320
260
  return new Response(body, { status: status2, headers: headers2 });
321
261
  }
322
262
  if (name === "PassThrough" || name === "Readable") {
323
- const headers2 = this.generateHeaders();
324
263
  return new Response(toWeb(body), { status: status2, headers: headers2 });
325
264
  }
326
- return this.json(body);
327
- }
328
- };
329
- var status = (...args) => new Reply().status(...args);
330
- var headers = (...args) => new Reply().headers(...args);
331
- var type = (...args) => new Reply().type(...args);
332
- var download = (...args) => new Reply().download(...args);
333
- var cookies = (...args) => new Reply().cookies(...args);
334
- var send = (...args) => new Reply().send(...args);
335
- var json = (...args) => new Reply().json(...args);
336
- var file = (...args) => new Reply().file(...args);
337
- var redirect = (...args) => new Reply().redirect(...args);
338
- var view = (...args) => new Reply().view(...args);
339
-
340
- // src/auth/logout.ts
341
- async function logout(ctx) {
342
- const { id, type: type2 } = ctx.auth;
343
- await ctx.options.auth.session.del(id);
344
- if (type2.includes("token")) {
345
- return { token: null };
346
- }
347
- if (type2.includes("cookie")) {
348
- return cookies({ authorization: null }).redirect("/");
349
- }
350
- if (type2.includes("jwt")) {
351
- throw new Error("JWT auth not supported yet");
352
- }
353
- if (type2.includes("key")) {
354
- throw new Error("Key auth not supported yet");
355
- }
356
- throw new Error("Unknown auth type");
357
- }
358
-
359
- // src/auth/updateUser.ts
360
- async function updateUser(user2, auth3, store) {
361
- if (auth3.provider === "email") {
362
- return await store.set(auth3.email, user2);
363
- }
364
- }
365
-
366
- // src/auth/providers/email.ts
367
- var createSession = async (user2, ctx) => {
368
- const { type: type2, session: session2, cleanUser, redirect: redirect2 = "/user" } = ctx.options.auth;
369
- user2 = cleanUser(user2);
370
- const id = createId();
371
- const provider = "email";
372
- const time = (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "");
373
- ctx.auth = {
374
- id,
375
- type: type2,
376
- provider,
377
- user: user2.email,
378
- email: user2.email,
379
- time
380
- };
381
- await session2.set(id, ctx.auth, { expires: "1w" });
382
- if (type2.includes("token")) {
383
- return status(201).json({ ...user2, token: id });
384
- }
385
- if (type2.includes("cookie")) {
386
- return status(302).cookies({ authentication: id }).redirect(redirect2);
387
- }
388
- if (type2.includes("jwt")) {
389
- throw new Error("JWT auth not supported yet");
390
- }
391
- if (type2.includes("key")) {
392
- throw new Error("Key auth not supported yet");
265
+ headers2.set("content-type", "application/json");
266
+ return new Response(JSON.stringify(body), { status: status2, headers: headers2 });
393
267
  }
394
- throw new Error("Unknown auth type");
395
- };
396
- async function emailLogin(ctx) {
397
- const { email, password } = ctx.body;
398
- if (!email) throw ServerError_default.LOGIN_NO_EMAIL();
399
- if (!/@/.test(email)) throw ServerError_default.LOGIN_INVALID_EMAIL();
400
- if (!password) throw ServerError_default.LOGIN_NO_PASSWORD();
401
- if (password.length < 8) throw ServerError_default.LOGIN_INVALID_PASSWORD();
402
- const store = ctx.options.auth.store;
403
- if (!await store.has(email)) throw ServerError_default.LOGIN_WRONG_EMAIL();
404
- const user2 = await store.get(email);
405
- const isValid = await verify(password, user2.password);
406
- if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
407
- return createSession(user2, ctx);
408
- }
409
- async function emailRegister(ctx) {
410
- const { email, password, ...data } = ctx.body;
411
- if (!email) throw ServerError_default.REGISTER_NO_EMAIL();
412
- if (!/@/.test(email)) throw ServerError_default.REGISTER_INVALID_EMAIL();
413
- if (!password) throw ServerError_default.REGISTER_NO_PASSWORD();
414
- if (password.length < 8) throw ServerError_default.REGISTER_INVALID_PASSWORD();
415
- const store = ctx.options.auth.store;
416
- if (await store.has(email)) throw ServerError_default.REGISTER_EMAIL_EXISTS();
417
- const time = (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "");
418
- const user2 = {
419
- id: createId(email),
420
- email,
421
- password: await hash2(password),
422
- time,
423
- ...data
424
- };
425
- await store.set(email, user2);
426
- return createSession(user2, ctx);
427
- }
428
- async function emailResetPassword() {
429
- }
430
- async function emailUpdatePassword(ctx) {
431
- const { previous, updated } = ctx.body;
432
- const fullUser = await ctx.options.auth.store.get(ctx.auth.user);
433
- const isValid = await verify(previous, fullUser.password);
434
- if (!isValid) throw ServerError_default.LOGIN_WRONG_PASSWORD();
435
- fullUser.password = await hash2(updated);
436
- await updateUser(fullUser, ctx.auth, ctx.options.auth.store);
437
- return 200;
438
- }
439
- var email_default = {
440
- login: emailLogin,
441
- register: emailRegister,
442
- reset: emailResetPassword,
443
- password: emailUpdatePassword
444
268
  };
269
+ var r = () => new Reply();
270
+ var status = (...args) => r().status(...args);
271
+ var headers = (...args) => r().headers(...args);
272
+ var type = (...args) => r().type(...args);
273
+ var download = (...args) => r().download(...args);
274
+ var cookies = (...args) => r().cookies(...args);
275
+ var send = (...args) => r().send(...args);
276
+ var json = (...args) => r().json(...args);
277
+ var file = (...args) => r().file(...args);
278
+ var redirect = (...args) => r().redirect(...args);
445
279
 
446
280
  // src/auth/providers/github.ts
447
281
  var oauth = async (code) => {
@@ -481,17 +315,17 @@ var getUserProfile = async (code) => {
481
315
  return { ...profile, email };
482
316
  };
483
317
  var callback = async (ctx) => {
484
- const { type: type2, cleanUser, store, session: session2, redirect: redirect2 } = ctx.options.auth;
318
+ const { strategy, cleanUser, store, session: session2, redirect: redirect2 } = ctx.options.auth;
485
319
  const profile = await getUserProfile(ctx.url.query.code);
486
- const auth3 = {
320
+ const auth2 = {
487
321
  id: createId(),
488
- type: type2,
322
+ strategy,
489
323
  provider: "github",
490
324
  user: createId(profile.email),
491
325
  email: profile.email,
492
326
  time: (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "")
493
327
  };
494
- const user2 = cleanUser({
328
+ const user = cleanUser({
495
329
  id: profile.id,
496
330
  name: profile.name,
497
331
  email: profile.email,
@@ -499,18 +333,18 @@ var callback = async (ctx) => {
499
333
  location: profile.location,
500
334
  created: profile.created_at
501
335
  });
502
- await store.set(auth3.user, user2);
503
- await session2.set(auth3.id, auth3, { expires: "1w" });
504
- if (auth3.type.includes("token")) {
505
- return status(201).json({ ...user2, token: auth3.id });
336
+ await store.set(auth2.user, user);
337
+ await session2.set(auth2.id, auth2, { expires: "1w" });
338
+ if (auth2.strategy.includes("token")) {
339
+ return status(201).json({ ...user, token: auth2.id });
506
340
  }
507
- if (auth3.type.includes("cookie")) {
508
- return status(302).cookies({ authentication: auth3.id }).redirect(redirect2);
341
+ if (auth2.strategy.includes("cookie")) {
342
+ return status(302).cookies({ authentication: auth2.id }).redirect(redirect2);
509
343
  }
510
- if (auth3.type.includes("jwt")) {
344
+ if (auth2.strategy.includes("jwt")) {
511
345
  throw new Error("JWT auth not supported yet");
512
346
  }
513
- if (auth3.type.includes("key")) {
347
+ if (auth2.strategy.includes("key")) {
514
348
  throw new Error("Key auth not supported yet");
515
349
  }
516
350
  throw new Error("Unknown auth type");
@@ -520,149 +354,70 @@ var github_default = { login, callback };
520
354
  // src/auth/providers/index.ts
521
355
  var providers_default = { email: email_default, github: github_default };
522
356
 
523
- // src/auth/NoSession.ts
524
- var NoSession = class {
525
- };
526
- function createNoSession() {
527
- return new Proxy(new NoSession(), {
528
- get(target, key) {
529
- if (target[key]) return target[key];
530
- if (key === "then") return target[key];
531
- if (typeof key === "symbol") return target[key];
532
- throw ServerError_default.NO_STORE_READ({ key: String(key) });
533
- },
534
- set(target, key, value) {
535
- if (target[key] || key === "then" || typeof key === "symbol") {
536
- target[key] = value;
537
- return true;
538
- } else {
539
- throw ServerError_default.NO_STORE_WRITE({ key: String(key) });
540
- }
541
- }
542
- });
357
+ // src/auth/parseAuthOptions.ts
358
+ var defaultRedirect = "/user";
359
+ function defaultCleanUser(fullUser) {
360
+ const { password: _password, ...user } = fullUser;
361
+ return user;
543
362
  }
544
-
545
- // src/auth/session.ts
546
- async function session(ctx) {
547
- const store = ctx.options.session?.store;
548
- if (!store) return createNoSession();
549
- if (ctx.cookies.session) {
550
- const session2 = await store.get(ctx.cookies.session);
551
- if (session2) return session2;
363
+ var providersKeys = Object.keys(providers_default);
364
+ function getProviders(provider) {
365
+ if (typeof provider === "string") {
366
+ provider = provider.split("|");
552
367
  }
553
- return {};
554
- }
555
-
556
- // src/auth/user.ts
557
- async function user(ctx) {
558
- if (!ctx.auth) return;
559
- const user2 = await ctx.options.auth.store.get(ctx.auth.user);
560
- if (!user2) throw ServerError_default.AUTH_NO_USER();
561
- return ctx.options.auth.cleanUser(user2);
562
- }
563
-
564
- // src/auth/index.ts
565
- var parseOptions = (auth3, all) => {
566
- if (!auth3) return null;
567
- if (typeof auth3 === "string") {
568
- const [type2, provider] = auth3.split(":");
569
- auth3 = { type: type2, provider };
570
- }
571
- if (typeof auth3.type === "string") {
572
- auth3.type = auth3.type.split("|").filter(Boolean);
573
- }
574
- if (typeof auth3.provider === "string") {
575
- auth3.provider = auth3.provider.split("|").filter(Boolean);
576
- }
577
- if (!auth3.type) {
578
- throw new Error("Auth options needs a type");
579
- }
580
- if (!auth3.type.length) {
581
- throw new Error("Auth options needs a type");
582
- }
583
- if (!auth3.provider || !auth3.provider.length) {
584
- throw new Error("Auth options needs a provider");
585
- }
586
- const providerNotFound = auth3.provider.find((p) => !providers_default[p]);
587
- if (providerNotFound) {
368
+ const invalidProvider = provider.find((p) => !providersKeys.includes(p));
369
+ if (invalidProvider) {
588
370
  throw new Error(
589
- `Provider "${providerNotFound}" not found, available ones are "${Object.keys(providers_default).join('", "')}"`
371
+ `Provider "${invalidProvider}" not found, available ones are "${providersKeys.join('", "')}"`
590
372
  );
591
373
  }
592
- if (!auth3.session && all.store) {
593
- auth3.session = all.store.prefix("auth:");
374
+ return provider;
375
+ }
376
+ function parseAuthOptions(auth2, all) {
377
+ if (!auth2) return null;
378
+ if (typeof auth2 === "string") {
379
+ const [strategy2, providerRaw] = auth2.split(":");
380
+ const provider2 = providerRaw && providerRaw.split("|");
381
+ auth2 = { strategy: strategy2, provider: provider2 };
594
382
  }
595
- if (!auth3.store && all.store) {
596
- auth3.store = all.store.prefix("user:");
383
+ if (!auth2.strategy) {
384
+ throw new Error("Auth options needs a strategy");
597
385
  }
598
- if (!auth3.cleanUser) {
599
- auth3.cleanUser = (fullUser) => {
600
- const { password: _password, ...user2 } = fullUser;
601
- return user2;
602
- };
386
+ if (!auth2.strategy.length) {
387
+ throw new Error("Auth options needs a strategy");
603
388
  }
604
- if (!auth3.redirect) {
605
- auth3.redirect = "/user";
389
+ const strategy = auth2.strategy;
390
+ if (!auth2.provider || !auth2.provider.length) {
391
+ throw new Error("Auth options needs a provider");
606
392
  }
607
- return auth3;
608
- };
609
- var load = async (ctx) => {
610
- ctx.session = await session(ctx);
611
- ctx.auth = await auth(ctx);
612
- ctx.user = await user(ctx);
613
- };
614
- var middle = async (ctx) => {
615
- if (ctx.options.auth) {
616
- if (ctx.options.auth.provider.includes("github")) {
617
- if (!env.GITHUB_ID) throw new Error("GITHUB_ID not defined");
618
- if (!env.GITHUB_SECRET) throw new Error("GITHUB_SECRET not defined");
619
- ctx.app.get(
620
- "/auth/logout",
621
- { tags: "Auth", title: "Github logout" },
622
- logout
623
- );
624
- ctx.app.get(
625
- "/auth/login/github",
626
- { tags: "Auth" },
627
- providers_default.github.login
628
- );
629
- ctx.app.get(
630
- "/auth/callback/github",
631
- { tags: "Auth", title: "Github callback" },
632
- providers_default.github.callback
633
- );
634
- }
635
- if (ctx.options.auth.provider.includes("email")) {
636
- ctx.app.post("/auth/logout", { tags: "Auth" }, logout);
637
- ctx.app.post(
638
- "/auth/register/email",
639
- { tags: "Auth" },
640
- providers_default.email.register
641
- );
642
- ctx.app.post(
643
- "/auth/login/email",
644
- { tags: "Auth" },
645
- providers_default.email.login
646
- );
647
- ctx.app.put(
648
- "/auth/password/email",
649
- { tags: "Auth" },
650
- providers_default.email.password
651
- );
652
- ctx.app.put(
653
- "/auth/reset/email",
654
- { tags: "Auth" },
655
- providers_default.email.reset
656
- );
657
- }
393
+ const provider = getProviders(auth2.provider);
394
+ const redirect2 = auth2.redirect || defaultRedirect;
395
+ const cleanUser = auth2.cleanUser || defaultCleanUser;
396
+ if (!auth2.store && !all.store) {
397
+ throw new Error("Need a userStore store for Auth");
658
398
  }
659
- };
660
- var auth_default = { load, parseOptions, middle };
399
+ if (!auth2.session && !all.store) {
400
+ throw new Error("Need a sessionStore store for Auth");
401
+ }
402
+ const store = auth2.store || all.store.prefix("user:");
403
+ const session2 = auth2.session || all.store.prefix("auth:");
404
+ return {
405
+ // Base main configuration
406
+ strategy,
407
+ provider,
408
+ // Extra configuration
409
+ redirect: redirect2,
410
+ cleanUser,
411
+ // Stores for the auth session and users
412
+ store,
413
+ session: session2
414
+ };
415
+ }
661
416
 
662
417
  // src/helpers/bucket.ts
663
418
  import * as fs from "fs";
664
- import * as path from "path";
665
419
  import * as fsp from "fs/promises";
420
+ import * as path from "path";
666
421
  function thinLocalBucket(root) {
667
422
  const absolute = (name) => {
668
423
  if (!name) throw new Error("File name is required");
@@ -737,6 +492,47 @@ function bucket_default(root) {
737
492
  return root;
738
493
  }
739
494
 
495
+ // src/helpers/createId.ts
496
+ var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
497
+ var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
498
+ var cyrb53 = (str, seed = 0) => {
499
+ if (typeof str !== "string") str = String(str);
500
+ let h1 = 3735928559 ^ seed;
501
+ let h2 = 1103547991 ^ seed;
502
+ for (let i = 0, ch; i < str.length; i++) {
503
+ ch = str.charCodeAt(i);
504
+ h1 = Math.imul(h1 ^ ch, 2654435761);
505
+ h2 = Math.imul(h2 ^ ch, 1597334677);
506
+ }
507
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
508
+ h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
509
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
510
+ h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
511
+ return 4294967296 * (2097151 & h2) + (h1 >>> 0);
512
+ };
513
+ var hash2 = (str, size) => {
514
+ let chars = "";
515
+ let num = cyrb53(str);
516
+ for (let i = 0; i < size; i++) {
517
+ if (num < alphabet.length) num = cyrb53(str, i);
518
+ chars += alphabet[num % alphabet.length];
519
+ num = Math.floor(num / alphabet.length);
520
+ }
521
+ return chars;
522
+ };
523
+ var randomId = (size = 16) => {
524
+ let id = "";
525
+ const bytes = random(size);
526
+ while (size--) {
527
+ id += alphabet[bytes[size] & 61];
528
+ }
529
+ return id;
530
+ };
531
+ function createId(source, size = 16) {
532
+ if (source) return hash2(source, size);
533
+ return randomId(size);
534
+ }
535
+
740
536
  // src/helpers/color.ts
741
537
  var map = {
742
538
  reset: 0,
@@ -846,7 +642,9 @@ function config(options = {}) {
846
642
  (session2) => session2?.store?.name || "working",
847
643
  "\u{1F510}"
848
644
  );
849
- settings.auth = auth_default.parseOptions(options.auth || env2.AUTH || null, options);
645
+ if (options.auth || env2.AUTH) {
646
+ settings.auth = parseAuthOptions(options.auth || env2.AUTH || null, options);
647
+ }
850
648
  if (options.openapi) {
851
649
  if (options.openapi === true) {
852
650
  settings.openapi = {};
@@ -869,6 +667,72 @@ function cors(config2, origin = "") {
869
667
  return null;
870
668
  }
871
669
 
670
+ // src/helpers/createCookies.ts
671
+ var EXPIRED2 = (/* @__PURE__ */ new Date(0)).toUTCString();
672
+ var times = /(-?(?:\d+\.?\d*|\d*\.?\d+)(?:e[-+]?\d+)?)\s*([\p{L}]*)/iu;
673
+ parse.millisecond = parse.ms = 1e-3;
674
+ parse.second = parse.sec = parse.s = parse[""] = 1;
675
+ parse.minute = parse.min = parse.m = parse.s * 60;
676
+ parse.hour = parse.hr = parse.h = parse.m * 60;
677
+ parse.day = parse.d = parse.h * 24;
678
+ parse.week = parse.wk = parse.w = parse.d * 7;
679
+ parse.year = parse.yr = parse.y = parse.d * 365.25;
680
+ parse.month = parse.b = parse.y / 12;
681
+ function parse(str) {
682
+ if (str === null || str === void 0) return null;
683
+ if (typeof str === "number") return str;
684
+ str = str.toLowerCase().replace(/[,_]/g, "");
685
+ const [_, value, units] = times.exec(str) || [];
686
+ if (!units) return null;
687
+ const unitValue = parse[units] || parse[units.replace(/s$/, "")];
688
+ if (!unitValue) return null;
689
+ const result = unitValue * parseFloat(value);
690
+ return Math.abs(Math.round(result * 1e3));
691
+ }
692
+ function normalizeExpires(expires) {
693
+ if (expires === null || expires === void 0) return void 0;
694
+ if (expires === 0) return EXPIRED2;
695
+ if (typeof expires === "string") {
696
+ if (/^[\d._]+\w+$/.test(expires)) {
697
+ return new Date(Date.now() + parse(expires)).toUTCString();
698
+ } else {
699
+ return expires;
700
+ }
701
+ }
702
+ if (typeof expires === "number") {
703
+ return new Date(Date.now() + expires).toUTCString();
704
+ }
705
+ if (expires instanceof Date) {
706
+ return expires.toUTCString();
707
+ }
708
+ return void 0;
709
+ }
710
+ function createCookies(key, val) {
711
+ if (val.value === null) val.expires = EXPIRED2;
712
+ const { value, path: path2, expires } = val;
713
+ const pathPart = `;Path=${path2 || "/"}`;
714
+ const expiresStr = normalizeExpires(expires);
715
+ const expiresPart = typeof expires !== "undefined" ? `;Expires=${expiresStr}` : "";
716
+ return `${key}=${value || ""}${pathPart}${expiresPart}`;
717
+ }
718
+
719
+ // src/helpers/createWebsocket.ts
720
+ function createWebsocket(sockets, handlers) {
721
+ return {
722
+ message: async (socket, body) => {
723
+ handlers.socket?.filter((s) => s[1] === "message")?.map((s) => s[2]({ socket, sockets, body }));
724
+ },
725
+ open: (socket) => {
726
+ sockets.push(socket);
727
+ handlers.socket?.filter((s) => s[1] === "open")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
728
+ },
729
+ close: (socket) => {
730
+ sockets.splice(sockets.indexOf(socket), 1);
731
+ handlers.socket?.filter((s) => s[1] === "close")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
732
+ }
733
+ };
734
+ }
735
+
872
736
  // src/helpers/define.ts
873
737
  function define(obj, key, cb) {
874
738
  Object.defineProperty(obj, key, {
@@ -965,7 +829,7 @@ async function parseResponse(out, ctx) {
965
829
  }
966
830
  if (Object.keys(ctx.session || {}).length) {
967
831
  if (!ctx.options.session?.store) {
968
- throw ServerError_default.NO_STORE({});
832
+ throw ServerError_default.NO_STORE();
969
833
  }
970
834
  if (!ctx.cookies.session) {
971
835
  ctx.res.cookies.session = createId();
@@ -990,6 +854,7 @@ async function parseResponse(out, ctx) {
990
854
 
991
855
  // src/pathPattern.ts
992
856
  function pathPattern(pattern, path2) {
857
+ if (pattern === "*" && path2 === "/") return {};
993
858
  pattern = `/${pattern.replace(/^\//, "")}`;
994
859
  pattern = pattern.replace(/\/$/, "") || "/";
995
860
  path2 = path2.replace(/\/$/, "") || "/";
@@ -1062,7 +927,9 @@ function validate(ctx, schema) {
1062
927
  }
1063
928
  } catch (error) {
1064
929
  if (error.name === "ZodError" || error.constructor.name === "ZodError") {
1065
- const message = error.issues.map(({ path: path2, message: message2 }) => `[${base}.${path2.join(".")}]: ${message2}`).sort().join("\n");
930
+ const message = error.issues.map(
931
+ ({ path: path2, message: message2 }) => `[${base}.${path2.join(".")}]: ${message2}`
932
+ ).sort().join("\n");
1066
933
  throw new StatusError(message, 422);
1067
934
  }
1068
935
  throw error;
@@ -1072,9 +939,6 @@ function validate(ctx, schema) {
1072
939
  // src/helpers/handleRequest.ts
1073
940
  async function handleRequest(handlers, ctx) {
1074
941
  try {
1075
- if (ctx.error) {
1076
- throw ctx.error;
1077
- }
1078
942
  for (const [method, matcher, ...cbs] of handlers[ctx.method]) {
1079
943
  const match = pathPattern(matcher, ctx.url.pathname || "/");
1080
944
  if (!match) continue;
@@ -1090,7 +954,7 @@ async function handleRequest(handlers, ctx) {
1090
954
  }
1091
955
  if (method !== "*") break;
1092
956
  }
1093
- if (ctx.machine?.provider === "netlify") return;
957
+ if (ctx.platform.provider === "netlify") return;
1094
958
  return new Response("Not Found", { status: 404 });
1095
959
  } catch (error) {
1096
960
  return new Response(error.message || "", { status: error.status || 500 });
@@ -1101,7 +965,7 @@ async function handleRequest(handlers, ctx) {
1101
965
  import * as crypto2 from "crypto";
1102
966
  import { getRandomValues } from "crypto";
1103
967
  import { promisify } from "util";
1104
- async function hash2(password) {
968
+ async function hash(password) {
1105
969
  if ("argon2" in crypto2) {
1106
970
  const argon23 = promisify(crypto2.argon2);
1107
971
  const buf = await argon23("argon2id", {
@@ -1131,18 +995,6 @@ async function iterate(stream, cb) {
1131
995
  }
1132
996
  }
1133
997
 
1134
- // src/helpers/iteratorToReadable.ts
1135
- function iteratorToReadable(generator) {
1136
- return new ReadableStream({
1137
- async start(controller) {
1138
- for await (const chunk of generator) {
1139
- controller.enqueue(chunk);
1140
- }
1141
- controller.close();
1142
- }
1143
- });
1144
- }
1145
-
1146
998
  // src/helpers/iteratorAsyncToReadable.ts
1147
999
  function iteratorAsyncToReadable(asyncGenerator) {
1148
1000
  return new ReadableStream({
@@ -1165,6 +1017,116 @@ function iteratorAsyncToReadable(asyncGenerator) {
1165
1017
  });
1166
1018
  }
1167
1019
 
1020
+ // src/helpers/iteratorToReadable.ts
1021
+ function iteratorToReadable(generator) {
1022
+ return new ReadableStream({
1023
+ async start(controller) {
1024
+ for await (const chunk of generator) {
1025
+ controller.enqueue(chunk);
1026
+ }
1027
+ controller.close();
1028
+ }
1029
+ });
1030
+ }
1031
+
1032
+ // src/helpers/parseBody.ts
1033
+ function getBoundary(header) {
1034
+ if (!header) return null;
1035
+ if (header.includes("multipart/form-data") && !header.includes("boundary=")) {
1036
+ console.error("Do not set the `Content-Type` manually for FormData");
1037
+ }
1038
+ const items = header.split(";");
1039
+ for (const item of items) {
1040
+ const trimmedItem = item.trim();
1041
+ if (trimmedItem.startsWith("boundary=")) {
1042
+ return trimmedItem.split("=")[1].trim();
1043
+ }
1044
+ }
1045
+ return null;
1046
+ }
1047
+ function getMatching(string, regex) {
1048
+ const matches = string.match(regex);
1049
+ return matches?.[1] ?? "";
1050
+ }
1051
+ var saveFile = async (name, value, bucket) => {
1052
+ const ext = name.split(".").pop();
1053
+ const id = `${createId()}.${ext}`;
1054
+ await bucket.write(id, value);
1055
+ return id;
1056
+ };
1057
+ function splitBuffer(buffer, delimiter) {
1058
+ const result = [];
1059
+ let start = 0;
1060
+ let index = buffer.indexOf(delimiter);
1061
+ while (index !== -1) {
1062
+ result.push(buffer.slice(start, index));
1063
+ start = index + delimiter.length;
1064
+ index = buffer.indexOf(delimiter, start);
1065
+ }
1066
+ result.push(buffer.slice(start));
1067
+ return result;
1068
+ }
1069
+ var BREAK_BUFFER = Buffer.from("\r\n\r\n");
1070
+ function isProbablyText(buffer) {
1071
+ for (let i = 0; i < Math.min(buffer.length, 512); i++) {
1072
+ const byte = buffer[i];
1073
+ if (byte === 0) return false;
1074
+ if (byte < 7 || byte > 13 && byte < 32) return false;
1075
+ }
1076
+ return true;
1077
+ }
1078
+ async function parseBody(raw, contentType, bucket) {
1079
+ const contentTypeStr = Array.isArray(contentType) ? contentType[0] : contentType;
1080
+ if (!raw) return {};
1081
+ if (!contentTypeStr || /^text\//.test(contentTypeStr)) {
1082
+ return raw.toString("utf-8");
1083
+ }
1084
+ if (/application\/json/.test(contentTypeStr)) {
1085
+ return JSON.parse(raw.toString("utf-8"));
1086
+ }
1087
+ const boundary = getBoundary(contentTypeStr);
1088
+ if (!boundary) return null;
1089
+ const body = {};
1090
+ const boundaryBuffer = Buffer.from(`--${boundary}`);
1091
+ const parts = splitBuffer(raw, boundaryBuffer);
1092
+ for (const part of parts) {
1093
+ if (part.length === 0 || part.equals(Buffer.from("--\r\n"))) continue;
1094
+ const idx = part.indexOf(BREAK_BUFFER);
1095
+ if (idx === -1) continue;
1096
+ const headerStr = part.slice(0, idx).toString("utf-8");
1097
+ const contentBuf = part.slice(idx + BREAK_BUFFER.length, part.length - 2);
1098
+ const name = getMatching(headerStr, /name="(.+?)"/).trim().replace(/\[\]$/, "");
1099
+ if (!name) continue;
1100
+ const filename = getMatching(headerStr, /filename="(.+?)"/).trim();
1101
+ if (filename) {
1102
+ if (!bucket) throw new Error("Bucket is required to save files");
1103
+ body[name] = await saveFile(filename, contentBuf, bucket);
1104
+ } else {
1105
+ const value = isProbablyText(contentBuf) ? contentBuf.toString("utf-8").trim() : contentBuf;
1106
+ if (body[name]) {
1107
+ if (!Array.isArray(body[name])) body[name] = [body[name]];
1108
+ body[name].push(value);
1109
+ } else {
1110
+ body[name] = value;
1111
+ }
1112
+ }
1113
+ }
1114
+ return body;
1115
+ }
1116
+
1117
+ // src/helpers/parseCookies.ts
1118
+ function parseCookies(cookies2) {
1119
+ if (!cookies2) return {};
1120
+ const cookieStr = Array.isArray(cookies2) ? cookies2[0] : cookies2;
1121
+ if (!cookieStr) return {};
1122
+ return Object.fromEntries(
1123
+ cookieStr.split(/;\s*/).map((part) => {
1124
+ const [key, ...rest] = part.split("=");
1125
+ return [key, decodeURIComponent(rest.join("="))];
1126
+ })
1127
+ );
1128
+ }
1129
+
1168
1130
  // src/helpers/parseHeaders.ts
1169
1131
  var parseHeaders_default = (raw) => {
1170
1132
  const headers2 = {};
@@ -1328,52 +1290,135 @@ async function verify(password, hash3) {
1328
1290
  });
1329
1291
  }
1330
1292
 
1331
- // src/middle/assets.ts
1332
- async function assets(ctx) {
1333
- if (!ctx.options.public) return;
1334
- if (ctx.method !== "get") return;
1335
- if (ctx.url.pathname === "/") return;
1336
- try {
1337
- const asset = await ctx.options.public.read(ctx.url.pathname);
1338
- if (!asset) return;
1339
- return type(ctx.url.pathname.split(".").pop()).send(asset);
1340
- } catch {
1293
+ // src/auth/findSessionId.ts
1294
+ var validateToken = (authorization) => {
1295
+ const [type2, id] = authorization.trim().split(" ");
1296
+ if (type2.toLowerCase() !== "bearer") {
1297
+ throw ServerError_default.AUTH_INVALID_HEADER({ type: type2 });
1341
1298
  }
1342
- }
1343
-
1344
- // src/middle/timer.ts
1345
- var createTime = () => {
1346
- const times = [["init", performance.now()]];
1347
- const time = (name) => times.push([name, performance.now()]);
1348
- time.times = times;
1349
- time.headers = () => {
1350
- const r = (t) => Math.round(t);
1351
- const times2 = time.times;
1352
- const timing = times2.slice(1).map(([name, time2], i) => `${name};dur=${r(time2 - times2[i][1])}`).join(", ");
1353
- return timing;
1354
- };
1355
- return time;
1299
+ if (id.length !== 16) {
1300
+ throw ServerError_default.AUTH_INVALID_TOKEN();
1301
+ }
1302
+ return id;
1356
1303
  };
1357
- function timer(ctx) {
1358
- ctx.time = createTime();
1304
+ var validateCookie = (authorization) => {
1305
+ if (authorization.length !== 16) {
1306
+ throw ServerError_default.AUTH_INVALID_COOKIE();
1307
+ }
1308
+ return authorization;
1309
+ };
1310
+ function findSessionId(ctx) {
1311
+ const strategy = ctx.options.auth.strategy;
1312
+ if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
1313
+ if (strategy.includes("token")) {
1314
+ if (!ctx.headers.authorization) return;
1315
+ return validateToken(ctx.headers.authorization);
1316
+ }
1317
+ if (strategy.includes("cookie")) {
1318
+ if (!ctx.cookies.authentication) return;
1319
+ return validateCookie(ctx.cookies.authentication);
1320
+ }
1321
+ throw new Error(`Invalid auth type "${strategy}"`);
1359
1322
  }
1360
1323
 
1361
- // src/middle/openapi.ts
1362
- import * as fsp2 from "fs/promises";
1363
- var entities = {
1364
- "&": "&amp;",
1365
- "<": "&lt;",
1366
- ">": "&gt;",
1367
- '"': "&quot;"
1368
- };
1369
- var encode = (str = "") => {
1324
+ // src/auth/getUser.ts
1325
+ async function getUser(ctx) {
1326
+ if (!ctx.options.auth) return;
1327
+ const options = ctx.options.auth;
1328
+ const sessionId = findSessionId(ctx);
1329
+ if (!sessionId) return;
1330
+ const auth2 = await options.session.get(sessionId);
1331
+ if (!auth2) return;
1332
+ if (options.strategy !== auth2.strategy) {
1333
+ throw ServerError_default.AUTH_INVALID_STRATEGY({
1334
+ strategy: auth2.strategy || "undefined",
1335
+ valid: options.strategy
1336
+ });
1337
+ }
1338
+ if (!options.provider.includes(auth2.provider)) {
1339
+ throw ServerError_default.AUTH_INVALID_PROVIDER({
1340
+ provider: auth2.provider,
1341
+ valid: options.provider
1342
+ });
1343
+ }
1344
+ const user = await ctx.options.auth.store.get(auth2.user);
1345
+ if (!user) throw ServerError_default.AUTH_NO_USER();
1346
+ user.strategy = auth2.strategy;
1347
+ user.provider = auth2.provider;
1348
+ return ctx.options.auth.cleanUser(user);
1349
+ }
1350
+
1351
+ // src/auth/logout.ts
1352
+ async function logout(ctx) {
1353
+ const session2 = findSessionId(ctx);
1354
+ const { strategy } = ctx.user;
1355
+ await ctx.options.auth.session.del(session2);
1356
+ if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
1357
+ if (strategy.includes("token")) {
1358
+ return { token: null };
1359
+ }
1360
+ if (strategy.includes("cookie")) {
1361
+ return cookies({ authorization: null }).redirect("/");
1362
+ }
1363
+ if (strategy.includes("jwt")) {
1364
+ throw new Error("JWT auth not supported yet");
1365
+ }
1366
+ if (strategy.includes("key")) {
1367
+ throw new Error("Key auth not supported yet");
1368
+ }
1369
+ throw new Error("Unknown auth type");
1370
+ }
1371
+
1372
+ // src/auth/index.ts
1373
+ function auth(app) {
1374
+ app.use(async function middle(ctx) {
1375
+ ctx.user = await getUser(ctx);
1376
+ });
1377
+ if (app.settings.auth.provider.includes("github")) {
1378
+ if (!env.GITHUB_ID) throw new Error("GITHUB_ID not defined");
1379
+ if (!env.GITHUB_SECRET) throw new Error("GITHUB_SECRET not defined");
1380
+ app.get("/auth/logout", logout);
1381
+ app.get("/auth/login/github", providers_default.github.login);
1382
+ app.get("/auth/callback/github", providers_default.github.callback);
1383
+ }
1384
+ if (app.settings.auth.provider.includes("email")) {
1385
+ app.post("/auth/logout", logout);
1386
+ app.post("/auth/register/email", providers_default.email.register);
1387
+ app.post("/auth/login/email", providers_default.email.login);
1388
+ app.put("/auth/password/email", providers_default.email.password);
1389
+ app.put("/auth/reset/email", providers_default.email.reset);
1390
+ }
1391
+ }
1392
+
1393
+ // src/middle/assets.ts
1394
+ async function assets(ctx) {
1395
+ if (!ctx.options.public) return;
1396
+ if (ctx.method !== "get") return;
1397
+ if (ctx.url.pathname === "/") return;
1398
+ try {
1399
+ const asset = await ctx.options.public.read(ctx.url.pathname);
1400
+ if (!asset) return;
1401
+ return type(ctx.url.pathname.split(".").pop()).send(asset);
1402
+ } catch {
1403
+ }
1404
+ }
1405
+
1406
+ // src/middle/openapi.ts
1407
+ import * as fsp2 from "fs/promises";
1408
+ var entities = {
1409
+ "&": "&amp;",
1410
+ "<": "&lt;",
1411
+ ">": "&gt;",
1412
+ '"': "&quot;"
1413
+ };
1414
+ var encode = (str = "") => {
1370
1415
  if (typeof str === "number") str = String(str);
1371
1416
  if (typeof str !== "string") return "";
1372
1417
  return str.replace(/[&<>"]/g, (tag) => entities[tag]);
1373
1418
  };
1374
1419
  var getConfig = (routes) => {
1375
1420
  const config2 = routes.find(
1376
- (r) => typeof r !== "string" && typeof r !== "function" && typeof r === "object"
1421
+ (r2) => typeof r2 !== "string" && typeof r2 !== "function" && typeof r2 === "object"
1377
1422
  );
1378
1423
  if (!config2) return {};
1379
1424
  if (config2.tags) {
@@ -1520,10 +1565,205 @@ var openapi_default = async (ctx) => {
1520
1565
  </html> `;
1521
1566
  };
1522
1567
 
1523
- // src/middle/index.ts
1524
- var auth2 = auth_default.middle;
1568
+ // src/middle/timer.ts
1569
+ var createTime = () => {
1570
+ const times2 = [["init", performance.now()]];
1571
+ const time = (name) => times2.push([name, performance.now()]);
1572
+ time.times = times2;
1573
+ time.headers = () => {
1574
+ const r2 = (t) => Math.round(t);
1575
+ const times3 = time.times;
1576
+ const timing = times3.slice(1).map(([name, time2], i) => `${name};dur=${r2(time2 - times3[i][1])}`).join(", ");
1577
+ return timing;
1578
+ };
1579
+ return time;
1580
+ };
1581
+ function timer(ctx) {
1582
+ ctx.time = createTime();
1583
+ }
1584
+
1585
+ // src/auth/NoSession.ts
1586
+ var NoSession = class {
1587
+ };
1588
+ function createNoSession() {
1589
+ return new Proxy(NoSession, {
1590
+ get(target, key) {
1591
+ if (target[key]) return target[key];
1592
+ if (key === "then") return target[key];
1593
+ if (typeof key === "symbol") return target[key];
1594
+ throw ServerError_default.NO_STORE_READ({ key: String(key) });
1595
+ },
1596
+ set(target, key, value) {
1597
+ if (target[key] || key === "then" || typeof key === "symbol") {
1598
+ target[key] = value;
1599
+ return true;
1600
+ }
1601
+ throw ServerError_default.NO_STORE_WRITE({ key: String(key) });
1602
+ }
1603
+ });
1604
+ }
1605
+
1606
+ // src/auth/session.ts
1607
+ async function session(ctx) {
1608
+ const store = ctx.options.session?.store;
1609
+ if (!store) {
1610
+ ctx.session = createNoSession();
1611
+ return;
1612
+ }
1613
+ if (ctx.cookies.session) {
1614
+ const session2 = await store.get(ctx.cookies.session);
1615
+ ctx.session = session2;
1616
+ return;
1617
+ }
1618
+ }
1619
+
1620
+ // src/context/node.ts
1621
+ import { TLSSocket } from "tls";
1622
+
1623
+ // src/context/createEvents.ts
1624
+ function createEvents() {
1625
+ const events = {};
1626
+ events.on = (name, callback2) => {
1627
+ events[name] = events[name] || [];
1628
+ events[name].push(callback2);
1629
+ };
1630
+ events.trigger = (name, data) => {
1631
+ if (!events[name]) return;
1632
+ for (const cb of events[name]) {
1633
+ cb(data);
1634
+ }
1635
+ };
1636
+ return events;
1637
+ }
1638
+
1639
+ // src/context/isValidMethod.ts
1640
+ var methods = [
1641
+ "get",
1642
+ "post",
1643
+ "put",
1644
+ "patch",
1645
+ "delete",
1646
+ "head",
1647
+ "options",
1648
+ "socket"
1649
+ ];
1650
+ function isValidMethod(method) {
1651
+ return methods.includes(method);
1652
+ }
1653
+
1654
+ // src/context/node.ts
1655
+ var chunkArray = (arr) => arr.length > 2 ? [[arr[0], arr[1]], ...chunkArray(arr.slice(2))] : [arr];
1656
+ async function createNode(req, app) {
1657
+ const init = performance.now();
1658
+ const method = req.method?.toLowerCase() || "get";
1659
+ if (!isValidMethod(method)) {
1660
+ throw new Error(`Invalid HTTP method: ${method}`);
1661
+ }
1662
+ const chunks = chunkArray(req.rawHeaders);
1663
+ const headers2 = parseHeaders_default(new Headers(chunks));
1664
+ const cookies2 = parseCookies(headers2.cookie);
1665
+ const scheme = req.socket instanceof TLSSocket ? "https" : "http";
1666
+ const host = headers2.host || `localhost:${app.settings.port}`;
1667
+ const path2 = (req.url || "/").replace(/\/$/, "") || "/";
1668
+ const baseUrl = `${scheme}://${host}`;
1669
+ const url = new URL(path2, baseUrl);
1670
+ define(
1671
+ url,
1672
+ "query",
1673
+ (url2) => Object.fromEntries(url2.searchParams.entries())
1674
+ );
1675
+ const rawBody = await new Promise((resolve2, reject) => {
1676
+ const body2 = [];
1677
+ req.on("data", (chunk) => body2.push(chunk)).on("end", () => resolve2(Buffer.concat(body2))).on("error", reject);
1678
+ });
1679
+ const body = rawBody ? await parseBody(rawBody, headers2["content-type"], app.settings.uploads) : void 0;
1680
+ const events = createEvents();
1681
+ return {
1682
+ options: app.settings,
1683
+ platform: app.platform,
1684
+ url,
1685
+ method,
1686
+ body,
1687
+ headers: headers2,
1688
+ cookies: cookies2,
1689
+ init,
1690
+ events,
1691
+ app
1692
+ };
1693
+ }
1694
+
1695
+ // src/context/winter.ts
1696
+ async function createWinter(req, app) {
1697
+ const init = performance.now();
1698
+ const method = req.method.toLowerCase();
1699
+ if (!isValidMethod(method)) {
1700
+ throw new Error(`Invalid HTTP method: ${method}`);
1701
+ }
1702
+ const headers2 = parseHeaders_default(req.headers);
1703
+ const cookies2 = parseCookies(headers2.cookie);
1704
+ const baseUrl = req.url.replace(/\/$/, "") || "/";
1705
+ const url = new URL(baseUrl);
1706
+ define(
1707
+ url,
1708
+ "query",
1709
+ (url2) => Object.fromEntries(url2.searchParams.entries())
1710
+ );
1711
+ const rawBody = Buffer.from(await req.arrayBuffer());
1712
+ const body = req.body ? await parseBody(rawBody, headers2["content-type"], app.settings.uploads) : void 0;
1713
+ const events = createEvents();
1714
+ return {
1715
+ options: app.settings,
1716
+ platform: app.platform,
1717
+ url,
1718
+ method,
1719
+ body,
1720
+ headers: headers2,
1721
+ cookies: cookies2,
1722
+ init,
1723
+ events,
1724
+ app
1725
+ };
1726
+ }
1727
+
1728
+ // src/context/handlers.ts
1729
+ var Winter = async (app, request, env3) => {
1730
+ if (env3?.upgrade(request)) return;
1731
+ Object.assign(globalThis.env, env3);
1732
+ const ctx = await createWinter(request, app);
1733
+ const res = await handleRequest(app.handlers, ctx);
1734
+ ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
1735
+ return res;
1736
+ };
1737
+ var Node = async (app) => {
1738
+ const http = await import("http");
1739
+ http.createServer(async (request, response) => {
1740
+ const ctx = await createNode(request, app);
1741
+ if ("error" in ctx) throw ctx.error;
1742
+ const out = await handleRequest(app.handlers, ctx);
1743
+ response.writeHead(out.status || 200, parseHeaders_default(out.headers));
1744
+ if (out.body instanceof ReadableStream) {
1745
+ await iterate(out.body, (chunk) => response.write(chunk));
1746
+ } else {
1747
+ response.write(out.body || "");
1748
+ }
1749
+ response.end();
1750
+ }).listen(app.settings.port);
1751
+ };
1752
+ var Netlify = async (app, request, context) => {
1753
+ request.context = context;
1754
+ if (typeof Netlify === "undefined") {
1755
+ throw new Error("Netlify doesn't exist");
1756
+ }
1757
+ const ctx = await createWinter(request, app);
1758
+ const res = await handleRequest(app.handlers, ctx);
1759
+ ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
1760
+ return res;
1761
+ };
1525
1762
 
1526
1763
  // src/router.ts
1764
+ function isMiddleware(x) {
1765
+ return typeof x === "function";
1766
+ }
1527
1767
  var Router = class _Router {
1528
1768
  handlers = {
1529
1769
  socket: [],
@@ -1545,35 +1785,59 @@ var Router = class _Router {
1545
1785
  middleware.unshift(path2);
1546
1786
  path2 = "*";
1547
1787
  }
1548
- const methods = method === "*" ? Object.keys(this.handlers) : [method];
1549
- for (const m of methods) {
1788
+ const methods2 = method === "*" ? Object.keys(this.handlers) : [method];
1789
+ for (const m of methods2) {
1550
1790
  this.handlers[m].push([method, path2, ...middleware]);
1551
1791
  }
1552
1792
  return this.self();
1553
1793
  }
1554
- socket(path2, ...middleware) {
1555
- return this.handle("socket", path2, ...middleware);
1794
+ socket(pathOrMid, optionsOrMid, ...middleware) {
1795
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1796
+ return this.handle("socket", pathOrMid, optionsOrMid, ...middleware);
1797
+ }
1798
+ return this.handle("socket", pathOrMid, ...middleware);
1556
1799
  }
1557
- get(path2, ...middleware) {
1558
- return this.handle("get", path2, ...middleware);
1800
+ get(pathOrMid, optionsOrMid, ...middleware) {
1801
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1802
+ return this.handle("get", pathOrMid, optionsOrMid, ...middleware);
1803
+ }
1804
+ return this.handle("get", pathOrMid, ...middleware);
1559
1805
  }
1560
- head(path2, ...middleware) {
1561
- return this.handle("head", path2, ...middleware);
1806
+ head(pathOrMid, optionsOrMid, ...middleware) {
1807
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1808
+ return this.handle("head", pathOrMid, optionsOrMid, ...middleware);
1809
+ }
1810
+ return this.handle("head", pathOrMid, ...middleware);
1562
1811
  }
1563
- post(path2, ...middleware) {
1564
- return this.handle("post", path2, ...middleware);
1812
+ post(pathOrMid, optionsOrMid, ...middleware) {
1813
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1814
+ return this.handle("post", pathOrMid, optionsOrMid, ...middleware);
1815
+ }
1816
+ return this.handle("post", pathOrMid, ...middleware);
1565
1817
  }
1566
- put(path2, ...middleware) {
1567
- return this.handle("put", path2, ...middleware);
1818
+ put(pathOrMid, optionsOrMid, ...middleware) {
1819
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1820
+ return this.handle("put", pathOrMid, optionsOrMid, ...middleware);
1821
+ }
1822
+ return this.handle("put", pathOrMid, ...middleware);
1568
1823
  }
1569
- patch(path2, ...middleware) {
1570
- return this.handle("patch", path2, ...middleware);
1824
+ patch(pathOrMid, optionsOrMid, ...middleware) {
1825
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1826
+ return this.handle("patch", pathOrMid, optionsOrMid, ...middleware);
1827
+ }
1828
+ return this.handle("patch", pathOrMid, ...middleware);
1571
1829
  }
1572
- del(path2, ...middleware) {
1573
- return this.handle("delete", path2, ...middleware);
1830
+ del(pathOrMid, optionsOrMid, ...middleware) {
1831
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1832
+ return this.handle("delete", pathOrMid, optionsOrMid, ...middleware);
1833
+ }
1834
+ return this.handle("delete", pathOrMid, ...middleware);
1574
1835
  }
1575
- options(path2, ...middleware) {
1576
- return this.handle("options", path2, ...middleware);
1836
+ options(pathOrMid, optionsOrMid, ...middleware) {
1837
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1838
+ return this.handle("options", pathOrMid, optionsOrMid, ...middleware);
1839
+ }
1840
+ return this.handle("options", pathOrMid, ...middleware);
1577
1841
  }
1578
1842
  use(...args) {
1579
1843
  const path2 = typeof args[0] === "string" ? args.shift() : "*";
@@ -1601,337 +1865,46 @@ function isSerializable(body) {
1601
1865
  if (typeof body === "string") return false;
1602
1866
  if (body instanceof ReadableStream) return false;
1603
1867
  if (body instanceof FormData) return false;
1868
+ if (body instanceof Blob) return false;
1869
+ if (body instanceof ArrayBuffer) return false;
1870
+ if (ArrayBuffer.isView(body)) return false;
1871
+ if (body instanceof URLSearchParams) return false;
1604
1872
  return true;
1605
1873
  }
1606
1874
  function ServerTest(app) {
1607
1875
  const port = app.settings.port;
1608
- const fetch2 = async (path2, method, options = {}) => {
1609
- try {
1610
- if (!options.headers) options.headers = {};
1611
- if (isSerializable(options.body)) {
1612
- options.headers["content-type"] = "application/json";
1613
- options.body = JSON.stringify(options.body);
1614
- }
1615
- const res = await app.fetch(
1616
- new Request(`http://localhost:${port}${path2}`, {
1617
- method,
1618
- ...options
1619
- })
1620
- );
1621
- const headers2 = parseHeaders_default(res.headers);
1622
- let body;
1623
- if (headers2["content-type"]?.includes("application/json")) {
1624
- body = await res.json();
1625
- } else {
1626
- body = await res.text();
1627
- }
1628
- return { status: res.status, headers: headers2, body };
1629
- } catch (error) {
1630
- return { status: 500, headers: {}, body: error.message };
1876
+ const fetch2 = async (method, path2, options = {}) => {
1877
+ if (!options.headers) options.headers = {};
1878
+ if (isSerializable(options.body)) {
1879
+ options.headers["content-type"] = "application/json";
1880
+ options.body = JSON.stringify(options.body);
1631
1881
  }
1882
+ return await app.fetch(
1883
+ new Request(`http://localhost:${port}${path2}`, {
1884
+ method,
1885
+ ...options
1886
+ })
1887
+ );
1632
1888
  };
1633
1889
  return {
1634
- get: (path2, options) => fetch2(path2, "get", options),
1635
- head: (path2, options) => fetch2(path2, "head", options),
1636
- post: (path2, body, options) => fetch2(path2, "post", { body, ...options }),
1637
- put: (path2, body, options) => fetch2(path2, "put", { body, ...options }),
1638
- patch: (path2, body, options) => fetch2(path2, "patch", { body, ...options }),
1639
- delete: (path2, options) => fetch2(path2, "delete", options),
1640
- options: (path2, options) => fetch2(path2, "options", options)
1890
+ get: (path2, options) => fetch2("get", path2, options),
1891
+ head: (path2, options) => fetch2("head", path2, options),
1892
+ post: (path2, body, options) => fetch2("post", path2, { body, ...options }),
1893
+ put: (path2, body, options) => fetch2("put", path2, { body, ...options }),
1894
+ patch: (path2, body, options) => fetch2("patch", path2, { body, ...options }),
1895
+ delete: (path2, options) => fetch2("delete", path2, options),
1896
+ options: (path2, options) => fetch2("options", path2, options)
1641
1897
  };
1642
1898
  }
1643
1899
 
1644
- // src/context/parseBody.ts
1645
- function getBoundary(header) {
1646
- if (!header) return null;
1647
- if (header.includes("multipart/form-data") && !header.includes("boundary=")) {
1648
- console.error("Do not set the `Content-Type` manually for FormData");
1649
- }
1650
- const items = header.split(";");
1651
- for (const item of items) {
1652
- const trimmedItem = item.trim();
1653
- if (trimmedItem.startsWith("boundary=")) {
1654
- return trimmedItem.split("=")[1].trim();
1655
- }
1656
- }
1657
- return null;
1658
- }
1659
- function getMatching(string, regex) {
1660
- const matches = string.match(regex);
1661
- return matches?.[1] ?? "";
1662
- }
1663
- var saveFile = async (name, value, bucket) => {
1664
- const ext = name.split(".").pop();
1665
- const id = `${createId()}.${ext}`;
1666
- await bucket.write(id, value);
1667
- return id;
1668
- };
1669
- function splitBuffer(buffer, delimiter) {
1670
- const result = [];
1671
- let start = 0;
1672
- let index = buffer.indexOf(delimiter);
1673
- while (index !== -1) {
1674
- result.push(buffer.slice(start, index));
1675
- start = index + delimiter.length;
1676
- index = buffer.indexOf(delimiter, start);
1677
- }
1678
- result.push(buffer.slice(start));
1679
- return result;
1680
- }
1681
- var BREAK_BUFFER = Buffer.from("\r\n\r\n");
1682
- function isProbablyText(buffer) {
1683
- for (let i = 0; i < Math.min(buffer.length, 512); i++) {
1684
- const byte = buffer[i];
1685
- if (byte === 0) return false;
1686
- if (byte < 7 || byte > 13 && byte < 32) return false;
1687
- }
1688
- return true;
1689
- }
1690
- async function parseBody(raw, contentType, bucket) {
1691
- const contentTypeStr = Array.isArray(contentType) ? contentType[0] : contentType;
1692
- let rawBuffer;
1693
- if (raw instanceof Buffer) {
1694
- rawBuffer = raw;
1695
- } else if ("arrayBuffer" in raw && typeof raw.arrayBuffer === "function") {
1696
- const arrayBuf = await raw.arrayBuffer();
1697
- rawBuffer = Buffer.from(arrayBuf);
1698
- } else {
1699
- throw new Error("Unsupported raw type");
1700
- }
1701
- if (!rawBuffer) return {};
1702
- if (!contentTypeStr || /text\/plain/.test(contentTypeStr)) {
1703
- return rawBuffer.toString("utf-8");
1704
- }
1705
- if (/application\/json/.test(contentTypeStr)) {
1706
- return JSON.parse(rawBuffer.toString("utf-8"));
1707
- }
1708
- const boundary = getBoundary(contentTypeStr);
1709
- if (!boundary) return null;
1710
- const body = {};
1711
- const boundaryBuffer = Buffer.from(`--${boundary}`);
1712
- const parts = splitBuffer(rawBuffer, boundaryBuffer);
1713
- for (const part of parts) {
1714
- if (part.length === 0 || part.equals(Buffer.from("--\r\n"))) continue;
1715
- const idx = part.indexOf(BREAK_BUFFER);
1716
- if (idx === -1) continue;
1717
- const headerStr = part.slice(0, idx).toString("utf-8");
1718
- const contentBuf = part.slice(idx + BREAK_BUFFER.length, part.length - 2);
1719
- const name = getMatching(headerStr, /name="(.+?)"/).trim().replace(/\[\]$/, "");
1720
- if (!name) continue;
1721
- const filename = getMatching(headerStr, /filename="(.+?)"/).trim();
1722
- if (filename) {
1723
- if (!bucket) throw new Error("Bucket is required to save files");
1724
- body[name] = await saveFile(filename, contentBuf, bucket);
1725
- } else {
1726
- const value = isProbablyText(contentBuf) ? contentBuf.toString("utf-8").trim() : contentBuf;
1727
- if (body[name]) {
1728
- if (!Array.isArray(body[name])) body[name] = [body[name]];
1729
- body[name].push(value);
1730
- } else {
1731
- body[name] = value;
1732
- }
1733
- }
1734
- }
1735
- return body;
1736
- }
1737
-
1738
- // src/context/parseCookies.ts
1739
- function parseCookies(cookies2) {
1740
- if (!cookies2) return {};
1741
- const cookieStr = Array.isArray(cookies2) ? cookies2[0] : cookies2;
1742
- if (!cookieStr) return {};
1743
- return Object.fromEntries(
1744
- cookieStr.split(/;\s*/).map((part) => {
1745
- const [key, ...rest] = part.split("=");
1746
- return [key, decodeURIComponent(rest.join("="))];
1747
- })
1748
- );
1749
- }
1750
-
1751
- // src/context/winter.ts
1752
- function isValidMethod(method) {
1753
- return [
1754
- "get",
1755
- "post",
1756
- "put",
1757
- "patch",
1758
- "delete",
1759
- "head",
1760
- "options",
1761
- "socket"
1762
- ].includes(method);
1763
- }
1764
- var winter_default = async (request, app) => {
1765
- try {
1766
- const ctx = {
1767
- headers: {},
1768
- cookies: {},
1769
- url: void 0,
1770
- options: app.settings || {},
1771
- method: "get",
1772
- init: performance.now(),
1773
- req: request
1774
- };
1775
- const method = request.method.toLowerCase();
1776
- if (!isValidMethod(method)) {
1777
- throw new Error(`Invalid HTTP method: ${method}`);
1778
- }
1779
- ctx.method = method;
1780
- const events = {};
1781
- ctx.on = (name, callback2) => {
1782
- events[name] = events[name] || [];
1783
- events[name].push(callback2);
1784
- };
1785
- ctx.trigger = (name, data) => {
1786
- if (!events[name]) return;
1787
- for (const cb of events[name]) {
1788
- cb(data);
1789
- }
1790
- };
1791
- ctx.headers = parseHeaders_default(request.headers);
1792
- ctx.cookies = parseCookies(ctx.headers.cookie);
1793
- await auth_default.load(ctx);
1794
- ctx.url = new URL(request.url.replace(/\/$/, ""));
1795
- define(
1796
- ctx.url,
1797
- "query",
1798
- (url) => Object.fromEntries(url.searchParams.entries())
1799
- );
1800
- if (request.body) {
1801
- const type2 = ctx.headers["content-type"];
1802
- ctx.body = await parseBody(request, type2, ctx.options.uploads);
1803
- }
1804
- ctx.app = app;
1805
- ctx.platform = app.platform;
1806
- ctx.machine = app.platform;
1807
- return ctx;
1808
- } catch (error) {
1809
- return { error };
1810
- }
1811
- };
1812
-
1813
- // src/context/node.ts
1814
- function isValidMethod2(method) {
1815
- return [
1816
- "get",
1817
- "post",
1818
- "put",
1819
- "patch",
1820
- "delete",
1821
- "head",
1822
- "options",
1823
- "socket"
1824
- ].includes(method);
1825
- }
1826
- var chunkArray = (arr, size) => arr.length > size ? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)] : [arr];
1827
- var node_default = async (request, app) => {
1828
- try {
1829
- const ctx = {
1830
- headers: {},
1831
- cookies: {},
1832
- url: void 0,
1833
- options: app.settings || {},
1834
- method: "get",
1835
- req: request
1836
- };
1837
- const method = request.method?.toLowerCase() || "get";
1838
- if (!isValidMethod2(method)) {
1839
- throw new Error(`Invalid HTTP method: ${method}`);
1840
- }
1841
- ctx.method = method;
1842
- const events = {};
1843
- ctx.on = (name, callback2) => {
1844
- events[name] = events[name] || [];
1845
- events[name].push(callback2);
1846
- };
1847
- ctx.trigger = (name, data) => {
1848
- if (!events[name]) return;
1849
- for (const cb of events[name]) {
1850
- cb(data);
1851
- }
1852
- };
1853
- ctx.headers = parseHeaders_default(
1854
- new Headers(chunkArray(request.rawHeaders, 2))
1855
- );
1856
- ctx.cookies = parseCookies(ctx.headers.cookie);
1857
- await auth_default.load(ctx);
1858
- const https = request.connection?.encrypted ? "https" : "http";
1859
- const host = ctx.headers.host || `localhost:${ctx.options.port}`;
1860
- const path2 = (request.url || "/").replace(/\/$/, "") || "/";
1861
- ctx.url = new URL(path2, `${https}://${host}`);
1862
- define(
1863
- ctx.url,
1864
- "query",
1865
- (url) => Object.fromEntries(url.searchParams.entries())
1866
- );
1867
- await new Promise((resolve2, reject) => {
1868
- const body = [];
1869
- request.on("data", (chunk) => {
1870
- body.push(chunk);
1871
- }).on("end", async () => {
1872
- const type2 = ctx.headers["content-type"];
1873
- const concatenated = Buffer.concat(body);
1874
- ctx.body = await parseBody(concatenated, type2, ctx.options.uploads);
1875
- resolve2();
1876
- }).on("error", reject);
1877
- });
1878
- ctx.app = app;
1879
- ctx.platform = app.platform;
1880
- ctx.machine = app.platform;
1881
- return ctx;
1882
- } catch (error) {
1883
- return { error };
1884
- }
1885
- };
1886
-
1887
- // src/context/handlers.ts
1888
- var Winter = async (app, request, env3) => {
1889
- if (env3?.upgrade(request)) return;
1890
- Object.assign(globalThis.env, env3);
1891
- const ctx = await winter_default(request, app);
1892
- if ("error" in ctx) {
1893
- throw ctx.error;
1894
- }
1895
- const res = await handleRequest(app.handlers, ctx);
1896
- ctx.trigger("finish", { ...ctx, res, end: performance.now() });
1897
- return res;
1898
- };
1899
- var Node = async (app) => {
1900
- const http = await import("http");
1901
- http.createServer(async (request, response) => {
1902
- const ctx = await node_default(request, app);
1903
- if ("error" in ctx) {
1904
- throw ctx.error;
1905
- }
1906
- const out = await handleRequest(app.handlers, ctx);
1907
- response.writeHead(out.status || 200, parseHeaders_default(out.headers));
1908
- if (out.body instanceof ReadableStream) {
1909
- await iterate(out.body, (chunk) => response.write(chunk));
1910
- } else {
1911
- response.write(out.body || "");
1912
- }
1913
- response.end();
1914
- }).listen(app.settings.port);
1915
- };
1916
- var Netlify = async (app, request, context) => {
1917
- request.context = context;
1918
- if (typeof Netlify === "undefined") {
1919
- throw new Error("Netlify doesn't exist");
1920
- }
1921
- const ctx = await winter_default(request, app);
1922
- if ("error" in ctx) {
1923
- throw ctx.error;
1924
- }
1925
- return await handleRequest(app.handlers, ctx);
1926
- };
1927
-
1928
1900
  // src/index.ts
1929
1901
  var Server = class extends Router {
1930
1902
  settings;
1931
1903
  platform;
1932
- port;
1933
1904
  sockets;
1934
1905
  websocket;
1906
+ // Needed to be explicit for Bun/WinterCG
1907
+ port;
1935
1908
  constructor(options = {}) {
1936
1909
  super();
1937
1910
  this.settings = config(options);
@@ -1946,12 +1919,13 @@ var Server = class extends Router {
1946
1919
  }
1947
1920
  this.use(timer);
1948
1921
  this.use(assets);
1922
+ this.use(session);
1923
+ if (this.settings.auth) {
1924
+ auth(this);
1925
+ }
1949
1926
  if (this.settings.openapi) {
1950
1927
  this.get(this.settings.openapi.path || "/docs", openapi_default);
1951
1928
  }
1952
- if (this.settings.auth) {
1953
- this.use(auth2);
1954
- }
1955
1929
  }
1956
1930
  // We need to return a function; some environment expect the default export
1957
1931
  // to be a function that is called with the request, but we also want to
@@ -1988,7 +1962,7 @@ function server(options = {}) {
1988
1962
  return new Server(options).self();
1989
1963
  }
1990
1964
  export {
1991
- Reply,
1965
+ Server,
1992
1966
  ServerError_default as ServerError,
1993
1967
  cookies,
1994
1968
  server as default,
@@ -2000,6 +1974,5 @@ export {
2000
1974
  router,
2001
1975
  send,
2002
1976
  status,
2003
- type,
2004
- view
1977
+ type
2005
1978
  };