@schemavaults/openapi-operations 0.1.4

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 (47) hide show
  1. package/README.md +164 -0
  2. package/dist/adapters/nextjs.d.ts +20 -0
  3. package/dist/adapters/nextjs.js +24 -0
  4. package/dist/adapters/nextjs.js.map +1 -0
  5. package/dist/adapters/vercel.d.ts +13 -0
  6. package/dist/adapters/vercel.js +15 -0
  7. package/dist/adapters/vercel.js.map +1 -0
  8. package/dist/auth-scheme.d.ts +101 -0
  9. package/dist/auth-scheme.js +125 -0
  10. package/dist/auth-scheme.js.map +1 -0
  11. package/dist/http-method.d.ts +8 -0
  12. package/dist/http-method.js +24 -0
  13. package/dist/http-method.js.map +1 -0
  14. package/dist/index.d.ts +20 -0
  15. package/dist/index.js +12 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/openapi/build-openapi-document.d.ts +27 -0
  18. package/dist/openapi/build-openapi-document.js +124 -0
  19. package/dist/openapi/build-openapi-document.js.map +1 -0
  20. package/dist/openapi/extensions.d.ts +26 -0
  21. package/dist/openapi/extensions.js +47 -0
  22. package/dist/openapi/extensions.js.map +1 -0
  23. package/dist/openapi/path-format.d.ts +7 -0
  24. package/dist/openapi/path-format.js +26 -0
  25. package/dist/openapi/path-format.js.map +1 -0
  26. package/dist/operation.d.ts +168 -0
  27. package/dist/operation.js +111 -0
  28. package/dist/operation.js.map +1 -0
  29. package/dist/runtime/create-operations-app.d.ts +40 -0
  30. package/dist/runtime/create-operations-app.js +92 -0
  31. package/dist/runtime/create-operations-app.js.map +1 -0
  32. package/dist/runtime/errors.d.ts +42 -0
  33. package/dist/runtime/errors.js +37 -0
  34. package/dist/runtime/errors.js.map +1 -0
  35. package/dist/runtime/index.d.ts +8 -0
  36. package/dist/runtime/index.js +5 -0
  37. package/dist/runtime/index.js.map +1 -0
  38. package/dist/runtime/resolve-auth.d.ts +21 -0
  39. package/dist/runtime/resolve-auth.js +95 -0
  40. package/dist/runtime/resolve-auth.js.map +1 -0
  41. package/dist/runtime/validate-request.d.ts +13 -0
  42. package/dist/runtime/validate-request.js +121 -0
  43. package/dist/runtime/validate-request.js.map +1 -0
  44. package/dist/zod-openapi.d.ts +13 -0
  45. package/dist/zod-openapi.js +15 -0
  46. package/dist/zod-openapi.js.map +1 -0
  47. package/package.json +78 -0
@@ -0,0 +1,92 @@
1
+ import { Hono } from "hono";
2
+ import { assertUniqueOperations } from "../operation";
3
+ import { openApiPathToHonoPath } from "../openapi/path-format";
4
+ import { OPERATION_ERROR_CODES, OperationError, jsonResponse } from "./errors";
5
+ import { validateRequest } from "./validate-request";
6
+ import { assertResolversForOperations, resolveAuth } from "./resolve-auth";
7
+ function buildHandlerContext(c, operation, validated, auth, context) {
8
+ const declaredStatuses = new Set(Object.keys(operation.responses).map(Number));
9
+ const assertDeclared = (status) => {
10
+ if (!declaredStatuses.has(status)) {
11
+ throw new TypeError(`${operation.method.toUpperCase()} ${operation.path} responded with undeclared status ${status}`);
12
+ }
13
+ };
14
+ return {
15
+ params: validated.params,
16
+ query: validated.query,
17
+ headers: validated.headers,
18
+ body: validated.body,
19
+ auth,
20
+ context,
21
+ request: c.req.raw,
22
+ url: new URL(c.req.url),
23
+ json(status, body, init) {
24
+ assertDeclared(status);
25
+ return jsonResponse(status, body, init?.headers);
26
+ },
27
+ empty(status, init) {
28
+ assertDeclared(status);
29
+ return new Response(null, { status, headers: init?.headers });
30
+ },
31
+ redirect(location, status = 302) {
32
+ return new Response(null, { status, headers: { Location: location } });
33
+ },
34
+ };
35
+ }
36
+ /**
37
+ * Builds a Hono app that routes, validates, authenticates and dispatches
38
+ * the given operations. Mount it on Vercel functions with
39
+ * `toVercelHandler()` or Next.js route handlers with `toNextRouteHandlers()`.
40
+ */
41
+ export function createOperationsApp(options) {
42
+ assertUniqueOperations(options.operations);
43
+ const resolvers = options.authResolvers ?? {};
44
+ assertResolversForOperations(options.operations, resolvers);
45
+ const root = new Hono();
46
+ const app = options.basePath ? root.basePath(options.basePath) : root;
47
+ options.configure?.(app);
48
+ if (options.openapi) {
49
+ const { document, path = "/openapi.json" } = options.openapi;
50
+ app.get(path, async (c) => {
51
+ const resolved = typeof document === "function" ? await document(c) : document;
52
+ return c.json(resolved);
53
+ });
54
+ }
55
+ for (const operation of options.operations) {
56
+ app.on(operation.method.toUpperCase(), openApiPathToHonoPath(operation.path), async (c) => {
57
+ try {
58
+ const context = options.context ? await options.context(c) : undefined;
59
+ const auth = await resolveAuth(c, operation.auth, resolvers);
60
+ const validated = await validateRequest(c, operation);
61
+ const ctx = buildHandlerContext(c, operation, validated, auth, context);
62
+ const result = await operation.handler(ctx);
63
+ if (!(result instanceof Response)) {
64
+ throw new TypeError(`${operation.method.toUpperCase()} ${operation.path} handler must return a Response (use ctx.json / ctx.empty)`);
65
+ }
66
+ return result;
67
+ }
68
+ catch (error) {
69
+ if (error instanceof OperationError)
70
+ return error.toResponse();
71
+ if (options.onError) {
72
+ try {
73
+ await options.onError(error, c);
74
+ }
75
+ catch {
76
+ // never let error reporting mask the response
77
+ }
78
+ }
79
+ else {
80
+ console.error(`[openapi-operations] ${operation.method.toUpperCase()} ${operation.path} failed:`, error);
81
+ }
82
+ return jsonResponse(500, {
83
+ success: false,
84
+ error: OPERATION_ERROR_CODES.internal,
85
+ message: "Internal Server Error",
86
+ });
87
+ }
88
+ });
89
+ }
90
+ return root;
91
+ }
92
+ //# sourceMappingURL=create-operations-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-operations-app.js","sourceRoot":"","sources":["../../src/runtime/create-operations-app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Wire format for every error produced by the operations runtime. Matches
3
+ * the `{ success: false, message }` envelope used throughout the auth
4
+ * server, with a machine readable `error` code in addition.
5
+ */
6
+ export interface OperationErrorBody {
7
+ readonly success: false;
8
+ readonly error: string;
9
+ readonly message: string;
10
+ readonly issues?: readonly OperationValidationIssue[];
11
+ /** Extra machine readable details (e.g. missing scopes). */
12
+ readonly details?: Readonly<Record<string, unknown>>;
13
+ }
14
+ export interface OperationValidationIssue {
15
+ /** Which part of the request failed. */
16
+ readonly location: "params" | "query" | "headers" | "body";
17
+ readonly path: string;
18
+ readonly message: string;
19
+ readonly code: string;
20
+ }
21
+ export declare const OPERATION_ERROR_CODES: {
22
+ readonly validation: "validation_error";
23
+ readonly unauthorized: "unauthorized";
24
+ readonly forbidden: "forbidden";
25
+ readonly insufficientScope: "insufficient_scope";
26
+ readonly organizationRequired: "organization_required";
27
+ readonly notMember: "not_an_organization_member";
28
+ readonly unsupportedMediaType: "unsupported_media_type";
29
+ readonly internal: "internal_server_error";
30
+ };
31
+ /**
32
+ * Throw from a handler (or an auth resolver) to short-circuit with a
33
+ * specific status and error body.
34
+ */
35
+ export declare class OperationError extends Error {
36
+ readonly status: number;
37
+ readonly body: OperationErrorBody;
38
+ readonly headers: Readonly<Record<string, string>>;
39
+ constructor(status: number, body: Omit<OperationErrorBody, "success">, headers?: Readonly<Record<string, string>>);
40
+ toResponse(): Response;
41
+ }
42
+ export declare function jsonResponse(status: number, body: unknown, headers?: HeadersInit): Response;
@@ -0,0 +1,37 @@
1
+ export const OPERATION_ERROR_CODES = {
2
+ validation: "validation_error",
3
+ unauthorized: "unauthorized",
4
+ forbidden: "forbidden",
5
+ insufficientScope: "insufficient_scope",
6
+ organizationRequired: "organization_required",
7
+ notMember: "not_an_organization_member",
8
+ unsupportedMediaType: "unsupported_media_type",
9
+ internal: "internal_server_error",
10
+ };
11
+ /**
12
+ * Throw from a handler (or an auth resolver) to short-circuit with a
13
+ * specific status and error body.
14
+ */
15
+ export class OperationError extends Error {
16
+ status;
17
+ body;
18
+ headers;
19
+ constructor(status, body, headers = {}) {
20
+ super(body.message);
21
+ this.name = "OperationError";
22
+ this.status = status;
23
+ this.body = { success: false, ...body };
24
+ this.headers = headers;
25
+ }
26
+ toResponse() {
27
+ return jsonResponse(this.status, this.body, this.headers);
28
+ }
29
+ }
30
+ export function jsonResponse(status, body, headers) {
31
+ const merged = new Headers(headers);
32
+ if (!merged.has("content-type")) {
33
+ merged.set("content-type", "application/json; charset=utf-8");
34
+ }
35
+ return new Response(JSON.stringify(body), { status, headers: merged });
36
+ }
37
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/runtime/errors.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,UAAU,EAAE,kBAAkB;IAC9B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,iBAAiB,EAAE,oBAAoB;IACvC,oBAAoB,EAAE,uBAAuB;IAC7C,SAAS,EAAE,4BAA4B;IACvC,oBAAoB,EAAE,wBAAwB;IAC9C,QAAQ,EAAE,uBAAuB;CACzB,CAAC;AAEX;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,MAAM,CAAS;IACf,IAAI,CAAqB;IACzB,OAAO,CAAmC;IAEnD,YACE,MAAc,EACd,IAAyC,EACzC,UAA4C,EAAE;QAE9C,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,UAAU;QACR,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,IAAa,EACb,OAAqB;IAErB,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { createOperationsApp } from "./create-operations-app";
2
+ export type { CreateOperationsAppOptions, OpenApiDocumentRouteOptions, } from "./create-operations-app";
3
+ export { OperationError, OPERATION_ERROR_CODES, jsonResponse } from "./errors";
4
+ export type { OperationErrorBody, OperationValidationIssue } from "./errors";
5
+ export { resolveAuth, grantedScopes, missingScopes } from "./resolve-auth";
6
+ export type { AuthResolver, AuthResolvers } from "./resolve-auth";
7
+ export { validateRequest, queryToObject, headersToObject, readRequestBody } from "./validate-request";
8
+ export type { ValidatedRequest } from "./validate-request";
@@ -0,0 +1,5 @@
1
+ export { createOperationsApp } from "./create-operations-app";
2
+ export { OperationError, OPERATION_ERROR_CODES, jsonResponse } from "./errors";
3
+ export { resolveAuth, grantedScopes, missingScopes } from "./resolve-auth";
4
+ export { validateRequest, queryToObject, headersToObject, readRequestBody } from "./validate-request";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAK9D,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { Context } from "hono";
2
+ import type { AuthSchemeDefinition, OperationAuth } from "../auth-scheme";
3
+ import type { AnyOperationDefinition, AuthPrincipal } from "../operation";
4
+ /**
5
+ * Verifies the credential transported by one auth scheme. Returns null when
6
+ * the request carries no credential for the scheme (so the next accepted
7
+ * scheme is tried); throws {@link OperationError} to reject outright (e.g.
8
+ * a credential that IS present but invalid).
9
+ */
10
+ export type AuthResolver<TUser = unknown> = (c: Context, scheme: AuthSchemeDefinition) => Promise<AuthPrincipal<TUser> | null> | AuthPrincipal<TUser> | null;
11
+ export type AuthResolvers<TUser = unknown> = Readonly<Record<string, AuthResolver<TUser>>>;
12
+ export declare function assertResolversForOperations(operations: readonly AnyOperationDefinition[], resolvers: AuthResolvers): void;
13
+ export declare function grantedScopes(principal: AuthPrincipal): string[];
14
+ export declare function missingScopes(principal: AuthPrincipal, required: readonly string[]): string[];
15
+ /**
16
+ * Applies an operation's {@link OperationAuth} to the request: tries each
17
+ * accepted scheme's resolver in order, then enforces the route guard,
18
+ * required scopes, and organization membership. Returns the principal, or
19
+ * null for public operations.
20
+ */
21
+ export declare function resolveAuth<TUser>(c: Context, auth: OperationAuth, resolvers: AuthResolvers<TUser>): Promise<AuthPrincipal<TUser> | null>;
@@ -0,0 +1,95 @@
1
+ import { OPERATION_ERROR_CODES, OperationError } from "./errors";
2
+ export function assertResolversForOperations(operations, resolvers) {
3
+ for (const operation of operations) {
4
+ if (operation.auth.type !== "required")
5
+ continue;
6
+ for (const scheme of operation.auth.schemes) {
7
+ if (typeof resolvers[scheme.name] !== "function") {
8
+ throw new TypeError(`${operation.method.toUpperCase()} ${operation.path} accepts auth scheme "${scheme.name}" but no resolver was registered for it`);
9
+ }
10
+ }
11
+ }
12
+ }
13
+ function challengeHeader(schemes) {
14
+ const challenges = schemes
15
+ .map((scheme) => scheme.challenge)
16
+ .filter((challenge) => typeof challenge === "string");
17
+ return challenges.length > 0 ? { "WWW-Authenticate": challenges.join(", ") } : {};
18
+ }
19
+ export function grantedScopes(principal) {
20
+ if (typeof principal.scope !== "string")
21
+ return [];
22
+ return principal.scope.split(" ").filter((scope) => scope.length > 0);
23
+ }
24
+ export function missingScopes(principal, required) {
25
+ const granted = new Set(grantedScopes(principal));
26
+ return required.filter((scope) => !granted.has(scope));
27
+ }
28
+ /**
29
+ * Applies an operation's {@link OperationAuth} to the request: tries each
30
+ * accepted scheme's resolver in order, then enforces the route guard,
31
+ * required scopes, and organization membership. Returns the principal, or
32
+ * null for public operations.
33
+ */
34
+ export async function resolveAuth(c, auth, resolvers) {
35
+ if (auth.type === "public")
36
+ return null;
37
+ let principal = null;
38
+ for (const scheme of auth.schemes) {
39
+ const resolver = resolvers[scheme.name];
40
+ if (!resolver)
41
+ continue;
42
+ principal = await resolver(c, scheme);
43
+ if (principal)
44
+ break;
45
+ }
46
+ if (!principal) {
47
+ throw new OperationError(401, { error: OPERATION_ERROR_CODES.unauthorized, message: "Authentication required" }, challengeHeader(auth.schemes));
48
+ }
49
+ if ((auth.routeGuard ?? "authenticated") === "admin" && !principal.isAdmin) {
50
+ throw new OperationError(403, {
51
+ error: OPERATION_ERROR_CODES.forbidden,
52
+ message: "Administrator access required",
53
+ });
54
+ }
55
+ const required = auth.requiredScopes ?? [];
56
+ if (required.length > 0) {
57
+ const missing = missingScopes(principal, required);
58
+ if (missing.length > 0) {
59
+ throw new OperationError(403, {
60
+ error: OPERATION_ERROR_CODES.insufficientScope,
61
+ message: `The presented credential is missing required scope(s): ${missing.join(" ")}`,
62
+ details: { required_scopes: [...required], missing_scopes: missing },
63
+ }, {
64
+ "WWW-Authenticate": `Bearer error="insufficient_scope", scope="${required.join(" ")}"`,
65
+ });
66
+ }
67
+ }
68
+ if (auth.organization) {
69
+ const { parameter, roles, adminBypass } = auth.organization;
70
+ const organizationId = c.req.param(parameter) ?? c.req.query(parameter) ?? undefined;
71
+ if (typeof organizationId !== "string" || organizationId.length === 0) {
72
+ throw new OperationError(400, {
73
+ error: OPERATION_ERROR_CODES.organizationRequired,
74
+ message: `Request parameter "${parameter}" (organization id) is required`,
75
+ });
76
+ }
77
+ const bypass = (adminBypass ?? true) && principal.isAdmin;
78
+ if (!bypass) {
79
+ const role = principal.getOrganizationRole
80
+ ? await principal.getOrganizationRole(organizationId)
81
+ : false;
82
+ const allowed = role !== false && (roles.length === 0 || roles.includes(role));
83
+ if (!allowed) {
84
+ throw new OperationError(403, {
85
+ error: OPERATION_ERROR_CODES.notMember,
86
+ message: roles.length === 0
87
+ ? "You are not a member of this organization"
88
+ : `This operation requires one of the organization roles: ${roles.join(", ")}`,
89
+ });
90
+ }
91
+ }
92
+ }
93
+ return principal;
94
+ }
95
+ //# sourceMappingURL=resolve-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-auth.js","sourceRoot":"","sources":["../../src/runtime/resolve-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Context } from "hono";
2
+ import type { AnyOperationDefinition, RequestBodyDefinition } from "../operation";
3
+ /** Query string as an object: single values as strings, repeated keys as arrays. */
4
+ export declare function queryToObject(c: Context): Record<string, string | string[]>;
5
+ export declare function headersToObject(c: Context): Record<string, string>;
6
+ export declare function readRequestBody(c: Context, definition: RequestBodyDefinition): Promise<unknown>;
7
+ export interface ValidatedRequest {
8
+ readonly params: Readonly<Record<string, unknown>>;
9
+ readonly query: Readonly<Record<string, unknown>>;
10
+ readonly headers: Readonly<Record<string, unknown>>;
11
+ readonly body: unknown;
12
+ }
13
+ export declare function validateRequest(c: Context, operation: AnyOperationDefinition): Promise<ValidatedRequest>;
@@ -0,0 +1,121 @@
1
+ import { OPERATION_ERROR_CODES, OperationError, } from "./errors";
2
+ async function parseWith(schema, value, location) {
3
+ const result = await schema.safeParseAsync(value);
4
+ if (result.success)
5
+ return result.data;
6
+ const issues = result.error.issues.map((issue) => ({
7
+ location,
8
+ path: issue.path.map((segment) => String(segment)).join("."),
9
+ message: issue.message,
10
+ code: issue.code,
11
+ }));
12
+ throw new OperationError(400, {
13
+ error: OPERATION_ERROR_CODES.validation,
14
+ message: `Invalid request ${location}`,
15
+ issues,
16
+ });
17
+ }
18
+ /** Query string as an object: single values as strings, repeated keys as arrays. */
19
+ export function queryToObject(c) {
20
+ const result = {};
21
+ for (const [key, values] of Object.entries(c.req.queries())) {
22
+ if (values.length === 1) {
23
+ const [single] = values;
24
+ if (typeof single === "string")
25
+ result[key] = single;
26
+ }
27
+ else if (values.length > 1) {
28
+ result[key] = values;
29
+ }
30
+ }
31
+ return result;
32
+ }
33
+ export function headersToObject(c) {
34
+ const result = {};
35
+ c.req.raw.headers.forEach((value, key) => {
36
+ result[key.toLowerCase()] = value;
37
+ });
38
+ return result;
39
+ }
40
+ function mediaTypeOf(c) {
41
+ const header = c.req.header("content-type");
42
+ if (typeof header !== "string")
43
+ return null;
44
+ const [type] = header.split(";");
45
+ return type?.trim().toLowerCase() ?? null;
46
+ }
47
+ export async function readRequestBody(c, definition) {
48
+ const expected = (definition.contentType ?? "application/json").toLowerCase();
49
+ const required = definition.required ?? true;
50
+ const actual = mediaTypeOf(c);
51
+ const contentLength = c.req.header("content-length");
52
+ const looksEmpty = actual === null && (contentLength === undefined || contentLength === "0");
53
+ if (looksEmpty) {
54
+ if (!required)
55
+ return undefined;
56
+ throw new OperationError(400, {
57
+ error: OPERATION_ERROR_CODES.validation,
58
+ message: `A ${expected} request body is required`,
59
+ issues: [
60
+ { location: "body", path: "", message: "Request body is required", code: "required" },
61
+ ],
62
+ });
63
+ }
64
+ if (actual !== expected && !(expected === "text/plain" && actual?.startsWith("text/"))) {
65
+ throw new OperationError(415, {
66
+ error: OPERATION_ERROR_CODES.unsupportedMediaType,
67
+ message: `Expected a ${expected} request body but received ${actual ?? "none"}`,
68
+ });
69
+ }
70
+ try {
71
+ switch (expected) {
72
+ case "application/json":
73
+ return await c.req.json();
74
+ case "application/x-www-form-urlencoded":
75
+ case "multipart/form-data":
76
+ return await c.req.parseBody({ all: true });
77
+ case "text/plain":
78
+ return await c.req.text();
79
+ default:
80
+ if (expected.endsWith("+json"))
81
+ return await c.req.json();
82
+ return await c.req.text();
83
+ }
84
+ }
85
+ catch (e) {
86
+ throw new OperationError(400, {
87
+ error: OPERATION_ERROR_CODES.validation,
88
+ message: `Malformed ${expected} request body`,
89
+ issues: [
90
+ {
91
+ location: "body",
92
+ path: "",
93
+ message: e instanceof Error ? e.message : "Could not parse body",
94
+ code: "malformed",
95
+ },
96
+ ],
97
+ });
98
+ }
99
+ }
100
+ const EMPTY = Object.freeze({});
101
+ export async function validateRequest(c, operation) {
102
+ const { params, query, headers, body } = operation.request;
103
+ const parsedParams = params
104
+ ? (await parseWith(params, c.req.param(), "params"))
105
+ : EMPTY;
106
+ const parsedQuery = query
107
+ ? (await parseWith(query, queryToObject(c), "query"))
108
+ : EMPTY;
109
+ const parsedHeaders = headers
110
+ ? (await parseWith(headers, headersToObject(c), "headers"))
111
+ : EMPTY;
112
+ let parsedBody = undefined;
113
+ if (body) {
114
+ const raw = await readRequestBody(c, body);
115
+ if (raw !== undefined || (body.required ?? true)) {
116
+ parsedBody = await parseWith(body.schema, raw, "body");
117
+ }
118
+ }
119
+ return { params: parsedParams, query: parsedQuery, headers: parsedHeaders, body: parsedBody };
120
+ }
121
+ //# sourceMappingURL=validate-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-request.js","sourceRoot":"","sources":["../../src/runtime/validate-request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Single zod entrypoint for this package.
3
+ *
4
+ * `@asteasolutions/zod-to-openapi` adds the `.openapi()` method to every zod
5
+ * schema (title, description, examples, refId for `components/schemas`, ...).
6
+ * The extension has to be installed exactly once per zod instance, before any
7
+ * schema that wants to call `.openapi()` is built, so consumers should import
8
+ * `z` from here (or call `extendZodWithOpenApi` themselves on the same zod
9
+ * instance) instead of importing zod directly.
10
+ */
11
+ import { z } from "zod";
12
+ export { z };
13
+ export type { ZodType, ZodObject } from "zod";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Single zod entrypoint for this package.
3
+ *
4
+ * `@asteasolutions/zod-to-openapi` adds the `.openapi()` method to every zod
5
+ * schema (title, description, examples, refId for `components/schemas`, ...).
6
+ * The extension has to be installed exactly once per zod instance, before any
7
+ * schema that wants to call `.openapi()` is built, so consumers should import
8
+ * `z` from here (or call `extendZodWithOpenApi` themselves on the same zod
9
+ * instance) instead of importing zod directly.
10
+ */
11
+ import { z } from "zod";
12
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
13
+ extendZodWithOpenApi(z);
14
+ export { z };
15
+ //# sourceMappingURL=zod-openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-openapi.js","sourceRoot":"","sources":["../src/zod-openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAEtE,oBAAoB,CAAC,CAAC,CAAC,CAAC;AAExB,OAAO,EAAE,CAAC,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@schemavaults/openapi-operations",
3
+ "description": "Define OpenAPI-representable HTTP operations (zod schemas + auth schemes + handlers) and serve them as Hono apps on Vercel functions / Next.js route handlers",
4
+ "version": "0.1.4",
5
+ "license": "UNLICENSED",
6
+ "private": false,
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/schemavaults/auth.git",
10
+ "directory": "packages/openapi-operations"
11
+ },
12
+ "type": "module",
13
+ "main": "dist/index.js",
14
+ "module": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "dependencies": {
17
+ "@asteasolutions/zod-to-openapi": "9.1.0",
18
+ "@schemavaults/auth-common": "0.26.6",
19
+ "hono": "4.13.7",
20
+ "openapi3-ts": "4.6.1",
21
+ "zod": "4.4.3"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc --project tsconfig.json && tsc-alias --project tsconfig.json",
25
+ "test": "NODE_ENV=test bun test",
26
+ "cleanup:compiled-tests-output": "find ./dist -type f \\( -name \"*.test.js\" -o -name \"*.test.js.map\" -o -name \"*.test.d.ts\" \\) -delete",
27
+ "cleanup": "bun run cleanup:compiled-tests-output",
28
+ "postbuild": "bun run cleanup",
29
+ "lint": "eslint src --ext .ts,.tsx",
30
+ "typecheck": "tsc --project tsconfig.json --noEmit"
31
+ },
32
+ "devDependencies": {
33
+ "typescript": "6.0.3",
34
+ "tsc-alias": "1.9.0",
35
+ "bun-types": "1.4.2",
36
+ "eslint": "9.39.1",
37
+ "@eslint/js": "9.39.1",
38
+ "globals": "16.5.0",
39
+ "@typescript-eslint/eslint-plugin": "8.48.1",
40
+ "@typescript-eslint/parser": "8.48.1"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "packageManager": "bun@1.4.2",
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/index.d.ts",
49
+ "import": "./dist/index.js",
50
+ "require": "./dist/index.js"
51
+ },
52
+ "./runtime": {
53
+ "types": "./dist/runtime/index.d.ts",
54
+ "import": "./dist/runtime/index.js",
55
+ "require": "./dist/runtime/index.js"
56
+ },
57
+ "./vercel": {
58
+ "types": "./dist/adapters/vercel.d.ts",
59
+ "import": "./dist/adapters/vercel.js",
60
+ "require": "./dist/adapters/vercel.js"
61
+ },
62
+ "./nextjs": {
63
+ "types": "./dist/adapters/nextjs.d.ts",
64
+ "import": "./dist/adapters/nextjs.js",
65
+ "require": "./dist/adapters/nextjs.js"
66
+ },
67
+ "./*": {
68
+ "types": "./dist/*",
69
+ "import": "./dist/*",
70
+ "require": "./dist/*"
71
+ },
72
+ "./dist/*": {
73
+ "types": "./dist/*",
74
+ "import": "./dist/*",
75
+ "require": "./dist/*"
76
+ }
77
+ }
78
+ }