@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.
- package/README.md +296 -0
- package/dist/adapters/bun.d.ts +16 -0
- package/dist/adapters/bun.d.ts.map +1 -0
- package/dist/adapters/bun.js +25 -0
- package/dist/adapters/bun.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +11 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -0
- package/dist/adapters/cloudflare.js +6 -0
- package/dist/adapters/cloudflare.js.map +1 -0
- package/dist/adapters/deno.d.ts +12 -0
- package/dist/adapters/deno.d.ts.map +1 -0
- package/dist/adapters/deno.js +13 -0
- package/dist/adapters/deno.js.map +1 -0
- package/dist/adapters/node.d.ts +25 -0
- package/dist/adapters/node.d.ts.map +1 -0
- package/dist/adapters/node.js +90 -0
- package/dist/adapters/node.js.map +1 -0
- package/dist/adapters/vercel.d.ts +13 -0
- package/dist/adapters/vercel.d.ts.map +1 -0
- package/dist/adapters/vercel.js +4 -0
- package/dist/adapters/vercel.js.map +1 -0
- package/dist/app.d.ts +104 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +487 -0
- package/dist/app.js.map +1 -0
- package/dist/client.d.ts +40 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +61 -0
- package/dist/client.js.map +1 -0
- package/dist/contract.d.ts +31 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +76 -0
- package/dist/contract.js.map +1 -0
- package/dist/docs.d.ts +16 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +51 -0
- package/dist/docs.js.map +1 -0
- package/dist/errors.d.ts +75 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +155 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +26 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +74 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware.d.ts +62 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +201 -0
- package/dist/middleware.js.map +1 -0
- package/dist/openapi.d.ts +28 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +178 -0
- package/dist/openapi.js.map +1 -0
- package/dist/router.d.ts +30 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +135 -0
- package/dist/router.js.map +1 -0
- package/dist/schema.d.ts +45 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +13 -0
- package/dist/schema.js.map +1 -0
- package/dist/security.d.ts +21 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +107 -0
- package/dist/security.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- 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":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAW1D,MAAM,UAAU,SAAS,CAAC,OAAyB,EAAE;IACnD,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC;IACvC,OAAO;QACL,YAAY,CAAC,GAAG;YACd,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7E,MAAM,EAAE,GAAG,QAAQ,IAAI,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YAClF,GAAG,CAAC,KAAiC,CAAC,SAAS,GAAG,EAAE,CAAC;YACtD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClC,CAAC;QACD,UAAU,CAAC,GAAG;YACZ,qEAAqE;YACrE,0BAA0B;YAC1B,KAAK,GAAG,CAAC;QACX,CAAC;KACF,CAAC;AACJ,CAAC;AAgBD,MAAM,UAAU,aAAa,CAAC,OAA6B,EAAE;IAC3D,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,qBAAqB,IAAI,4CAA4C,CAAC;IACvF,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,CAAC,yBAAyB,CAAC,GAAG,GAAG,CAAC;IAE5D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAC/E,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,aAAa,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,iBAAiB;YAAE,CAAC,IAAI,qBAAqB,CAAC;QACvD,IAAI,IAAI,CAAC,OAAO;YAAE,CAAC,IAAI,WAAW,CAAC;QACnC,OAAO,CAAC,2BAA2B,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC;IAC1C,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,KAAK,CAAC;IAExD,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,IAAI,aAAa,CAAC;IACjD,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,GAAG,CAAC;IAEpD,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,IAAI,0CAA0C,CAAC;IAClF,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;IAEzD,MAAM,IAAI,GAAG,IAAI,CAAC,uBAAuB,IAAI,aAAa,CAAC;IAC3D,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;IAEjE,MAAM,IAAI,GAAG,IAAI,CAAC,yBAAyB,IAAI,aAAa,CAAC;IAC7D,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,CAAC,8BAA8B,CAAC,GAAG,IAAI,CAAC;IAEnE,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,SAAS,CAAC;IAC1E,IAAI,IAAI,CAAC,aAAa,IAAI,KAAK;QAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,GAAG,CAAC,CAAC,kBAAkB;IAEtF,OAAO;QACL,UAAU,CAAC,GAAG;YACZ,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC7C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAaD,MAAM,UAAU,IAAI,CAAC,IAAiB;IACpC,MAAM,KAAK,GAAG,CAAC,MAAqB,EAAiB,EAAE;QACrD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/G,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACpF,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClG,MAAM,cAAc,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,IAAI,GAAG,CAAC,CAAC;IAEjD,OAAO;QACL,YAAY,CAAC,GAAG;YACd,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YAC9B,IAAI,OAAO,EAAE,CAAC;gBACZ,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;gBAC5D,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBACtC,IAAI,IAAI,CAAC,WAAW;oBAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;gBACtF,IAAI,OAAO;oBAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,+BAA+B,EAAE,OAAO,CAAC,CAAC;YAC7E,CAAC;YACD,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBACrC,MAAM,CAAC,GAAG,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,OAAO,EAAE,CAAC;oBACZ,CAAC,CAAC,GAAG,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;oBAC9C,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;oBACxB,IAAI,IAAI,CAAC,WAAW;wBAAE,CAAC,CAAC,GAAG,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;gBAC1E,CAAC;gBACD,CAAC,CAAC,GAAG,CAAC,8BAA8B,EAAE,OAAO,CAAC,CAAC;gBAC/C,CAAC,CAAC,GAAG,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;gBACtD,CAAC,CAAC,GAAG,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;gBACxC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,UAAU,CAAC,GAAG;YACZ,8CAA8C;YAC9C,8BAA8B;YAC9B,KAAK,GAAG,CAAC;QACX,CAAC;KACF,CAAC;AACJ,CAAC;AAiBD,MAAM,WAAW;IACP,OAAO,GAAG,IAAI,GAAG,EAA8C,CAAC;IACxE,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,QAAgB;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAC;YACpD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC7B,6DAA6D;YAC7D,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC;gBAC/B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO;oBAAE,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG;wBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAClF,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,CAAC;IACX,CAAC;CACF;AAED,MAAM,UAAU,SAAS,CAAC,IAAsB;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,WAAW,EAAE,CAAC;IAC9C,MAAM,KAAK,GACT,IAAI,CAAC,YAAY;QACjB,CAAC,CAAC,GAA0B,EAAE,EAAE,CAC9B,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;YACjE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;YACpC,QAAQ,CAAC,CAAC;IAEd,OAAO;QACL,KAAK,CAAC,YAAY,CAAC,GAAG;YACpB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACvB,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC;YAChD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;YAChE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5E,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;gBACvD,MAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAChF,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+BAA+B;AAE/B,MAAM,UAAU,MAAM,CAAC,UAAU,GAAG,eAAe;IACjD,MAAM,GAAG,GAAG,GAAW,EAAE,CACvB,OAAO,WAAW,KAAK,WAAW,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACzF,OAAO;QACL,YAAY,CAAC,GAAG;YACb,GAAG,CAAC,KAAiC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;QACzD,CAAC;QACD,WAAW,CAAC,GAAG,EAAE,KAAK;YACpB,MAAM,KAAK,GAAI,GAAG,CAAC,KAAiC,CAAC,OAA6B,CAAC;YACnF,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3E,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC;AAED,2CAA2C;AAE3C,MAAM,UAAU,UAAU,CAAC,IAG1B;IACC,OAAO;QACL,KAAK,CAAC,YAAY,CAAC,GAAG;YACpB,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;YACzD,MAAM,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACrC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACP,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,IAAI,EAAE,yCAAyC;oBAC/C,KAAK,EAAE,cAAc;oBACrB,MAAM,EAAE,GAAG;iBACZ,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE;wBACP,cAAc,EAAE,0BAA0B;wBAC1C,kBAAkB,EAAE,iBAAiB,IAAI,CAAC,KAAK,IAAI,KAAK,GAAG;qBAC5D;iBACF,CACF,CAAC;YACJ,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;YACtC,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,cAAc,CAAC,eAAe,CAAC,CAAC;YACnD,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -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"}
|
package/dist/openapi.js
ADDED
|
@@ -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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"}
|
package/dist/router.d.ts
ADDED
|
@@ -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"}
|