@kozojs/core 0.2.4 → 0.2.6

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/lib/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  // src/app.ts
2
- import { Hono } from "hono";
2
+ import { Hono } from "hono/quick";
3
3
  import { serve } from "@hono/node-server";
4
- import Ajv3 from "ajv";
5
- import addFormats3 from "ajv-formats";
4
+ import { createServer } from "http";
6
5
 
7
6
  // src/client-generator.ts
8
7
  function generateMethodName(method, path) {
@@ -202,431 +201,2735 @@ function capitalize(str) {
202
201
  return str.charAt(0).toUpperCase() + str.slice(1);
203
202
  }
204
203
 
205
- // src/optimizations/serializer.ts
206
- import fastJson from "fast-json-stringify";
207
- import { zodToJsonSchema } from "zod-to-json-schema";
208
- function isZodSchema(schema) {
209
- return typeof schema === "object" && schema !== null && "safeParse" in schema;
204
+ // src/uws-transport.ts
205
+ import { createServer as netCreateServer } from "net";
206
+
207
+ // src/errors.ts
208
+ var CONTENT_TYPE_PROBLEM = "application/problem+json";
209
+ var ERROR_RESPONSES = {
210
+ VALIDATION_FAILED: {
211
+ type: "https://kozo.dev/errors/validation-failed",
212
+ title: "Validation Failed",
213
+ status: 400
214
+ },
215
+ INVALID_BODY: {
216
+ type: "https://kozo.dev/errors/invalid-body",
217
+ title: "Invalid Request Body",
218
+ status: 400
219
+ },
220
+ INVALID_QUERY: {
221
+ type: "https://kozo.dev/errors/invalid-query",
222
+ title: "Invalid Query Parameters",
223
+ status: 400
224
+ },
225
+ INVALID_PARAMS: {
226
+ type: "https://kozo.dev/errors/invalid-params",
227
+ title: "Invalid Path Parameters",
228
+ status: 400
229
+ },
230
+ INTERNAL_ERROR: {
231
+ type: "https://kozo.dev/errors/internal-error",
232
+ title: "Internal Server Error",
233
+ status: 500
234
+ },
235
+ NOT_FOUND: {
236
+ type: "https://kozo.dev/errors/not-found",
237
+ title: "Resource Not Found",
238
+ status: 404
239
+ },
240
+ UNAUTHORIZED: {
241
+ type: "https://kozo.dev/errors/unauthorized",
242
+ title: "Unauthorized",
243
+ status: 401
244
+ },
245
+ FORBIDDEN: {
246
+ type: "https://kozo.dev/errors/forbidden",
247
+ title: "Forbidden",
248
+ status: 403
249
+ }
250
+ };
251
+ var HDR_PROBLEM = { "Content-Type": CONTENT_TYPE_PROBLEM };
252
+ var INIT_400 = Object.freeze({ status: 400, headers: HDR_PROBLEM });
253
+ var INIT_401 = Object.freeze({ status: 401, headers: HDR_PROBLEM });
254
+ var INIT_403 = Object.freeze({ status: 403, headers: HDR_PROBLEM });
255
+ var INIT_404 = Object.freeze({ status: 404, headers: HDR_PROBLEM });
256
+ var INIT_500 = Object.freeze({ status: 500, headers: HDR_PROBLEM });
257
+ function formatAjvErrors(errors) {
258
+ if (!errors || errors.length === 0) return [];
259
+ return errors.map((err) => ({
260
+ field: err.instancePath?.replace(/^\//, "").replace(/\//g, ".") || err.params?.missingProperty || "unknown",
261
+ message: err.message || "Invalid value",
262
+ code: err.keyword || "invalid",
263
+ value: err.data
264
+ }));
210
265
  }
211
- var ResponseSerializer = class {
212
- serializers = /* @__PURE__ */ new Map();
213
- fallback = JSON.stringify;
214
- compile(routeId, schema) {
215
- try {
216
- let jsonSchema;
217
- const isZod = isZodSchema(schema);
218
- if (isZod) {
219
- jsonSchema = zodToJsonSchema(schema, { $refStrategy: "none", target: "jsonSchema7" });
220
- if (process.env.KOZO_DEBUG_SCHEMA === "true") {
221
- console.log(`
222
- [DIAGNOSTIC] Schema for ${routeId}:`);
223
- console.log(JSON.stringify(jsonSchema, null, 2));
224
- const schemaStr = JSON.stringify(jsonSchema);
225
- const hasAnyOf = schemaStr.includes("anyOf");
226
- const hasOneOf = schemaStr.includes("oneOf");
227
- const hasAllOf = schemaStr.includes("allOf");
228
- if (hasAnyOf || hasOneOf || hasAllOf) {
229
- console.warn(`\u26A0\uFE0F Complex schema detected for ${routeId}:`, { hasAnyOf, hasOneOf, hasAllOf });
230
- console.warn(" This may reduce fast-json-stringify performance");
231
- }
232
- }
233
- } else {
234
- jsonSchema = schema;
235
- }
236
- const stringify = fastJson(jsonSchema);
237
- this.serializers.set(routeId, stringify);
238
- } catch (err) {
239
- console.error(`Failed to compile serializer for ${routeId}`, err);
240
- this.serializers.set(routeId, this.fallback);
241
- }
266
+ function formatZodErrors(errors) {
267
+ if (!errors?.issues) return [];
268
+ return errors.issues.map((issue) => ({
269
+ field: issue.path?.join(".") || "unknown",
270
+ message: issue.message || "Invalid value",
271
+ code: issue.code || "invalid",
272
+ value: issue.input
273
+ }));
274
+ }
275
+ function validationErrorResponse(field, ajvErrors, instance) {
276
+ const body = {
277
+ type: ERROR_RESPONSES.VALIDATION_FAILED.type,
278
+ title: ERROR_RESPONSES.VALIDATION_FAILED.title,
279
+ status: 400,
280
+ errors: formatAjvErrors(ajvErrors)
281
+ };
282
+ if (instance) body.instance = instance;
283
+ return new Response(JSON.stringify(body), INIT_400);
284
+ }
285
+ function internalErrorResponse(err, instance) {
286
+ const body = {
287
+ type: ERROR_RESPONSES.INTERNAL_ERROR.type,
288
+ title: ERROR_RESPONSES.INTERNAL_ERROR.title,
289
+ status: 500,
290
+ detail: err?.message
291
+ };
292
+ if (instance) body.instance = instance;
293
+ return new Response(JSON.stringify(body), INIT_500);
294
+ }
295
+ var BODY_404_STATIC = JSON.stringify({
296
+ type: ERROR_RESPONSES.NOT_FOUND.type,
297
+ title: ERROR_RESPONSES.NOT_FOUND.title,
298
+ status: 404
299
+ });
300
+ function notFoundResponse(instance) {
301
+ if (!instance) return new Response(BODY_404_STATIC, INIT_404);
302
+ const body = {
303
+ type: ERROR_RESPONSES.NOT_FOUND.type,
304
+ title: ERROR_RESPONSES.NOT_FOUND.title,
305
+ status: 404,
306
+ instance
307
+ };
308
+ return new Response(JSON.stringify(body), INIT_404);
309
+ }
310
+ var BODY_401_STATIC = JSON.stringify({
311
+ type: ERROR_RESPONSES.UNAUTHORIZED.type,
312
+ title: ERROR_RESPONSES.UNAUTHORIZED.title,
313
+ status: 401
314
+ });
315
+ function unauthorizedResponse(instance) {
316
+ if (!instance) return new Response(BODY_401_STATIC, INIT_401);
317
+ const body = {
318
+ type: ERROR_RESPONSES.UNAUTHORIZED.type,
319
+ title: ERROR_RESPONSES.UNAUTHORIZED.title,
320
+ status: 401,
321
+ instance
322
+ };
323
+ return new Response(JSON.stringify(body), INIT_401);
324
+ }
325
+ var BODY_403_STATIC = JSON.stringify({
326
+ type: ERROR_RESPONSES.FORBIDDEN.type,
327
+ title: ERROR_RESPONSES.FORBIDDEN.title,
328
+ status: 403
329
+ });
330
+ function forbiddenResponse(instance) {
331
+ if (!instance) return new Response(BODY_403_STATIC, INIT_403);
332
+ const body = {
333
+ type: ERROR_RESPONSES.FORBIDDEN.type,
334
+ title: ERROR_RESPONSES.FORBIDDEN.title,
335
+ status: 403,
336
+ instance
337
+ };
338
+ return new Response(JSON.stringify(body), INIT_403);
339
+ }
340
+ var BODY_500_STATIC = JSON.stringify({
341
+ type: ERROR_RESPONSES.INTERNAL_ERROR.type,
342
+ title: ERROR_RESPONSES.INTERNAL_ERROR.title,
343
+ status: 500
344
+ });
345
+ var KozoError = class extends Error {
346
+ statusCode;
347
+ code;
348
+ constructor(message, statusCode, code) {
349
+ super(message);
350
+ this.name = "KozoError";
351
+ this.statusCode = statusCode;
352
+ this.code = code;
242
353
  }
243
- // Deprecated: for backward compatibility
244
- compileFromZod(routeId, zodSchema) {
245
- this.compile(routeId, zodSchema);
354
+ toResponse(instance) {
355
+ const body = {
356
+ type: `https://kozo.dev/errors/${this.code}`,
357
+ title: this.message,
358
+ status: this.statusCode
359
+ };
360
+ if (instance) body.instance = instance;
361
+ const init = _initForStatus(this.statusCode);
362
+ return new Response(JSON.stringify(body), init);
246
363
  }
247
- // Deprecated: for backward compatibility
248
- compileFromJsonSchema(routeId, schema) {
249
- this.compile(routeId, schema);
364
+ };
365
+ function _initForStatus(status) {
366
+ if (status === 400) return INIT_400;
367
+ if (status === 401) return INIT_401;
368
+ if (status === 403) return INIT_403;
369
+ if (status === 404) return INIT_404;
370
+ if (status === 500) return INIT_500;
371
+ return Object.freeze({ status, headers: HDR_PROBLEM });
372
+ }
373
+ var ValidationFailedError = class extends KozoError {
374
+ errors;
375
+ constructor(message, errors = []) {
376
+ super(message, 400, "validation-failed");
377
+ this.name = "ValidationFailedError";
378
+ this.errors = errors;
250
379
  }
251
- serialize(routeId, data) {
252
- const serializer = this.serializers.get(routeId);
253
- return serializer ? serializer(data) : this.fallback(data);
380
+ toResponse(instance) {
381
+ const body = {
382
+ type: "https://kozo.dev/errors/validation-failed",
383
+ title: this.message,
384
+ status: 400,
385
+ errors: this.errors
386
+ };
387
+ if (instance) body.instance = instance;
388
+ return new Response(JSON.stringify(body), INIT_400);
254
389
  }
255
- has(routeId) {
256
- return this.serializers.has(routeId);
390
+ };
391
+ var NotFoundError = class extends KozoError {
392
+ constructor(message = "Resource Not Found") {
393
+ super(message, 404, "not-found");
394
+ this.name = "NotFoundError";
257
395
  }
258
- getSerializer(routeId) {
259
- return this.serializers.get(routeId);
396
+ };
397
+ var UnauthorizedError = class extends KozoError {
398
+ constructor(message = "Unauthorized") {
399
+ super(message, 401, "unauthorized");
400
+ this.name = "UnauthorizedError";
260
401
  }
261
402
  };
262
- var commonSchemas = {
263
- health: {
264
- type: "object",
265
- properties: {
266
- status: { type: "string" },
267
- timestamp: { type: "number" }
268
- }
269
- },
270
- error: {
271
- type: "object",
272
- properties: {
273
- error: { type: "string" },
274
- status: { type: "number" }
275
- }
276
- },
277
- success: {
278
- type: "object",
279
- properties: {
280
- success: { type: "boolean" },
281
- data: {}
282
- }
403
+ var ForbiddenError = class extends KozoError {
404
+ constructor(message = "Forbidden") {
405
+ super(message, 403, "forbidden");
406
+ this.name = "ForbiddenError";
283
407
  }
284
408
  };
285
- var globalSerializer = new ResponseSerializer();
286
- globalSerializer.compile("__health", commonSchemas.health);
287
- globalSerializer.compile("__error", commonSchemas.error);
288
409
 
289
- // src/optimizations/validator.ts
290
- import Ajv from "ajv";
291
- import addFormats from "ajv-formats";
292
- import { zodToJsonSchema as zodToJsonSchema2 } from "zod-to-json-schema";
293
- function isZodSchema2(schema) {
294
- return typeof schema === "object" && schema !== null && "safeParse" in schema;
410
+ // src/uws-transport.ts
411
+ var STATUS_TEXT = {
412
+ 200: "200 OK",
413
+ 201: "201 Created",
414
+ 204: "204 No Content",
415
+ 301: "301 Moved Permanently",
416
+ 302: "302 Found",
417
+ 400: "400 Bad Request",
418
+ 401: "401 Unauthorized",
419
+ 403: "403 Forbidden",
420
+ 404: "404 Not Found",
421
+ 405: "405 Method Not Allowed",
422
+ 422: "422 Unprocessable Entity",
423
+ 429: "429 Too Many Requests",
424
+ 500: "500 Internal Server Error",
425
+ 503: "503 Service Unavailable"
426
+ };
427
+ var BODY_404 = JSON.stringify({
428
+ type: "https://kozo.dev/errors/not-found",
429
+ title: "Resource Not Found",
430
+ status: 404
431
+ });
432
+ var NO_BODY_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "DELETE", "OPTIONS", "TRACE"]);
433
+ var CT_JSON = "application/json";
434
+ var CT_PROBLEM = "application/problem+json";
435
+ var BODY_500 = JSON.stringify({
436
+ type: "https://kozo.dev/errors/internal-error",
437
+ title: "Internal Server Error",
438
+ status: 500
439
+ });
440
+ function uwsFastWriteJson(uwsRes, body) {
441
+ uwsRes.cork(() => {
442
+ uwsRes.writeStatus("200 OK");
443
+ uwsRes.writeHeader("Content-Type", CT_JSON);
444
+ uwsRes.end(body);
445
+ });
295
446
  }
296
- var HybridValidator = class {
297
- ajv;
298
- validators = /* @__PURE__ */ new Map();
299
- constructor() {
300
- this.ajv = new Ajv({
301
- allErrors: false,
302
- // Stop at first error (faster)
303
- coerceTypes: true,
304
- // Auto-coerce strings to numbers etc
305
- removeAdditional: true
306
- // Strip unknown properties
447
+ function uwsFastWrite400(field, errors, uwsRes) {
448
+ const body = JSON.stringify({
449
+ type: "https://kozo.dev/errors/validation-failed",
450
+ title: "Validation Failed",
451
+ status: 400,
452
+ errors: (errors ?? []).map((e) => ({
453
+ field: e.instancePath?.replace(/^\//, "").replace(/\//g, ".") || e.params?.missingProperty || "unknown",
454
+ message: e.message || "Invalid value",
455
+ code: e.keyword || "invalid"
456
+ }))
457
+ });
458
+ uwsRes.cork(() => {
459
+ uwsRes.writeStatus("400 Bad Request");
460
+ uwsRes.writeHeader("Content-Type", CT_PROBLEM);
461
+ uwsRes.end(body);
462
+ });
463
+ }
464
+ function uwsFastWrite500(uwsRes) {
465
+ uwsRes.cork(() => {
466
+ uwsRes.writeStatus("500 Internal Server Error");
467
+ uwsRes.writeHeader("Content-Type", CT_PROBLEM);
468
+ uwsRes.end(BODY_500);
469
+ });
470
+ }
471
+ function uwsFastWriteError(err, uwsRes) {
472
+ if (err instanceof KozoError) {
473
+ const body = JSON.stringify({
474
+ type: `https://kozo.dev/errors/${err.code}`,
475
+ title: err.message,
476
+ status: err.statusCode
477
+ });
478
+ uwsRes.cork(() => {
479
+ uwsRes.writeStatus(STATUS_TEXT[err.statusCode] ?? `${err.statusCode}`);
480
+ uwsRes.writeHeader("Content-Type", CT_PROBLEM);
481
+ uwsRes.end(body);
307
482
  });
308
- addFormats(this.ajv);
483
+ } else {
484
+ uwsFastWrite500(uwsRes);
309
485
  }
310
- compile(routeId, schema) {
311
- try {
312
- let jsonSchema;
313
- if (isZodSchema2(schema)) {
314
- jsonSchema = zodToJsonSchema2(schema, { $refStrategy: "none", target: "jsonSchema7" });
315
- } else {
316
- jsonSchema = schema;
317
- }
318
- const validate = this.ajv.compile(jsonSchema);
319
- this.validators.set(routeId, (data) => {
320
- const valid = validate(data);
321
- if (valid) {
322
- return { valid: true, data };
323
- }
324
- return {
325
- valid: false,
326
- errors: validate.errors?.map((e) => `${e.instancePath} ${e.message}`) || ["Validation failed"]
327
- };
328
- });
329
- } catch (err) {
330
- if (isZodSchema2(schema)) {
331
- this.validators.set(routeId, (data) => {
332
- const result = schema.safeParse(data);
333
- if (result.success) {
334
- return { valid: true, data: result.data };
335
- }
336
- return {
337
- valid: false,
338
- errors: result.error.issues.map((i) => `${i.path.join(".")} ${i.message}`)
339
- };
486
+ }
487
+ async function tryLoadUws() {
488
+ try {
489
+ const { createRequire } = await import("module");
490
+ const req = createRequire(import.meta.url);
491
+ return req("uWebSockets.js");
492
+ } catch {
493
+ return null;
494
+ }
495
+ }
496
+ function uwsWrite404(uwsRes) {
497
+ uwsRes.cork(() => {
498
+ uwsRes.writeStatus("404 Not Found");
499
+ uwsRes.writeHeader("Content-Type", "application/problem+json");
500
+ uwsRes.end(BODY_404);
501
+ });
502
+ }
503
+ function getFreePort() {
504
+ return new Promise((resolve2, reject) => {
505
+ const srv = netCreateServer();
506
+ srv.listen(0, "0.0.0.0", () => {
507
+ const port = srv.address().port;
508
+ srv.close((err) => err ? reject(err) : resolve2(port));
509
+ });
510
+ });
511
+ }
512
+ var UWS_METHOD = {
513
+ GET: "get",
514
+ POST: "post",
515
+ PUT: "put",
516
+ PATCH: "patch",
517
+ DELETE: "del",
518
+ OPTIONS: "options",
519
+ HEAD: "head"
520
+ };
521
+ async function createUwsServer(opts) {
522
+ const { uws, routes } = opts;
523
+ const port = opts.port === 0 ? await getFreePort() : opts.port;
524
+ const emptyParams = Object.freeze({});
525
+ return new Promise((resolve2, reject) => {
526
+ const uwsApp = uws.App();
527
+ for (const route of routes) {
528
+ const fn = UWS_METHOD[route.method];
529
+ if (!fn) continue;
530
+ const h = route.handler;
531
+ const names = route.paramNames;
532
+ const hasParams = names.length > 0;
533
+ const noBody = NO_BODY_METHODS.has(route.method);
534
+ if (noBody && !hasParams) {
535
+ uwsApp[fn](route.path, (uwsRes, uwsReq) => {
536
+ const query = uwsReq.getQuery();
537
+ h(uwsRes, query ? `${uwsReq.getUrl()}?${query}` : uwsReq.getUrl(), "", emptyParams);
538
+ });
539
+ } else if (noBody && hasParams) {
540
+ uwsApp[fn](route.path, (uwsRes, uwsReq) => {
541
+ const rawPath = uwsReq.getUrl();
542
+ const query = uwsReq.getQuery();
543
+ const params = {};
544
+ for (let i = 0; i < names.length; i++) params[names[i]] = uwsReq.getParameter(i);
545
+ h(uwsRes, query ? `${rawPath}?${query}` : rawPath, "", params);
546
+ });
547
+ } else if (!hasParams) {
548
+ uwsApp[fn](route.path, (uwsRes, uwsReq) => {
549
+ const rawPath = uwsReq.getUrl();
550
+ const query = uwsReq.getQuery();
551
+ const url = query ? `${rawPath}?${query}` : rawPath;
552
+ let aborted = false;
553
+ let bodyStr = "";
554
+ uwsRes.onAborted(() => {
555
+ aborted = true;
556
+ });
557
+ uwsRes.onData((chunk, isLast) => {
558
+ if (aborted) return;
559
+ if (chunk.byteLength > 0) bodyStr += Buffer.from(chunk).toString("utf8");
560
+ if (isLast) h(uwsRes, url, bodyStr, emptyParams);
561
+ });
340
562
  });
341
563
  } else {
342
- console.error(`Failed to compile validator for ${routeId}`, err);
343
- this.validators.set(routeId, () => ({ valid: false, errors: ["Internal Server Error: Validator compilation failed"] }));
564
+ uwsApp[fn](route.path, (uwsRes, uwsReq) => {
565
+ const rawPath = uwsReq.getUrl();
566
+ const query = uwsReq.getQuery();
567
+ const url = query ? `${rawPath}?${query}` : rawPath;
568
+ const params = {};
569
+ for (let i = 0; i < names.length; i++) params[names[i]] = uwsReq.getParameter(i);
570
+ let aborted = false;
571
+ let bodyStr = "";
572
+ uwsRes.onAborted(() => {
573
+ aborted = true;
574
+ });
575
+ uwsRes.onData((chunk, isLast) => {
576
+ if (aborted) return;
577
+ if (chunk.byteLength > 0) bodyStr += Buffer.from(chunk).toString("utf8");
578
+ if (isLast) h(uwsRes, url, bodyStr, params);
579
+ });
580
+ });
344
581
  }
345
582
  }
583
+ uwsApp.any("/*", (uwsRes) => {
584
+ uwsWrite404(uwsRes);
585
+ });
586
+ let listenToken = null;
587
+ uwsApp.listen(port, (token) => {
588
+ if (!token) {
589
+ reject(new Error(`[Kozo] uWS failed to listen on port ${port}`));
590
+ return;
591
+ }
592
+ listenToken = token;
593
+ resolve2({
594
+ port,
595
+ server: {
596
+ close() {
597
+ if (listenToken) uws.us_listen_socket_close(listenToken);
598
+ }
599
+ }
600
+ });
601
+ });
602
+ });
603
+ }
604
+
605
+ // src/compiler.ts
606
+ import { zodToJsonSchema } from "zod-to-json-schema";
607
+ import Ajv from "ajv";
608
+ import addFormats from "ajv-formats";
609
+ import fastJson from "fast-json-stringify";
610
+
611
+ // src/fast-response.ts
612
+ var CL_CACHE = /* @__PURE__ */ (() => {
613
+ const arr = new Array(1e4);
614
+ for (let i = 0; i < 1e4; i++) arr[i] = String(i);
615
+ return arr;
616
+ })();
617
+ function fastCL(n) {
618
+ return n < 1e4 ? CL_CACHE[n] : String(n);
619
+ }
620
+ var CT_JSON2 = "application/json";
621
+ var CT_PROBLEM2 = "application/problem+json";
622
+ var CT_TEXT = "text/plain";
623
+ var CT_HTML = "text/html; charset=utf-8";
624
+ var BODY_4042 = JSON.stringify({
625
+ type: "https://kozo.dev/errors/not-found",
626
+ title: "Resource Not Found",
627
+ status: 404
628
+ });
629
+ var LEN_404 = fastCL(BODY_4042.length);
630
+ var BODY_5002 = JSON.stringify({
631
+ type: "https://kozo.dev/errors/internal-error",
632
+ title: "Internal Server Error",
633
+ status: 500
634
+ });
635
+ var LEN_500 = fastCL(BODY_5002.length);
636
+ function fastWriteJson(res, body) {
637
+ res.writeHead(200, [
638
+ "Content-Type",
639
+ CT_JSON2,
640
+ "Content-Length",
641
+ fastCL(body.length)
642
+ ]);
643
+ res.end(body);
644
+ }
645
+ function fastWriteText(res, body, status = 200) {
646
+ const len = Buffer.byteLength(body);
647
+ res.writeHead(status, [
648
+ "Content-Type",
649
+ CT_TEXT,
650
+ "Content-Length",
651
+ fastCL(len)
652
+ ]);
653
+ res.end(body);
654
+ }
655
+ function fastWriteHtml(res, body, status = 200) {
656
+ const len = Buffer.byteLength(body);
657
+ res.writeHead(status, [
658
+ "Content-Type",
659
+ CT_HTML,
660
+ "Content-Length",
661
+ fastCL(len)
662
+ ]);
663
+ res.end(body);
664
+ }
665
+ function fastWriteJsonStatus(res, body, status) {
666
+ res.writeHead(status, [
667
+ "Content-Type",
668
+ CT_JSON2,
669
+ "Content-Length",
670
+ fastCL(body.length)
671
+ ]);
672
+ res.end(body);
673
+ }
674
+ function fastWrite404(res) {
675
+ res.writeHead(404, [
676
+ "Content-Type",
677
+ CT_PROBLEM2,
678
+ "Content-Length",
679
+ LEN_404
680
+ ]);
681
+ res.end(BODY_4042);
682
+ }
683
+ function fastWrite500(res) {
684
+ res.writeHead(500, [
685
+ "Content-Type",
686
+ CT_PROBLEM2,
687
+ "Content-Length",
688
+ LEN_500
689
+ ]);
690
+ res.end(BODY_5002);
691
+ }
692
+ function fastWrite400(field, errors, res) {
693
+ const body = JSON.stringify({
694
+ type: "https://kozo.dev/errors/validation-failed",
695
+ title: "Validation Failed",
696
+ status: 400,
697
+ errors: (errors ?? []).map((e) => ({
698
+ field: e.instancePath?.replace(/^\//, "").replace(/\//g, ".") || e.params?.missingProperty || "unknown",
699
+ message: e.message || "Invalid value",
700
+ code: e.keyword || "invalid"
701
+ }))
702
+ });
703
+ res.writeHead(400, [
704
+ "Content-Type",
705
+ CT_PROBLEM2,
706
+ "Content-Length",
707
+ fastCL(body.length)
708
+ ]);
709
+ res.end(body);
710
+ }
711
+ function fastWriteError(err, res) {
712
+ if (err instanceof KozoError) {
713
+ const body = JSON.stringify({
714
+ type: `https://kozo.dev/errors/${err.code}`,
715
+ title: err.message,
716
+ status: err.statusCode
717
+ });
718
+ res.writeHead(err.statusCode, [
719
+ "Content-Type",
720
+ CT_PROBLEM2,
721
+ "Content-Length",
722
+ fastCL(body.length)
723
+ ]);
724
+ res.end(body);
725
+ } else {
726
+ fastWrite500(res);
346
727
  }
347
- // Deprecated: kept for backward compatibility if needed, but redirects to compile
348
- compileFromZod(routeId, zodSchema) {
349
- this.compile(routeId, zodSchema);
350
- }
351
- validate(routeId, data) {
352
- const validator = this.validators.get(routeId);
353
- if (!validator) {
354
- return { valid: true, data };
355
- }
356
- return validator(data);
357
- }
358
- // For production: throws on invalid (faster than returning errors)
359
- validateOrThrow(routeId, data) {
360
- const result = this.validate(routeId, data);
361
- if (!result.valid) {
362
- const error = new Error("Validation failed");
363
- error.errors = result.errors;
364
- throw error;
365
- }
366
- return result.data;
367
- }
368
- has(routeId) {
369
- return this.validators.has(routeId);
370
- }
371
- };
372
- var globalValidator = new HybridValidator();
728
+ }
373
729
 
374
730
  // src/compiler.ts
375
- import { zodToJsonSchema as zodToJsonSchema3 } from "zod-to-json-schema";
376
- import Ajv2 from "ajv";
377
- import addFormats2 from "ajv-formats";
378
- import fastJson2 from "fast-json-stringify";
379
- var ajv = new Ajv2({
731
+ var ajv = new Ajv({
380
732
  removeAdditional: "all",
381
733
  useDefaults: true,
382
734
  coerceTypes: true
383
735
  });
384
- addFormats2(ajv);
385
- function isZodSchema3(schema) {
736
+ addFormats(ajv);
737
+ function isZodSchema(schema) {
386
738
  return typeof schema === "object" && schema !== null && "safeParse" in schema;
387
739
  }
740
+ var ZOD_OPTS = { $refStrategy: "none", target: "jsonSchema7" };
741
+ var HDR_JSON = { "Content-Type": "application/json" };
742
+ var RESPONSE_INIT_200 = Object.freeze({ status: 200, headers: HDR_JSON });
743
+ function jsonResponse200(body) {
744
+ return new Response(body, RESPONSE_INIT_200);
745
+ }
746
+ var EMPTY_BODY = Object.freeze({});
747
+ var EMPTY_BODY_HANDLER = () => EMPTY_BODY;
748
+ function toJsonBody(result) {
749
+ if (typeof result === "string") return result;
750
+ return JSON.stringify(result);
751
+ }
388
752
  var SchemaCompiler = class {
389
753
  static compile(schema) {
390
754
  const compiled = {};
391
755
  if (schema.body) {
392
- const jsonSchema = isZodSchema3(schema.body) ? zodToJsonSchema3(schema.body, { target: "jsonSchema7" }) : schema.body;
756
+ const jsonSchema = isZodSchema(schema.body) ? zodToJsonSchema(schema.body, ZOD_OPTS) : schema.body;
393
757
  compiled.validateBody = ajv.compile(jsonSchema);
394
758
  }
395
759
  if (schema.query) {
396
- const jsonSchema = isZodSchema3(schema.query) ? zodToJsonSchema3(schema.query, { target: "jsonSchema7" }) : schema.query;
760
+ const jsonSchema = isZodSchema(schema.query) ? zodToJsonSchema(schema.query, ZOD_OPTS) : schema.query;
397
761
  compiled.validateQuery = ajv.compile(jsonSchema);
398
762
  }
399
763
  if (schema.params) {
400
- const jsonSchema = isZodSchema3(schema.params) ? zodToJsonSchema3(schema.params, { target: "jsonSchema7" }) : schema.params;
764
+ const jsonSchema = isZodSchema(schema.params) ? zodToJsonSchema(schema.params, ZOD_OPTS) : schema.params;
401
765
  compiled.validateParams = ajv.compile(jsonSchema);
402
766
  }
403
767
  if (schema.response) {
404
- if (typeof schema.response === "object" && !isZodSchema3(schema.response)) {
768
+ if (typeof schema.response === "object" && !isZodSchema(schema.response)) {
405
769
  const keys = Object.keys(schema.response);
406
770
  const isStatusMap = keys.length > 0 && keys.every((k) => !isNaN(Number(k)));
407
771
  if (isStatusMap) {
408
772
  const responseSchemas = schema.response;
409
773
  if (responseSchemas[200]) {
410
- const jsonSchema = isZodSchema3(responseSchemas[200]) ? zodToJsonSchema3(responseSchemas[200], { target: "jsonSchema7" }) : responseSchemas[200];
411
- compiled.serialize = fastJson2(jsonSchema);
774
+ const jsonSchema = isZodSchema(responseSchemas[200]) ? zodToJsonSchema(responseSchemas[200], ZOD_OPTS) : responseSchemas[200];
775
+ compiled.serialize = fastJson(jsonSchema);
412
776
  }
413
777
  } else {
414
- compiled.serialize = fastJson2(schema.response);
778
+ compiled.serialize = fastJson(schema.response);
415
779
  }
416
780
  } else {
417
781
  const responseSchema = schema.response;
418
- const jsonSchema = zodToJsonSchema3(responseSchema, { target: "jsonSchema7" });
419
- compiled.serialize = fastJson2(jsonSchema);
782
+ const jsonSchema = zodToJsonSchema(responseSchema, ZOD_OPTS);
783
+ compiled.serialize = fastJson(jsonSchema);
420
784
  }
421
785
  }
422
786
  return compiled;
423
787
  }
424
788
  };
425
- var RESPONSE_INIT_200 = { status: 200, headers: { "Content-Type": "application/json" } };
426
- var RESPONSE_INIT_400 = { status: 400, headers: { "Content-Type": "application/json" } };
427
- var RESPONSE_INIT_500 = { status: 500, headers: { "Content-Type": "application/json" } };
428
- var ERROR_INVALID = '{"error":"Validation failed"}';
429
- var ERROR_INTERNAL = '{"error":"Internal server error"}';
430
- function compileRouteHandler(userHandler, schema, services, compiled) {
431
- const bodyValidator = compiled.validateBody;
432
- const queryValidator = compiled.validateQuery;
433
- const paramsValidator = compiled.validateParams;
434
- const serializer = compiled.serialize;
435
- const needsBody = !!bodyValidator;
436
- const needsQuery = !!queryValidator;
437
- const needsParams = !!paramsValidator;
438
- const needsSerialize = !!serializer;
439
- if (!needsBody && !needsQuery && !needsParams) {
440
- if (needsSerialize) {
441
- return (c) => {
442
- c.services = services;
443
- const result = userHandler(c);
444
- if (result instanceof Promise) {
445
- return result.then((res) => {
446
- if (res instanceof Response) return res;
447
- return new Response(serializer(res), RESPONSE_INIT_200);
448
- });
789
+ function compileRouteHandler(handler, schema, services, compiled) {
790
+ const { validateBody, validateQuery, validateParams, serialize } = compiled;
791
+ const hasServices = services != null && Object.keys(services).length > 0;
792
+ const hasQuery = !!validateQuery;
793
+ const hasParams = !!validateParams;
794
+ const hasBody = !!validateBody;
795
+ const hasSer = !!serialize;
796
+ const handlerIgnoresArgs = handler.length === 0;
797
+ if (!hasBody && !hasQuery && !hasParams && !hasServices) {
798
+ if (hasSer) {
799
+ const ser = serialize;
800
+ return function scenario1_bare_ser(c) {
801
+ try {
802
+ const result = handlerIgnoresArgs ? handler() : handler({ c });
803
+ if (result instanceof Response) return result;
804
+ if (result != null && typeof result.then === "function") {
805
+ return result.then(
806
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
807
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
808
+ );
809
+ }
810
+ return jsonResponse200(ser(result));
811
+ } catch (err) {
812
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
813
+ return internalErrorResponse(err, c.req.path);
449
814
  }
450
- if (result instanceof Response) return result;
451
- return new Response(serializer(result), RESPONSE_INIT_200);
452
815
  };
453
- } else {
454
- return (c) => {
455
- c.services = services;
456
- const result = userHandler(c);
457
- if (result instanceof Promise) {
458
- return result.then((res) => {
459
- if (res instanceof Response) return res;
460
- return new Response(JSON.stringify(res), RESPONSE_INIT_200);
461
- });
462
- }
816
+ }
817
+ return function scenario1_bare(c) {
818
+ try {
819
+ const result = handlerIgnoresArgs ? handler() : handler({ c });
463
820
  if (result instanceof Response) return result;
464
- return new Response(JSON.stringify(result), RESPONSE_INIT_200);
821
+ if (result != null && typeof result.then === "function") {
822
+ return result.then(
823
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
824
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
825
+ );
826
+ }
827
+ return jsonResponse200(toJsonBody(result));
828
+ } catch (err) {
829
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
830
+ return internalErrorResponse(err, c.req.path);
831
+ }
832
+ };
833
+ }
834
+ if (!hasBody && !hasQuery && !hasParams && hasServices) {
835
+ if (hasSer) {
836
+ const ser = serialize;
837
+ return function scenario1_services_ser(c) {
838
+ try {
839
+ const result = handlerIgnoresArgs ? handler() : handler({ c, services });
840
+ if (result instanceof Response) return result;
841
+ if (result != null && typeof result.then === "function") {
842
+ return result.then(
843
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
844
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
845
+ );
846
+ }
847
+ return jsonResponse200(ser(result));
848
+ } catch (err) {
849
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
850
+ return internalErrorResponse(err, c.req.path);
851
+ }
465
852
  };
466
853
  }
854
+ return function scenario1_services(c) {
855
+ try {
856
+ const result = handlerIgnoresArgs ? handler() : handler({ c, services });
857
+ if (result instanceof Response) return result;
858
+ if (result != null && typeof result.then === "function") {
859
+ return result.then(
860
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
861
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
862
+ );
863
+ }
864
+ return jsonResponse200(toJsonBody(result));
865
+ } catch (err) {
866
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
867
+ return internalErrorResponse(err, c.req.path);
868
+ }
869
+ };
467
870
  }
468
- if (!needsBody) {
469
- if (needsSerialize) {
470
- return (c) => {
471
- const ctx = c;
472
- ctx.services = services;
473
- if (needsParams) {
474
- const params = c.req.param();
475
- if (!paramsValidator(params)) {
476
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
871
+ if (hasQuery && !hasParams && !hasBody) {
872
+ const vq = validateQuery;
873
+ if (hasSer) {
874
+ const ser = serialize;
875
+ if (!hasServices) {
876
+ return function scenario2a_query_ser(c) {
877
+ try {
878
+ const query = c.req.query();
879
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
880
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query });
881
+ if (result instanceof Response) return result;
882
+ if (result != null && typeof result.then === "function") {
883
+ return result.then(
884
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
885
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
886
+ );
887
+ }
888
+ return jsonResponse200(ser(result));
889
+ } catch (err) {
890
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
891
+ return internalErrorResponse(err, c.req.path);
477
892
  }
478
- ctx.params = params;
479
- }
480
- if (needsQuery) {
893
+ };
894
+ }
895
+ return function scenario2a_query_svc_ser(c) {
896
+ try {
481
897
  const query = c.req.query();
482
- if (!queryValidator(query)) {
483
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
898
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
899
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query, services });
900
+ if (result instanceof Response) return result;
901
+ if (result != null && typeof result.then === "function") {
902
+ return result.then(
903
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
904
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
905
+ );
484
906
  }
485
- ctx.query = query;
907
+ return jsonResponse200(ser(result));
908
+ } catch (err) {
909
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
910
+ return internalErrorResponse(err, c.req.path);
486
911
  }
487
- const result = userHandler(c);
488
- if (result instanceof Promise) {
489
- return result.then((res) => {
490
- if (res instanceof Response) return res;
491
- return new Response(serializer(res), RESPONSE_INIT_200);
492
- });
912
+ };
913
+ }
914
+ if (!hasServices) {
915
+ return function scenario2a_query(c) {
916
+ try {
917
+ const query = c.req.query();
918
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
919
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query });
920
+ if (result instanceof Response) return result;
921
+ if (result != null && typeof result.then === "function") {
922
+ return result.then(
923
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
924
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
925
+ );
926
+ }
927
+ return jsonResponse200(toJsonBody(result));
928
+ } catch (err) {
929
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
930
+ return internalErrorResponse(err, c.req.path);
493
931
  }
494
- if (result instanceof Response) return result;
495
- return new Response(serializer(result), RESPONSE_INIT_200);
496
932
  };
497
- } else {
498
- return (c) => {
499
- const ctx = c;
500
- ctx.services = services;
501
- if (needsParams) {
933
+ }
934
+ return function scenario2a_query_svc(c) {
935
+ try {
936
+ const query = c.req.query();
937
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
938
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query, services });
939
+ if (result instanceof Response) return result;
940
+ if (result != null && typeof result.then === "function") {
941
+ return result.then(
942
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
943
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
944
+ );
945
+ }
946
+ return jsonResponse200(toJsonBody(result));
947
+ } catch (err) {
948
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
949
+ return internalErrorResponse(err, c.req.path);
950
+ }
951
+ };
952
+ }
953
+ if (hasParams && !hasQuery && !hasBody) {
954
+ const vp = validateParams;
955
+ if (hasSer) {
956
+ const ser = serialize;
957
+ if (!hasServices) {
958
+ return function scenario2b_params_ser(c) {
959
+ try {
960
+ const params = c.req.param();
961
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
962
+ const result = handlerIgnoresArgs ? handler() : handler({ c, params });
963
+ if (result instanceof Response) return result;
964
+ if (result != null && typeof result.then === "function") {
965
+ return result.then(
966
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
967
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
968
+ );
969
+ }
970
+ return jsonResponse200(ser(result));
971
+ } catch (err) {
972
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
973
+ return internalErrorResponse(err, c.req.path);
974
+ }
975
+ };
976
+ }
977
+ return function scenario2b_params_svc_ser(c) {
978
+ try {
502
979
  const params = c.req.param();
503
- if (!paramsValidator(params)) {
504
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
980
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
981
+ const result = handlerIgnoresArgs ? handler() : handler({ c, params, services });
982
+ if (result instanceof Response) return result;
983
+ if (result != null && typeof result.then === "function") {
984
+ return result.then(
985
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
986
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
987
+ );
505
988
  }
506
- ctx.params = params;
989
+ return jsonResponse200(ser(result));
990
+ } catch (err) {
991
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
992
+ return internalErrorResponse(err, c.req.path);
507
993
  }
508
- if (needsQuery) {
509
- const query = c.req.query();
510
- if (!queryValidator(query)) {
511
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
994
+ };
995
+ }
996
+ if (!hasServices) {
997
+ return function scenario2b_params(c) {
998
+ try {
999
+ const params = c.req.param();
1000
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
1001
+ const result = handlerIgnoresArgs ? handler() : handler({ c, params });
1002
+ if (result instanceof Response) return result;
1003
+ if (result != null && typeof result.then === "function") {
1004
+ return result.then(
1005
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
1006
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
1007
+ );
512
1008
  }
513
- ctx.query = query;
514
- }
515
- const result = userHandler(c);
516
- if (result instanceof Promise) {
517
- return result.then((res) => {
518
- if (res instanceof Response) return res;
519
- return new Response(JSON.stringify(res), RESPONSE_INIT_200);
520
- });
1009
+ return jsonResponse200(toJsonBody(result));
1010
+ } catch (err) {
1011
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
1012
+ return internalErrorResponse(err, c.req.path);
521
1013
  }
522
- if (result instanceof Response) return result;
523
- return new Response(JSON.stringify(result), RESPONSE_INIT_200);
524
1014
  };
525
1015
  }
526
- }
527
- if (needsSerialize) {
528
- return async (c) => {
1016
+ return function scenario2b_params_svc(c) {
529
1017
  try {
530
- const ctx = c;
531
- ctx.services = services;
532
- if (needsParams) {
1018
+ const params = c.req.param();
1019
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
1020
+ const result = handlerIgnoresArgs ? handler() : handler({ c, params, services });
1021
+ if (result instanceof Response) return result;
1022
+ if (result != null && typeof result.then === "function") {
1023
+ return result.then(
1024
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
1025
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
1026
+ );
1027
+ }
1028
+ return jsonResponse200(toJsonBody(result));
1029
+ } catch (err) {
1030
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
1031
+ return internalErrorResponse(err, c.req.path);
1032
+ }
1033
+ };
1034
+ }
1035
+ if (hasQuery && hasParams && !hasBody) {
1036
+ const vq = validateQuery;
1037
+ const vp = validateParams;
1038
+ if (hasSer) {
1039
+ const ser = serialize;
1040
+ if (!hasServices) {
1041
+ return function scenario2c_qp_ser(c) {
1042
+ try {
1043
+ const query = c.req.query();
1044
+ const params = c.req.param();
1045
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
1046
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
1047
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query, params });
1048
+ if (result instanceof Response) return result;
1049
+ if (result != null && typeof result.then === "function") {
1050
+ return result.then(
1051
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
1052
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
1053
+ );
1054
+ }
1055
+ return jsonResponse200(ser(result));
1056
+ } catch (err) {
1057
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
1058
+ return internalErrorResponse(err, c.req.path);
1059
+ }
1060
+ };
1061
+ }
1062
+ return function scenario2c_qp_svc_ser(c) {
1063
+ try {
1064
+ const query = c.req.query();
533
1065
  const params = c.req.param();
534
- if (!paramsValidator(params)) {
535
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
1066
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
1067
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
1068
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query, params, services });
1069
+ if (result instanceof Response) return result;
1070
+ if (result != null && typeof result.then === "function") {
1071
+ return result.then(
1072
+ (r) => r instanceof Response ? r : jsonResponse200(ser(r)),
1073
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
1074
+ );
536
1075
  }
537
- ctx.params = params;
1076
+ return jsonResponse200(ser(result));
1077
+ } catch (err) {
1078
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
1079
+ return internalErrorResponse(err, c.req.path);
538
1080
  }
539
- if (needsQuery) {
1081
+ };
1082
+ }
1083
+ if (!hasServices) {
1084
+ return function scenario2c_qp(c) {
1085
+ try {
540
1086
  const query = c.req.query();
541
- if (!queryValidator(query)) {
542
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
1087
+ const params = c.req.param();
1088
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
1089
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
1090
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query, params });
1091
+ if (result instanceof Response) return result;
1092
+ if (result != null && typeof result.then === "function") {
1093
+ return result.then(
1094
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
1095
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
1096
+ );
543
1097
  }
544
- ctx.query = query;
1098
+ return jsonResponse200(toJsonBody(result));
1099
+ } catch (err) {
1100
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
1101
+ return internalErrorResponse(err, c.req.path);
545
1102
  }
546
- if (needsBody) {
547
- const rawBody = await c.req.json().catch(() => ({}));
548
- if (!bodyValidator(rawBody)) {
549
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
1103
+ };
1104
+ }
1105
+ return function scenario2c_qp_svc(c) {
1106
+ try {
1107
+ const query = c.req.query();
1108
+ const params = c.req.param();
1109
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, c.req.path);
1110
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, c.req.path);
1111
+ const result = handlerIgnoresArgs ? handler() : handler({ c, query, params, services });
1112
+ if (result instanceof Response) return result;
1113
+ if (result != null && typeof result.then === "function") {
1114
+ return result.then(
1115
+ (r) => r instanceof Response ? r : jsonResponse200(toJsonBody(r)),
1116
+ (err) => err instanceof KozoError ? err.toResponse(c.req.path) : internalErrorResponse(err, c.req.path)
1117
+ );
1118
+ }
1119
+ return jsonResponse200(toJsonBody(result));
1120
+ } catch (err) {
1121
+ if (err instanceof KozoError) return err.toResponse(c.req.path);
1122
+ return internalErrorResponse(err, c.req.path);
1123
+ }
1124
+ };
1125
+ }
1126
+ if (hasBody && hasSer) {
1127
+ const vb = validateBody;
1128
+ const ser = serialize;
1129
+ const vq = validateQuery;
1130
+ const vp = validateParams;
1131
+ if (!hasServices) {
1132
+ return async function scenario3_full_ser(c) {
1133
+ const path = c.req.path;
1134
+ try {
1135
+ const body = await c.req.json().catch(EMPTY_BODY_HANDLER);
1136
+ if (!vb(body)) return validationErrorResponse("body", vb.errors, path);
1137
+ let query;
1138
+ let params;
1139
+ if (vq) {
1140
+ query = c.req.query();
1141
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, path);
1142
+ }
1143
+ if (vp) {
1144
+ params = c.req.param();
1145
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, path);
550
1146
  }
551
- ctx.body = rawBody;
1147
+ const result = await handler({ c, body, query, params });
1148
+ if (result instanceof Response) return result;
1149
+ return jsonResponse200(ser(result));
1150
+ } catch (err) {
1151
+ if (err instanceof KozoError) return err.toResponse(path);
1152
+ return internalErrorResponse(err, path);
1153
+ }
1154
+ };
1155
+ }
1156
+ return async function scenario3_full_ser_svc(c) {
1157
+ const path = c.req.path;
1158
+ try {
1159
+ const body = await c.req.json().catch(EMPTY_BODY_HANDLER);
1160
+ if (!vb(body)) return validationErrorResponse("body", vb.errors, path);
1161
+ let query;
1162
+ let params;
1163
+ if (vq) {
1164
+ query = c.req.query();
1165
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, path);
552
1166
  }
553
- const result = await userHandler(c);
1167
+ if (vp) {
1168
+ params = c.req.param();
1169
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, path);
1170
+ }
1171
+ const result = await handler({ c, body, query, params, services });
554
1172
  if (result instanceof Response) return result;
555
- return new Response(serializer(result), RESPONSE_INIT_200);
1173
+ return jsonResponse200(ser(result));
556
1174
  } catch (err) {
557
- console.error("[Kozo] Handler error:", err);
558
- return new Response(ERROR_INTERNAL, RESPONSE_INIT_500);
1175
+ if (err instanceof KozoError) return err.toResponse(path);
1176
+ return internalErrorResponse(err, path);
559
1177
  }
560
1178
  };
561
- } else {
562
- return async (c) => {
563
- try {
564
- const ctx = c;
565
- ctx.services = services;
566
- if (needsParams) {
567
- const params = c.req.param();
568
- if (!paramsValidator(params)) {
569
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
1179
+ }
1180
+ if (hasBody && !hasSer) {
1181
+ const vb = validateBody;
1182
+ const vq = validateQuery;
1183
+ const vp = validateParams;
1184
+ if (!hasServices) {
1185
+ return async function scenario4_full_noser(c) {
1186
+ const path = c.req.path;
1187
+ try {
1188
+ const body = await c.req.json().catch(EMPTY_BODY_HANDLER);
1189
+ if (!vb(body)) return validationErrorResponse("body", vb.errors, path);
1190
+ let query;
1191
+ let params;
1192
+ if (vq) {
1193
+ query = c.req.query();
1194
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, path);
570
1195
  }
571
- ctx.params = params;
572
- }
573
- if (needsQuery) {
574
- const query = c.req.query();
575
- if (!queryValidator(query)) {
576
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
1196
+ if (vp) {
1197
+ params = c.req.param();
1198
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, path);
577
1199
  }
578
- ctx.query = query;
1200
+ const result = await handler({ c, body, query, params });
1201
+ if (result instanceof Response) return result;
1202
+ return jsonResponse200(toJsonBody(result));
1203
+ } catch (err) {
1204
+ if (err instanceof KozoError) return err.toResponse(path);
1205
+ return internalErrorResponse(err, path);
579
1206
  }
580
- if (needsBody) {
581
- const rawBody = await c.req.json().catch(() => ({}));
582
- if (!bodyValidator(rawBody)) {
583
- return new Response(ERROR_INVALID, RESPONSE_INIT_400);
584
- }
585
- ctx.body = rawBody;
1207
+ };
1208
+ }
1209
+ return async function scenario4_full_noser_svc(c) {
1210
+ const path = c.req.path;
1211
+ try {
1212
+ const body = await c.req.json().catch(EMPTY_BODY_HANDLER);
1213
+ if (!vb(body)) return validationErrorResponse("body", vb.errors, path);
1214
+ let query;
1215
+ let params;
1216
+ if (vq) {
1217
+ query = c.req.query();
1218
+ if (!vq(query)) return validationErrorResponse("query", vq.errors, path);
1219
+ }
1220
+ if (vp) {
1221
+ params = c.req.param();
1222
+ if (!vp(params)) return validationErrorResponse("params", vp.errors, path);
586
1223
  }
587
- const result = await userHandler(c);
1224
+ const result = await handler({ c, body, query, params, services });
588
1225
  if (result instanceof Response) return result;
589
- return new Response(JSON.stringify(result), RESPONSE_INIT_200);
1226
+ return jsonResponse200(toJsonBody(result));
590
1227
  } catch (err) {
591
- console.error("[Kozo] Handler error:", err);
592
- return new Response(ERROR_INTERNAL, RESPONSE_INIT_500);
1228
+ if (err instanceof KozoError) return err.toResponse(path);
1229
+ return internalErrorResponse(err, path);
593
1230
  }
594
1231
  };
595
1232
  }
1233
+ return async function scenario5_fallback(c) {
1234
+ const path = c.req.path;
1235
+ try {
1236
+ const result = await handler({ c, services: hasServices ? services : void 0 });
1237
+ if (result instanceof Response) return result;
1238
+ return jsonResponse200(toJsonBody(result));
1239
+ } catch (err) {
1240
+ if (err instanceof KozoError) return err.toResponse(path);
1241
+ return internalErrorResponse(err, path);
1242
+ }
1243
+ };
596
1244
  }
597
-
598
- // src/app.ts
599
- var ajv2 = new Ajv3({
600
- removeAdditional: "all",
601
- useDefaults: true,
602
- coerceTypes: true
603
- });
604
- addFormats3(ajv2);
605
- var isBun = typeof Bun !== "undefined";
606
- if (isBun) {
607
- try {
608
- if (typeof Bun !== "undefined" && Bun.json) {
609
- globalThis.JSON = {
610
- parse: Bun.json.parse,
611
- stringify: Bun.json.stringify
1245
+ function parseNativeQuery(url) {
1246
+ const qIdx = url.indexOf("?");
1247
+ if (qIdx === -1) return {};
1248
+ const sp = new URLSearchParams(url.slice(qIdx + 1));
1249
+ const result = {};
1250
+ sp.forEach((v, k) => {
1251
+ result[k] = v;
1252
+ });
1253
+ return result;
1254
+ }
1255
+ function readNativeBody(req) {
1256
+ return new Promise((resolve2, reject) => {
1257
+ const chunks = [];
1258
+ req.on("data", (chunk) => chunks.push(chunk));
1259
+ req.on("end", () => {
1260
+ try {
1261
+ const str = Buffer.concat(chunks).toString("utf8");
1262
+ resolve2(str ? JSON.parse(str) : {});
1263
+ } catch {
1264
+ resolve2({});
1265
+ }
1266
+ });
1267
+ req.on("error", reject);
1268
+ });
1269
+ }
1270
+ function compileNativeHandler(handler, schema, services, compiled) {
1271
+ const { validateBody, validateQuery, validateParams, serialize } = compiled;
1272
+ const hasServices = services != null && Object.keys(services).length > 0;
1273
+ const hasQuery = !!validateQuery;
1274
+ const hasParams = !!validateParams;
1275
+ const hasBody = !!validateBody;
1276
+ const hasSer = !!serialize;
1277
+ const handlerIgnoresArgs = handler.length === 0;
1278
+ if (!hasBody && !hasQuery && !hasParams && !hasServices) {
1279
+ if (hasSer) {
1280
+ const ser = serialize;
1281
+ if (handlerIgnoresArgs) {
1282
+ return function native_s1_bare_ser_noargs(req, res, _p) {
1283
+ try {
1284
+ const result = handler();
1285
+ if (result != null && typeof result.then === "function") {
1286
+ result.then(
1287
+ (r) => fastWriteJson(res, ser(r)),
1288
+ (err) => fastWriteError(err, res)
1289
+ );
1290
+ return;
1291
+ }
1292
+ fastWriteJson(res, ser(result));
1293
+ } catch (err) {
1294
+ fastWriteError(err, res);
1295
+ }
1296
+ };
1297
+ }
1298
+ return function native_s1_bare_ser(req, res, _p) {
1299
+ try {
1300
+ const result = handler({ req });
1301
+ if (result != null && typeof result.then === "function") {
1302
+ result.then(
1303
+ (r) => fastWriteJson(res, ser(r)),
1304
+ (err) => fastWriteError(err, res)
1305
+ );
1306
+ return;
1307
+ }
1308
+ fastWriteJson(res, ser(result));
1309
+ } catch (err) {
1310
+ fastWriteError(err, res);
1311
+ }
1312
+ };
1313
+ }
1314
+ if (handlerIgnoresArgs) {
1315
+ return function native_s1_bare_noargs(req, res, _p) {
1316
+ try {
1317
+ const result = handler();
1318
+ if (result != null && typeof result.then === "function") {
1319
+ result.then(
1320
+ (r) => fastWriteJson(res, toJsonBody(r)),
1321
+ (err) => fastWriteError(err, res)
1322
+ );
1323
+ return;
1324
+ }
1325
+ fastWriteJson(res, toJsonBody(result));
1326
+ } catch (err) {
1327
+ fastWriteError(err, res);
1328
+ }
612
1329
  };
613
1330
  }
614
- } catch (e) {
1331
+ return function native_s1_bare(req, res, _p) {
1332
+ try {
1333
+ const result = handler({ req });
1334
+ if (result != null && typeof result.then === "function") {
1335
+ result.then(
1336
+ (r) => fastWriteJson(res, toJsonBody(r)),
1337
+ (err) => fastWriteError(err, res)
1338
+ );
1339
+ return;
1340
+ }
1341
+ fastWriteJson(res, toJsonBody(result));
1342
+ } catch (err) {
1343
+ fastWriteError(err, res);
1344
+ }
1345
+ };
615
1346
  }
616
- }
617
- var Kozo = class {
618
- app;
619
- services;
620
- routes = [];
621
- constructor(config = {}) {
622
- this.app = new Hono();
623
- this.services = config.services || {};
1347
+ if (!hasBody && !hasQuery && !hasParams && hasServices) {
1348
+ if (hasSer) {
1349
+ const ser = serialize;
1350
+ if (handlerIgnoresArgs) {
1351
+ return function native_s1_svc_ser_noargs(req, res, _p) {
1352
+ try {
1353
+ const result = handler();
1354
+ if (result != null && typeof result.then === "function") {
1355
+ result.then(
1356
+ (r) => fastWriteJson(res, ser(r)),
1357
+ (err) => fastWriteError(err, res)
1358
+ );
1359
+ return;
1360
+ }
1361
+ fastWriteJson(res, ser(result));
1362
+ } catch (err) {
1363
+ fastWriteError(err, res);
1364
+ }
1365
+ };
1366
+ }
1367
+ return function native_s1_svc_ser(req, res, _p) {
1368
+ try {
1369
+ const result = handler({ req, services });
1370
+ if (result != null && typeof result.then === "function") {
1371
+ result.then(
1372
+ (r) => fastWriteJson(res, ser(r)),
1373
+ (err) => fastWriteError(err, res)
1374
+ );
1375
+ return;
1376
+ }
1377
+ fastWriteJson(res, ser(result));
1378
+ } catch (err) {
1379
+ fastWriteError(err, res);
1380
+ }
1381
+ };
1382
+ }
1383
+ if (handlerIgnoresArgs) {
1384
+ return function native_s1_svc_noargs(req, res, _p) {
1385
+ try {
1386
+ const result = handler();
1387
+ if (result != null && typeof result.then === "function") {
1388
+ result.then(
1389
+ (r) => fastWriteJson(res, toJsonBody(r)),
1390
+ (err) => fastWriteError(err, res)
1391
+ );
1392
+ return;
1393
+ }
1394
+ fastWriteJson(res, toJsonBody(result));
1395
+ } catch (err) {
1396
+ fastWriteError(err, res);
1397
+ }
1398
+ };
1399
+ }
1400
+ return function native_s1_svc(req, res, _p) {
1401
+ try {
1402
+ const result = handler({ req, services });
1403
+ if (result != null && typeof result.then === "function") {
1404
+ result.then(
1405
+ (r) => fastWriteJson(res, toJsonBody(r)),
1406
+ (err) => fastWriteError(err, res)
1407
+ );
1408
+ return;
1409
+ }
1410
+ fastWriteJson(res, toJsonBody(result));
1411
+ } catch (err) {
1412
+ fastWriteError(err, res);
1413
+ }
1414
+ };
624
1415
  }
625
- // Plugin system
626
- use(plugin) {
1416
+ if (hasQuery && !hasParams && !hasBody) {
1417
+ const vq = validateQuery;
1418
+ if (hasSer) {
1419
+ const ser = serialize;
1420
+ if (!hasServices) {
1421
+ return function native_s2a_q_ser(req, res, _p) {
1422
+ try {
1423
+ const query = parseNativeQuery(req.url ?? "/");
1424
+ if (!vq(query)) {
1425
+ fastWrite400("query", vq.errors, res);
1426
+ return;
1427
+ }
1428
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query });
1429
+ if (result != null && typeof result.then === "function") {
1430
+ result.then(
1431
+ (r) => fastWriteJson(res, ser(r)),
1432
+ (err) => fastWriteError(err, res)
1433
+ );
1434
+ return;
1435
+ }
1436
+ fastWriteJson(res, ser(result));
1437
+ } catch (err) {
1438
+ fastWriteError(err, res);
1439
+ }
1440
+ };
1441
+ }
1442
+ return function native_s2a_q_svc_ser(req, res, _p) {
1443
+ try {
1444
+ const query = parseNativeQuery(req.url ?? "/");
1445
+ if (!vq(query)) {
1446
+ fastWrite400("query", vq.errors, res);
1447
+ return;
1448
+ }
1449
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query, services });
1450
+ if (result != null && typeof result.then === "function") {
1451
+ result.then(
1452
+ (r) => fastWriteJson(res, ser(r)),
1453
+ (err) => fastWriteError(err, res)
1454
+ );
1455
+ return;
1456
+ }
1457
+ fastWriteJson(res, ser(result));
1458
+ } catch (err) {
1459
+ fastWriteError(err, res);
1460
+ }
1461
+ };
1462
+ }
1463
+ if (!hasServices) {
1464
+ return function native_s2a_q(req, res, _p) {
1465
+ try {
1466
+ const query = parseNativeQuery(req.url ?? "/");
1467
+ if (!vq(query)) {
1468
+ fastWrite400("query", vq.errors, res);
1469
+ return;
1470
+ }
1471
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query });
1472
+ if (result != null && typeof result.then === "function") {
1473
+ result.then(
1474
+ (r) => fastWriteJson(res, toJsonBody(r)),
1475
+ (err) => fastWriteError(err, res)
1476
+ );
1477
+ return;
1478
+ }
1479
+ fastWriteJson(res, toJsonBody(result));
1480
+ } catch (err) {
1481
+ fastWriteError(err, res);
1482
+ }
1483
+ };
1484
+ }
1485
+ return function native_s2a_q_svc(req, res, _p) {
1486
+ try {
1487
+ const query = parseNativeQuery(req.url ?? "/");
1488
+ if (!vq(query)) {
1489
+ fastWrite400("query", vq.errors, res);
1490
+ return;
1491
+ }
1492
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query, services });
1493
+ if (result != null && typeof result.then === "function") {
1494
+ result.then(
1495
+ (r) => fastWriteJson(res, toJsonBody(r)),
1496
+ (err) => fastWriteError(err, res)
1497
+ );
1498
+ return;
1499
+ }
1500
+ fastWriteJson(res, toJsonBody(result));
1501
+ } catch (err) {
1502
+ fastWriteError(err, res);
1503
+ }
1504
+ };
1505
+ }
1506
+ if (hasParams && !hasQuery && !hasBody) {
1507
+ const vp = validateParams;
1508
+ if (hasSer) {
1509
+ const ser = serialize;
1510
+ if (!hasServices) {
1511
+ return function native_s2b_p_ser(req, res, params) {
1512
+ try {
1513
+ if (!vp(params)) {
1514
+ fastWrite400("params", vp.errors, res);
1515
+ return;
1516
+ }
1517
+ const result = handlerIgnoresArgs ? handler() : handler({ req, params });
1518
+ if (result != null && typeof result.then === "function") {
1519
+ result.then(
1520
+ (r) => fastWriteJson(res, ser(r)),
1521
+ (err) => fastWriteError(err, res)
1522
+ );
1523
+ return;
1524
+ }
1525
+ fastWriteJson(res, ser(result));
1526
+ } catch (err) {
1527
+ fastWriteError(err, res);
1528
+ }
1529
+ };
1530
+ }
1531
+ return function native_s2b_p_svc_ser(req, res, params) {
1532
+ try {
1533
+ if (!vp(params)) {
1534
+ fastWrite400("params", vp.errors, res);
1535
+ return;
1536
+ }
1537
+ const result = handlerIgnoresArgs ? handler() : handler({ req, params, services });
1538
+ if (result != null && typeof result.then === "function") {
1539
+ result.then(
1540
+ (r) => fastWriteJson(res, ser(r)),
1541
+ (err) => fastWriteError(err, res)
1542
+ );
1543
+ return;
1544
+ }
1545
+ fastWriteJson(res, ser(result));
1546
+ } catch (err) {
1547
+ fastWriteError(err, res);
1548
+ }
1549
+ };
1550
+ }
1551
+ if (!hasServices) {
1552
+ return function native_s2b_p(req, res, params) {
1553
+ try {
1554
+ if (!vp(params)) {
1555
+ fastWrite400("params", vp.errors, res);
1556
+ return;
1557
+ }
1558
+ const result = handlerIgnoresArgs ? handler() : handler({ req, params });
1559
+ if (result != null && typeof result.then === "function") {
1560
+ result.then(
1561
+ (r) => fastWriteJson(res, toJsonBody(r)),
1562
+ (err) => fastWriteError(err, res)
1563
+ );
1564
+ return;
1565
+ }
1566
+ fastWriteJson(res, toJsonBody(result));
1567
+ } catch (err) {
1568
+ fastWriteError(err, res);
1569
+ }
1570
+ };
1571
+ }
1572
+ return function native_s2b_p_svc(req, res, params) {
1573
+ try {
1574
+ if (!vp(params)) {
1575
+ fastWrite400("params", vp.errors, res);
1576
+ return;
1577
+ }
1578
+ const result = handlerIgnoresArgs ? handler() : handler({ req, params, services });
1579
+ if (result != null && typeof result.then === "function") {
1580
+ result.then(
1581
+ (r) => fastWriteJson(res, toJsonBody(r)),
1582
+ (err) => fastWriteError(err, res)
1583
+ );
1584
+ return;
1585
+ }
1586
+ fastWriteJson(res, toJsonBody(result));
1587
+ } catch (err) {
1588
+ fastWriteError(err, res);
1589
+ }
1590
+ };
1591
+ }
1592
+ if (hasQuery && hasParams && !hasBody) {
1593
+ const vq = validateQuery;
1594
+ const vp = validateParams;
1595
+ if (hasSer) {
1596
+ const ser = serialize;
1597
+ if (!hasServices) {
1598
+ return function native_s2c_qp_ser(req, res, params) {
1599
+ try {
1600
+ const query = parseNativeQuery(req.url ?? "/");
1601
+ if (!vq(query)) {
1602
+ fastWrite400("query", vq.errors, res);
1603
+ return;
1604
+ }
1605
+ if (!vp(params)) {
1606
+ fastWrite400("params", vp.errors, res);
1607
+ return;
1608
+ }
1609
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query, params });
1610
+ if (result != null && typeof result.then === "function") {
1611
+ result.then(
1612
+ (r) => fastWriteJson(res, ser(r)),
1613
+ (err) => fastWriteError(err, res)
1614
+ );
1615
+ return;
1616
+ }
1617
+ fastWriteJson(res, ser(result));
1618
+ } catch (err) {
1619
+ fastWriteError(err, res);
1620
+ }
1621
+ };
1622
+ }
1623
+ return function native_s2c_qp_svc_ser(req, res, params) {
1624
+ try {
1625
+ const query = parseNativeQuery(req.url ?? "/");
1626
+ if (!vq(query)) {
1627
+ fastWrite400("query", vq.errors, res);
1628
+ return;
1629
+ }
1630
+ if (!vp(params)) {
1631
+ fastWrite400("params", vp.errors, res);
1632
+ return;
1633
+ }
1634
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query, params, services });
1635
+ if (result != null && typeof result.then === "function") {
1636
+ result.then(
1637
+ (r) => fastWriteJson(res, ser(r)),
1638
+ (err) => fastWriteError(err, res)
1639
+ );
1640
+ return;
1641
+ }
1642
+ fastWriteJson(res, ser(result));
1643
+ } catch (err) {
1644
+ fastWriteError(err, res);
1645
+ }
1646
+ };
1647
+ }
1648
+ if (!hasServices) {
1649
+ return function native_s2c_qp(req, res, params) {
1650
+ try {
1651
+ const query = parseNativeQuery(req.url ?? "/");
1652
+ if (!vq(query)) {
1653
+ fastWrite400("query", vq.errors, res);
1654
+ return;
1655
+ }
1656
+ if (!vp(params)) {
1657
+ fastWrite400("params", vp.errors, res);
1658
+ return;
1659
+ }
1660
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query, params });
1661
+ if (result != null && typeof result.then === "function") {
1662
+ result.then(
1663
+ (r) => fastWriteJson(res, toJsonBody(r)),
1664
+ (err) => fastWriteError(err, res)
1665
+ );
1666
+ return;
1667
+ }
1668
+ fastWriteJson(res, toJsonBody(result));
1669
+ } catch (err) {
1670
+ fastWriteError(err, res);
1671
+ }
1672
+ };
1673
+ }
1674
+ return function native_s2c_qp_svc(req, res, params) {
1675
+ try {
1676
+ const query = parseNativeQuery(req.url ?? "/");
1677
+ if (!vq(query)) {
1678
+ fastWrite400("query", vq.errors, res);
1679
+ return;
1680
+ }
1681
+ if (!vp(params)) {
1682
+ fastWrite400("params", vp.errors, res);
1683
+ return;
1684
+ }
1685
+ const result = handlerIgnoresArgs ? handler() : handler({ req, query, params, services });
1686
+ if (result != null && typeof result.then === "function") {
1687
+ result.then(
1688
+ (r) => fastWriteJson(res, toJsonBody(r)),
1689
+ (err) => fastWriteError(err, res)
1690
+ );
1691
+ return;
1692
+ }
1693
+ fastWriteJson(res, toJsonBody(result));
1694
+ } catch (err) {
1695
+ fastWriteError(err, res);
1696
+ }
1697
+ };
1698
+ }
1699
+ if (hasBody && hasSer) {
1700
+ const vb = validateBody;
1701
+ const ser = serialize;
1702
+ const vq = validateQuery;
1703
+ const vp = validateParams;
1704
+ if (!hasServices) {
1705
+ return async function native_s3_body_ser(req, res, params) {
1706
+ try {
1707
+ const body = await readNativeBody(req);
1708
+ if (!vb(body)) {
1709
+ fastWrite400("body", vb.errors, res);
1710
+ return;
1711
+ }
1712
+ let query;
1713
+ if (vq) {
1714
+ query = parseNativeQuery(req.url ?? "/");
1715
+ if (!vq(query)) {
1716
+ fastWrite400("query", vq.errors, res);
1717
+ return;
1718
+ }
1719
+ }
1720
+ if (vp && !vp(params)) {
1721
+ fastWrite400("params", vp.errors, res);
1722
+ return;
1723
+ }
1724
+ const result = await handler({ req, body, query, params });
1725
+ fastWriteJson(res, ser(result));
1726
+ } catch (err) {
1727
+ fastWriteError(err, res);
1728
+ }
1729
+ };
1730
+ }
1731
+ return async function native_s3_body_ser_svc(req, res, params) {
1732
+ try {
1733
+ const body = await readNativeBody(req);
1734
+ if (!vb(body)) {
1735
+ fastWrite400("body", vb.errors, res);
1736
+ return;
1737
+ }
1738
+ let query;
1739
+ if (vq) {
1740
+ query = parseNativeQuery(req.url ?? "/");
1741
+ if (!vq(query)) {
1742
+ fastWrite400("query", vq.errors, res);
1743
+ return;
1744
+ }
1745
+ }
1746
+ if (vp && !vp(params)) {
1747
+ fastWrite400("params", vp.errors, res);
1748
+ return;
1749
+ }
1750
+ const result = await handler({ req, body, query, params, services });
1751
+ fastWriteJson(res, ser(result));
1752
+ } catch (err) {
1753
+ fastWriteError(err, res);
1754
+ }
1755
+ };
1756
+ }
1757
+ if (hasBody && !hasSer) {
1758
+ const vb = validateBody;
1759
+ const vq = validateQuery;
1760
+ const vp = validateParams;
1761
+ if (!hasServices) {
1762
+ return async function native_s4_body_noser(req, res, params) {
1763
+ try {
1764
+ const body = await readNativeBody(req);
1765
+ if (!vb(body)) {
1766
+ fastWrite400("body", vb.errors, res);
1767
+ return;
1768
+ }
1769
+ let query;
1770
+ if (vq) {
1771
+ query = parseNativeQuery(req.url ?? "/");
1772
+ if (!vq(query)) {
1773
+ fastWrite400("query", vq.errors, res);
1774
+ return;
1775
+ }
1776
+ }
1777
+ if (vp && !vp(params)) {
1778
+ fastWrite400("params", vp.errors, res);
1779
+ return;
1780
+ }
1781
+ const result = await handler({ req, body, query, params });
1782
+ fastWriteJson(res, toJsonBody(result));
1783
+ } catch (err) {
1784
+ fastWriteError(err, res);
1785
+ }
1786
+ };
1787
+ }
1788
+ return async function native_s4_body_noser_svc(req, res, params) {
1789
+ try {
1790
+ const body = await readNativeBody(req);
1791
+ if (!vb(body)) {
1792
+ fastWrite400("body", vb.errors, res);
1793
+ return;
1794
+ }
1795
+ let query;
1796
+ if (vq) {
1797
+ query = parseNativeQuery(req.url ?? "/");
1798
+ if (!vq(query)) {
1799
+ fastWrite400("query", vq.errors, res);
1800
+ return;
1801
+ }
1802
+ }
1803
+ if (vp && !vp(params)) {
1804
+ fastWrite400("params", vp.errors, res);
1805
+ return;
1806
+ }
1807
+ const result = await handler({ req, body, query, params, services });
1808
+ fastWriteJson(res, toJsonBody(result));
1809
+ } catch (err) {
1810
+ fastWriteError(err, res);
1811
+ }
1812
+ };
1813
+ }
1814
+ return function native_fallback(req, res, _p) {
1815
+ fastWriteError(new Error("Route scenario not implemented"), res);
1816
+ };
1817
+ }
1818
+ function compileUwsNativeHandler(handler, schema, services, compiled) {
1819
+ const { validateBody, validateQuery, validateParams, serialize } = compiled;
1820
+ const hasServices = services != null && Object.keys(services).length > 0;
1821
+ const hasQuery = !!validateQuery;
1822
+ const hasParams = !!validateParams;
1823
+ const hasBody = !!validateBody;
1824
+ const hasSer = !!serialize;
1825
+ const handlerIgnoresArgs = handler.length === 0;
1826
+ if (!hasBody && !hasQuery && !hasParams && !hasServices) {
1827
+ if (hasSer) {
1828
+ const ser = serialize;
1829
+ if (handlerIgnoresArgs) {
1830
+ return function uws_s1_bare_ser_noargs(uwsRes, _url, _raw, _p) {
1831
+ try {
1832
+ const result = handler();
1833
+ if (result != null && typeof result.then === "function") {
1834
+ result.then(
1835
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
1836
+ (err) => uwsFastWriteError(err, uwsRes)
1837
+ );
1838
+ return;
1839
+ }
1840
+ uwsFastWriteJson(uwsRes, ser(result));
1841
+ } catch (err) {
1842
+ uwsFastWriteError(err, uwsRes);
1843
+ }
1844
+ };
1845
+ }
1846
+ return function uws_s1_bare_ser(uwsRes, url, _raw, _p) {
1847
+ try {
1848
+ const result = handler({ req: { url } });
1849
+ if (result != null && typeof result.then === "function") {
1850
+ result.then(
1851
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
1852
+ (err) => uwsFastWriteError(err, uwsRes)
1853
+ );
1854
+ return;
1855
+ }
1856
+ uwsFastWriteJson(uwsRes, ser(result));
1857
+ } catch (err) {
1858
+ uwsFastWriteError(err, uwsRes);
1859
+ }
1860
+ };
1861
+ }
1862
+ if (handlerIgnoresArgs) {
1863
+ return function uws_s1_bare_noargs(uwsRes, _url, _raw, _p) {
1864
+ try {
1865
+ const result = handler();
1866
+ if (result != null && typeof result.then === "function") {
1867
+ result.then(
1868
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
1869
+ (err) => uwsFastWriteError(err, uwsRes)
1870
+ );
1871
+ return;
1872
+ }
1873
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
1874
+ } catch (err) {
1875
+ uwsFastWriteError(err, uwsRes);
1876
+ }
1877
+ };
1878
+ }
1879
+ return function uws_s1_bare(uwsRes, url, _raw, _p) {
1880
+ try {
1881
+ const result = handler({ req: { url } });
1882
+ if (result != null && typeof result.then === "function") {
1883
+ result.then(
1884
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
1885
+ (err) => uwsFastWriteError(err, uwsRes)
1886
+ );
1887
+ return;
1888
+ }
1889
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
1890
+ } catch (err) {
1891
+ uwsFastWriteError(err, uwsRes);
1892
+ }
1893
+ };
1894
+ }
1895
+ if (!hasBody && !hasQuery && !hasParams && hasServices) {
1896
+ if (hasSer) {
1897
+ const ser = serialize;
1898
+ if (handlerIgnoresArgs) {
1899
+ return function uws_s1b_svc_ser_noargs(uwsRes, _url, _raw, _p) {
1900
+ try {
1901
+ const result = handler();
1902
+ if (result != null && typeof result.then === "function") {
1903
+ result.then(
1904
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
1905
+ (err) => uwsFastWriteError(err, uwsRes)
1906
+ );
1907
+ return;
1908
+ }
1909
+ uwsFastWriteJson(uwsRes, ser(result));
1910
+ } catch (err) {
1911
+ uwsFastWriteError(err, uwsRes);
1912
+ }
1913
+ };
1914
+ }
1915
+ return function uws_s1b_svc_ser(uwsRes, url, _raw, _p) {
1916
+ try {
1917
+ const result = handler({ req: { url }, services });
1918
+ if (result != null && typeof result.then === "function") {
1919
+ result.then(
1920
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
1921
+ (err) => uwsFastWriteError(err, uwsRes)
1922
+ );
1923
+ return;
1924
+ }
1925
+ uwsFastWriteJson(uwsRes, ser(result));
1926
+ } catch (err) {
1927
+ uwsFastWriteError(err, uwsRes);
1928
+ }
1929
+ };
1930
+ }
1931
+ if (handlerIgnoresArgs) {
1932
+ return function uws_s1b_svc_noargs(uwsRes, _url, _raw, _p) {
1933
+ try {
1934
+ const result = handler();
1935
+ if (result != null && typeof result.then === "function") {
1936
+ result.then(
1937
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
1938
+ (err) => uwsFastWriteError(err, uwsRes)
1939
+ );
1940
+ return;
1941
+ }
1942
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
1943
+ } catch (err) {
1944
+ uwsFastWriteError(err, uwsRes);
1945
+ }
1946
+ };
1947
+ }
1948
+ return function uws_s1b_svc(uwsRes, url, _raw, _p) {
1949
+ try {
1950
+ const result = handler({ req: { url }, services });
1951
+ if (result != null && typeof result.then === "function") {
1952
+ result.then(
1953
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
1954
+ (err) => uwsFastWriteError(err, uwsRes)
1955
+ );
1956
+ return;
1957
+ }
1958
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
1959
+ } catch (err) {
1960
+ uwsFastWriteError(err, uwsRes);
1961
+ }
1962
+ };
1963
+ }
1964
+ if (hasQuery && !hasParams && !hasBody) {
1965
+ const vq = validateQuery;
1966
+ if (hasSer) {
1967
+ const ser = serialize;
1968
+ if (!hasServices) {
1969
+ return function uws_s2a_q_ser(uwsRes, url, _raw, _p) {
1970
+ try {
1971
+ const query = parseNativeQuery(url);
1972
+ if (!vq(query)) {
1973
+ uwsFastWrite400("query", vq.errors, uwsRes);
1974
+ return;
1975
+ }
1976
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query });
1977
+ if (result != null && typeof result.then === "function") {
1978
+ result.then(
1979
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
1980
+ (err) => uwsFastWriteError(err, uwsRes)
1981
+ );
1982
+ return;
1983
+ }
1984
+ uwsFastWriteJson(uwsRes, ser(result));
1985
+ } catch (err) {
1986
+ uwsFastWriteError(err, uwsRes);
1987
+ }
1988
+ };
1989
+ }
1990
+ return function uws_s2a_q_svc_ser(uwsRes, url, _raw, _p) {
1991
+ try {
1992
+ const query = parseNativeQuery(url);
1993
+ if (!vq(query)) {
1994
+ uwsFastWrite400("query", vq.errors, uwsRes);
1995
+ return;
1996
+ }
1997
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query, services });
1998
+ if (result != null && typeof result.then === "function") {
1999
+ result.then(
2000
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
2001
+ (err) => uwsFastWriteError(err, uwsRes)
2002
+ );
2003
+ return;
2004
+ }
2005
+ uwsFastWriteJson(uwsRes, ser(result));
2006
+ } catch (err) {
2007
+ uwsFastWriteError(err, uwsRes);
2008
+ }
2009
+ };
2010
+ }
2011
+ if (!hasServices) {
2012
+ return function uws_s2a_q(uwsRes, url, _raw, _p) {
2013
+ try {
2014
+ const query = parseNativeQuery(url);
2015
+ if (!vq(query)) {
2016
+ uwsFastWrite400("query", vq.errors, uwsRes);
2017
+ return;
2018
+ }
2019
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query });
2020
+ if (result != null && typeof result.then === "function") {
2021
+ result.then(
2022
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2023
+ (err) => uwsFastWriteError(err, uwsRes)
2024
+ );
2025
+ return;
2026
+ }
2027
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2028
+ } catch (err) {
2029
+ uwsFastWriteError(err, uwsRes);
2030
+ }
2031
+ };
2032
+ }
2033
+ return function uws_s2a_q_svc(uwsRes, url, _raw, _p) {
2034
+ try {
2035
+ const query = parseNativeQuery(url);
2036
+ if (!vq(query)) {
2037
+ uwsFastWrite400("query", vq.errors, uwsRes);
2038
+ return;
2039
+ }
2040
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query, services });
2041
+ if (result != null && typeof result.then === "function") {
2042
+ result.then(
2043
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2044
+ (err) => uwsFastWriteError(err, uwsRes)
2045
+ );
2046
+ return;
2047
+ }
2048
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2049
+ } catch (err) {
2050
+ uwsFastWriteError(err, uwsRes);
2051
+ }
2052
+ };
2053
+ }
2054
+ if (hasParams && !hasQuery && !hasBody) {
2055
+ const vp = validateParams;
2056
+ if (hasSer) {
2057
+ const ser = serialize;
2058
+ if (!hasServices) {
2059
+ return function uws_s2b_p_ser(uwsRes, url, _raw, params) {
2060
+ try {
2061
+ if (!vp(params)) {
2062
+ uwsFastWrite400("params", vp.errors, uwsRes);
2063
+ return;
2064
+ }
2065
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, params });
2066
+ if (result != null && typeof result.then === "function") {
2067
+ result.then(
2068
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
2069
+ (err) => uwsFastWriteError(err, uwsRes)
2070
+ );
2071
+ return;
2072
+ }
2073
+ uwsFastWriteJson(uwsRes, ser(result));
2074
+ } catch (err) {
2075
+ uwsFastWriteError(err, uwsRes);
2076
+ }
2077
+ };
2078
+ }
2079
+ return function uws_s2b_p_svc_ser(uwsRes, url, _raw, params) {
2080
+ try {
2081
+ if (!vp(params)) {
2082
+ uwsFastWrite400("params", vp.errors, uwsRes);
2083
+ return;
2084
+ }
2085
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, params, services });
2086
+ if (result != null && typeof result.then === "function") {
2087
+ result.then(
2088
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
2089
+ (err) => uwsFastWriteError(err, uwsRes)
2090
+ );
2091
+ return;
2092
+ }
2093
+ uwsFastWriteJson(uwsRes, ser(result));
2094
+ } catch (err) {
2095
+ uwsFastWriteError(err, uwsRes);
2096
+ }
2097
+ };
2098
+ }
2099
+ if (!hasServices) {
2100
+ return function uws_s2b_p(uwsRes, url, _raw, params) {
2101
+ try {
2102
+ if (!vp(params)) {
2103
+ uwsFastWrite400("params", vp.errors, uwsRes);
2104
+ return;
2105
+ }
2106
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, params });
2107
+ if (result != null && typeof result.then === "function") {
2108
+ result.then(
2109
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2110
+ (err) => uwsFastWriteError(err, uwsRes)
2111
+ );
2112
+ return;
2113
+ }
2114
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2115
+ } catch (err) {
2116
+ uwsFastWriteError(err, uwsRes);
2117
+ }
2118
+ };
2119
+ }
2120
+ return function uws_s2b_p_svc(uwsRes, url, _raw, params) {
2121
+ try {
2122
+ if (!vp(params)) {
2123
+ uwsFastWrite400("params", vp.errors, uwsRes);
2124
+ return;
2125
+ }
2126
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, params, services });
2127
+ if (result != null && typeof result.then === "function") {
2128
+ result.then(
2129
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2130
+ (err) => uwsFastWriteError(err, uwsRes)
2131
+ );
2132
+ return;
2133
+ }
2134
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2135
+ } catch (err) {
2136
+ uwsFastWriteError(err, uwsRes);
2137
+ }
2138
+ };
2139
+ }
2140
+ if (hasQuery && hasParams && !hasBody) {
2141
+ const vq = validateQuery;
2142
+ const vp = validateParams;
2143
+ if (hasSer) {
2144
+ const ser = serialize;
2145
+ if (!hasServices) {
2146
+ return function uws_s2c_qp_ser(uwsRes, url, _raw, params) {
2147
+ try {
2148
+ const query = parseNativeQuery(url);
2149
+ if (!vq(query)) {
2150
+ uwsFastWrite400("query", vq.errors, uwsRes);
2151
+ return;
2152
+ }
2153
+ if (!vp(params)) {
2154
+ uwsFastWrite400("params", vp.errors, uwsRes);
2155
+ return;
2156
+ }
2157
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query, params });
2158
+ if (result != null && typeof result.then === "function") {
2159
+ result.then(
2160
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
2161
+ (err) => uwsFastWriteError(err, uwsRes)
2162
+ );
2163
+ return;
2164
+ }
2165
+ uwsFastWriteJson(uwsRes, ser(result));
2166
+ } catch (err) {
2167
+ uwsFastWriteError(err, uwsRes);
2168
+ }
2169
+ };
2170
+ }
2171
+ return function uws_s2c_qp_svc_ser(uwsRes, url, _raw, params) {
2172
+ try {
2173
+ const query = parseNativeQuery(url);
2174
+ if (!vq(query)) {
2175
+ uwsFastWrite400("query", vq.errors, uwsRes);
2176
+ return;
2177
+ }
2178
+ if (!vp(params)) {
2179
+ uwsFastWrite400("params", vp.errors, uwsRes);
2180
+ return;
2181
+ }
2182
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query, params, services });
2183
+ if (result != null && typeof result.then === "function") {
2184
+ result.then(
2185
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
2186
+ (err) => uwsFastWriteError(err, uwsRes)
2187
+ );
2188
+ return;
2189
+ }
2190
+ uwsFastWriteJson(uwsRes, ser(result));
2191
+ } catch (err) {
2192
+ uwsFastWriteError(err, uwsRes);
2193
+ }
2194
+ };
2195
+ }
2196
+ if (!hasServices) {
2197
+ return function uws_s2c_qp(uwsRes, url, _raw, params) {
2198
+ try {
2199
+ const query = parseNativeQuery(url);
2200
+ if (!vq(query)) {
2201
+ uwsFastWrite400("query", vq.errors, uwsRes);
2202
+ return;
2203
+ }
2204
+ if (!vp(params)) {
2205
+ uwsFastWrite400("params", vp.errors, uwsRes);
2206
+ return;
2207
+ }
2208
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query, params });
2209
+ if (result != null && typeof result.then === "function") {
2210
+ result.then(
2211
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2212
+ (err) => uwsFastWriteError(err, uwsRes)
2213
+ );
2214
+ return;
2215
+ }
2216
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2217
+ } catch (err) {
2218
+ uwsFastWriteError(err, uwsRes);
2219
+ }
2220
+ };
2221
+ }
2222
+ return function uws_s2c_qp_svc(uwsRes, url, _raw, params) {
2223
+ try {
2224
+ const query = parseNativeQuery(url);
2225
+ if (!vq(query)) {
2226
+ uwsFastWrite400("query", vq.errors, uwsRes);
2227
+ return;
2228
+ }
2229
+ if (!vp(params)) {
2230
+ uwsFastWrite400("params", vp.errors, uwsRes);
2231
+ return;
2232
+ }
2233
+ const result = handlerIgnoresArgs ? handler() : handler({ req: { url }, query, params, services });
2234
+ if (result != null && typeof result.then === "function") {
2235
+ result.then(
2236
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2237
+ (err) => uwsFastWriteError(err, uwsRes)
2238
+ );
2239
+ return;
2240
+ }
2241
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2242
+ } catch (err) {
2243
+ uwsFastWriteError(err, uwsRes);
2244
+ }
2245
+ };
2246
+ }
2247
+ if (hasBody && hasSer) {
2248
+ const vb = validateBody;
2249
+ const ser = serialize;
2250
+ const vq = validateQuery;
2251
+ const vp = validateParams;
2252
+ if (!hasServices) {
2253
+ return function uws_s3_body_ser(uwsRes, url, rawBody, params) {
2254
+ try {
2255
+ let body;
2256
+ try {
2257
+ body = rawBody ? JSON.parse(rawBody) : {};
2258
+ } catch {
2259
+ body = {};
2260
+ }
2261
+ if (!vb(body)) {
2262
+ uwsFastWrite400("body", vb.errors, uwsRes);
2263
+ return;
2264
+ }
2265
+ let query;
2266
+ if (vq) {
2267
+ query = parseNativeQuery(url);
2268
+ if (!vq(query)) {
2269
+ uwsFastWrite400("query", vq.errors, uwsRes);
2270
+ return;
2271
+ }
2272
+ }
2273
+ if (vp && !vp(params)) {
2274
+ uwsFastWrite400("params", vp.errors, uwsRes);
2275
+ return;
2276
+ }
2277
+ const result = handler({ req: { url }, body, query, params });
2278
+ if (result != null && typeof result.then === "function") {
2279
+ result.then(
2280
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
2281
+ (err) => uwsFastWriteError(err, uwsRes)
2282
+ );
2283
+ return;
2284
+ }
2285
+ uwsFastWriteJson(uwsRes, ser(result));
2286
+ } catch (err) {
2287
+ uwsFastWriteError(err, uwsRes);
2288
+ }
2289
+ };
2290
+ }
2291
+ return function uws_s3_body_ser_svc(uwsRes, url, rawBody, params) {
2292
+ try {
2293
+ let body;
2294
+ try {
2295
+ body = rawBody ? JSON.parse(rawBody) : {};
2296
+ } catch {
2297
+ body = {};
2298
+ }
2299
+ if (!vb(body)) {
2300
+ uwsFastWrite400("body", vb.errors, uwsRes);
2301
+ return;
2302
+ }
2303
+ let query;
2304
+ if (vq) {
2305
+ query = parseNativeQuery(url);
2306
+ if (!vq(query)) {
2307
+ uwsFastWrite400("query", vq.errors, uwsRes);
2308
+ return;
2309
+ }
2310
+ }
2311
+ if (vp && !vp(params)) {
2312
+ uwsFastWrite400("params", vp.errors, uwsRes);
2313
+ return;
2314
+ }
2315
+ const result = handler({ req: { url }, body, query, params, services });
2316
+ if (result != null && typeof result.then === "function") {
2317
+ result.then(
2318
+ (r) => uwsFastWriteJson(uwsRes, ser(r)),
2319
+ (err) => uwsFastWriteError(err, uwsRes)
2320
+ );
2321
+ return;
2322
+ }
2323
+ uwsFastWriteJson(uwsRes, ser(result));
2324
+ } catch (err) {
2325
+ uwsFastWriteError(err, uwsRes);
2326
+ }
2327
+ };
2328
+ }
2329
+ if (hasBody && !hasSer) {
2330
+ const vb = validateBody;
2331
+ const vq = validateQuery;
2332
+ const vp = validateParams;
2333
+ if (!hasServices) {
2334
+ return function uws_s4_body_noser(uwsRes, url, rawBody, params) {
2335
+ try {
2336
+ let body;
2337
+ try {
2338
+ body = rawBody ? JSON.parse(rawBody) : {};
2339
+ } catch {
2340
+ body = {};
2341
+ }
2342
+ if (!vb(body)) {
2343
+ uwsFastWrite400("body", vb.errors, uwsRes);
2344
+ return;
2345
+ }
2346
+ let query;
2347
+ if (vq) {
2348
+ query = parseNativeQuery(url);
2349
+ if (!vq(query)) {
2350
+ uwsFastWrite400("query", vq.errors, uwsRes);
2351
+ return;
2352
+ }
2353
+ }
2354
+ if (vp && !vp(params)) {
2355
+ uwsFastWrite400("params", vp.errors, uwsRes);
2356
+ return;
2357
+ }
2358
+ const result = handler({ req: { url }, body, query, params });
2359
+ if (result != null && typeof result.then === "function") {
2360
+ result.then(
2361
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2362
+ (err) => uwsFastWriteError(err, uwsRes)
2363
+ );
2364
+ return;
2365
+ }
2366
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2367
+ } catch (err) {
2368
+ uwsFastWriteError(err, uwsRes);
2369
+ }
2370
+ };
2371
+ }
2372
+ return function uws_s4_body_noser_svc(uwsRes, url, rawBody, params) {
2373
+ try {
2374
+ let body;
2375
+ try {
2376
+ body = rawBody ? JSON.parse(rawBody) : {};
2377
+ } catch {
2378
+ body = {};
2379
+ }
2380
+ if (!vb(body)) {
2381
+ uwsFastWrite400("body", vb.errors, uwsRes);
2382
+ return;
2383
+ }
2384
+ let query;
2385
+ if (vq) {
2386
+ query = parseNativeQuery(url);
2387
+ if (!vq(query)) {
2388
+ uwsFastWrite400("query", vq.errors, uwsRes);
2389
+ return;
2390
+ }
2391
+ }
2392
+ if (vp && !vp(params)) {
2393
+ uwsFastWrite400("params", vp.errors, uwsRes);
2394
+ return;
2395
+ }
2396
+ const result = handler({ req: { url }, body, query, params, services });
2397
+ if (result != null && typeof result.then === "function") {
2398
+ result.then(
2399
+ (r) => uwsFastWriteJson(uwsRes, toJsonBody(r)),
2400
+ (err) => uwsFastWriteError(err, uwsRes)
2401
+ );
2402
+ return;
2403
+ }
2404
+ uwsFastWriteJson(uwsRes, toJsonBody(result));
2405
+ } catch (err) {
2406
+ uwsFastWriteError(err, uwsRes);
2407
+ }
2408
+ };
2409
+ }
2410
+ return function uws_fallback(uwsRes, _url, _raw, _p) {
2411
+ uwsFastWriteError(new Error("Route scenario not implemented"), uwsRes);
2412
+ };
2413
+ }
2414
+
2415
+ // src/shutdown.ts
2416
+ function createInflightTracker() {
2417
+ return {
2418
+ count: 0,
2419
+ requests: /* @__PURE__ */ new Set()
2420
+ };
2421
+ }
2422
+ function trackRequest(tracker) {
2423
+ tracker.count++;
2424
+ let resolvePromise;
2425
+ const promise = new Promise((resolve2) => {
2426
+ resolvePromise = resolve2;
2427
+ });
2428
+ tracker.requests.add(promise);
2429
+ return () => {
2430
+ tracker.count--;
2431
+ tracker.requests.delete(promise);
2432
+ resolvePromise();
2433
+ };
2434
+ }
2435
+ var ShutdownManager = class {
2436
+ state = "running";
2437
+ abortController = null;
2438
+ server = null;
2439
+ tracker;
2440
+ database = null;
2441
+ databaseProvider = null;
2442
+ constructor() {
2443
+ this.tracker = createInflightTracker();
2444
+ }
2445
+ /**
2446
+ * Get current shutdown state
2447
+ */
2448
+ getState() {
2449
+ return this.state;
2450
+ }
2451
+ /**
2452
+ * Check if server is shutting down
2453
+ */
2454
+ isShuttingDown() {
2455
+ return this.state !== "running";
2456
+ }
2457
+ /**
2458
+ * Get current in-flight request count
2459
+ */
2460
+ getInflightCount() {
2461
+ return this.tracker.count;
2462
+ }
2463
+ /**
2464
+ * Set the server instance for shutdown
2465
+ */
2466
+ setServer(server) {
2467
+ this.server = server;
2468
+ }
2469
+ /**
2470
+ * Set database for cleanup
2471
+ */
2472
+ setDatabase(db, provider) {
2473
+ this.database = db;
2474
+ this.databaseProvider = provider;
2475
+ }
2476
+ /**
2477
+ * Get the AbortController signal for request cancellation
2478
+ */
2479
+ getAbortSignal() {
2480
+ return this.abortController?.signal;
2481
+ }
2482
+ /**
2483
+ * Create a request tracker middleware
2484
+ * Returns an untrack function to call when request completes
2485
+ */
2486
+ trackRequest() {
2487
+ return trackRequest(this.tracker);
2488
+ }
2489
+ /**
2490
+ * Initiate graceful shutdown
2491
+ */
2492
+ async shutdown(options = {}) {
2493
+ const {
2494
+ timeoutMs = 3e4,
2495
+ onShutdownStart,
2496
+ onShutdownComplete,
2497
+ onShutdownTimeout
2498
+ } = options;
2499
+ if (this.state === "shutdown") {
2500
+ console.warn("[Kozo] Shutdown already completed");
2501
+ return;
2502
+ }
2503
+ if (this.state === "shutting-down") {
2504
+ console.warn("[Kozo] Shutdown already in progress");
2505
+ return;
2506
+ }
2507
+ this.state = "shutting-down";
2508
+ this.abortController = new AbortController();
2509
+ this.abortController.abort();
2510
+ const inflightCount = this.tracker.count;
2511
+ onShutdownStart?.(inflightCount);
2512
+ if (inflightCount > 0) {
2513
+ console.log(`[Kozo] Graceful shutdown: waiting for ${inflightCount} in-flight requests`);
2514
+ }
2515
+ if (this.server) {
2516
+ this.server.close(() => {
2517
+ console.log("[Kozo] HTTP server closed");
2518
+ });
2519
+ }
2520
+ const drainPromise = this.drainRequests();
2521
+ const timeoutPromise = new Promise((resolve2) => {
2522
+ setTimeout(() => {
2523
+ onShutdownTimeout?.(this.tracker.count);
2524
+ resolve2();
2525
+ }, timeoutMs);
2526
+ });
2527
+ await Promise.race([drainPromise, timeoutPromise]);
2528
+ await this.closeDatabase();
2529
+ this.state = "shutdown";
2530
+ onShutdownComplete?.();
2531
+ console.log("[Kozo] Graceful shutdown complete");
2532
+ }
2533
+ /**
2534
+ * Wait for all in-flight requests to complete
2535
+ */
2536
+ async drainRequests() {
2537
+ if (this.tracker.requests.size === 0) {
2538
+ return;
2539
+ }
2540
+ await Promise.all([...this.tracker.requests]);
2541
+ }
2542
+ /**
2543
+ * Close database connections based on provider
2544
+ */
2545
+ async closeDatabase() {
2546
+ if (!this.database || !this.databaseProvider) {
2547
+ return;
2548
+ }
2549
+ try {
2550
+ switch (this.databaseProvider) {
2551
+ case "postgresql": {
2552
+ const client = this.database.$client;
2553
+ if (client && typeof client.end === "function") {
2554
+ await client.end();
2555
+ console.log("[Kozo] PostgreSQL connection closed");
2556
+ }
2557
+ break;
2558
+ }
2559
+ case "mysql": {
2560
+ const client = this.database.$client;
2561
+ if (client && typeof client.end === "function") {
2562
+ await client.end();
2563
+ console.log("[Kozo] MySQL connection closed");
2564
+ }
2565
+ break;
2566
+ }
2567
+ case "sqlite": {
2568
+ const client = this.database.$client;
2569
+ if (client && typeof client.close === "function") {
2570
+ client.close();
2571
+ console.log("[Kozo] SQLite connection closed");
2572
+ }
2573
+ break;
2574
+ }
2575
+ }
2576
+ } catch (err) {
2577
+ console.error("[Kozo] Error closing database connection:", err);
2578
+ }
2579
+ }
2580
+ };
2581
+ function createShutdownManager() {
2582
+ return new ShutdownManager();
2583
+ }
2584
+
2585
+ // src/router.ts
2586
+ import { glob } from "glob";
2587
+ import { join } from "path";
2588
+ import { pathToFileURL } from "url";
2589
+
2590
+ // src/utils/file-to-path.ts
2591
+ import { parse } from "path";
2592
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
2593
+ function fileToPath(filePath) {
2594
+ const normalized = filePath.replace(/\\/g, "/");
2595
+ const parsed = parse(normalized);
2596
+ const filename = parsed.name.toLowerCase();
2597
+ let method = "get";
2598
+ let includeName = true;
2599
+ if (HTTP_METHODS.includes(filename)) {
2600
+ method = filename;
2601
+ includeName = false;
2602
+ } else if (filename === "index") {
2603
+ includeName = false;
2604
+ }
2605
+ let segments = parsed.dir ? parsed.dir.split("/").filter(Boolean) : [];
2606
+ if (includeName) {
2607
+ segments.push(parsed.name);
2608
+ }
2609
+ const urlSegments = segments.map((segment) => {
2610
+ if (segment.startsWith("[...") && segment.endsWith("]")) {
2611
+ return "*";
2612
+ }
2613
+ if (segment.startsWith("[") && segment.endsWith("?]")) {
2614
+ return ":" + segment.slice(1, -2) + "?";
2615
+ }
2616
+ if (segment.startsWith("[") && segment.endsWith("]")) {
2617
+ return ":" + segment.slice(1, -1);
2618
+ }
2619
+ return segment;
2620
+ });
2621
+ let path = "/" + urlSegments.join("/");
2622
+ path = path.replace(/\/+/g, "/");
2623
+ if (path.length > 1 && path.endsWith("/")) {
2624
+ path = path.slice(0, -1);
2625
+ }
2626
+ return { path, method };
2627
+ }
2628
+ function isRouteFile(filename) {
2629
+ const segments = filename.replace(/\\/g, "/").split("/");
2630
+ if (segments.some((s) => s.startsWith("_"))) return false;
2631
+ if (filename.includes(".test.") || filename.includes(".spec.")) return false;
2632
+ return filename.endsWith(".ts") || filename.endsWith(".js");
2633
+ }
2634
+
2635
+ // src/router.ts
2636
+ async function scanRoutes(options) {
2637
+ const { routesDir, verbose = true } = options;
2638
+ const routes = [];
2639
+ if (verbose) {
2640
+ console.log(`
2641
+ \u{1F50D} Scanning routes in: ${routesDir}
2642
+ `);
2643
+ }
2644
+ const pattern = "**/*.{ts,js}";
2645
+ const files = await glob(pattern, {
2646
+ cwd: routesDir,
2647
+ nodir: true,
2648
+ ignore: ["**/_*.ts", "**/_*.js", "**/*.test.ts", "**/*.spec.ts"]
2649
+ });
2650
+ for (const file of files) {
2651
+ if (!isRouteFile(file)) continue;
2652
+ const parsed = fileToPath(file);
2653
+ if (!parsed) continue;
2654
+ const fullPath = join(routesDir, file);
2655
+ try {
2656
+ const fileUrl = pathToFileURL(fullPath).href;
2657
+ const module = await import(fileUrl);
2658
+ if (typeof module.default !== "function") {
2659
+ console.warn(`\u26A0\uFE0F Skipping ${file}: no default export function`);
2660
+ continue;
2661
+ }
2662
+ routes.push({
2663
+ path: parsed.path,
2664
+ method: parsed.method,
2665
+ filePath: fullPath,
2666
+ module
2667
+ });
2668
+ if (verbose) {
2669
+ const methodLabel = parsed.method.toUpperCase().padEnd(6);
2670
+ console.log(` ${methodLabel} ${parsed.path}`);
2671
+ }
2672
+ } catch (err) {
2673
+ console.error(`\u274C Failed to load route: ${file}`);
2674
+ console.error(err);
2675
+ }
2676
+ }
2677
+ if (verbose) {
2678
+ console.log(`
2679
+ \u2705 Loaded ${routes.length} routes
2680
+ `);
2681
+ }
2682
+ routes.sort((a, b) => {
2683
+ const aScore = routeScore(a.path);
2684
+ const bScore = routeScore(b.path);
2685
+ return bScore - aScore;
2686
+ });
2687
+ return routes;
2688
+ }
2689
+ function routeScore(path) {
2690
+ const segments = path.split("/").filter(Boolean);
2691
+ let score = segments.length * 10;
2692
+ for (const segment of segments) {
2693
+ if (segment === "*") {
2694
+ score -= 100;
2695
+ } else if (segment.startsWith(":")) {
2696
+ score -= 5;
2697
+ } else {
2698
+ score += 1;
2699
+ }
2700
+ }
2701
+ return score;
2702
+ }
2703
+
2704
+ // src/wasm-router.ts
2705
+ import { readFile } from "fs/promises";
2706
+ import { fileURLToPath } from "url";
2707
+ import { dirname, join as join2 } from "path";
2708
+ var METHOD_INDEX = {
2709
+ GET: 0,
2710
+ POST: 1,
2711
+ PUT: 2,
2712
+ PATCH: 3,
2713
+ DELETE: 4,
2714
+ OPTIONS: 5,
2715
+ HEAD: 6
2716
+ };
2717
+ function methodToIndex(m) {
2718
+ switch (m.length) {
2719
+ case 3:
2720
+ return m.charCodeAt(0) === 71 ? 0 : 2;
2721
+ case 4:
2722
+ return m.charCodeAt(0) === 80 ? 1 : 6;
2723
+ case 5:
2724
+ return 3;
2725
+ case 6:
2726
+ return 4;
2727
+ case 7:
2728
+ return 5;
2729
+ default:
2730
+ return METHOD_INDEX[m] ?? -1;
2731
+ }
2732
+ }
2733
+ var WasmRadixRouter = class {
2734
+ exports = null;
2735
+ urlView = null;
2736
+ paramView = null;
2737
+ patternView = null;
2738
+ // v2: plain arrays indexed by route_id — O(1) with no hash overhead
2739
+ handlers = [];
2740
+ paramNames = [];
2741
+ /** monotonic route counter */
2742
+ nextRouteId = 0;
2743
+ /** whether the WASM module loaded successfully */
2744
+ ready = false;
2745
+ // v3: pre-allocated match result — reused across calls to avoid allocation.
2746
+ // Safe because dispatch() reads result synchronously and never stores it.
2747
+ _emptyParams = Object.freeze({});
2748
+ // ── Lifecycle ─────────────────────────────────────────────────────────
2749
+ /**
2750
+ * Attempt to load and instantiate the WASM module.
2751
+ * Returns `true` on success, `false` if the .wasm file is missing or
2752
+ * instantiation fails (caller should fall back to JS routing).
2753
+ */
2754
+ async init() {
2755
+ try {
2756
+ const thisDir = dirname(fileURLToPath(import.meta.url));
2757
+ const wasmPath = join2(thisDir, "wasm", "radix.wasm");
2758
+ const wasmBytes = await readFile(wasmPath);
2759
+ const { instance } = await WebAssembly.instantiate(wasmBytes);
2760
+ this.exports = instance.exports;
2761
+ const mem = this.exports.memory;
2762
+ const urlPtr = this.exports.get_url_buf_ptr();
2763
+ const paramPtr = this.exports.get_param_buf_ptr();
2764
+ const patPtr = this.exports.get_pattern_buf_ptr();
2765
+ this.urlView = new Uint8Array(mem.buffer, urlPtr, 4096);
2766
+ this.paramView = new DataView(mem.buffer, paramPtr, 32);
2767
+ this.patternView = new Uint8Array(mem.buffer, patPtr, 2048);
2768
+ this.exports.init();
2769
+ this.ready = true;
2770
+ return true;
2771
+ } catch {
2772
+ this.ready = false;
2773
+ return false;
2774
+ }
2775
+ }
2776
+ /** Is the WASM module loaded and operational? */
2777
+ get isReady() {
2778
+ return this.ready;
2779
+ }
2780
+ // ── Route registration (called at startup, not hot-path) ──────────────
2781
+ /**
2782
+ * Register a route in the WASM radix trie.
2783
+ *
2784
+ * @param method HTTP method (GET, POST, …)
2785
+ * @param path Express-style pattern, e.g. `/api/users/:id` or `/blog/*`
2786
+ * @param handler Compiled native handler (same type used by nativeListen)
2787
+ * @returns The assigned route ID
2788
+ */
2789
+ addRoute(method, path, handler) {
2790
+ if (!this.ready) throw new Error("WasmRadixRouter not initialized");
2791
+ const routeId = this.nextRouteId++;
2792
+ const methodIdx = METHOD_INDEX[method.toUpperCase()];
2793
+ if (methodIdx === void 0) throw new Error(`Unsupported method: ${method}`);
2794
+ const names = [];
2795
+ const segments = path.split("/");
2796
+ for (const seg of segments) {
2797
+ if (seg.startsWith(":")) names.push(seg.slice(1));
2798
+ else if (seg === "*") names.push("*");
2799
+ }
2800
+ this.paramNames[routeId] = names;
2801
+ this.handlers[routeId] = handler;
2802
+ const patLen = this.writeAscii(this.patternView, path);
2803
+ this.exports.insert_route(methodIdx, patLen, routeId);
2804
+ return routeId;
2805
+ }
2806
+ // ── Hot-path matching ─────────────────────────────────────────────────
2807
+ /**
2808
+ * Match a request path against the radix trie.
2809
+ *
2810
+ * ZERO-COPY: the path string is written byte-by-byte into WASM memory
2811
+ * without allocating a Buffer or Uint8Array. Param values are read
2812
+ * back as slices of the **original JS string** (the offsets produced by
2813
+ * WASM coincide with JS character indices for ASCII paths).
2814
+ *
2815
+ * v3: Returns a pre-allocated result object that is reused across calls.
2816
+ * For static routes (paramCount === 0), the params object is a frozen
2817
+ * singleton — ZERO allocations per request on the hot path.
2818
+ *
2819
+ * @returns `null` on miss; otherwise the handler + extracted params.
2820
+ */
2821
+ match(method, path) {
2822
+ const methodIdx = methodToIndex(method);
2823
+ if (methodIdx === -1) return null;
2824
+ const urlView = this.urlView;
2825
+ const len = path.length;
2826
+ for (let i = 0; i < len; i++) {
2827
+ urlView[i] = path.charCodeAt(i);
2828
+ }
2829
+ const routeId = this.exports.match_url(methodIdx, len);
2830
+ if (routeId === -1) return null;
2831
+ const handler = this.handlers[routeId];
2832
+ if (!handler) return null;
2833
+ const paramCount = this.exports.get_param_count();
2834
+ if (paramCount === 0) {
2835
+ return { handler, params: this._emptyParams };
2836
+ }
2837
+ const names = this.paramNames[routeId];
2838
+ const params = {};
2839
+ const pv = this.paramView;
2840
+ for (let i = 0; i < paramCount && i < names.length; i++) {
2841
+ const byteOff = i * 4;
2842
+ const offset = pv.getUint16(byteOff, true);
2843
+ const plen = pv.getUint16(byteOff + 2, true);
2844
+ params[names[i]] = path.slice(offset, offset + plen);
2845
+ }
2846
+ return { handler, params };
2847
+ }
2848
+ // ── Exposed internals for inline dispatch in app.ts ────────────────────
2849
+ /**
2850
+ * Expose raw WASM buffers + handler arrays for zero-overhead inline
2851
+ * dispatch in app.ts. Avoids the match() → WasmMatchResult wrapper
2852
+ * allocation on dynamic routes (~30-50 ns saved per request).
2853
+ *
2854
+ * SAFETY: single-threaded — no concurrent access.
2855
+ */
2856
+ getInternals() {
2857
+ return {
2858
+ urlView: this.urlView,
2859
+ exports: this.exports,
2860
+ handlers: this.handlers,
2861
+ paramNames: this.paramNames,
2862
+ paramView: this.paramView,
2863
+ emptyParams: this._emptyParams
2864
+ };
2865
+ }
2866
+ // ── Internals ─────────────────────────────────────────────────────────
2867
+ /**
2868
+ * Fast ASCII write — avoids TextEncoder allocation overhead (~50 ns).
2869
+ * URL paths are always ASCII, so charCodeAt() is byte-identical.
2870
+ */
2871
+ writeAscii(view, str) {
2872
+ const len = str.length;
2873
+ for (let i = 0; i < len; i++) {
2874
+ view[i] = str.charCodeAt(i);
2875
+ }
2876
+ return len;
2877
+ }
2878
+ };
2879
+
2880
+ // src/app.ts
2881
+ var Kozo = class {
2882
+ app;
2883
+ services;
2884
+ routes = [];
2885
+ nativeRoutes = [];
2886
+ shutdownManager = new ShutdownManager();
2887
+ wasmRouter = new WasmRadixRouter();
2888
+ /** Maps each NativeRouteHandler to its zero-shim uWS counterpart */
2889
+ uwsHandlerMap = /* @__PURE__ */ new Map();
2890
+ _routesDir;
2891
+ constructor(config = {}) {
2892
+ this.app = new Hono();
2893
+ this.services = config.services ?? {};
2894
+ this._routesDir = config.routesDir;
2895
+ this.app.onError((err, c) => {
2896
+ if (err instanceof KozoError) {
2897
+ return err.toResponse(c.req.path);
2898
+ }
2899
+ console.error("[Kozo] Unhandled error:", err);
2900
+ return internalErrorResponse(err, c.req.path);
2901
+ });
2902
+ }
2903
+ // Plugin system
2904
+ use(plugin) {
627
2905
  plugin.install(this);
628
2906
  return this;
629
2907
  }
2908
+ /**
2909
+ * Load routes from the file system using the configured routesDir.
2910
+ * Each route file is dynamically imported, its schema compiled, and handler registered.
2911
+ * This is a no-op if routesDir is not configured.
2912
+ */
2913
+ async loadRoutes(routesDir) {
2914
+ const dir = routesDir ?? this._routesDir;
2915
+ if (!dir) return this;
2916
+ const routes = await scanRoutes({ routesDir: dir, verbose: false });
2917
+ for (const route of routes) {
2918
+ const { path, method, module } = route;
2919
+ const schema = module.schema ?? {};
2920
+ const compiled = SchemaCompiler.compile(schema);
2921
+ const userHandler = module.default;
2922
+ const optimizedHandler = compileRouteHandler(
2923
+ (ctx) => userHandler(ctx),
2924
+ schema,
2925
+ this.services,
2926
+ compiled
2927
+ );
2928
+ this.routes.push({ method, path, schema });
2929
+ this.app[method](path, optimizedHandler);
2930
+ }
2931
+ return this;
2932
+ }
630
2933
  generateClient(baseUrlOrOptions) {
631
2934
  const options = typeof baseUrlOrOptions === "string" ? { baseUrl: baseUrlOrOptions, includeValidation: false } : baseUrlOrOptions || {};
632
2935
  const routeInfos = this.routes.map((r) => ({
@@ -637,19 +2940,19 @@ var Kozo = class {
637
2940
  return generateTypedClient(routeInfos, options);
638
2941
  }
639
2942
  get(path, schema, handler) {
640
- this.register("get", path, schema, handler);
2943
+ return this.register("get", path, schema, handler);
641
2944
  }
642
2945
  post(path, schema, handler) {
643
- this.register("post", path, schema, handler);
2946
+ return this.register("post", path, schema, handler);
644
2947
  }
645
2948
  put(path, schema, handler) {
646
- this.register("put", path, schema, handler);
2949
+ return this.register("put", path, schema, handler);
647
2950
  }
648
2951
  patch(path, schema, handler) {
649
- this.register("patch", path, schema, handler);
2952
+ return this.register("patch", path, schema, handler);
650
2953
  }
651
2954
  delete(path, schema, handler) {
652
- this.register("delete", path, schema, handler);
2955
+ return this.register("delete", path, schema, handler);
653
2956
  }
654
2957
  register(method, path, schema, handler) {
655
2958
  this.routes.push({ method, path, schema });
@@ -661,15 +2964,191 @@ var Kozo = class {
661
2964
  compiled
662
2965
  );
663
2966
  this.app[method](path, optimizedHandler);
2967
+ const nativeHandler = compileNativeHandler(handler, schema, this.services, compiled);
2968
+ const uwsHandler = compileUwsNativeHandler(handler, schema, this.services, compiled);
2969
+ this.uwsHandlerMap.set(nativeHandler, uwsHandler);
2970
+ const paramNames = [];
2971
+ const regexStr = path.replace(/:([^/]+)/g, (_, name) => {
2972
+ paramNames.push(name);
2973
+ return "([^/]+)";
2974
+ });
2975
+ if (paramNames.length === 0) {
2976
+ this.nativeRoutes.push({ method: method.toUpperCase(), path, staticPath: path, handler: nativeHandler });
2977
+ } else {
2978
+ this.nativeRoutes.push({
2979
+ method: method.toUpperCase(),
2980
+ path,
2981
+ regex: new RegExp(`^${regexStr}$`),
2982
+ paramNames,
2983
+ handler: nativeHandler
2984
+ });
2985
+ }
2986
+ return this;
2987
+ }
2988
+ /**
2989
+ * Start a raw Node.js HTTP server (no @hono/node-server adapter).
2990
+ * Writes directly to ServerResponse — eliminates Web API Request/Response
2991
+ * allocation and adapter overhead for maximum throughput.
2992
+ *
2993
+ * When the Zig WASM radix trie is available (`src/wasm/radix.wasm`),
2994
+ * ALL route matching (static + dynamic) runs through the zero-copy
2995
+ * trie — one call into WASM per request.
2996
+ *
2997
+ * When the .wasm file is absent (Edge deploy, CI without Zig, etc.)
2998
+ * routing falls back transparently to the JS Map + RegExp approach.
2999
+ *
3000
+ * Returns { port, server } so the caller can close the server when done.
3001
+ */
3002
+ async nativeListen(port = 3e3) {
3003
+ const wasmOk = await this.wasmRouter.init();
3004
+ if (wasmOk) {
3005
+ for (const entry of this.nativeRoutes) {
3006
+ const path = entry.staticPath ?? entry.regex?.source.replace(/^\^/, "").replace(/\$$/, "").replace(/\(\[\\^\/\]\+\)/g, () => `:${entry.paramNames?.shift() ?? "p"}`) ?? "/";
3007
+ this.wasmRouter.addRoute(entry.method, entry.staticPath ?? path, entry.handler);
3008
+ }
3009
+ console.log("\u26A1 WASM radix router active (zero-copy Zig trie)");
3010
+ } else {
3011
+ console.log("\u2139\uFE0F WASM radix router unavailable \u2014 using JS fallback");
3012
+ }
3013
+ const staticMap = /* @__PURE__ */ new Map();
3014
+ const dynamicRoutes = [];
3015
+ for (const entry of this.nativeRoutes) {
3016
+ if (entry.staticPath !== void 0) {
3017
+ staticMap.set(`${entry.method}:${entry.staticPath}`, entry.handler);
3018
+ } else {
3019
+ dynamicRoutes.push({
3020
+ method: entry.method,
3021
+ regex: entry.regex,
3022
+ paramNames: entry.paramNames,
3023
+ handler: entry.handler
3024
+ });
3025
+ }
3026
+ }
3027
+ const wasm = wasmOk ? this.wasmRouter : null;
3028
+ const emptyParams = Object.freeze({});
3029
+ const matchRoute = (method, path) => {
3030
+ if (wasm) {
3031
+ const r = wasm.match(method, path);
3032
+ if (r) return { handler: r.handler, params: r.params };
3033
+ return null;
3034
+ }
3035
+ const staticHandler = staticMap.get(`${method}:${path}`);
3036
+ if (staticHandler) return { handler: staticHandler, params: emptyParams };
3037
+ for (const route of dynamicRoutes) {
3038
+ if (route.method !== method) continue;
3039
+ const m = route.regex.exec(path);
3040
+ if (!m) continue;
3041
+ const params = {};
3042
+ for (let i = 0; i < route.paramNames.length; i++) {
3043
+ params[route.paramNames[i]] = m[i + 1];
3044
+ }
3045
+ return { handler: route.handler, params };
3046
+ }
3047
+ return null;
3048
+ };
3049
+ const uwsBindings = await tryLoadUws();
3050
+ if (uwsBindings) {
3051
+ console.log("\u{1F680} uWebSockets.js transport active (native per-route C++ matching)");
3052
+ const uwsMap = this.uwsHandlerMap;
3053
+ const uwsRoutes = [];
3054
+ for (const entry of this.nativeRoutes) {
3055
+ const uwsH = uwsMap.get(entry.handler);
3056
+ if (!uwsH) continue;
3057
+ uwsRoutes.push({
3058
+ method: entry.method,
3059
+ path: entry.path,
3060
+ paramNames: entry.paramNames ?? [],
3061
+ handler: uwsH
3062
+ });
3063
+ }
3064
+ return createUwsServer({
3065
+ uws: uwsBindings,
3066
+ routes: uwsRoutes,
3067
+ port
3068
+ });
3069
+ }
3070
+ const dispatch = (req, res) => {
3071
+ const method = req.method;
3072
+ const rawUrl = req.url;
3073
+ const qIdx = rawUrl.indexOf("?");
3074
+ const path = qIdx === -1 ? rawUrl : rawUrl.slice(0, qIdx);
3075
+ if (wasm) {
3076
+ const result = wasm.match(method, path);
3077
+ if (result) {
3078
+ result.handler(req, res, result.params);
3079
+ return;
3080
+ }
3081
+ fastWrite404(res);
3082
+ return;
3083
+ }
3084
+ const staticHandler = staticMap.get(`${method}:${path}`);
3085
+ if (staticHandler) {
3086
+ staticHandler(req, res, emptyParams);
3087
+ return;
3088
+ }
3089
+ for (const route of dynamicRoutes) {
3090
+ if (route.method !== method) continue;
3091
+ const match = route.regex.exec(path);
3092
+ if (!match) continue;
3093
+ const params = {};
3094
+ for (let i = 0; i < route.paramNames.length; i++) {
3095
+ params[route.paramNames[i]] = match[i + 1];
3096
+ }
3097
+ route.handler(req, res, params);
3098
+ return;
3099
+ }
3100
+ fastWrite404(res);
3101
+ };
3102
+ return new Promise((resolve2) => {
3103
+ const server = createServer(dispatch);
3104
+ server.listen(port, () => {
3105
+ const addr = server.address();
3106
+ resolve2({ port: addr.port, server });
3107
+ });
3108
+ });
664
3109
  }
665
3110
  async listen(port) {
666
- const finalPort = port || 3e3;
667
- serve({
668
- fetch: this.app.fetch,
3111
+ const finalPort = port ?? 3e3;
3112
+ const manager = this.shutdownManager;
3113
+ const shutdownFetch = async (req, ...args) => {
3114
+ if (manager.isShuttingDown()) {
3115
+ return new Response(
3116
+ JSON.stringify({
3117
+ type: "about:blank",
3118
+ title: "Service Unavailable",
3119
+ status: 503,
3120
+ detail: "Server is shutting down, please retry later"
3121
+ }),
3122
+ {
3123
+ status: 503,
3124
+ headers: { "Content-Type": "application/problem+json" }
3125
+ }
3126
+ );
3127
+ }
3128
+ const untrack = manager.trackRequest();
3129
+ try {
3130
+ return await this.app.fetch(req, ...args);
3131
+ } finally {
3132
+ untrack();
3133
+ }
3134
+ };
3135
+ const server = serve({
3136
+ fetch: shutdownFetch,
669
3137
  port: finalPort
670
3138
  });
3139
+ manager.setServer(server);
671
3140
  console.log(`\u{1F680} Kozo server listening on http://localhost:${finalPort}`);
672
3141
  }
3142
+ /**
3143
+ * Graceful shutdown — drains in-flight requests before closing.
3144
+ * Use getShutdownManager().setDatabase(db, provider) to register DB cleanup.
3145
+ */
3146
+ async shutdown(options) {
3147
+ return this.shutdownManager.shutdown(options);
3148
+ }
3149
+ getShutdownManager() {
3150
+ return this.shutdownManager;
3151
+ }
673
3152
  getApp() {
674
3153
  return this.app;
675
3154
  }
@@ -681,14 +3160,628 @@ function createKozo(config) {
681
3160
  return new Kozo(config);
682
3161
  }
683
3162
 
3163
+ // src/native-context.ts
3164
+ function buildNativeContext(req, res, params, body, services, serialize) {
3165
+ let _query;
3166
+ let _extraHeaders;
3167
+ const ctx = {
3168
+ req,
3169
+ res,
3170
+ params,
3171
+ body,
3172
+ services,
3173
+ get query() {
3174
+ if (_query === void 0) {
3175
+ const url = req.url ?? "/";
3176
+ const qIdx = url.indexOf("?");
3177
+ if (qIdx === -1) {
3178
+ _query = {};
3179
+ } else {
3180
+ const sp = new URLSearchParams(url.slice(qIdx + 1));
3181
+ _query = {};
3182
+ sp.forEach((v, k) => {
3183
+ _query[k] = v;
3184
+ });
3185
+ }
3186
+ }
3187
+ return _query;
3188
+ },
3189
+ json(data, status) {
3190
+ const jsonBody = serialize ? serialize(data) : JSON.stringify(data);
3191
+ if (_extraHeaders) {
3192
+ const hdrs = ["Content-Type", "application/json", "Content-Length", fastCL(jsonBody.length)];
3193
+ for (const [k, v] of _extraHeaders) hdrs.push(k, v);
3194
+ res.writeHead(status ?? 200, hdrs);
3195
+ res.end(jsonBody);
3196
+ } else if (status !== void 0 && status !== 200) {
3197
+ fastWriteJsonStatus(res, jsonBody, status);
3198
+ } else {
3199
+ fastWriteJson(res, jsonBody);
3200
+ }
3201
+ },
3202
+ text(data, status) {
3203
+ fastWriteText(res, data, status ?? 200);
3204
+ },
3205
+ html(data, status) {
3206
+ fastWriteHtml(res, data, status ?? 200);
3207
+ },
3208
+ header(name, value) {
3209
+ if (!_extraHeaders) _extraHeaders = [];
3210
+ _extraHeaders.push([name, value]);
3211
+ return ctx;
3212
+ },
3213
+ redirect(url, status) {
3214
+ const code = status ?? 302;
3215
+ res.writeHead(code, ["Location", url, "Content-Length", "0"]);
3216
+ res.end();
3217
+ }
3218
+ };
3219
+ return ctx;
3220
+ }
3221
+
684
3222
  // src/index.ts
685
3223
  import { z } from "zod";
3224
+
3225
+ // src/openapi.ts
3226
+ function zodToJsonSchema2(zodSchema) {
3227
+ const schema = zodSchema;
3228
+ const def = schema._def;
3229
+ if (!def) {
3230
+ return { type: "object" };
3231
+ }
3232
+ const typeName = def.typeName;
3233
+ switch (typeName) {
3234
+ case "ZodString": {
3235
+ const result = { type: "string" };
3236
+ for (const check of def.checks || []) {
3237
+ if (check.kind === "min") result.minLength = check.value;
3238
+ if (check.kind === "max") result.maxLength = check.value;
3239
+ if (check.kind === "email") result.format = "email";
3240
+ if (check.kind === "url") result.format = "uri";
3241
+ if (check.kind === "uuid") result.format = "uuid";
3242
+ if (check.kind === "regex") result.pattern = check.regex.source;
3243
+ }
3244
+ return result;
3245
+ }
3246
+ case "ZodNumber": {
3247
+ const result = { type: "number" };
3248
+ for (const check of def.checks || []) {
3249
+ if (check.kind === "min") result.minimum = check.value;
3250
+ if (check.kind === "max") result.maximum = check.value;
3251
+ if (check.kind === "int") result.type = "integer";
3252
+ }
3253
+ return result;
3254
+ }
3255
+ case "ZodBoolean":
3256
+ return { type: "boolean" };
3257
+ case "ZodNull":
3258
+ return { type: "null" };
3259
+ case "ZodArray":
3260
+ return {
3261
+ type: "array",
3262
+ items: zodToJsonSchema2(def.type)
3263
+ };
3264
+ case "ZodObject": {
3265
+ const properties = {};
3266
+ const required = [];
3267
+ const shape = def.shape?.() || def.shape;
3268
+ if (shape) {
3269
+ for (const [key, value] of Object.entries(shape)) {
3270
+ properties[key] = zodToJsonSchema2(value);
3271
+ const fieldDef = value._def;
3272
+ if (fieldDef?.typeName !== "ZodOptional" && fieldDef?.typeName !== "ZodDefault") {
3273
+ required.push(key);
3274
+ }
3275
+ }
3276
+ }
3277
+ return {
3278
+ type: "object",
3279
+ properties,
3280
+ ...required.length > 0 ? { required } : {}
3281
+ };
3282
+ }
3283
+ case "ZodEnum":
3284
+ return {
3285
+ type: "string",
3286
+ enum: def.values
3287
+ };
3288
+ case "ZodNativeEnum":
3289
+ return {
3290
+ type: "string",
3291
+ enum: Object.values(def.values)
3292
+ };
3293
+ case "ZodLiteral":
3294
+ return {
3295
+ type: typeof def.value,
3296
+ enum: [def.value]
3297
+ };
3298
+ case "ZodUnion":
3299
+ return {
3300
+ oneOf: def.options.map((opt) => zodToJsonSchema2(opt))
3301
+ };
3302
+ case "ZodOptional":
3303
+ return {
3304
+ ...zodToJsonSchema2(def.innerType),
3305
+ nullable: true
3306
+ };
3307
+ case "ZodNullable":
3308
+ return {
3309
+ ...zodToJsonSchema2(def.innerType),
3310
+ nullable: true
3311
+ };
3312
+ case "ZodDefault":
3313
+ return {
3314
+ ...zodToJsonSchema2(def.innerType),
3315
+ default: def.defaultValue()
3316
+ };
3317
+ case "ZodRecord":
3318
+ return {
3319
+ type: "object",
3320
+ additionalProperties: zodToJsonSchema2(def.valueType)
3321
+ };
3322
+ case "ZodDate":
3323
+ return { type: "string", format: "date-time" };
3324
+ case "ZodAny":
3325
+ case "ZodUnknown":
3326
+ return {};
3327
+ default:
3328
+ return { type: "object" };
3329
+ }
3330
+ }
3331
+ var OpenAPIGenerator = class {
3332
+ config;
3333
+ schemas = /* @__PURE__ */ new Map();
3334
+ schemaCounter = 0;
3335
+ constructor(config) {
3336
+ this.config = config;
3337
+ }
3338
+ /**
3339
+ * Generate OpenAPI spec from routes
3340
+ */
3341
+ generate(routes) {
3342
+ const paths = {};
3343
+ for (const route of routes) {
3344
+ const openApiPath = this.honoPathToOpenApi(route.path);
3345
+ if (!paths[openApiPath]) {
3346
+ paths[openApiPath] = {};
3347
+ }
3348
+ paths[openApiPath][route.method] = this.routeToOperation(route);
3349
+ }
3350
+ return {
3351
+ openapi: "3.1.0",
3352
+ info: this.config.info,
3353
+ servers: this.config.servers,
3354
+ tags: this.config.tags,
3355
+ paths,
3356
+ components: {
3357
+ schemas: Object.fromEntries(this.schemas),
3358
+ securitySchemes: {
3359
+ bearerAuth: {
3360
+ type: "http",
3361
+ scheme: "bearer",
3362
+ bearerFormat: "JWT"
3363
+ }
3364
+ }
3365
+ },
3366
+ security: this.config.security
3367
+ };
3368
+ }
3369
+ /**
3370
+ * Convert Hono path params to OpenAPI format
3371
+ * :id -> {id}
3372
+ */
3373
+ honoPathToOpenApi(path) {
3374
+ return path.replace(/:([^/]+)/g, "{$1}");
3375
+ }
3376
+ /**
3377
+ * Convert route to OpenAPI operation
3378
+ */
3379
+ routeToOperation(route) {
3380
+ const { path, method, module } = route;
3381
+ const { schema, meta } = module;
3382
+ const operation = {
3383
+ operationId: this.generateOperationId(path, method),
3384
+ summary: meta?.summary || `${method.toUpperCase()} ${path}`,
3385
+ description: meta?.description,
3386
+ tags: meta?.tags || [this.extractTag(path)],
3387
+ parameters: [],
3388
+ responses: {
3389
+ "200": {
3390
+ description: "Successful response",
3391
+ content: {
3392
+ "application/json": {
3393
+ schema: { type: "object" }
3394
+ }
3395
+ }
3396
+ },
3397
+ "400": {
3398
+ description: "Validation error"
3399
+ },
3400
+ "500": {
3401
+ description: "Internal server error"
3402
+ }
3403
+ }
3404
+ };
3405
+ const pathParams = path.match(/:([^/]+)/g);
3406
+ if (pathParams) {
3407
+ for (const param of pathParams) {
3408
+ const paramName = param.slice(1);
3409
+ operation.parameters.push({
3410
+ name: paramName,
3411
+ in: "path",
3412
+ required: true,
3413
+ schema: { type: "string" }
3414
+ });
3415
+ }
3416
+ }
3417
+ if (schema?.query) {
3418
+ const querySchema = zodToJsonSchema2(schema.query);
3419
+ if (querySchema.properties) {
3420
+ for (const [name, propSchema] of Object.entries(querySchema.properties)) {
3421
+ operation.parameters.push({
3422
+ name,
3423
+ in: "query",
3424
+ required: querySchema.required?.includes(name) || false,
3425
+ schema: propSchema
3426
+ });
3427
+ }
3428
+ }
3429
+ }
3430
+ if (schema?.params) {
3431
+ const paramsSchema = zodToJsonSchema2(schema.params);
3432
+ if (paramsSchema.properties) {
3433
+ for (const [name, propSchema] of Object.entries(paramsSchema.properties)) {
3434
+ const existingIdx = operation.parameters.findIndex(
3435
+ (p) => p.name === name && p.in === "path"
3436
+ );
3437
+ if (existingIdx >= 0) {
3438
+ operation.parameters[existingIdx].schema = propSchema;
3439
+ }
3440
+ }
3441
+ }
3442
+ }
3443
+ if (["post", "put", "patch"].includes(method) && schema?.body) {
3444
+ operation.requestBody = {
3445
+ required: true,
3446
+ content: {
3447
+ "application/json": {
3448
+ schema: zodToJsonSchema2(schema.body)
3449
+ }
3450
+ }
3451
+ };
3452
+ }
3453
+ if (schema?.response) {
3454
+ for (const [status, responseSchema] of Object.entries(schema.response)) {
3455
+ operation.responses[status] = {
3456
+ description: this.getStatusDescription(parseInt(status)),
3457
+ content: {
3458
+ "application/json": {
3459
+ schema: zodToJsonSchema2(responseSchema)
3460
+ }
3461
+ }
3462
+ };
3463
+ }
3464
+ }
3465
+ if (meta?.auth) {
3466
+ operation.security = [{ bearerAuth: [] }];
3467
+ }
3468
+ return operation;
3469
+ }
3470
+ /**
3471
+ * Generate operation ID from path and method
3472
+ */
3473
+ generateOperationId(path, method) {
3474
+ const parts = path.split("/").filter(Boolean).map((part) => {
3475
+ if (part.startsWith(":")) {
3476
+ return "By" + this.capitalize(part.slice(1));
3477
+ }
3478
+ return this.capitalize(part);
3479
+ });
3480
+ return method + parts.join("");
3481
+ }
3482
+ /**
3483
+ * Extract tag from path (first segment)
3484
+ */
3485
+ extractTag(path) {
3486
+ const firstSegment = path.split("/").filter(Boolean)[0];
3487
+ return firstSegment ? this.capitalize(firstSegment) : "Default";
3488
+ }
3489
+ /**
3490
+ * Get HTTP status description
3491
+ */
3492
+ getStatusDescription(status) {
3493
+ const descriptions = {
3494
+ 200: "OK",
3495
+ 201: "Created",
3496
+ 204: "No Content",
3497
+ 400: "Bad Request",
3498
+ 401: "Unauthorized",
3499
+ 403: "Forbidden",
3500
+ 404: "Not Found",
3501
+ 500: "Internal Server Error"
3502
+ };
3503
+ return descriptions[status] || "Response";
3504
+ }
3505
+ capitalize(str) {
3506
+ return str.charAt(0).toUpperCase() + str.slice(1);
3507
+ }
3508
+ };
3509
+ function generateSwaggerHtml(specUrl, title = "API Documentation") {
3510
+ return `<!DOCTYPE html>
3511
+ <html lang="en">
3512
+ <head>
3513
+ <meta charset="utf-8" />
3514
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
3515
+ <title>${title}</title>
3516
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui.css" />
3517
+ <style>
3518
+ body { margin: 0; padding: 0; }
3519
+ .swagger-ui .topbar { display: none; }
3520
+ </style>
3521
+ </head>
3522
+ <body>
3523
+ <div id="swagger-ui"></div>
3524
+ <script src="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui-bundle.js" crossorigin></script>
3525
+ <script>
3526
+ window.onload = () => {
3527
+ window.ui = SwaggerUIBundle({
3528
+ url: '${specUrl}',
3529
+ dom_id: '#swagger-ui',
3530
+ deepLinking: true,
3531
+ presets: [
3532
+ SwaggerUIBundle.presets.apis,
3533
+ SwaggerUIBundle.SwaggerUIStandalonePreset
3534
+ ],
3535
+ layout: "BaseLayout",
3536
+ persistAuthorization: true
3537
+ });
3538
+ };
3539
+ </script>
3540
+ </body>
3541
+ </html>`;
3542
+ }
3543
+ function createOpenAPIGenerator(config) {
3544
+ return new OpenAPIGenerator(config);
3545
+ }
3546
+
3547
+ // src/middleware/logger.ts
3548
+ function logger(options = {}) {
3549
+ const { prefix = "\u{1F310}", colorize = true } = options;
3550
+ return async (c, next) => {
3551
+ const start = Date.now();
3552
+ const method = c.req.method;
3553
+ const path = new URL(c.req.url).pathname;
3554
+ await next();
3555
+ const duration = Date.now() - start;
3556
+ const status = c.res.status;
3557
+ const statusColor = status >= 500 ? "\u{1F534}" : status >= 400 ? "\u{1F7E1}" : "\u{1F7E2}";
3558
+ const log = `${prefix} ${method.padEnd(6)} ${path} ${statusColor} ${status} ${duration}ms`;
3559
+ console.log(log);
3560
+ };
3561
+ }
3562
+
3563
+ // src/middleware/cors.ts
3564
+ import { cors as honoCors } from "hono/cors";
3565
+ function cors(options = {}) {
3566
+ return honoCors({
3567
+ origin: options.origin || "*",
3568
+ allowMethods: options.allowMethods || ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
3569
+ allowHeaders: options.allowHeaders || ["Content-Type", "Authorization"],
3570
+ exposeHeaders: options.exposeHeaders || [],
3571
+ maxAge: options.maxAge || 86400,
3572
+ credentials: options.credentials || false
3573
+ });
3574
+ }
3575
+
3576
+ // src/middleware/rate-limit.ts
3577
+ var store = /* @__PURE__ */ new Map();
3578
+ function rateLimit(options) {
3579
+ const {
3580
+ max = 100,
3581
+ window = 60,
3582
+ keyGenerator = (c) => c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip") ?? "anonymous",
3583
+ message = "Too many requests"
3584
+ } = options;
3585
+ return async (c, next) => {
3586
+ const key = keyGenerator(c);
3587
+ const now = Date.now();
3588
+ const windowMs = window * 1e3;
3589
+ let record = store.get(key);
3590
+ if (!record || now > record.resetAt) {
3591
+ record = { count: 0, resetAt: now + windowMs };
3592
+ }
3593
+ record.count++;
3594
+ store.set(key, record);
3595
+ c.header("X-RateLimit-Limit", String(max));
3596
+ c.header("X-RateLimit-Remaining", String(Math.max(0, max - record.count)));
3597
+ c.header("X-RateLimit-Reset", String(Math.ceil(record.resetAt / 1e3)));
3598
+ if (record.count > max) {
3599
+ return c.json({ error: message }, 429);
3600
+ }
3601
+ await next();
3602
+ };
3603
+ }
3604
+ function clearRateLimitStore() {
3605
+ store.clear();
3606
+ }
3607
+
3608
+ // src/middleware/error-handler.ts
3609
+ var HttpError = class extends Error {
3610
+ constructor(statusCode, message, details) {
3611
+ super(message);
3612
+ this.statusCode = statusCode;
3613
+ this.details = details;
3614
+ this.name = "HttpError";
3615
+ }
3616
+ };
3617
+ var BadRequestError = class extends HttpError {
3618
+ constructor(message = "Bad Request", details) {
3619
+ super(400, message, details);
3620
+ }
3621
+ };
3622
+ var UnauthorizedError2 = class extends HttpError {
3623
+ constructor(message = "Unauthorized") {
3624
+ super(401, message);
3625
+ }
3626
+ };
3627
+ var ForbiddenError2 = class extends HttpError {
3628
+ constructor(message = "Forbidden") {
3629
+ super(403, message);
3630
+ }
3631
+ };
3632
+ var NotFoundError2 = class extends HttpError {
3633
+ constructor(message = "Not Found") {
3634
+ super(404, message);
3635
+ }
3636
+ };
3637
+ var ConflictError = class extends HttpError {
3638
+ constructor(message = "Conflict", details) {
3639
+ super(409, message, details);
3640
+ }
3641
+ };
3642
+ var InternalServerError = class extends HttpError {
3643
+ constructor(message = "Internal Server Error") {
3644
+ super(500, message);
3645
+ }
3646
+ };
3647
+ function errorHandler() {
3648
+ return async (c, next) => {
3649
+ try {
3650
+ await next();
3651
+ } catch (err) {
3652
+ if (err instanceof HttpError) {
3653
+ return c.json({
3654
+ error: err.message,
3655
+ status: err.statusCode,
3656
+ ...err.details ? { details: err.details } : {}
3657
+ }, err.statusCode);
3658
+ }
3659
+ console.error("Unhandled error:", err);
3660
+ return c.json({
3661
+ error: "Internal Server Error",
3662
+ status: 500
3663
+ }, 500);
3664
+ }
3665
+ };
3666
+ }
3667
+
3668
+ // src/middleware/fileSystemRouting.ts
3669
+ import { readFile as readFile2 } from "fs/promises";
3670
+ import { resolve } from "path";
3671
+ import { pathToFileURL as pathToFileURL2 } from "url";
3672
+ async function readManifest(manifestPath, onMissing) {
3673
+ try {
3674
+ const raw = await readFile2(manifestPath, "utf-8");
3675
+ return JSON.parse(raw);
3676
+ } catch (err) {
3677
+ onMissing(err instanceof Error ? err : new Error(String(err)));
3678
+ return null;
3679
+ }
3680
+ }
3681
+ async function importHandler(handlerPath) {
3682
+ try {
3683
+ const url = handlerPath.startsWith("file://") ? handlerPath : pathToFileURL2(handlerPath).href;
3684
+ const mod = await import(url);
3685
+ if (typeof mod.default !== "function") {
3686
+ console.warn(
3687
+ `[kozo:fsr] Skipping ${handlerPath}: no default export function`
3688
+ );
3689
+ return null;
3690
+ }
3691
+ return mod.default;
3692
+ } catch (err) {
3693
+ console.warn(
3694
+ `[kozo:fsr] Failed to import handler ${handlerPath}:`,
3695
+ err.message
3696
+ );
3697
+ return null;
3698
+ }
3699
+ }
3700
+ async function applyFileSystemRouting(app, options = {}) {
3701
+ const {
3702
+ manifestPath = resolve(process.cwd(), "routes-manifest.json"),
3703
+ verbose = false,
3704
+ onMissingManifest = () => {
3705
+ },
3706
+ logger: logger2 = console.log
3707
+ } = options;
3708
+ const manifest = await readManifest(manifestPath, onMissingManifest);
3709
+ if (!manifest) return;
3710
+ const log = logger2;
3711
+ if (verbose) {
3712
+ log(
3713
+ `
3714
+ \u{1F4CB} [kozo:fsr] Loading ${manifest.routes.length} route(s) from manifest
3715
+ `
3716
+ );
3717
+ }
3718
+ for (const route of manifest.routes) {
3719
+ const handler = await importHandler(route.handler);
3720
+ if (!handler) continue;
3721
+ app[route.method](route.path, handler);
3722
+ if (verbose) {
3723
+ log(
3724
+ ` ${route.method.toUpperCase().padEnd(6)} ${route.path} \u2192 ${route.handler}`
3725
+ );
3726
+ }
3727
+ }
3728
+ if (verbose) {
3729
+ log("");
3730
+ }
3731
+ }
3732
+ function createFileSystemRouting(options = {}) {
3733
+ return (app) => applyFileSystemRouting(app, options);
3734
+ }
686
3735
  export {
3736
+ ERROR_RESPONSES,
3737
+ ForbiddenError,
3738
+ BadRequestError as HttpBadRequestError,
3739
+ ConflictError as HttpConflictError,
3740
+ HttpError,
3741
+ ForbiddenError2 as HttpForbiddenError,
3742
+ InternalServerError as HttpInternalServerError,
3743
+ NotFoundError2 as HttpNotFoundError,
3744
+ UnauthorizedError2 as HttpUnauthorizedError,
687
3745
  Kozo,
3746
+ KozoError,
3747
+ NotFoundError,
3748
+ OpenAPIGenerator,
688
3749
  SchemaCompiler,
3750
+ ShutdownManager,
3751
+ UnauthorizedError,
3752
+ ValidationFailedError,
3753
+ applyFileSystemRouting,
3754
+ buildNativeContext,
3755
+ clearRateLimitStore,
689
3756
  compileRouteHandler,
3757
+ cors,
3758
+ createFileSystemRouting,
3759
+ createInflightTracker,
690
3760
  createKozo,
3761
+ createOpenAPIGenerator,
3762
+ createShutdownManager,
3763
+ errorHandler,
3764
+ fastCL,
3765
+ fastWrite400,
3766
+ fastWrite404,
3767
+ fastWrite500,
3768
+ fastWriteError,
3769
+ fastWriteHtml,
3770
+ fastWriteJson,
3771
+ fastWriteJsonStatus,
3772
+ fastWriteText,
3773
+ forbiddenResponse,
3774
+ formatAjvErrors,
3775
+ formatZodErrors,
3776
+ generateSwaggerHtml,
691
3777
  generateTypedClient,
3778
+ internalErrorResponse,
3779
+ logger,
3780
+ notFoundResponse,
3781
+ rateLimit,
3782
+ trackRequest,
3783
+ unauthorizedResponse,
3784
+ validationErrorResponse,
692
3785
  z
693
3786
  };
694
3787
  //# sourceMappingURL=index.js.map