@kaito-http/core 3.0.3 → 4.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/cors/cors.cjs +85 -17
- package/dist/cors/cors.d.cts +122 -26
- package/dist/cors/cors.d.ts +122 -26
- package/dist/cors/cors.js +85 -17
- package/dist/index.cjs +223 -124
- package/dist/index.d.cts +104 -83
- package/dist/index.d.ts +104 -83
- package/dist/index.js +223 -120
- package/dist/stream/stream.cjs +2 -4
- package/dist/stream/stream.d.cts +1 -1
- package/dist/stream/stream.d.ts +1 -1
- package/dist/stream/stream.js +2 -4
- package/package.json +25 -20
package/dist/index.cjs
CHANGED
|
@@ -25,19 +25,18 @@ __export(index_exports, {
|
|
|
25
25
|
KaitoRequest: () => KaitoRequest,
|
|
26
26
|
Router: () => Router,
|
|
27
27
|
WrappedError: () => WrappedError,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
isNodeLikeDev: () => isNodeLikeDev,
|
|
31
|
-
parsable: () => parsable
|
|
28
|
+
create: () => create,
|
|
29
|
+
isNodeLikeDev: () => isNodeLikeDev
|
|
32
30
|
});
|
|
33
31
|
module.exports = __toCommonJS(index_exports);
|
|
34
32
|
|
|
33
|
+
// src/router/router.ts
|
|
34
|
+
var import_zod = require("zod");
|
|
35
|
+
var import_zod_openapi = require("zod-openapi");
|
|
36
|
+
var import_extend = require("zod-openapi/extend");
|
|
37
|
+
|
|
35
38
|
// src/error.ts
|
|
36
39
|
var WrappedError = class _WrappedError extends Error {
|
|
37
|
-
constructor(data) {
|
|
38
|
-
super("Something was thrown, but it was not an instance of Error, so a WrappedError was created.");
|
|
39
|
-
this.data = data;
|
|
40
|
-
}
|
|
41
40
|
static maybe(maybeError) {
|
|
42
41
|
if (maybeError instanceof Error) {
|
|
43
42
|
return maybeError;
|
|
@@ -47,56 +46,39 @@ var WrappedError = class _WrappedError extends Error {
|
|
|
47
46
|
static from(data) {
|
|
48
47
|
return new _WrappedError(data);
|
|
49
48
|
}
|
|
49
|
+
data;
|
|
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
|
+
}
|
|
50
54
|
};
|
|
51
55
|
var KaitoError = class extends Error {
|
|
56
|
+
status;
|
|
52
57
|
constructor(status, message) {
|
|
53
58
|
super(message);
|
|
54
59
|
this.status = status;
|
|
55
60
|
}
|
|
56
61
|
};
|
|
57
62
|
|
|
58
|
-
// src/handler.ts
|
|
59
|
-
function createKaitoHandler(config) {
|
|
60
|
-
const handle = config.router.freeze(config);
|
|
61
|
-
return async (request) => {
|
|
62
|
-
if (config.before) {
|
|
63
|
-
const result = await config.before(request);
|
|
64
|
-
if (result instanceof Response) {
|
|
65
|
-
if (config.transform) {
|
|
66
|
-
const result2 = await config.transform(request, result);
|
|
67
|
-
if (result2 instanceof Response) return result;
|
|
68
|
-
}
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const response = await handle(request);
|
|
73
|
-
if (config.transform) {
|
|
74
|
-
const result = await config.transform(request, response);
|
|
75
|
-
if (result instanceof Response) return result;
|
|
76
|
-
}
|
|
77
|
-
return response;
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
63
|
// src/head.ts
|
|
82
64
|
var KaitoHead = class {
|
|
83
|
-
|
|
84
|
-
|
|
65
|
+
#headers;
|
|
66
|
+
#status;
|
|
85
67
|
constructor() {
|
|
86
|
-
this
|
|
87
|
-
this
|
|
68
|
+
this.#headers = null;
|
|
69
|
+
this.#status = 200;
|
|
88
70
|
}
|
|
89
71
|
get headers() {
|
|
90
|
-
if (this
|
|
91
|
-
this
|
|
72
|
+
if (this.#headers === null) {
|
|
73
|
+
this.#headers = new Headers();
|
|
92
74
|
}
|
|
93
|
-
return this
|
|
75
|
+
return this.#headers;
|
|
94
76
|
}
|
|
95
77
|
status(status) {
|
|
96
78
|
if (status === void 0) {
|
|
97
|
-
return this
|
|
79
|
+
return this.#status;
|
|
98
80
|
}
|
|
99
|
-
this
|
|
81
|
+
this.#status = status;
|
|
100
82
|
return this;
|
|
101
83
|
}
|
|
102
84
|
/**
|
|
@@ -106,10 +88,10 @@ var KaitoHead = class {
|
|
|
106
88
|
*/
|
|
107
89
|
toResponse(body) {
|
|
108
90
|
const init = {
|
|
109
|
-
status: this
|
|
91
|
+
status: this.#status
|
|
110
92
|
};
|
|
111
|
-
if (this
|
|
112
|
-
init.headers = this
|
|
93
|
+
if (this.#headers) {
|
|
94
|
+
init.headers = this.#headers;
|
|
113
95
|
}
|
|
114
96
|
return Response.json(body, init);
|
|
115
97
|
}
|
|
@@ -117,7 +99,7 @@ var KaitoHead = class {
|
|
|
117
99
|
* Whether this KaitoHead instance has been touched/modified
|
|
118
100
|
*/
|
|
119
101
|
get touched() {
|
|
120
|
-
return this
|
|
102
|
+
return this.#status !== 200 || this.#headers !== null;
|
|
121
103
|
}
|
|
122
104
|
};
|
|
123
105
|
|
|
@@ -161,102 +143,102 @@ var KaitoRequest = class {
|
|
|
161
143
|
|
|
162
144
|
// src/util.ts
|
|
163
145
|
var isNodeLikeDev = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.NODE_ENV === "development";
|
|
164
|
-
function createUtilities(getContext) {
|
|
165
|
-
return {
|
|
166
|
-
getContext,
|
|
167
|
-
router: () => Router.create()
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
function parsable(parse) {
|
|
171
|
-
return {
|
|
172
|
-
parse
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
146
|
|
|
176
147
|
// src/router/router.ts
|
|
177
148
|
var Router = class _Router {
|
|
178
149
|
state;
|
|
179
|
-
static create = () => new _Router({
|
|
150
|
+
static create = (config) => new _Router({
|
|
180
151
|
through: async (context) => context,
|
|
181
|
-
routes: /* @__PURE__ */ new Set()
|
|
152
|
+
routes: /* @__PURE__ */ new Set(),
|
|
153
|
+
config,
|
|
154
|
+
paramsSchema: null
|
|
182
155
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return {};
|
|
186
|
-
}
|
|
187
|
-
const result = {};
|
|
188
|
-
for (const key in schema) {
|
|
189
|
-
if (!schema.hasOwnProperty(key)) continue;
|
|
190
|
-
const value = url.searchParams.get(key);
|
|
191
|
-
result[key] = schema[key].parse(value);
|
|
192
|
-
}
|
|
193
|
-
return result;
|
|
194
|
-
}
|
|
195
|
-
constructor(options) {
|
|
196
|
-
this.state = options;
|
|
156
|
+
constructor(state) {
|
|
157
|
+
this.state = state;
|
|
197
158
|
}
|
|
198
159
|
get routes() {
|
|
199
160
|
return this.state.routes;
|
|
200
161
|
}
|
|
201
162
|
add = (method, path, route) => {
|
|
202
163
|
const merged = {
|
|
203
|
-
// TODO: Ideally fix the typing here, but this will be replaced in Kaito v4 where all routes must return a Response (which we can type)
|
|
204
164
|
...typeof route === "object" ? route : { run: route },
|
|
205
165
|
method,
|
|
206
166
|
path,
|
|
207
|
-
|
|
167
|
+
router: this
|
|
208
168
|
};
|
|
209
169
|
return new _Router({
|
|
210
170
|
...this.state,
|
|
211
171
|
routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
|
|
212
172
|
});
|
|
213
173
|
};
|
|
174
|
+
params = (spec) => new _Router({
|
|
175
|
+
...this.state,
|
|
176
|
+
paramsSchema: import_zod.z.object(spec)
|
|
177
|
+
});
|
|
214
178
|
merge = (pathPrefix, other) => {
|
|
215
179
|
const newRoutes = [...other.state.routes].map((route) => ({
|
|
216
180
|
...route,
|
|
217
|
-
|
|
181
|
+
// handle pathPrefix = / & route.path = / case causing //
|
|
182
|
+
// we intentionally are replacing on the joining path and not the pathPrefix, in case of
|
|
183
|
+
// /named -> merged to -> / causing /named/ not /named
|
|
184
|
+
path: `${pathPrefix}${route.path === "/" ? "" : route.path}`
|
|
218
185
|
}));
|
|
219
186
|
return new _Router({
|
|
220
187
|
...this.state,
|
|
221
188
|
routes: /* @__PURE__ */ new Set([...this.state.routes, ...newRoutes])
|
|
222
189
|
});
|
|
223
190
|
};
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
191
|
+
static getFindRoute = (routes) => (method, path) => {
|
|
192
|
+
const params = {};
|
|
193
|
+
const pathParts = path.split("/").filter(Boolean);
|
|
194
|
+
const methodRoutes = routes.get(method);
|
|
195
|
+
if (!methodRoutes) return {};
|
|
196
|
+
for (const [routePath, route] of methodRoutes) {
|
|
197
|
+
const routeParts = routePath.split("/").filter(Boolean);
|
|
198
|
+
if (routeParts.length !== pathParts.length) {
|
|
199
|
+
continue;
|
|
229
200
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
for (let i = 0; i < routeParts.length; i++) {
|
|
240
|
-
const routePart = routeParts[i];
|
|
241
|
-
const pathPart = pathParts[i];
|
|
242
|
-
if (routePart && pathPart && routePart.startsWith(":")) {
|
|
243
|
-
params[routePart.slice(1)] = pathPart;
|
|
244
|
-
} else if (routePart !== pathPart) {
|
|
245
|
-
matches = false;
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
if (matches) {
|
|
250
|
-
const route = methodHandlers.get(method);
|
|
251
|
-
if (route) return { route, params };
|
|
201
|
+
let matches = true;
|
|
202
|
+
for (let i = 0; i < routeParts.length; i++) {
|
|
203
|
+
const routePart = routeParts[i];
|
|
204
|
+
const pathPart = pathParts[i];
|
|
205
|
+
if (routePart && pathPart && routePart.startsWith(":")) {
|
|
206
|
+
params[routePart.slice(1)] = pathPart;
|
|
207
|
+
} else if (routePart !== pathPart) {
|
|
208
|
+
matches = false;
|
|
209
|
+
break;
|
|
252
210
|
}
|
|
253
211
|
}
|
|
254
|
-
return { params };
|
|
255
|
-
}
|
|
256
|
-
return
|
|
212
|
+
if (matches) return { route, params };
|
|
213
|
+
}
|
|
214
|
+
return {};
|
|
215
|
+
};
|
|
216
|
+
static buildQuerySchema = (schema) => {
|
|
217
|
+
const keys = Object.keys(schema);
|
|
218
|
+
return import_zod.z.instanceof(URLSearchParams).transform((params) => {
|
|
219
|
+
const result = {};
|
|
220
|
+
for (const key of keys) {
|
|
221
|
+
result[key] = params.get(key);
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
}).pipe(import_zod.z.object(schema));
|
|
225
|
+
};
|
|
226
|
+
serve = () => {
|
|
227
|
+
const methodToRoutesMap = /* @__PURE__ */ new Map();
|
|
228
|
+
for (const route of this.state.routes) {
|
|
229
|
+
if (!methodToRoutesMap.has(route.method)) {
|
|
230
|
+
methodToRoutesMap.set(route.method, /* @__PURE__ */ new Map());
|
|
231
|
+
}
|
|
232
|
+
methodToRoutesMap.get(route.method).set(route.path, {
|
|
233
|
+
...route,
|
|
234
|
+
fastQuerySchema: route.query ? _Router.buildQuerySchema(route.query) : void 0
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const findRoute = _Router.getFindRoute(methodToRoutesMap);
|
|
238
|
+
const handle = async (req) => {
|
|
257
239
|
const url = new URL(req.url);
|
|
258
240
|
const method = req.method;
|
|
259
|
-
const { route, params } = findRoute(method, url.pathname);
|
|
241
|
+
const { route, params: rawParams } = findRoute(method, url.pathname);
|
|
260
242
|
if (!route) {
|
|
261
243
|
const body = {
|
|
262
244
|
success: false,
|
|
@@ -268,10 +250,13 @@ var Router = class _Router {
|
|
|
268
250
|
const request = new KaitoRequest(url, req);
|
|
269
251
|
const head = new KaitoHead();
|
|
270
252
|
try {
|
|
271
|
-
const body = route.body ? await route.body.
|
|
272
|
-
const query =
|
|
273
|
-
const
|
|
274
|
-
const ctx = await route.through(
|
|
253
|
+
const body = route.body ? await route.body.parseAsync(await req.json()) : void 0;
|
|
254
|
+
const query = route.fastQuerySchema ? await route.fastQuerySchema.parseAsync(url.searchParams) : {};
|
|
255
|
+
const params = route.router.state.paramsSchema ? route.router.state.paramsSchema.parse(rawParams) : rawParams;
|
|
256
|
+
const ctx = await route.router.state.through(
|
|
257
|
+
await this.state.config.getContext?.(request, head) ?? null,
|
|
258
|
+
params
|
|
259
|
+
);
|
|
275
260
|
const result = await route.run({
|
|
276
261
|
ctx,
|
|
277
262
|
body,
|
|
@@ -294,8 +279,7 @@ var Router = class _Router {
|
|
|
294
279
|
}
|
|
295
280
|
return head.toResponse({
|
|
296
281
|
success: true,
|
|
297
|
-
data: result
|
|
298
|
-
message: "OK"
|
|
282
|
+
data: result
|
|
299
283
|
});
|
|
300
284
|
} catch (e) {
|
|
301
285
|
const error = WrappedError.maybe(e);
|
|
@@ -306,17 +290,129 @@ var Router = class _Router {
|
|
|
306
290
|
message: error.message
|
|
307
291
|
});
|
|
308
292
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
293
|
+
if (!this.state.config.onError) {
|
|
294
|
+
return head.status(500).toResponse({
|
|
295
|
+
success: false,
|
|
296
|
+
data: null,
|
|
297
|
+
message: "Internal Server Error"
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
const { status, message } = await this.state.config.onError(error, request);
|
|
302
|
+
return head.status(status).toResponse({
|
|
303
|
+
success: false,
|
|
304
|
+
data: null,
|
|
305
|
+
message
|
|
306
|
+
});
|
|
307
|
+
} catch (e2) {
|
|
308
|
+
console.error("KAITO - Failed to handle error inside `.onError()`, returning 500 and Internal Server Error");
|
|
309
|
+
console.error(e2);
|
|
310
|
+
return head.status(500).toResponse({
|
|
311
|
+
success: false,
|
|
312
|
+
data: null,
|
|
313
|
+
message: "Internal Server Error"
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
return async (request) => {
|
|
319
|
+
if (this.state.config.before) {
|
|
320
|
+
const result = await this.state.config.before(request);
|
|
321
|
+
if (result instanceof Response) {
|
|
322
|
+
if (this.state.config.transform) {
|
|
323
|
+
const transformed = await this.state.config.transform(request, result);
|
|
324
|
+
if (transformed instanceof Response) {
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const response = await handle(request);
|
|
332
|
+
if (this.state.config.transform) {
|
|
333
|
+
const transformed = await this.state.config.transform(request, response);
|
|
334
|
+
if (transformed instanceof Response) {
|
|
335
|
+
return transformed;
|
|
336
|
+
}
|
|
315
337
|
}
|
|
338
|
+
return response;
|
|
316
339
|
};
|
|
317
340
|
};
|
|
318
|
-
|
|
319
|
-
|
|
341
|
+
openapi = (highLevelSpec) => {
|
|
342
|
+
const OPENAPI_VERSION = "3.0.0";
|
|
343
|
+
const paths = {};
|
|
344
|
+
for (const route of this.state.routes) {
|
|
345
|
+
const path = route.path;
|
|
346
|
+
if (!route.openapi) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const pathWithColonParamsReplaceWithCurlyBraces = path.replace(/:(\w+)/g, "{$1}");
|
|
350
|
+
if (!paths[pathWithColonParamsReplaceWithCurlyBraces]) {
|
|
351
|
+
paths[pathWithColonParamsReplaceWithCurlyBraces] = {};
|
|
352
|
+
}
|
|
353
|
+
const content = route.openapi.body.type === "json" ? {
|
|
354
|
+
"application/json": {
|
|
355
|
+
schema: import_zod.z.object({
|
|
356
|
+
success: import_zod.z.literal(true).openapi({
|
|
357
|
+
type: "boolean",
|
|
358
|
+
enum: [true]
|
|
359
|
+
// Need this as zod-openapi doesn't properly work with literals
|
|
360
|
+
}),
|
|
361
|
+
data: route.openapi.body.schema
|
|
362
|
+
})
|
|
363
|
+
}
|
|
364
|
+
} : {
|
|
365
|
+
"text/event-stream": {
|
|
366
|
+
schema: route.openapi.body.schema
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
const item = {
|
|
370
|
+
description: route.openapi?.description ?? "Successful response",
|
|
371
|
+
responses: {
|
|
372
|
+
200: {
|
|
373
|
+
description: route.openapi?.description ?? "Successful response",
|
|
374
|
+
content
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
if (route.body) {
|
|
379
|
+
item.requestBody = {
|
|
380
|
+
content: {
|
|
381
|
+
"application/json": { schema: route.body }
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
const params = {};
|
|
386
|
+
if (route.query) {
|
|
387
|
+
params.query = import_zod.z.object(route.query);
|
|
388
|
+
}
|
|
389
|
+
const urlParams = path.match(/:(\w+)/g);
|
|
390
|
+
if (urlParams) {
|
|
391
|
+
const pathParams = {};
|
|
392
|
+
for (const param of urlParams) {
|
|
393
|
+
pathParams[param.slice(1)] = import_zod.z.string();
|
|
394
|
+
}
|
|
395
|
+
params.path = import_zod.z.object(pathParams);
|
|
396
|
+
}
|
|
397
|
+
item.requestParams = params;
|
|
398
|
+
paths[pathWithColonParamsReplaceWithCurlyBraces][route.method.toLowerCase()] = item;
|
|
399
|
+
}
|
|
400
|
+
const doc = (0, import_zod_openapi.createDocument)({
|
|
401
|
+
openapi: OPENAPI_VERSION,
|
|
402
|
+
paths,
|
|
403
|
+
...highLevelSpec,
|
|
404
|
+
servers: Object.entries(highLevelSpec.servers ?? {}).map((entry) => {
|
|
405
|
+
const [url, description] = entry;
|
|
406
|
+
return {
|
|
407
|
+
url,
|
|
408
|
+
description
|
|
409
|
+
};
|
|
410
|
+
})
|
|
411
|
+
});
|
|
412
|
+
return this.get("/openapi.json", () => Response.json(doc));
|
|
413
|
+
};
|
|
414
|
+
method = (method) => {
|
|
415
|
+
return (path, route) => this.add(method, path, route);
|
|
320
416
|
};
|
|
321
417
|
get = this.method("GET");
|
|
322
418
|
post = this.method("POST");
|
|
@@ -328,10 +424,15 @@ var Router = class _Router {
|
|
|
328
424
|
through = (through) => {
|
|
329
425
|
return new _Router({
|
|
330
426
|
...this.state,
|
|
331
|
-
through: async (context) => through(await this.state.through(context))
|
|
427
|
+
through: async (context, params) => await through(await this.state.through(context, params), params)
|
|
332
428
|
});
|
|
333
429
|
};
|
|
334
430
|
};
|
|
431
|
+
|
|
432
|
+
// src/create.ts
|
|
433
|
+
function create(config = {}) {
|
|
434
|
+
return Router.create(config);
|
|
435
|
+
}
|
|
335
436
|
// Annotate the CommonJS export names for ESM import in node:
|
|
336
437
|
0 && (module.exports = {
|
|
337
438
|
KaitoError,
|
|
@@ -339,8 +440,6 @@ var Router = class _Router {
|
|
|
339
440
|
KaitoRequest,
|
|
340
441
|
Router,
|
|
341
442
|
WrappedError,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
isNodeLikeDev,
|
|
345
|
-
parsable
|
|
443
|
+
create,
|
|
444
|
+
isNodeLikeDev
|
|
346
445
|
});
|