@server/next 0.27.0 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +686 -739
  2. package/package.json +3 -2
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,126 +67,109 @@ 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() {
223
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;
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
226
175
  var Reply = class {
@@ -337,112 +286,6 @@ var file = (...args) => new Reply().file(...args);
337
286
  var redirect = (...args) => new Reply().redirect(...args);
338
287
  var view = (...args) => new Reply().view(...args);
339
288
 
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");
393
- }
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
- };
445
-
446
289
  // src/auth/providers/github.ts
447
290
  var oauth = async (code) => {
448
291
  const fch = async (url, { body, headers: headers2 = {}, ...rest } = {}) => {
@@ -481,17 +324,17 @@ var getUserProfile = async (code) => {
481
324
  return { ...profile, email };
482
325
  };
483
326
  var callback = async (ctx) => {
484
- const { type: type2, cleanUser, store, session: session2, redirect: redirect2 } = ctx.options.auth;
327
+ const { strategy, cleanUser, store, session: session2, redirect: redirect2 } = ctx.options.auth;
485
328
  const profile = await getUserProfile(ctx.url.query.code);
486
- const auth3 = {
329
+ const auth2 = {
487
330
  id: createId(),
488
- type: type2,
331
+ strategy,
489
332
  provider: "github",
490
333
  user: createId(profile.email),
491
334
  email: profile.email,
492
335
  time: (/* @__PURE__ */ new Date()).toISOString().replace(/\.[0-9]*/, "")
493
336
  };
494
- const user2 = cleanUser({
337
+ const user = cleanUser({
495
338
  id: profile.id,
496
339
  name: profile.name,
497
340
  email: profile.email,
@@ -499,18 +342,18 @@ var callback = async (ctx) => {
499
342
  location: profile.location,
500
343
  created: profile.created_at
501
344
  });
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 });
345
+ await store.set(auth2.user, user);
346
+ await session2.set(auth2.id, auth2, { expires: "1w" });
347
+ if (auth2.strategy.includes("token")) {
348
+ return status(201).json({ ...user, token: auth2.id });
506
349
  }
507
- if (auth3.type.includes("cookie")) {
508
- return status(302).cookies({ authentication: auth3.id }).redirect(redirect2);
350
+ if (auth2.strategy.includes("cookie")) {
351
+ return status(302).cookies({ authentication: auth2.id }).redirect(redirect2);
509
352
  }
510
- if (auth3.type.includes("jwt")) {
353
+ if (auth2.strategy.includes("jwt")) {
511
354
  throw new Error("JWT auth not supported yet");
512
355
  }
513
- if (auth3.type.includes("key")) {
356
+ if (auth2.strategy.includes("key")) {
514
357
  throw new Error("Key auth not supported yet");
515
358
  }
516
359
  throw new Error("Unknown auth type");
@@ -520,149 +363,53 @@ var github_default = { login, callback };
520
363
  // src/auth/providers/index.ts
521
364
  var providers_default = { email: email_default, github: github_default };
522
365
 
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
- });
543
- }
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;
552
- }
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 };
366
+ // src/auth/parseAuthOptions.ts
367
+ function parseAuthOptions(auth2, all) {
368
+ if (!auth2) return null;
369
+ if (typeof auth2 === "string") {
370
+ const [strategy, provider] = auth2.split(":");
371
+ auth2 = { strategy, provider };
570
372
  }
571
- if (typeof auth3.type === "string") {
572
- auth3.type = auth3.type.split("|").filter(Boolean);
373
+ if (typeof auth2.provider === "string") {
374
+ auth2.provider = auth2.provider.split("|").filter(Boolean);
573
375
  }
574
- if (typeof auth3.provider === "string") {
575
- auth3.provider = auth3.provider.split("|").filter(Boolean);
376
+ if (!auth2.strategy) {
377
+ throw new Error("Auth options needs a strategy");
576
378
  }
577
- if (!auth3.type) {
578
- throw new Error("Auth options needs a type");
379
+ if (!auth2.strategy.length) {
380
+ throw new Error("Auth options needs a strategy");
579
381
  }
580
- if (!auth3.type.length) {
581
- throw new Error("Auth options needs a type");
582
- }
583
- if (!auth3.provider || !auth3.provider.length) {
382
+ if (!auth2.provider || !auth2.provider.length) {
584
383
  throw new Error("Auth options needs a provider");
585
384
  }
586
- const providerNotFound = auth3.provider.find((p) => !providers_default[p]);
385
+ const providerNotFound = auth2.provider.find((p) => !providers_default[p]);
587
386
  if (providerNotFound) {
588
387
  throw new Error(
589
388
  `Provider "${providerNotFound}" not found, available ones are "${Object.keys(providers_default).join('", "')}"`
590
389
  );
591
390
  }
592
- if (!auth3.session && all.store) {
593
- auth3.session = all.store.prefix("auth:");
391
+ if (!auth2.session && all.store) {
392
+ auth2.session = all.store.prefix("auth:");
594
393
  }
595
- if (!auth3.store && all.store) {
596
- auth3.store = all.store.prefix("user:");
394
+ if (!auth2.store && all.store) {
395
+ auth2.store = all.store.prefix("user:");
597
396
  }
598
- if (!auth3.cleanUser) {
599
- auth3.cleanUser = (fullUser) => {
600
- const { password: _password, ...user2 } = fullUser;
601
- return user2;
397
+ if (!auth2.cleanUser) {
398
+ auth2.cleanUser = (fullUser) => {
399
+ const { password: _password, ...user } = fullUser;
400
+ return user;
602
401
  };
603
402
  }
604
- if (!auth3.redirect) {
605
- auth3.redirect = "/user";
606
- }
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
- }
403
+ if (!auth2.redirect) {
404
+ auth2.redirect = "/user";
658
405
  }
659
- };
660
- var auth_default = { load, parseOptions, middle };
406
+ return auth2;
407
+ }
661
408
 
662
409
  // src/helpers/bucket.ts
663
410
  import * as fs from "fs";
664
- import * as path from "path";
665
411
  import * as fsp from "fs/promises";
412
+ import * as path from "path";
666
413
  function thinLocalBucket(root) {
667
414
  const absolute = (name) => {
668
415
  if (!name) throw new Error("File name is required");
@@ -737,6 +484,47 @@ function bucket_default(root) {
737
484
  return root;
738
485
  }
739
486
 
487
+ // src/helpers/createId.ts
488
+ var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
489
+ var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
490
+ var cyrb53 = (str, seed = 0) => {
491
+ if (typeof str !== "string") str = String(str);
492
+ let h1 = 3735928559 ^ seed;
493
+ let h2 = 1103547991 ^ seed;
494
+ for (let i = 0, ch; i < str.length; i++) {
495
+ ch = str.charCodeAt(i);
496
+ h1 = Math.imul(h1 ^ ch, 2654435761);
497
+ h2 = Math.imul(h2 ^ ch, 1597334677);
498
+ }
499
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
500
+ h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
501
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
502
+ h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
503
+ return 4294967296 * (2097151 & h2) + (h1 >>> 0);
504
+ };
505
+ var hash2 = (str, size) => {
506
+ let chars = "";
507
+ let num = cyrb53(str);
508
+ for (let i = 0; i < size; i++) {
509
+ if (num < alphabet.length) num = cyrb53(str, i);
510
+ chars += alphabet[num % alphabet.length];
511
+ num = Math.floor(num / alphabet.length);
512
+ }
513
+ return chars;
514
+ };
515
+ var randomId = (size = 16) => {
516
+ let id = "";
517
+ const bytes = random(size);
518
+ while (size--) {
519
+ id += alphabet[bytes[size] & 61];
520
+ }
521
+ return id;
522
+ };
523
+ function createId(source, size = 16) {
524
+ if (source) return hash2(source, size);
525
+ return randomId(size);
526
+ }
527
+
740
528
  // src/helpers/color.ts
741
529
  var map = {
742
530
  reset: 0,
@@ -846,7 +634,9 @@ function config(options = {}) {
846
634
  (session2) => session2?.store?.name || "working",
847
635
  "\u{1F510}"
848
636
  );
849
- settings.auth = auth_default.parseOptions(options.auth || env2.AUTH || null, options);
637
+ if (options.auth || env2.AUTH) {
638
+ settings.auth = parseAuthOptions(options.auth || env2.AUTH || null, options);
639
+ }
850
640
  if (options.openapi) {
851
641
  if (options.openapi === true) {
852
642
  settings.openapi = {};
@@ -869,6 +659,40 @@ function cors(config2, origin = "") {
869
659
  return null;
870
660
  }
871
661
 
662
+ // src/helpers/createCookies.ts
663
+ function createCookies(cookies2) {
664
+ if (!cookies2 || !Object.keys(cookies2).length) return [];
665
+ return Object.entries(cookies2).map(([key, val]) => {
666
+ if (!val) {
667
+ val = { value: "", expires: (/* @__PURE__ */ new Date(0)).toUTCString() };
668
+ }
669
+ if (typeof val === "string") {
670
+ val = { value: val };
671
+ }
672
+ const { value, path: path2, expires } = val;
673
+ const pathPart = `;Path=${path2 || "/"}`;
674
+ const expiresPart = expires ? `;Expires=${expires}` : "";
675
+ return `${key}=${value || ""}${pathPart}${expiresPart}`;
676
+ });
677
+ }
678
+
679
+ // src/helpers/createWebsocket.ts
680
+ function createWebsocket(sockets, handlers) {
681
+ return {
682
+ message: async (socket, body) => {
683
+ handlers.socket?.filter((s) => s[1] === "message")?.map((s) => s[2]({ socket, sockets, body }));
684
+ },
685
+ open: (socket) => {
686
+ sockets.push(socket);
687
+ handlers.socket?.filter((s) => s[1] === "open")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
688
+ },
689
+ close: (socket) => {
690
+ sockets.splice(sockets.indexOf(socket), 1);
691
+ handlers.socket?.filter((s) => s[1] === "close")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
692
+ }
693
+ };
694
+ }
695
+
872
696
  // src/helpers/define.ts
873
697
  function define(obj, key, cb) {
874
698
  Object.defineProperty(obj, key, {
@@ -990,6 +814,7 @@ async function parseResponse(out, ctx) {
990
814
 
991
815
  // src/pathPattern.ts
992
816
  function pathPattern(pattern, path2) {
817
+ if (pattern === "*" && path2 === "/") return {};
993
818
  pattern = `/${pattern.replace(/^\//, "")}`;
994
819
  pattern = pattern.replace(/\/$/, "") || "/";
995
820
  path2 = path2.replace(/\/$/, "") || "/";
@@ -1062,7 +887,9 @@ function validate(ctx, schema) {
1062
887
  }
1063
888
  } catch (error) {
1064
889
  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");
890
+ const message = error.issues.map(
891
+ ({ path: path2, message: message2 }) => `[${base}.${path2.join(".")}]: ${message2}`
892
+ ).sort().join("\n");
1066
893
  throw new StatusError(message, 422);
1067
894
  }
1068
895
  throw error;
@@ -1072,9 +899,6 @@ function validate(ctx, schema) {
1072
899
  // src/helpers/handleRequest.ts
1073
900
  async function handleRequest(handlers, ctx) {
1074
901
  try {
1075
- if (ctx.error) {
1076
- throw ctx.error;
1077
- }
1078
902
  for (const [method, matcher, ...cbs] of handlers[ctx.method]) {
1079
903
  const match = pathPattern(matcher, ctx.url.pathname || "/");
1080
904
  if (!match) continue;
@@ -1090,7 +914,7 @@ async function handleRequest(handlers, ctx) {
1090
914
  }
1091
915
  if (method !== "*") break;
1092
916
  }
1093
- if (ctx.machine?.provider === "netlify") return;
917
+ if (ctx.platform.provider === "netlify") return;
1094
918
  return new Response("Not Found", { status: 404 });
1095
919
  } catch (error) {
1096
920
  return new Response(error.message || "", { status: error.status || 500 });
@@ -1101,7 +925,7 @@ async function handleRequest(handlers, ctx) {
1101
925
  import * as crypto2 from "crypto";
1102
926
  import { getRandomValues } from "crypto";
1103
927
  import { promisify } from "util";
1104
- async function hash2(password) {
928
+ async function hash(password) {
1105
929
  if ("argon2" in crypto2) {
1106
930
  const argon23 = promisify(crypto2.argon2);
1107
931
  const buf = await argon23("argon2id", {
@@ -1131,18 +955,6 @@ async function iterate(stream, cb) {
1131
955
  }
1132
956
  }
1133
957
 
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
958
  // src/helpers/iteratorAsyncToReadable.ts
1147
959
  function iteratorAsyncToReadable(asyncGenerator) {
1148
960
  return new ReadableStream({
@@ -1165,6 +977,116 @@ function iteratorAsyncToReadable(asyncGenerator) {
1165
977
  });
1166
978
  }
1167
979
 
980
+ // src/helpers/iteratorToReadable.ts
981
+ function iteratorToReadable(generator) {
982
+ return new ReadableStream({
983
+ async start(controller) {
984
+ for await (const chunk of generator) {
985
+ controller.enqueue(chunk);
986
+ }
987
+ controller.close();
988
+ }
989
+ });
990
+ }
991
+
992
+ // src/helpers/parseBody.ts
993
+ function getBoundary(header) {
994
+ if (!header) return null;
995
+ if (header.includes("multipart/form-data") && !header.includes("boundary=")) {
996
+ console.error("Do not set the `Content-Type` manually for FormData");
997
+ }
998
+ const items = header.split(";");
999
+ for (const item of items) {
1000
+ const trimmedItem = item.trim();
1001
+ if (trimmedItem.startsWith("boundary=")) {
1002
+ return trimmedItem.split("=")[1].trim();
1003
+ }
1004
+ }
1005
+ return null;
1006
+ }
1007
+ function getMatching(string, regex) {
1008
+ const matches = string.match(regex);
1009
+ return matches?.[1] ?? "";
1010
+ }
1011
+ var saveFile = async (name, value, bucket) => {
1012
+ const ext = name.split(".").pop();
1013
+ const id = `${createId()}.${ext}`;
1014
+ await bucket.write(id, value);
1015
+ return id;
1016
+ };
1017
+ function splitBuffer(buffer, delimiter) {
1018
+ const result = [];
1019
+ let start = 0;
1020
+ let index = buffer.indexOf(delimiter);
1021
+ while (index !== -1) {
1022
+ result.push(buffer.slice(start, index));
1023
+ start = index + delimiter.length;
1024
+ index = buffer.indexOf(delimiter, start);
1025
+ }
1026
+ result.push(buffer.slice(start));
1027
+ return result;
1028
+ }
1029
+ var BREAK_BUFFER = Buffer.from("\r\n\r\n");
1030
+ function isProbablyText(buffer) {
1031
+ for (let i = 0; i < Math.min(buffer.length, 512); i++) {
1032
+ const byte = buffer[i];
1033
+ if (byte === 0) return false;
1034
+ if (byte < 7 || byte > 13 && byte < 32) return false;
1035
+ }
1036
+ return true;
1037
+ }
1038
+ async function parseBody(raw, contentType, bucket) {
1039
+ const contentTypeStr = Array.isArray(contentType) ? contentType[0] : contentType;
1040
+ if (!raw) return {};
1041
+ if (!contentTypeStr || /^text\//.test(contentTypeStr)) {
1042
+ return raw.toString("utf-8");
1043
+ }
1044
+ if (/application\/json/.test(contentTypeStr)) {
1045
+ return JSON.parse(raw.toString("utf-8"));
1046
+ }
1047
+ const boundary = getBoundary(contentTypeStr);
1048
+ if (!boundary) return null;
1049
+ const body = {};
1050
+ const boundaryBuffer = Buffer.from(`--${boundary}`);
1051
+ const parts = splitBuffer(raw, boundaryBuffer);
1052
+ for (const part of parts) {
1053
+ if (part.length === 0 || part.equals(Buffer.from("--\r\n"))) continue;
1054
+ const idx = part.indexOf(BREAK_BUFFER);
1055
+ if (idx === -1) continue;
1056
+ const headerStr = part.slice(0, idx).toString("utf-8");
1057
+ const contentBuf = part.slice(idx + BREAK_BUFFER.length, part.length - 2);
1058
+ const name = getMatching(headerStr, /name="(.+?)"/).trim().replace(/\[\]$/, "");
1059
+ if (!name) continue;
1060
+ const filename = getMatching(headerStr, /filename="(.+?)"/).trim();
1061
+ if (filename) {
1062
+ if (!bucket) throw new Error("Bucket is required to save files");
1063
+ body[name] = await saveFile(filename, contentBuf, bucket);
1064
+ } else {
1065
+ const value = isProbablyText(contentBuf) ? contentBuf.toString("utf-8").trim() : contentBuf;
1066
+ if (body[name]) {
1067
+ if (!Array.isArray(body[name])) body[name] = [body[name]];
1068
+ body[name].push(value);
1069
+ } else {
1070
+ body[name] = value;
1071
+ }
1072
+ }
1073
+ }
1074
+ return body;
1075
+ }
1076
+
1077
+ // src/helpers/parseCookies.ts
1078
+ function parseCookies(cookies2) {
1079
+ if (!cookies2) return {};
1080
+ const cookieStr = Array.isArray(cookies2) ? cookies2[0] : cookies2;
1081
+ if (!cookieStr) return {};
1082
+ return Object.fromEntries(
1083
+ cookieStr.split(/;\s*/).map((part) => {
1084
+ const [key, ...rest] = part.split("=");
1085
+ return [key, decodeURIComponent(rest.join("="))];
1086
+ })
1087
+ );
1088
+ }
1089
+
1168
1090
  // src/helpers/parseHeaders.ts
1169
1091
  var parseHeaders_default = (raw) => {
1170
1092
  const headers2 = {};
@@ -1328,34 +1250,117 @@ async function verify(password, hash3) {
1328
1250
  });
1329
1251
  }
1330
1252
 
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 {
1253
+ // src/auth/findSessionId.ts
1254
+ var validateToken = (authorization) => {
1255
+ const [type2, id] = authorization.trim().split(" ");
1256
+ if (type2.toLowerCase() !== "bearer") {
1257
+ throw ServerError_default.AUTH_INVALID_HEADER({ type: type2 });
1258
+ }
1259
+ if (id.length !== 16) {
1260
+ throw ServerError_default.AUTH_INVALID_TOKEN();
1261
+ }
1262
+ return id;
1263
+ };
1264
+ var validateCookie = (authorization) => {
1265
+ if (authorization.length !== 16) {
1266
+ throw ServerError_default.AUTH_INVALID_COOKIE();
1267
+ }
1268
+ return authorization;
1269
+ };
1270
+ function findSessionId(ctx) {
1271
+ const strategy = ctx.options.auth.strategy;
1272
+ if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
1273
+ if (strategy.includes("token")) {
1274
+ if (!ctx.headers.authorization) return;
1275
+ return validateToken(ctx.headers.authorization);
1276
+ }
1277
+ if (strategy.includes("cookie")) {
1278
+ if (!ctx.cookies.authentication) return;
1279
+ return validateCookie(ctx.cookies.authentication);
1341
1280
  }
1281
+ throw new Error(`Invalid auth type "${strategy}"`);
1342
1282
  }
1343
1283
 
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;
1356
- };
1357
- function timer(ctx) {
1358
- ctx.time = createTime();
1284
+ // src/auth/getUser.ts
1285
+ async function getUser(ctx) {
1286
+ if (!ctx.options.auth) return;
1287
+ const options = ctx.options.auth;
1288
+ const sessionId = findSessionId(ctx);
1289
+ if (!sessionId) return;
1290
+ const auth2 = await options.session.get(sessionId);
1291
+ if (!auth2) return;
1292
+ if (options.strategy !== auth2.strategy) {
1293
+ throw ServerError_default.AUTH_INVALID_STRATEGY({
1294
+ strategy: auth2.strategy || "undefined",
1295
+ valid: options.strategy
1296
+ });
1297
+ }
1298
+ if (!options.provider.includes(auth2.provider)) {
1299
+ throw ServerError_default.AUTH_INVALID_PROVIDER({
1300
+ provider: auth2.provider,
1301
+ valid: options.provider
1302
+ });
1303
+ }
1304
+ const user = await ctx.options.auth.store.get(auth2.user);
1305
+ if (!user) throw ServerError_default.AUTH_NO_USER();
1306
+ user.strategy = auth2.strategy;
1307
+ user.provider = auth2.provider;
1308
+ return ctx.options.auth.cleanUser(user);
1309
+ }
1310
+
1311
+ // src/auth/logout.ts
1312
+ async function logout(ctx) {
1313
+ const session2 = findSessionId(ctx);
1314
+ const { strategy } = ctx.user;
1315
+ await ctx.options.auth.session.del(session2);
1316
+ if (!strategy) throw new Error(`Invalid strategy "${strategy}"`);
1317
+ if (strategy.includes("token")) {
1318
+ return { token: null };
1319
+ }
1320
+ if (strategy.includes("cookie")) {
1321
+ return cookies({ authorization: null }).redirect("/");
1322
+ }
1323
+ if (strategy.includes("jwt")) {
1324
+ throw new Error("JWT auth not supported yet");
1325
+ }
1326
+ if (strategy.includes("key")) {
1327
+ throw new Error("Key auth not supported yet");
1328
+ }
1329
+ throw new Error("Unknown auth type");
1330
+ }
1331
+
1332
+ // src/auth/index.ts
1333
+ function auth(app) {
1334
+ app.use(async function middle(ctx) {
1335
+ ctx.user = await getUser(ctx);
1336
+ });
1337
+ if (app.settings.auth.provider.includes("github")) {
1338
+ if (!env.GITHUB_ID) throw new Error("GITHUB_ID not defined");
1339
+ if (!env.GITHUB_SECRET) throw new Error("GITHUB_SECRET not defined");
1340
+ app.get("/auth/logout", logout);
1341
+ app.get("/auth/login/github", providers_default.github.login);
1342
+ app.get("/auth/callback/github", providers_default.github.callback);
1343
+ }
1344
+ if (app.settings.auth.provider.includes("email")) {
1345
+ app.post("/auth/logout", logout);
1346
+ app.post("/auth/register/email", providers_default.email.register);
1347
+ app.post("/auth/login/email", providers_default.email.login);
1348
+ app.put("/auth/password/email", providers_default.email.password);
1349
+ app.put("/auth/reset/email", providers_default.email.reset);
1350
+ }
1351
+ }
1352
+
1353
+ // src/middle/assets.ts
1354
+ async function assets(ctx) {
1355
+ if (!ctx.options.public) return;
1356
+ if (ctx.method !== "get") return;
1357
+ if (ctx.url.pathname === "/") return;
1358
+ try {
1359
+ const asset = await ctx.options.public.read(ctx.url.pathname);
1360
+ if (!asset) return;
1361
+ return type(ctx.url.pathname.split(".").pop()).send(asset);
1362
+ } catch {
1363
+ }
1359
1364
  }
1360
1365
 
1361
1366
  // src/middle/openapi.ts
@@ -1520,10 +1525,205 @@ var openapi_default = async (ctx) => {
1520
1525
  </html> `;
1521
1526
  };
1522
1527
 
1523
- // src/middle/index.ts
1524
- var auth2 = auth_default.middle;
1528
+ // src/middle/timer.ts
1529
+ var createTime = () => {
1530
+ const times = [["init", performance.now()]];
1531
+ const time = (name) => times.push([name, performance.now()]);
1532
+ time.times = times;
1533
+ time.headers = () => {
1534
+ const r = (t) => Math.round(t);
1535
+ const times2 = time.times;
1536
+ const timing = times2.slice(1).map(([name, time2], i) => `${name};dur=${r(time2 - times2[i][1])}`).join(", ");
1537
+ return timing;
1538
+ };
1539
+ return time;
1540
+ };
1541
+ function timer(ctx) {
1542
+ ctx.time = createTime();
1543
+ }
1544
+
1545
+ // src/auth/NoSession.ts
1546
+ var NoSession = class {
1547
+ };
1548
+ function createNoSession() {
1549
+ return new Proxy(NoSession, {
1550
+ get(target, key) {
1551
+ if (target[key]) return target[key];
1552
+ if (key === "then") return target[key];
1553
+ if (typeof key === "symbol") return target[key];
1554
+ throw ServerError_default.NO_STORE_READ({ key: String(key) });
1555
+ },
1556
+ set(target, key, value) {
1557
+ if (target[key] || key === "then" || typeof key === "symbol") {
1558
+ target[key] = value;
1559
+ return true;
1560
+ }
1561
+ throw ServerError_default.NO_STORE_WRITE({ key: String(key) });
1562
+ }
1563
+ });
1564
+ }
1565
+
1566
+ // src/auth/session.ts
1567
+ async function session(ctx) {
1568
+ const store = ctx.options.session?.store;
1569
+ if (!store) {
1570
+ ctx.session = createNoSession();
1571
+ return;
1572
+ }
1573
+ if (ctx.cookies.session) {
1574
+ const session2 = await store.get(ctx.cookies.session);
1575
+ ctx.session = session2;
1576
+ return;
1577
+ }
1578
+ }
1579
+
1580
+ // src/context/node.ts
1581
+ import { TLSSocket } from "tls";
1582
+
1583
+ // src/context/createEvents.ts
1584
+ function createEvents() {
1585
+ const events = {};
1586
+ events.on = (name, callback2) => {
1587
+ events[name] = events[name] || [];
1588
+ events[name].push(callback2);
1589
+ };
1590
+ events.trigger = (name, data) => {
1591
+ if (!events[name]) return;
1592
+ for (const cb of events[name]) {
1593
+ cb(data);
1594
+ }
1595
+ };
1596
+ return events;
1597
+ }
1598
+
1599
+ // src/context/isValidMethod.ts
1600
+ var methods = [
1601
+ "get",
1602
+ "post",
1603
+ "put",
1604
+ "patch",
1605
+ "delete",
1606
+ "head",
1607
+ "options",
1608
+ "socket"
1609
+ ];
1610
+ function isValidMethod(method) {
1611
+ return methods.includes(method);
1612
+ }
1613
+
1614
+ // src/context/node.ts
1615
+ var chunkArray = (arr) => arr.length > 2 ? [[arr[0], arr[1]], ...chunkArray(arr.slice(2))] : [arr];
1616
+ async function createNode(req, app) {
1617
+ const init = performance.now();
1618
+ const method = req.method?.toLowerCase() || "get";
1619
+ if (!isValidMethod(method)) {
1620
+ throw new Error(`Invalid HTTP method: ${method}`);
1621
+ }
1622
+ const chunks = chunkArray(req.rawHeaders);
1623
+ const headers2 = parseHeaders_default(new Headers(chunks));
1624
+ const cookies2 = parseCookies(headers2.cookie);
1625
+ const scheme = req.socket instanceof TLSSocket ? "https" : "http";
1626
+ const host = headers2.host || `localhost:${app.settings.port}`;
1627
+ const path2 = (req.url || "/").replace(/\/$/, "") || "/";
1628
+ const baseUrl = `${scheme}://${host}`;
1629
+ const url = new URL(path2, baseUrl);
1630
+ define(
1631
+ url,
1632
+ "query",
1633
+ (url2) => Object.fromEntries(url2.searchParams.entries())
1634
+ );
1635
+ const rawBody = await new Promise((resolve2, reject) => {
1636
+ const body2 = [];
1637
+ req.on("data", (chunk) => body2.push(chunk)).on("end", () => resolve2(Buffer.concat(body2))).on("error", reject);
1638
+ });
1639
+ const body = rawBody ? await parseBody(rawBody, headers2["content-type"], app.settings.uploads) : void 0;
1640
+ const events = createEvents();
1641
+ return {
1642
+ options: app.settings,
1643
+ platform: app.platform,
1644
+ url,
1645
+ method,
1646
+ body,
1647
+ headers: headers2,
1648
+ cookies: cookies2,
1649
+ init,
1650
+ events,
1651
+ app
1652
+ };
1653
+ }
1654
+
1655
+ // src/context/winter.ts
1656
+ async function createWinter(req, app) {
1657
+ const init = performance.now();
1658
+ const method = req.method.toLowerCase();
1659
+ if (!isValidMethod(method)) {
1660
+ throw new Error(`Invalid HTTP method: ${method}`);
1661
+ }
1662
+ const headers2 = parseHeaders_default(req.headers);
1663
+ const cookies2 = parseCookies(headers2.cookie);
1664
+ const baseUrl = req.url.replace(/\/$/, "") || "/";
1665
+ const url = new URL(baseUrl);
1666
+ define(
1667
+ url,
1668
+ "query",
1669
+ (url2) => Object.fromEntries(url2.searchParams.entries())
1670
+ );
1671
+ const rawBody = Buffer.from(await req.arrayBuffer());
1672
+ const body = req.body ? await parseBody(rawBody, headers2["content-type"], app.settings.uploads) : void 0;
1673
+ const events = createEvents();
1674
+ return {
1675
+ options: app.settings,
1676
+ platform: app.platform,
1677
+ url,
1678
+ method,
1679
+ body,
1680
+ headers: headers2,
1681
+ cookies: cookies2,
1682
+ init,
1683
+ events,
1684
+ app
1685
+ };
1686
+ }
1687
+
1688
+ // src/context/handlers.ts
1689
+ var Winter = async (app, request, env3) => {
1690
+ if (env3?.upgrade(request)) return;
1691
+ Object.assign(globalThis.env, env3);
1692
+ const ctx = await createWinter(request, app);
1693
+ const res = await handleRequest(app.handlers, ctx);
1694
+ ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
1695
+ return res;
1696
+ };
1697
+ var Node = async (app) => {
1698
+ const http = await import("http");
1699
+ http.createServer(async (request, response) => {
1700
+ const ctx = await createNode(request, app);
1701
+ if ("error" in ctx) throw ctx.error;
1702
+ const out = await handleRequest(app.handlers, ctx);
1703
+ response.writeHead(out.status || 200, parseHeaders_default(out.headers));
1704
+ if (out.body instanceof ReadableStream) {
1705
+ await iterate(out.body, (chunk) => response.write(chunk));
1706
+ } else {
1707
+ response.write(out.body || "");
1708
+ }
1709
+ response.end();
1710
+ }).listen(app.settings.port);
1711
+ };
1712
+ var Netlify = async (app, request, context) => {
1713
+ request.context = context;
1714
+ if (typeof Netlify === "undefined") {
1715
+ throw new Error("Netlify doesn't exist");
1716
+ }
1717
+ const ctx = await createWinter(request, app);
1718
+ const res = await handleRequest(app.handlers, ctx);
1719
+ ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
1720
+ return res;
1721
+ };
1525
1722
 
1526
1723
  // src/router.ts
1724
+ function isMiddleware(x) {
1725
+ return typeof x === "function";
1726
+ }
1527
1727
  var Router = class _Router {
1528
1728
  handlers = {
1529
1729
  socket: [],
@@ -1545,35 +1745,59 @@ var Router = class _Router {
1545
1745
  middleware.unshift(path2);
1546
1746
  path2 = "*";
1547
1747
  }
1548
- const methods = method === "*" ? Object.keys(this.handlers) : [method];
1549
- for (const m of methods) {
1748
+ const methods2 = method === "*" ? Object.keys(this.handlers) : [method];
1749
+ for (const m of methods2) {
1550
1750
  this.handlers[m].push([method, path2, ...middleware]);
1551
1751
  }
1552
1752
  return this.self();
1553
1753
  }
1554
- socket(path2, ...middleware) {
1555
- return this.handle("socket", path2, ...middleware);
1754
+ socket(pathOrMid, optionsOrMid, ...middleware) {
1755
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1756
+ return this.handle("socket", pathOrMid, optionsOrMid, ...middleware);
1757
+ }
1758
+ return this.handle("socket", pathOrMid, ...middleware);
1556
1759
  }
1557
- get(path2, ...middleware) {
1558
- return this.handle("get", path2, ...middleware);
1760
+ get(pathOrMid, optionsOrMid, ...middleware) {
1761
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1762
+ return this.handle("get", pathOrMid, optionsOrMid, ...middleware);
1763
+ }
1764
+ return this.handle("get", pathOrMid, ...middleware);
1559
1765
  }
1560
- head(path2, ...middleware) {
1561
- return this.handle("head", path2, ...middleware);
1766
+ head(pathOrMid, optionsOrMid, ...middleware) {
1767
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1768
+ return this.handle("head", pathOrMid, optionsOrMid, ...middleware);
1769
+ }
1770
+ return this.handle("head", pathOrMid, ...middleware);
1562
1771
  }
1563
- post(path2, ...middleware) {
1564
- return this.handle("post", path2, ...middleware);
1772
+ post(pathOrMid, optionsOrMid, ...middleware) {
1773
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1774
+ return this.handle("post", pathOrMid, optionsOrMid, ...middleware);
1775
+ }
1776
+ return this.handle("post", pathOrMid, ...middleware);
1565
1777
  }
1566
- put(path2, ...middleware) {
1567
- return this.handle("put", path2, ...middleware);
1778
+ put(pathOrMid, optionsOrMid, ...middleware) {
1779
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1780
+ return this.handle("put", pathOrMid, optionsOrMid, ...middleware);
1781
+ }
1782
+ return this.handle("put", pathOrMid, ...middleware);
1568
1783
  }
1569
- patch(path2, ...middleware) {
1570
- return this.handle("patch", path2, ...middleware);
1784
+ patch(pathOrMid, optionsOrMid, ...middleware) {
1785
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1786
+ return this.handle("patch", pathOrMid, optionsOrMid, ...middleware);
1787
+ }
1788
+ return this.handle("patch", pathOrMid, ...middleware);
1571
1789
  }
1572
- del(path2, ...middleware) {
1573
- return this.handle("delete", path2, ...middleware);
1790
+ del(pathOrMid, optionsOrMid, ...middleware) {
1791
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1792
+ return this.handle("delete", pathOrMid, optionsOrMid, ...middleware);
1793
+ }
1794
+ return this.handle("delete", pathOrMid, ...middleware);
1574
1795
  }
1575
- options(path2, ...middleware) {
1576
- return this.handle("options", path2, ...middleware);
1796
+ options(pathOrMid, optionsOrMid, ...middleware) {
1797
+ if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
1798
+ return this.handle("options", pathOrMid, optionsOrMid, ...middleware);
1799
+ }
1800
+ return this.handle("options", pathOrMid, ...middleware);
1577
1801
  }
1578
1802
  use(...args) {
1579
1803
  const path2 = typeof args[0] === "string" ? args.shift() : "*";
@@ -1601,6 +1825,10 @@ function isSerializable(body) {
1601
1825
  if (typeof body === "string") return false;
1602
1826
  if (body instanceof ReadableStream) return false;
1603
1827
  if (body instanceof FormData) return false;
1828
+ if (body instanceof Blob) return false;
1829
+ if (body instanceof ArrayBuffer) return false;
1830
+ if (ArrayBuffer.isView(body)) return false;
1831
+ if (body instanceof URLSearchParams) return false;
1604
1832
  return true;
1605
1833
  }
1606
1834
  function ServerTest(app) {
@@ -1641,297 +1869,14 @@ function ServerTest(app) {
1641
1869
  };
1642
1870
  }
1643
1871
 
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
1872
  // src/index.ts
1929
1873
  var Server = class extends Router {
1930
1874
  settings;
1931
1875
  platform;
1932
- port;
1933
1876
  sockets;
1934
1877
  websocket;
1878
+ // Needed to be explicit for Bun/WinterCG
1879
+ port;
1935
1880
  constructor(options = {}) {
1936
1881
  super();
1937
1882
  this.settings = config(options);
@@ -1946,12 +1891,13 @@ var Server = class extends Router {
1946
1891
  }
1947
1892
  this.use(timer);
1948
1893
  this.use(assets);
1894
+ this.use(session);
1895
+ if (this.settings.auth) {
1896
+ auth(this);
1897
+ }
1949
1898
  if (this.settings.openapi) {
1950
1899
  this.get(this.settings.openapi.path || "/docs", openapi_default);
1951
1900
  }
1952
- if (this.settings.auth) {
1953
- this.use(auth2);
1954
- }
1955
1901
  }
1956
1902
  // We need to return a function; some environment expect the default export
1957
1903
  // to be a function that is called with the request, but we also want to
@@ -1989,6 +1935,7 @@ function server(options = {}) {
1989
1935
  }
1990
1936
  export {
1991
1937
  Reply,
1938
+ Server,
1992
1939
  ServerError_default as ServerError,
1993
1940
  cookies,
1994
1941
  server as default,