@kaito-http/core 3.0.0-beta.1 → 3.0.0-beta.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/dist/index.js +389 -0
- package/package.json +18 -15
- package/dist/declarations/src/error.d.ts +0 -10
- package/dist/declarations/src/index.d.ts +0 -8
- package/dist/declarations/src/req.d.ts +0 -32
- package/dist/declarations/src/res.d.ts +0 -45
- package/dist/declarations/src/route.d.ts +0 -22
- package/dist/declarations/src/router.d.ts +0 -34
- package/dist/declarations/src/server.d.ts +0 -45
- package/dist/declarations/src/util.d.ts +0 -48
- package/dist/kaito-http-core.d.ts +0 -2
- package/dist/kaito-http-core.js +0 -534
package/dist/index.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
// src/error.ts
|
|
2
|
+
var WrappedError = class _WrappedError extends Error {
|
|
3
|
+
constructor(data) {
|
|
4
|
+
super("Something was thrown, but it was not an instance of Error, so a WrappedError was created.");
|
|
5
|
+
this.data = data;
|
|
6
|
+
}
|
|
7
|
+
static maybe(maybeError) {
|
|
8
|
+
if (maybeError instanceof Error) {
|
|
9
|
+
return maybeError;
|
|
10
|
+
}
|
|
11
|
+
return _WrappedError.from(maybeError);
|
|
12
|
+
}
|
|
13
|
+
static from(data) {
|
|
14
|
+
return new _WrappedError(data);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var KaitoError = class extends Error {
|
|
18
|
+
constructor(status, message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.status = status;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/req.ts
|
|
25
|
+
import { TLSSocket } from "node:tls";
|
|
26
|
+
|
|
27
|
+
// src/util.ts
|
|
28
|
+
import { parse as parseContentType } from "content-type";
|
|
29
|
+
import { Readable } from "node:stream";
|
|
30
|
+
import { json } from "node:stream/consumers";
|
|
31
|
+
import getRawBody from "raw-body";
|
|
32
|
+
|
|
33
|
+
// src/router.ts
|
|
34
|
+
import fmw from "find-my-way";
|
|
35
|
+
|
|
36
|
+
// src/res.ts
|
|
37
|
+
import { serialize } from "cookie";
|
|
38
|
+
var KaitoResponse = class {
|
|
39
|
+
constructor(raw) {
|
|
40
|
+
this.raw = raw;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Send a response
|
|
44
|
+
* @param key The key of the header
|
|
45
|
+
* @param value The value of the header
|
|
46
|
+
* @returns The response object
|
|
47
|
+
*/
|
|
48
|
+
header(key, value) {
|
|
49
|
+
this.raw.setHeader(key, value);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Set the status code of the response
|
|
54
|
+
* @param code The status code
|
|
55
|
+
* @returns The response object
|
|
56
|
+
*/
|
|
57
|
+
status(code) {
|
|
58
|
+
this.raw.statusCode = code;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Set a cookie
|
|
63
|
+
* @param name The name of the cookie
|
|
64
|
+
* @param value The value of the cookie
|
|
65
|
+
* @param options The options for the cookie
|
|
66
|
+
* @returns The response object
|
|
67
|
+
*/
|
|
68
|
+
cookie(name, value, options) {
|
|
69
|
+
this.raw.setHeader("Set-Cookie", serialize(name, value, options));
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Send a JSON APIResponse body
|
|
74
|
+
* @param data The data to send
|
|
75
|
+
* @returns The response object
|
|
76
|
+
*/
|
|
77
|
+
json(data) {
|
|
78
|
+
const json2 = JSON.stringify(data);
|
|
79
|
+
this.raw.setHeader("Content-Type", "application/json");
|
|
80
|
+
this.raw.setHeader("Content-Length", Buffer.byteLength(json2));
|
|
81
|
+
this.raw.end(json2);
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/router.ts
|
|
87
|
+
var getSend = (res) => (status, response) => {
|
|
88
|
+
if (res.raw.headersSent) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
res.status(status).json(response);
|
|
92
|
+
};
|
|
93
|
+
var Router = class _Router {
|
|
94
|
+
routerOptions;
|
|
95
|
+
routes;
|
|
96
|
+
static create = () => new _Router([], {
|
|
97
|
+
through: async (context) => context
|
|
98
|
+
});
|
|
99
|
+
static parseQuery(schema, url) {
|
|
100
|
+
if (!schema) {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
const result = {};
|
|
104
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
105
|
+
const parsable = schema[key];
|
|
106
|
+
if (!parsable) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const parsed = parsable.parse(value);
|
|
110
|
+
result[key] = parsed;
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
static async handle(server, route, options) {
|
|
115
|
+
const send = getSend(options.res);
|
|
116
|
+
try {
|
|
117
|
+
const rootCtx = await server.getContext(options.req, options.res);
|
|
118
|
+
const ctx = await route.through(rootCtx);
|
|
119
|
+
const body = await route.body?.parse(await getBody(options.req)) ?? void 0;
|
|
120
|
+
const query = _Router.parseQuery(route.query, options.req.url);
|
|
121
|
+
const result = await route.run({
|
|
122
|
+
ctx,
|
|
123
|
+
body,
|
|
124
|
+
query,
|
|
125
|
+
params: options.params
|
|
126
|
+
});
|
|
127
|
+
if (options.res.raw.headersSent) {
|
|
128
|
+
return {
|
|
129
|
+
success: true,
|
|
130
|
+
data: result
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
send(200, {
|
|
134
|
+
success: true,
|
|
135
|
+
data: result,
|
|
136
|
+
message: "OK"
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
success: true,
|
|
140
|
+
data: result
|
|
141
|
+
};
|
|
142
|
+
} catch (e) {
|
|
143
|
+
const error = WrappedError.maybe(e);
|
|
144
|
+
if (error instanceof KaitoError) {
|
|
145
|
+
send(error.status, {
|
|
146
|
+
success: false,
|
|
147
|
+
data: null,
|
|
148
|
+
message: error.message
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const { status, message } = await server.onError({ error, req: options.req, res: options.res }).catch(() => ({ status: 500, message: "Internal Server Error" }));
|
|
153
|
+
send(status, {
|
|
154
|
+
success: false,
|
|
155
|
+
data: null,
|
|
156
|
+
message
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
data: { status, message }
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
constructor(routes, options) {
|
|
165
|
+
this.routerOptions = options;
|
|
166
|
+
this.routes = new Set(routes);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Adds a new route to the router
|
|
170
|
+
* @deprecated Use the method-specific methods instead
|
|
171
|
+
*/
|
|
172
|
+
add = (method, path, route) => {
|
|
173
|
+
const merged = {
|
|
174
|
+
...typeof route === "object" ? route : { run: route },
|
|
175
|
+
method,
|
|
176
|
+
path,
|
|
177
|
+
through: this.routerOptions.through
|
|
178
|
+
};
|
|
179
|
+
return new _Router([...this.routes, merged], this.routerOptions);
|
|
180
|
+
};
|
|
181
|
+
merge = (pathPrefix, other) => {
|
|
182
|
+
const newRoutes = [...other.routes].map((route) => ({
|
|
183
|
+
...route,
|
|
184
|
+
path: `${pathPrefix}${route.path}`
|
|
185
|
+
}));
|
|
186
|
+
return new _Router(
|
|
187
|
+
[...this.routes, ...newRoutes],
|
|
188
|
+
this.routerOptions
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
// Allow for any server context to be passed
|
|
192
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
193
|
+
freeze = (server) => {
|
|
194
|
+
const instance = fmw({
|
|
195
|
+
ignoreTrailingSlash: true,
|
|
196
|
+
async defaultRoute(req, serverResponse) {
|
|
197
|
+
const res = new KaitoResponse(serverResponse);
|
|
198
|
+
const message = `Cannot ${req.method} ${req.url ?? "/"}`;
|
|
199
|
+
getSend(res)(404, {
|
|
200
|
+
success: false,
|
|
201
|
+
data: null,
|
|
202
|
+
message
|
|
203
|
+
});
|
|
204
|
+
return {
|
|
205
|
+
success: false,
|
|
206
|
+
data: { status: 404, message }
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
for (const route of this.routes) {
|
|
211
|
+
const handler = async (incomingMessage, serverResponse, params) => {
|
|
212
|
+
const req = new KaitoRequest(incomingMessage);
|
|
213
|
+
const res = new KaitoResponse(serverResponse);
|
|
214
|
+
return _Router.handle(server, route, {
|
|
215
|
+
params,
|
|
216
|
+
req,
|
|
217
|
+
res
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
if (route.method === "*") {
|
|
221
|
+
instance.all(route.path, handler);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
instance.on(route.method, route.path, handler);
|
|
225
|
+
}
|
|
226
|
+
return instance;
|
|
227
|
+
};
|
|
228
|
+
method = (method) => (path, route) => {
|
|
229
|
+
return this.add(method, path, route);
|
|
230
|
+
};
|
|
231
|
+
get = this.method("GET");
|
|
232
|
+
post = this.method("POST");
|
|
233
|
+
put = this.method("PUT");
|
|
234
|
+
patch = this.method("PATCH");
|
|
235
|
+
delete = this.method("DELETE");
|
|
236
|
+
head = this.method("HEAD");
|
|
237
|
+
options = this.method("OPTIONS");
|
|
238
|
+
through = (transform) => new _Router(this.routes, {
|
|
239
|
+
through: async (context) => {
|
|
240
|
+
const fromCurrentRouter = await this.routerOptions.through(context);
|
|
241
|
+
return transform(fromCurrentRouter);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// src/util.ts
|
|
247
|
+
function createGetContext(callback) {
|
|
248
|
+
return callback;
|
|
249
|
+
}
|
|
250
|
+
function createUtilities(getContext) {
|
|
251
|
+
return {
|
|
252
|
+
getContext,
|
|
253
|
+
router: () => Router.create()
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function getLastEntryInMultiHeaderValue(headerValue) {
|
|
257
|
+
const normalized = Array.isArray(headerValue) ? headerValue.join(",") : headerValue;
|
|
258
|
+
const lastIndex = normalized.lastIndexOf(",");
|
|
259
|
+
return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
|
|
260
|
+
}
|
|
261
|
+
async function getBody(req) {
|
|
262
|
+
if (!req.headers["content-type"]) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const buffer = await getRawBody(req.raw);
|
|
266
|
+
const { type } = parseContentType(req.headers["content-type"]);
|
|
267
|
+
switch (type) {
|
|
268
|
+
case "application/json": {
|
|
269
|
+
return json(Readable.from(buffer));
|
|
270
|
+
}
|
|
271
|
+
default: {
|
|
272
|
+
if (process.env.NODE_ENV === "development") {
|
|
273
|
+
console.warn("[kaito] Unsupported content type:", type);
|
|
274
|
+
console.warn("[kaito] This message is only shown in development mode.");
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/req.ts
|
|
282
|
+
var KaitoRequest = class {
|
|
283
|
+
constructor(raw) {
|
|
284
|
+
this.raw = raw;
|
|
285
|
+
}
|
|
286
|
+
_url = null;
|
|
287
|
+
/**
|
|
288
|
+
* The full URL of the request, including the protocol, hostname, and path.
|
|
289
|
+
* Note: does not include the query string or hash
|
|
290
|
+
*/
|
|
291
|
+
get fullURL() {
|
|
292
|
+
return `${this.protocol}://${this.hostname}${this.raw.url ?? ""}`;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* A new URL instance for the full URL of the request.
|
|
296
|
+
*/
|
|
297
|
+
get url() {
|
|
298
|
+
if (this._url) {
|
|
299
|
+
return this._url;
|
|
300
|
+
}
|
|
301
|
+
this._url = new URL(this.fullURL);
|
|
302
|
+
return this._url;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* The HTTP method of the request.
|
|
306
|
+
*/
|
|
307
|
+
get method() {
|
|
308
|
+
if (!this.raw.method) {
|
|
309
|
+
throw new Error("Request method is not defined, somehow...");
|
|
310
|
+
}
|
|
311
|
+
return this.raw.method;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The protocol of the request, either `http` or `https`.
|
|
315
|
+
*/
|
|
316
|
+
get protocol() {
|
|
317
|
+
if (this.raw.socket instanceof TLSSocket) {
|
|
318
|
+
return this.raw.socket.encrypted ? "https" : "http";
|
|
319
|
+
}
|
|
320
|
+
return "http";
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The request headers
|
|
324
|
+
*/
|
|
325
|
+
get headers() {
|
|
326
|
+
return this.raw.headers;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The hostname of the request.
|
|
330
|
+
*/
|
|
331
|
+
get hostname() {
|
|
332
|
+
return this.raw.headers.host ?? getLastEntryInMultiHeaderValue(this.raw.headers[":authority"] ?? []);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// src/server.ts
|
|
337
|
+
import * as http from "node:http";
|
|
338
|
+
function createFMWServer(config) {
|
|
339
|
+
const router = config.router.freeze(config);
|
|
340
|
+
const rawRoutes = config.rawRoutes ?? {};
|
|
341
|
+
for (const method in rawRoutes) {
|
|
342
|
+
if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
const routes = rawRoutes[method];
|
|
346
|
+
if (!routes || routes.length === 0) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
for (const route of routes) {
|
|
350
|
+
if (method === "*") {
|
|
351
|
+
router.all(route.path, route.handler);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
router[method.toLowerCase()](route.path, route.handler);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const server = http.createServer(async (req, res) => {
|
|
358
|
+
let before;
|
|
359
|
+
if (config.before) {
|
|
360
|
+
before = await config.before(req, res);
|
|
361
|
+
} else {
|
|
362
|
+
before = void 0;
|
|
363
|
+
}
|
|
364
|
+
if (res.headersSent) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const result = await router.lookup(req, res);
|
|
368
|
+
if ("after" in config && config.after) {
|
|
369
|
+
await config.after(before, result);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
return { server, fmw: router };
|
|
373
|
+
}
|
|
374
|
+
function createServer2(config) {
|
|
375
|
+
return createFMWServer(config).server;
|
|
376
|
+
}
|
|
377
|
+
export {
|
|
378
|
+
KaitoError,
|
|
379
|
+
KaitoRequest,
|
|
380
|
+
KaitoResponse,
|
|
381
|
+
Router,
|
|
382
|
+
WrappedError,
|
|
383
|
+
createFMWServer,
|
|
384
|
+
createGetContext,
|
|
385
|
+
createServer2 as createServer,
|
|
386
|
+
createUtilities,
|
|
387
|
+
getBody,
|
|
388
|
+
getLastEntryInMultiHeaderValue
|
|
389
|
+
};
|
package/package.json
CHANGED
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaito-http/core",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "Alistair Smith <hi@alistair.sh>",
|
|
4
6
|
"description": "Functional HTTP Framework for TypeScript",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup"
|
|
9
|
+
},
|
|
5
10
|
"exports": {
|
|
6
|
-
".": "./
|
|
7
|
-
"
|
|
11
|
+
"./package.json": "./package.json",
|
|
12
|
+
".": "./dist/index.js"
|
|
8
13
|
},
|
|
14
|
+
"homepage": "https://github.com/kaito-http/kaito",
|
|
9
15
|
"repository": "https://github.com/kaito-http/kaito",
|
|
10
|
-
"
|
|
16
|
+
"keywords": [
|
|
17
|
+
"typescript",
|
|
18
|
+
"http",
|
|
19
|
+
"framework"
|
|
20
|
+
],
|
|
11
21
|
"license": "MIT",
|
|
12
|
-
"type": "module",
|
|
13
22
|
"devDependencies": {
|
|
14
|
-
"@types/content-type": "^1.1.
|
|
15
|
-
"@types/cookie": "^0.
|
|
16
|
-
"@types/node": "^
|
|
23
|
+
"@types/content-type": "^1.1.8",
|
|
24
|
+
"@types/cookie": "^0.6.0",
|
|
25
|
+
"@types/node": "^22.7.4",
|
|
17
26
|
"typescript": "^5.6.2"
|
|
18
27
|
},
|
|
19
28
|
"files": [
|
|
20
29
|
"package.json",
|
|
21
|
-
"
|
|
30
|
+
"README.md",
|
|
22
31
|
"dist"
|
|
23
32
|
],
|
|
24
33
|
"bugs": {
|
|
25
34
|
"url": "https://github.com/kaito-http/kaito/issues"
|
|
26
35
|
},
|
|
27
|
-
"homepage": "https://github.com/kaito-http/kaito",
|
|
28
|
-
"keywords": [
|
|
29
|
-
"typescript",
|
|
30
|
-
"http",
|
|
31
|
-
"framework"
|
|
32
|
-
],
|
|
33
36
|
"dependencies": {
|
|
34
37
|
"content-type": "^1.0.5",
|
|
35
38
|
"cookie": "^0.6.0",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare class WrappedError<T> extends Error {
|
|
2
|
-
readonly data: T;
|
|
3
|
-
static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
|
|
4
|
-
static from<T>(data: T): WrappedError<T>;
|
|
5
|
-
private constructor();
|
|
6
|
-
}
|
|
7
|
-
export declare class KaitoError extends Error {
|
|
8
|
-
readonly status: number;
|
|
9
|
-
constructor(status: number, message: string);
|
|
10
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { HTTPMethod } from 'find-my-way';
|
|
2
|
-
import type { IncomingMessage } from 'node:http';
|
|
3
|
-
export declare class KaitoRequest {
|
|
4
|
-
readonly raw: IncomingMessage;
|
|
5
|
-
private _url;
|
|
6
|
-
constructor(raw: IncomingMessage);
|
|
7
|
-
/**
|
|
8
|
-
* The full URL of the request, including the protocol, hostname, and path.
|
|
9
|
-
* Note: does not include the query string or hash
|
|
10
|
-
*/
|
|
11
|
-
get fullURL(): string;
|
|
12
|
-
/**
|
|
13
|
-
* A new URL instance for the full URL of the request.
|
|
14
|
-
*/
|
|
15
|
-
get url(): URL;
|
|
16
|
-
/**
|
|
17
|
-
* The HTTP method of the request.
|
|
18
|
-
*/
|
|
19
|
-
get method(): HTTPMethod;
|
|
20
|
-
/**
|
|
21
|
-
* The protocol of the request, either `http` or `https`.
|
|
22
|
-
*/
|
|
23
|
-
get protocol(): 'http' | 'https';
|
|
24
|
-
/**
|
|
25
|
-
* The request headers
|
|
26
|
-
*/
|
|
27
|
-
get headers(): import("http").IncomingHttpHeaders;
|
|
28
|
-
/**
|
|
29
|
-
* The hostname of the request.
|
|
30
|
-
*/
|
|
31
|
-
get hostname(): string;
|
|
32
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { ServerResponse } from 'node:http';
|
|
2
|
-
import type { CookieSerializeOptions } from 'cookie';
|
|
3
|
-
export type ErroredAPIResponse = {
|
|
4
|
-
success: false;
|
|
5
|
-
data: null;
|
|
6
|
-
message: string;
|
|
7
|
-
};
|
|
8
|
-
export type SuccessfulAPIResponse<T> = {
|
|
9
|
-
success: true;
|
|
10
|
-
data: T;
|
|
11
|
-
message: 'OK';
|
|
12
|
-
};
|
|
13
|
-
export type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
|
|
14
|
-
export type AnyResponse = APIResponse<unknown>;
|
|
15
|
-
export declare class KaitoResponse<T = unknown> {
|
|
16
|
-
readonly raw: ServerResponse;
|
|
17
|
-
constructor(raw: ServerResponse);
|
|
18
|
-
/**
|
|
19
|
-
* Send a response
|
|
20
|
-
* @param key The key of the header
|
|
21
|
-
* @param value The value of the header
|
|
22
|
-
* @returns The response object
|
|
23
|
-
*/
|
|
24
|
-
header(key: string, value: string | readonly string[]): this;
|
|
25
|
-
/**
|
|
26
|
-
* Set the status code of the response
|
|
27
|
-
* @param code The status code
|
|
28
|
-
* @returns The response object
|
|
29
|
-
*/
|
|
30
|
-
status(code: number): this;
|
|
31
|
-
/**
|
|
32
|
-
* Set a cookie
|
|
33
|
-
* @param name The name of the cookie
|
|
34
|
-
* @param value The value of the cookie
|
|
35
|
-
* @param options The options for the cookie
|
|
36
|
-
* @returns The response object
|
|
37
|
-
*/
|
|
38
|
-
cookie(name: string, value: string, options: CookieSerializeOptions): this;
|
|
39
|
-
/**
|
|
40
|
-
* Send a JSON APIResponse body
|
|
41
|
-
* @param data The data to send
|
|
42
|
-
* @returns The response object
|
|
43
|
-
*/
|
|
44
|
-
json(data: APIResponse<T>): this;
|
|
45
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ExtractRouteParams, KaitoMethod, Parsable } from "./util.js";
|
|
2
|
-
export type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
|
|
3
|
-
ctx: Context;
|
|
4
|
-
body: BodyOutput;
|
|
5
|
-
query: QueryOutput;
|
|
6
|
-
params: ExtractRouteParams<Path>;
|
|
7
|
-
};
|
|
8
|
-
export type AnyQueryDefinition = Record<string, Parsable<any>>;
|
|
9
|
-
export type InferQuery<T extends AnyQueryDefinition> = {
|
|
10
|
-
[Key in keyof T]: InferParsable<T[Key]>;
|
|
11
|
-
};
|
|
12
|
-
export type InferParsable<T> = T extends Parsable<infer U> ? U : never;
|
|
13
|
-
export type RouteRunner<Result, Path extends string, Context, QueryOutput, BodyOutput> = (args: RouteArgument<Path, Context, QueryOutput, BodyOutput>) => Promise<Result>;
|
|
14
|
-
export type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, BodyOutput> = {
|
|
15
|
-
through: (context: ContextFrom) => Promise<ContextTo>;
|
|
16
|
-
body?: Parsable<BodyOutput>;
|
|
17
|
-
query?: Query;
|
|
18
|
-
path: Path;
|
|
19
|
-
method: Method;
|
|
20
|
-
run(arg: RouteArgument<Path, ContextTo, InferQuery<Query>, BodyOutput>): Promise<Result>;
|
|
21
|
-
};
|
|
22
|
-
export type AnyRoute<FromContext = any, ToContext = any> = Route<FromContext, ToContext, any, any, any, AnyQueryDefinition, any>;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import fmw from 'find-my-way';
|
|
2
|
-
import type { AnyQueryDefinition, AnyRoute, Route } from "./route.js";
|
|
3
|
-
import type { ServerConfig } from "./server.js";
|
|
4
|
-
import type { KaitoMethod } from "./util.js";
|
|
5
|
-
type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextFrom, infer ContextTo, infer Result, infer Path, infer Method, infer Query, infer BodyOutput> ? Route<ContextFrom, ContextTo, Result, `${Prefix}${Path}`, Method, Query, BodyOutput> : never;
|
|
6
|
-
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
7
|
-
export type RouterOptions<ContextFrom, ContextTo> = {
|
|
8
|
-
through: (context: ContextFrom) => Promise<ContextTo>;
|
|
9
|
-
};
|
|
10
|
-
export declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
|
|
11
|
-
private readonly routerOptions;
|
|
12
|
-
readonly routes: Set<R>;
|
|
13
|
-
static create: <Context>() => Router<Context, Context, never>;
|
|
14
|
-
private static parseQuery;
|
|
15
|
-
private static handle;
|
|
16
|
-
constructor(routes: Iterable<R>, options: RouterOptions<ContextFrom, ContextTo>);
|
|
17
|
-
/**
|
|
18
|
-
* Adds a new route to the router
|
|
19
|
-
* @deprecated Use the method-specific methods instead
|
|
20
|
-
*/
|
|
21
|
-
add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, BodyOutput = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, BodyOutput>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, BodyOutput>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, BodyOutput>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, Method, Query, BodyOutput>>;
|
|
22
|
-
readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
|
|
23
|
-
freeze: (server: ServerConfig<ContextFrom, any>) => fmw.Instance<fmw.HTTPVersion.V1>;
|
|
24
|
-
private readonly method;
|
|
25
|
-
get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never>(path: Path, route: ((arg: import("./route.js").RouteArgument<Path, ContextTo, import("./route.js").InferQuery<Query>, BodyOutput>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, 'GET', Query, BodyOutput>, 'body' | 'path' | 'method' | 'through'>) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, 'GET', Query, BodyOutput>>;
|
|
26
|
-
post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never>(path: Path, route: ((arg: import("./route.js").RouteArgument<Path, ContextTo, import("./route.js").InferQuery<Query>, BodyOutput>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, BodyOutput>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "POST", Query, BodyOutput>>;
|
|
27
|
-
put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never>(path: Path, route: ((arg: import("./route.js").RouteArgument<Path, ContextTo, import("./route.js").InferQuery<Query>, BodyOutput>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, BodyOutput>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, BodyOutput>>;
|
|
28
|
-
patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never>(path: Path, route: ((arg: import("./route.js").RouteArgument<Path, ContextTo, import("./route.js").InferQuery<Query>, BodyOutput>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, BodyOutput>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, BodyOutput>>;
|
|
29
|
-
delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never>(path: Path, route: ((arg: import("./route.js").RouteArgument<Path, ContextTo, import("./route.js").InferQuery<Query>, BodyOutput>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, BodyOutput>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, BodyOutput>>;
|
|
30
|
-
head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never>(path: Path, route: ((arg: import("./route.js").RouteArgument<Path, ContextTo, import("./route.js").InferQuery<Query>, BodyOutput>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, BodyOutput>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, BodyOutput>>;
|
|
31
|
-
options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never>(path: Path, route: ((arg: import("./route.js").RouteArgument<Path, ContextTo, import("./route.js").InferQuery<Query>, BodyOutput>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, BodyOutput>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, BodyOutput>>;
|
|
32
|
-
through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
|
|
33
|
-
}
|
|
34
|
-
export {};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as http from 'node:http';
|
|
2
|
-
import type { KaitoError } from "./error.js";
|
|
3
|
-
import type { KaitoRequest } from "./req.js";
|
|
4
|
-
import type { KaitoResponse } from "./res.js";
|
|
5
|
-
import type { Router } from "./router.js";
|
|
6
|
-
import type { GetContext, KaitoMethod } from "./util.js";
|
|
7
|
-
export type Before<BeforeAfterContext> = (req: http.IncomingMessage, res: http.ServerResponse) => Promise<BeforeAfterContext>;
|
|
8
|
-
export type HandlerResult = {
|
|
9
|
-
success: true;
|
|
10
|
-
data: unknown;
|
|
11
|
-
} | {
|
|
12
|
-
success: false;
|
|
13
|
-
data: {
|
|
14
|
-
status: number;
|
|
15
|
-
message: string;
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
export type After<BeforeAfterContext> = (ctx: BeforeAfterContext, result: HandlerResult) => Promise<void>;
|
|
19
|
-
export type ServerConfigWithBefore<BeforeAfterContext> = {
|
|
20
|
-
before: Before<BeforeAfterContext>;
|
|
21
|
-
after?: After<BeforeAfterContext>;
|
|
22
|
-
} | {
|
|
23
|
-
before?: undefined;
|
|
24
|
-
};
|
|
25
|
-
export type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
|
|
26
|
-
router: Router<ContextFrom, unknown, any>;
|
|
27
|
-
getContext: GetContext<ContextFrom>;
|
|
28
|
-
rawRoutes?: Partial<Record<KaitoMethod, Array<{
|
|
29
|
-
path: string;
|
|
30
|
-
handler: (request: http.IncomingMessage, response: http.ServerResponse) => unknown;
|
|
31
|
-
}>>>;
|
|
32
|
-
onError(arg: {
|
|
33
|
-
error: Error;
|
|
34
|
-
req: KaitoRequest;
|
|
35
|
-
res: KaitoResponse;
|
|
36
|
-
}): Promise<KaitoError | {
|
|
37
|
-
status: number;
|
|
38
|
-
message: string;
|
|
39
|
-
}>;
|
|
40
|
-
};
|
|
41
|
-
export declare function createFMWServer<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): {
|
|
42
|
-
readonly server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
43
|
-
readonly fmw: import("find-my-way").Instance<import("find-my-way").HTTPVersion.V1>;
|
|
44
|
-
};
|
|
45
|
-
export declare function createServer<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { HTTPMethod } from 'find-my-way';
|
|
2
|
-
import type { KaitoRequest } from "./req.js";
|
|
3
|
-
import type { KaitoResponse } from "./res.js";
|
|
4
|
-
import { Router } from "./router.js";
|
|
5
|
-
export type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
|
|
6
|
-
[k in Param | keyof ExtractRouteParams<Rest>]: string;
|
|
7
|
-
} : T extends `${string}:${infer Param}` ? {
|
|
8
|
-
[k in Param]: string;
|
|
9
|
-
} : {};
|
|
10
|
-
export type KaitoMethod = HTTPMethod | '*';
|
|
11
|
-
export type GetContext<Result> = (req: KaitoRequest, res: KaitoResponse) => Promise<Result>;
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated use `createUtilities` instead
|
|
14
|
-
*/
|
|
15
|
-
export declare function createGetContext<Context>(callback: GetContext<Context>): GetContext<Context>;
|
|
16
|
-
/**
|
|
17
|
-
* A helper function to create typed necessary functions
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* const {router, getContext} = createUtilities(async (req, res) => {
|
|
22
|
-
* // Return context here
|
|
23
|
-
* })
|
|
24
|
-
*
|
|
25
|
-
* const app = router().get('/', async () => "hello");
|
|
26
|
-
*
|
|
27
|
-
* const server = createServer({
|
|
28
|
-
* router: app,
|
|
29
|
-
* getContext,
|
|
30
|
-
* // ...
|
|
31
|
-
* });
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
export declare function createUtilities<Context>(getContext: GetContext<Context>): {
|
|
35
|
-
getContext: GetContext<Context>;
|
|
36
|
-
router: () => Router<Context, Context, never>;
|
|
37
|
-
};
|
|
38
|
-
export type InferContext<T> = T extends (req: KaitoRequest, res: KaitoResponse) => Promise<infer U> ? U : never;
|
|
39
|
-
export declare function getLastEntryInMultiHeaderValue(headerValue: string | string[]): string;
|
|
40
|
-
export interface Parsable<T> {
|
|
41
|
-
parse: (value: unknown) => T;
|
|
42
|
-
}
|
|
43
|
-
export type RemoveEndSlashes<T extends string> = T extends `${infer U}/` ? U : T;
|
|
44
|
-
export type AddStartSlashes<T extends string> = T extends `/${infer U}` ? `/${U}` : `/${T}`;
|
|
45
|
-
export type NormalizePath<T extends string> = AddStartSlashes<RemoveEndSlashes<T>>;
|
|
46
|
-
export type Values<T> = T[keyof T];
|
|
47
|
-
export type NoEmpty<T> = [keyof T] extends [never] ? never : T;
|
|
48
|
-
export declare function getBody(req: KaitoRequest): Promise<unknown>;
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from "./declarations/src/index";
|
|
2
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2FpdG8taHR0cC1jb3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuL2RlY2xhcmF0aW9ucy9zcmMvaW5kZXguZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSJ9
|
package/dist/kaito-http-core.js
DELETED
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
import { TLSSocket } from 'node:tls';
|
|
2
|
-
import { parse } from 'content-type';
|
|
3
|
-
import { Readable } from 'node:stream';
|
|
4
|
-
import { json } from 'node:stream/consumers';
|
|
5
|
-
import getRawBody from 'raw-body';
|
|
6
|
-
import fmw from 'find-my-way';
|
|
7
|
-
import { serialize } from 'cookie';
|
|
8
|
-
import * as http from 'node:http';
|
|
9
|
-
|
|
10
|
-
class WrappedError extends Error {
|
|
11
|
-
static maybe(maybeError) {
|
|
12
|
-
if (maybeError instanceof Error) {
|
|
13
|
-
return maybeError;
|
|
14
|
-
}
|
|
15
|
-
return WrappedError.from(maybeError);
|
|
16
|
-
}
|
|
17
|
-
static from(data) {
|
|
18
|
-
return new WrappedError(data);
|
|
19
|
-
}
|
|
20
|
-
constructor(data) {
|
|
21
|
-
super('Something was thrown, but it was not an instance of Error, so a WrappedError was created.');
|
|
22
|
-
this.data = data;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
class KaitoError extends Error {
|
|
26
|
-
constructor(status, message) {
|
|
27
|
-
super(message);
|
|
28
|
-
this.status = status;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function _defineProperty(obj, key, value) {
|
|
33
|
-
if (key in obj) {
|
|
34
|
-
Object.defineProperty(obj, key, {
|
|
35
|
-
value: value,
|
|
36
|
-
enumerable: true,
|
|
37
|
-
configurable: true,
|
|
38
|
-
writable: true
|
|
39
|
-
});
|
|
40
|
-
} else {
|
|
41
|
-
obj[key] = value;
|
|
42
|
-
}
|
|
43
|
-
return obj;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
47
|
-
try {
|
|
48
|
-
var info = gen[key](arg);
|
|
49
|
-
var value = info.value;
|
|
50
|
-
} catch (error) {
|
|
51
|
-
reject(error);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (info.done) {
|
|
55
|
-
resolve(value);
|
|
56
|
-
} else {
|
|
57
|
-
Promise.resolve(value).then(_next, _throw);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function _asyncToGenerator(fn) {
|
|
61
|
-
return function () {
|
|
62
|
-
var self = this,
|
|
63
|
-
args = arguments;
|
|
64
|
-
return new Promise(function (resolve, reject) {
|
|
65
|
-
var gen = fn.apply(self, args);
|
|
66
|
-
function _next(value) {
|
|
67
|
-
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
68
|
-
}
|
|
69
|
-
function _throw(err) {
|
|
70
|
-
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
71
|
-
}
|
|
72
|
-
_next(undefined);
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function ownKeys(object, enumerableOnly) {
|
|
78
|
-
var keys = Object.keys(object);
|
|
79
|
-
if (Object.getOwnPropertySymbols) {
|
|
80
|
-
var symbols = Object.getOwnPropertySymbols(object);
|
|
81
|
-
enumerableOnly && (symbols = symbols.filter(function (sym) {
|
|
82
|
-
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
83
|
-
})), keys.push.apply(keys, symbols);
|
|
84
|
-
}
|
|
85
|
-
return keys;
|
|
86
|
-
}
|
|
87
|
-
function _objectSpread2(target) {
|
|
88
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
89
|
-
var source = null != arguments[i] ? arguments[i] : {};
|
|
90
|
-
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
|
|
91
|
-
_defineProperty(target, key, source[key]);
|
|
92
|
-
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
|
|
93
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return target;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
class KaitoResponse {
|
|
100
|
-
constructor(raw) {
|
|
101
|
-
this.raw = raw;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Send a response
|
|
106
|
-
* @param key The key of the header
|
|
107
|
-
* @param value The value of the header
|
|
108
|
-
* @returns The response object
|
|
109
|
-
*/
|
|
110
|
-
header(key, value) {
|
|
111
|
-
this.raw.setHeader(key, value);
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Set the status code of the response
|
|
117
|
-
* @param code The status code
|
|
118
|
-
* @returns The response object
|
|
119
|
-
*/
|
|
120
|
-
status(code) {
|
|
121
|
-
this.raw.statusCode = code;
|
|
122
|
-
return this;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Set a cookie
|
|
127
|
-
* @param name The name of the cookie
|
|
128
|
-
* @param value The value of the cookie
|
|
129
|
-
* @param options The options for the cookie
|
|
130
|
-
* @returns The response object
|
|
131
|
-
*/
|
|
132
|
-
cookie(name, value, options) {
|
|
133
|
-
this.raw.setHeader('Set-Cookie', serialize(name, value, options));
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Send a JSON APIResponse body
|
|
139
|
-
* @param data The data to send
|
|
140
|
-
* @returns The response object
|
|
141
|
-
*/
|
|
142
|
-
json(data) {
|
|
143
|
-
var json = JSON.stringify(data);
|
|
144
|
-
this.raw.setHeader('Content-Type', 'application/json');
|
|
145
|
-
this.raw.setHeader('Content-Length', Buffer.byteLength(json));
|
|
146
|
-
this.raw.end(json);
|
|
147
|
-
return this;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
var _Router;
|
|
152
|
-
var getSend = res => (status, response) => {
|
|
153
|
-
if (res.raw.headersSent) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
res.status(status).json(response);
|
|
157
|
-
};
|
|
158
|
-
class Router {
|
|
159
|
-
static parseQuery(schema, url) {
|
|
160
|
-
if (!schema) {
|
|
161
|
-
return {};
|
|
162
|
-
}
|
|
163
|
-
var result = {};
|
|
164
|
-
for (var [key, value] of url.searchParams.entries()) {
|
|
165
|
-
var parsable = schema[key];
|
|
166
|
-
if (!parsable) {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
var parsed = parsable.parse(value);
|
|
170
|
-
result[key] = parsed;
|
|
171
|
-
}
|
|
172
|
-
return result;
|
|
173
|
-
}
|
|
174
|
-
static handle(
|
|
175
|
-
// Allow for any server to be passed
|
|
176
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
177
|
-
server, route, options) {
|
|
178
|
-
return _asyncToGenerator(function* () {
|
|
179
|
-
var send = getSend(options.res);
|
|
180
|
-
try {
|
|
181
|
-
var _yield$route$body$par, _route$body;
|
|
182
|
-
var rootCtx = yield server.getContext(options.req, options.res);
|
|
183
|
-
var ctx = yield route.through(rootCtx);
|
|
184
|
-
var body = (_yield$route$body$par = yield (_route$body = route.body) === null || _route$body === void 0 ? void 0 : _route$body.parse(yield getBody(options.req))) !== null && _yield$route$body$par !== void 0 ? _yield$route$body$par : undefined;
|
|
185
|
-
var query = Router.parseQuery(route.query, options.req.url);
|
|
186
|
-
var result = yield route.run({
|
|
187
|
-
ctx,
|
|
188
|
-
body,
|
|
189
|
-
query,
|
|
190
|
-
params: options.params
|
|
191
|
-
});
|
|
192
|
-
if (options.res.raw.headersSent) {
|
|
193
|
-
return {
|
|
194
|
-
success: true,
|
|
195
|
-
data: result
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
send(200, {
|
|
199
|
-
success: true,
|
|
200
|
-
data: result,
|
|
201
|
-
message: 'OK'
|
|
202
|
-
});
|
|
203
|
-
return {
|
|
204
|
-
success: true,
|
|
205
|
-
data: result
|
|
206
|
-
};
|
|
207
|
-
} catch (e) {
|
|
208
|
-
var error = WrappedError.maybe(e);
|
|
209
|
-
if (error instanceof KaitoError) {
|
|
210
|
-
send(error.status, {
|
|
211
|
-
success: false,
|
|
212
|
-
data: null,
|
|
213
|
-
message: error.message
|
|
214
|
-
});
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
var {
|
|
218
|
-
status,
|
|
219
|
-
message
|
|
220
|
-
} = yield server.onError({
|
|
221
|
-
error,
|
|
222
|
-
req: options.req,
|
|
223
|
-
res: options.res
|
|
224
|
-
}).catch(() => ({
|
|
225
|
-
status: 500,
|
|
226
|
-
message: 'Internal Server Error'
|
|
227
|
-
}));
|
|
228
|
-
send(status, {
|
|
229
|
-
success: false,
|
|
230
|
-
data: null,
|
|
231
|
-
message
|
|
232
|
-
});
|
|
233
|
-
return {
|
|
234
|
-
success: false,
|
|
235
|
-
data: {
|
|
236
|
-
status,
|
|
237
|
-
message
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
})();
|
|
242
|
-
}
|
|
243
|
-
constructor(routes, options) {
|
|
244
|
-
var _this = this;
|
|
245
|
-
/**
|
|
246
|
-
* Adds a new route to the router
|
|
247
|
-
* @deprecated Use the method-specific methods instead
|
|
248
|
-
*/
|
|
249
|
-
_defineProperty(this, "add", (method, path, route) => {
|
|
250
|
-
var merged = _objectSpread2(_objectSpread2({}, typeof route === 'object' ? route : {
|
|
251
|
-
run: route
|
|
252
|
-
}), {}, {
|
|
253
|
-
method,
|
|
254
|
-
path,
|
|
255
|
-
through: this.routerOptions.through
|
|
256
|
-
});
|
|
257
|
-
return new Router([...this.routes, merged], this.routerOptions);
|
|
258
|
-
});
|
|
259
|
-
_defineProperty(this, "merge", (pathPrefix, other) => {
|
|
260
|
-
var newRoutes = [...other.routes].map(route => _objectSpread2(_objectSpread2({}, route), {}, {
|
|
261
|
-
path: "".concat(pathPrefix).concat(route.path)
|
|
262
|
-
}));
|
|
263
|
-
return new Router([...this.routes, ...newRoutes], this.routerOptions);
|
|
264
|
-
});
|
|
265
|
-
// Allow for any server context to be passed
|
|
266
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
267
|
-
_defineProperty(this, "freeze", server => {
|
|
268
|
-
var instance = fmw({
|
|
269
|
-
ignoreTrailingSlash: true,
|
|
270
|
-
defaultRoute(req, serverResponse) {
|
|
271
|
-
return _asyncToGenerator(function* () {
|
|
272
|
-
var _req$url;
|
|
273
|
-
var res = new KaitoResponse(serverResponse);
|
|
274
|
-
var message = "Cannot ".concat(req.method, " ").concat((_req$url = req.url) !== null && _req$url !== void 0 ? _req$url : '/');
|
|
275
|
-
getSend(res)(404, {
|
|
276
|
-
success: false,
|
|
277
|
-
data: null,
|
|
278
|
-
message
|
|
279
|
-
});
|
|
280
|
-
return {
|
|
281
|
-
success: false,
|
|
282
|
-
data: {
|
|
283
|
-
status: 404,
|
|
284
|
-
message
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
})();
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
var _loop = function _loop(_route) {
|
|
291
|
-
var handler = /*#__PURE__*/function () {
|
|
292
|
-
var _ref = _asyncToGenerator(function* (incomingMessage, serverResponse, params) {
|
|
293
|
-
var req = new KaitoRequest(incomingMessage);
|
|
294
|
-
var res = new KaitoResponse(serverResponse);
|
|
295
|
-
return Router.handle(server, _route, {
|
|
296
|
-
params,
|
|
297
|
-
req,
|
|
298
|
-
res
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
return function handler(_x, _x2, _x3) {
|
|
302
|
-
return _ref.apply(this, arguments);
|
|
303
|
-
};
|
|
304
|
-
}();
|
|
305
|
-
if (_route.method === '*') {
|
|
306
|
-
instance.all(_route.path, handler);
|
|
307
|
-
return 1; // continue
|
|
308
|
-
}
|
|
309
|
-
instance.on(_route.method, _route.path, handler);
|
|
310
|
-
};
|
|
311
|
-
for (var _route2 of this.routes) {
|
|
312
|
-
if (_loop(_route2)) continue;
|
|
313
|
-
}
|
|
314
|
-
return instance;
|
|
315
|
-
});
|
|
316
|
-
_defineProperty(this, "method", method => (path, route) => {
|
|
317
|
-
return this.add(method, path, route);
|
|
318
|
-
});
|
|
319
|
-
_defineProperty(this, "get", this.method('GET'));
|
|
320
|
-
_defineProperty(this, "post", this.method('POST'));
|
|
321
|
-
_defineProperty(this, "put", this.method('PUT'));
|
|
322
|
-
_defineProperty(this, "patch", this.method('PATCH'));
|
|
323
|
-
_defineProperty(this, "delete", this.method('DELETE'));
|
|
324
|
-
_defineProperty(this, "head", this.method('HEAD'));
|
|
325
|
-
_defineProperty(this, "options", this.method('OPTIONS'));
|
|
326
|
-
_defineProperty(this, "through", transform => new Router(this.routes, {
|
|
327
|
-
through: function () {
|
|
328
|
-
var _through = _asyncToGenerator(function* (context) {
|
|
329
|
-
var fromCurrentRouter = yield _this.routerOptions.through(context);
|
|
330
|
-
return transform(fromCurrentRouter);
|
|
331
|
-
});
|
|
332
|
-
function through(_x4) {
|
|
333
|
-
return _through.apply(this, arguments);
|
|
334
|
-
}
|
|
335
|
-
return through;
|
|
336
|
-
}()
|
|
337
|
-
}));
|
|
338
|
-
this.routerOptions = options;
|
|
339
|
-
this.routes = new Set(routes);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
_Router = Router;
|
|
343
|
-
_defineProperty(Router, "create", () => new _Router([], {
|
|
344
|
-
through: function () {
|
|
345
|
-
var _through2 = _asyncToGenerator(function* (context) {
|
|
346
|
-
return context;
|
|
347
|
-
});
|
|
348
|
-
function through(_x5) {
|
|
349
|
-
return _through2.apply(this, arguments);
|
|
350
|
-
}
|
|
351
|
-
return through;
|
|
352
|
-
}()
|
|
353
|
-
}));
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* @deprecated use `createUtilities` instead
|
|
357
|
-
*/
|
|
358
|
-
function createGetContext(callback) {
|
|
359
|
-
return callback;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* A helper function to create typed necessary functions
|
|
364
|
-
*
|
|
365
|
-
* @example
|
|
366
|
-
* ```ts
|
|
367
|
-
* const {router, getContext} = createUtilities(async (req, res) => {
|
|
368
|
-
* // Return context here
|
|
369
|
-
* })
|
|
370
|
-
*
|
|
371
|
-
* const app = router().get('/', async () => "hello");
|
|
372
|
-
*
|
|
373
|
-
* const server = createServer({
|
|
374
|
-
* router: app,
|
|
375
|
-
* getContext,
|
|
376
|
-
* // ...
|
|
377
|
-
* });
|
|
378
|
-
* ```
|
|
379
|
-
*/
|
|
380
|
-
function createUtilities(getContext) {
|
|
381
|
-
return {
|
|
382
|
-
getContext,
|
|
383
|
-
router: () => Router.create()
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
function getLastEntryInMultiHeaderValue(headerValue) {
|
|
387
|
-
var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
|
|
388
|
-
var lastIndex = normalized.lastIndexOf(',');
|
|
389
|
-
return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
|
|
390
|
-
}
|
|
391
|
-
function getBody(_x) {
|
|
392
|
-
return _getBody.apply(this, arguments);
|
|
393
|
-
}
|
|
394
|
-
function _getBody() {
|
|
395
|
-
_getBody = _asyncToGenerator(function* (req) {
|
|
396
|
-
if (!req.headers['content-type']) {
|
|
397
|
-
return null;
|
|
398
|
-
}
|
|
399
|
-
var buffer = yield getRawBody(req.raw);
|
|
400
|
-
var {
|
|
401
|
-
type
|
|
402
|
-
} = parse(req.headers['content-type']);
|
|
403
|
-
switch (type) {
|
|
404
|
-
case 'application/json':
|
|
405
|
-
{
|
|
406
|
-
return json(Readable.from(buffer));
|
|
407
|
-
}
|
|
408
|
-
default:
|
|
409
|
-
{
|
|
410
|
-
if (process.env.NODE_ENV === 'development') {
|
|
411
|
-
console.warn('[kaito] Unsupported content type:', type);
|
|
412
|
-
console.warn('[kaito] This message is only shown in development mode.');
|
|
413
|
-
}
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
return _getBody.apply(this, arguments);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
class KaitoRequest {
|
|
422
|
-
constructor(raw) {
|
|
423
|
-
_defineProperty(this, "_url", null);
|
|
424
|
-
this.raw = raw;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* The full URL of the request, including the protocol, hostname, and path.
|
|
429
|
-
* Note: does not include the query string or hash
|
|
430
|
-
*/
|
|
431
|
-
get fullURL() {
|
|
432
|
-
var _this$raw$url;
|
|
433
|
-
return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* A new URL instance for the full URL of the request.
|
|
438
|
-
*/
|
|
439
|
-
get url() {
|
|
440
|
-
if (this._url) {
|
|
441
|
-
return this._url;
|
|
442
|
-
}
|
|
443
|
-
this._url = new URL(this.fullURL);
|
|
444
|
-
return this._url;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* The HTTP method of the request.
|
|
449
|
-
*/
|
|
450
|
-
get method() {
|
|
451
|
-
if (!this.raw.method) {
|
|
452
|
-
throw new Error('Request method is not defined, somehow...');
|
|
453
|
-
}
|
|
454
|
-
return this.raw.method;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* The protocol of the request, either `http` or `https`.
|
|
459
|
-
*/
|
|
460
|
-
get protocol() {
|
|
461
|
-
if (this.raw.socket instanceof TLSSocket) {
|
|
462
|
-
return this.raw.socket.encrypted ? 'https' : 'http';
|
|
463
|
-
}
|
|
464
|
-
return 'http';
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* The request headers
|
|
469
|
-
*/
|
|
470
|
-
get headers() {
|
|
471
|
-
return this.raw.headers;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* The hostname of the request.
|
|
476
|
-
*/
|
|
477
|
-
get hostname() {
|
|
478
|
-
var _this$raw$headers$hos, _this$raw$headers$Au;
|
|
479
|
-
return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function createFMWServer(config) {
|
|
484
|
-
var _config$rawRoutes;
|
|
485
|
-
var router = config.router.freeze(config);
|
|
486
|
-
var rawRoutes = (_config$rawRoutes = config.rawRoutes) !== null && _config$rawRoutes !== void 0 ? _config$rawRoutes : {};
|
|
487
|
-
for (var method in rawRoutes) {
|
|
488
|
-
if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
var routes = rawRoutes[method];
|
|
492
|
-
if (!routes || routes.length === 0) {
|
|
493
|
-
continue;
|
|
494
|
-
}
|
|
495
|
-
for (var route of routes) {
|
|
496
|
-
if (method === '*') {
|
|
497
|
-
router.all(route.path, route.handler);
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
router[method.toLowerCase()](route.path, route.handler);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
var server = http.createServer( /*#__PURE__*/function () {
|
|
504
|
-
var _ref = _asyncToGenerator(function* (req, res) {
|
|
505
|
-
var before;
|
|
506
|
-
if (config.before) {
|
|
507
|
-
before = yield config.before(req, res);
|
|
508
|
-
} else {
|
|
509
|
-
before = undefined;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// If the user has sent a response (e.g. replying to CORS), we don't want to do anything else.
|
|
513
|
-
if (res.headersSent) {
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
var result = yield router.lookup(req, res);
|
|
517
|
-
if ('after' in config && config.after) {
|
|
518
|
-
yield config.after(before, result);
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
return function (_x, _x2) {
|
|
522
|
-
return _ref.apply(this, arguments);
|
|
523
|
-
};
|
|
524
|
-
}());
|
|
525
|
-
return {
|
|
526
|
-
server,
|
|
527
|
-
fmw: router
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
function createServer(config) {
|
|
531
|
-
return createFMWServer(config).server;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
export { KaitoError, KaitoRequest, KaitoResponse, Router, WrappedError, createFMWServer, createGetContext, createServer, createUtilities, getBody, getLastEntryInMultiHeaderValue };
|