@kaito-http/core 3.0.0-beta.1 → 3.0.0-beta.10
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 +290 -0
- package/dist/index.d.cts +149 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +256 -0
- package/package.json +21 -22
- 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.cjs
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
KaitoError: () => KaitoError,
|
|
24
|
+
KaitoRequest: () => KaitoRequest,
|
|
25
|
+
Router: () => Router,
|
|
26
|
+
WrappedError: () => WrappedError,
|
|
27
|
+
apiresponse: () => apiresponse,
|
|
28
|
+
createKaitoHandler: () => createKaitoHandler,
|
|
29
|
+
createUtilities: () => createUtilities,
|
|
30
|
+
parsable: () => parsable
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(index_exports);
|
|
33
|
+
|
|
34
|
+
// src/error.ts
|
|
35
|
+
var WrappedError = class _WrappedError extends Error {
|
|
36
|
+
constructor(data) {
|
|
37
|
+
super("Something was thrown, but it was not an instance of Error, so a WrappedError was created.");
|
|
38
|
+
this.data = data;
|
|
39
|
+
}
|
|
40
|
+
static maybe(maybeError) {
|
|
41
|
+
if (maybeError instanceof Error) {
|
|
42
|
+
return maybeError;
|
|
43
|
+
}
|
|
44
|
+
return _WrappedError.from(maybeError);
|
|
45
|
+
}
|
|
46
|
+
static from(data) {
|
|
47
|
+
return new _WrappedError(data);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var KaitoError = class extends Error {
|
|
51
|
+
constructor(status, message) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.status = status;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/request.ts
|
|
58
|
+
var KaitoRequest = class {
|
|
59
|
+
request;
|
|
60
|
+
_url;
|
|
61
|
+
constructor(request) {
|
|
62
|
+
this.request = request;
|
|
63
|
+
}
|
|
64
|
+
get headers() {
|
|
65
|
+
return this.request.headers;
|
|
66
|
+
}
|
|
67
|
+
get method() {
|
|
68
|
+
return this.request.method;
|
|
69
|
+
}
|
|
70
|
+
get url() {
|
|
71
|
+
return this.request.url;
|
|
72
|
+
}
|
|
73
|
+
parseURL() {
|
|
74
|
+
if (!this._url) {
|
|
75
|
+
this._url = new URL(this.url);
|
|
76
|
+
}
|
|
77
|
+
return this._url;
|
|
78
|
+
}
|
|
79
|
+
async arrayBuffer() {
|
|
80
|
+
return this.request.arrayBuffer();
|
|
81
|
+
}
|
|
82
|
+
async blob() {
|
|
83
|
+
return this.request.blob();
|
|
84
|
+
}
|
|
85
|
+
async formData() {
|
|
86
|
+
return this.request.formData();
|
|
87
|
+
}
|
|
88
|
+
async bytes() {
|
|
89
|
+
const buffer = await this.arrayBuffer();
|
|
90
|
+
return new Uint8Array(buffer);
|
|
91
|
+
}
|
|
92
|
+
async json() {
|
|
93
|
+
return this.request.json();
|
|
94
|
+
}
|
|
95
|
+
async text() {
|
|
96
|
+
return this.request.text();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// src/util.ts
|
|
101
|
+
function apiresponse(status, response) {
|
|
102
|
+
return Response.json(response, {
|
|
103
|
+
status
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function createUtilities(getContext) {
|
|
107
|
+
return {
|
|
108
|
+
getContext,
|
|
109
|
+
router: () => Router.create()
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function parsable(parse) {
|
|
113
|
+
return {
|
|
114
|
+
parse
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/router/router.ts
|
|
119
|
+
var Router = class _Router {
|
|
120
|
+
state;
|
|
121
|
+
static create = () => new _Router({
|
|
122
|
+
through: async (context) => context,
|
|
123
|
+
routes: /* @__PURE__ */ new Set()
|
|
124
|
+
});
|
|
125
|
+
static parseQuery(schema, url) {
|
|
126
|
+
if (!schema) {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
const result = {};
|
|
130
|
+
for (const [key, parsable2] of Object.entries(schema)) {
|
|
131
|
+
const value = url.searchParams.get(key);
|
|
132
|
+
result[key] = parsable2.parse(value);
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
constructor(options) {
|
|
137
|
+
this.state = options;
|
|
138
|
+
}
|
|
139
|
+
get routes() {
|
|
140
|
+
return this.state.routes;
|
|
141
|
+
}
|
|
142
|
+
add = (method, path, route) => {
|
|
143
|
+
const merged = {
|
|
144
|
+
...typeof route === "object" ? route : { run: route },
|
|
145
|
+
method,
|
|
146
|
+
path,
|
|
147
|
+
through: this.state.through
|
|
148
|
+
};
|
|
149
|
+
return new _Router({
|
|
150
|
+
...this.state,
|
|
151
|
+
routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
merge = (pathPrefix, other) => {
|
|
155
|
+
const newRoutes = [...other.state.routes].map((route) => ({
|
|
156
|
+
...route,
|
|
157
|
+
path: `${pathPrefix}${route.path}`
|
|
158
|
+
}));
|
|
159
|
+
return new _Router({
|
|
160
|
+
...this.state,
|
|
161
|
+
routes: /* @__PURE__ */ new Set([...this.state.routes, ...newRoutes])
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
freeze = (server) => {
|
|
165
|
+
const routes = /* @__PURE__ */ new Map();
|
|
166
|
+
for (const route of this.state.routes) {
|
|
167
|
+
if (!routes.has(route.path)) {
|
|
168
|
+
routes.set(route.path, /* @__PURE__ */ new Map());
|
|
169
|
+
}
|
|
170
|
+
routes.get(route.path).set(route.method, route);
|
|
171
|
+
}
|
|
172
|
+
const findRoute = (method, path) => {
|
|
173
|
+
const params = {};
|
|
174
|
+
const pathParts = path.split("/").filter(Boolean);
|
|
175
|
+
for (const [routePath, methodHandlers] of routes) {
|
|
176
|
+
const routeParts = routePath.split("/").filter(Boolean);
|
|
177
|
+
if (routeParts.length !== pathParts.length) continue;
|
|
178
|
+
let matches = true;
|
|
179
|
+
for (let i = 0; i < routeParts.length; i++) {
|
|
180
|
+
const routePart = routeParts[i];
|
|
181
|
+
const pathPart = pathParts[i];
|
|
182
|
+
if (routePart && pathPart && routePart.startsWith(":")) {
|
|
183
|
+
params[routePart.slice(1)] = pathPart;
|
|
184
|
+
} else if (routePart !== pathPart) {
|
|
185
|
+
matches = false;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (matches) {
|
|
190
|
+
const route = methodHandlers.get(method);
|
|
191
|
+
if (route) return { route, params };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return { params };
|
|
195
|
+
};
|
|
196
|
+
return async (req) => {
|
|
197
|
+
const url = new URL(req.url);
|
|
198
|
+
const method = req.method;
|
|
199
|
+
const { route, params } = findRoute(method, url.pathname);
|
|
200
|
+
if (!route) {
|
|
201
|
+
return apiresponse(404, {
|
|
202
|
+
success: false,
|
|
203
|
+
data: null,
|
|
204
|
+
message: `Cannot ${method} ${url.pathname}`
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const request = new KaitoRequest(req);
|
|
208
|
+
try {
|
|
209
|
+
const rootCtx = await server.getContext(request);
|
|
210
|
+
const ctx = await route.through(rootCtx);
|
|
211
|
+
const body = route.body ? await route.body.parse(await req.json()) : void 0;
|
|
212
|
+
const query = _Router.parseQuery(route.query, url);
|
|
213
|
+
const result = await route.run({
|
|
214
|
+
ctx,
|
|
215
|
+
body,
|
|
216
|
+
query,
|
|
217
|
+
params
|
|
218
|
+
});
|
|
219
|
+
return apiresponse(200, {
|
|
220
|
+
success: true,
|
|
221
|
+
data: result,
|
|
222
|
+
message: "OK"
|
|
223
|
+
});
|
|
224
|
+
} catch (e) {
|
|
225
|
+
const error = WrappedError.maybe(e);
|
|
226
|
+
if (error instanceof KaitoError) {
|
|
227
|
+
return apiresponse(error.status, {
|
|
228
|
+
success: false,
|
|
229
|
+
data: null,
|
|
230
|
+
message: error.message
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const { status, message } = await server.onError({ error, req: request }).catch(() => ({ status: 500, message: "Internal Server Error" }));
|
|
234
|
+
return apiresponse(status, {
|
|
235
|
+
success: false,
|
|
236
|
+
data: null,
|
|
237
|
+
message
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
method = (method) => (path, route) => {
|
|
243
|
+
return this.add(method, path, route);
|
|
244
|
+
};
|
|
245
|
+
get = this.method("GET");
|
|
246
|
+
post = this.method("POST");
|
|
247
|
+
put = this.method("PUT");
|
|
248
|
+
patch = this.method("PATCH");
|
|
249
|
+
delete = this.method("DELETE");
|
|
250
|
+
head = this.method("HEAD");
|
|
251
|
+
options = this.method("OPTIONS");
|
|
252
|
+
through = (transform) => new _Router({
|
|
253
|
+
routes: this.state.routes,
|
|
254
|
+
through: async (context) => {
|
|
255
|
+
const fromCurrentRouter = await this.state.through(context);
|
|
256
|
+
return transform(fromCurrentRouter);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/server.ts
|
|
262
|
+
function createKaitoHandler(config) {
|
|
263
|
+
const match = config.router.freeze(config);
|
|
264
|
+
return async (request) => {
|
|
265
|
+
let before = void 0;
|
|
266
|
+
if (config.before) {
|
|
267
|
+
const result = await config.before(request);
|
|
268
|
+
if (result instanceof Response) {
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
before = result;
|
|
272
|
+
}
|
|
273
|
+
const response = await match(request);
|
|
274
|
+
if ("after" in config && config.after) {
|
|
275
|
+
await config.after(before, response);
|
|
276
|
+
}
|
|
277
|
+
return response;
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
281
|
+
0 && (module.exports = {
|
|
282
|
+
KaitoError,
|
|
283
|
+
KaitoRequest,
|
|
284
|
+
Router,
|
|
285
|
+
WrappedError,
|
|
286
|
+
apiresponse,
|
|
287
|
+
createKaitoHandler,
|
|
288
|
+
createUtilities,
|
|
289
|
+
parsable
|
|
290
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
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
|
+
declare class KaitoError extends Error {
|
|
8
|
+
readonly status: number;
|
|
9
|
+
constructor(status: number, message: string);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare class KaitoRequest {
|
|
13
|
+
private readonly request;
|
|
14
|
+
private _url;
|
|
15
|
+
constructor(request: Request);
|
|
16
|
+
get headers(): Headers;
|
|
17
|
+
get method(): string;
|
|
18
|
+
get url(): string;
|
|
19
|
+
parseURL(): URL;
|
|
20
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
21
|
+
blob(): Promise<Blob>;
|
|
22
|
+
formData(): Promise<FormData>;
|
|
23
|
+
bytes(): Promise<Uint8Array>;
|
|
24
|
+
json(): Promise<unknown>;
|
|
25
|
+
text(): Promise<string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
|
|
29
|
+
|
|
30
|
+
type Before<BeforeAfterContext> = (req: Request) => Promise<BeforeAfterContext | Response>;
|
|
31
|
+
type After<BeforeAfterContext> = (ctx: BeforeAfterContext, response: Response) => Promise<void>;
|
|
32
|
+
type ServerConfigWithBefore<BeforeAfterContext> = {
|
|
33
|
+
before: Before<BeforeAfterContext>;
|
|
34
|
+
after?: After<BeforeAfterContext>;
|
|
35
|
+
} | {
|
|
36
|
+
before?: undefined;
|
|
37
|
+
};
|
|
38
|
+
type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
|
|
39
|
+
router: Router<ContextFrom, unknown, any>;
|
|
40
|
+
getContext: GetContext<ContextFrom>;
|
|
41
|
+
onError(arg: {
|
|
42
|
+
error: Error;
|
|
43
|
+
req: KaitoRequest;
|
|
44
|
+
}): Promise<KaitoError | {
|
|
45
|
+
status: number;
|
|
46
|
+
message: string;
|
|
47
|
+
}>;
|
|
48
|
+
};
|
|
49
|
+
declare function createKaitoHandler<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): (request: Request) => Promise<Response>;
|
|
50
|
+
|
|
51
|
+
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;
|
|
52
|
+
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
53
|
+
type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
|
|
54
|
+
routes: Set<Routes>;
|
|
55
|
+
through: (context: ContextFrom) => Promise<ContextTo>;
|
|
56
|
+
};
|
|
57
|
+
type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
|
|
58
|
+
declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
|
|
59
|
+
private readonly state;
|
|
60
|
+
static create: <Context>() => Router<Context, Context, never>;
|
|
61
|
+
private static parseQuery;
|
|
62
|
+
constructor(options: RouterState<R, ContextFrom, ContextTo>);
|
|
63
|
+
get routes(): Set<R>;
|
|
64
|
+
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>>;
|
|
65
|
+
readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
|
|
66
|
+
freeze: (server: ServerConfig<ContextFrom, any>) => (req: Request) => Promise<Response>;
|
|
67
|
+
private readonly method;
|
|
68
|
+
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>>;
|
|
69
|
+
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>>;
|
|
70
|
+
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>>;
|
|
71
|
+
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>>;
|
|
72
|
+
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>>;
|
|
73
|
+
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>>;
|
|
74
|
+
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>>;
|
|
75
|
+
through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type ErroredAPIResponse = {
|
|
79
|
+
success: false;
|
|
80
|
+
data: null;
|
|
81
|
+
message: string;
|
|
82
|
+
};
|
|
83
|
+
type SuccessfulAPIResponse<T> = {
|
|
84
|
+
success: true;
|
|
85
|
+
data: T;
|
|
86
|
+
message: 'OK';
|
|
87
|
+
};
|
|
88
|
+
type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
|
|
89
|
+
type AnyResponse = APIResponse<unknown>;
|
|
90
|
+
declare function apiresponse<T>(status: number, response: APIResponse<T>): Response;
|
|
91
|
+
type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
|
|
92
|
+
[k in Param | keyof ExtractRouteParams<Rest>]: string;
|
|
93
|
+
} : T extends `${string}:${infer Param}` ? {
|
|
94
|
+
[k in Param]: string;
|
|
95
|
+
} : {};
|
|
96
|
+
type GetContext<Result> = (req: KaitoRequest) => Promise<Result>;
|
|
97
|
+
/**
|
|
98
|
+
* A helper function to create typed necessary functions
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const {router, getContext} = createUtilities(async (req, res) => {
|
|
103
|
+
* // Return context here
|
|
104
|
+
* })
|
|
105
|
+
*
|
|
106
|
+
* const app = router().get('/', async () => "hello");
|
|
107
|
+
*
|
|
108
|
+
* const server = createServer({
|
|
109
|
+
* router: app,
|
|
110
|
+
* getContext,
|
|
111
|
+
* // ...
|
|
112
|
+
* });
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
declare function createUtilities<Context>(getContext: GetContext<Context>): {
|
|
116
|
+
getContext: GetContext<Context>;
|
|
117
|
+
router: () => Router<Context, Context, never>;
|
|
118
|
+
};
|
|
119
|
+
interface Parsable<Output = any, Input = Output> {
|
|
120
|
+
_input: Input;
|
|
121
|
+
parse: (value: unknown) => Output;
|
|
122
|
+
}
|
|
123
|
+
type InferParsable<T> = T extends Parsable<infer Output, infer Input> ? {
|
|
124
|
+
input: Input;
|
|
125
|
+
output: Output;
|
|
126
|
+
} : never;
|
|
127
|
+
declare function parsable<T>(parse: (value: unknown) => T): Parsable<T, T>;
|
|
128
|
+
|
|
129
|
+
type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
|
|
130
|
+
ctx: Context;
|
|
131
|
+
body: BodyOutput;
|
|
132
|
+
query: QueryOutput;
|
|
133
|
+
params: ExtractRouteParams<Path>;
|
|
134
|
+
};
|
|
135
|
+
type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
|
|
136
|
+
type Through<From, To> = (context: From) => Promise<To>;
|
|
137
|
+
type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, Body extends Parsable> = {
|
|
138
|
+
through: Through<ContextFrom, ContextTo>;
|
|
139
|
+
body?: Body;
|
|
140
|
+
query?: Query;
|
|
141
|
+
path: Path;
|
|
142
|
+
method: Method;
|
|
143
|
+
run(arg: RouteArgument<Path, ContextTo, {
|
|
144
|
+
[Key in keyof Query]: InferParsable<Query[Key]>['output'];
|
|
145
|
+
}, InferParsable<Body>['output']>): Promise<Result>;
|
|
146
|
+
};
|
|
147
|
+
type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
|
|
148
|
+
|
|
149
|
+
export { type APIResponse, type After, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type ServerConfigWithBefore, type SuccessfulAPIResponse, type Through, WrappedError, apiresponse, createKaitoHandler, createUtilities, parsable };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
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
|
+
declare class KaitoError extends Error {
|
|
8
|
+
readonly status: number;
|
|
9
|
+
constructor(status: number, message: string);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare class KaitoRequest {
|
|
13
|
+
private readonly request;
|
|
14
|
+
private _url;
|
|
15
|
+
constructor(request: Request);
|
|
16
|
+
get headers(): Headers;
|
|
17
|
+
get method(): string;
|
|
18
|
+
get url(): string;
|
|
19
|
+
parseURL(): URL;
|
|
20
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
21
|
+
blob(): Promise<Blob>;
|
|
22
|
+
formData(): Promise<FormData>;
|
|
23
|
+
bytes(): Promise<Uint8Array>;
|
|
24
|
+
json(): Promise<unknown>;
|
|
25
|
+
text(): Promise<string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
|
|
29
|
+
|
|
30
|
+
type Before<BeforeAfterContext> = (req: Request) => Promise<BeforeAfterContext | Response>;
|
|
31
|
+
type After<BeforeAfterContext> = (ctx: BeforeAfterContext, response: Response) => Promise<void>;
|
|
32
|
+
type ServerConfigWithBefore<BeforeAfterContext> = {
|
|
33
|
+
before: Before<BeforeAfterContext>;
|
|
34
|
+
after?: After<BeforeAfterContext>;
|
|
35
|
+
} | {
|
|
36
|
+
before?: undefined;
|
|
37
|
+
};
|
|
38
|
+
type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
|
|
39
|
+
router: Router<ContextFrom, unknown, any>;
|
|
40
|
+
getContext: GetContext<ContextFrom>;
|
|
41
|
+
onError(arg: {
|
|
42
|
+
error: Error;
|
|
43
|
+
req: KaitoRequest;
|
|
44
|
+
}): Promise<KaitoError | {
|
|
45
|
+
status: number;
|
|
46
|
+
message: string;
|
|
47
|
+
}>;
|
|
48
|
+
};
|
|
49
|
+
declare function createKaitoHandler<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): (request: Request) => Promise<Response>;
|
|
50
|
+
|
|
51
|
+
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;
|
|
52
|
+
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
53
|
+
type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
|
|
54
|
+
routes: Set<Routes>;
|
|
55
|
+
through: (context: ContextFrom) => Promise<ContextTo>;
|
|
56
|
+
};
|
|
57
|
+
type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
|
|
58
|
+
declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
|
|
59
|
+
private readonly state;
|
|
60
|
+
static create: <Context>() => Router<Context, Context, never>;
|
|
61
|
+
private static parseQuery;
|
|
62
|
+
constructor(options: RouterState<R, ContextFrom, ContextTo>);
|
|
63
|
+
get routes(): Set<R>;
|
|
64
|
+
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>>;
|
|
65
|
+
readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
|
|
66
|
+
freeze: (server: ServerConfig<ContextFrom, any>) => (req: Request) => Promise<Response>;
|
|
67
|
+
private readonly method;
|
|
68
|
+
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>>;
|
|
69
|
+
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>>;
|
|
70
|
+
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>>;
|
|
71
|
+
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>>;
|
|
72
|
+
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>>;
|
|
73
|
+
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>>;
|
|
74
|
+
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>>;
|
|
75
|
+
through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type ErroredAPIResponse = {
|
|
79
|
+
success: false;
|
|
80
|
+
data: null;
|
|
81
|
+
message: string;
|
|
82
|
+
};
|
|
83
|
+
type SuccessfulAPIResponse<T> = {
|
|
84
|
+
success: true;
|
|
85
|
+
data: T;
|
|
86
|
+
message: 'OK';
|
|
87
|
+
};
|
|
88
|
+
type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
|
|
89
|
+
type AnyResponse = APIResponse<unknown>;
|
|
90
|
+
declare function apiresponse<T>(status: number, response: APIResponse<T>): Response;
|
|
91
|
+
type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
|
|
92
|
+
[k in Param | keyof ExtractRouteParams<Rest>]: string;
|
|
93
|
+
} : T extends `${string}:${infer Param}` ? {
|
|
94
|
+
[k in Param]: string;
|
|
95
|
+
} : {};
|
|
96
|
+
type GetContext<Result> = (req: KaitoRequest) => Promise<Result>;
|
|
97
|
+
/**
|
|
98
|
+
* A helper function to create typed necessary functions
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const {router, getContext} = createUtilities(async (req, res) => {
|
|
103
|
+
* // Return context here
|
|
104
|
+
* })
|
|
105
|
+
*
|
|
106
|
+
* const app = router().get('/', async () => "hello");
|
|
107
|
+
*
|
|
108
|
+
* const server = createServer({
|
|
109
|
+
* router: app,
|
|
110
|
+
* getContext,
|
|
111
|
+
* // ...
|
|
112
|
+
* });
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
declare function createUtilities<Context>(getContext: GetContext<Context>): {
|
|
116
|
+
getContext: GetContext<Context>;
|
|
117
|
+
router: () => Router<Context, Context, never>;
|
|
118
|
+
};
|
|
119
|
+
interface Parsable<Output = any, Input = Output> {
|
|
120
|
+
_input: Input;
|
|
121
|
+
parse: (value: unknown) => Output;
|
|
122
|
+
}
|
|
123
|
+
type InferParsable<T> = T extends Parsable<infer Output, infer Input> ? {
|
|
124
|
+
input: Input;
|
|
125
|
+
output: Output;
|
|
126
|
+
} : never;
|
|
127
|
+
declare function parsable<T>(parse: (value: unknown) => T): Parsable<T, T>;
|
|
128
|
+
|
|
129
|
+
type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
|
|
130
|
+
ctx: Context;
|
|
131
|
+
body: BodyOutput;
|
|
132
|
+
query: QueryOutput;
|
|
133
|
+
params: ExtractRouteParams<Path>;
|
|
134
|
+
};
|
|
135
|
+
type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
|
|
136
|
+
type Through<From, To> = (context: From) => Promise<To>;
|
|
137
|
+
type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, Body extends Parsable> = {
|
|
138
|
+
through: Through<ContextFrom, ContextTo>;
|
|
139
|
+
body?: Body;
|
|
140
|
+
query?: Query;
|
|
141
|
+
path: Path;
|
|
142
|
+
method: Method;
|
|
143
|
+
run(arg: RouteArgument<Path, ContextTo, {
|
|
144
|
+
[Key in keyof Query]: InferParsable<Query[Key]>['output'];
|
|
145
|
+
}, InferParsable<Body>['output']>): Promise<Result>;
|
|
146
|
+
};
|
|
147
|
+
type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
|
|
148
|
+
|
|
149
|
+
export { type APIResponse, type After, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type ServerConfigWithBefore, type SuccessfulAPIResponse, type Through, WrappedError, apiresponse, createKaitoHandler, createUtilities, parsable };
|