@daloyjs/core 0.1.0

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.
Files changed (74) hide show
  1. package/README.md +296 -0
  2. package/dist/adapters/bun.d.ts +16 -0
  3. package/dist/adapters/bun.d.ts.map +1 -0
  4. package/dist/adapters/bun.js +25 -0
  5. package/dist/adapters/bun.js.map +1 -0
  6. package/dist/adapters/cloudflare.d.ts +11 -0
  7. package/dist/adapters/cloudflare.d.ts.map +1 -0
  8. package/dist/adapters/cloudflare.js +6 -0
  9. package/dist/adapters/cloudflare.js.map +1 -0
  10. package/dist/adapters/deno.d.ts +12 -0
  11. package/dist/adapters/deno.d.ts.map +1 -0
  12. package/dist/adapters/deno.js +13 -0
  13. package/dist/adapters/deno.js.map +1 -0
  14. package/dist/adapters/node.d.ts +25 -0
  15. package/dist/adapters/node.d.ts.map +1 -0
  16. package/dist/adapters/node.js +90 -0
  17. package/dist/adapters/node.js.map +1 -0
  18. package/dist/adapters/vercel.d.ts +13 -0
  19. package/dist/adapters/vercel.d.ts.map +1 -0
  20. package/dist/adapters/vercel.js +4 -0
  21. package/dist/adapters/vercel.js.map +1 -0
  22. package/dist/app.d.ts +104 -0
  23. package/dist/app.d.ts.map +1 -0
  24. package/dist/app.js +487 -0
  25. package/dist/app.js.map +1 -0
  26. package/dist/client.d.ts +40 -0
  27. package/dist/client.d.ts.map +1 -0
  28. package/dist/client.js +61 -0
  29. package/dist/client.js.map +1 -0
  30. package/dist/contract.d.ts +31 -0
  31. package/dist/contract.d.ts.map +1 -0
  32. package/dist/contract.js +76 -0
  33. package/dist/contract.js.map +1 -0
  34. package/dist/docs.d.ts +16 -0
  35. package/dist/docs.d.ts.map +1 -0
  36. package/dist/docs.js +51 -0
  37. package/dist/docs.js.map +1 -0
  38. package/dist/errors.d.ts +75 -0
  39. package/dist/errors.d.ts.map +1 -0
  40. package/dist/errors.js +155 -0
  41. package/dist/errors.js.map +1 -0
  42. package/dist/index.d.ts +13 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +7 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/logger.d.ts +26 -0
  47. package/dist/logger.d.ts.map +1 -0
  48. package/dist/logger.js +74 -0
  49. package/dist/logger.js.map +1 -0
  50. package/dist/middleware.d.ts +62 -0
  51. package/dist/middleware.d.ts.map +1 -0
  52. package/dist/middleware.js +201 -0
  53. package/dist/middleware.js.map +1 -0
  54. package/dist/openapi.d.ts +28 -0
  55. package/dist/openapi.d.ts.map +1 -0
  56. package/dist/openapi.js +178 -0
  57. package/dist/openapi.js.map +1 -0
  58. package/dist/router.d.ts +30 -0
  59. package/dist/router.d.ts.map +1 -0
  60. package/dist/router.js +135 -0
  61. package/dist/router.js.map +1 -0
  62. package/dist/schema.d.ts +45 -0
  63. package/dist/schema.d.ts.map +1 -0
  64. package/dist/schema.js +13 -0
  65. package/dist/schema.js.map +1 -0
  66. package/dist/security.d.ts +21 -0
  67. package/dist/security.d.ts.map +1 -0
  68. package/dist/security.js +107 -0
  69. package/dist/security.js.map +1 -0
  70. package/dist/types.d.ts +98 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +2 -0
  73. package/dist/types.js.map +1 -0
  74. package/package.json +108 -0
package/dist/logger.js ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Pluggable logger interface.
3
+ *
4
+ * The default logger is a tiny structured JSON logger writing to stdout.
5
+ * Plug pino / winston / your own by implementing `Logger`.
6
+ */
7
+ const LEVELS = {
8
+ trace: 10,
9
+ debug: 20,
10
+ info: 30,
11
+ warn: 40,
12
+ error: 50,
13
+ fatal: 60,
14
+ };
15
+ export function createLogger(opts = {}) {
16
+ const level = opts.level ?? "info";
17
+ const threshold = LEVELS[level];
18
+ const bindings = opts.bindings ?? {};
19
+ const write = opts.write ??
20
+ (typeof process !== "undefined" && process.stdout?.write
21
+ ? (line) => {
22
+ process.stdout.write(line + "\n");
23
+ }
24
+ : (line) => console.log(line));
25
+ function emit(lvl, obj, msg) {
26
+ if (LEVELS[lvl] < threshold)
27
+ return;
28
+ const base = {
29
+ level: lvl,
30
+ time: new Date().toISOString(),
31
+ ...bindings,
32
+ };
33
+ if (typeof obj === "string") {
34
+ base.msg = obj;
35
+ }
36
+ else {
37
+ Object.assign(base, obj);
38
+ if (msg !== undefined)
39
+ base.msg = msg;
40
+ }
41
+ try {
42
+ write(JSON.stringify(base));
43
+ }
44
+ catch {
45
+ write(`{"level":"${lvl}","time":"${base.time}","msg":"<unserializable log>"}`);
46
+ }
47
+ }
48
+ const logger = {
49
+ level,
50
+ trace: (o, m) => emit("trace", o, m),
51
+ debug: (o, m) => emit("debug", o, m),
52
+ info: (o, m) => emit("info", o, m),
53
+ warn: (o, m) => emit("warn", o, m),
54
+ error: (o, m) => emit("error", o, m),
55
+ fatal: (o, m) => emit("fatal", o, m),
56
+ child(extra) {
57
+ return createLogger({ level, bindings: { ...bindings, ...extra }, write });
58
+ },
59
+ };
60
+ return logger;
61
+ }
62
+ export const noopLogger = {
63
+ level: "fatal",
64
+ trace() { },
65
+ debug() { },
66
+ info() { },
67
+ warn() { },
68
+ error() { },
69
+ fatal() { },
70
+ child() {
71
+ return noopLogger;
72
+ },
73
+ };
74
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Built-in security & operational middleware.
3
+ *
4
+ * All middlewares return `Hooks` objects so they compose with `app.use(...)`,
5
+ * groups, and per-route hooks identically.
6
+ */
7
+ import type { Hooks, BaseContext } from "./types.js";
8
+ import { timingSafeEqual } from "./security.js";
9
+ export interface RequestIdOptions {
10
+ header?: string;
11
+ /** Trust an incoming header value (e.g. from a proxy). Default: false. */
12
+ trustIncoming?: boolean;
13
+ generator?: () => string;
14
+ }
15
+ export declare function requestId(opts?: RequestIdOptions): Hooks;
16
+ export interface SecureHeadersOptions {
17
+ contentSecurityPolicy?: string | false;
18
+ hsts?: {
19
+ maxAgeSeconds: number;
20
+ includeSubDomains?: boolean;
21
+ preload?: boolean;
22
+ } | false;
23
+ frameOptions?: "DENY" | "SAMEORIGIN" | false;
24
+ referrerPolicy?: string | false;
25
+ permissionsPolicy?: string | false;
26
+ crossOriginOpenerPolicy?: string | false;
27
+ crossOriginResourcePolicy?: string | false;
28
+ noSniff?: boolean;
29
+ xssProtection?: boolean;
30
+ }
31
+ export declare function secureHeaders(opts?: SecureHeadersOptions): Hooks;
32
+ export interface CorsOptions {
33
+ origin: string | string[] | ((origin: string) => boolean);
34
+ methods?: string[];
35
+ allowedHeaders?: string[];
36
+ exposedHeaders?: string[];
37
+ credentials?: boolean;
38
+ maxAgeSeconds?: number;
39
+ }
40
+ export declare function cors(opts: CorsOptions): Hooks;
41
+ export interface RateLimitStore {
42
+ hit(key: string, windowMs: number): Promise<{
43
+ count: number;
44
+ resetMs: number;
45
+ }>;
46
+ }
47
+ export interface RateLimitOptions {
48
+ windowMs: number;
49
+ max: number;
50
+ keyGenerator?: (ctx: BaseContext<any, any>) => string;
51
+ store?: RateLimitStore;
52
+ /** When true, set Retry-After header on 429. Default: true. */
53
+ retryAfter?: boolean;
54
+ }
55
+ export declare function rateLimit(opts: RateLimitOptions): Hooks;
56
+ export declare function timing(headerName?: string): Hooks;
57
+ export declare function bearerAuth(opts: {
58
+ validate: (token: string) => boolean | Promise<boolean>;
59
+ realm?: string;
60
+ }): Hooks;
61
+ export { timingSafeEqual };
62
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAY,eAAe,EAAE,MAAM,eAAe,CAAC;AAI1D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,KAAK,CAgB5D;AAID,MAAM,WAAW,oBAAoB;IACnC,qBAAqB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACvC,IAAI,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,KAAK,CAAC;IACzF,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,KAAK,CAAC;IAC7C,cAAc,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACnC,uBAAuB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACzC,yBAAyB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,KAAK,CAsCpE;AAID,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC1D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,KAAK,CA0C7C;AAID,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjF;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,MAAM,CAAC;IACtD,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAqBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,KAAK,CAwBvD;AAID,wBAAgB,MAAM,CAAC,UAAU,SAAkB,GAAG,KAAK,CAe1D;AAID,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,KAAK,CA0BR;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Built-in security & operational middleware.
3
+ *
4
+ * All middlewares return `Hooks` objects so they compose with `app.use(...)`,
5
+ * groups, and per-route hooks identically.
6
+ */
7
+ import { TooManyRequestsError, ForbiddenError } from "./errors.js";
8
+ import { randomId, timingSafeEqual } from "./security.js";
9
+ export function requestId(opts = {}) {
10
+ const header = (opts.header ?? "x-request-id").toLowerCase();
11
+ const gen = opts.generator ?? randomId;
12
+ return {
13
+ beforeHandle(ctx) {
14
+ const incoming = opts.trustIncoming ? ctx.request.headers.get(header) : null;
15
+ const id = incoming && /^[A-Za-z0-9._-]{1,200}$/.test(incoming) ? incoming : gen();
16
+ ctx.state.requestId = id;
17
+ ctx.set.headers.set(header, id);
18
+ },
19
+ onResponse(res) {
20
+ // Defence in depth: also stamp on responses produced by error paths.
21
+ // (No-op if already set.)
22
+ void res;
23
+ },
24
+ };
25
+ }
26
+ export function secureHeaders(opts = {}) {
27
+ const headers = {};
28
+ const csp = opts.contentSecurityPolicy ?? "default-src 'self'; frame-ancestors 'none'";
29
+ if (csp !== false)
30
+ headers["content-security-policy"] = csp;
31
+ const hsts = opts.hsts ?? { maxAgeSeconds: 31536000, includeSubDomains: true };
32
+ if (hsts !== false) {
33
+ let v = `max-age=${hsts.maxAgeSeconds}`;
34
+ if (hsts.includeSubDomains)
35
+ v += "; includeSubDomains";
36
+ if (hsts.preload)
37
+ v += "; preload";
38
+ headers["strict-transport-security"] = v;
39
+ }
40
+ const frame = opts.frameOptions ?? "DENY";
41
+ if (frame !== false)
42
+ headers["x-frame-options"] = frame;
43
+ const ref = opts.referrerPolicy ?? "no-referrer";
44
+ if (ref !== false)
45
+ headers["referrer-policy"] = ref;
46
+ const perm = opts.permissionsPolicy ?? "camera=(), microphone=(), geolocation=()";
47
+ if (perm !== false)
48
+ headers["permissions-policy"] = perm;
49
+ const coop = opts.crossOriginOpenerPolicy ?? "same-origin";
50
+ if (coop !== false)
51
+ headers["cross-origin-opener-policy"] = coop;
52
+ const corp = opts.crossOriginResourcePolicy ?? "same-origin";
53
+ if (corp !== false)
54
+ headers["cross-origin-resource-policy"] = corp;
55
+ if (opts.noSniff !== false)
56
+ headers["x-content-type-options"] = "nosniff";
57
+ if (opts.xssProtection ?? false)
58
+ headers["x-xss-protection"] = "0"; // modern guidance
59
+ return {
60
+ onResponse(res) {
61
+ for (const [k, v] of Object.entries(headers)) {
62
+ if (!res.headers.has(k))
63
+ res.headers.set(k, v);
64
+ }
65
+ },
66
+ };
67
+ }
68
+ export function cors(opts) {
69
+ const allow = (origin) => {
70
+ if (!origin)
71
+ return null;
72
+ if (typeof opts.origin === "string")
73
+ return opts.origin === "*" || opts.origin === origin ? opts.origin : null;
74
+ if (Array.isArray(opts.origin))
75
+ return opts.origin.includes(origin) ? origin : null;
76
+ return opts.origin(origin) ? origin : null;
77
+ };
78
+ const methods = (opts.methods ?? ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]).join(", ");
79
+ const allowedHeaders = (opts.allowedHeaders ?? ["content-type", "authorization"]).join(", ");
80
+ const exposed = opts.exposedHeaders?.join(", ");
81
+ const maxAge = String(opts.maxAgeSeconds ?? 600);
82
+ return {
83
+ beforeHandle(ctx) {
84
+ const origin = ctx.request.headers.get("origin");
85
+ const allowed = allow(origin);
86
+ if (allowed) {
87
+ ctx.set.headers.set("access-control-allow-origin", allowed);
88
+ ctx.set.headers.set("vary", "Origin");
89
+ if (opts.credentials)
90
+ ctx.set.headers.set("access-control-allow-credentials", "true");
91
+ if (exposed)
92
+ ctx.set.headers.set("access-control-expose-headers", exposed);
93
+ }
94
+ if (ctx.request.method === "OPTIONS") {
95
+ const h = new Headers();
96
+ if (allowed) {
97
+ h.set("access-control-allow-origin", allowed);
98
+ h.set("vary", "Origin");
99
+ if (opts.credentials)
100
+ h.set("access-control-allow-credentials", "true");
101
+ }
102
+ h.set("access-control-allow-methods", methods);
103
+ h.set("access-control-allow-headers", allowedHeaders);
104
+ h.set("access-control-max-age", maxAge);
105
+ return new Response(null, { status: 204, headers: h });
106
+ }
107
+ return undefined;
108
+ },
109
+ onResponse(res) {
110
+ // Mirror set headers onto the final response.
111
+ // (No-op if already present.)
112
+ void res;
113
+ },
114
+ };
115
+ }
116
+ class MemoryStore {
117
+ buckets = new Map();
118
+ async hit(key, windowMs) {
119
+ const now = Date.now();
120
+ const b = this.buckets.get(key);
121
+ if (!b || b.resetMs <= now) {
122
+ const fresh = { count: 1, resetMs: now + windowMs };
123
+ this.buckets.set(key, fresh);
124
+ // Opportunistic cleanup so the map can't grow without bound.
125
+ if (this.buckets.size > 10_000) {
126
+ for (const [k, v] of this.buckets)
127
+ if (v.resetMs <= now)
128
+ this.buckets.delete(k);
129
+ }
130
+ return fresh;
131
+ }
132
+ b.count++;
133
+ return b;
134
+ }
135
+ }
136
+ export function rateLimit(opts) {
137
+ const store = opts.store ?? new MemoryStore();
138
+ const keyOf = opts.keyGenerator ??
139
+ ((ctx) => ctx.request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
140
+ ctx.request.headers.get("x-real-ip") ||
141
+ "global");
142
+ return {
143
+ async beforeHandle(ctx) {
144
+ const key = keyOf(ctx);
145
+ const { count, resetMs } = await store.hit(key, opts.windowMs);
146
+ const remaining = Math.max(0, opts.max - count);
147
+ ctx.set.headers.set("x-ratelimit-limit", String(opts.max));
148
+ ctx.set.headers.set("x-ratelimit-remaining", String(remaining));
149
+ ctx.set.headers.set("x-ratelimit-reset", String(Math.ceil(resetMs / 1000)));
150
+ if (count > opts.max) {
151
+ const retry = Math.ceil((resetMs - Date.now()) / 1000);
152
+ throw new TooManyRequestsError(opts.retryAfter !== false ? retry : undefined);
153
+ }
154
+ return undefined;
155
+ },
156
+ };
157
+ }
158
+ // ---------- Timing ----------
159
+ export function timing(headerName = "server-timing") {
160
+ const now = () => typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
161
+ return {
162
+ beforeHandle(ctx) {
163
+ ctx.state.__start = now();
164
+ },
165
+ afterHandle(ctx, value) {
166
+ const start = ctx.state.__start;
167
+ if (typeof start === "number") {
168
+ ctx.set.headers.set(headerName, `app;dur=${(now() - start).toFixed(2)}`);
169
+ }
170
+ return value;
171
+ },
172
+ };
173
+ }
174
+ // ---------- Bearer auth helper ----------
175
+ export function bearerAuth(opts) {
176
+ return {
177
+ async beforeHandle(ctx) {
178
+ const h = ctx.request.headers.get("authorization") ?? "";
179
+ const m = /^Bearer\s+(.+)$/i.exec(h);
180
+ if (!m) {
181
+ return new Response(JSON.stringify({
182
+ type: "https://daloyjs.dev/errors/unauthorized",
183
+ title: "Unauthorized",
184
+ status: 401,
185
+ }), {
186
+ status: 401,
187
+ headers: {
188
+ "content-type": "application/problem+json",
189
+ "www-authenticate": `Bearer realm="${opts.realm ?? "api"}"`,
190
+ },
191
+ });
192
+ }
193
+ const ok = await opts.validate(m[1]);
194
+ if (!ok)
195
+ throw new ForbiddenError("Invalid token");
196
+ return undefined;
197
+ },
198
+ };
199
+ }
200
+ export { timingSafeEqual };
201
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * OpenAPI 3.1 document generator.
3
+ *
4
+ * Built-in (not a plugin afterthought) — that's the whole point.
5
+ *
6
+ * If a schema exposes a `toJSONSchema()` method (Zod 4, Valibot, etc.)
7
+ * we use it. Otherwise we emit a permissive `{}` placeholder rather than
8
+ * fail — codegen and docs still work, just with looser types for that field.
9
+ */
10
+ import type { App } from "./app.js";
11
+ export interface OpenAPIInfo {
12
+ title: string;
13
+ version: string;
14
+ description?: string;
15
+ }
16
+ export interface SecuritySchemeMap {
17
+ [name: string]: Record<string, unknown>;
18
+ }
19
+ export interface OpenAPIOptions {
20
+ info: OpenAPIInfo;
21
+ servers?: Array<{
22
+ url: string;
23
+ description?: string;
24
+ }>;
25
+ securitySchemes?: SecuritySchemeMap;
26
+ }
27
+ export declare function generateOpenAPI(app: App, options: OpenAPIOptions): Record<string, unknown>;
28
+ //# sourceMappingURL=openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAIpC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,eAAe,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyC1F"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * OpenAPI 3.1 document generator.
3
+ *
4
+ * Built-in (not a plugin afterthought) — that's the whole point.
5
+ *
6
+ * If a schema exposes a `toJSONSchema()` method (Zod 4, Valibot, etc.)
7
+ * we use it. Otherwise we emit a permissive `{}` placeholder rather than
8
+ * fail — codegen and docs still work, just with looser types for that field.
9
+ */
10
+ export function generateOpenAPI(app, options) {
11
+ const paths = {};
12
+ for (const route of app.routes) {
13
+ const oasPath = route.path.replace(/:([A-Za-z0-9_]+)/g, "{$1}");
14
+ paths[oasPath] ??= {};
15
+ paths[oasPath][route.method.toLowerCase()] = buildOperation(route);
16
+ }
17
+ return {
18
+ openapi: "3.1.0",
19
+ info: options.info,
20
+ ...(options.servers ? { servers: options.servers } : {}),
21
+ paths,
22
+ components: {
23
+ ...(options.securitySchemes ? { securitySchemes: options.securitySchemes } : {}),
24
+ schemas: {
25
+ Problem: {
26
+ type: "object",
27
+ required: ["type", "title", "status"],
28
+ properties: {
29
+ type: { type: "string", format: "uri" },
30
+ title: { type: "string" },
31
+ status: { type: "integer" },
32
+ detail: { type: "string" },
33
+ instance: { type: "string" },
34
+ errors: {
35
+ type: "array",
36
+ items: {
37
+ type: "object",
38
+ properties: {
39
+ path: { type: "string" },
40
+ message: { type: "string" },
41
+ },
42
+ },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ },
48
+ };
49
+ }
50
+ function buildOperation(route) {
51
+ const op = {
52
+ ...(route.operationId ? { operationId: route.operationId } : {}),
53
+ ...(route.summary ? { summary: route.summary } : {}),
54
+ ...(route.description ? { description: route.description } : {}),
55
+ ...(route.tags?.length ? { tags: route.tags } : {}),
56
+ ...(route.deprecated ? { deprecated: true } : {}),
57
+ };
58
+ const parameters = [];
59
+ // Path params: emit one entry per :name in the path.
60
+ const paramNames = [...route.path.matchAll(/:([A-Za-z0-9_]+)/g)].map((m) => m[1]);
61
+ for (const name of paramNames) {
62
+ parameters.push({
63
+ name,
64
+ in: "path",
65
+ required: true,
66
+ schema: extractPropertySchema(route.request?.params, name) ?? { type: "string" },
67
+ });
68
+ }
69
+ if (route.request?.query) {
70
+ const schema = toJsonSchema(route.request.query) ?? { type: "object" };
71
+ const props = schema.properties ?? {};
72
+ const required = schema.required ?? [];
73
+ for (const [name, propSchema] of Object.entries(props)) {
74
+ parameters.push({
75
+ name,
76
+ in: "query",
77
+ required: required.includes(name),
78
+ schema: propSchema,
79
+ });
80
+ }
81
+ }
82
+ if (parameters.length)
83
+ op.parameters = parameters;
84
+ if (route.request?.body) {
85
+ op.requestBody = {
86
+ required: true,
87
+ content: {
88
+ "application/json": { schema: toJsonSchema(route.request.body) ?? {} },
89
+ },
90
+ };
91
+ }
92
+ const responses = {};
93
+ const responseEntries = Object.entries(route.responses);
94
+ for (const [status, spec] of responseEntries) {
95
+ if (!spec)
96
+ continue;
97
+ responses[status] = {
98
+ description: spec.description,
99
+ ...(spec.body
100
+ ? {
101
+ content: {
102
+ "application/json": {
103
+ schema: toJsonSchema(spec.body) ?? {},
104
+ ...(spec.examples ? { examples: spec.examples } : {}),
105
+ },
106
+ },
107
+ }
108
+ : {}),
109
+ };
110
+ }
111
+ op.responses = responses;
112
+ if (route.auth) {
113
+ op.security = [{ [route.auth.scheme]: route.auth.scopes ?? [] }];
114
+ }
115
+ return op;
116
+ }
117
+ function toJsonSchema(schema) {
118
+ if (!schema)
119
+ return undefined;
120
+ const anySchema = schema;
121
+ // Zod 4 has `.toJSONSchema()`; Valibot/TypeBox vary by adapter/version.
122
+ if (typeof anySchema.toJSONSchema === "function") {
123
+ try {
124
+ return anySchema.toJSONSchema();
125
+ }
126
+ catch {
127
+ /* fall through */
128
+ }
129
+ }
130
+ if (anySchema._def && typeof anySchema._def === "object") {
131
+ // Zod fallback — tiny heuristic; real apps should pass `.toJSONSchema()`-capable schemas.
132
+ return zodFallback(anySchema);
133
+ }
134
+ return {};
135
+ }
136
+ function extractPropertySchema(schema, prop) {
137
+ if (!schema)
138
+ return undefined;
139
+ const js = toJsonSchema(schema);
140
+ return js?.properties?.[prop];
141
+ }
142
+ function zodFallback(z) {
143
+ const t = z._def?.typeName ?? z._def?.type;
144
+ switch (t) {
145
+ case "ZodString":
146
+ case "string":
147
+ return { type: "string" };
148
+ case "ZodNumber":
149
+ case "number":
150
+ return { type: "number" };
151
+ case "ZodBoolean":
152
+ case "boolean":
153
+ return { type: "boolean" };
154
+ case "object":
155
+ case "ZodObject": {
156
+ const shape = typeof z._def.shape === "function" ? z._def.shape() : z._def.shape;
157
+ const properties = {};
158
+ const required = [];
159
+ for (const [k, v] of Object.entries(shape)) {
160
+ properties[k] = zodFallback(v);
161
+ if (!v.isOptional?.())
162
+ required.push(k);
163
+ }
164
+ return { type: "object", properties, required };
165
+ }
166
+ case "ZodArray":
167
+ case "array":
168
+ return { type: "array", items: zodFallback(z._def.element ?? z._def.type) };
169
+ case "ZodOptional":
170
+ case "optional":
171
+ case "ZodNullable":
172
+ case "nullable":
173
+ return zodFallback(z._def.innerType);
174
+ default:
175
+ return {};
176
+ }
177
+ }
178
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Trie / radix-style router with a static-route fast path.
3
+ *
4
+ * Performance:
5
+ * - Static (parameter-free) paths resolve via a single Map.get — O(1).
6
+ * - Dynamic paths walk a trie, O(path-segments) regardless of route count.
7
+ * - Path string is split by `indexOf` rather than a regex/replace.
8
+ *
9
+ * Safety:
10
+ * - Path traversal (`..`) and empty segments are rejected at lookup time.
11
+ * - Duplicate routes and duplicate operationIds throw at registration.
12
+ * - Wildcard segments must be terminal.
13
+ */
14
+ import type { HttpMethod } from "./types.js";
15
+ export interface RouteMatch<T> {
16
+ handler: T;
17
+ params: Record<string, string>;
18
+ }
19
+ export declare class Router<T> {
20
+ private root;
21
+ private operationIds;
22
+ /** Static (no-param/no-wildcard) routes for O(1) lookup. */
23
+ private staticTable;
24
+ add(method: HttpMethod, path: string, handler: T, operationId?: string): void;
25
+ find(method: HttpMethod, path: string): RouteMatch<T> | undefined;
26
+ /** Returns the set of methods registered at this exact path (for 405 responses). */
27
+ allowedMethods(path: string): HttpMethod[];
28
+ private walk;
29
+ }
30
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAaD,qBAAa,MAAM,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,YAAY,CAAqB;IACzC,4DAA4D;IAC5D,OAAO,CAAC,WAAW,CAAqD;IAExE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAsD7E,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS;IAsBjE,oFAAoF;IACpF,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE;IAS1C,OAAO,CAAC,IAAI;CA0Bb"}