@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/client.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed client factory.
|
|
3
|
+
*
|
|
4
|
+
* `createClient<typeof app>(...)` produces a typed fetch wrapper whose
|
|
5
|
+
* methods correspond to operationIds, with parameters and response
|
|
6
|
+
* types derived from the *same* route definitions used on the server.
|
|
7
|
+
*
|
|
8
|
+
* One source of truth: server, validation, OpenAPI, and client all
|
|
9
|
+
* line up. No drift, no separate codegen step required (though one
|
|
10
|
+
* can still be generated from the OpenAPI doc for non-TS clients).
|
|
11
|
+
*/
|
|
12
|
+
import type { App } from "./app.js";
|
|
13
|
+
import type { HandlerReturn, InferRequest, RequestSchemas, ResponsesMap, RouteDefinition } from "./types.js";
|
|
14
|
+
export type RoutesOf<A extends App> = A["routes"][number];
|
|
15
|
+
export type ClientFor<A extends App> = {
|
|
16
|
+
[R in Extract<RoutesOf<A>, {
|
|
17
|
+
operationId: string;
|
|
18
|
+
}> as R["operationId"]]: ClientMethod<R>;
|
|
19
|
+
};
|
|
20
|
+
type ClientMethod<R> = R extends RouteDefinition<infer P, infer _M, infer Req, infer Res> ? (input: ClientInput<P, Req>) => Promise<ClientOutput<Res>> : never;
|
|
21
|
+
type ClientInput<P extends string, Req extends RequestSchemas | undefined> = {
|
|
22
|
+
params: InferRequest<Req, P>["params"];
|
|
23
|
+
query?: Partial<InferRequest<Req, P>["query"]>;
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
} & (Req extends {
|
|
26
|
+
body: infer _B;
|
|
27
|
+
} ? {
|
|
28
|
+
body: InferRequest<Req, P>["body"];
|
|
29
|
+
} : {
|
|
30
|
+
body?: undefined;
|
|
31
|
+
});
|
|
32
|
+
type ClientOutput<Res extends ResponsesMap> = HandlerReturn<Res>;
|
|
33
|
+
export interface ClientOptions {
|
|
34
|
+
baseUrl: string;
|
|
35
|
+
fetch?: typeof fetch;
|
|
36
|
+
headers?: Record<string, string>;
|
|
37
|
+
}
|
|
38
|
+
export declare function createClient<A extends App>(app: A, opts: ClientOptions): ClientFor<A>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,eAAe,EAChB,MAAM,YAAY,CAAC;AAGpB,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,IAAI;KACpC,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;CAC1F,CAAC;AAEF,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,eAAe,CAC9C,MAAM,CAAC,EACP,MAAM,EAAE,EACR,MAAM,GAAG,EACT,MAAM,GAAG,CACV,GACG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAC1D,KAAK,CAAC;AAEV,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,SAAS,cAAc,GAAG,SAAS,IAAI;IAC3E,MAAM,EAAE,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,GAAG,CAAC,GAAG,SAAS;IAAE,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;CAAE,GAAG;IAAE,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAErG,KAAK,YAAY,CAAC,GAAG,SAAS,YAAY,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,CAsCrF"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed client factory.
|
|
3
|
+
*
|
|
4
|
+
* `createClient<typeof app>(...)` produces a typed fetch wrapper whose
|
|
5
|
+
* methods correspond to operationIds, with parameters and response
|
|
6
|
+
* types derived from the *same* route definitions used on the server.
|
|
7
|
+
*
|
|
8
|
+
* One source of truth: server, validation, OpenAPI, and client all
|
|
9
|
+
* line up. No drift, no separate codegen step required (though one
|
|
10
|
+
* can still be generated from the OpenAPI doc for non-TS clients).
|
|
11
|
+
*/
|
|
12
|
+
export function createClient(app, opts) {
|
|
13
|
+
const f = opts.fetch ?? fetch;
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const route of app.routes) {
|
|
16
|
+
if (!route.operationId)
|
|
17
|
+
continue;
|
|
18
|
+
out[route.operationId] = async (input = {}) => {
|
|
19
|
+
let path = route.path;
|
|
20
|
+
const params = input.params ?? {};
|
|
21
|
+
for (const [k, v] of Object.entries(params)) {
|
|
22
|
+
path = path.replace(`:${k}`, encodeURIComponent(String(v)));
|
|
23
|
+
}
|
|
24
|
+
const url = new URL(path, opts.baseUrl);
|
|
25
|
+
if (input.query) {
|
|
26
|
+
for (const [k, v] of Object.entries(input.query)) {
|
|
27
|
+
if (v === undefined)
|
|
28
|
+
continue;
|
|
29
|
+
if (Array.isArray(v))
|
|
30
|
+
v.forEach((x) => url.searchParams.append(k, String(x)));
|
|
31
|
+
else
|
|
32
|
+
url.searchParams.set(k, String(v));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const headers = { ...opts.headers, ...input.headers };
|
|
36
|
+
let body;
|
|
37
|
+
if (input.body !== undefined) {
|
|
38
|
+
headers["content-type"] ??= "application/json";
|
|
39
|
+
body = JSON.stringify(input.body);
|
|
40
|
+
}
|
|
41
|
+
const res = await f(url.toString(), { method: route.method, headers, body });
|
|
42
|
+
const text = await res.text();
|
|
43
|
+
const parsed = text ? safeJson(text) : undefined;
|
|
44
|
+
const headersOut = {};
|
|
45
|
+
res.headers.forEach((v, k) => {
|
|
46
|
+
headersOut[k] = v;
|
|
47
|
+
});
|
|
48
|
+
return { status: res.status, body: parsed, headers: headersOut };
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
function safeJson(text) {
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(text);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return text;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract-test runner.
|
|
3
|
+
*
|
|
4
|
+
* Iterates every registered route and verifies:
|
|
5
|
+
* - declared response examples (if any) actually validate against the
|
|
6
|
+
* declared response body schema
|
|
7
|
+
* - duplicate operationIds, missing operationIds, and dead routes
|
|
8
|
+
* - that no route declares a `body` schema for GET/HEAD/DELETE without
|
|
9
|
+
* opting in via `allowBodyOnSafeMethods`
|
|
10
|
+
*
|
|
11
|
+
* Returns a structured report. CI should `process.exit(1)` if `ok === false`.
|
|
12
|
+
*/
|
|
13
|
+
import type { App } from "./app.js";
|
|
14
|
+
export interface ContractIssue {
|
|
15
|
+
level: "error" | "warning";
|
|
16
|
+
route: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ContractReport {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
checked: number;
|
|
22
|
+
issues: ContractIssue[];
|
|
23
|
+
}
|
|
24
|
+
export interface ContractTestOptions {
|
|
25
|
+
/** Require every route to have an operationId. Default: true. */
|
|
26
|
+
requireOperationId?: boolean;
|
|
27
|
+
/** Allow body schemas on GET/HEAD/DELETE. Default: false. */
|
|
28
|
+
allowBodyOnSafeMethods?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function runContractTests(app: App, opts?: ContractTestOptions): Promise<ContractReport>;
|
|
31
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAGpC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,6DAA6D;IAC7D,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,GAAG,EACR,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,cAAc,CAAC,CAuEzB"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract-test runner.
|
|
3
|
+
*
|
|
4
|
+
* Iterates every registered route and verifies:
|
|
5
|
+
* - declared response examples (if any) actually validate against the
|
|
6
|
+
* declared response body schema
|
|
7
|
+
* - duplicate operationIds, missing operationIds, and dead routes
|
|
8
|
+
* - that no route declares a `body` schema for GET/HEAD/DELETE without
|
|
9
|
+
* opting in via `allowBodyOnSafeMethods`
|
|
10
|
+
*
|
|
11
|
+
* Returns a structured report. CI should `process.exit(1)` if `ok === false`.
|
|
12
|
+
*/
|
|
13
|
+
import { validate } from "./schema.js";
|
|
14
|
+
export async function runContractTests(app, opts = {}) {
|
|
15
|
+
const requireOperationId = opts.requireOperationId ?? true;
|
|
16
|
+
const allowBodyOnSafeMethods = opts.allowBodyOnSafeMethods ?? false;
|
|
17
|
+
const issues = [];
|
|
18
|
+
const seenOpIds = new Map();
|
|
19
|
+
for (const r of app.routes) {
|
|
20
|
+
const id = `${r.method} ${r.path}`;
|
|
21
|
+
if (requireOperationId && !r.operationId) {
|
|
22
|
+
issues.push({ level: "error", route: id, message: "Missing operationId" });
|
|
23
|
+
}
|
|
24
|
+
if (r.operationId) {
|
|
25
|
+
const prior = seenOpIds.get(r.operationId);
|
|
26
|
+
if (prior) {
|
|
27
|
+
issues.push({
|
|
28
|
+
level: "error",
|
|
29
|
+
route: id,
|
|
30
|
+
message: `Duplicate operationId "${r.operationId}" (also on ${prior})`,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
seenOpIds.set(r.operationId, id);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (!allowBodyOnSafeMethods &&
|
|
38
|
+
r.request?.body &&
|
|
39
|
+
(r.method === "GET" || r.method === "HEAD" || r.method === "DELETE")) {
|
|
40
|
+
issues.push({
|
|
41
|
+
level: "warning",
|
|
42
|
+
route: id,
|
|
43
|
+
message: `Body schema declared on ${r.method} (likely a mistake)`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (Object.keys(r.responses).length === 0) {
|
|
47
|
+
issues.push({ level: "error", route: id, message: "No responses declared" });
|
|
48
|
+
}
|
|
49
|
+
// Validate examples against schemas.
|
|
50
|
+
const responseEntries = Object.entries(r.responses);
|
|
51
|
+
for (const [status, spec] of responseEntries) {
|
|
52
|
+
if (!spec)
|
|
53
|
+
continue;
|
|
54
|
+
if (spec.body && spec.examples) {
|
|
55
|
+
for (const [name, example] of Object.entries(spec.examples)) {
|
|
56
|
+
const result = await validate(spec.body, example);
|
|
57
|
+
if (result.issues) {
|
|
58
|
+
issues.push({
|
|
59
|
+
level: "error",
|
|
60
|
+
route: id,
|
|
61
|
+
message: `Example "${name}" for ${status} violates schema: ${result.issues
|
|
62
|
+
.map((i) => i.message)
|
|
63
|
+
.join("; ")}`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
ok: !issues.some((i) => i.level === "error"),
|
|
72
|
+
checked: app.routes.length,
|
|
73
|
+
issues,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/docs.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in API documentation handlers.
|
|
3
|
+
*
|
|
4
|
+
* Both serve a single self-contained HTML page that loads the spec at
|
|
5
|
+
* `specUrl` from a CDN. No build step, no extra deps.
|
|
6
|
+
*
|
|
7
|
+
* (You can self-host the assets if your CSP forbids CDNs.)
|
|
8
|
+
*/
|
|
9
|
+
export interface DocsOptions {
|
|
10
|
+
specUrl: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function scalarHtml(opts: DocsOptions): string;
|
|
14
|
+
export declare function swaggerUiHtml(opts: DocsOptions): string;
|
|
15
|
+
export declare function htmlResponse(html: string): Response;
|
|
16
|
+
//# sourceMappingURL=docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAYpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAcvD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAWnD"}
|
package/dist/docs.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in API documentation handlers.
|
|
3
|
+
*
|
|
4
|
+
* Both serve a single self-contained HTML page that loads the spec at
|
|
5
|
+
* `specUrl` from a CDN. No build step, no extra deps.
|
|
6
|
+
*
|
|
7
|
+
* (You can self-host the assets if your CSP forbids CDNs.)
|
|
8
|
+
*/
|
|
9
|
+
export function scalarHtml(opts) {
|
|
10
|
+
const title = escapeHtml(opts.title ?? "API Reference");
|
|
11
|
+
const url = escapeHtml(opts.specUrl);
|
|
12
|
+
return `<!doctype html>
|
|
13
|
+
<html><head>
|
|
14
|
+
<meta charset="utf-8" />
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
16
|
+
<title>${title}</title>
|
|
17
|
+
</head><body>
|
|
18
|
+
<script id="api-reference" data-url="${url}"></script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
20
|
+
</body></html>`;
|
|
21
|
+
}
|
|
22
|
+
export function swaggerUiHtml(opts) {
|
|
23
|
+
const title = escapeHtml(opts.title ?? "API Docs");
|
|
24
|
+
const url = escapeHtml(opts.specUrl);
|
|
25
|
+
return `<!doctype html>
|
|
26
|
+
<html><head>
|
|
27
|
+
<meta charset="utf-8" />
|
|
28
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
29
|
+
<title>${title}</title>
|
|
30
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui.css" />
|
|
31
|
+
</head><body>
|
|
32
|
+
<div id="swagger"></div>
|
|
33
|
+
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui-bundle.js"></script>
|
|
34
|
+
<script>window.onload=()=>SwaggerUIBundle({url:"${url}",dom_id:"#swagger"});</script>
|
|
35
|
+
</body></html>`;
|
|
36
|
+
}
|
|
37
|
+
export function htmlResponse(html) {
|
|
38
|
+
return new Response(html, {
|
|
39
|
+
headers: {
|
|
40
|
+
"content-type": "text/html; charset=utf-8",
|
|
41
|
+
// Docs pages are intentionally permissive about external scripts; lock to those CDNs.
|
|
42
|
+
"content-security-policy": "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data: https:; connect-src 'self'",
|
|
43
|
+
"x-content-type-options": "nosniff",
|
|
44
|
+
"referrer-policy": "no-referrer",
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function escapeHtml(s) {
|
|
49
|
+
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=docs.js.map
|
package/dist/docs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,UAAU,UAAU,CAAC,IAAiB;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,eAAe,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO;;;;SAIA,KAAK;;uCAEyB,GAAG;;eAE3B,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAiB;IAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO;;;;SAIA,KAAK;;;;;kDAKoC,GAAG;eACtC,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,OAAO,EAAE;YACP,cAAc,EAAE,0BAA0B;YAC1C,sFAAsF;YACtF,yBAAyB,EACvB,4LAA4L;YAC9L,wBAAwB,EAAE,SAAS;YACnC,iBAAiB,EAAE,aAAa;SACjC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC;AACrH,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework error model.
|
|
3
|
+
*
|
|
4
|
+
* Wire format follows RFC 9457 (application/problem+json) so clients
|
|
5
|
+
* across teams and languages have a predictable, OpenAPI-documentable
|
|
6
|
+
* error shape.
|
|
7
|
+
*
|
|
8
|
+
* Production-mode rendering scrubs `detail` for 5xx responses so internal
|
|
9
|
+
* messages never leak to clients (security: information disclosure).
|
|
10
|
+
*/
|
|
11
|
+
export interface ProblemDetails {
|
|
12
|
+
type: string;
|
|
13
|
+
title: string;
|
|
14
|
+
status: number;
|
|
15
|
+
detail?: string;
|
|
16
|
+
instance?: string;
|
|
17
|
+
/** Validation issues, our extension. */
|
|
18
|
+
errors?: Array<{
|
|
19
|
+
path: string;
|
|
20
|
+
message: string;
|
|
21
|
+
}>;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
export interface ProblemRenderOptions {
|
|
25
|
+
/** When true, scrub `detail` for 5xx responses. Default: NODE_ENV === "production". */
|
|
26
|
+
production?: boolean;
|
|
27
|
+
/** Optional request id to embed for correlation. */
|
|
28
|
+
requestId?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare class HttpError extends Error {
|
|
31
|
+
readonly status: number;
|
|
32
|
+
readonly problem: ProblemDetails;
|
|
33
|
+
readonly headers?: Record<string, string>;
|
|
34
|
+
constructor(status: number, problem: Partial<ProblemDetails> & {
|
|
35
|
+
title: string;
|
|
36
|
+
}, headers?: Record<string, string>);
|
|
37
|
+
toResponse(opts?: ProblemRenderOptions): Response;
|
|
38
|
+
}
|
|
39
|
+
export declare class BadRequestError extends HttpError {
|
|
40
|
+
constructor(detail?: string);
|
|
41
|
+
}
|
|
42
|
+
export declare class ValidationError extends HttpError {
|
|
43
|
+
constructor(where: "params" | "query" | "headers" | "body", issues: Array<{
|
|
44
|
+
path: string;
|
|
45
|
+
message: string;
|
|
46
|
+
}>);
|
|
47
|
+
}
|
|
48
|
+
export declare class NotFoundError extends HttpError {
|
|
49
|
+
constructor(detail?: string);
|
|
50
|
+
}
|
|
51
|
+
export declare class UnauthorizedError extends HttpError {
|
|
52
|
+
constructor(detail?: string);
|
|
53
|
+
}
|
|
54
|
+
export declare class ForbiddenError extends HttpError {
|
|
55
|
+
constructor(detail?: string);
|
|
56
|
+
}
|
|
57
|
+
export declare class MethodNotAllowedError extends HttpError {
|
|
58
|
+
constructor(allow: string[]);
|
|
59
|
+
}
|
|
60
|
+
export declare class PayloadTooLargeError extends HttpError {
|
|
61
|
+
constructor(limit: number);
|
|
62
|
+
}
|
|
63
|
+
export declare class UnsupportedMediaTypeError extends HttpError {
|
|
64
|
+
constructor(got: string, expected: string[]);
|
|
65
|
+
}
|
|
66
|
+
export declare class TooManyRequestsError extends HttpError {
|
|
67
|
+
constructor(retryAfterSeconds?: number);
|
|
68
|
+
}
|
|
69
|
+
export declare class RequestTimeoutError extends HttpError {
|
|
70
|
+
constructor(ms: number);
|
|
71
|
+
}
|
|
72
|
+
export declare class InternalError extends HttpError {
|
|
73
|
+
constructor(detail?: string);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,uFAAuF;IACvF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAGxC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,EACpD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAclC,UAAU,CAAC,IAAI,GAAE,oBAAyB,GAAG,QAAQ;CAetD;AAED,qBAAa,eAAgB,SAAQ,SAAS;gBAChC,MAAM,CAAC,EAAE,MAAM;CAQ5B;AAED,qBAAa,eAAgB,SAAQ,SAAS;gBAE1C,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,EAC9C,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAUnD;AAED,qBAAa,aAAc,SAAQ,SAAS;gBAC9B,MAAM,CAAC,EAAE,MAAM;CAQ5B;AAED,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,MAAM,CAAC,EAAE,MAAM;CAQ5B;AAED,qBAAa,cAAe,SAAQ,SAAS;gBAC/B,MAAM,CAAC,EAAE,MAAM;CAQ5B;AAED,qBAAa,qBAAsB,SAAQ,SAAS;gBACtC,KAAK,EAAE,MAAM,EAAE;CAW5B;AAED,qBAAa,oBAAqB,SAAQ,SAAS;gBACrC,KAAK,EAAE,MAAM;CAQ1B;AAED,qBAAa,yBAA0B,SAAQ,SAAS;gBAC1C,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;CAQ5C;AAED,qBAAa,oBAAqB,SAAQ,SAAS;gBACrC,iBAAiB,CAAC,EAAE,MAAM;CAavC;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,EAAE,EAAE,MAAM;CAQvB;AAED,qBAAa,aAAc,SAAQ,SAAS;gBAC9B,MAAM,CAAC,EAAE,MAAM;CAQ5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework error model.
|
|
3
|
+
*
|
|
4
|
+
* Wire format follows RFC 9457 (application/problem+json) so clients
|
|
5
|
+
* across teams and languages have a predictable, OpenAPI-documentable
|
|
6
|
+
* error shape.
|
|
7
|
+
*
|
|
8
|
+
* Production-mode rendering scrubs `detail` for 5xx responses so internal
|
|
9
|
+
* messages never leak to clients (security: information disclosure).
|
|
10
|
+
*/
|
|
11
|
+
export class HttpError extends Error {
|
|
12
|
+
status;
|
|
13
|
+
problem;
|
|
14
|
+
headers;
|
|
15
|
+
constructor(status, problem, headers) {
|
|
16
|
+
super(problem.title);
|
|
17
|
+
this.name = "HttpError";
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.problem = {
|
|
20
|
+
type: problem.type ?? `https://httpstatuses.io/${status}`,
|
|
21
|
+
...problem,
|
|
22
|
+
title: problem.title,
|
|
23
|
+
status,
|
|
24
|
+
};
|
|
25
|
+
if (headers)
|
|
26
|
+
this.headers = headers;
|
|
27
|
+
}
|
|
28
|
+
toResponse(opts = {}) {
|
|
29
|
+
const isProd = opts.production ??
|
|
30
|
+
(typeof process !== "undefined" && process.env?.NODE_ENV === "production");
|
|
31
|
+
const out = { ...this.problem };
|
|
32
|
+
if (isProd && this.status >= 500) {
|
|
33
|
+
delete out.detail; // do not leak internals
|
|
34
|
+
}
|
|
35
|
+
if (opts.requestId)
|
|
36
|
+
out.instance = `urn:request:${opts.requestId}`;
|
|
37
|
+
const headers = {
|
|
38
|
+
"content-type": "application/problem+json",
|
|
39
|
+
...(this.headers ?? {}),
|
|
40
|
+
};
|
|
41
|
+
return new Response(JSON.stringify(out), { status: this.status, headers });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class BadRequestError extends HttpError {
|
|
45
|
+
constructor(detail) {
|
|
46
|
+
super(400, {
|
|
47
|
+
type: "https://daloyjs.dev/errors/bad-request",
|
|
48
|
+
title: "Bad Request",
|
|
49
|
+
...(detail ? { detail } : {}),
|
|
50
|
+
});
|
|
51
|
+
this.name = "BadRequestError";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class ValidationError extends HttpError {
|
|
55
|
+
constructor(where, issues) {
|
|
56
|
+
super(422, {
|
|
57
|
+
type: "https://daloyjs.dev/errors/validation",
|
|
58
|
+
title: "Request validation failed",
|
|
59
|
+
detail: `Invalid ${where}`,
|
|
60
|
+
errors: issues,
|
|
61
|
+
});
|
|
62
|
+
this.name = "ValidationError";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export class NotFoundError extends HttpError {
|
|
66
|
+
constructor(detail) {
|
|
67
|
+
super(404, {
|
|
68
|
+
type: "https://daloyjs.dev/errors/not-found",
|
|
69
|
+
title: "Not Found",
|
|
70
|
+
...(detail ? { detail } : {}),
|
|
71
|
+
});
|
|
72
|
+
this.name = "NotFoundError";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export class UnauthorizedError extends HttpError {
|
|
76
|
+
constructor(detail) {
|
|
77
|
+
super(401, {
|
|
78
|
+
type: "https://daloyjs.dev/errors/unauthorized",
|
|
79
|
+
title: "Unauthorized",
|
|
80
|
+
...(detail ? { detail } : {}),
|
|
81
|
+
});
|
|
82
|
+
this.name = "UnauthorizedError";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class ForbiddenError extends HttpError {
|
|
86
|
+
constructor(detail) {
|
|
87
|
+
super(403, {
|
|
88
|
+
type: "https://daloyjs.dev/errors/forbidden",
|
|
89
|
+
title: "Forbidden",
|
|
90
|
+
...(detail ? { detail } : {}),
|
|
91
|
+
});
|
|
92
|
+
this.name = "ForbiddenError";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export class MethodNotAllowedError extends HttpError {
|
|
96
|
+
constructor(allow) {
|
|
97
|
+
super(405, {
|
|
98
|
+
type: "https://daloyjs.dev/errors/method-not-allowed",
|
|
99
|
+
title: "Method Not Allowed",
|
|
100
|
+
}, { allow: allow.join(", ") });
|
|
101
|
+
this.name = "MethodNotAllowedError";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export class PayloadTooLargeError extends HttpError {
|
|
105
|
+
constructor(limit) {
|
|
106
|
+
super(413, {
|
|
107
|
+
type: "https://daloyjs.dev/errors/payload-too-large",
|
|
108
|
+
title: "Payload Too Large",
|
|
109
|
+
detail: `Body exceeds ${limit} bytes`,
|
|
110
|
+
});
|
|
111
|
+
this.name = "PayloadTooLargeError";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export class UnsupportedMediaTypeError extends HttpError {
|
|
115
|
+
constructor(got, expected) {
|
|
116
|
+
super(415, {
|
|
117
|
+
type: "https://daloyjs.dev/errors/unsupported-media-type",
|
|
118
|
+
title: "Unsupported Media Type",
|
|
119
|
+
detail: `Got "${got}", expected one of: ${expected.join(", ")}`,
|
|
120
|
+
});
|
|
121
|
+
this.name = "UnsupportedMediaTypeError";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export class TooManyRequestsError extends HttpError {
|
|
125
|
+
constructor(retryAfterSeconds) {
|
|
126
|
+
super(429, {
|
|
127
|
+
type: "https://daloyjs.dev/errors/too-many-requests",
|
|
128
|
+
title: "Too Many Requests",
|
|
129
|
+
}, retryAfterSeconds !== undefined
|
|
130
|
+
? { "retry-after": String(retryAfterSeconds) }
|
|
131
|
+
: undefined);
|
|
132
|
+
this.name = "TooManyRequestsError";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export class RequestTimeoutError extends HttpError {
|
|
136
|
+
constructor(ms) {
|
|
137
|
+
super(408, {
|
|
138
|
+
type: "https://daloyjs.dev/errors/request-timeout",
|
|
139
|
+
title: "Request Timeout",
|
|
140
|
+
detail: `Request exceeded ${ms}ms`,
|
|
141
|
+
});
|
|
142
|
+
this.name = "RequestTimeoutError";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export class InternalError extends HttpError {
|
|
146
|
+
constructor(detail) {
|
|
147
|
+
super(500, {
|
|
148
|
+
type: "https://daloyjs.dev/errors/internal",
|
|
149
|
+
title: "Internal Server Error",
|
|
150
|
+
...(detail ? { detail } : {}),
|
|
151
|
+
});
|
|
152
|
+
this.name = "InternalError";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { App } from "./app.js";
|
|
2
|
+
export type { AppOptions, IntrospectedRoute } from "./app.js";
|
|
3
|
+
export type { RouteDefinition, HttpMethod, PathString, RequestSchemas, ResponsesMap, ResponseSpec, AuthSpec, Hooks, BaseContext, HandlerReturn, InferRequest, ParamsOf, PathParams, } from "./types.js";
|
|
4
|
+
export { HttpError, BadRequestError, ValidationError, NotFoundError, UnauthorizedError, ForbiddenError, MethodNotAllowedError, PayloadTooLargeError, UnsupportedMediaTypeError, TooManyRequestsError, RequestTimeoutError, InternalError, } from "./errors.js";
|
|
5
|
+
export type { ProblemDetails, ProblemRenderOptions } from "./errors.js";
|
|
6
|
+
export type { StandardSchemaV1 } from "./schema.js";
|
|
7
|
+
export { validate, isStandardSchema } from "./schema.js";
|
|
8
|
+
export { readBodyLimited, safeJsonParse, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, } from "./security.js";
|
|
9
|
+
export { requestId, secureHeaders, cors, rateLimit, timing, bearerAuth, } from "./middleware.js";
|
|
10
|
+
export type { RequestIdOptions, SecureHeadersOptions, CorsOptions, RateLimitOptions, RateLimitStore, } from "./middleware.js";
|
|
11
|
+
export { createLogger, noopLogger } from "./logger.js";
|
|
12
|
+
export type { Logger, LogLevel, ConsoleLoggerOptions } from "./logger.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE9D,YAAY,EACV,eAAe,EACf,UAAU,EACV,UAAU,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,KAAK,EACL,WAAW,EACX,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,aAAa,EACb,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,GACX,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { App } from "./app.js";
|
|
2
|
+
export { HttpError, BadRequestError, ValidationError, NotFoundError, UnauthorizedError, ForbiddenError, MethodNotAllowedError, PayloadTooLargeError, UnsupportedMediaTypeError, TooManyRequestsError, RequestTimeoutError, InternalError, } from "./errors.js";
|
|
3
|
+
export { validate, isStandardSchema } from "./schema.js";
|
|
4
|
+
export { readBodyLimited, safeJsonParse, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, } from "./security.js";
|
|
5
|
+
export { requestId, secureHeaders, cors, rateLimit, timing, bearerAuth, } from "./middleware.js";
|
|
6
|
+
export { createLogger, noopLogger } from "./logger.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAmB/B,OAAO,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,aAAa,EACb,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,GACX,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
|
8
|
+
export interface Logger {
|
|
9
|
+
level: LogLevel;
|
|
10
|
+
trace(obj: object | string, msg?: string): void;
|
|
11
|
+
debug(obj: object | string, msg?: string): void;
|
|
12
|
+
info(obj: object | string, msg?: string): void;
|
|
13
|
+
warn(obj: object | string, msg?: string): void;
|
|
14
|
+
error(obj: object | string, msg?: string): void;
|
|
15
|
+
fatal(obj: object | string, msg?: string): void;
|
|
16
|
+
child(bindings: Record<string, unknown>): Logger;
|
|
17
|
+
}
|
|
18
|
+
export interface ConsoleLoggerOptions {
|
|
19
|
+
level?: LogLevel;
|
|
20
|
+
bindings?: Record<string, unknown>;
|
|
21
|
+
/** Where to write. Defaults to process.stdout.write or console.log. */
|
|
22
|
+
write?: (line: string) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function createLogger(opts?: ConsoleLoggerOptions): Logger;
|
|
25
|
+
export declare const noopLogger: Logger;
|
|
26
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE/E,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CAClD;AAWD,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,uEAAuE;IACvE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,wBAAgB,YAAY,CAAC,IAAI,GAAE,oBAAyB,GAAG,MAAM,CA6CpE;AAED,eAAO,MAAM,UAAU,EAAE,MAWxB,CAAC"}
|