@kaito-http/core 4.0.0-beta.2 → 4.0.0-beta.21

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 CHANGED
@@ -1,6 +1,25 @@
1
+ import {
2
+ BaseSchema,
3
+ KArray,
4
+ KBoolean,
5
+ KLiteral,
6
+ KNull,
7
+ KNumber,
8
+ KObject,
9
+ KObjectFromURLSearchParams,
10
+ KRef,
11
+ KScalar,
12
+ KString,
13
+ KUnion,
14
+ ParseContext,
15
+ STRING_FORMAT_REGEXES,
16
+ SchemaError,
17
+ isPrimitiveJSONValue,
18
+ k
19
+ } from "./chunk-TL3E52YN.js";
20
+
1
21
  // src/router/router.ts
2
- import { z } from "zod";
3
- import { createDocument } from "zod-openapi";
22
+ import "openapi3-ts/oas31";
4
23
 
5
24
  // src/error.ts
6
25
  var WrappedError = class _WrappedError extends Error {
@@ -113,38 +132,44 @@ var isNodeLikeDev = typeof process !== "undefined" && typeof process.env !== "un
113
132
 
114
133
  // src/router/router.ts
115
134
  var Router = class _Router {
116
- state;
117
- static create = (config) => new _Router({
118
- through: async (context) => context,
119
- routes: /* @__PURE__ */ new Set(),
120
- config
121
- });
135
+ #state;
136
+ static create = (config = {}) => {
137
+ return new _Router({
138
+ through: (context) => context,
139
+ routes: /* @__PURE__ */ new Set(),
140
+ config
141
+ });
142
+ };
122
143
  constructor(state) {
123
- this.state = state;
144
+ this.#state = state;
124
145
  }
125
146
  get routes() {
126
- return this.state.routes;
147
+ return this.#state.routes;
127
148
  }
128
149
  add = (method, path, route) => {
129
150
  const merged = {
130
151
  ...typeof route === "object" ? route : { run: route },
131
152
  method,
132
153
  path,
133
- through: this.state.through
154
+ router: this
134
155
  };
135
156
  return new _Router({
136
- ...this.state,
137
- routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
157
+ ...this.#state,
158
+ routes: /* @__PURE__ */ new Set([...this.#state.routes, merged])
138
159
  });
139
160
  };
161
+ params = () => this;
140
162
  merge = (pathPrefix, other) => {
141
- const newRoutes = [...other.state.routes].map((route) => ({
163
+ const newRoutes = [...other.#state.routes].map((route) => ({
142
164
  ...route,
143
- path: `${pathPrefix}${route.path}`
165
+ // handle pathPrefix = / & route.path = / case causing //
166
+ // we intentionally are replacing on the joining path and not the pathPrefix, in case of
167
+ // /named -> merged to -> / causing /named/ not /named
168
+ path: `${pathPrefix}${route.path === "/" ? "" : route.path}`
144
169
  }));
145
170
  return new _Router({
146
- ...this.state,
147
- routes: /* @__PURE__ */ new Set([...this.state.routes, ...newRoutes])
171
+ ...this.#state,
172
+ routes: /* @__PURE__ */ new Set([...this.#state.routes, ...newRoutes])
148
173
  });
149
174
  };
150
175
  static getFindRoute = (routes) => (method, path) => {
@@ -172,32 +197,22 @@ var Router = class _Router {
172
197
  }
173
198
  return {};
174
199
  };
175
- static buildQuerySchema = (schema) => {
176
- const keys = Object.keys(schema);
177
- return z.instanceof(URLSearchParams).transform((params) => {
178
- const result = {};
179
- for (const key of keys) {
180
- result[key] = params.get(key);
181
- }
182
- return result;
183
- }).pipe(z.object(schema));
184
- };
185
200
  serve = () => {
186
201
  const methodToRoutesMap = /* @__PURE__ */ new Map();
187
- for (const route of this.state.routes) {
202
+ for (const route of this.#state.routes) {
188
203
  if (!methodToRoutesMap.has(route.method)) {
189
204
  methodToRoutesMap.set(route.method, /* @__PURE__ */ new Map());
190
205
  }
191
206
  methodToRoutesMap.get(route.method).set(route.path, {
192
207
  ...route,
193
- fastQuerySchema: route.query ? _Router.buildQuerySchema(route.query) : void 0
208
+ fastQuerySchema: route.query ? k.objectFromURLSearchParams(route.query) : void 0
194
209
  });
195
210
  }
196
211
  const findRoute = _Router.getFindRoute(methodToRoutesMap);
197
- const handle = async (req) => {
212
+ const handle = async (req, ...args) => {
198
213
  const url = new URL(req.url);
199
214
  const method = req.method;
200
- const { route, params } = findRoute(method, url.pathname);
215
+ const { route, params: rawParams } = findRoute(method, url.pathname);
201
216
  if (!route) {
202
217
  const body = {
203
218
  success: false,
@@ -209,14 +224,17 @@ var Router = class _Router {
209
224
  const request = new KaitoRequest(url, req);
210
225
  const head = new KaitoHead();
211
226
  try {
212
- const body = route.body ? await route.body.parseAsync(await req.json()) : void 0;
213
- const query = route.fastQuerySchema ? await route.fastQuerySchema.parseAsync(url.searchParams) : {};
214
- const ctx = await route.through(await this.state.config.getContext?.(request, head) ?? null);
227
+ const body = route.body ? await route.body.parse(await req.json()) : void 0;
228
+ const query = route.fastQuerySchema ? route.fastQuerySchema.parse(url.searchParams) : {};
229
+ const ctx = await route.router.#state.through(
230
+ await this.#state.config.getContext?.(request, head, ...args) ?? null,
231
+ rawParams
232
+ );
215
233
  const result = await route.run({
216
234
  ctx,
217
235
  body,
218
236
  query,
219
- params
237
+ params: rawParams
220
238
  });
221
239
  if (result instanceof Response) {
222
240
  if (isNodeLikeDev) {
@@ -232,6 +250,18 @@ var Router = class _Router {
232
250
  }
233
251
  return result;
234
252
  }
253
+ if (route.openapi?.schema) {
254
+ if (route.openapi.type !== "json") {
255
+ throw new Error(
256
+ `Cannot use openapi schema for ${route.method} ${route.path} because it is not a json output type`
257
+ );
258
+ }
259
+ const parsed = route.openapi.schema.serialize(result);
260
+ return head.toResponse({
261
+ success: true,
262
+ data: parsed
263
+ });
264
+ }
235
265
  return head.toResponse({
236
266
  success: true,
237
267
  data: result
@@ -245,7 +275,7 @@ var Router = class _Router {
245
275
  message: error.message
246
276
  });
247
277
  }
248
- if (!this.state.config.onError) {
278
+ if (!this.#state.config.onError) {
249
279
  return head.status(500).toResponse({
250
280
  success: false,
251
281
  data: null,
@@ -253,14 +283,14 @@ var Router = class _Router {
253
283
  });
254
284
  }
255
285
  try {
256
- const { status, message } = await this.state.config.onError(error, request);
286
+ const { status, message } = await this.#state.config.onError(error, request);
257
287
  return head.status(status).toResponse({
258
288
  success: false,
259
289
  data: null,
260
290
  message
261
291
  });
262
292
  } catch (e2) {
263
- console.error("KAITO - Failed to handle error inside `.onError()`, returning 500 and Internal Server Error");
293
+ console.error("[Kaito] Failed to handle error inside `.onError()`, returning 500 and Internal Server Error");
264
294
  console.error(e2);
265
295
  return head.status(500).toResponse({
266
296
  success: false,
@@ -270,12 +300,12 @@ var Router = class _Router {
270
300
  }
271
301
  }
272
302
  };
273
- return async (request) => {
274
- if (this.state.config.before) {
275
- const result = await this.state.config.before(request);
303
+ return async (request, ...args) => {
304
+ if (this.#state.config.before) {
305
+ const result = await this.#state.config.before(request);
276
306
  if (result instanceof Response) {
277
- if (this.state.config.transform) {
278
- const transformed = await this.state.config.transform(request, result);
307
+ if (this.#state.config.transform) {
308
+ const transformed = await this.#state.config.transform(request, result);
279
309
  if (transformed instanceof Response) {
280
310
  return result;
281
311
  }
@@ -283,9 +313,9 @@ var Router = class _Router {
283
313
  return result;
284
314
  }
285
315
  }
286
- const response = await handle(request);
287
- if (this.state.config.transform) {
288
- const transformed = await this.state.config.transform(request, response);
316
+ const response = await handle(request, ...args);
317
+ if (this.#state.config.transform) {
318
+ const transformed = await this.#state.config.transform(request, response);
289
319
  if (transformed instanceof Response) {
290
320
  return transformed;
291
321
  }
@@ -293,66 +323,70 @@ var Router = class _Router {
293
323
  return response;
294
324
  };
295
325
  };
296
- openapi = (highLevelSpec) => {
297
- const OPENAPI_VERSION = "3.0.0";
326
+ openapi = ({
327
+ info,
328
+ servers
329
+ }) => {
330
+ const OPENAPI_VERSION = "3.1.0";
298
331
  const paths = {};
299
- for (const route of this.state.routes) {
300
- const path = route.path;
301
- const pathWithColonParamsReplaceWithCurlyBraces = path.replace(/:(\w+)/g, "{$1}");
332
+ for (const route of this.#state.routes) {
333
+ if (!route.openapi) {
334
+ continue;
335
+ }
336
+ const pathWithColonParamsReplaceWithCurlyBraces = route.path.replace(/:(\w+)/g, "{$1}");
302
337
  if (!paths[pathWithColonParamsReplaceWithCurlyBraces]) {
303
338
  paths[pathWithColonParamsReplaceWithCurlyBraces] = {};
304
339
  }
340
+ let contentType;
341
+ const type = route.openapi.type;
342
+ switch (type) {
343
+ case "json":
344
+ contentType = "application/json";
345
+ break;
346
+ case "sse":
347
+ contentType = "text/event-stream";
348
+ break;
349
+ default:
350
+ throw new Error(`Unknown output type in route ${route.method} ${route.path}: ${type}`);
351
+ }
305
352
  const item = {
306
353
  description: route.openapi?.description ?? "Successful response",
307
354
  responses: {
308
355
  200: {
309
356
  description: route.openapi?.description ?? "Successful response",
310
- ...route.openapi ? {
311
- content: {
312
- [{
313
- json: "application/json",
314
- sse: "text/event-stream"
315
- }[route.openapi.body.type]]: { schema: route.openapi?.body.schema }
357
+ content: {
358
+ [contentType]: {
359
+ schema: k.object({
360
+ success: k.literal(true),
361
+ data: route.openapi.schema
362
+ }).toOpenAPI()
316
363
  }
317
- } : {}
364
+ }
318
365
  }
319
366
  }
320
367
  };
321
368
  if (route.body) {
322
369
  item.requestBody = {
323
370
  content: {
324
- "application/json": { schema: route.body }
371
+ "application/json": { schema: route.body.toOpenAPI() }
325
372
  }
326
373
  };
327
374
  }
328
- const params = {};
329
- if (route.query) {
330
- params.query = z.object(route.query);
331
- }
332
- const urlParams = path.match(/:(\w+)/g);
333
- if (urlParams) {
334
- const pathParams = {};
335
- for (const param of urlParams) {
336
- pathParams[param.slice(1)] = z.string();
337
- }
338
- params.path = z.object(pathParams);
339
- }
340
- item.requestParams = params;
341
375
  paths[pathWithColonParamsReplaceWithCurlyBraces][route.method.toLowerCase()] = item;
342
376
  }
343
- const doc = createDocument({
377
+ const doc = {
344
378
  openapi: OPENAPI_VERSION,
379
+ info,
345
380
  paths,
346
- ...highLevelSpec,
347
- servers: Object.entries(highLevelSpec.servers ?? {}).map((entry) => {
381
+ servers: Object.entries(servers ?? {}).map((entry) => {
348
382
  const [url, description] = entry;
349
383
  return {
350
384
  url,
351
385
  description
352
386
  };
353
387
  })
354
- });
355
- return this.add("GET", "/openapi.json", async () => Response.json(doc));
388
+ };
389
+ return this.get("/openapi.json", () => Response.json(doc));
356
390
  };
357
391
  method = (method) => {
358
392
  return (path, route) => this.add(method, path, route);
@@ -366,21 +400,43 @@ var Router = class _Router {
366
400
  options = this.method("OPTIONS");
367
401
  through = (through) => {
368
402
  return new _Router({
369
- ...this.state,
370
- through: async (context) => await through(await this.state.through(context))
403
+ ...this.#state,
404
+ through: (context, params) => {
405
+ const next = this.#state.through(context, params);
406
+ if (next instanceof Promise) {
407
+ return next.then((next2) => through(next2, params));
408
+ }
409
+ return through(next, params);
410
+ }
371
411
  });
372
412
  };
373
413
  };
374
414
 
375
- // src/create.ts
376
- function create(config = {}) {
377
- return () => Router.create(config);
378
- }
415
+ // src/index.ts
416
+ var create = Router.create;
379
417
  export {
418
+ BaseSchema,
419
+ KArray,
420
+ KBoolean,
421
+ KLiteral,
422
+ KNull,
423
+ KNumber,
424
+ KObject,
425
+ KObjectFromURLSearchParams,
426
+ KRef,
427
+ KScalar,
428
+ KString,
429
+ KUnion,
380
430
  KaitoError,
431
+ KaitoHead,
381
432
  KaitoRequest,
433
+ ParseContext,
382
434
  Router,
435
+ STRING_FORMAT_REGEXES,
436
+ SchemaError,
383
437
  WrappedError,
384
438
  create,
385
- isNodeLikeDev
439
+ isNodeLikeDev,
440
+ isPrimitiveJSONValue,
441
+ k
386
442
  };