@kozojs/core 0.3.1 → 0.3.3

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,20 +1,3 @@
1
- import {
2
- BadRequestError,
3
- ConflictError,
4
- ForbiddenError,
5
- HttpError,
6
- InternalServerError,
7
- NotFoundError,
8
- UnauthorizedError,
9
- applyFileSystemRouting,
10
- clearRateLimitStore,
11
- cors,
12
- createFileSystemRouting,
13
- errorHandler,
14
- logger,
15
- rateLimit
16
- } from "./chunk-W44TTZNJ.js";
17
-
18
1
  // src/app.ts
19
2
  import { Hono } from "hono/quick";
20
3
  import { serve } from "@hono/node-server";
@@ -404,19 +387,19 @@ var ValidationFailedError = class extends KozoError {
404
387
  return new Response(JSON.stringify(body), INIT_400);
405
388
  }
406
389
  };
407
- var NotFoundError2 = class extends KozoError {
390
+ var NotFoundError = class extends KozoError {
408
391
  constructor(message = "Resource Not Found") {
409
392
  super(message, 404, "not-found");
410
393
  this.name = "NotFoundError";
411
394
  }
412
395
  };
413
- var UnauthorizedError2 = class extends KozoError {
396
+ var UnauthorizedError = class extends KozoError {
414
397
  constructor(message = "Unauthorized") {
415
398
  super(message, 401, "unauthorized");
416
399
  this.name = "UnauthorizedError";
417
400
  }
418
401
  };
419
- var ForbiddenError2 = class extends KozoError {
402
+ var ForbiddenError = class extends KozoError {
420
403
  constructor(message = "Forbidden") {
421
404
  super(message, 403, "forbidden");
422
405
  this.name = "ForbiddenError";
@@ -517,11 +500,11 @@ function uwsWrite404(uwsRes) {
517
500
  });
518
501
  }
519
502
  function getFreePort() {
520
- return new Promise((resolve, reject) => {
503
+ return new Promise((resolve2, reject) => {
521
504
  const srv = netCreateServer();
522
505
  srv.listen(0, "0.0.0.0", () => {
523
506
  const port = srv.address().port;
524
- srv.close((err) => err ? reject(err) : resolve(port));
507
+ srv.close((err) => err ? reject(err) : resolve2(port));
525
508
  });
526
509
  });
527
510
  }
@@ -538,7 +521,7 @@ async function createUwsServer(opts) {
538
521
  const { uws, routes } = opts;
539
522
  const port = opts.port === 0 ? await getFreePort() : opts.port;
540
523
  const emptyParams = Object.freeze({});
541
- return new Promise((resolve, reject) => {
524
+ return new Promise((resolve2, reject) => {
542
525
  const uwsApp = uws.App();
543
526
  for (const route of routes) {
544
527
  const fn = UWS_METHOD[route.method];
@@ -606,7 +589,7 @@ async function createUwsServer(opts) {
606
589
  return;
607
590
  }
608
591
  listenToken = token;
609
- resolve({
592
+ resolve2({
610
593
  port,
611
594
  server: {
612
595
  close() {
@@ -620,8 +603,6 @@ async function createUwsServer(opts) {
620
603
 
621
604
  // src/compiler.ts
622
605
  import { zodToJsonSchema } from "zod-to-json-schema";
623
- import Ajv from "ajv";
624
- import addFormats from "ajv-formats";
625
606
  import fastJson from "fast-json-stringify";
626
607
 
627
608
  // src/fast-response.ts
@@ -744,12 +725,28 @@ function fastWriteError(err, res) {
744
725
  }
745
726
 
746
727
  // src/compiler.ts
747
- var ajv = new Ajv({
748
- removeAdditional: "all",
749
- useDefaults: true,
750
- coerceTypes: true
751
- });
752
- addFormats(ajv);
728
+ function makeZValidator(schema) {
729
+ const fn = function(data) {
730
+ const r = schema.safeParse(data);
731
+ fn.errors = null;
732
+ if (r.success) {
733
+ if (data !== null && typeof data === "object" && !Array.isArray(data)) {
734
+ const d = data;
735
+ const rd = r.data;
736
+ for (const k of Object.keys(d)) if (!(k in rd)) delete d[k];
737
+ Object.assign(d, rd);
738
+ }
739
+ return true;
740
+ }
741
+ fn.errors = r.error.issues.map((i) => ({
742
+ instancePath: i.path.length ? "/" + i.path.join("/") : "",
743
+ message: i.message
744
+ }));
745
+ return false;
746
+ };
747
+ fn.errors = null;
748
+ return fn;
749
+ }
753
750
  function isZodSchema(schema) {
754
751
  return typeof schema === "object" && schema !== null && "safeParse" in schema;
755
752
  }
@@ -768,17 +765,14 @@ function toJsonBody(result) {
768
765
  var SchemaCompiler = class {
769
766
  static compile(schema) {
770
767
  const compiled = {};
771
- if (schema.body) {
772
- const jsonSchema = isZodSchema(schema.body) ? zodToJsonSchema(schema.body, ZOD_OPTS) : schema.body;
773
- compiled.validateBody = ajv.compile(jsonSchema);
768
+ if (schema.body && isZodSchema(schema.body)) {
769
+ compiled.validateBody = makeZValidator(schema.body);
774
770
  }
775
- if (schema.query) {
776
- const jsonSchema = isZodSchema(schema.query) ? zodToJsonSchema(schema.query, ZOD_OPTS) : schema.query;
777
- compiled.validateQuery = ajv.compile(jsonSchema);
771
+ if (schema.query && isZodSchema(schema.query)) {
772
+ compiled.validateQuery = makeZValidator(schema.query);
778
773
  }
779
- if (schema.params) {
780
- const jsonSchema = isZodSchema(schema.params) ? zodToJsonSchema(schema.params, ZOD_OPTS) : schema.params;
781
- compiled.validateParams = ajv.compile(jsonSchema);
774
+ if (schema.params && isZodSchema(schema.params)) {
775
+ compiled.validateParams = makeZValidator(schema.params);
782
776
  }
783
777
  if (schema.response) {
784
778
  if (typeof schema.response === "object" && !isZodSchema(schema.response)) {
@@ -1875,8 +1869,8 @@ function createInflightTracker() {
1875
1869
  function trackRequest(tracker) {
1876
1870
  tracker.count++;
1877
1871
  let resolvePromise;
1878
- const promise = new Promise((resolve) => {
1879
- resolvePromise = resolve;
1872
+ const promise = new Promise((resolve2) => {
1873
+ resolvePromise = resolve2;
1880
1874
  });
1881
1875
  tracker.requests.add(promise);
1882
1876
  return () => {
@@ -1971,10 +1965,10 @@ var ShutdownManager = class {
1971
1965
  });
1972
1966
  }
1973
1967
  const drainPromise = this.drainRequests();
1974
- const timeoutPromise = new Promise((resolve) => {
1968
+ const timeoutPromise = new Promise((resolve2) => {
1975
1969
  setTimeout(() => {
1976
1970
  onShutdownTimeout?.(this.tracker.count);
1977
- resolve();
1971
+ resolve2();
1978
1972
  }, timeoutMs);
1979
1973
  });
1980
1974
  await Promise.race([drainPromise, timeoutPromise]);
@@ -2715,23 +2709,212 @@ function generateSwaggerHtml(specUrl, title = "API Documentation") {
2715
2709
  function createOpenAPIGenerator(config) {
2716
2710
  return new OpenAPIGenerator(config);
2717
2711
  }
2712
+
2713
+ // src/middleware/logger.ts
2714
+ function logger(options = {}) {
2715
+ const { prefix = "\u{1F310}", colorize = true } = options;
2716
+ return async (c, next) => {
2717
+ const start = Date.now();
2718
+ const method = c.req.method;
2719
+ const path = new URL(c.req.url).pathname;
2720
+ await next();
2721
+ const duration = Date.now() - start;
2722
+ const status = c.res.status;
2723
+ const statusColor = status >= 500 ? "\u{1F534}" : status >= 400 ? "\u{1F7E1}" : "\u{1F7E2}";
2724
+ const log = `${prefix} ${method.padEnd(6)} ${path} ${statusColor} ${status} ${duration}ms`;
2725
+ console.log(log);
2726
+ };
2727
+ }
2728
+
2729
+ // src/middleware/cors.ts
2730
+ import { cors as honoCors } from "hono/cors";
2731
+ function cors(options = {}) {
2732
+ return honoCors({
2733
+ origin: options.origin || "*",
2734
+ allowMethods: options.allowMethods || ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
2735
+ allowHeaders: options.allowHeaders || ["Content-Type", "Authorization"],
2736
+ exposeHeaders: options.exposeHeaders || [],
2737
+ maxAge: options.maxAge || 86400,
2738
+ credentials: options.credentials || false
2739
+ });
2740
+ }
2741
+
2742
+ // src/middleware/rate-limit.ts
2743
+ var store = /* @__PURE__ */ new Map();
2744
+ function rateLimit(options) {
2745
+ const {
2746
+ max = 100,
2747
+ window = 60,
2748
+ keyGenerator = (c) => c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip") ?? "anonymous",
2749
+ message = "Too many requests"
2750
+ } = options;
2751
+ return async (c, next) => {
2752
+ const key = keyGenerator(c);
2753
+ const now = Date.now();
2754
+ const windowMs = window * 1e3;
2755
+ let record = store.get(key);
2756
+ if (!record || now > record.resetAt) {
2757
+ record = { count: 0, resetAt: now + windowMs };
2758
+ }
2759
+ record.count++;
2760
+ store.set(key, record);
2761
+ c.header("X-RateLimit-Limit", String(max));
2762
+ c.header("X-RateLimit-Remaining", String(Math.max(0, max - record.count)));
2763
+ c.header("X-RateLimit-Reset", String(Math.ceil(record.resetAt / 1e3)));
2764
+ if (record.count > max) {
2765
+ return c.json({ error: message }, 429);
2766
+ }
2767
+ await next();
2768
+ };
2769
+ }
2770
+ function clearRateLimitStore() {
2771
+ store.clear();
2772
+ }
2773
+
2774
+ // src/middleware/error-handler.ts
2775
+ var HttpError = class extends Error {
2776
+ constructor(statusCode, message, details) {
2777
+ super(message);
2778
+ this.statusCode = statusCode;
2779
+ this.details = details;
2780
+ this.name = "HttpError";
2781
+ }
2782
+ };
2783
+ var BadRequestError = class extends HttpError {
2784
+ constructor(message = "Bad Request", details) {
2785
+ super(400, message, details);
2786
+ }
2787
+ };
2788
+ var UnauthorizedError2 = class extends HttpError {
2789
+ constructor(message = "Unauthorized") {
2790
+ super(401, message);
2791
+ }
2792
+ };
2793
+ var ForbiddenError2 = class extends HttpError {
2794
+ constructor(message = "Forbidden") {
2795
+ super(403, message);
2796
+ }
2797
+ };
2798
+ var NotFoundError2 = class extends HttpError {
2799
+ constructor(message = "Not Found") {
2800
+ super(404, message);
2801
+ }
2802
+ };
2803
+ var ConflictError = class extends HttpError {
2804
+ constructor(message = "Conflict", details) {
2805
+ super(409, message, details);
2806
+ }
2807
+ };
2808
+ var InternalServerError = class extends HttpError {
2809
+ constructor(message = "Internal Server Error") {
2810
+ super(500, message);
2811
+ }
2812
+ };
2813
+ function errorHandler() {
2814
+ return async (c, next) => {
2815
+ try {
2816
+ await next();
2817
+ } catch (err) {
2818
+ if (err instanceof HttpError) {
2819
+ return c.json({
2820
+ error: err.message,
2821
+ status: err.statusCode,
2822
+ ...err.details ? { details: err.details } : {}
2823
+ }, err.statusCode);
2824
+ }
2825
+ console.error("Unhandled error:", err);
2826
+ return c.json({
2827
+ error: "Internal Server Error",
2828
+ status: 500
2829
+ }, 500);
2830
+ }
2831
+ };
2832
+ }
2833
+
2834
+ // src/middleware/fileSystemRouting.ts
2835
+ import { readFile } from "fs/promises";
2836
+ import { resolve } from "path";
2837
+ import { pathToFileURL as pathToFileURL2 } from "url";
2838
+ async function readManifest(manifestPath, onMissing) {
2839
+ try {
2840
+ const raw = await readFile(manifestPath, "utf-8");
2841
+ return JSON.parse(raw);
2842
+ } catch (err) {
2843
+ onMissing(err instanceof Error ? err : new Error(String(err)));
2844
+ return null;
2845
+ }
2846
+ }
2847
+ async function importHandler(handlerPath) {
2848
+ try {
2849
+ const url = handlerPath.startsWith("file://") ? handlerPath : pathToFileURL2(handlerPath).href;
2850
+ const mod = await import(url);
2851
+ if (typeof mod.default !== "function") {
2852
+ console.warn(
2853
+ `[kozo:fsr] Skipping ${handlerPath}: no default export function`
2854
+ );
2855
+ return null;
2856
+ }
2857
+ return mod.default;
2858
+ } catch (err) {
2859
+ console.warn(
2860
+ `[kozo:fsr] Failed to import handler ${handlerPath}:`,
2861
+ err.message
2862
+ );
2863
+ return null;
2864
+ }
2865
+ }
2866
+ async function applyFileSystemRouting(app, options = {}) {
2867
+ const {
2868
+ manifestPath = resolve(process.cwd(), "routes-manifest.json"),
2869
+ verbose = false,
2870
+ onMissingManifest = () => {
2871
+ },
2872
+ logger: logger2 = console.log
2873
+ } = options;
2874
+ const manifest = await readManifest(manifestPath, onMissingManifest);
2875
+ if (!manifest) return;
2876
+ const log = logger2;
2877
+ if (verbose) {
2878
+ log(
2879
+ `
2880
+ \u{1F4CB} [kozo:fsr] Loading ${manifest.routes.length} route(s) from manifest
2881
+ `
2882
+ );
2883
+ }
2884
+ for (const route of manifest.routes) {
2885
+ const handler = await importHandler(route.handler);
2886
+ if (!handler) continue;
2887
+ app[route.method](route.path, handler);
2888
+ if (verbose) {
2889
+ log(
2890
+ ` ${route.method.toUpperCase().padEnd(6)} ${route.path} \u2192 ${route.handler}`
2891
+ );
2892
+ }
2893
+ }
2894
+ if (verbose) {
2895
+ log("");
2896
+ }
2897
+ }
2898
+ function createFileSystemRouting(options = {}) {
2899
+ return (app) => applyFileSystemRouting(app, options);
2900
+ }
2718
2901
  export {
2719
2902
  ERROR_RESPONSES,
2720
- ForbiddenError2 as ForbiddenError,
2903
+ ForbiddenError,
2721
2904
  BadRequestError as HttpBadRequestError,
2722
2905
  ConflictError as HttpConflictError,
2723
2906
  HttpError,
2724
- ForbiddenError as HttpForbiddenError,
2907
+ ForbiddenError2 as HttpForbiddenError,
2725
2908
  InternalServerError as HttpInternalServerError,
2726
- NotFoundError as HttpNotFoundError,
2727
- UnauthorizedError as HttpUnauthorizedError,
2909
+ NotFoundError2 as HttpNotFoundError,
2910
+ UnauthorizedError2 as HttpUnauthorizedError,
2728
2911
  Kozo,
2729
2912
  KozoError,
2730
- NotFoundError2 as NotFoundError,
2913
+ NotFoundError,
2731
2914
  OpenAPIGenerator,
2732
2915
  SchemaCompiler,
2733
2916
  ShutdownManager,
2734
- UnauthorizedError2 as UnauthorizedError,
2917
+ UnauthorizedError,
2735
2918
  ValidationFailedError,
2736
2919
  applyFileSystemRouting,
2737
2920
  buildNativeContext,
@@ -2767,3 +2950,4 @@ export {
2767
2950
  validationErrorResponse,
2768
2951
  z
2769
2952
  };
2953
+ //# sourceMappingURL=index.js.map