@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 +828 -855
- package/package.json +4 -3
- package/src/jsx/jsx-dev-runtime.js +1 -91
- package/src/jsx/jsx-runtime.js +91 -0
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
|
|
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/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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/
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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/
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (
|
|
183
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
203
|
-
|
|
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(
|
|
119
|
+
throw new Error("Unknown auth type");
|
|
207
120
|
};
|
|
208
|
-
async function
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (!
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (!
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
190
|
+
this.res.headers.set("content-type", type2);
|
|
191
|
+
return this;
|
|
249
192
|
}
|
|
250
|
-
download(name
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
const filename = name ? `; filename="${name}"` : "";
|
|
254
|
-
return this.headers(
|
|
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(
|
|
257
|
-
if (
|
|
258
|
-
|
|
259
|
-
this
|
|
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(
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
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
|
-
|
|
277
|
-
|
|
226
|
+
return this.headers("content-type", "application/json").send(
|
|
227
|
+
JSON.stringify(body)
|
|
228
|
+
);
|
|
278
229
|
}
|
|
279
|
-
redirect(
|
|
280
|
-
return this.headers(
|
|
230
|
+
redirect(path2) {
|
|
231
|
+
return this.headers("location", path2).status(302).send();
|
|
281
232
|
}
|
|
282
|
-
async file(path2
|
|
233
|
+
async file(path2) {
|
|
283
234
|
try {
|
|
284
|
-
const fs2 = await import("fs
|
|
285
|
-
const data = await fs2.readFile(path2);
|
|
235
|
+
const fs2 = await import("fs");
|
|
286
236
|
const ext = path2.split(".").pop();
|
|
287
|
-
|
|
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 (!
|
|
307
|
-
const isHtml = body.startsWith("<");
|
|
308
|
-
|
|
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 (
|
|
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
|
-
|
|
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 {
|
|
318
|
+
const { strategy, cleanUser, store, session: session2, redirect: redirect2 } = ctx.options.auth;
|
|
485
319
|
const profile = await getUserProfile(ctx.url.query.code);
|
|
486
|
-
const
|
|
320
|
+
const auth2 = {
|
|
487
321
|
id: createId(),
|
|
488
|
-
|
|
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
|
|
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(
|
|
503
|
-
await session2.set(
|
|
504
|
-
if (
|
|
505
|
-
return status(201).json({ ...
|
|
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 (
|
|
508
|
-
return status(302).cookies({ authentication:
|
|
341
|
+
if (auth2.strategy.includes("cookie")) {
|
|
342
|
+
return status(302).cookies({ authentication: auth2.id }).redirect(redirect2);
|
|
509
343
|
}
|
|
510
|
-
if (
|
|
344
|
+
if (auth2.strategy.includes("jwt")) {
|
|
511
345
|
throw new Error("JWT auth not supported yet");
|
|
512
346
|
}
|
|
513
|
-
if (
|
|
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/
|
|
524
|
-
var
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return
|
|
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
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
-
|
|
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 "${
|
|
371
|
+
`Provider "${invalidProvider}" not found, available ones are "${providersKeys.join('", "')}"`
|
|
590
372
|
);
|
|
591
373
|
}
|
|
592
|
-
|
|
593
|
-
|
|
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 (!
|
|
596
|
-
|
|
383
|
+
if (!auth2.strategy) {
|
|
384
|
+
throw new Error("Auth options needs a strategy");
|
|
597
385
|
}
|
|
598
|
-
if (!
|
|
599
|
-
|
|
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
|
-
|
|
605
|
-
|
|
389
|
+
const strategy = auth2.strategy;
|
|
390
|
+
if (!auth2.provider || !auth2.provider.length) {
|
|
391
|
+
throw new Error("Auth options needs a provider");
|
|
606
392
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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
|
|
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/
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
if (
|
|
1335
|
-
|
|
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
|
-
|
|
1345
|
-
|
|
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
|
-
|
|
1358
|
-
|
|
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/
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
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
|
+
"&": "&",
|
|
1410
|
+
"<": "<",
|
|
1411
|
+
">": ">",
|
|
1412
|
+
'"': """
|
|
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
|
-
(
|
|
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/
|
|
1524
|
-
var
|
|
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
|
|
1549
|
-
for (const m of
|
|
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(
|
|
1555
|
-
|
|
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(
|
|
1558
|
-
|
|
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(
|
|
1561
|
-
|
|
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(
|
|
1564
|
-
|
|
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(
|
|
1567
|
-
|
|
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(
|
|
1570
|
-
|
|
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(
|
|
1573
|
-
|
|
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(
|
|
1576
|
-
|
|
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 (
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
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(
|
|
1635
|
-
head: (path2, options) => fetch2(
|
|
1636
|
-
post: (path2, body, options) => fetch2(
|
|
1637
|
-
put: (path2, body, options) => fetch2(
|
|
1638
|
-
patch: (path2, body, options) => fetch2(
|
|
1639
|
-
delete: (path2, options) => fetch2(
|
|
1640
|
-
options: (path2, options) => fetch2(
|
|
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
|
-
|
|
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
|
};
|