@kaito-http/core 3.0.0-beta.7 → 3.0.0-beta.8
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.cjs +439 -0
- package/dist/index.d.cts +237 -0
- package/package.json +5 -2
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
KaitoError: () => KaitoError,
|
|
34
|
+
KaitoRequest: () => KaitoRequest,
|
|
35
|
+
KaitoResponse: () => KaitoResponse,
|
|
36
|
+
Router: () => Router,
|
|
37
|
+
WrappedError: () => WrappedError,
|
|
38
|
+
createFMWServer: () => createFMWServer,
|
|
39
|
+
createGetContext: () => createGetContext,
|
|
40
|
+
createServer: () => createServer2,
|
|
41
|
+
createUtilities: () => createUtilities,
|
|
42
|
+
getBody: () => getBody,
|
|
43
|
+
getLastEntryInMultiHeaderValue: () => getLastEntryInMultiHeaderValue,
|
|
44
|
+
parsable: () => parsable
|
|
45
|
+
});
|
|
46
|
+
module.exports = __toCommonJS(src_exports);
|
|
47
|
+
|
|
48
|
+
// src/error.ts
|
|
49
|
+
var WrappedError = class _WrappedError extends Error {
|
|
50
|
+
constructor(data) {
|
|
51
|
+
super("Something was thrown, but it was not an instance of Error, so a WrappedError was created.");
|
|
52
|
+
this.data = data;
|
|
53
|
+
}
|
|
54
|
+
static maybe(maybeError) {
|
|
55
|
+
if (maybeError instanceof Error) {
|
|
56
|
+
return maybeError;
|
|
57
|
+
}
|
|
58
|
+
return _WrappedError.from(maybeError);
|
|
59
|
+
}
|
|
60
|
+
static from(data) {
|
|
61
|
+
return new _WrappedError(data);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var KaitoError = class extends Error {
|
|
65
|
+
constructor(status, message) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.status = status;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/req.ts
|
|
72
|
+
var import_node_tls = require("tls");
|
|
73
|
+
|
|
74
|
+
// src/util.ts
|
|
75
|
+
var import_content_type = require("content-type");
|
|
76
|
+
var import_node_stream = require("stream");
|
|
77
|
+
var import_consumers = require("stream/consumers");
|
|
78
|
+
var import_raw_body = __toESM(require("raw-body"), 1);
|
|
79
|
+
|
|
80
|
+
// src/router.ts
|
|
81
|
+
var import_find_my_way = __toESM(require("find-my-way"), 1);
|
|
82
|
+
|
|
83
|
+
// src/res.ts
|
|
84
|
+
var import_cookie = require("cookie");
|
|
85
|
+
var KaitoResponse = class {
|
|
86
|
+
constructor(raw) {
|
|
87
|
+
this.raw = raw;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Send a response
|
|
91
|
+
* @param key The key of the header
|
|
92
|
+
* @param value The value of the header
|
|
93
|
+
* @returns The response object
|
|
94
|
+
*/
|
|
95
|
+
header(key, value) {
|
|
96
|
+
this.raw.setHeader(key, value);
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Set the status code of the response
|
|
101
|
+
* @param code The status code
|
|
102
|
+
* @returns The response object
|
|
103
|
+
*/
|
|
104
|
+
status(code) {
|
|
105
|
+
this.raw.statusCode = code;
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Set a cookie
|
|
110
|
+
* @param name The name of the cookie
|
|
111
|
+
* @param value The value of the cookie
|
|
112
|
+
* @param options The options for the cookie
|
|
113
|
+
* @returns The response object
|
|
114
|
+
*/
|
|
115
|
+
cookie(name, value, options) {
|
|
116
|
+
this.raw.setHeader("Set-Cookie", (0, import_cookie.serialize)(name, value, options));
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Send a JSON APIResponse body
|
|
121
|
+
* @param data The data to send
|
|
122
|
+
* @returns The response object
|
|
123
|
+
*/
|
|
124
|
+
json(data) {
|
|
125
|
+
const json2 = JSON.stringify(data);
|
|
126
|
+
this.raw.setHeader("Content-Type", "application/json");
|
|
127
|
+
this.raw.setHeader("Content-Length", Buffer.byteLength(json2));
|
|
128
|
+
this.raw.end(json2);
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/router.ts
|
|
134
|
+
var getSend = (res) => (status, response) => {
|
|
135
|
+
if (res.raw.headersSent) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
res.status(status).json(response);
|
|
139
|
+
};
|
|
140
|
+
var Router = class _Router {
|
|
141
|
+
routerOptions;
|
|
142
|
+
routes;
|
|
143
|
+
static create = () => new _Router([], {
|
|
144
|
+
through: async (context) => context
|
|
145
|
+
});
|
|
146
|
+
static parseQuery(schema, url) {
|
|
147
|
+
if (!schema) {
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
const result = {};
|
|
151
|
+
for (const [key, parsable2] of Object.entries(schema)) {
|
|
152
|
+
const value = url.searchParams.get(key);
|
|
153
|
+
result[key] = parsable2.parse(value);
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
static async handle(server, route, options) {
|
|
158
|
+
const send = getSend(options.res);
|
|
159
|
+
try {
|
|
160
|
+
const rootCtx = await server.getContext(options.req, options.res);
|
|
161
|
+
const ctx = await route.through(rootCtx);
|
|
162
|
+
const body = await route.body?.parse(await getBody(options.req)) ?? void 0;
|
|
163
|
+
const query = _Router.parseQuery(route.query, options.req.url);
|
|
164
|
+
const result = await route.run({
|
|
165
|
+
ctx,
|
|
166
|
+
body,
|
|
167
|
+
query,
|
|
168
|
+
params: options.params
|
|
169
|
+
});
|
|
170
|
+
if (options.res.raw.headersSent) {
|
|
171
|
+
return {
|
|
172
|
+
success: true,
|
|
173
|
+
data: result
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
send(200, {
|
|
177
|
+
success: true,
|
|
178
|
+
data: result,
|
|
179
|
+
message: "OK"
|
|
180
|
+
});
|
|
181
|
+
return {
|
|
182
|
+
success: true,
|
|
183
|
+
data: result
|
|
184
|
+
};
|
|
185
|
+
} catch (e) {
|
|
186
|
+
const error = WrappedError.maybe(e);
|
|
187
|
+
if (error instanceof KaitoError) {
|
|
188
|
+
send(error.status, {
|
|
189
|
+
success: false,
|
|
190
|
+
data: null,
|
|
191
|
+
message: error.message
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const { status, message } = await server.onError({ error, req: options.req, res: options.res }).catch(() => ({ status: 500, message: "Internal Server Error" }));
|
|
196
|
+
send(status, {
|
|
197
|
+
success: false,
|
|
198
|
+
data: null,
|
|
199
|
+
message
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
success: false,
|
|
203
|
+
data: { status, message }
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
constructor(routes, options) {
|
|
208
|
+
this.routerOptions = options;
|
|
209
|
+
this.routes = new Set(routes);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Adds a new route to the router
|
|
213
|
+
* @deprecated Use the method-specific methods instead
|
|
214
|
+
*/
|
|
215
|
+
add = (method, path, route) => {
|
|
216
|
+
const merged = {
|
|
217
|
+
...typeof route === "object" ? route : { run: route },
|
|
218
|
+
method,
|
|
219
|
+
path,
|
|
220
|
+
through: this.routerOptions.through
|
|
221
|
+
};
|
|
222
|
+
return new _Router([...this.routes, merged], this.routerOptions);
|
|
223
|
+
};
|
|
224
|
+
merge = (pathPrefix, other) => {
|
|
225
|
+
const newRoutes = [...other.routes].map((route) => ({
|
|
226
|
+
...route,
|
|
227
|
+
path: `${pathPrefix}${route.path}`
|
|
228
|
+
}));
|
|
229
|
+
return new _Router(
|
|
230
|
+
[...this.routes, ...newRoutes],
|
|
231
|
+
this.routerOptions
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
// Allow for any server context to be passed
|
|
235
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
236
|
+
freeze = (server) => {
|
|
237
|
+
const instance = (0, import_find_my_way.default)({
|
|
238
|
+
ignoreTrailingSlash: true,
|
|
239
|
+
async defaultRoute(req, serverResponse) {
|
|
240
|
+
const res = new KaitoResponse(serverResponse);
|
|
241
|
+
const message = `Cannot ${req.method} ${req.url ?? "/"}`;
|
|
242
|
+
getSend(res)(404, {
|
|
243
|
+
success: false,
|
|
244
|
+
data: null,
|
|
245
|
+
message
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
success: false,
|
|
249
|
+
data: { status: 404, message }
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
for (const route of this.routes) {
|
|
254
|
+
const handler = async (incomingMessage, serverResponse, params) => {
|
|
255
|
+
const req = new KaitoRequest(incomingMessage);
|
|
256
|
+
const res = new KaitoResponse(serverResponse);
|
|
257
|
+
return _Router.handle(server, route, {
|
|
258
|
+
params,
|
|
259
|
+
req,
|
|
260
|
+
res
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
if (route.method === "*") {
|
|
264
|
+
instance.all(route.path, handler);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
instance.on(route.method, route.path, handler);
|
|
268
|
+
}
|
|
269
|
+
return instance;
|
|
270
|
+
};
|
|
271
|
+
method = (method) => (path, route) => {
|
|
272
|
+
return this.add(method, path, route);
|
|
273
|
+
};
|
|
274
|
+
get = this.method("GET");
|
|
275
|
+
post = this.method("POST");
|
|
276
|
+
put = this.method("PUT");
|
|
277
|
+
patch = this.method("PATCH");
|
|
278
|
+
delete = this.method("DELETE");
|
|
279
|
+
head = this.method("HEAD");
|
|
280
|
+
options = this.method("OPTIONS");
|
|
281
|
+
through = (transform) => new _Router(this.routes, {
|
|
282
|
+
through: async (context) => {
|
|
283
|
+
const fromCurrentRouter = await this.routerOptions.through(context);
|
|
284
|
+
return transform(fromCurrentRouter);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// src/util.ts
|
|
290
|
+
function createGetContext(callback) {
|
|
291
|
+
return callback;
|
|
292
|
+
}
|
|
293
|
+
function createUtilities(getContext) {
|
|
294
|
+
return {
|
|
295
|
+
getContext,
|
|
296
|
+
router: () => Router.create()
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function getLastEntryInMultiHeaderValue(headerValue) {
|
|
300
|
+
const normalized = Array.isArray(headerValue) ? headerValue.join(",") : headerValue;
|
|
301
|
+
const lastIndex = normalized.lastIndexOf(",");
|
|
302
|
+
return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
|
|
303
|
+
}
|
|
304
|
+
function parsable(parse) {
|
|
305
|
+
return {
|
|
306
|
+
parse
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
async function getBody(req) {
|
|
310
|
+
if (!req.headers["content-type"]) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
const buffer = await (0, import_raw_body.default)(req.raw);
|
|
314
|
+
const { type } = (0, import_content_type.parse)(req.headers["content-type"]);
|
|
315
|
+
switch (type) {
|
|
316
|
+
case "application/json": {
|
|
317
|
+
return (0, import_consumers.json)(import_node_stream.Readable.from(buffer));
|
|
318
|
+
}
|
|
319
|
+
default: {
|
|
320
|
+
if (process.env.NODE_ENV === "development") {
|
|
321
|
+
console.warn("[kaito] Unsupported content type:", type);
|
|
322
|
+
console.warn("[kaito] This message is only shown in development mode.");
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/req.ts
|
|
330
|
+
var KaitoRequest = class {
|
|
331
|
+
constructor(raw) {
|
|
332
|
+
this.raw = raw;
|
|
333
|
+
}
|
|
334
|
+
_url = null;
|
|
335
|
+
/**
|
|
336
|
+
* The full URL of the request, including the protocol, hostname, and path.
|
|
337
|
+
* Note: does not include the query string or hash
|
|
338
|
+
*/
|
|
339
|
+
get fullURL() {
|
|
340
|
+
return `${this.protocol}://${this.hostname}${this.raw.url ?? ""}`;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* A new URL instance for the full URL of the request.
|
|
344
|
+
*/
|
|
345
|
+
get url() {
|
|
346
|
+
if (this._url) {
|
|
347
|
+
return this._url;
|
|
348
|
+
}
|
|
349
|
+
this._url = new URL(this.fullURL);
|
|
350
|
+
return this._url;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* The HTTP method of the request.
|
|
354
|
+
*/
|
|
355
|
+
get method() {
|
|
356
|
+
if (!this.raw.method) {
|
|
357
|
+
throw new Error("Request method is not defined, somehow...");
|
|
358
|
+
}
|
|
359
|
+
return this.raw.method;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* The protocol of the request, either `http` or `https`.
|
|
363
|
+
*/
|
|
364
|
+
get protocol() {
|
|
365
|
+
if (this.raw.socket instanceof import_node_tls.TLSSocket) {
|
|
366
|
+
return this.raw.socket.encrypted ? "https" : "http";
|
|
367
|
+
}
|
|
368
|
+
return "http";
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* The request headers
|
|
372
|
+
*/
|
|
373
|
+
get headers() {
|
|
374
|
+
return this.raw.headers;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* The hostname of the request.
|
|
378
|
+
*/
|
|
379
|
+
get hostname() {
|
|
380
|
+
return this.raw.headers.host ?? getLastEntryInMultiHeaderValue(this.raw.headers[":authority"] ?? []);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// src/server.ts
|
|
385
|
+
var http = __toESM(require("http"), 1);
|
|
386
|
+
function createFMWServer(config) {
|
|
387
|
+
const router = config.router.freeze(config);
|
|
388
|
+
const rawRoutes = config.rawRoutes ?? {};
|
|
389
|
+
for (const method in rawRoutes) {
|
|
390
|
+
if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const routes = rawRoutes[method];
|
|
394
|
+
if (!routes || routes.length === 0) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
for (const route of routes) {
|
|
398
|
+
if (method === "*") {
|
|
399
|
+
router.all(route.path, route.handler);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
router[method.toLowerCase()](route.path, route.handler);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const server = http.createServer(async (req, res) => {
|
|
406
|
+
let before;
|
|
407
|
+
if (config.before) {
|
|
408
|
+
before = await config.before(req, res);
|
|
409
|
+
} else {
|
|
410
|
+
before = void 0;
|
|
411
|
+
}
|
|
412
|
+
if (res.headersSent) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const result = await router.lookup(req, res);
|
|
416
|
+
if ("after" in config && config.after) {
|
|
417
|
+
await config.after(before, result);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return { server, fmw: router };
|
|
421
|
+
}
|
|
422
|
+
function createServer2(config) {
|
|
423
|
+
return createFMWServer(config).server;
|
|
424
|
+
}
|
|
425
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
426
|
+
0 && (module.exports = {
|
|
427
|
+
KaitoError,
|
|
428
|
+
KaitoRequest,
|
|
429
|
+
KaitoResponse,
|
|
430
|
+
Router,
|
|
431
|
+
WrappedError,
|
|
432
|
+
createFMWServer,
|
|
433
|
+
createGetContext,
|
|
434
|
+
createServer,
|
|
435
|
+
createUtilities,
|
|
436
|
+
getBody,
|
|
437
|
+
getLastEntryInMultiHeaderValue,
|
|
438
|
+
parsable
|
|
439
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import * as find_my_way from 'find-my-way';
|
|
2
|
+
import find_my_way__default, { HTTPMethod } from 'find-my-way';
|
|
3
|
+
export { HTTPMethod } from 'find-my-way';
|
|
4
|
+
import * as http from 'http';
|
|
5
|
+
import * as http$1 from 'node:http';
|
|
6
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
7
|
+
import { CookieSerializeOptions } from 'cookie';
|
|
8
|
+
|
|
9
|
+
declare class WrappedError<T> extends Error {
|
|
10
|
+
readonly data: T;
|
|
11
|
+
static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
|
|
12
|
+
static from<T>(data: T): WrappedError<T>;
|
|
13
|
+
private constructor();
|
|
14
|
+
}
|
|
15
|
+
declare class KaitoError extends Error {
|
|
16
|
+
readonly status: number;
|
|
17
|
+
constructor(status: number, message: string);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
declare class KaitoRequest {
|
|
21
|
+
readonly raw: IncomingMessage;
|
|
22
|
+
private _url;
|
|
23
|
+
constructor(raw: IncomingMessage);
|
|
24
|
+
/**
|
|
25
|
+
* The full URL of the request, including the protocol, hostname, and path.
|
|
26
|
+
* Note: does not include the query string or hash
|
|
27
|
+
*/
|
|
28
|
+
get fullURL(): string;
|
|
29
|
+
/**
|
|
30
|
+
* A new URL instance for the full URL of the request.
|
|
31
|
+
*/
|
|
32
|
+
get url(): URL;
|
|
33
|
+
/**
|
|
34
|
+
* The HTTP method of the request.
|
|
35
|
+
*/
|
|
36
|
+
get method(): HTTPMethod;
|
|
37
|
+
/**
|
|
38
|
+
* The protocol of the request, either `http` or `https`.
|
|
39
|
+
*/
|
|
40
|
+
get protocol(): 'http' | 'https';
|
|
41
|
+
/**
|
|
42
|
+
* The request headers
|
|
43
|
+
*/
|
|
44
|
+
get headers(): http.IncomingHttpHeaders;
|
|
45
|
+
/**
|
|
46
|
+
* The hostname of the request.
|
|
47
|
+
*/
|
|
48
|
+
get hostname(): string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type ErroredAPIResponse = {
|
|
52
|
+
success: false;
|
|
53
|
+
data: null;
|
|
54
|
+
message: string;
|
|
55
|
+
};
|
|
56
|
+
type SuccessfulAPIResponse<T> = {
|
|
57
|
+
success: true;
|
|
58
|
+
data: T;
|
|
59
|
+
message: 'OK';
|
|
60
|
+
};
|
|
61
|
+
type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
|
|
62
|
+
type AnyResponse = APIResponse<unknown>;
|
|
63
|
+
declare class KaitoResponse<T = unknown> {
|
|
64
|
+
readonly raw: ServerResponse;
|
|
65
|
+
constructor(raw: ServerResponse);
|
|
66
|
+
/**
|
|
67
|
+
* Send a response
|
|
68
|
+
* @param key The key of the header
|
|
69
|
+
* @param value The value of the header
|
|
70
|
+
* @returns The response object
|
|
71
|
+
*/
|
|
72
|
+
header(key: string, value: string | readonly string[]): this;
|
|
73
|
+
/**
|
|
74
|
+
* Set the status code of the response
|
|
75
|
+
* @param code The status code
|
|
76
|
+
* @returns The response object
|
|
77
|
+
*/
|
|
78
|
+
status(code: number): this;
|
|
79
|
+
/**
|
|
80
|
+
* Set a cookie
|
|
81
|
+
* @param name The name of the cookie
|
|
82
|
+
* @param value The value of the cookie
|
|
83
|
+
* @param options The options for the cookie
|
|
84
|
+
* @returns The response object
|
|
85
|
+
*/
|
|
86
|
+
cookie(name: string, value: string, options: CookieSerializeOptions): this;
|
|
87
|
+
/**
|
|
88
|
+
* Send a JSON APIResponse body
|
|
89
|
+
* @param data The data to send
|
|
90
|
+
* @returns The response object
|
|
91
|
+
*/
|
|
92
|
+
json(data: APIResponse<T>): this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type Before<BeforeAfterContext> = (req: http$1.IncomingMessage, res: http$1.ServerResponse) => Promise<BeforeAfterContext>;
|
|
96
|
+
type HandlerResult = {
|
|
97
|
+
success: true;
|
|
98
|
+
data: unknown;
|
|
99
|
+
} | {
|
|
100
|
+
success: false;
|
|
101
|
+
data: {
|
|
102
|
+
status: number;
|
|
103
|
+
message: string;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
type After<BeforeAfterContext> = (ctx: BeforeAfterContext, result: HandlerResult) => Promise<void>;
|
|
107
|
+
type ServerConfigWithBefore<BeforeAfterContext> = {
|
|
108
|
+
before: Before<BeforeAfterContext>;
|
|
109
|
+
after?: After<BeforeAfterContext>;
|
|
110
|
+
} | {
|
|
111
|
+
before?: undefined;
|
|
112
|
+
};
|
|
113
|
+
type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
|
|
114
|
+
router: Router<ContextFrom, unknown, any>;
|
|
115
|
+
getContext: GetContext<ContextFrom>;
|
|
116
|
+
rawRoutes?: Partial<Record<KaitoMethod, Array<{
|
|
117
|
+
path: string;
|
|
118
|
+
handler: (request: http$1.IncomingMessage, response: http$1.ServerResponse) => unknown;
|
|
119
|
+
}>>>;
|
|
120
|
+
onError(arg: {
|
|
121
|
+
error: Error;
|
|
122
|
+
req: KaitoRequest;
|
|
123
|
+
res: KaitoResponse;
|
|
124
|
+
}): Promise<KaitoError | {
|
|
125
|
+
status: number;
|
|
126
|
+
message: string;
|
|
127
|
+
}>;
|
|
128
|
+
};
|
|
129
|
+
declare function createFMWServer<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): {
|
|
130
|
+
readonly server: http$1.Server<typeof http$1.IncomingMessage, typeof http$1.ServerResponse>;
|
|
131
|
+
readonly fmw: find_my_way.Instance<find_my_way.HTTPVersion.V1>;
|
|
132
|
+
};
|
|
133
|
+
declare function createServer<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): http$1.Server<typeof http$1.IncomingMessage, typeof http$1.ServerResponse>;
|
|
134
|
+
|
|
135
|
+
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;
|
|
136
|
+
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
137
|
+
type RouterOptions<ContextFrom, ContextTo> = {
|
|
138
|
+
through: (context: ContextFrom) => Promise<ContextTo>;
|
|
139
|
+
};
|
|
140
|
+
type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
|
|
141
|
+
declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
|
|
142
|
+
private readonly routerOptions;
|
|
143
|
+
readonly routes: Set<R>;
|
|
144
|
+
static create: <Context>() => Router<Context, Context, never>;
|
|
145
|
+
private static parseQuery;
|
|
146
|
+
private static handle;
|
|
147
|
+
constructor(routes: Iterable<R>, options: RouterOptions<ContextFrom, ContextTo>);
|
|
148
|
+
/**
|
|
149
|
+
* Adds a new route to the router
|
|
150
|
+
* @deprecated Use the method-specific methods instead
|
|
151
|
+
*/
|
|
152
|
+
add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>>;
|
|
153
|
+
readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
|
|
154
|
+
freeze: (server: ServerConfig<ContextFrom, any>) => find_my_way__default.Instance<find_my_way__default.HTTPVersion.V1>;
|
|
155
|
+
private readonly method;
|
|
156
|
+
get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>>;
|
|
157
|
+
post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>>;
|
|
158
|
+
put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>>;
|
|
159
|
+
patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>>;
|
|
160
|
+
delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>>;
|
|
161
|
+
head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>>;
|
|
162
|
+
options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>>;
|
|
163
|
+
through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
|
|
167
|
+
[k in Param | keyof ExtractRouteParams<Rest>]: string;
|
|
168
|
+
} : T extends `${string}:${infer Param}` ? {
|
|
169
|
+
[k in Param]: string;
|
|
170
|
+
} : {};
|
|
171
|
+
type KaitoMethod = HTTPMethod | '*';
|
|
172
|
+
type GetContext<Result> = (req: KaitoRequest, res: KaitoResponse) => Promise<Result>;
|
|
173
|
+
/**
|
|
174
|
+
* @deprecated use `createUtilities` instead
|
|
175
|
+
*/
|
|
176
|
+
declare function createGetContext<Context>(callback: GetContext<Context>): GetContext<Context>;
|
|
177
|
+
/**
|
|
178
|
+
* A helper function to create typed necessary functions
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* const {router, getContext} = createUtilities(async (req, res) => {
|
|
183
|
+
* // Return context here
|
|
184
|
+
* })
|
|
185
|
+
*
|
|
186
|
+
* const app = router().get('/', async () => "hello");
|
|
187
|
+
*
|
|
188
|
+
* const server = createServer({
|
|
189
|
+
* router: app,
|
|
190
|
+
* getContext,
|
|
191
|
+
* // ...
|
|
192
|
+
* });
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
declare function createUtilities<Context>(getContext: GetContext<Context>): {
|
|
196
|
+
getContext: GetContext<Context>;
|
|
197
|
+
router: () => Router<Context, Context, never>;
|
|
198
|
+
};
|
|
199
|
+
type InferContext<T> = T extends (req: KaitoRequest, res: KaitoResponse) => Promise<infer U> ? U : never;
|
|
200
|
+
declare function getLastEntryInMultiHeaderValue(headerValue: string | string[]): string;
|
|
201
|
+
interface Parsable<Output = any, Input = Output> {
|
|
202
|
+
_input: Input;
|
|
203
|
+
parse: (value: unknown) => Output;
|
|
204
|
+
}
|
|
205
|
+
type InferParsable<T> = T extends Parsable<infer Output, infer Input> ? {
|
|
206
|
+
input: Input;
|
|
207
|
+
output: Output;
|
|
208
|
+
} : never;
|
|
209
|
+
declare function parsable<T>(parse: (value: unknown) => T): Parsable<T, T>;
|
|
210
|
+
type RemoveEndSlashes<T extends string> = T extends `${infer U}/` ? U : T;
|
|
211
|
+
type AddStartSlashes<T extends string> = T extends `/${infer U}` ? `/${U}` : `/${T}`;
|
|
212
|
+
type NormalizePath<T extends string> = AddStartSlashes<RemoveEndSlashes<T>>;
|
|
213
|
+
type Values<T> = T[keyof T];
|
|
214
|
+
type NoEmpty<T> = [keyof T] extends [never] ? never : T;
|
|
215
|
+
declare function getBody(req: KaitoRequest): Promise<unknown>;
|
|
216
|
+
|
|
217
|
+
type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
|
|
218
|
+
ctx: Context;
|
|
219
|
+
body: BodyOutput;
|
|
220
|
+
query: QueryOutput;
|
|
221
|
+
params: ExtractRouteParams<Path>;
|
|
222
|
+
};
|
|
223
|
+
type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
|
|
224
|
+
type RouteRunner<Result, Path extends string, Context, QueryOutput, BodyOutput> = (args: RouteArgument<Path, Context, QueryOutput, BodyOutput>) => Promise<Result>;
|
|
225
|
+
type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, Body extends Parsable> = {
|
|
226
|
+
through: (context: ContextFrom) => Promise<ContextTo>;
|
|
227
|
+
body?: Body;
|
|
228
|
+
query?: Query;
|
|
229
|
+
path: Path;
|
|
230
|
+
method: Method;
|
|
231
|
+
run(arg: RouteArgument<Path, ContextTo, {
|
|
232
|
+
[Key in keyof Query]: InferParsable<Query[Key]>['output'];
|
|
233
|
+
}, InferParsable<Body>['output']>): Promise<Result>;
|
|
234
|
+
};
|
|
235
|
+
type AnyRoute<FromContext = any, ToContext = any> = Route<FromContext, ToContext, any, any, any, AnyQueryDefinition, any>;
|
|
236
|
+
|
|
237
|
+
export { type APIResponse, type AddStartSlashes, type After, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type HandlerResult, type InferContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, KaitoResponse, type NoEmpty, type NormalizePath, type Parsable, type RemoveEndSlashes, type Route, type RouteArgument, type RouteRunner, Router, type RouterOptions, type ServerConfig, type ServerConfigWithBefore, type SuccessfulAPIResponse, type Values, WrappedError, createFMWServer, createGetContext, createServer, createUtilities, getBody, getLastEntryInMultiHeaderValue, parsable };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaito-http/core",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Alistair Smith <hi@alistair.sh>",
|
|
6
6
|
"description": "Functional HTTP Framework for TypeScript",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
},
|
|
10
10
|
"exports": {
|
|
11
11
|
"./package.json": "./package.json",
|
|
12
|
-
".":
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
13
16
|
},
|
|
14
17
|
"homepage": "https://github.com/kaito-http/kaito",
|
|
15
18
|
"repository": "https://github.com/kaito-http/kaito",
|