@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,124 @@
1
+ import { OpenAPIRegistry, OpenApiGeneratorV31, } from "@asteasolutions/zod-to-openapi";
2
+ import { assertUniqueOperations } from "../operation";
3
+ import { SCHEMAVAULTS_AUTH_EXTENSION, SCHEMAVAULTS_SCHEME_CHALLENGE_EXTENSION, SCHEMAVAULTS_SCHEME_TITLE_EXTENSION, toSchemaVaultsAuthExtension, } from "./extensions";
4
+ /** Every distinct auth scheme referenced by the operations (first definition wins per name). */
5
+ export function collectAuthSchemes(operations, additional = []) {
6
+ const byName = new Map();
7
+ const add = (scheme) => {
8
+ const existing = byName.get(scheme.name);
9
+ if (existing && existing !== scheme) {
10
+ const same = JSON.stringify(existing.securityScheme) === JSON.stringify(scheme.securityScheme);
11
+ if (!same) {
12
+ throw new TypeError(`Auth scheme "${scheme.name}" is defined twice with different security scheme objects`);
13
+ }
14
+ return;
15
+ }
16
+ byName.set(scheme.name, scheme);
17
+ };
18
+ for (const scheme of additional)
19
+ add(scheme);
20
+ for (const operation of operations) {
21
+ if (operation.auth.type === "required") {
22
+ for (const scheme of operation.auth.schemes)
23
+ add(scheme);
24
+ }
25
+ }
26
+ return [...byName.values()];
27
+ }
28
+ export function toSecuritySchemeComponent(scheme) {
29
+ return {
30
+ ...scheme.securityScheme,
31
+ description: scheme.securityScheme.description ?? scheme.description,
32
+ [SCHEMAVAULTS_SCHEME_TITLE_EXTENSION]: scheme.title,
33
+ ...(scheme.challenge !== undefined
34
+ ? { [SCHEMAVAULTS_SCHEME_CHALLENGE_EXTENSION]: scheme.challenge }
35
+ : {}),
36
+ };
37
+ }
38
+ function toSecurityRequirements(operation) {
39
+ if (operation.auth.type === "public")
40
+ return [];
41
+ const scopes = [...(operation.auth.requiredScopes ?? [])];
42
+ return operation.auth.schemes.map((scheme) => ({ [scheme.name]: scopes }));
43
+ }
44
+ /** zod-to-openapi route config for one operation (also usable with `@hono/zod-openapi`). */
45
+ export function toRouteConfig(operation) {
46
+ const responses = {};
47
+ for (const [status, response] of Object.entries(operation.responses)) {
48
+ responses[status] = {
49
+ description: response.description,
50
+ ...(response.headers ? { headers: response.headers } : {}),
51
+ ...(response.schema
52
+ ? {
53
+ content: {
54
+ [response.contentType ?? "application/json"]: { schema: response.schema },
55
+ },
56
+ }
57
+ : {}),
58
+ };
59
+ }
60
+ const { params, query, headers, body } = operation.request;
61
+ return {
62
+ method: operation.method,
63
+ path: operation.path,
64
+ operationId: operation.operationId,
65
+ summary: operation.summary,
66
+ ...(operation.description !== undefined ? { description: operation.description } : {}),
67
+ tags: [...operation.tags],
68
+ ...(operation.deprecated ? { deprecated: true } : {}),
69
+ security: toSecurityRequirements(operation),
70
+ request: {
71
+ ...(params ? { params } : {}),
72
+ ...(query ? { query } : {}),
73
+ ...(headers ? { headers } : {}),
74
+ ...(body
75
+ ? {
76
+ body: {
77
+ required: body.required ?? true,
78
+ ...(body.description !== undefined ? { description: body.description } : {}),
79
+ content: {
80
+ [body.contentType ?? "application/json"]: { schema: body.schema },
81
+ },
82
+ },
83
+ }
84
+ : {}),
85
+ },
86
+ responses,
87
+ [SCHEMAVAULTS_AUTH_EXTENSION]: toSchemaVaultsAuthExtension(operation.auth),
88
+ ...(operation.extensions ?? {}),
89
+ };
90
+ }
91
+ /**
92
+ * Builds an OpenAPI 3.1 document from operation definitions using
93
+ * `@asteasolutions/zod-to-openapi`. Zod schemas registered with
94
+ * `.openapi("RefId")` are emitted under `components.schemas`.
95
+ */
96
+ export function buildOpenApiDocument(options) {
97
+ assertUniqueOperations(options.operations);
98
+ const registry = new OpenAPIRegistry();
99
+ for (const scheme of collectAuthSchemes(options.operations, options.additionalAuthSchemes)) {
100
+ registry.registerComponent("securitySchemes", scheme.name, toSecuritySchemeComponent(scheme));
101
+ }
102
+ for (const operation of options.operations) {
103
+ registry.registerPath(toRouteConfig(operation));
104
+ }
105
+ const declaredTags = new Map();
106
+ for (const tag of options.tags ?? [])
107
+ declaredTags.set(tag.name, tag);
108
+ for (const operation of options.operations) {
109
+ for (const tag of operation.tags) {
110
+ if (!declaredTags.has(tag))
111
+ declaredTags.set(tag, { name: tag });
112
+ }
113
+ }
114
+ const generator = new OpenApiGeneratorV31(registry.definitions);
115
+ return generator.generateDocument({
116
+ openapi: "3.1.0",
117
+ info: options.info,
118
+ ...(options.servers ? { servers: [...options.servers] } : {}),
119
+ ...(declaredTags.size > 0 ? { tags: [...declaredTags.values()] } : {}),
120
+ ...(options.externalDocs ? { externalDocs: options.externalDocs } : {}),
121
+ ...(options.extensions ?? {}),
122
+ });
123
+ }
124
+ //# sourceMappingURL=build-openapi-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-openapi-document.js","sourceRoot":"","sources":["../../src/openapi/build-openapi-document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { OrganizationMembershipRoleType } from "@schemavaults/auth-common/organizations";
2
+ import type { OperationAuth, RouteGuardType } from "../auth-scheme";
3
+ /**
4
+ * Vendor extension attached to every operation object so API docs can show
5
+ * the SchemaVaults authorization requirements that plain OpenAPI `security`
6
+ * requirements cannot express (route guard level, organization role).
7
+ */
8
+ export declare const SCHEMAVAULTS_AUTH_EXTENSION: "x-schemavaults-auth";
9
+ /** Extra keys on `components.securitySchemes[name]` describing the scheme for docs. */
10
+ export declare const SCHEMAVAULTS_SCHEME_TITLE_EXTENSION: "x-schemavaults-title";
11
+ export declare const SCHEMAVAULTS_SCHEME_CHALLENGE_EXTENSION: "x-schemavaults-challenge";
12
+ export interface SchemaVaultsAuthExtension {
13
+ readonly public: boolean;
14
+ /** Names of the accepted security schemes (any one satisfies the operation). */
15
+ readonly schemes: readonly string[];
16
+ readonly routeGuard: RouteGuardType | null;
17
+ readonly requiredScopes: readonly string[];
18
+ readonly organization: {
19
+ readonly parameter: string;
20
+ readonly roles: readonly OrganizationMembershipRoleType[];
21
+ readonly adminBypass: boolean;
22
+ } | null;
23
+ readonly notes?: string;
24
+ }
25
+ export declare function toSchemaVaultsAuthExtension(auth: OperationAuth): SchemaVaultsAuthExtension;
26
+ export declare function isSchemaVaultsAuthExtension(value: unknown): value is SchemaVaultsAuthExtension;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Vendor extension attached to every operation object so API docs can show
3
+ * the SchemaVaults authorization requirements that plain OpenAPI `security`
4
+ * requirements cannot express (route guard level, organization role).
5
+ */
6
+ export const SCHEMAVAULTS_AUTH_EXTENSION = "x-schemavaults-auth";
7
+ /** Extra keys on `components.securitySchemes[name]` describing the scheme for docs. */
8
+ export const SCHEMAVAULTS_SCHEME_TITLE_EXTENSION = "x-schemavaults-title";
9
+ export const SCHEMAVAULTS_SCHEME_CHALLENGE_EXTENSION = "x-schemavaults-challenge";
10
+ export function toSchemaVaultsAuthExtension(auth) {
11
+ if (auth.type === "public") {
12
+ return {
13
+ public: true,
14
+ schemes: [],
15
+ routeGuard: null,
16
+ requiredScopes: [],
17
+ organization: null,
18
+ ...(auth.notes !== undefined ? { notes: auth.notes } : {}),
19
+ };
20
+ }
21
+ return {
22
+ public: false,
23
+ schemes: auth.schemes.map((scheme) => scheme.name),
24
+ routeGuard: auth.routeGuard ?? "authenticated",
25
+ requiredScopes: [...(auth.requiredScopes ?? [])],
26
+ organization: auth.organization
27
+ ? {
28
+ parameter: auth.organization.parameter,
29
+ roles: [...auth.organization.roles],
30
+ adminBypass: auth.organization.adminBypass ?? true,
31
+ }
32
+ : null,
33
+ ...(auth.notes !== undefined ? { notes: auth.notes } : {}),
34
+ };
35
+ }
36
+ export function isSchemaVaultsAuthExtension(value) {
37
+ if (typeof value !== "object" || value === null)
38
+ return false;
39
+ const record = value;
40
+ return (typeof record.public === "boolean" &&
41
+ Array.isArray(record.schemes) &&
42
+ Array.isArray(record.requiredScopes) &&
43
+ (record.routeGuard === null ||
44
+ record.routeGuard === "authenticated" ||
45
+ record.routeGuard === "admin"));
46
+ }
47
+ //# sourceMappingURL=extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../src/openapi/extensions.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,qBAA8B,CAAC;AAE1E,uFAAuF;AACvF,MAAM,CAAC,MAAM,mCAAmC,GAAG,sBAA+B,CAAC;AACnF,MAAM,CAAC,MAAM,uCAAuC,GAAG,0BAAmC,CAAC;AAgB3F,MAAM,UAAU,2BAA2B,CAAC,IAAmB;IAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,IAAI;YAChB,cAAc,EAAE,EAAE;YAClB,YAAY,EAAE,IAAI;YAClB,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3D,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;QAClD,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,eAAe;QAC9C,cAAc,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;QAChD,YAAY,EAAE,IAAI,CAAC,YAAY;YAC7B,CAAC,CAAC;gBACE,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,SAAS;gBACtC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;gBACnC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,WAAW,IAAI,IAAI;aACnD;YACH,CAAC,CAAC,IAAI;QACR,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,KAAc;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,OAAO,CACL,OAAO,MAAM,CAAC,MAAM,KAAK,SAAS;QAClC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;QAC7B,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;QACpC,CAAC,MAAM,CAAC,UAAU,KAAK,IAAI;YACzB,MAAM,CAAC,UAAU,KAAK,eAAe;YACrC,MAAM,CAAC,UAAU,KAAK,OAAO,CAAC,CACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare function isOpenApiPath(path: string): boolean;
2
+ /** Names of the `{placeholders}` in an OpenAPI path, in order. */
3
+ export declare function extractPathParameterNames(path: string): string[];
4
+ /** `/api/apps/{app_id}` → `/api/apps/:app_id` (Hono / Express style). */
5
+ export declare function openApiPathToHonoPath(path: string): string;
6
+ /** `/api/apps/:app_id` → `/api/apps/{app_id}`. */
7
+ export declare function honoPathToOpenApiPath(path: string): string;
@@ -0,0 +1,26 @@
1
+ const OPENAPI_PATH_PARAM_REGEX = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
2
+ export function isOpenApiPath(path) {
3
+ return (typeof path === "string" &&
4
+ path.startsWith("/") &&
5
+ !path.includes(":") &&
6
+ !/\{[^}]*[^A-Za-z0-9_}][^}]*\}/.test(path));
7
+ }
8
+ /** Names of the `{placeholders}` in an OpenAPI path, in order. */
9
+ export function extractPathParameterNames(path) {
10
+ const names = [];
11
+ for (const match of path.matchAll(OPENAPI_PATH_PARAM_REGEX)) {
12
+ const name = match[1];
13
+ if (typeof name === "string")
14
+ names.push(name);
15
+ }
16
+ return names;
17
+ }
18
+ /** `/api/apps/{app_id}` → `/api/apps/:app_id` (Hono / Express style). */
19
+ export function openApiPathToHonoPath(path) {
20
+ return path.replace(OPENAPI_PATH_PARAM_REGEX, ":$1");
21
+ }
22
+ /** `/api/apps/:app_id` → `/api/apps/{app_id}`. */
23
+ export function honoPathToOpenApiPath(path) {
24
+ return path.replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, "{$1}");
25
+ }
26
+ //# sourceMappingURL=path-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-format.js","sourceRoot":"","sources":["../../src/openapi/path-format.ts"],"names":[],"mappings":"AAAA,MAAM,wBAAwB,GAAG,+BAA+B,CAAC;AAEjE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QACnB,CAAC,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3C,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,yBAAyB,CAAC,IAAY;IACpD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,IAAI,CAAC,OAAO,CAAC,4BAA4B,EAAE,MAAM,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,168 @@
1
+ import type { z, ZodObject, ZodType } from "zod";
2
+ import type { OrganizationMembershipRoleType } from "@schemavaults/auth-common/organizations";
3
+ import { type HttpMethod } from "./http-method";
4
+ import type { OperationAuth, PublicOperationAuth } from "./auth-scheme";
5
+ export type RequestBodyContentType = "application/json" | "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain" | (string & {});
6
+ export interface RequestBodyDefinition<TSchema extends ZodType = ZodType> {
7
+ /** Media type the body is parsed as (default `application/json`). */
8
+ readonly contentType?: RequestBodyContentType;
9
+ readonly schema: TSchema;
10
+ readonly description?: string;
11
+ /** Default true. */
12
+ readonly required?: boolean;
13
+ }
14
+ export interface ResponseDefinition<TSchema extends ZodType | undefined = ZodType | undefined> {
15
+ readonly description: string;
16
+ /** Omit for empty responses (204, 304, redirects, ...). */
17
+ readonly schema?: TSchema;
18
+ /** Media type of the response body (default `application/json`). */
19
+ readonly contentType?: string;
20
+ /** Documented response headers. */
21
+ readonly headers?: ZodObject;
22
+ }
23
+ export type ResponsesDefinition = Readonly<Record<number, ResponseDefinition>>;
24
+ export interface OperationRequestDefinition<TParams extends ZodObject | undefined = ZodObject | undefined, TQuery extends ZodObject | undefined = ZodObject | undefined, THeaders extends ZodObject | undefined = ZodObject | undefined, TBody extends RequestBodyDefinition | undefined = RequestBodyDefinition | undefined> {
25
+ /** Path parameters; keys must match the `{placeholders}` in `path`. */
26
+ readonly params?: TParams;
27
+ /** Query string parameters (repeated keys arrive as `string[]`). */
28
+ readonly query?: TQuery;
29
+ /** Request headers (lower-case keys). */
30
+ readonly headers?: THeaders;
31
+ readonly body?: TBody;
32
+ }
33
+ export type InferParsed<T> = T extends ZodType ? z.output<T> : Readonly<Record<string, never>>;
34
+ export type InferBody<T> = T extends RequestBodyDefinition<infer S> ? S extends ZodType ? z.output<S> : undefined : undefined;
35
+ export type ResponseStatusOf<TResponses extends ResponsesDefinition> = Extract<keyof TResponses, number>;
36
+ export type ResponseBodyOf<TResponses extends ResponsesDefinition, S extends keyof TResponses> = TResponses[S] extends {
37
+ readonly schema: infer TSchema;
38
+ } ? TSchema extends ZodType ? z.output<TSchema> : undefined : undefined;
39
+ export type EmptyResponseStatusOf<TResponses extends ResponsesDefinition> = {
40
+ [S in ResponseStatusOf<TResponses>]: TResponses[S] extends {
41
+ readonly schema: ZodType;
42
+ } ? never : S;
43
+ }[ResponseStatusOf<TResponses>];
44
+ /**
45
+ * Result of a successful credential resolution for one auth scheme. Built
46
+ * by the {@link AuthResolver} registered for the scheme, so its `user` type
47
+ * is whatever the host application resolves (e.g. `UserData` from
48
+ * `@schemavaults/auth-common` on the auth server).
49
+ */
50
+ export interface AuthPrincipal<TUser = unknown> {
51
+ /** Name of the auth scheme whose resolver produced this principal. */
52
+ readonly scheme: string;
53
+ /** Resolved user, or null for non-user principals (client credentials, api keys). */
54
+ readonly user: TUser | null;
55
+ /** Platform administrator flag, used for the "admin" route guard and org bypass. */
56
+ readonly isAdmin: boolean;
57
+ /**
58
+ * Space separated scope string granted to the presented token (RFC 6749
59
+ * §3.3), or null when the credential carries no scope claim.
60
+ */
61
+ readonly scope: string | null;
62
+ /** OAuth client id for client-credential style principals. */
63
+ readonly clientId?: string;
64
+ /**
65
+ * Resolves the principal's membership role in an organization, used for
66
+ * {@link OrganizationRoleRequirement}. Absent means "cannot be a member".
67
+ */
68
+ readonly getOrganizationRole?: (organizationId: string) => Promise<OrganizationMembershipRoleType | false>;
69
+ }
70
+ export interface ResponseInit_ {
71
+ readonly headers?: HeadersInit;
72
+ }
73
+ export interface OperationHandlerContext<TParams, TQuery, THeaders, TBody, TResponses extends ResponsesDefinition, TContext, TAuth> {
74
+ readonly params: TParams;
75
+ readonly query: TQuery;
76
+ readonly headers: THeaders;
77
+ readonly body: TBody;
78
+ /** Resolved principal, or null on public operations. */
79
+ readonly auth: TAuth;
80
+ /** Host-provided per-request context (database handle, environment, ...). */
81
+ readonly context: TContext;
82
+ readonly request: Request;
83
+ readonly url: URL;
84
+ /** Type-checked JSON response for one of the declared status codes. */
85
+ json<S extends ResponseStatusOf<TResponses>>(status: S, body: ResponseBodyOf<TResponses, S>, init?: ResponseInit_): Response;
86
+ /** Body-less response for a declared status code without a schema. */
87
+ empty(status: EmptyResponseStatusOf<TResponses>, init?: ResponseInit_): Response;
88
+ redirect(location: string, status?: 301 | 302 | 303 | 307 | 308): Response;
89
+ }
90
+ export type OperationHandlerResult = Response | Promise<Response>;
91
+ export interface OperationDefinition<TParams extends ZodObject | undefined = ZodObject | undefined, TQuery extends ZodObject | undefined = ZodObject | undefined, THeaders extends ZodObject | undefined = ZodObject | undefined, TBody extends RequestBodyDefinition | undefined = RequestBodyDefinition | undefined, TResponses extends ResponsesDefinition = ResponsesDefinition, TContext = unknown, TUser = unknown, TAuth extends OperationAuth = OperationAuth> {
92
+ readonly method: HttpMethod;
93
+ /** OpenAPI style path with `{param}` placeholders, e.g. `/api/apps/{app_id}`. */
94
+ readonly path: string;
95
+ /** Unique id; defaults to `<method>_<path>` slug. */
96
+ readonly operationId: string;
97
+ readonly summary: string;
98
+ readonly description?: string;
99
+ readonly tags: readonly string[];
100
+ readonly deprecated?: boolean;
101
+ readonly auth: TAuth;
102
+ readonly request: OperationRequestDefinition<TParams, TQuery, THeaders, TBody>;
103
+ readonly responses: TResponses;
104
+ /** Extra OpenAPI vendor extensions merged into the operation object. */
105
+ readonly extensions?: Readonly<Record<`x-${string}`, unknown>>;
106
+ handler(ctx: OperationHandlerContext<InferParsed<TParams>, InferParsed<TQuery>, InferParsed<THeaders>, InferBody<TBody>, TResponses, TContext, TAuth extends PublicOperationAuth ? null : AuthPrincipal<TUser>>): OperationHandlerResult;
107
+ }
108
+ /**
109
+ * Structural, type-erased view of any operation definition. Every
110
+ * `OperationDefinition<...>` is assignable to it, so heterogeneous
111
+ * operation lists can be passed to `buildOpenApiDocument` and
112
+ * `createOperationsApp`.
113
+ */
114
+ export interface AnyOperationDefinition {
115
+ readonly method: HttpMethod;
116
+ readonly path: string;
117
+ readonly operationId: string;
118
+ readonly summary: string;
119
+ readonly description?: string;
120
+ readonly tags: readonly string[];
121
+ readonly deprecated?: boolean;
122
+ readonly auth: OperationAuth;
123
+ readonly request: OperationRequestDefinition;
124
+ readonly responses: ResponsesDefinition;
125
+ readonly extensions?: Readonly<Record<`x-${string}`, unknown>>;
126
+ handler(ctx: any): OperationHandlerResult;
127
+ }
128
+ export interface OperationInput<TParams extends ZodObject | undefined, TQuery extends ZodObject | undefined, THeaders extends ZodObject | undefined, TBody extends RequestBodyDefinition | undefined, TResponses extends ResponsesDefinition, TContext, TUser, TAuth extends OperationAuth> {
129
+ readonly method: HttpMethod;
130
+ readonly path: string;
131
+ readonly operationId?: string;
132
+ readonly summary: string;
133
+ readonly description?: string;
134
+ readonly tags?: readonly string[];
135
+ readonly deprecated?: boolean;
136
+ readonly auth: TAuth;
137
+ readonly request?: OperationRequestDefinition<TParams, TQuery, THeaders, TBody>;
138
+ readonly responses: TResponses;
139
+ readonly extensions?: Readonly<Record<`x-${string}`, unknown>>;
140
+ readonly handler: OperationDefinition<TParams, TQuery, THeaders, TBody, TResponses, TContext, TUser, TAuth>["handler"];
141
+ }
142
+ export type OperationDefiner<TContext, TUser> = <const TParams extends ZodObject | undefined = undefined, const TQuery extends ZodObject | undefined = undefined, const THeaders extends ZodObject | undefined = undefined, const TBody extends RequestBodyDefinition | undefined = undefined, const TResponses extends ResponsesDefinition = ResponsesDefinition, const TAuth extends OperationAuth = OperationAuth>(input: OperationInput<TParams, TQuery, THeaders, TBody, TResponses, TContext, TUser, TAuth>) => OperationDefinition<TParams, TQuery, THeaders, TBody, TResponses, TContext, TUser, TAuth>;
143
+ export declare function defaultOperationId(method: HttpMethod, path: string): string;
144
+ /**
145
+ * Creates a `defineOperation` bound to the host application's per-request
146
+ * context and resolved user types:
147
+ *
148
+ * ```ts
149
+ * const defineOperation = createOperationDefiner<{ dbh: Kysely<AuthDatabase> }, UserData>();
150
+ * ```
151
+ */
152
+ export declare function createOperationDefiner<TContext = unknown, TUser = unknown>(): OperationDefiner<TContext, TUser>;
153
+ /** Untyped-context `defineOperation`; prefer {@link createOperationDefiner} in apps. */
154
+ export declare const defineOperation: OperationDefiner<unknown, unknown>;
155
+ export interface OperationGroup {
156
+ /** Tags applied to every operation (prepended to the operation's own). */
157
+ readonly tags?: readonly string[];
158
+ /** Path prefix (OpenAPI style) prepended to every operation path. */
159
+ readonly pathPrefix?: string;
160
+ readonly operations: readonly AnyOperationDefinition[];
161
+ }
162
+ /**
163
+ * Flattens a group into standalone operations, applying the shared tags and
164
+ * path prefix. The result can be passed to `buildOpenApiDocument` and
165
+ * `createOperationsApp` like any other operation list.
166
+ */
167
+ export declare function defineOperationGroup(group: OperationGroup): AnyOperationDefinition[];
168
+ export declare function assertUniqueOperations(operations: readonly AnyOperationDefinition[]): void;
@@ -0,0 +1,111 @@
1
+ import { HTTP_METHODS_WITH_REQUEST_BODY, isHttpMethod } from "./http-method";
2
+ import { extractPathParameterNames, isOpenApiPath } from "./openapi/path-format";
3
+ export function defaultOperationId(method, path) {
4
+ const slug = path
5
+ .replace(/[{}]/g, "")
6
+ .split("/")
7
+ .filter((segment) => segment.length > 0)
8
+ .join("_")
9
+ .replace(/[^A-Za-z0-9_]+/g, "_");
10
+ return slug.length > 0 ? `${method}_${slug}` : method;
11
+ }
12
+ function validateOperationInput(input) {
13
+ if (!isHttpMethod(input.method)) {
14
+ throw new TypeError(`Unsupported HTTP method "${String(input.method)}"`);
15
+ }
16
+ if (!isOpenApiPath(input.path)) {
17
+ throw new TypeError(`Operation path "${input.path}" must start with "/" and use {param} placeholders`);
18
+ }
19
+ if (typeof input.summary !== "string" || input.summary.trim().length === 0) {
20
+ throw new TypeError(`Operation ${input.method.toUpperCase()} ${input.path} needs a summary`);
21
+ }
22
+ const placeholders = extractPathParameterNames(input.path);
23
+ const declared = input.request?.params
24
+ ? Object.keys(input.request.params.shape)
25
+ : [];
26
+ for (const name of placeholders) {
27
+ if (!declared.includes(name)) {
28
+ throw new TypeError(`Path parameter {${name}} of ${input.path} is not declared in request.params`);
29
+ }
30
+ }
31
+ for (const name of declared) {
32
+ if (!placeholders.includes(name)) {
33
+ throw new TypeError(`request.params declares "${name}" but ${input.path} has no {${name}} placeholder`);
34
+ }
35
+ }
36
+ if (input.request?.body && !HTTP_METHODS_WITH_REQUEST_BODY.has(input.method)) {
37
+ throw new TypeError(`${input.method.toUpperCase()} ${input.path} declares a request body, which is not allowed for that method`);
38
+ }
39
+ const statuses = Object.keys(input.responses);
40
+ if (statuses.length === 0) {
41
+ throw new TypeError(`${input.method.toUpperCase()} ${input.path} declares no responses`);
42
+ }
43
+ for (const status of statuses) {
44
+ const code = Number(status);
45
+ if (!Number.isInteger(code) || code < 100 || code > 599) {
46
+ throw new TypeError(`Invalid response status "${status}" on ${input.path}`);
47
+ }
48
+ }
49
+ }
50
+ /**
51
+ * Creates a `defineOperation` bound to the host application's per-request
52
+ * context and resolved user types:
53
+ *
54
+ * ```ts
55
+ * const defineOperation = createOperationDefiner<{ dbh: Kysely<AuthDatabase> }, UserData>();
56
+ * ```
57
+ */
58
+ export function createOperationDefiner() {
59
+ return function defineOperationForContext(input) {
60
+ validateOperationInput(input);
61
+ const operation = {
62
+ method: input.method,
63
+ path: input.path,
64
+ operationId: input.operationId ?? defaultOperationId(input.method, input.path),
65
+ summary: input.summary,
66
+ description: input.description,
67
+ tags: Object.freeze([...(input.tags ?? [])]),
68
+ deprecated: input.deprecated,
69
+ auth: input.auth,
70
+ request: input.request ?? {},
71
+ responses: input.responses,
72
+ extensions: input.extensions,
73
+ handler: input.handler,
74
+ };
75
+ return Object.freeze(operation);
76
+ };
77
+ }
78
+ /** Untyped-context `defineOperation`; prefer {@link createOperationDefiner} in apps. */
79
+ export const defineOperation = createOperationDefiner();
80
+ /**
81
+ * Flattens a group into standalone operations, applying the shared tags and
82
+ * path prefix. The result can be passed to `buildOpenApiDocument` and
83
+ * `createOperationsApp` like any other operation list.
84
+ */
85
+ export function defineOperationGroup(group) {
86
+ const prefix = group.pathPrefix ?? "";
87
+ if (prefix.length > 0 && !isOpenApiPath(prefix)) {
88
+ throw new TypeError(`Group pathPrefix "${prefix}" must start with "/"`);
89
+ }
90
+ return group.operations.map((operation) => {
91
+ const path = prefix.length > 0 ? `${prefix.replace(/\/+$/, "")}${operation.path}` : operation.path;
92
+ const tags = Array.from(new Set([...(group.tags ?? []), ...operation.tags]));
93
+ return Object.freeze({ ...operation, path, tags });
94
+ });
95
+ }
96
+ export function assertUniqueOperations(operations) {
97
+ const ids = new Set();
98
+ const routes = new Set();
99
+ for (const operation of operations) {
100
+ if (ids.has(operation.operationId)) {
101
+ throw new TypeError(`Duplicate operationId "${operation.operationId}"`);
102
+ }
103
+ ids.add(operation.operationId);
104
+ const route = `${operation.method} ${operation.path}`;
105
+ if (routes.has(route)) {
106
+ throw new TypeError(`Duplicate route ${route.toUpperCase()}`);
107
+ }
108
+ routes.add(route);
109
+ }
110
+ }
111
+ //# sourceMappingURL=operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.js","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { Hono, type Context } from "hono";
2
+ import type { OpenAPIObject } from "openapi3-ts/oas31";
3
+ import type { AnyOperationDefinition } from "../operation";
4
+ import { type AuthResolvers } from "./resolve-auth";
5
+ export interface OpenApiDocumentRouteOptions {
6
+ /** Path (relative to `basePath`) serving the JSON document. Default `/openapi.json`. */
7
+ readonly path?: string;
8
+ /**
9
+ * The document, or a function producing it per request. The function
10
+ * receives the Hono context so hosts can derive request-dependent parts
11
+ * such as `servers` from the incoming Host / X-Forwarded-* headers.
12
+ */
13
+ readonly document: OpenAPIObject | ((c: Context) => OpenAPIObject | Promise<OpenAPIObject>);
14
+ }
15
+ export interface CreateOperationsAppOptions<TContext = unknown, TUser = unknown> {
16
+ readonly operations: readonly AnyOperationDefinition[];
17
+ /**
18
+ * Prefix stripped by the deployment before routing (e.g. `/api` when the
19
+ * app is mounted from `app/api/[[...route]]/route.ts`). Operation paths
20
+ * stay absolute in the OpenAPI document; leave unset to route on them
21
+ * verbatim.
22
+ */
23
+ readonly basePath?: string;
24
+ /** Credential resolvers keyed by auth scheme name. */
25
+ readonly authResolvers?: AuthResolvers<TUser>;
26
+ /** Builds the per-request context handed to handlers as `ctx.context`. */
27
+ readonly context?: (c: Context) => Promise<TContext> | TContext;
28
+ /** Serve the OpenAPI document from the app; omit to not expose it. */
29
+ readonly openapi?: OpenApiDocumentRouteOptions;
30
+ /** Called for unexpected (non-OperationError) failures before the 500 is sent. */
31
+ readonly onError?: (error: unknown, c: Context) => void | Promise<void>;
32
+ /** Extra middleware / routes registered before the operations (CORS, logging, ...). */
33
+ readonly configure?: (app: Hono) => void;
34
+ }
35
+ /**
36
+ * Builds a Hono app that routes, validates, authenticates and dispatches
37
+ * the given operations. Mount it on Vercel functions with
38
+ * `toVercelHandler()` or Next.js route handlers with `toNextRouteHandlers()`.
39
+ */
40
+ export declare function createOperationsApp<TContext = unknown, TUser = unknown>(options: CreateOperationsAppOptions<TContext, TUser>): Hono;